diff --git a/include/NetworkManager.h b/include/NetworkManager.h index 9df444f27..d374b96ae 100644 --- a/include/NetworkManager.h +++ b/include/NetworkManager.h @@ -48,6 +48,7 @@ #define NM_DBUS_SERVICE_USER_SETTINGS "org.freedesktop.NetworkManagerUserSettings" #define NM_DBUS_SERVICE_SYSTEM_SETTINGS "org.freedesktop.NetworkManagerSystemSettings" #define NM_DBUS_IFACE_SETTINGS "org.freedesktop.NetworkManagerSettings" +#define NM_DBUS_IFACE_SETTINGS_SECRETS "org.freedesktop.NetworkManagerSettings.Secrets" #define NM_DBUS_IFACE_SETTINGS_SYSTEM "org.freedesktop.NetworkManagerSettings.System" #define NM_DBUS_PATH_SETTINGS "/org/freedesktop/NetworkManagerSettings" diff --git a/introspection/nm-settings.xml b/introspection/nm-settings.xml index b252c00d5..f198badfe 100644 --- a/introspection/nm-settings.xml +++ b/introspection/nm-settings.xml @@ -43,5 +43,54 @@ + + + + Secrets have a separate interface so that they can be locked down via + D-Bus policy configuration. + + + + + Get the secrets for the requested connection. If the connection is + provided by the settings service, it should save the updated secrets + if they are changed by the user. If the connection is not provided + by the settings service, the new secrets will be saved by the settings + service that provides the connection. + + + + + + The D-Bus service name of the settings service that provides this connection. + + + + + Object path of the connection. + + + + + Name of the setting for which secrets are requested. + + + + + Array of strings of key names in the Setting for which NM thinks + a secrets may be required. + + + + + + Nested settings maps containing secrets. Each setting MUST contain at + least the 'name' field, containing the name of the setting, and one or + more secrets. + + + + + diff --git a/libnm-glib/libnm-glib.ver b/libnm-glib/libnm-glib.ver index 1596afaad..74640eff9 100644 --- a/libnm-glib/libnm-glib.ver +++ b/libnm-glib/libnm-glib.ver @@ -139,6 +139,7 @@ global: nm_settings_interface_error_get_type; nm_settings_interface_error_quark; nm_settings_interface_get_connection_by_path; + nm_settings_interface_get_secrets_for_connection; nm_settings_interface_get_type; nm_settings_interface_list_connections; nm_settings_service_export; diff --git a/libnm-glib/nm-settings-interface.c b/libnm-glib/nm-settings-interface.c index a1d548ed1..c03f3493f 100644 --- a/libnm-glib/nm-settings-interface.c +++ b/libnm-glib/nm-settings-interface.c @@ -143,6 +143,48 @@ nm_settings_interface_add_connection (NMSettingsInterface *settings, return FALSE; } +/** + * nm_settings_interface_get_secrets_for_connection: + * @settings: a object implementing %NMSettingsInterface + * @settings_service: name of the settings service providing this connection + * @connection_path: object path of the connection provided by @settings_service + * @setting_name: the name of the setting for which to get secrets + * @hints: a list of hints + * @callback: function to call when the operation is complete + * @user_data: context-specific data passed to @callback + * + * Requests that the settings service get the secrets for the requested connection. + * + * Returns: TRUE if the request was successful, FALSE if it failed + **/ +gboolean +nm_settings_interface_get_secrets_for_connection (NMSettingsInterface *settings, + const char *settings_service, + const char *connection_path, + const char *setting_name, + const char **hints, + NMSettingsGetSecretsForConnectionFunc callback, + gpointer user_data) +{ + g_return_val_if_fail (settings != NULL, FALSE); + g_return_val_if_fail (NM_IS_SETTINGS_INTERFACE (settings), FALSE); + g_return_val_if_fail (connection_path != NULL, FALSE); + g_return_val_if_fail (setting_name != NULL, FALSE); + g_return_val_if_fail (hints != NULL, FALSE); + g_return_val_if_fail (callback != NULL, FALSE); + + if (NM_SETTINGS_INTERFACE_GET_INTERFACE (settings)->get_secrets_for_connection) { + return NM_SETTINGS_INTERFACE_GET_INTERFACE (settings)->get_secrets_for_connection (settings, + settings_service, + connection_path, + setting_name, + hints, + callback, + user_data); + } + return FALSE; +} + /*****************************************************************/ static void diff --git a/libnm-glib/nm-settings-interface.h b/libnm-glib/nm-settings-interface.h index dc7bd0eb8..47ca3f73b 100644 --- a/libnm-glib/nm-settings-interface.h +++ b/libnm-glib/nm-settings-interface.h @@ -60,6 +60,11 @@ typedef void (*NMSettingsAddConnectionFunc) (NMSettingsInterface *settings, GError *error, gpointer user_data); +typedef void (*NMSettingsGetSecretsForConnectionFunc) (NMSettingsInterface *settings, + GHashTable *secrets, + GError *error, + gpointer user_data); + struct _NMSettingsInterface { GTypeInterface g_iface; @@ -81,13 +86,21 @@ struct _NMSettingsInterface { void (*connections_read) (NMSettingsInterface *settings); + /* Function */ + gboolean (*get_secrets_for_connection) (NMSettingsInterface *self, + const char *settings_service, + const char *connection_path, + const char *setting_name, + const char **hints, + NMSettingsGetSecretsForConnectionFunc callback, + gpointer user_data); + /* Padding for future expansion */ void (*_reserved1) (void); void (*_reserved2) (void); void (*_reserved3) (void); void (*_reserved4) (void); void (*_reserved5) (void); - void (*_reserved6) (void); }; GType nm_settings_interface_get_type (void); @@ -103,6 +116,14 @@ gboolean nm_settings_interface_add_connection (NMSettingsInterface *settings, NMSettingsAddConnectionFunc callback, gpointer user_data); +gboolean nm_settings_interface_get_secrets_for_connection (NMSettingsInterface *settings, + const char *settings_service, + const char *connection_path, + const char *setting_name, + const char **hints, + NMSettingsGetSecretsForConnectionFunc callback, + gpointer user_data); + G_END_DECLS #endif /* NM_SETTINGS_INTERFACE_H */ diff --git a/libnm-glib/nm-settings-service.c b/libnm-glib/nm-settings-service.c index 6266d10da..58f678dfe 100644 --- a/libnm-glib/nm-settings-service.c +++ b/libnm-glib/nm-settings-service.c @@ -36,6 +36,13 @@ static void impl_settings_add_connection (NMSettingsService *self, GHashTable *settings, DBusGMethodInvocation *context); +static void impl_settings_get_secrets_for_connection (NMSettingsService *self, + const char *settings_service, + const char *connection_path, + const char *setting_name, + const char **hints, + DBusGMethodInvocation *context); + #include "nm-settings-glue.h" static void settings_interface_init (NMSettingsInterface *class); @@ -219,6 +226,49 @@ impl_settings_add_connection (NMSettingsService *self, g_object_unref (tmp); } +static void +dbus_get_secrets_cb (NMSettingsInterface *settings, + GHashTable *secrets, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context, secrets); +} + +static void +impl_settings_get_secrets_for_connection (NMSettingsService *self, + const char *settings_service, + const char *connection_path, + const char *setting_name, + const char **hints, + DBusGMethodInvocation *context) +{ + GError *error = NULL; + + if (NM_SETTINGS_SERVICE_GET_CLASS (self)->get_secrets_for_connection) + NM_SETTINGS_SERVICE_GET_CLASS (self)->get_secrets_for_connection (self, + settings_service, + connection_path, + setting_name, + hints, + context, + dbus_get_secrets_cb, + context); + else { + error = g_error_new (NM_SETTINGS_INTERFACE_ERROR, + NM_SETTINGS_INTERFACE_ERROR_INTERNAL_ERROR, + "%s: %s:%d get_secrets_for_connection() not implemented", + __func__, __FILE__, __LINE__); + dbus_g_method_return_error (context, error); + g_error_free (error); + } +} + void nm_settings_service_export_connection (NMSettingsService *self, NMSettingsConnectionInterface *connection) diff --git a/libnm-glib/nm-settings-service.h b/libnm-glib/nm-settings-service.h index 9f4b95fcc..e59b07463 100644 --- a/libnm-glib/nm-settings-service.h +++ b/libnm-glib/nm-settings-service.h @@ -58,13 +58,21 @@ typedef struct { NMSettingsAddConnectionFunc callback, gpointer user_data); + void (*get_secrets_for_connection) (NMSettingsService *self, + const char *settings_service, + const char *connection_path, + const char *setting_name, + const char **hints, + DBusGMethodInvocation *context, + NMSettingsGetSecretsForConnectionFunc callback, + gpointer user_data); + /* Padding for future expansion */ void (*_reserved1) (void); void (*_reserved2) (void); void (*_reserved3) (void); void (*_reserved4) (void); void (*_reserved5) (void); - void (*_reserved6) (void); } NMSettingsServiceClass; GType nm_settings_service_get_type (void); diff --git a/src/nm-manager.c b/src/nm-manager.c index b52c6ce17..f70063b22 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -2447,7 +2447,7 @@ typedef struct GetSecretsInfo { guint32 idle_id; char *hint1; char *hint2; - char *connection_path; + NMConnection *connection; } GetSecretsInfo; static void @@ -2472,7 +2472,7 @@ free_get_secrets_info (gpointer data) g_free (info->hint1); g_free (info->hint2); g_free (info->setting_name); - g_free (info->connection_path); + g_object_unref (info->connection); memset (info, 0, sizeof (GetSecretsInfo)); g_free (info); } @@ -2493,6 +2493,16 @@ provider_cancel_secrets (NMSecretsProviderInterface *provider, gpointer user_dat } } +static void +system_connection_update_cb (NMSettingsConnectionInterface *connection, + GError *error, + gpointer user_data) +{ + if (error != NULL) { + nm_log_warn (LOGD_SYS_SET, "could not update system connection: %s", error->message); + } +} + static void user_get_secrets_cb (DBusGProxy *proxy, DBusGProxyCall *call, @@ -2517,6 +2527,14 @@ user_get_secrets_cb (DBusGProxy *proxy, info->caller, settings, NULL); + + /* If this connection is a system one, we need to update it on our end */ + if (nm_connection_get_scope (info->connection) == NM_CONNECTION_SCOPE_SYSTEM) { + nm_settings_connection_interface_update (NM_SETTINGS_CONNECTION_INTERFACE (info->connection), + system_connection_update_cb, + NULL); + } + g_hash_table_destroy (settings); } else { nm_secrets_provider_interface_get_secrets_result (info->provider, @@ -2607,6 +2625,50 @@ system_get_secrets_reply_cb (NMSettingsConnectionInterface *connection, g_object_unref (provider); } +static gboolean +system_get_secrets_from_user (GetSecretsInfo *info) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (info->manager); + DBusGConnection *g_connection; + GPtrArray *hints = NULL; + + /* Only user settings services that are allowed to control the network + * are allowed to provide new secrets for system connections. + */ + if (priv->user_net_perm != NM_AUTH_CALL_RESULT_YES) + return FALSE; + + g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr); + info->proxy = dbus_g_proxy_new_for_name (g_connection, + NM_DBUS_SERVICE_USER_SETTINGS, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_IFACE_SETTINGS_SECRETS); + if (!info->proxy) { + nm_log_warn (LOGD_SYS_SET, "could not create user settings secrets proxy"); + system_get_secrets_reply_cb (NULL, NULL, NULL, info); // FIXME pass error + return FALSE; + } + + hints = g_ptr_array_sized_new (2); + if (info->hint1) + g_ptr_array_add (hints, (char *) info->hint1); + if (info->hint2) + g_ptr_array_add (hints, (char *) info->hint2); + + info->call = dbus_g_proxy_begin_call_with_timeout (info->proxy, "GetSecretsForConnection", + user_get_secrets_cb, + info, + NULL, + G_MAXINT32, + G_TYPE_STRING, NM_DBUS_SERVICE_SYSTEM_SETTINGS, + DBUS_TYPE_G_OBJECT_PATH, nm_connection_get_path (info->connection), + G_TYPE_STRING, info->setting_name, + DBUS_TYPE_G_ARRAY_OF_STRING, hints, + G_TYPE_INVALID); + g_ptr_array_free (hints, TRUE); + return TRUE; +} + static gboolean system_get_secrets_idle_cb (gpointer user_data) { @@ -2614,12 +2676,12 @@ system_get_secrets_idle_cb (gpointer user_data) NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (info->manager); NMSettingsConnectionInterface *connection; GError *error = NULL; - const char *hints[3] = { NULL, NULL, NULL }; + gboolean success = FALSE; info->idle_id = 0; connection = nm_settings_interface_get_connection_by_path (NM_SETTINGS_INTERFACE (priv->sys_settings), - info->connection_path); + nm_connection_get_path (info->connection)); if (!connection) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION, @@ -2634,14 +2696,29 @@ system_get_secrets_idle_cb (gpointer user_data) return FALSE; } - hints[0] = info->hint1; - hints[1] = info->hint2; - nm_settings_connection_interface_get_secrets (connection, - info->setting_name, - hints, - info->request_new, - system_get_secrets_reply_cb, - info); + /* If new secrets are requested, try asking the user settings service for + * them since the system settings service can't interact with anything + * to get new secrets. + */ + if (info->request_new) + success = system_get_secrets_from_user (info); + + /* If the user wasn't authorized, or we should retry using existing + * secrets, just ask the system settings service. + */ + if (!success) { + const char *hints[3] = { NULL, NULL, NULL }; + + hints[0] = info->hint1; + hints[1] = info->hint2; + nm_settings_connection_interface_get_secrets (connection, + info->setting_name, + hints, + info->request_new, + system_get_secrets_reply_cb, + info); + } + return FALSE; } @@ -2664,7 +2741,7 @@ system_get_secrets (NMManager *self, info->setting_name = g_strdup (setting_name); info->hint1 = hint1 ? g_strdup (hint1) : NULL; info->hint2 = hint2 ? g_strdup (hint2) : NULL; - info->connection_path = g_strdup (nm_connection_get_path (connection)); + info->connection = g_object_ref (connection); info->request_new = request_new; g_object_weak_ref (G_OBJECT (provider), (GWeakNotify) free_get_secrets_info, info); @@ -3934,7 +4011,9 @@ firmware_dir_changed (GFileMonitor *monitor, switch (event_type) { case G_FILE_MONITOR_EVENT_CREATED: case G_FILE_MONITOR_EVENT_CHANGED: +#if GLIB_CHECK_VERSION(2,23,4) case G_FILE_MONITOR_EVENT_MOVED: +#endif case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: if (!priv->fw_changed_id) {