123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
- /* NetworkManager Wireless Applet -- Display wireless access points and allow user control
- *
- * Dan Williams <dcbw@redhat.com>
- * Tim Niemueller <tim@niemueller.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2004 - 2008 Red Hat, Inc.
- * 2005 Tim Niemueller [www.niemueller.de]
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #include <errno.h>
- #include <string.h>
- #include <stdlib.h>
- #include <glib/gi18n.h>
- #include <gtk/gtk.h>
- #define SECRET_API_SUBJECT_TO_CHANGE
- #include <libsecret/secret.h>
- #include <nm-setting-vpn.h>
- #include <nm-setting-connection.h>
- #include <nm-vpn-plugin-utils.h>
- #include <nm-vpn-password-dialog.h>
- #include "common/utils.h"
- #include "src/nm-openvpn-service.h"
- #define KEYRING_UUID_TAG "connection-uuid"
- #define KEYRING_SN_TAG "setting-name"
- #define KEYRING_SK_TAG "setting-key"
- static const SecretSchema network_manager_secret_schema = {
- "org.freedesktop.NetworkManager.Connection",
- SECRET_SCHEMA_DONT_MATCH_NAME,
- {
- { KEYRING_UUID_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
- { KEYRING_SN_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
- { KEYRING_SK_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
- { NULL, 0 },
- }
- };
- #define UI_KEYFILE_GROUP "VPN Plugin UI"
- static char *
- keyring_lookup_secret (const char *uuid, const char *secret_name)
- {
- GHashTable *attrs;
- GList *list;
- char *secret = NULL;
- attrs = secret_attributes_build (&network_manager_secret_schema,
- KEYRING_UUID_TAG, uuid,
- KEYRING_SN_TAG, NM_SETTING_VPN_SETTING_NAME,
- KEYRING_SK_TAG, secret_name,
- NULL);
- list = secret_service_search_sync (NULL, &network_manager_secret_schema, attrs,
- SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS,
- NULL, NULL);
- if (list && list->data) {
- SecretItem *item = list->data;
- SecretValue *value = secret_item_get_secret (item);
- if (value) {
- secret = g_strdup (secret_value_get (value, NULL));
- secret_value_unref (value);
- }
- }
- g_list_free_full (list, g_object_unref);
- g_hash_table_unref (attrs);
- return secret;
- }
- /*****************************************************************/
- typedef void (*NoSecretsRequiredFunc) (void);
- /* Returns TRUE on success, FALSE on cancel */
- typedef gboolean (*AskUserFunc) (const char *vpn_name,
- const char *prompt,
- gboolean need_password,
- const char *existing_password,
- char **out_new_password,
- gboolean need_certpass,
- const char *existing_certpass,
- char **out_new_certpass);
- typedef void (*FinishFunc) (const char *vpn_name,
- const char *prompt,
- gboolean allow_interaction,
- gboolean need_password,
- const char *password,
- gboolean need_certpass,
- const char *certpass);
- /*****************************************************************/
- /* External UI mode stuff */
- static void
- keyfile_add_entry_info (GKeyFile *keyfile,
- const gchar *key,
- const gchar *value,
- const gchar *label,
- gboolean is_secret,
- gboolean should_ask)
- {
- g_key_file_set_string (keyfile, key, "Value", value);
- g_key_file_set_string (keyfile, key, "Label", label);
- g_key_file_set_boolean (keyfile, key, "IsSecret", is_secret);
- g_key_file_set_boolean (keyfile, key, "ShouldAsk", should_ask);
- }
- static void
- keyfile_print_stdout (GKeyFile *keyfile)
- {
- gchar *data;
- gsize length;
- data = g_key_file_to_data (keyfile, &length, NULL);
- fputs (data, stdout);
- g_free (data);
- }
- static void
- eui_no_secrets_required (void)
- {
- GKeyFile *keyfile;
- keyfile = g_key_file_new ();
- g_key_file_set_integer (keyfile, UI_KEYFILE_GROUP, "Version", 2);
- keyfile_add_entry_info (keyfile, NM_OPENVPN_KEY_NOSECRET, "true", "", TRUE, FALSE);
- keyfile_print_stdout (keyfile);
- g_key_file_unref (keyfile);
- }
- static void
- eui_finish (const char *vpn_name,
- const char *prompt,
- gboolean allow_interaction,
- gboolean need_password,
- const char *existing_password,
- gboolean need_certpass,
- const char *existing_certpass)
- {
- GKeyFile *keyfile;
- char *title;
- keyfile = g_key_file_new ();
- g_key_file_set_integer (keyfile, UI_KEYFILE_GROUP, "Version", 2);
- g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Description", prompt);
- title = g_strdup_printf (_("Authenticate VPN %s"), vpn_name);
- g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Title", title);
- g_free (title);
- keyfile_add_entry_info (keyfile,
- NM_OPENVPN_KEY_PASSWORD,
- existing_password ? existing_password : "",
- _("Password:"),
- TRUE,
- need_password && allow_interaction);
- keyfile_add_entry_info (keyfile,
- NM_OPENVPN_KEY_CERTPASS,
- existing_certpass ? existing_certpass : "",
- _("Certificate password:"),
- TRUE,
- need_certpass && allow_interaction);
- keyfile_print_stdout (keyfile);
- g_key_file_unref (keyfile);
- }
- /*****************************************************************/
- static void
- std_no_secrets_required (void)
- {
- printf ("%s\n%s\n\n\n", NM_OPENVPN_KEY_NOSECRET, "true");
- }
- static gboolean
- std_ask_user (const char *vpn_name,
- const char *prompt,
- gboolean need_password,
- const char *existing_password,
- char **out_new_password,
- gboolean need_certpass,
- const char *existing_certpass,
- char **out_new_certpass)
- {
- NMAVpnPasswordDialog *dialog;
- gboolean success = FALSE;
- g_return_val_if_fail (vpn_name != NULL, FALSE);
- g_return_val_if_fail (prompt != NULL, FALSE);
- g_return_val_if_fail (out_new_password != NULL, FALSE);
- g_return_val_if_fail (out_new_certpass != NULL, FALSE);
- dialog = NMA_VPN_PASSWORD_DIALOG (nma_vpn_password_dialog_new (_("Authenticate VPN"), prompt, NULL));
- /* pre-fill dialog with existing passwords */
- nma_vpn_password_dialog_set_show_password (dialog, need_password);
- if (need_password)
- nma_vpn_password_dialog_set_password (dialog, existing_password);
- nma_vpn_password_dialog_set_show_password_secondary (dialog, need_certpass);
- if (need_certpass) {
- nma_vpn_password_dialog_set_password_secondary_label (dialog, _("Certificate pass_word:") );
- nma_vpn_password_dialog_set_password_secondary (dialog, existing_certpass);
- }
- gtk_widget_show (GTK_WIDGET (dialog));
- if (nma_vpn_password_dialog_run_and_block (dialog)) {
- if (need_password)
- *out_new_password = g_strdup (nma_vpn_password_dialog_get_password (dialog));
- if (need_certpass)
- *out_new_certpass = g_strdup (nma_vpn_password_dialog_get_password_secondary (dialog));
- success = TRUE;
- }
- gtk_widget_destroy (GTK_WIDGET (dialog));
- return success;
- }
- static void
- wait_for_quit (void)
- {
- GString *str;
- char c;
- ssize_t n;
- time_t start;
- str = g_string_sized_new (10);
- start = time (NULL);
- do {
- errno = 0;
- n = read (0, &c, 1);
- if (n == 0 || (n < 0 && errno == EAGAIN))
- g_usleep (G_USEC_PER_SEC / 10);
- else if (n == 1) {
- g_string_append_c (str, c);
- if (strstr (str->str, "QUIT") || (str->len > 10))
- break;
- } else
- break;
- } while (time (NULL) < start + 20);
- g_string_free (str, TRUE);
- }
- static void
- std_finish (const char *vpn_name,
- const char *prompt,
- gboolean allow_interaction,
- gboolean need_password,
- const char *password,
- gboolean need_certpass,
- const char *certpass)
- {
- /* Send the passwords back to our parent */
- if (password)
- printf ("%s\n%s\n", NM_OPENVPN_KEY_PASSWORD, password);
- if (certpass)
- printf ("%s\n%s\n", NM_OPENVPN_KEY_CERTPASS, certpass);
- printf ("\n\n");
- /* for good measure, flush stdout since Kansas is going Bye-Bye */
- fflush (stdout);
- /* Wait for quit signal */
- wait_for_quit ();
- }
- /*****************************************************************/
- static void
- get_existing_passwords (GHashTable *vpn_data,
- GHashTable *existing_secrets,
- const char *vpn_uuid,
- gboolean need_password,
- gboolean need_certpass,
- char **out_password,
- char **out_certpass)
- {
- NMSettingSecretFlags pw_flags = NM_SETTING_SECRET_FLAG_NONE;
- NMSettingSecretFlags cp_flags = NM_SETTING_SECRET_FLAG_NONE;
- g_return_if_fail (out_password != NULL);
- g_return_if_fail (out_certpass != NULL);
- nm_vpn_plugin_utils_get_secret_flags (vpn_data, NM_OPENVPN_KEY_PASSWORD, &pw_flags);
- if (need_password) {
- if (!(pw_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) {
- *out_password = g_strdup (g_hash_table_lookup (existing_secrets, NM_OPENVPN_KEY_PASSWORD));
- if (!*out_password)
- *out_password = keyring_lookup_secret (vpn_uuid, NM_OPENVPN_KEY_PASSWORD);
- }
- }
- nm_vpn_plugin_utils_get_secret_flags (vpn_data, NM_OPENVPN_KEY_CERTPASS, &cp_flags);
- if (need_certpass) {
- if (!(cp_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) {
- *out_certpass = g_strdup (g_hash_table_lookup (existing_secrets, NM_OPENVPN_KEY_CERTPASS));
- if (!*out_certpass)
- *out_certpass = keyring_lookup_secret (vpn_uuid, NM_OPENVPN_KEY_CERTPASS);
- }
- }
- }
- #define VPN_MSG_TAG "x-vpn-message:"
- static char *
- get_passwords_required (GHashTable *data,
- char **hints,
- gboolean *out_need_password,
- gboolean *out_need_certpass)
- {
- const char *ctype, *val;
- NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
- char *prompt = NULL;
- char **iter;
- /* If hints are given, then always ask for what the hints require */
- if (hints && g_strv_length (hints)) {
- for (iter = hints; iter && *iter; iter++) {
- if (!prompt && g_str_has_prefix (*iter, VPN_MSG_TAG))
- prompt = g_strdup (*iter + strlen (VPN_MSG_TAG));
- else if (strcmp (*iter, NM_OPENVPN_KEY_PASSWORD) == 0)
- *out_need_password = TRUE;
- else if (strcmp (*iter, NM_OPENVPN_KEY_CERTPASS) == 0)
- *out_need_certpass = TRUE;
- }
- return prompt;
- }
- ctype = g_hash_table_lookup (data, NM_OPENVPN_KEY_CONNECTION_TYPE);
- g_return_val_if_fail (ctype != NULL, NULL);
- if (!strcmp (ctype, NM_OPENVPN_CONTYPE_TLS) || !strcmp (ctype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
- /* Normal user password */
- nm_vpn_plugin_utils_get_secret_flags (data, NM_OPENVPN_KEY_PASSWORD, &flags);
- if ( !strcmp (ctype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)
- && !(flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED))
- *out_need_password = TRUE;
- /* Encrypted private key password */
- val = g_hash_table_lookup (data, NM_OPENVPN_KEY_KEY);
- if (val)
- *out_need_certpass = is_encrypted (val);
- } else if (!strcmp (ctype, NM_OPENVPN_CONTYPE_PASSWORD)) {
- nm_vpn_plugin_utils_get_secret_flags (data, NM_OPENVPN_KEY_PASSWORD, &flags);
- if (!(flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED))
- *out_need_password = TRUE;
- }
- return NULL;
- }
- static void
- free_secret (char *p)
- {
- if (p) {
- memset (p, 0, strlen (p));
- g_free (p);
- }
- }
- int
- main (int argc, char *argv[])
- {
- gboolean retry = FALSE, allow_interaction = FALSE;
- gchar *vpn_name = NULL;
- gchar *vpn_uuid = NULL;
- gchar *vpn_service = NULL;
- GHashTable *data = NULL, *secrets = NULL;
- gboolean need_password = FALSE, need_certpass = FALSE;
- char *existing_password = NULL, *existing_certpass = NULL;
- char *new_password = NULL, *new_certpass = NULL;
- char **hints = NULL;
- char *prompt = NULL;
- gboolean external_ui_mode = FALSE, canceled = FALSE, ask_user = FALSE;
- NoSecretsRequiredFunc no_secrets_required_func = NULL;
- AskUserFunc ask_user_func = NULL;
- FinishFunc finish_func = NULL;
- GOptionContext *context;
- GOptionEntry entries[] = {
- { "reprompt", 'r', 0, G_OPTION_ARG_NONE, &retry, "Reprompt for passwords", NULL},
- { "uuid", 'u', 0, G_OPTION_ARG_STRING, &vpn_uuid, "UUID of VPN connection", NULL},
- { "name", 'n', 0, G_OPTION_ARG_STRING, &vpn_name, "Name of VPN connection", NULL},
- { "service", 's', 0, G_OPTION_ARG_STRING, &vpn_service, "VPN service type", NULL},
- { "allow-interaction", 'i', 0, G_OPTION_ARG_NONE, &allow_interaction, "Allow user interaction", NULL},
- { "external-ui-mode", 0, 0, G_OPTION_ARG_NONE, &external_ui_mode, "External UI mode", NULL},
- { "hint", 't', 0, G_OPTION_ARG_STRING_ARRAY, &hints, "Hints from the VPN plugin", NULL},
- { NULL }
- };
- bindtextdomain (GETTEXT_PACKAGE, NULL);
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
- textdomain (GETTEXT_PACKAGE);
- gtk_init (&argc, &argv);
- context = g_option_context_new ("- openvpn auth dialog");
- g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
- g_option_context_parse (context, &argc, &argv, NULL);
- g_option_context_free (context);
- if (vpn_uuid == NULL || vpn_name == NULL || vpn_service == NULL) {
- fprintf (stderr, "Have to supply ID, name, and service\n");
- return EXIT_FAILURE;
- }
- if (strcmp (vpn_service, NM_DBUS_SERVICE_OPENVPN) != 0) {
- fprintf (stderr, "This dialog only works with the '%s' service\n", NM_DBUS_SERVICE_OPENVPN);
- return EXIT_FAILURE;
- }
- if (!nm_vpn_plugin_utils_read_vpn_details (0, &data, &secrets)) {
- fprintf (stderr, "Failed to read '%s' (%s) data and secrets from stdin.\n",
- vpn_name, vpn_uuid);
- return 1;
- }
- if (external_ui_mode) {
- no_secrets_required_func = eui_no_secrets_required;
- finish_func = eui_finish;
- } else {
- no_secrets_required_func = std_no_secrets_required;
- ask_user_func = std_ask_user;
- finish_func = std_finish;
- }
- /* Determine which passwords are actually required, either from hints or
- * from looking at the VPN configuration.
- */
- prompt = get_passwords_required (data, hints, &need_password, &need_certpass);
- if (!prompt)
- prompt = g_strdup_printf (_("You need to authenticate to access the Virtual Private Network '%s'."), vpn_name);
- /* Exit early if we don't need any passwords */
- if (!need_password && !need_certpass)
- no_secrets_required_func ();
- else {
- get_existing_passwords (data,
- secrets,
- vpn_uuid,
- need_password,
- need_certpass,
- &existing_password,
- &existing_certpass);
- if (need_password && !existing_password)
- ask_user = TRUE;
- if (need_certpass && !existing_certpass)
- ask_user = TRUE;
- /* If interaction is allowed then ask the user, otherwise pass back
- * whatever existing secrets we can find.
- */
- if (ask_user_func && allow_interaction && (ask_user || retry)) {
- canceled = !ask_user_func (vpn_name,
- prompt,
- need_password,
- existing_password,
- &new_password,
- need_certpass,
- existing_certpass,
- &new_certpass);
- }
- if (!canceled) {
- finish_func (vpn_name,
- prompt,
- allow_interaction,
- need_password,
- new_password ? new_password : existing_password,
- need_certpass,
- new_certpass ? new_certpass : existing_certpass);
- }
- free_secret (existing_password);
- free_secret (existing_certpass);
- free_secret (new_password);
- free_secret (new_certpass);
- }
- if (data)
- g_hash_table_unref (data);
- if (secrets)
- g_hash_table_unref (secrets);
- if (hints)
- g_strfreev (hints);
- g_free (prompt);
- return canceled ? 1 : 0;
- }
|