diff options
Diffstat (limited to 'clients/common')
| -rw-r--r-- | clients/common/nm-polkit-listener.c | 415 | ||||
| -rw-r--r-- | clients/common/nm-polkit-listener.h | 104 | ||||
| -rw-r--r-- | clients/common/nm-secret-agent-simple.c | 695 | ||||
| -rw-r--r-- | clients/common/nm-secret-agent-simple.h | 62 |
4 files changed, 1276 insertions, 0 deletions
diff --git a/clients/common/nm-polkit-listener.c b/clients/common/nm-polkit-listener.c new file mode 100644 index 00000000..82df1b2d --- /dev/null +++ b/clients/common/nm-polkit-listener.c @@ -0,0 +1,415 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * 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, see <http://www.gnu.org/licenses/>. + * + * Copyright 2014 Red Hat, Inc. + */ + +/** + * SECTION:nm-polkit-listener + * @short_description: A polkit agent listener + * + * #NMPolkitListener is the polkit agent listener used by nmcli and nmtui. + * http://www.freedesktop.org/software/polkit/docs/latest/index.html + * + * For an example polkit agent you can look at polkit source tree: + * http://cgit.freedesktop.org/polkit/tree/src/polkitagent/polkitagenttextlistener.c + * http://cgit.freedesktop.org/polkit/tree/src/programs/pkttyagent.c + * or LXDE polkit agent: + * http://git.lxde.org/gitweb/?p=debian/lxpolkit.git;a=blob;f=src/lxpolkit-listener.c + * https://github.com/lxde/lxqt-policykit/tree/master/src + */ + +#include "config.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nm-glib-compat.h" +#include "nm-polkit-listener.h" + +G_DEFINE_TYPE (NMPolkitListener, nm_polkit_listener, POLKIT_AGENT_TYPE_LISTENER) + +#define NM_POLKIT_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_POLKIT_LISTENER, NMPolkitListenerPrivate)) + +typedef struct { + gpointer reg_handle; /* handle of polkit agent registration */ + + GSimpleAsyncResult *simple; + PolkitAgentSession *active_session; + gulong cancel_id; + GCancellable *cancellable; + + char *action_id; + char *message; + char *icon_name; + char *identity; + + /* callbacks */ + NMPolkitListenerOnRequestFunc on_request_callback; + NMPolkitListenerOnShowInfoFunc on_show_info_callback; + NMPolkitListenerOnShowErrorFunc on_show_error_callback; + NMPolkitListenerOnCompletedFunc on_completed_callback; + gpointer request_callback_data; +} NMPolkitListenerPrivate; + + +static void +on_request (PolkitAgentSession *session, + const char *request, + gboolean echo_on, + gpointer user_data) +{ + NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (user_data); + char *response = NULL; + + if (priv->on_request_callback) { + response = priv->on_request_callback (request, priv->action_id, + priv->message, priv->icon_name, + priv->identity, echo_on, + priv->request_callback_data); + } + + if (response) { + polkit_agent_session_response (session, response); + g_free (response); + } else { + //FIXME: polkit_agent_session_cancel() should emit "completed", but it doesn't work for me ??? + //polkit_agent_session_cancel (session); + polkit_agent_session_response (session, ""); + } +} + +static void +on_show_info (PolkitAgentSession *session, + const char *text, + gpointer user_data) +{ + NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (user_data); + + if (priv->on_show_info_callback) + priv->on_show_info_callback (text); +} + +static void +on_show_error (PolkitAgentSession *session, + const char *text, + gpointer user_data) +{ + NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (user_data); + + if (priv->on_show_error_callback) + priv->on_show_error_callback (text); +} + +static void +on_completed (PolkitAgentSession *session, + gboolean gained_authorization, + gpointer user_data) +{ + NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (user_data); + + if (priv->on_completed_callback) + priv->on_completed_callback (gained_authorization); + + g_simple_async_result_complete_in_idle (priv->simple); + + g_object_unref (priv->simple); + g_object_unref (priv->active_session); + if (priv->cancellable) { + g_cancellable_disconnect (priv->cancellable, priv->cancel_id); + g_object_unref (priv->cancellable); + } + + priv->simple = NULL; + priv->active_session = NULL; + priv->cancel_id = 0; + + g_clear_pointer (&priv->action_id, g_free); + g_clear_pointer (&priv->message, g_free); + g_clear_pointer (&priv->icon_name, g_free); + g_clear_pointer (&priv->identity, g_free); +} + +static void +on_cancelled (GCancellable *cancellable, gpointer user_data) +{ + NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (user_data); + + polkit_agent_session_cancel (priv->active_session); +} + +static gint +compare_users (gconstpointer a, gconstpointer b) +{ + char *user; + int ret; + + if (POLKIT_IS_UNIX_USER (a)) + user = g_strdup (polkit_unix_user_get_name (POLKIT_UNIX_USER (a))); + else + user = polkit_identity_to_string (POLKIT_IDENTITY (a)); + + ret = g_strcmp0 ((const char *) user, (const char *) b); + g_free (user); + return ret; +} + +static PolkitIdentity * +choose_identity (GList *identities) +{ + const char *user; + GList *elem; + + /* Choose identity. First try current user, then root, and else + * take the firts one */ + user = getenv("USER"); + elem = g_list_find_custom (identities, user, (GCompareFunc) compare_users); + if (!elem) { + elem = g_list_find_custom (identities, "root", (GCompareFunc) compare_users); + if (!elem) + elem = identities; + } + + return elem->data; +} + +static void +initiate_authentication (PolkitAgentListener *listener, + const char *action_id, + const char *message, + const char *icon_name, + PolkitDetails *details, + const char *cookie, + GList *identities, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (listener); + GSimpleAsyncResult *simple; + PolkitIdentity *identity; + + simple = g_simple_async_result_new (G_OBJECT (listener), + callback, + user_data, + initiate_authentication); + if (priv->active_session != NULL) { + g_simple_async_result_set_error (simple, + POLKIT_ERROR, + POLKIT_ERROR_FAILED, + _("An authentication session is already underway.")); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + return; + } + + /* Choose identity */ + identity = choose_identity (identities); + + priv->active_session = polkit_agent_session_new (identity, cookie); + g_signal_connect (priv->active_session, + "completed", + G_CALLBACK (on_completed), + listener); + g_signal_connect (priv->active_session, + "request", + G_CALLBACK (on_request), + listener); + g_signal_connect (priv->active_session, + "show-info", + G_CALLBACK (on_show_info), + listener); + g_signal_connect (priv->active_session, + "show-error", + G_CALLBACK (on_show_error), + listener); + + priv->action_id = g_strdup (action_id); + priv->message = g_strdup (message); + priv->icon_name = g_strdup (icon_name); + if (POLKIT_IS_UNIX_USER (identity)) + priv->identity = g_strdup (polkit_unix_user_get_name (POLKIT_UNIX_USER (identity))); + else + priv->identity = polkit_identity_to_string (identity); + + priv->simple = simple; + priv->cancellable = g_object_ref (cancellable); + priv->cancel_id = g_cancellable_connect (cancellable, + G_CALLBACK (on_cancelled), + listener, + NULL); + + polkit_agent_session_initiate (priv->active_session); +} + +static gboolean +initiate_authentication_finish (PolkitAgentListener *listener, + GAsyncResult *result, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); +} + + +static void +nm_polkit_listener_init (NMPolkitListener *agent) +{ +} + +static void +nm_polkit_listener_finalize (GObject *object) +{ + NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (object); + + if (priv->reg_handle) + polkit_agent_listener_unregister (priv->reg_handle); + + g_free (priv->action_id); + g_free (priv->message); + g_free (priv->icon_name); + g_free (priv->identity); + + G_OBJECT_CLASS (nm_polkit_listener_parent_class)->finalize (object); +} + +static void +nm_polkit_listener_class_init (NMPolkitListenerClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + PolkitAgentListenerClass *pkal_class = POLKIT_AGENT_LISTENER_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMPolkitListenerPrivate)); + + gobject_class->finalize = nm_polkit_listener_finalize; + + pkal_class->initiate_authentication = initiate_authentication; + pkal_class->initiate_authentication_finish = initiate_authentication_finish; +} + +/** + * nm_polkit_listener_new: + * @for_session: %TRUE for registering the polkit agent for the user session, + * %FALSE for registering it for the running process + * @error: location to store error, or %NULL + * + * Creates a new #NMPolkitListener and registers it as a polkit agent. + * + * Returns: a new #NMPolkitListener + */ +PolkitAgentListener * +nm_polkit_listener_new (gboolean for_session, GError **error) +{ + PolkitAgentListener *listener; + PolkitSubject* session; + NMPolkitListenerPrivate *priv; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + listener = g_object_new (NM_TYPE_POLKIT_LISTENER, NULL); + priv = NM_POLKIT_LISTENER_GET_PRIVATE (listener); + + if (for_session) + session = polkit_unix_session_new_for_process_sync (getpid (), NULL, NULL); + else + session = polkit_unix_process_new_for_owner (getpid (), 0, getuid ()); + + priv->reg_handle = polkit_agent_listener_register (listener, POLKIT_AGENT_REGISTER_FLAGS_NONE, + session, NULL, NULL, error); + if (!priv->reg_handle) { + g_object_unref (listener); + g_object_unref (session); + return NULL; + } + + return listener; +} + +/** + * nm_polkit_listener_set_request_callback: + * @self: a #NMPolkitListener object + * @request_callback: callback to install for polkit requests + * @request_callback_data: usaer data passed to request_callback when it is called + * + * Set a callback for "request" signal. The callback will be invoked when polkit + * requests an authorization. + */ +void +nm_polkit_listener_set_request_callback (NMPolkitListener *self, + NMPolkitListenerOnRequestFunc request_callback, + gpointer request_callback_data) +{ + NMPolkitListenerPrivate *priv; + + g_return_if_fail (NM_IS_POLKIT_LISTENER (self)); + + priv = NM_POLKIT_LISTENER_GET_PRIVATE (self); + + priv->on_request_callback = request_callback; + priv->request_callback_data = request_callback_data; +} + +/** + * nm_polkit_listener_set_show_info_callback: + * @self: a #NMPolkitListener object + * @show_info_callback: callback to install for polkit show info trigger + * + * Set a callback for "show-info" signal. The callback will be invoked when polkit + * has an info text to display. + */ +void +nm_polkit_listener_set_show_info_callback (NMPolkitListener *self, + NMPolkitListenerOnShowInfoFunc show_info_callback) +{ + g_return_if_fail (NM_IS_POLKIT_LISTENER (self)); + + NM_POLKIT_LISTENER_GET_PRIVATE (self)->on_show_info_callback = show_info_callback; +} + +/** + * nm_polkit_listener_set_show_error_callback: + * @self: a #NMPolkitListener object + * @show_error_callback: callback to install for polkit show error trigger + * + * Set a callback for "show-error" signal. The callback will be invoked when polkit + * has an error text to display. + */ +void +nm_polkit_listener_set_show_error_callback (NMPolkitListener *self, + NMPolkitListenerOnShowErrorFunc show_error_callback) +{ + g_return_if_fail (NM_IS_POLKIT_LISTENER (self)); + + NM_POLKIT_LISTENER_GET_PRIVATE (self)->on_show_error_callback = show_error_callback; +} + +/** + * nm_polkit_listener_set_completed_callback: + * @self: a #NMPolkitListener object + * @completed_callback: callback to install for polkit completing authorization + * + * Set a callback for "completed" signal. The callback will be invoked when polkit + * completed the request. + */ +void +nm_polkit_listener_set_completed_callback (NMPolkitListener *self, + NMPolkitListenerOnCompletedFunc completed_callback) +{ + g_return_if_fail (NM_IS_POLKIT_LISTENER (self)); + + NM_POLKIT_LISTENER_GET_PRIVATE (self)->on_completed_callback = completed_callback; +} diff --git a/clients/common/nm-polkit-listener.h b/clients/common/nm-polkit-listener.h new file mode 100644 index 00000000..3cd75019 --- /dev/null +++ b/clients/common/nm-polkit-listener.h @@ -0,0 +1,104 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * 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, see <http://www.gnu.org/licenses/>. + * + * Copyright 2014 Red Hat, Inc. + */ + +#ifndef __NM_POLKIT_LISTENER_H__ +#define __NM_POLKIT_LISTENER_H__ + +#include <glib.h> + +#define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE +#include <polkitagent/polkitagent.h> + +G_BEGIN_DECLS + +#define NM_TYPE_POLKIT_LISTENER (nm_polkit_listener_get_type ()) +#define NM_POLKIT_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_POLKIT_LISTENER, NMPolkitListener)) +#define NM_POLKIT_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_POLKIT_LISTENER, NMPolkitListenerClass)) +#define NM_IS_POLKIT_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_POLKIT_LISTENER)) +#define NM_IS_POLKIT_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_POLKIT_LISTENER)) +#define NM_POLKIT_LISTENER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_POLKIT_LISTENER, NMPolkitListenerClass)) + +/** + * NMPolkitListenerOnRequestFunc: + * @request: the request asked by polkit agent + * @action_id: the action_id of the polkit request + * @message: the message of the polkit request + * @icon_name: the icon name of the polkit request + * @user: user name + * @echo_on: whether the response to the request should be echoed to the screen + * @user_data: user data for the callback + * + * Called as a result of a request by polkit. The function should obtain response + * to the request from user, i.e. get the password required. + */ +typedef char * (*NMPolkitListenerOnRequestFunc) (const char *request, + const char *action_id, + const char *message, + const char *icon_name, + const char *user, + gboolean echo_on, + gpointer user_data); +/** + * NMPolkitListenerOnShowInfoFunc: + * @text: the info text from polkit + * + * Called as a result of show-info signal by polkit. + */ +typedef void (*NMPolkitListenerOnShowInfoFunc) (const char *text); +/** + * NMPolkitListenerOnShowErrorFunc: + * @text: the error text from polkit + * + * Called as a result of show-error signal by polkit. + */ +typedef void (*NMPolkitListenerOnShowErrorFunc) (const char *text); +/** + * NMPolkitListenerCompletedFunc: + * @gained_authorization: whether the autorization was successful + * + * Called as a result of completed signal by polkit. + */ +typedef void (*NMPolkitListenerOnCompletedFunc) (gboolean gained_authorization); + + +typedef struct { + PolkitAgentListener parent; + +} NMPolkitListener; + +typedef struct { + PolkitAgentListenerClass parent; + +} NMPolkitListenerClass; + +GType nm_polkit_listener_get_type (void); + +PolkitAgentListener* nm_polkit_listener_new (gboolean for_session, GError **error); +void nm_polkit_listener_set_request_callback (NMPolkitListener *self, + NMPolkitListenerOnRequestFunc request_callback, + gpointer request_callback_data); +void nm_polkit_listener_set_show_info_callback (NMPolkitListener *self, + NMPolkitListenerOnShowInfoFunc show_info_callback); +void nm_polkit_listener_set_show_error_callback (NMPolkitListener *self, + NMPolkitListenerOnShowErrorFunc show_error_callback); +void nm_polkit_listener_set_completed_callback (NMPolkitListener *self, + NMPolkitListenerOnCompletedFunc completed_callback); + +G_END_DECLS + +#endif /* __NM_POLKIT_LISTENER_H__ */ diff --git a/clients/common/nm-secret-agent-simple.c b/clients/common/nm-secret-agent-simple.c new file mode 100644 index 00000000..80022a4d --- /dev/null +++ b/clients/common/nm-secret-agent-simple.c @@ -0,0 +1,695 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * 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, see <http://www.gnu.org/licenses/>. + * + * Copyright 2011-2013 Red Hat, Inc. + * Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com> + */ + +/** + * SECTION:nm-secret-agent-simple + * @short_description: A simple secret agent for NetworkManager + * + * #NMSecretAgentSimple is the secret agent used by nmtui-connect and nmcli. + * + * This is a stripped-down version of gnome-shell's ShellNetworkAgent, + * with bits of the corresponding JavaScript code squished down into + * it. It is intended to eventually be generic enough that it could + * replace ShellNetworkAgent. + */ + +#include "config.h" + +#include <string.h> +#include <glib/gi18n-lib.h> + +#include "nm-secret-agent-simple.h" + +G_DEFINE_TYPE (NMSecretAgentSimple, nm_secret_agent_simple, NM_TYPE_SECRET_AGENT_OLD) + +#define NM_SECRET_AGENT_SIMPLE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimplePrivate)) + +enum { + REQUEST_SECRETS, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct { + NMSecretAgentSimple *self; + + gchar *request_id; + NMConnection *connection; + gchar **hints; + NMSecretAgentOldGetSecretsFunc callback; + gpointer callback_data; +} NMSecretAgentSimpleRequest; + +typedef struct { + /* <char *request_id, NMSecretAgentSimpleRequest *request> */ + GHashTable *requests; + + char *path; + gboolean enabled; +} NMSecretAgentSimplePrivate; + +static void +nm_secret_agent_simple_request_free (gpointer data) +{ + NMSecretAgentSimpleRequest *request = data; + + g_object_unref (request->self); + g_object_unref (request->connection); + g_strfreev (request->hints); + + g_slice_free (NMSecretAgentSimpleRequest, request); +} + +static void +nm_secret_agent_simple_init (NMSecretAgentSimple *agent) +{ + NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (agent); + + priv->requests = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, nm_secret_agent_simple_request_free); +} + +static void +nm_secret_agent_simple_finalize (GObject *object) +{ + NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (object); + GError *error; + GHashTableIter iter; + gpointer key; + gpointer value; + + error = g_error_new (NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_AGENT_CANCELED, + "The secret agent is going away"); + + g_hash_table_iter_init (&iter, priv->requests); + while (g_hash_table_iter_next (&iter, &key, &value)) { + NMSecretAgentSimpleRequest *request = value; + + request->callback (NM_SECRET_AGENT_OLD (object), + request->connection, + NULL, error, + request->callback_data); + } + + g_hash_table_destroy (priv->requests); + g_error_free (error); + + g_free (priv->path); + + G_OBJECT_CLASS (nm_secret_agent_simple_parent_class)->finalize (object); +} + +static gboolean +strv_has (gchar **haystack, + gchar *needle) +{ + gchar *iter; + + for (iter = *haystack; iter; iter++) { + if (g_strcmp0 (iter, needle) == 0) + return TRUE; + } + + return FALSE; +} + +/** + * NMSecretAgentSimpleSecret: + * @name: the user-visible name of the secret. Eg, "WEP Passphrase". + * @value: the value of the secret + * @password: %TRUE if this secret represents a password, %FALSE + * if it represents non-secret data. + * + * A single "secret" being requested. + */ + +typedef struct { + NMSecretAgentSimpleSecret base; + + NMSetting *setting; + char *property; +} NMSecretAgentSimpleSecretReal; + +static void +nm_secret_agent_simple_secret_free (NMSecretAgentSimpleSecret *secret) +{ + NMSecretAgentSimpleSecretReal *real = (NMSecretAgentSimpleSecretReal *)secret; + + g_free (secret->name); + g_free (secret->prop_name); + g_free (secret->value); + g_free (real->property); + g_clear_object (&real->setting); + + g_slice_free (NMSecretAgentSimpleSecretReal, real); +} + +static NMSecretAgentSimpleSecret * +nm_secret_agent_simple_secret_new (const char *name, + NMSetting *setting, + const char *property, + gboolean password) +{ + NMSecretAgentSimpleSecretReal *real; + + real = g_slice_new0 (NMSecretAgentSimpleSecretReal); + real->base.name = g_strdup (name); + real->base.prop_name = g_strdup_printf ("%s.%s", nm_setting_get_name (setting), property); + real->base.password = password; + + if (setting) { + real->setting = g_object_ref (setting); + real->property = g_strdup (property); + + g_object_get (setting, property, &real->base.value, NULL); + } + + return &real->base; +} + +static gboolean +add_8021x_secrets (NMSecretAgentSimpleRequest *request, + GPtrArray *secrets) +{ + NMSetting8021x *s_8021x = nm_connection_get_setting_802_1x (request->connection); + const char *eap_method; + NMSecretAgentSimpleSecret *secret; + + eap_method = nm_setting_802_1x_get_eap_method (s_8021x, 0); + if (!eap_method) + return FALSE; + + if ( !strcmp (eap_method, "md5") + || !strcmp (eap_method, "leap") + || !strcmp (eap_method, "ttls") + || !strcmp (eap_method, "peap")) { + /* TTLS and PEAP are actually much more complicated, but this complication + * is not visible here since we only care about phase2 authentication + * (and don't even care of which one) + */ + secret = nm_secret_agent_simple_secret_new (_("Username"), + NM_SETTING (s_8021x), + NM_SETTING_802_1X_IDENTITY, + FALSE); + g_ptr_array_add (secrets, secret); + secret = nm_secret_agent_simple_secret_new (_("Password"), + NM_SETTING (s_8021x), + NM_SETTING_802_1X_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; + } + + if (!strcmp (eap_method, "tls")) { + secret = nm_secret_agent_simple_secret_new (_("Identity"), + NM_SETTING (s_8021x), + NM_SETTING_802_1X_IDENTITY, + FALSE); + g_ptr_array_add (secrets, secret); + secret = nm_secret_agent_simple_secret_new (_("Private key password"), + NM_SETTING (s_8021x), + NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; + } + + return FALSE; +} + +static gboolean +add_wireless_secrets (NMSecretAgentSimpleRequest *request, + GPtrArray *secrets) +{ + NMSettingWirelessSecurity *s_wsec = nm_connection_get_setting_wireless_security (request->connection); + const char *key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + NMSecretAgentSimpleSecret *secret; + + if (!key_mgmt) + return FALSE; + + if (!strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk")) { + secret = nm_secret_agent_simple_secret_new (_("Password"), + NM_SETTING (s_wsec), + NM_SETTING_WIRELESS_SECURITY_PSK, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; + } + + if (!strcmp (key_mgmt, "none")) { + int index; + char *key; + + index = nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec); + key = g_strdup_printf ("wep-key%d", index); + secret = nm_secret_agent_simple_secret_new (_("Key"), + NM_SETTING (s_wsec), + key, + TRUE); + g_free (key); + + g_ptr_array_add (secrets, secret); + return TRUE; + } + + if (!strcmp (key_mgmt, "iee8021x")) { + if (!g_strcmp0 (nm_setting_wireless_security_get_auth_alg (s_wsec), "leap")) { + secret = nm_secret_agent_simple_secret_new (_("Password"), + NM_SETTING (s_wsec), + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; + } else + return add_8021x_secrets (request, secrets); + } + + if (!strcmp (key_mgmt, "wpa-eap")) + return add_8021x_secrets (request, secrets); + + return FALSE; +} + +static gboolean +add_pppoe_secrets (NMSecretAgentSimpleRequest *request, + GPtrArray *secrets) +{ + NMSettingPppoe *s_pppoe = nm_connection_get_setting_pppoe (request->connection); + NMSecretAgentSimpleSecret *secret; + + secret = nm_secret_agent_simple_secret_new (_("Username"), + NM_SETTING (s_pppoe), + NM_SETTING_PPPOE_USERNAME, + FALSE); + g_ptr_array_add (secrets, secret); + secret = nm_secret_agent_simple_secret_new (_("Service"), + NM_SETTING (s_pppoe), + NM_SETTING_PPPOE_SERVICE, + FALSE); + g_ptr_array_add (secrets, secret); + secret = nm_secret_agent_simple_secret_new (_("Password"), + NM_SETTING (s_pppoe), + NM_SETTING_PPPOE_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; +} + +static void +request_secrets_from_ui (NMSecretAgentSimpleRequest *request) +{ + GPtrArray *secrets; + NMSecretAgentSimpleSecret *secret; + const char *title; + char *msg; + gboolean ok = TRUE; + + secrets = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_secret_agent_simple_secret_free); + + if (nm_connection_is_type (request->connection, NM_SETTING_WIRELESS_SETTING_NAME)) { + NMSettingWireless *s_wireless; + GBytes *ssid; + char *ssid_utf8; + + s_wireless = nm_connection_get_setting_wireless (request->connection); + ssid = nm_setting_wireless_get_ssid (s_wireless); + ssid_utf8 = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), + g_bytes_get_size (ssid)); + + title = _("Authentication required by wireless network"); + msg = g_strdup_printf (_("Passwords or encryption keys are required to access the wireless network '%s'."), ssid_utf8); + + ok = add_wireless_secrets (request, secrets); + } else if (nm_connection_is_type (request->connection, NM_SETTING_WIRED_SETTING_NAME)) { + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection (request->connection); + + title = _("Wired 802.1X authentication"); + msg = NULL; + + secret = nm_secret_agent_simple_secret_new (_("Network name"), + NM_SETTING (s_con), + NM_SETTING_CONNECTION_ID, + FALSE); + g_ptr_array_add (secrets, secret); + ok = add_8021x_secrets (request, secrets); + } else if (nm_connection_is_type (request->connection, NM_SETTING_PPPOE_SETTING_NAME)) { + title = _("DSL authentication"); + msg = NULL; + + ok = add_pppoe_secrets (request, secrets); + } else if (nm_connection_is_type (request->connection, NM_SETTING_GSM_SETTING_NAME)) { + NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (request->connection); + + if (strv_has (request->hints, "pin")) { + title = _("PIN code required"); + msg = g_strdup (_("PIN code is needed for the mobile broadband device")); + + secret = nm_secret_agent_simple_secret_new (_("PIN"), + NM_SETTING (s_gsm), + NM_SETTING_GSM_PIN, + FALSE); + g_ptr_array_add (secrets, secret); + } else { + title = _("Mobile broadband network password"); + msg = g_strdup_printf (_("A password is required to connect to '%s'."), + nm_connection_get_id (request->connection)); + + secret = nm_secret_agent_simple_secret_new (_("Password"), + NM_SETTING (s_gsm), + NM_SETTING_GSM_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + } + } else if (nm_connection_is_type (request->connection, NM_SETTING_CDMA_SETTING_NAME)) { + NMSettingCdma *s_cdma = nm_connection_get_setting_cdma (request->connection); + + title = _("Mobile broadband network password"); + msg = g_strdup_printf (_("A password is required to connect to '%s'."), + nm_connection_get_id (request->connection)); + + secret = nm_secret_agent_simple_secret_new (_("Password"), + NM_SETTING (s_cdma), + NM_SETTING_CDMA_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + } else if (nm_connection_is_type (request->connection, NM_SETTING_BLUETOOTH_SETTING_NAME)) { + NMSetting *setting; + + setting = nm_connection_get_setting_by_name (request->connection, NM_SETTING_GSM_SETTING_NAME); + if (!setting) + setting = nm_connection_get_setting_by_name (request->connection, NM_SETTING_CDMA_SETTING_NAME); + + title = _("Mobile broadband network password"); + msg = g_strdup_printf (_("A password is required to connect to '%s'."), + nm_connection_get_id (request->connection)); + + secret = nm_secret_agent_simple_secret_new (_("Password"), + setting, + "password", + TRUE); + g_ptr_array_add (secrets, secret); + } else + ok = FALSE; + + if (!ok) { + g_ptr_array_unref (secrets); + return; + } + + g_signal_emit (request->self, signals[REQUEST_SECRETS], 0, + request->request_id, title, msg, secrets); +} + +static void +nm_secret_agent_simple_get_secrets (NMSecretAgentOld *agent, + NMConnection *connection, + const gchar *connection_path, + const gchar *setting_name, + const gchar **hints, + NMSecretAgentGetSecretsFlags flags, + NMSecretAgentOldGetSecretsFunc callback, + gpointer callback_data) +{ + NMSecretAgentSimple *self = NM_SECRET_AGENT_SIMPLE (agent); + NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self); + NMSecretAgentSimpleRequest *request; + NMSettingConnection *s_con; + const char *connection_type; + char *request_id; + GError *error; + + request_id = g_strdup_printf ("%s/%s", connection_path, setting_name); + if (g_hash_table_lookup (priv->requests, request_id) != NULL) { + /* We already have a request pending for this (connection, setting) */ + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED, + "Request for %s secrets already pending", request_id); + nope: + callback (agent, connection, NULL, error, callback_data); + g_error_free (error); + g_free (request_id); + return; + } + + s_con = nm_connection_get_setting_connection (connection); + connection_type = nm_setting_connection_get_connection_type (s_con); + + if (!strcmp (connection_type, NM_SETTING_VPN_SETTING_NAME)) { + /* We don't support VPN secrets yet */ + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS, + "VPN secrets not supported"); + goto nope; + } + + if (!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION)) { + /* We don't do stored passwords */ + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS, + "Stored passwords not supported"); + goto nope; + } + + request = g_slice_new (NMSecretAgentSimpleRequest); + request->self = g_object_ref (self); + request->connection = g_object_ref (connection); + request->hints = g_strdupv ((gchar **)hints); + request->callback = callback; + request->callback_data = callback_data; + request->request_id = request_id; + g_hash_table_replace (priv->requests, request->request_id, request); + + if (priv->enabled) + request_secrets_from_ui (request); +} + +/** + * nm_secret_agent_simple_response: + * @self: the #NMSecretAgentSimple + * @request_id: the request ID being responded to + * @secrets: (allow-none): the array of secrets, or %NULL + * + * Response to a #NMSecretAgentSimple::get-secrets signal. + * + * If the user provided secrets, the caller should set the + * corresponding <literal>value</literal> fields in the + * #NMSecretAgentSimpleSecrets (freeing any initial values they had), and + * pass the array to nm_secret_agent_simple_response(). If the user + * cancelled the request, @secrets should be NULL. + */ +void +nm_secret_agent_simple_response (NMSecretAgentSimple *self, + const char *request_id, + GPtrArray *secrets) +{ + NMSecretAgentSimplePrivate *priv; + NMSecretAgentSimpleRequest *request; + GVariant *dict = NULL; + GError *error = NULL; + int i; + + g_return_if_fail (NM_IS_SECRET_AGENT_SIMPLE (self)); + + priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self); + request = g_hash_table_lookup (priv->requests, request_id); + g_return_if_fail (request != NULL); + + if (secrets) { + GVariantBuilder conn_builder, *setting_builder; + GHashTable *settings; + GHashTableIter iter; + const char *name; + + settings = g_hash_table_new (g_str_hash, g_str_equal); + for (i = 0; i < secrets->len; i++) { + NMSecretAgentSimpleSecretReal *secret = secrets->pdata[i]; + + setting_builder = g_hash_table_lookup (settings, nm_setting_get_name (secret->setting)); + if (!setting_builder) { + setting_builder = g_variant_builder_new (NM_VARIANT_TYPE_SETTING); + g_hash_table_insert (settings, (char *) nm_setting_get_name (secret->setting), + setting_builder); + } + + g_variant_builder_add (setting_builder, "{sv}", + secret->property, + g_variant_new_string (secret->base.value)); + } + + g_variant_builder_init (&conn_builder, NM_VARIANT_TYPE_CONNECTION); + g_hash_table_iter_init (&iter, settings); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &setting_builder)) + g_variant_builder_add (&conn_builder, "{sa{sv}}", name, setting_builder); + dict = g_variant_builder_end (&conn_builder); + g_hash_table_destroy (settings); + } else { + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_USER_CANCELED, + "User cancelled"); + } + + request->callback (NM_SECRET_AGENT_OLD (self), request->connection, dict, error, request->callback_data); + + g_clear_error (&error); + g_hash_table_remove (priv->requests, request_id); +} + +static void +nm_secret_agent_simple_cancel_get_secrets (NMSecretAgentOld *agent, + const gchar *connection_path, + const gchar *setting_name) +{ + /* We don't support cancellation. Sorry! */ +} + +static void +nm_secret_agent_simple_save_secrets (NMSecretAgentOld *agent, + NMConnection *connection, + const gchar *connection_path, + NMSecretAgentOldSaveSecretsFunc callback, + gpointer callback_data) +{ + /* We don't support secret storage */ + callback (agent, connection, NULL, callback_data); +} + +static void +nm_secret_agent_simple_delete_secrets (NMSecretAgentOld *agent, + NMConnection *connection, + const gchar *connection_path, + NMSecretAgentOldDeleteSecretsFunc callback, + gpointer callback_data) +{ + /* We don't support secret storage, so there's nothing to delete. */ + callback (agent, connection, NULL, callback_data); +} + +/** + * nm_secret_agent_simple_enable: + * @self: the #NMSecretAgentSimple + * @path: (allow-none): the path of the connection (if any) to handle secrets + * for. If %NULL, secrets for any connection will be handled. + * + * Enables servicing the requests including the already queued ones. If @path + * is given, the agent will only handle requests for connections that match + * @path. + */ +void +nm_secret_agent_simple_enable (NMSecretAgentSimple *self, const char *path) +{ + NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self); + GList *requests, *iter; + GError *error; + + if (g_strcmp0 (path, priv->path) != 0) { + g_free (priv->path); + priv->path = g_strdup (path); + } + + if (priv->enabled) + return; + priv->enabled = TRUE; + + /* Service pending secret requests. */ + requests = g_hash_table_get_values (priv->requests); + for (iter = requests; iter; iter = g_list_next (iter)) { + NMSecretAgentSimpleRequest *request = iter->data; + + if (g_str_has_prefix (request->request_id, priv->path)) { + request_secrets_from_ui (request); + } else { + /* We only handle requests for connection with @path if set. */ + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED, + "Request for %s secrets doesn't match path %s", + request->request_id, priv->path); + request->callback (NM_SECRET_AGENT_OLD (self), request->connection, NULL, error, request->callback_data); + g_hash_table_remove (priv->requests, request->request_id); + g_error_free (error); + } + } + g_list_free (requests); +} + +void +nm_secret_agent_simple_class_init (NMSecretAgentSimpleClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + NMSecretAgentOldClass *agent_class = NM_SECRET_AGENT_OLD_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMSecretAgentSimplePrivate)); + + gobject_class->finalize = nm_secret_agent_simple_finalize; + + agent_class->get_secrets = nm_secret_agent_simple_get_secrets; + agent_class->cancel_get_secrets = nm_secret_agent_simple_cancel_get_secrets; + agent_class->save_secrets = nm_secret_agent_simple_save_secrets; + agent_class->delete_secrets = nm_secret_agent_simple_delete_secrets; + + /** + * NMSecretAgentSimple::request-secrets: + * @agent: the #NMSecretAgentSimple + * @request_id: request ID, to eventually pass to + * nm_secret_agent_simple_response(). + * @title: a title for the password dialog + * @prompt: a prompt message for the password dialog + * @secrets: (element-type #NMSecretAgentSimpleSecret): array of secrets + * being requested. + * + * Emitted when the agent requires secrets from the user. + * + * The application should ask user for the secrets. For example, + * nmtui should create a password dialog (#NmtPasswordDialog) + * with the given title and prompt, and an entry for each + * element of @secrets. If any of the secrets already have a + * <literal>value</literal> filled in, the corresponding entry + * should be initialized to that value. + * + * When the dialog is complete, the app must call + * nm_secret_agent_simple_response() with the results. + */ + signals[REQUEST_SECRETS] = g_signal_new ("request-secrets", + G_TYPE_FROM_CLASS (klass), + 0, 0, NULL, NULL, NULL, + G_TYPE_NONE, + 4, + G_TYPE_STRING, /* request_id */ + G_TYPE_STRING, /* title */ + G_TYPE_STRING, /* prompt */ + G_TYPE_PTR_ARRAY); +} + +/** + * nm_secret_agent_simple_new: + * @name: the identifier of secret agent + * + * Creates a new #NMSecretAgentSimple. It does not serve any requests until + * nm_secret_agent_simple_enable() is called. + * + * Returns: a new #NMSecretAgentSimple if the agent creation is successful + * or %NULL in case of a failure. + */ +NMSecretAgentOld * +nm_secret_agent_simple_new (const char *name) +{ + return g_initable_new (NM_TYPE_SECRET_AGENT_SIMPLE, NULL, NULL, + NM_SECRET_AGENT_OLD_IDENTIFIER, name, + NULL); +} diff --git a/clients/common/nm-secret-agent-simple.h b/clients/common/nm-secret-agent-simple.h new file mode 100644 index 00000000..81fec651 --- /dev/null +++ b/clients/common/nm-secret-agent-simple.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * 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, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 - 2014 Red Hat, Inc. + */ + +#ifndef __NM_SECRET_AGENT_SIMPLE_H__ +#define __NM_SECRET_AGENT_SIMPLE_H__ + +#include <NetworkManager.h> +#include <nm-secret-agent-old.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SECRET_AGENT_SIMPLE (nm_secret_agent_simple_get_type ()) +#define NM_SECRET_AGENT_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimple)) +#define NM_SECRET_AGENT_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimpleClass)) +#define NM_IS_SECRET_AGENT_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SECRET_AGENT_SIMPLE)) +#define NM_IS_SECRET_AGENT_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SECRET_AGENT_SIMPLE)) +#define NM_SECRET_AGENT_SIMPLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimpleClass)) + +typedef struct { + NMSecretAgentOld parent; + +} NMSecretAgentSimple; + +typedef struct { + NMSecretAgentOldClass parent; + +} NMSecretAgentSimpleClass; + +typedef struct { + char *name, *prop_name, *value; + gboolean password; +} NMSecretAgentSimpleSecret; + +GType nm_secret_agent_simple_get_type (void); + +NMSecretAgentOld *nm_secret_agent_simple_new (const char *name); + +void nm_secret_agent_simple_response (NMSecretAgentSimple *self, + const char *request_id, + GPtrArray *secrets); + +void nm_secret_agent_simple_enable (NMSecretAgentSimple *self, + const char *path); + +G_END_DECLS + +#endif /* __NM_SECRET_AGENT_SIMPLE_H__ */ |