diff options
Diffstat (limited to 'clients/common/nm-secret-agent-simple.c')
-rw-r--r-- | clients/common/nm-secret-agent-simple.c | 1402 |
1 files changed, 0 insertions, 1402 deletions
diff --git a/clients/common/nm-secret-agent-simple.c b/clients/common/nm-secret-agent-simple.c deleted file mode 100644 index 69617d0fee..0000000000 --- a/clients/common/nm-secret-agent-simple.c +++ /dev/null @@ -1,1402 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2011 - 2015 Red Hat, Inc. - * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.h" - -#include "nm-secret-agent-simple.h" - -#include <gio/gunixoutputstream.h> -#include <gio/gunixinputstream.h> - -#include "nm-vpn-service-plugin.h" -#include "nm-vpn-helpers.h" -#include "libnm-glib-aux/nm-secret-utils.h" - -/*****************************************************************************/ - -typedef struct { - char *request_id; - - NMSecretAgentSimple *self; - - NMConnection * connection; - const char * setting_name; - char ** hints; - NMSecretAgentOldGetSecretsFunc callback; - gpointer callback_data; - GCancellable * cancellable; - NMSecretAgentGetSecretsFlags flags; -} RequestData; - -enum { - REQUEST_SECRETS, - - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = {0}; - -typedef struct { - GHashTable *requests; - - char * path; - gboolean enabled; -} NMSecretAgentSimplePrivate; - -struct _NMSecretAgentSimple { - NMSecretAgentOld parent; - NMSecretAgentSimplePrivate _priv; -}; - -struct _NMSecretAgentSimpleClass { - NMSecretAgentOldClass parent; -}; - -G_DEFINE_TYPE(NMSecretAgentSimple, nm_secret_agent_simple, NM_TYPE_SECRET_AGENT_OLD) - -#define NM_SECRET_AGENT_SIMPLE_GET_PRIVATE(self) \ - _NM_GET_PRIVATE(self, NMSecretAgentSimple, NM_IS_SECRET_AGENT_SIMPLE, NMSecretAgentOld) - -/*****************************************************************************/ - -static void -_request_data_free(gpointer data) -{ - RequestData *request = data; - - g_free(request->request_id); - nm_clear_g_cancellable(&request->cancellable); - g_object_unref(request->connection); - g_strfreev(request->hints); - - g_slice_free(RequestData, request); -} - -static void -_request_data_complete(RequestData * request, - GVariant * secrets, - GError * error, - GHashTableIter *iter_to_remove) -{ - NMSecretAgentSimple * self = request->self; - NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE(self); - - nm_assert((secrets != NULL) != (error != NULL)); - - request->callback(NM_SECRET_AGENT_OLD(request->self), - request->connection, - secrets, - error, - request->callback_data); - - if (iter_to_remove) - g_hash_table_iter_remove(iter_to_remove); - else - g_hash_table_remove(priv->requests, request); -} - -/*****************************************************************************/ - -/** - * 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; -} SecretReal; - -static void -_secret_real_free(NMSecretAgentSimpleSecret *secret) -{ - SecretReal *real = (SecretReal *) secret; - - g_free((char *) secret->pretty_name); - g_free((char *) secret->entry_id); - nm_free_secret(secret->value); - g_free((char *) secret->vpn_type); - g_free(real->property); - g_clear_object(&real->setting); - - g_slice_free(SecretReal, real); -} - -static NMSecretAgentSimpleSecret * -_secret_real_new_plain(NMSecretAgentSecretType secret_type, - const char * pretty_name, - NMSetting * setting, - const char * property) -{ - SecretReal * real; - gs_free char *value = NULL; - - nm_assert(property); - nm_assert(NM_IS_SETTING(setting)); - nm_assert(NM_IN_SET(secret_type, - NM_SECRET_AGENT_SECRET_TYPE_PROPERTY, - NM_SECRET_AGENT_SECRET_TYPE_SECRET)); - nm_assert(g_object_class_find_property(G_OBJECT_GET_CLASS(setting), property)); - nm_assert((secret_type == NM_SECRET_AGENT_SECRET_TYPE_SECRET) - == nm_setting_get_secret_flags(setting, property, NULL, NULL)); - - g_object_get(setting, property, &value, NULL); - - real = g_slice_new(SecretReal); - *real = (SecretReal){ - .base.secret_type = secret_type, - .base.pretty_name = g_strdup(pretty_name), - .base.entry_id = g_strdup_printf("%s.%s", nm_setting_get_name(setting), property), - .base.value = g_steal_pointer(&value), - .base.is_secret = (secret_type != NM_SECRET_AGENT_SECRET_TYPE_PROPERTY), - .setting = g_object_ref(setting), - .property = g_strdup(property), - }; - return &real->base; -} - -static NMSecretAgentSimpleSecret * -_secret_real_new_vpn_secret(const char *pretty_name, - NMSetting * setting, - const char *property, - const char *vpn_type) -{ - SecretReal *real; - const char *value; - - nm_assert(property); - nm_assert(NM_IS_SETTING_VPN(setting)); - nm_assert(vpn_type); - - value = nm_setting_vpn_get_secret(NM_SETTING_VPN(setting), property); - - real = g_slice_new(SecretReal); - *real = (SecretReal){ - .base.secret_type = NM_SECRET_AGENT_SECRET_TYPE_VPN_SECRET, - .base.pretty_name = g_strdup(pretty_name), - .base.entry_id = - g_strdup_printf("%s%s", NM_SECRET_AGENT_ENTRY_ID_PREFX_VPN_SECRETS, property), - .base.value = g_strdup(value), - .base.is_secret = TRUE, - .base.vpn_type = g_strdup(vpn_type), - .setting = g_object_ref(setting), - .property = g_strdup(property), - }; - return &real->base; -} - -static NMSecretAgentSimpleSecret * -_secret_real_new_wireguard_peer_psk(NMSettingWireGuard *s_wg, - const char * public_key, - const char * preshared_key) -{ - SecretReal *real; - - nm_assert(NM_IS_SETTING_WIREGUARD(s_wg)); - nm_assert(public_key); - - real = g_slice_new(SecretReal); - *real = (SecretReal){ - .base.secret_type = NM_SECRET_AGENT_SECRET_TYPE_WIREGUARD_PEER_PSK, - .base.pretty_name = g_strdup_printf(_("Preshared-key for %s"), public_key), - .base.entry_id = g_strdup_printf(NM_SETTING_WIREGUARD_SETTING_NAME - "." NM_SETTING_WIREGUARD_PEERS - ".%s." NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, - public_key), - .base.value = g_strdup(preshared_key), - .base.is_secret = TRUE, - .base.no_prompt_entry_id = TRUE, - .setting = NM_SETTING(g_object_ref(s_wg)), - .property = g_strdup(public_key), - }; - return &real->base; -} - -/*****************************************************************************/ - -static gboolean -add_8021x_secrets(RequestData *request, GPtrArray *secrets) -{ - NMSetting8021x * s_8021x = nm_connection_get_setting_802_1x(request->connection); - const char * eap_method; - NMSecretAgentSimpleSecret *secret; - - /* If hints are given, then always ask for what the hints require */ - if (request->hints && request->hints[0]) { - char **iter; - - for (iter = request->hints; *iter; iter++) { - secret = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_SECRET, - _(*iter), - NM_SETTING(s_8021x), - *iter); - g_ptr_array_add(secrets, secret); - } - - return TRUE; - } - - eap_method = nm_setting_802_1x_get_eap_method(s_8021x, 0); - if (!eap_method) - return FALSE; - - if (NM_IN_STRSET(eap_method, "md5", "leap", "ttls", "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 = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_PROPERTY, - _("Username"), - NM_SETTING(s_8021x), - NM_SETTING_802_1X_IDENTITY); - g_ptr_array_add(secrets, secret); - secret = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_SECRET, - _("Password"), - NM_SETTING(s_8021x), - NM_SETTING_802_1X_PASSWORD); - g_ptr_array_add(secrets, secret); - return TRUE; - } - - if (nm_streq(eap_method, "tls")) { - secret = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_PROPERTY, - _("Identity"), - NM_SETTING(s_8021x), - NM_SETTING_802_1X_IDENTITY); - g_ptr_array_add(secrets, secret); - secret = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_SECRET, - _("Private key password"), - NM_SETTING(s_8021x), - NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); - g_ptr_array_add(secrets, secret); - return TRUE; - } - - return FALSE; -} - -static gboolean -add_wireless_secrets(RequestData *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 || nm_streq(key_mgmt, "owe")) - return FALSE; - - if (NM_IN_STRSET(key_mgmt, "wpa-psk", "sae")) { - secret = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_SECRET, - _("Password"), - NM_SETTING(s_wsec), - NM_SETTING_WIRELESS_SECURITY_PSK); - g_ptr_array_add(secrets, secret); - return TRUE; - } - - if (nm_streq(key_mgmt, "none")) { - guint32 index; - char key[100]; - - index = nm_setting_wireless_security_get_wep_tx_keyidx(s_wsec); - secret = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_SECRET, - _("Key"), - NM_SETTING(s_wsec), - nm_sprintf_buf(key, "wep-key%u", (guint) index)); - g_ptr_array_add(secrets, secret); - return TRUE; - } - - if (nm_streq(key_mgmt, "iee8021x")) { - if (nm_streq0(nm_setting_wireless_security_get_auth_alg(s_wsec), "leap")) { - secret = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_SECRET, - _("Password"), - NM_SETTING(s_wsec), - NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD); - g_ptr_array_add(secrets, secret); - return TRUE; - } else - return add_8021x_secrets(request, secrets); - } - - if (nm_streq(key_mgmt, "wpa-eap") || nm_streq(key_mgmt, "wpa-eap-suite-b-192")) - return add_8021x_secrets(request, secrets); - - return FALSE; -} - -static gboolean -add_pppoe_secrets(RequestData *request, GPtrArray *secrets) -{ - NMSettingPppoe * s_pppoe = nm_connection_get_setting_pppoe(request->connection); - NMSecretAgentSimpleSecret *secret; - - secret = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_PROPERTY, - _("Username"), - NM_SETTING(s_pppoe), - NM_SETTING_PPPOE_USERNAME); - g_ptr_array_add(secrets, secret); - secret = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_PROPERTY, - _("Service"), - NM_SETTING(s_pppoe), - NM_SETTING_PPPOE_SERVICE); - g_ptr_array_add(secrets, secret); - secret = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_SECRET, - _("Password"), - NM_SETTING(s_pppoe), - NM_SETTING_PPPOE_PASSWORD); - g_ptr_array_add(secrets, secret); - return TRUE; -} - -static NMSettingSecretFlags -get_vpn_secret_flags(NMSettingVpn *s_vpn, const char *secret_name) -{ - NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; - GHashTable * vpn_data; - - g_object_get(s_vpn, NM_SETTING_VPN_DATA, &vpn_data, NULL); - nm_vpn_service_plugin_get_secret_flags(vpn_data, secret_name, &flags); - g_hash_table_unref(vpn_data); - - return flags; -} - -static void -add_vpn_secret_helper(GPtrArray * secrets, - NMSettingVpn *s_vpn, - const char * name, - const char * ui_name) -{ - NMSecretAgentSimpleSecret *secret; - NMSettingSecretFlags flags; - int i; - - flags = get_vpn_secret_flags(s_vpn, name); - if (flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED || flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) { - secret = _secret_real_new_vpn_secret(ui_name, - NM_SETTING(s_vpn), - name, - nm_setting_vpn_get_service_type(s_vpn)); - - /* Check for duplicates */ - for (i = 0; i < secrets->len; i++) { - NMSecretAgentSimpleSecret *s = secrets->pdata[i]; - - if (s->secret_type == secret->secret_type && nm_streq0(s->vpn_type, secret->vpn_type) - && nm_streq0(s->entry_id, secret->entry_id)) { - _secret_real_free(secret); - return; - } - } - - g_ptr_array_add(secrets, secret); - } -} - -#define VPN_MSG_TAG "x-vpn-message:" - -static gboolean -add_vpn_secrets(RequestData *request, GPtrArray *secrets, char **msg) -{ - NMSettingVpn * s_vpn = nm_connection_get_setting_vpn(request->connection); - const NmcVpnPasswordName *p; - const char * vpn_msg = NULL; - char ** iter; - - /* If hints are given, then always ask for what the hints require */ - if (request->hints) { - for (iter = request->hints; *iter; iter++) { - if (!vpn_msg && g_str_has_prefix(*iter, VPN_MSG_TAG)) - vpn_msg = &(*iter)[NM_STRLEN(VPN_MSG_TAG)]; - else - add_vpn_secret_helper(secrets, s_vpn, *iter, *iter); - } - } - - NM_SET_OUT(msg, g_strdup(vpn_msg)); - - /* Now add what client thinks might be required, because hints may be empty or incomplete */ - p = nm_vpn_get_secret_names(nm_setting_vpn_get_service_type(s_vpn)); - while (p && p->name) { - add_vpn_secret_helper(secrets, s_vpn, p->name, _(p->ui_name)); - p++; - } - - return TRUE; -} - -static gboolean -add_wireguard_secrets(RequestData *request, GPtrArray *secrets, char **msg, GError **error) -{ - NMSettingWireGuard * s_wg; - NMSecretAgentSimpleSecret *secret; - guint i; - - s_wg = NM_SETTING_WIREGUARD( - nm_connection_get_setting(request->connection, NM_TYPE_SETTING_WIREGUARD)); - if (!s_wg) { - g_set_error(error, - NM_SECRET_AGENT_ERROR, - NM_SECRET_AGENT_ERROR_FAILED, - "Cannot service a WireGuard secrets request %s for a connection without " - "WireGuard settings", - request->request_id); - return FALSE; - } - - if (!request->hints || !request->hints[0] - || g_strv_contains(NM_CAST_STRV_CC(request->hints), NM_SETTING_WIREGUARD_PRIVATE_KEY)) { - secret = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_SECRET, - _("WireGuard private-key"), - NM_SETTING(s_wg), - NM_SETTING_WIREGUARD_PRIVATE_KEY); - g_ptr_array_add(secrets, secret); - } - - if (request->hints) { - for (i = 0; request->hints[i]; i++) { - NMWireGuardPeer *peer; - const char * name = request->hints[i]; - gs_free char * public_key = NULL; - - if (nm_streq(name, NM_SETTING_WIREGUARD_PRIVATE_KEY)) - continue; - - if (NM_STR_HAS_PREFIX(name, NM_SETTING_WIREGUARD_PEERS ".")) { - const char *tmp; - - tmp = &name[NM_STRLEN(NM_SETTING_WIREGUARD_PEERS ".")]; - if (NM_STR_HAS_SUFFIX(tmp, "." NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY)) { - public_key = g_strndup( - tmp, - strlen(tmp) - NM_STRLEN("." NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY)); - } - } - - if (!public_key) - continue; - - peer = nm_setting_wireguard_get_peer_by_public_key(s_wg, public_key, NULL); - - g_ptr_array_add(secrets, - _secret_real_new_wireguard_peer_psk( - s_wg, - (peer ? nm_wireguard_peer_get_public_key(peer) : public_key), - (peer ? nm_wireguard_peer_get_preshared_key(peer) : NULL))); - } - } - - *msg = g_strdup_printf(_("Secrets are required to connect WireGuard VPN '%s'"), - nm_connection_get_id(request->connection)); - return TRUE; -} - -typedef struct { - GPid auth_dialog_pid; - GString * auth_dialog_response; - RequestData * request; - GPtrArray * secrets; - GCancellable * cancellable; - gulong cancellable_id; - guint child_watch_id; - GInputStream * input_stream; - GOutputStream *output_stream; - char read_buf[5]; -} AuthDialogData; - -static void -_auth_dialog_data_free(AuthDialogData *data) -{ - nm_clear_g_signal_handler(data->cancellable, &data->cancellable_id); - g_clear_object(&data->cancellable); - nm_clear_g_source(&data->child_watch_id); - g_ptr_array_unref(data->secrets); - g_spawn_close_pid(data->auth_dialog_pid); - g_string_free(data->auth_dialog_response, TRUE); - g_object_unref(data->input_stream); - g_object_unref(data->output_stream); - g_slice_free(AuthDialogData, data); -} - -static void -_auth_dialog_exited(GPid pid, int status, gpointer user_data) -{ - AuthDialogData * data = user_data; - RequestData * request = data->request; - GPtrArray * secrets = data->secrets; - NMSettingVpn * s_vpn = nm_connection_get_setting_vpn(request->connection); - nm_auto_unref_keyfile GKeyFile *keyfile = NULL; - gs_strfreev char ** groups = NULL; - gs_free char * title = NULL; - gs_free char * message = NULL; - int i; - gs_free_error GError *error = NULL; - - data->child_watch_id = 0; - - nm_clear_g_cancellable_disconnect(data->cancellable, &data->cancellable_id); - - if (status != 0) { - g_set_error(&error, - NM_SECRET_AGENT_ERROR, - NM_SECRET_AGENT_ERROR_FAILED, - "Auth dialog failed with error code %d\n", - status); - goto out; - } - - keyfile = g_key_file_new(); - if (!g_key_file_load_from_data(keyfile, - data->auth_dialog_response->str, - data->auth_dialog_response->len, - G_KEY_FILE_NONE, - &error)) { - goto out; - } - - groups = g_key_file_get_groups(keyfile, NULL); - if (!nm_streq0(groups[0], "VPN Plugin UI")) { - g_set_error(&error, - NM_SECRET_AGENT_ERROR, - NM_SECRET_AGENT_ERROR_FAILED, - "Expected [VPN Plugin UI] in auth dialog response"); - goto out; - } - - title = g_key_file_get_string(keyfile, "VPN Plugin UI", "Title", &error); - if (!title) - goto out; - - message = g_key_file_get_string(keyfile, "VPN Plugin UI", "Description", &error); - if (!message) - goto out; - - for (i = 1; groups[i]; i++) { - gs_free char *pretty_name = NULL; - - if (!g_key_file_get_boolean(keyfile, groups[i], "IsSecret", NULL)) - continue; - if (!g_key_file_get_boolean(keyfile, groups[i], "ShouldAsk", NULL)) - continue; - - pretty_name = g_key_file_get_string(keyfile, groups[i], "Label", NULL); - g_ptr_array_add(secrets, - _secret_real_new_vpn_secret(pretty_name, - NM_SETTING(s_vpn), - groups[i], - nm_setting_vpn_get_service_type(s_vpn))); - } - -out: - /* Try to fall back to the hardwired VPN support if the auth dialog fails. - * We may eventually get rid of the whole hardwired secrets handling at some point, - * when the auth helpers are goode enough.. */ - if (error && add_vpn_secrets(request, secrets, &message)) { - g_clear_error(&error); - if (!message) { - message = g_strdup_printf(_("A password is required to connect to '%s'."), - nm_connection_get_id(request->connection)); - } - } - - if (error) - _request_data_complete(request, NULL, error, NULL); - else { - g_signal_emit(request->self, - signals[REQUEST_SECRETS], - 0, - request->request_id, - title, - message, - secrets); - } - - _auth_dialog_data_free(data); -} - -static void -_request_cancelled(GObject *object, gpointer user_data) -{ - _auth_dialog_data_free(user_data); -} - -static void -_auth_dialog_read_done(GObject *source_object, GAsyncResult *res, gpointer user_data) -{ - GInputStream * auth_dialog_out = G_INPUT_STREAM(source_object); - AuthDialogData *data = user_data; - gssize read_size; - gs_free_error GError *error = NULL; - - read_size = g_input_stream_read_finish(auth_dialog_out, res, &error); - switch (read_size) { - case -1: - if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - _request_data_complete(data->request, NULL, error, NULL); - _auth_dialog_data_free(data); - break; - case 0: - /* Done reading. Let's wait for the auth dialog to exit so that we're able to collect the status. - * Remember we can be cancelled in between. */ - data->child_watch_id = g_child_watch_add(data->auth_dialog_pid, _auth_dialog_exited, data); - data->cancellable = g_object_ref(data->request->cancellable); - data->cancellable_id = - g_cancellable_connect(data->cancellable, G_CALLBACK(_request_cancelled), data, NULL); - break; - default: - g_string_append_len(data->auth_dialog_response, data->read_buf, read_size); - g_input_stream_read_async(auth_dialog_out, - data->read_buf, - sizeof(data->read_buf), - G_PRIORITY_DEFAULT, - NULL, - _auth_dialog_read_done, - data); - return; - } - - g_input_stream_close(auth_dialog_out, NULL, NULL); -} - -static void -_auth_dialog_write_done(GObject *source_object, GAsyncResult *res, gpointer user_data) -{ - GOutputStream *auth_dialog_out = G_OUTPUT_STREAM(source_object); - _nm_unused gs_free char *auth_dialog_request_free = user_data; - - /* We don't care about write errors. If there are any problems, the - * reader shall notice. */ - g_output_stream_write_finish(auth_dialog_out, res, NULL); - g_output_stream_close(auth_dialog_out, NULL, NULL); -} - -static void -_add_to_string(GString *string, const char *key, const char *value) -{ - gs_strfreev char **lines = NULL; - int i; - - lines = g_strsplit(value, "\n", -1); - - g_string_append(string, key); - for (i = 0; lines[i]; i++) { - g_string_append_c(string, '='); - g_string_append(string, lines[i]); - g_string_append_c(string, '\n'); - } -} - -static void -_add_data_item_to_string(const char *key, const char *value, gpointer user_data) -{ - GString *string = user_data; - - _add_to_string(string, "DATA_KEY", key); - _add_to_string(string, "DATA_VAL", value); - g_string_append_c(string, '\n'); -} - -static void -_add_secret_to_string(const char *key, const char *value, gpointer user_data) -{ - GString *string = user_data; - - _add_to_string(string, "SECRET_KEY", key); - _add_to_string(string, "SECRET_VAL", value); - g_string_append_c(string, '\n'); -} - -static gboolean -try_spawn_vpn_auth_helper(RequestData *request, GPtrArray *secrets) -{ - NMSettingVpn * s_vpn = nm_connection_get_setting_vpn(request->connection); - gs_unref_ptrarray GPtrArray *auth_dialog_argv = NULL; - NMVpnPluginInfo * plugin_info; - const char * s; - GPid auth_dialog_pid; - int auth_dialog_in_fd; - int auth_dialog_out_fd; - GOutputStream * auth_dialog_in; - GInputStream * auth_dialog_out; - GError * error = NULL; - GString * auth_dialog_request; - char * auth_dialog_request_str; - gsize auth_dialog_request_len; - AuthDialogData * data; - int i; - - plugin_info = nm_vpn_plugin_info_list_find_by_service(nm_vpn_get_plugin_infos(), - nm_setting_vpn_get_service_type(s_vpn)); - if (!plugin_info) - return FALSE; - - s = nm_vpn_plugin_info_lookup_property(plugin_info, "GNOME", "supports-external-ui-mode"); - if (!_nm_utils_ascii_str_to_bool(s, FALSE)) - return FALSE; - - auth_dialog_argv = g_ptr_array_new(); - - s = nm_vpn_plugin_info_lookup_property(plugin_info, "GNOME", "auth-dialog"); - g_return_val_if_fail(s, FALSE); - g_ptr_array_add(auth_dialog_argv, (gpointer) s); - - g_ptr_array_add(auth_dialog_argv, "-u"); - g_ptr_array_add(auth_dialog_argv, (gpointer) nm_connection_get_uuid(request->connection)); - g_ptr_array_add(auth_dialog_argv, "-n"); - g_ptr_array_add(auth_dialog_argv, (gpointer) nm_connection_get_id(request->connection)); - g_ptr_array_add(auth_dialog_argv, "-s"); - g_ptr_array_add(auth_dialog_argv, (gpointer) nm_setting_vpn_get_service_type(s_vpn)); - g_ptr_array_add(auth_dialog_argv, "--external-ui-mode"); - g_ptr_array_add(auth_dialog_argv, "-i"); - - if (request->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW) - g_ptr_array_add(auth_dialog_argv, "-r"); - - s = nm_vpn_plugin_info_lookup_property(plugin_info, "GNOME", "supports-hints"); - if (_nm_utils_ascii_str_to_bool(s, FALSE)) { - for (i = 0; request->hints[i]; i++) { - g_ptr_array_add(auth_dialog_argv, "-t"); - g_ptr_array_add(auth_dialog_argv, request->hints[i]); - } - } - - g_ptr_array_add(auth_dialog_argv, NULL); - if (!g_spawn_async_with_pipes(NULL, - (char **) auth_dialog_argv->pdata, - NULL, - G_SPAWN_DO_NOT_REAP_CHILD, - NULL, - NULL, - &auth_dialog_pid, - &auth_dialog_in_fd, - &auth_dialog_out_fd, - NULL, - &error)) { - g_warning("Failed to spawn the auth dialog%s\n", error->message); - return FALSE; - } - - auth_dialog_in = g_unix_output_stream_new(auth_dialog_in_fd, TRUE); - auth_dialog_out = g_unix_input_stream_new(auth_dialog_out_fd, TRUE); - - auth_dialog_request = g_string_new_len(NULL, 1024); - nm_setting_vpn_foreach_data_item(s_vpn, _add_data_item_to_string, auth_dialog_request); - nm_setting_vpn_foreach_secret(s_vpn, _add_secret_to_string, auth_dialog_request); - g_string_append(auth_dialog_request, "DONE\nQUIT\n"); - auth_dialog_request_len = auth_dialog_request->len; - auth_dialog_request_str = g_string_free(auth_dialog_request, FALSE); - - data = g_slice_new(AuthDialogData); - *data = (AuthDialogData){ - .auth_dialog_response = g_string_new_len(NULL, sizeof(data->read_buf)), - .auth_dialog_pid = auth_dialog_pid, - .request = request, - .secrets = g_ptr_array_ref(secrets), - .input_stream = auth_dialog_out, - .output_stream = auth_dialog_in, - }; - - g_output_stream_write_async(auth_dialog_in, - auth_dialog_request_str, - auth_dialog_request_len, - G_PRIORITY_DEFAULT, - request->cancellable, - _auth_dialog_write_done, - auth_dialog_request_str); - - g_input_stream_read_async(auth_dialog_out, - data->read_buf, - sizeof(data->read_buf), - G_PRIORITY_DEFAULT, - request->cancellable, - _auth_dialog_read_done, - data); - - return TRUE; -} - -static void -request_secrets_from_ui(RequestData *request) -{ - gs_unref_ptrarray GPtrArray *secrets = NULL; - gs_free_error GError * error = NULL; - NMSecretAgentSimplePrivate *priv; - NMSecretAgentSimpleSecret * secret; - const char * title; - gs_free char * msg = NULL; - - priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE(request->self); - g_return_if_fail(priv->enabled); - - /* We only handle requests for connection with @path if set. */ - if (priv->path && !g_str_has_prefix(request->request_id, priv->path)) { - g_set_error(&error, - NM_SECRET_AGENT_ERROR, - NM_SECRET_AGENT_ERROR_FAILED, - "Request for %s secrets doesn't match path %s", - request->request_id, - priv->path); - goto out_fail_error; - } - - secrets = g_ptr_array_new_with_free_func((GDestroyNotify) _secret_real_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); - - if (!add_wireless_secrets(request, secrets)) - goto out_fail; - } else if (nm_connection_is_type(request->connection, NM_SETTING_WIRED_SETTING_NAME)) { - title = _("Wired 802.1X authentication"); - msg = g_strdup_printf(_("Secrets are required to access the wired network '%s'"), - nm_connection_get_id(request->connection)); - - if (!add_8021x_secrets(request, secrets)) - goto out_fail; - } else if (nm_connection_is_type(request->connection, NM_SETTING_PPPOE_SETTING_NAME)) { - title = _("DSL authentication"); - msg = g_strdup_printf(_("Secrets are required for the DSL connection '%s'"), - nm_connection_get_id(request->connection)); - - if (!add_pppoe_secrets(request, secrets)) - goto out_fail; - } else if (nm_connection_is_type(request->connection, NM_SETTING_GSM_SETTING_NAME)) { - NMSettingGsm *s_gsm = nm_connection_get_setting_gsm(request->connection); - - if (g_strv_contains(NM_CAST_STRV_CC(request->hints), NM_SETTING_GSM_PIN)) { - title = _("PIN code required"); - msg = g_strdup(_("PIN code is needed for the mobile broadband device")); - - secret = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_SECRET, - _("PIN"), - NM_SETTING(s_gsm), - NM_SETTING_GSM_PIN); - 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 = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_SECRET, - _("Password"), - NM_SETTING(s_gsm), - NM_SETTING_GSM_PASSWORD); - g_ptr_array_add(secrets, secret); - } - } else if (nm_connection_is_type(request->connection, NM_SETTING_MACSEC_SETTING_NAME)) { - NMSettingMacsec *s_macsec = nm_connection_get_setting_macsec(request->connection); - - msg = g_strdup_printf(_("Secrets are required to access the MACsec network '%s'"), - nm_connection_get_id(request->connection)); - - if (nm_setting_macsec_get_mode(s_macsec) == NM_SETTING_MACSEC_MODE_PSK) { - title = _("MACsec PSK authentication"); - secret = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_SECRET, - _("MKA CAK"), - NM_SETTING(s_macsec), - NM_SETTING_MACSEC_MKA_CAK); - g_ptr_array_add(secrets, secret); - } else { - title = _("MACsec EAP authentication"); - if (!add_8021x_secrets(request, secrets)) - goto out_fail; - } - } else if (nm_connection_is_type(request->connection, NM_SETTING_WIREGUARD_SETTING_NAME)) { - title = _("WireGuard VPN secret"); - if (!add_wireguard_secrets(request, secrets, &msg, &error)) - goto out_fail_error; - } 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 = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_SECRET, - _("Password"), - NM_SETTING(s_cdma), - NM_SETTING_CDMA_PASSWORD); - g_ptr_array_add(secrets, secret); - } else if (nm_connection_is_type(request->connection, NM_SETTING_BLUETOOTH_SETTING_NAME)) { - NMSetting *setting = NULL; - - setting = nm_connection_get_setting_by_name(request->connection, - NM_SETTING_BLUETOOTH_SETTING_NAME); - if (setting - && !nm_streq0(nm_setting_bluetooth_get_connection_type(NM_SETTING_BLUETOOTH(setting)), - NM_SETTING_BLUETOOTH_TYPE_NAP)) { - 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); - } - - if (!setting) - goto out_fail; - - title = _("Mobile broadband network password"); - msg = g_strdup_printf(_("A password is required to connect to '%s'."), - nm_connection_get_id(request->connection)); - - secret = _secret_real_new_plain(NM_SECRET_AGENT_SECRET_TYPE_SECRET, - _("Password"), - setting, - "password"); - g_ptr_array_add(secrets, secret); - } else if (nm_connection_is_type(request->connection, NM_SETTING_VPN_SETTING_NAME)) { - title = _("VPN password required"); - - if (try_spawn_vpn_auth_helper(request, secrets)) { - /* This will emit REQUEST_SECRETS when ready */ - return; - } - - if (!add_vpn_secrets(request, secrets, &msg)) - goto out_fail; - if (!msg) { - msg = g_strdup_printf(_("A password is required to connect to '%s'."), - nm_connection_get_id(request->connection)); - } - } else - goto out_fail; - - if (secrets->len == 0) - goto out_fail; - - g_signal_emit(request->self, - signals[REQUEST_SECRETS], - 0, - request->request_id, - title, - msg, - secrets); - return; - -out_fail: - g_set_error(&error, - NM_SECRET_AGENT_ERROR, - NM_SECRET_AGENT_ERROR_FAILED, - "Cannot service a secrets request %s for a %s connection", - request->request_id, - nm_connection_get_connection_type(request->connection)); -out_fail_error: - _request_data_complete(request, NULL, error, NULL); -} - -static void -get_secrets(NMSecretAgentOld * agent, - NMConnection * connection, - const char * connection_path, - const char * setting_name, - const char ** hints, - NMSecretAgentGetSecretsFlags flags, - NMSecretAgentOldGetSecretsFunc callback, - gpointer callback_data) -{ - NMSecretAgentSimple * self = NM_SECRET_AGENT_SIMPLE(agent); - NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE(self); - RequestData * request; - gs_free_error GError *error = NULL; - gs_free char * request_id = NULL; - const char * request_id_setting_name; - - request_id = g_strdup_printf("%s/%s", connection_path, setting_name); - - if (g_hash_table_contains(priv->requests, &request_id)) { - /* 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); - callback(agent, connection, NULL, error, callback_data); - return; - } - - 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"); - callback(agent, connection, NULL, error, callback_data); - return; - } - - nm_assert(g_str_has_suffix(request_id, setting_name)); - request_id_setting_name = &request_id[strlen(request_id) - strlen(setting_name)]; - nm_assert(nm_streq(request_id_setting_name, setting_name)); - - request = g_slice_new(RequestData); - *request = (RequestData){ - .self = self, - .connection = g_object_ref(connection), - .setting_name = request_id_setting_name, - .hints = g_strdupv((char **) hints), - .callback = callback, - .callback_data = callback_data, - .request_id = g_steal_pointer(&request_id), - .flags = flags, - .cancellable = g_cancellable_new(), - }; - g_hash_table_add(priv->requests, 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; - RequestData * request; - gs_unref_variant GVariant *secrets_dict = NULL; - gs_free_error 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; - GVariantBuilder vpn_secrets_builder; - GVariantBuilder wg_secrets_builder; - GVariantBuilder wg_peer_builder; - GHashTable * settings; - GHashTableIter iter; - const char * name; - gboolean has_vpn = FALSE; - gboolean has_wg = FALSE; - - settings = g_hash_table_new_full(nm_str_hash, - g_str_equal, - NULL, - (GDestroyNotify) g_variant_builder_unref); - for (i = 0; i < secrets->len; i++) { - SecretReal *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); - } - - switch (secret->base.secret_type) { - case NM_SECRET_AGENT_SECRET_TYPE_PROPERTY: - case NM_SECRET_AGENT_SECRET_TYPE_SECRET: - g_variant_builder_add(setting_builder, - "{sv}", - secret->property, - g_variant_new_string(secret->base.value)); - break; - case NM_SECRET_AGENT_SECRET_TYPE_VPN_SECRET: - if (!has_vpn) { - g_variant_builder_init(&vpn_secrets_builder, G_VARIANT_TYPE("a{ss}")); - has_vpn = TRUE; - } - g_variant_builder_add(&vpn_secrets_builder, - "{ss}", - secret->property, - secret->base.value); - break; - case NM_SECRET_AGENT_SECRET_TYPE_WIREGUARD_PEER_PSK: - if (!has_wg) { - g_variant_builder_init(&wg_secrets_builder, G_VARIANT_TYPE("aa{sv}")); - has_wg = TRUE; - } - g_variant_builder_init(&wg_peer_builder, G_VARIANT_TYPE("a{sv}")); - g_variant_builder_add(&wg_peer_builder, - "{sv}", - NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY, - g_variant_new_string(secret->property)); - g_variant_builder_add(&wg_peer_builder, - "{sv}", - NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, - g_variant_new_string(secret->base.value)); - g_variant_builder_add(&wg_secrets_builder, "a{sv}", &wg_peer_builder); - break; - } - } - - if (has_vpn) { - g_variant_builder_add(setting_builder, - "{sv}", - "secrets", - g_variant_builder_end(&vpn_secrets_builder)); - } - - if (has_wg) { - g_variant_builder_add(setting_builder, - "{sv}", - NM_SETTING_WIREGUARD_PEERS, - g_variant_builder_end(&wg_secrets_builder)); - } - - 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); - secrets_dict = g_variant_ref_sink(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_data_complete(request, secrets_dict, error, NULL); -} - -static void -cancel_get_secrets(NMSecretAgentOld *agent, const char *connection_path, const char *setting_name) -{ - NMSecretAgentSimple * self = NM_SECRET_AGENT_SIMPLE(agent); - NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE(self); - gs_free_error GError *error = NULL; - gs_free char * request_id = NULL; - RequestData * request; - - request_id = g_strdup_printf("%s/%s", connection_path, setting_name); - request = g_hash_table_lookup(priv->requests, &request_id); - if (!request) { - /* this is really a bug of the caller (or us?). We cannot invoke a callback, - * hence the caller cannot cleanup the request. */ - g_return_if_reached(); - } - - g_set_error(&error, - NM_SECRET_AGENT_ERROR, - NM_SECRET_AGENT_ERROR_AGENT_CANCELED, - "The secret agent is going away"); - _request_data_complete(request, NULL, error, NULL); -} - -static void -save_secrets(NMSecretAgentOld * agent, - NMConnection * connection, - const char * connection_path, - NMSecretAgentOldSaveSecretsFunc callback, - gpointer callback_data) -{ - /* We don't support secret storage */ - callback(agent, connection, NULL, callback_data); -} - -static void -delete_secrets(NMSecretAgentOld * agent, - NMConnection * connection, - const char * 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); - gs_free RequestData **requests = NULL; - gsize i; - gs_free char * path_full = NULL; - - /* The path is only used to match a request_id with the current - * connection. Since the request_id is "${CONNECTION_PATH}/${SETTING}", - * add a trailing '/' to the path to match the full connection path. - */ - path_full = path ? g_strdup_printf("%s/", path) : NULL; - - if (!nm_streq0(path_full, priv->path)) { - g_free(priv->path); - priv->path = g_steal_pointer(&path_full); - } - - if (priv->enabled) - return; - priv->enabled = TRUE; - - /* Service pending secret requests. */ - requests = (RequestData **) g_hash_table_get_keys_as_array(priv->requests, NULL); - for (i = 0; requests[i]; i++) - request_secrets_from_ui(requests[i]); -} - -/*****************************************************************************/ - -static void -nm_secret_agent_simple_init(NMSecretAgentSimple *agent) -{ - NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE(agent); - - G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(RequestData, request_id) == 0); - priv->requests = g_hash_table_new_full(nm_pstr_hash, nm_pstr_equal, NULL, _request_data_free); -} - -/** - * 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. - */ -NMSecretAgentSimple * -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, - NM_SECRET_AGENT_OLD_CAPABILITIES, - NM_SECRET_AGENT_CAPABILITY_VPN_HINTS, - NULL); -} - -static void -dispose(GObject *object) -{ - NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE(object); - gs_free_error GError *error = NULL; - GHashTableIter iter; - RequestData * request; - - g_hash_table_iter_init(&iter, priv->requests); - while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &request)) { - if (!error) - nm_utils_error_set_cancelled(&error, TRUE, "NMSecretAgentSimple"); - _request_data_complete(request, NULL, error, &iter); - } - - G_OBJECT_CLASS(nm_secret_agent_simple_parent_class)->dispose(object); -} - -static void -finalize(GObject *object) -{ - NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE(object); - - g_hash_table_destroy(priv->requests); - - g_free(priv->path); - - G_OBJECT_CLASS(nm_secret_agent_simple_parent_class)->finalize(object); -} - -void -nm_secret_agent_simple_class_init(NMSecretAgentSimpleClass *klass) -{ - GObjectClass * object_class = G_OBJECT_CLASS(klass); - NMSecretAgentOldClass *agent_class = NM_SECRET_AGENT_OLD_CLASS(klass); - - object_class->dispose = dispose; - object_class->finalize = finalize; - - agent_class->get_secrets = get_secrets; - agent_class->cancel_get_secrets = cancel_get_secrets; - agent_class->save_secrets = save_secrets; - agent_class->delete_secrets = 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(NM_SECRET_AGENT_SIMPLE_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); -} |