diff options
| author | Michael Biebl <biebl@debian.org> | 2014-07-06 02:16:10 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2014-07-06 02:16:10 +0200 |
| commit | 33491bc4279481db8ae47213e34a6d695a0e8830 (patch) | |
| tree | 097d2b0fdff3fae6885381ae5e57a182cd8cbbba /src/settings/nm-settings-connection.c | |
| parent | 59c3714a494c3b3765657c0551ad82842d98a7d2 (diff) | |
Imported Upstream version 0.9.10.0 upstream/0.9.10.0
Diffstat (limited to 'src/settings/nm-settings-connection.c')
| -rw-r--r-- | src/settings/nm-settings-connection.c | 793 |
1 files changed, 552 insertions, 241 deletions
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 7bd35cf8..d20dbc98 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * (C) Copyright 2008 Novell, Inc. - * (C) Copyright 2008 - 2012 Red Hat, Inc. + * (C) Copyright 2008 - 2013 Red Hat, Inc. */ #include "config.h" @@ -38,9 +38,9 @@ #include "nm-dbus-glib-types.h" #include "nm-logging.h" #include "nm-manager-auth.h" -#include "nm-marshal.h" #include "nm-agent-manager.h" #include "NetworkManagerUtils.h" +#include "nm-properties-changed-signal.h" #define SETTINGS_TIMESTAMPS_FILE NMSTATEDIR "/timestamps" #define SETTINGS_SEEN_BSSIDS_FILE NMSTATEDIR "/seen-bssids" @@ -52,6 +52,13 @@ static void impl_settings_connection_update (NMSettingsConnection *connection, GHashTable *new_settings, DBusGMethodInvocation *context); +static void impl_settings_connection_update_unsaved (NMSettingsConnection *connection, + GHashTable *new_settings, + DBusGMethodInvocation *context); + +static void impl_settings_connection_save (NMSettingsConnection *connection, + DBusGMethodInvocation *context); + static void impl_settings_connection_delete (NMSettingsConnection *connection, DBusGMethodInvocation *context); @@ -70,12 +77,13 @@ G_DEFINE_TYPE (NMSettingsConnection, nm_settings_connection, NM_TYPE_CONNECTION) enum { PROP_0 = 0, PROP_VISIBLE, + PROP_UNSAVED, }; enum { UPDATED, REMOVED, - UNREGISTER, + UPDATED_BY_USER, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -83,11 +91,22 @@ static guint signals[LAST_SIGNAL] = { 0 }; typedef struct { gboolean disposed; - NMDBusManager *dbus_mgr; NMAgentManager *agent_mgr; NMSessionMonitor *session_monitor; guint session_changed_id; + /* TRUE if the connection has not yet been saved to disk, + * or if it contains changes that have not been saved to disk. + */ + gboolean unsaved; + + /* TRUE if the connection was generated by NetworkManager and has + * not been saved or modified by the user. + */ + gboolean nm_generated; + + guint updated_idle_id; + GSList *pending_auths; /* List of pending authentication requests */ gboolean visible; /* Is this connection is visible by some session? */ GSList *reqs; /* in-progress secrets requests */ @@ -110,6 +129,11 @@ typedef struct { guint64 timestamp; /* Up-to-date timestamp of connection use */ gboolean timestamp_set; GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */ + + int autoconnect_retries; + gint32 autoconnect_retry_time; + NMDeviceStateReason autoconnect_blocked_reason; + } NMSettingsConnectionPrivate; /**************************************************************/ @@ -205,7 +229,6 @@ set_visible (NMSettingsConnection *self, gboolean new_visible) gboolean nm_settings_connection_is_visible (NMSettingsConnection *self) { - g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->visible; @@ -218,7 +241,6 @@ nm_settings_connection_recheck_visibility (NMSettingsConnection *self) NMSettingConnection *s_con; guint32 num, i; - g_return_if_fail (self != NULL); g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); @@ -269,7 +291,6 @@ nm_settings_connection_check_permission (NMSettingsConnection *self, guint32 num, i; const char *puser; - g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); @@ -371,28 +392,80 @@ secrets_cleared_cb (NMSettingsConnection *self) priv->agent_secrets = NULL; } -/* Update the settings of this connection to match that of 'new', taking care to - * make a private copy of secrets. +static gboolean +emit_updated (NMSettingsConnection *self) +{ + NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->updated_idle_id = 0; + g_signal_emit (self, signals[UPDATED], 0); + return FALSE; +} + +static void +set_unsaved (NMSettingsConnection *self, gboolean now_unsaved) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + + if (priv->unsaved != now_unsaved) { + priv->unsaved = now_unsaved; + if (!priv->unsaved) + priv->nm_generated = FALSE; + + g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_UNSAVED); + } +} + +static void +changed_cb (NMSettingsConnection *self, gpointer user_data) +{ + gboolean update_unsaved = !!user_data; + + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + + if (update_unsaved) + set_unsaved (self, TRUE); + if (priv->updated_idle_id == 0) + priv->updated_idle_id = g_idle_add ((GSourceFunc) emit_updated, self); +} + +/* Update the settings of this connection to match that of 'new_connection', + * taking care to make a private copy of secrets. */ gboolean nm_settings_connection_replace_settings (NMSettingsConnection *self, - NMConnection *new, + NMConnection *new_connection, + gboolean update_unsaved, GError **error) { NMSettingsConnectionPrivate *priv; - GHashTable *new_settings, *hash = NULL; + GHashTable *hash = NULL; gboolean success = FALSE; - g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); - g_return_val_if_fail (new != NULL, FALSE); - g_return_val_if_fail (NM_IS_CONNECTION (new), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE); priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - new_settings = nm_connection_to_hash (new, NM_SETTING_HASH_FLAG_ALL); - g_assert (new_settings); - if (nm_connection_replace_settings (NM_CONNECTION (self), new_settings, error)) { + nm_utils_normalize_connection (new_connection, TRUE); + if (!nm_connection_verify (new_connection, error)) + return FALSE; + + /* Do nothing if there's nothing to update */ + if (nm_connection_compare (NM_CONNECTION (self), + new_connection, + NM_SETTING_COMPARE_FLAG_EXACT)) { + return TRUE; + } + + /* Disconnect the changed signal to ensure we don't set Unsaved when + * it's not required. + */ + g_signal_handlers_block_by_func (self, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE)); + + if (nm_connection_replace_settings_from_connection (NM_CONNECTION (self), + new_connection, + error)) { + priv->nm_generated = FALSE; + /* Cache the just-updated system secrets in case something calls * nm_connection_clear_secrets() and clears them. */ @@ -405,14 +478,23 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self, if (priv->agent_secrets) { hash = nm_connection_to_hash (priv->agent_secrets, NM_SETTING_HASH_FLAG_ONLY_SECRETS); if (hash) { - success = nm_connection_update_secrets (NM_CONNECTION (self), NULL, hash, error); + (void) nm_connection_update_secrets (NM_CONNECTION (self), NULL, hash, NULL); g_hash_table_destroy (hash); } } nm_settings_connection_recheck_visibility (self); + + /* Manually emit changed signal since we disconnected the handler, but + * only update Unsaved if the caller wanted us to. + */ + changed_cb (self, GUINT_TO_POINTER (update_unsaved)); + + g_signal_emit (self, signals[UPDATED_BY_USER], 0); } - g_hash_table_destroy (new_settings); + + g_signal_handlers_unblock_by_func (self, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE)); + return success; } @@ -423,61 +505,63 @@ ignore_cb (NMSettingsConnection *connection, { } -/* Replaces the settings in this connection with those in 'new'. If any changes - * are made, commits them to permanent storage and to any other subsystems - * watching this connection. Before returning, 'callback' is run with the given - * 'user_data' along with any errors encountered. +/* Replaces the settings in this connection with those in 'new_connection'. If + * any changes are made, commits them to permanent storage and to any other + * subsystems watching this connection. Before returning, 'callback' is run + * with the given 'user_data' along with any errors encountered. */ void nm_settings_connection_replace_and_commit (NMSettingsConnection *self, - NMConnection *new, + NMConnection *new_connection, NMSettingsConnectionCommitFunc callback, gpointer user_data) { GError *error = NULL; - g_return_if_fail (self != NULL); g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); - g_return_if_fail (new != NULL); - g_return_if_fail (NM_IS_CONNECTION (new)); + g_return_if_fail (NM_IS_CONNECTION (new_connection)); - if (!callback) - callback = ignore_cb; - - /* Do nothing if there's nothing to update */ - if (nm_connection_compare (NM_CONNECTION (self), - NM_CONNECTION (new), - NM_SETTING_COMPARE_FLAG_EXACT)) { - callback (self, NULL, user_data); - return; - } - - if (nm_settings_connection_replace_settings (self, new, &error)) { + if (nm_settings_connection_replace_settings (self, new_connection, TRUE, &error)) { nm_settings_connection_commit_changes (self, callback, user_data); } else { - callback (self, error, user_data); + if (callback) + callback (self, error, user_data); g_clear_error (&error); } } +static void +commit_changes (NMSettingsConnection *self, + NMSettingsConnectionCommitFunc callback, + gpointer user_data) +{ + /* Subclasses only call this function if the save was successful, so at + * this point the connection is synced to disk and no longer unsaved. + */ + set_unsaved (self, FALSE); + + g_object_ref (self); + callback (self, NULL, user_data); + g_object_unref (self); +} + void nm_settings_connection_commit_changes (NMSettingsConnection *connection, NMSettingsConnectionCommitFunc callback, gpointer user_data) { - g_return_if_fail (connection != NULL); g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection)); - g_return_if_fail (callback != NULL); if (NM_SETTINGS_CONNECTION_GET_CLASS (connection)->commit_changes) { NM_SETTINGS_CONNECTION_GET_CLASS (connection)->commit_changes (connection, - callback, + callback ? callback : ignore_cb, user_data); } else { GError *error = g_error_new (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INTERNAL_ERROR, "%s: %s:%d commit_changes() unimplemented", __func__, __FILE__, __LINE__); - callback (connection, error, user_data); + if (callback) + callback (connection, error, user_data); g_error_free (error); } } @@ -487,35 +571,23 @@ nm_settings_connection_delete (NMSettingsConnection *connection, NMSettingsConnectionDeleteFunc callback, gpointer user_data) { - g_return_if_fail (connection != NULL); g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection)); - g_return_if_fail (callback != NULL); if (NM_SETTINGS_CONNECTION_GET_CLASS (connection)->delete) { NM_SETTINGS_CONNECTION_GET_CLASS (connection)->delete (connection, - callback, + callback ? callback : ignore_cb, user_data); } else { GError *error = g_error_new (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INTERNAL_ERROR, "%s: %s:%d delete() unimplemented", __func__, __FILE__, __LINE__); - callback (connection, error, user_data); + if (callback) + callback (connection, error, user_data); g_error_free (error); } } static void -commit_changes (NMSettingsConnection *connection, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) -{ - g_object_ref (connection); - g_signal_emit (connection, signals[UPDATED], 0); - callback (connection, NULL, user_data); - g_object_unref (connection); -} - -static void remove_entry_from_db (NMSettingsConnection *connection, const char* db_name) { GKeyFile *key_file; @@ -565,7 +637,7 @@ do_delete (NMSettingsConnection *connection, /* Tell agents to remove secrets for this connection */ for_agents = nm_connection_duplicate (NM_CONNECTION (connection)); nm_connection_clear_secrets (for_agents); - nm_agent_manager_delete_secrets (priv->agent_mgr, for_agents, FALSE, 0); + nm_agent_manager_delete_secrets (priv->agent_mgr, for_agents); g_object_unref (for_agents); /* Remove timestamp from timestamps database file */ @@ -574,7 +646,10 @@ do_delete (NMSettingsConnection *connection, /* Remove connection from seen-bssids database file */ remove_entry_from_db (connection, "seen-bssids"); + nm_settings_connection_signal_remove (connection); + callback (connection, NULL, user_data); + g_object_unref (connection); } @@ -792,13 +867,11 @@ agent_secrets_done_cb (NMAgentManager *manager, /** * nm_settings_connection_get_secrets: * @connection: the #NMSettingsConnection - * @filter_by_uid: if TRUE, only request secrets from agents registered by the - * same UID as @uid. - * @uid: when @filter_by_uid is TRUE, only request secrets from agents belonging - * to this UID + * @subject: the #NMAuthSubject originating the request * @setting_name: the setting to return secrets for * @flags: flags to modify the secrets request - * @hint: the name of a key in @setting_name for which a secret may be required + * @hints: key names in @setting_name for which secrets may be required, or some + * other information about the request * @callback: the function to call with returned secrets * @callback_data: user data to pass to @callback * @@ -809,11 +882,10 @@ agent_secrets_done_cb (NMAgentManager *manager, **/ guint32 nm_settings_connection_get_secrets (NMSettingsConnection *self, - gboolean filter_by_uid, - gulong uid, + NMAuthSubject *subject, const char *setting_name, NMSettingsGetSecretsFlags flags, - const char *hint, + const char **hints, NMSettingsConnectionSecretsFunc callback, gpointer callback_data, GError **error) @@ -821,6 +893,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self, NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); GHashTable *existing_secrets; guint32 call_id = 0; + char *joined_hints = NULL; /* Use priv->secrets to work around the fact that nm_connection_clear_secrets() * will clear secrets on this object's settings. @@ -843,12 +916,11 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self, existing_secrets = nm_connection_to_hash (priv->system_secrets, NM_SETTING_HASH_FLAG_ONLY_SECRETS); call_id = nm_agent_manager_get_secrets (priv->agent_mgr, NM_CONNECTION (self), - filter_by_uid, - uid, + subject, existing_secrets, setting_name, flags, - hint, + hints, agent_secrets_done_cb, self, callback, @@ -856,12 +928,17 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self, if (existing_secrets) g_hash_table_unref (existing_secrets); - nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets requested flags 0x%X hint '%s'", - nm_connection_get_uuid (NM_CONNECTION (self)), - setting_name, - call_id, - flags, - hint); + if (nm_logging_enabled (LOGL_DEBUG, LOGD_SETTINGS)) { + if (hints) + joined_hints = g_strjoinv (",", (char **) hints); + nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets requested flags 0x%X hints '%s'", + nm_connection_get_uuid (NM_CONNECTION (self)), + setting_name, + call_id, + flags, + joined_hints ? joined_hints : "(none)"); + g_free (joined_hints); + } return call_id; } @@ -884,7 +961,7 @@ nm_settings_connection_cancel_secrets (NMSettingsConnection *self, typedef void (*AuthCallback) (NMSettingsConnection *connection, DBusGMethodInvocation *context, - gulong sender_uid, + NMAuthSubject *subject, GError *error, gpointer data); @@ -901,115 +978,114 @@ pk_auth_cb (NMAuthChain *chain, const char *perm; AuthCallback callback; gpointer callback_data; - gulong sender_uid; + NMAuthSubject *subject; priv->pending_auths = g_slist_remove (priv->pending_auths, chain); + perm = nm_auth_chain_get_data (chain, "perm"); + g_assert (perm); + result = nm_auth_chain_get_result (chain, perm); + /* If our NMSettingsConnection is already gone, do nothing */ if (chain_error) { error = g_error_new (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_GENERAL, "Error checking authorization: %s", chain_error->message ? chain_error->message : "(unknown)"); - } else { - perm = nm_auth_chain_get_data (chain, "perm"); - g_assert (perm); - result = nm_auth_chain_get_result (chain, perm); - - /* Caller didn't successfully authenticate */ - if (result != NM_AUTH_CALL_RESULT_YES) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_NOT_PRIVILEGED, - "Insufficient privileges."); - } + } else if (result != NM_AUTH_CALL_RESULT_YES) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + "Insufficient privileges."); } callback = nm_auth_chain_get_data (chain, "callback"); callback_data = nm_auth_chain_get_data (chain, "callback-data"); - sender_uid = nm_auth_chain_get_data_ulong (chain, "sender-uid"); - callback (self, context, sender_uid, error, callback_data); + subject = nm_auth_chain_get_data (chain, "subject"); + callback (self, context, subject, error, callback_data); g_clear_error (&error); nm_auth_chain_unref (chain); } -static gboolean -check_user_in_acl (NMConnection *connection, - DBusGMethodInvocation *context, - NMDBusManager *dbus_mgr, - NMSessionMonitor *session_monitor, - gulong *out_sender_uid, - GError **error) -{ - gulong sender_uid = G_MAXULONG; - char *error_desc = NULL; - - g_return_val_if_fail (connection != NULL, FALSE); - g_return_val_if_fail (context != NULL, FALSE); - g_return_val_if_fail (session_monitor != NULL, FALSE); - - /* Get the caller's UID */ - if (!nm_auth_get_caller_uid (context, dbus_mgr, &sender_uid, &error_desc)) { - g_set_error_literal (error, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - error_desc); - g_free (error_desc); - return FALSE; - } +/** + * _new_auth_subject: + * @context: the D-Bus method invocation context + * @error: on failure, a #GError + * + * Creates an NMAuthSubject for the caller. + * + * Returns: the #NMAuthSubject on success, or %NULL on failure and sets @error + */ +static NMAuthSubject * +_new_auth_subject (DBusGMethodInvocation *context, GError **error) +{ + NMAuthSubject *subject; - /* Make sure the UID can view this connection */ - if (!nm_auth_uid_in_acl (connection, session_monitor, sender_uid, &error_desc)) { + subject = nm_auth_subject_new_from_context (context); + if (!subject) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, - error_desc); - g_free (error_desc); - return FALSE; + "Unable to determine UID of request."); } - if (out_sender_uid) - *out_sender_uid = sender_uid; - return TRUE; + return subject; } static void auth_start (NMSettingsConnection *self, DBusGMethodInvocation *context, + NMAuthSubject *subject, const char *check_permission, AuthCallback callback, gpointer callback_data) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); NMAuthChain *chain; - gulong sender_uid = G_MAXULONG; GError *error = NULL; + char *error_desc = NULL; - if (!check_user_in_acl (NM_CONNECTION (self), - context, - priv->dbus_mgr, - priv->session_monitor, - &sender_uid, - &error)) { - callback (self, context, G_MAXULONG, error, callback_data); + g_return_if_fail (context != NULL); + g_return_if_fail (NM_IS_AUTH_SUBJECT (subject)); + + /* Ensure the caller can view this connection */ + if (!nm_auth_uid_in_acl (NM_CONNECTION (self), + priv->session_monitor, + nm_auth_subject_get_uid (subject), + &error_desc)) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + error_desc); + g_free (error_desc); + + callback (self, context, subject, error, callback_data); g_clear_error (&error); return; } - if (check_permission) { - chain = nm_auth_chain_new (context, NULL, pk_auth_cb, self); - g_assert (chain); - nm_auth_chain_set_data (chain, "perm", (gpointer) check_permission, NULL); - nm_auth_chain_set_data (chain, "callback", callback, NULL); - nm_auth_chain_set_data (chain, "callback-data", callback_data, NULL); - nm_auth_chain_set_data_ulong (chain, "sender-uid", sender_uid); - - nm_auth_chain_add_call (chain, check_permission, TRUE); - priv->pending_auths = g_slist_append (priv->pending_auths, chain); - } else { + if (!check_permission) { /* Don't need polkit auth, automatic success */ - callback (self, context, sender_uid, NULL, callback_data); + callback (self, context, subject, NULL, callback_data); + return; + } + + chain = nm_auth_chain_new_subject (subject, context, pk_auth_cb, self); + if (!chain) { + g_set_error_literal (&error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + "Unable to authenticate the request."); + callback (self, context, subject, error, callback_data); + g_clear_error (&error); + return; } + + priv->pending_auths = g_slist_append (priv->pending_auths, chain); + nm_auth_chain_set_data (chain, "perm", (gpointer) check_permission, NULL); + nm_auth_chain_set_data (chain, "callback", callback, NULL); + nm_auth_chain_set_data (chain, "callback-data", callback_data, NULL); + nm_auth_chain_set_data (chain, "subject", g_object_ref (subject), g_object_unref); + nm_auth_chain_add_call (chain, check_permission, TRUE); } /**** DBus method handlers ************************************/ @@ -1019,7 +1095,6 @@ check_writable (NMConnection *connection, GError **error) { NMSettingConnection *s_con; - g_return_val_if_fail (connection != NULL, FALSE); g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); s_con = nm_connection_get_setting_connection (connection); @@ -1049,7 +1124,7 @@ check_writable (NMConnection *connection, GError **error) static void get_settings_auth_cb (NMSettingsConnection *self, DBusGMethodInvocation *context, - gulong sender_uid, + NMAuthSubject *subject, GError *error, gpointer data) { @@ -1086,7 +1161,19 @@ get_settings_auth_cb (NMSettingsConnection *self, s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (dupl_con)); if (bssid_list && s_wifi) { g_object_set (s_wifi, NM_SETTING_WIRELESS_SEEN_BSSIDS, bssid_list, NULL); - nm_utils_slist_free (bssid_list, g_free); + g_slist_free_full (bssid_list, g_free); + } + + /* 802-11-wireless.security property is deprecated. But we set it here so that + * we don't disturb old clients that might expect it being properly set for + * secured Wi-Fi connections. + */ + if (nm_connection_get_setting_wireless_security (NM_CONNECTION (dupl_con))) { + s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (dupl_con)); + g_assert (s_wifi); + g_object_set (s_wifi, + NM_SETTING_WIRELESS_SEC, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NULL); } /* Secrets should *never* be returned by the GetSettings method, they @@ -1105,16 +1192,45 @@ static void impl_settings_connection_get_settings (NMSettingsConnection *self, DBusGMethodInvocation *context) { - auth_start (self, context, NULL, get_settings_auth_cb, NULL); + NMAuthSubject *subject; + GError *error = NULL; + + subject = _new_auth_subject (context, &error); + if (subject) { + auth_start (self, context, subject, NULL, get_settings_auth_cb, NULL); + g_object_unref (subject); + } else { + dbus_g_method_return_error (context, error); + g_error_free (error); + } } typedef struct { DBusGMethodInvocation *context; NMAgentManager *agent_mgr; - gulong sender_uid; + NMAuthSubject *subject; + NMConnection *new_settings; + gboolean save_to_disk; } UpdateInfo; static void +update_complete (NMSettingsConnection *self, + UpdateInfo *info, + GError *error) +{ + if (error) + dbus_g_method_return_error (info->context, error); + else + dbus_g_method_return (info->context); + + g_clear_object (&info->subject); + g_clear_object (&info->agent_mgr); + g_clear_object (&info->new_settings); + memset (info, 0, sizeof (*info)); + g_free (info); +} + +static void con_update_cb (NMSettingsConnection *self, GError *error, gpointer user_data) @@ -1122,9 +1238,7 @@ con_update_cb (NMSettingsConnection *self, UpdateInfo *info = user_data; NMConnection *for_agent; - if (error) - dbus_g_method_return_error (info->context, error); - else { + if (!error) { /* Dupe the connection so we can clear out non-agent-owned secrets, * as agent-owned secrets are the only ones we send back be saved. * Only send secrets to agents of the same UID that called update too. @@ -1133,54 +1247,52 @@ con_update_cb (NMSettingsConnection *self, nm_connection_clear_secrets_with_flags (for_agent, secrets_filter_cb, GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); - nm_agent_manager_save_secrets (info->agent_mgr, for_agent, TRUE, info->sender_uid); + nm_agent_manager_save_secrets (info->agent_mgr, for_agent, info->subject); g_object_unref (for_agent); - - dbus_g_method_return (info->context); } - g_object_unref (info->agent_mgr); - memset (info, 0, sizeof (*info)); - g_free (info); + update_complete (self, info, error); } static void update_auth_cb (NMSettingsConnection *self, DBusGMethodInvocation *context, - gulong sender_uid, + NMAuthSubject *subject, GError *error, gpointer data) { - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - NMConnection *new_settings = data; - UpdateInfo *info; + UpdateInfo *info = data; + GError *local = NULL; - if (error) - dbus_g_method_return_error (context, error); - else { - info = g_malloc0 (sizeof (*info)); - info->context = context; - info->agent_mgr = g_object_ref (priv->agent_mgr); - info->sender_uid = sender_uid; - - /* Cache the new secrets from the agent, as stuff like inotify-triggered - * changes to connection's backing config files will blow them away if - * they're in the main connection. - */ - update_agent_secrets_cache (self, new_settings); + if (error) { + update_complete (self, info, error); + return; + } - /* Update and commit our settings. */ + /* Cache the new secrets from the agent, as stuff like inotify-triggered + * changes to connection's backing config files will blow them away if + * they're in the main connection. + */ + update_agent_secrets_cache (self, info->new_settings); + + if (info->save_to_disk) { nm_settings_connection_replace_and_commit (self, - new_settings, - con_update_cb, - info); + info->new_settings, + con_update_cb, + info); + } else { + /* Do nothing if there's nothing to update */ + if (!nm_connection_compare (NM_CONNECTION (self), info->new_settings, NM_SETTING_COMPARE_FLAG_EXACT)) { + if (!nm_settings_connection_replace_settings (self, info->new_settings, TRUE, &local)) + g_assert (local); + } + con_update_cb (self, local, info); + g_clear_error (&local); } - - g_object_unref (new_settings); } static const char * -get_modify_permission_update (NMConnection *old, NMConnection *new) +get_update_modify_permission (NMConnection *old, NMConnection *new) { NMSettingConnection *s_con; guint32 orig_num = 0, new_num = 0; @@ -1206,54 +1318,103 @@ get_modify_permission_update (NMConnection *old, NMConnection *new) } static void -impl_settings_connection_update (NMSettingsConnection *self, - GHashTable *new_settings, - DBusGMethodInvocation *context) +impl_settings_connection_update_helper (NMSettingsConnection *self, + GHashTable *new_settings, + DBusGMethodInvocation *context, + gboolean save_to_disk) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - NMConnection *tmp; + NMAuthSubject *subject = NULL; + NMConnection *tmp = NULL; GError *error = NULL; + UpdateInfo *info; + const char *permission; + char *error_desc = NULL; + + g_assert (new_settings != NULL || save_to_disk == TRUE); /* If the connection is read-only, that has to be changed at the source of * the problem (ex a system settings plugin that can't write connections out) * instead of over D-Bus. */ - if (!check_writable (NM_CONNECTION (self), &error)) { - dbus_g_method_return_error (context, error); - g_error_free (error); - return; - } + if (!check_writable (NM_CONNECTION (self), &error)) + goto error; /* Check if the settings are valid first */ - tmp = nm_connection_new_from_hash (new_settings, &error); - if (!tmp) { - g_assert (error); - dbus_g_method_return_error (context, error); - g_error_free (error); - return; + if (new_settings) { + tmp = nm_connection_new_from_hash (new_settings, &error); + if (!tmp) { + g_assert (error); + goto error; + } } + subject = _new_auth_subject (context, &error); + if (!subject) + goto error; + /* And that the new connection settings will be visible to the user * that's sending the update request. You can't make a connection * invisible to yourself. */ - if (!check_user_in_acl (tmp, - context, - priv->dbus_mgr, - priv->session_monitor, - NULL, - &error)) { - dbus_g_method_return_error (context, error); - g_clear_error (&error); - g_object_unref (tmp); - return; + if (!nm_auth_uid_in_acl (tmp ? tmp : NM_CONNECTION (self), + priv->session_monitor, + nm_auth_subject_get_uid (subject), + &error_desc)) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + error_desc); + g_free (error_desc); + goto error; } - auth_start (self, - context, - get_modify_permission_update (NM_CONNECTION (self), tmp), - update_auth_cb, - tmp); + info = g_malloc0 (sizeof (*info)); + info->context = context; + info->agent_mgr = g_object_ref (priv->agent_mgr); + info->subject = subject; + info->save_to_disk = save_to_disk; + info->new_settings = tmp; + + permission = get_update_modify_permission (NM_CONNECTION (self), + tmp ? tmp : NM_CONNECTION (self)); + auth_start (self, context, subject, permission, update_auth_cb, info); + return; + +error: + g_clear_object (&tmp); + g_clear_object (&subject); + + dbus_g_method_return_error (context, error); + g_clear_error (&error); +} + +static void +impl_settings_connection_update (NMSettingsConnection *self, + GHashTable *new_settings, + DBusGMethodInvocation *context) +{ + g_assert (new_settings); + impl_settings_connection_update_helper (self, new_settings, context, TRUE); +} + +static void +impl_settings_connection_update_unsaved (NMSettingsConnection *self, + GHashTable *new_settings, + DBusGMethodInvocation *context) +{ + g_assert (new_settings); + impl_settings_connection_update_helper (self, new_settings, context, FALSE); +} + +static void +impl_settings_connection_save (NMSettingsConnection *self, + DBusGMethodInvocation *context) +{ + /* Do nothing if the connection is already synced with disk */ + if (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->unsaved == TRUE) + impl_settings_connection_update_helper (self, NULL, context, TRUE); + else + dbus_g_method_return (context); } static void @@ -1272,7 +1433,7 @@ con_delete_cb (NMSettingsConnection *connection, static void delete_auth_cb (NMSettingsConnection *self, DBusGMethodInvocation *context, - gulong sender_uid, + NMAuthSubject *subject, GError *error, gpointer data) { @@ -1305,6 +1466,7 @@ static void impl_settings_connection_delete (NMSettingsConnection *self, DBusGMethodInvocation *context) { + NMAuthSubject *subject; GError *error = NULL; if (!check_writable (NM_CONNECTION (self), &error)) { @@ -1313,7 +1475,14 @@ impl_settings_connection_delete (NMSettingsConnection *self, return; } - auth_start (self, context, get_modify_permission_basic (self), delete_auth_cb, NULL); + subject = _new_auth_subject (context, &error); + if (subject) { + auth_start (self, context, subject, get_modify_permission_basic (self), delete_auth_cb, NULL); + g_object_unref (subject); + } else { + dbus_g_method_return_error (context, error); + g_error_free (error); + } } /**************************************************************/ @@ -1351,7 +1520,7 @@ dbus_get_agent_secrets_cb (NMSettingsConnection *self, static void dbus_secrets_auth_cb (NMSettingsConnection *self, DBusGMethodInvocation *context, - gulong sender_uid, + NMAuthSubject *subject, GError *error, gpointer user_data) { @@ -1362,8 +1531,7 @@ dbus_secrets_auth_cb (NMSettingsConnection *self, if (!error) { call_id = nm_settings_connection_get_secrets (self, - TRUE, - sender_uid, + subject, setting_name, NM_SETTINGS_GET_SECRETS_FLAG_USER_REQUESTED, NULL, @@ -1389,11 +1557,22 @@ impl_settings_connection_get_secrets (NMSettingsConnection *self, const gchar *setting_name, DBusGMethodInvocation *context) { - auth_start (self, - context, - get_modify_permission_basic (self), - dbus_secrets_auth_cb, - g_strdup (setting_name)); + NMAuthSubject *subject; + GError *error = NULL; + + subject = _new_auth_subject (context, &error); + if (subject) { + auth_start (self, + context, + subject, + get_modify_permission_basic (self), + dbus_secrets_auth_cb, + g_strdup (setting_name)); + g_object_unref (subject); + } else { + dbus_g_method_return_error (context, error); + g_error_free (error); + } } /**************************************************************/ @@ -1407,9 +1586,17 @@ nm_settings_connection_signal_remove (NMSettingsConnection *self) /* And unregistered last to ensure the removed signal goes out before * we take the connection off the bus. */ - g_signal_emit_by_name (self, "unregister"); + nm_dbus_manager_unregister_object (nm_dbus_manager_get (), G_OBJECT (self)); +} + +gboolean +nm_settings_connection_get_unsaved (NMSettingsConnection *self) +{ + return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->unsaved; } +/**************************************************************/ + /** * nm_settings_connection_get_timestamp: * @connection: the #NMSettingsConnection @@ -1573,7 +1760,6 @@ nm_settings_connection_get_seen_bssids (NMSettingsConnection *connection) char *bssid_str; GSList *bssid_list = NULL; - g_return_val_if_fail (connection != NULL, 0); g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), NULL); g_hash_table_iter_init (&iter, priv->seen_bssids); @@ -1594,7 +1780,6 @@ gboolean nm_settings_connection_has_seen_bssid (NMSettingsConnection *connection, const struct ether_addr *bssid) { - g_return_val_if_fail (connection != NULL, FALSE); g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE); g_return_val_if_fail (bssid != NULL, FALSE); @@ -1630,7 +1815,6 @@ nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection, /* Add the new BSSID; let the hash take ownership of the allocated BSSID string */ bssid_str = nm_utils_hwaddr_ntoa (seen_bssid, ARPHRD_ETHER); - g_return_if_fail (bssid_str != NULL); g_hash_table_insert (priv->seen_bssids, mac_dup (seen_bssid), bssid_str); /* Build up a list of all the BSSIDs in string form */ @@ -1729,6 +1913,111 @@ nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connecti } } +#define AUTOCONNECT_RETRIES_DEFAULT 4 +#define AUTOCONNECT_RESET_RETRIES_TIMER 300 + +int +nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *connection) +{ + return NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->autoconnect_retries; +} + +void +nm_settings_connection_set_autoconnect_retries (NMSettingsConnection *connection, + int retries) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection); + + priv->autoconnect_retries = retries; + if (retries) + priv->autoconnect_retry_time = 0; + else + priv->autoconnect_retry_time = nm_utils_get_monotonic_timestamp_s () + AUTOCONNECT_RESET_RETRIES_TIMER; +} + +void +nm_settings_connection_reset_autoconnect_retries (NMSettingsConnection *connection) +{ + nm_settings_connection_set_autoconnect_retries (connection, AUTOCONNECT_RETRIES_DEFAULT); +} + +gint32 +nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *connection) +{ + return NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->autoconnect_retry_time; +} + +NMDeviceStateReason +nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *connection) +{ + return NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->autoconnect_blocked_reason; +} + +void +nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *connection, + NMDeviceStateReason reason) +{ + NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->autoconnect_blocked_reason = reason; +} + +gboolean +nm_settings_connection_can_autoconnect (NMSettingsConnection *connection) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection); + NMSettingConnection *s_con; + const char *permission; + + if ( !priv->visible + || priv->autoconnect_retries == 0 + || priv->autoconnect_blocked_reason != NM_DEVICE_STATE_REASON_NONE) + return FALSE; + + s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection)); + if (!nm_setting_connection_get_autoconnect (s_con)) + return FALSE; + + permission = nm_utils_get_shared_wifi_permission (NM_CONNECTION (connection)); + if (permission) { + if (nm_settings_connection_check_permission (connection, permission) == FALSE) + return FALSE; + } + + return TRUE; +} + +/** + * nm_settings_connection_get_nm_generated: + * @connection: an #NMSettingsConnection + * + * Gets the "nm-generated" flag on @connection. + * + * A connection is "nm-generated" if it was generated by + * nm_device_generate_connection() and then assumed by #NMManager, and + * it has not been modified or saved by the user since then. In other + * words, an "nm-generated" connection reflects state that is entirely + * external to NetworkManager. + */ +gboolean +nm_settings_connection_get_nm_generated (NMSettingsConnection *connection) +{ + return NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->nm_generated; +} + +/** + * nm_settings_connection_set_nm_generated: + * @connection: an #NMSettingsConnection + * + * Sets the "nm-generated" flag on @connection; see + * nm_settings_connection_get_nm_generated(). + */ +void +nm_settings_connection_set_nm_generated (NMSettingsConnection *connection) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection); + + priv->nm_generated = TRUE; +} + /**************************************************************/ static void @@ -1736,8 +2025,6 @@ nm_settings_connection_init (NMSettingsConnection *self) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - priv->dbus_mgr = nm_dbus_manager_get (); - priv->visible = FALSE; priv->session_monitor = nm_session_monitor_get (); @@ -1750,7 +2037,11 @@ nm_settings_connection_init (NMSettingsConnection *self) priv->seen_bssids = g_hash_table_new_full (mac_hash, mac_equal, g_free, g_free); - g_signal_connect (self, "secrets-cleared", G_CALLBACK (secrets_cleared_cb), NULL); + priv->autoconnect_retries = AUTOCONNECT_RETRIES_DEFAULT; + priv->autoconnect_blocked_reason = NM_DEVICE_STATE_REASON_NONE; + + g_signal_connect (self, NM_CONNECTION_SECRETS_CLEARED, G_CALLBACK (secrets_cleared_cb), NULL); + g_signal_connect (self, NM_CONNECTION_CHANGED, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE)); } static void @@ -1764,6 +2055,11 @@ dispose (GObject *object) goto out; priv->disposed = TRUE; + if (priv->updated_idle_id) { + g_source_remove (priv->updated_idle_id); + priv->updated_idle_id = 0; + } + if (priv->system_secrets) g_object_unref (priv->system_secrets); if (priv->agent_secrets) @@ -1786,9 +2082,7 @@ dispose (GObject *object) if (priv->session_changed_id) g_signal_handler_disconnect (priv->session_monitor, priv->session_changed_id); - g_object_unref (priv->session_monitor); g_object_unref (priv->agent_mgr); - g_object_unref (priv->dbus_mgr); out: G_OBJECT_CLASS (nm_settings_connection_parent_class)->dispose (object); @@ -1798,9 +2092,14 @@ static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (object); + switch (prop_id) { case PROP_VISIBLE: - g_value_set_boolean (value, NM_SETTINGS_CONNECTION_GET_PRIVATE (object)->visible); + g_value_set_boolean (value, priv->visible); + break; + case PROP_UNSAVED: + g_value_set_boolean (value, priv->unsaved); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -1840,7 +2139,19 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class) FALSE, G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_UNSAVED, + g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED, + "Unsaved", + "TRUE when the connection has not yet been saved " + "to permanent storage (eg disk) or when it " + "has been changed but not yet saved.", + FALSE, + G_PARAM_READABLE)); + /* Signals */ + + /* Emitted when the connection is changed for any reason */ signals[UPDATED] = g_signal_new (NM_SETTINGS_CONNECTION_UPDATED, G_TYPE_FROM_CLASS (class), @@ -1850,18 +2161,17 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - signals[REMOVED] = - g_signal_new (NM_SETTINGS_CONNECTION_REMOVED, + /* Emitted when connection is changed from D-Bus */ + signals[UPDATED_BY_USER] = + g_signal_new (NM_SETTINGS_CONNECTION_UPDATED_BY_USER, G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, + 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - /* Not exported */ - signals[UNREGISTER] = - g_signal_new ("unregister", + signals[REMOVED] = + g_signal_new (NM_SETTINGS_CONNECTION_REMOVED, G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_FIRST, 0, @@ -1869,6 +2179,7 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (class), - &dbus_glib_nm_settings_connection_object_info); + nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), + G_TYPE_FROM_CLASS (class), + &dbus_glib_nm_settings_connection_object_info); } |