diff options
| author | Michael Biebl <biebl@debian.org> | 2010-11-29 19:00:22 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2010-11-29 19:00:22 +0100 |
| commit | a6b89d35fabd9f5934b84d7cde22e9268f92c3d3 (patch) | |
| tree | 9e44755880d5933fcc90c6008d4585a76298a1bd /src/nm-manager.c | |
| parent | 9fae01fa351805b2903e535736e06971bc0b925e (diff) | |
Imported Upstream version 0.8.2 upstream/0.8.2
Diffstat (limited to 'src/nm-manager.c')
| -rw-r--r-- | src/nm-manager.c | 2299 |
1 files changed, 1847 insertions, 452 deletions
diff --git a/src/nm-manager.c b/src/nm-manager.c index 7d46285b..eb393fac 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -19,6 +19,8 @@ * Copyright (C) 2007 - 2010 Red Hat, Inc. */ +#include <config.h> + #include <netinet/ether.h> #include <string.h> #include <dbus/dbus-glib-lowlevel.h> @@ -54,25 +56,35 @@ #include "nm-secrets-provider-interface.h" #include "nm-settings-interface.h" #include "nm-settings-system-interface.h" +#include "nm-manager-auth.h" #define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd" #define NM_AUTOIP_DBUS_IFACE "org.freedesktop.nm_avahi_autoipd" +#define UPOWER_DBUS_SERVICE "org.freedesktop.UPower" + static gboolean impl_manager_get_devices (NMManager *manager, GPtrArray **devices, GError **err); static void impl_manager_activate_connection (NMManager *manager, - const char *service_name, - const char *connection_path, - const char *device_path, - const char *specific_object_path, - DBusGMethodInvocation *context); + const char *service_name, + const char *connection_path, + const char *device_path, + const char *specific_object_path, + DBusGMethodInvocation *context); -static gboolean impl_manager_deactivate_connection (NMManager *manager, - const char *connection_path, - GError **error); +static void impl_manager_deactivate_connection (NMManager *manager, + const char *connection_path, + DBusGMethodInvocation *context); -static gboolean impl_manager_sleep (NMManager *manager, gboolean sleep, GError **err); +static void impl_manager_sleep (NMManager *manager, + gboolean do_sleep, + DBusGMethodInvocation *context); -static gboolean impl_manager_enable (NMManager *manager, gboolean enable, GError **err); +static void impl_manager_enable (NMManager *manager, + gboolean enable, + DBusGMethodInvocation *context); + +static void impl_manager_get_permissions (NMManager *manager, + DBusGMethodInvocation *context); static gboolean impl_manager_set_logging (NMManager *manager, const char *level, @@ -81,14 +93,12 @@ static gboolean impl_manager_set_logging (NMManager *manager, /* Legacy 0.6 compatibility interface */ -static gboolean impl_manager_legacy_sleep (NMManager *manager, GError **err); -static gboolean impl_manager_legacy_wake (NMManager *manager, GError **err); +static void impl_manager_legacy_sleep (NMManager *manager, DBusGMethodInvocation *context); +static void impl_manager_legacy_wake (NMManager *manager, DBusGMethodInvocation *context); static gboolean impl_manager_legacy_state (NMManager *manager, guint32 *state, GError **err); #include "nm-manager-glue.h" -static void user_destroy_connections (NMManager *manager); - static void connection_added_default_handler (NMManager *manager, NMConnection *connection, NMConnectionScope scope); @@ -133,25 +143,52 @@ static NMDevice *find_device_by_iface (NMManager *self, const gchar *iface); static GSList * remove_one_device (NMManager *manager, GSList *list, NMDevice *device, - gboolean quitting, - gboolean force_unmanage); + gboolean quitting); static NMDevice *nm_manager_get_device_by_udi (NMManager *manager, const char *udi); +/* Fix for polkit 0.97 and later */ +#if !HAVE_POLKIT_AUTHORITY_GET_SYNC +static inline PolkitAuthority * +polkit_authority_get_sync (GCancellable *cancellable, GError **error) +{ + PolkitAuthority *authority; + + authority = polkit_authority_get (); + if (!authority) + g_set_error (error, 0, 0, "failed to get the PolicyKit authority"); + return authority; +} +#endif + #define SSD_POKE_INTERVAL 120 #define ORIGDEV_TAG "originating-device" -typedef struct { +typedef struct PendingActivation PendingActivation; +typedef void (*PendingActivationFunc) (PendingActivation *pending, + GError *error); + +struct PendingActivation { + NMManager *manager; + DBusGMethodInvocation *context; + PolkitAuthority *authority; + PendingActivationFunc callback; + NMAuthChain *chain; + + gboolean have_connection; + gboolean authorized; + NMConnectionScope scope; char *connection_path; char *specific_object_path; char *device_path; guint timeout_id; -} PendingConnectionInfo; +}; typedef struct { - gboolean enabled; + gboolean user_enabled; + gboolean sw_enabled; gboolean hw_enabled; RfKillType rtype; const char *desc; @@ -174,6 +211,8 @@ typedef struct { GHashTable *user_connections; DBusGProxy *user_proxy; + NMAuthCallResult user_con_perm; + NMAuthCallResult user_net_perm; GHashTable *system_connections; NMSysconfigSettings *sys_settings; @@ -181,7 +220,7 @@ typedef struct { GSList *secrets_calls; - PendingConnectionInfo *pending_connection_info; + GSList *pending_activations; RadioState radio_states[RFKILL_TYPE_MAX]; gboolean sleeping; @@ -195,6 +234,18 @@ typedef struct { guint modem_removed_id; DBusGProxy *aipd_proxy; + DBusGProxy *upower_proxy; + + PolkitAuthority *authority; + guint auth_changed_id; + GSList *auth_chains; + + /* Firmware dir monitor */ + GFileMonitor *fw_monitor; + guint fw_monitor_id; + guint fw_changed_id; + + guint timestamp_update_id; gboolean disposed; } NMManagerPrivate; @@ -215,6 +266,8 @@ enum { CONNECTION_ADDED, CONNECTION_UPDATED, CONNECTION_REMOVED, + CHECK_PERMISSIONS, + USER_PERMISSIONS_CHANGED, LAST_SIGNAL }; @@ -223,6 +276,7 @@ static guint signals[LAST_SIGNAL] = { 0 }; enum { PROP_0, + PROP_VERSION, PROP_STATE, PROP_NETWORKING_ENABLED, PROP_WIRELESS_ENABLED, @@ -339,8 +393,7 @@ modem_added (NMModemManager *modem_manager, priv->devices = remove_one_device (NM_MANAGER (user_data), priv->devices, replace_device, - FALSE, - TRUE); + FALSE); } /* Give Bluetooth DUN devices first chance to claim the modem */ @@ -411,6 +464,45 @@ nm_manager_update_state (NMManager *manager) } static void +ignore_cb (NMSettingsConnectionInterface *connection, GError *error, gpointer user_data) +{ +} + +static void +update_active_connection_timestamp (NMManager *manager, NMDevice *device) +{ + NMActRequest *req; + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingsConnectionInterface *connection_interface; + NMManagerPrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (device)); + + priv = NM_MANAGER_GET_PRIVATE (manager); + req = nm_device_get_act_request (device); + if (!req) + return; + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + if (nm_connection_get_scope (connection) != NM_CONNECTION_SCOPE_SYSTEM) + return; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (NM_CONNECTION (connection), NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL), NULL); + + if (nm_setting_connection_get_read_only (s_con)) + return; + + connection_interface = nm_settings_interface_get_connection_by_path (NM_SETTINGS_INTERFACE (priv->sys_settings), + nm_connection_get_path (connection)); + nm_settings_connection_interface_update (connection_interface, ignore_cb, NULL); +} + +static void manager_device_state_changed (NMDevice *device, NMDeviceState new_state, NMDeviceState old_state, @@ -432,6 +524,9 @@ manager_device_state_changed (NMDevice *device, } nm_manager_update_state (manager); + + if (new_state == NM_DEVICE_STATE_ACTIVATED) + update_active_connection_timestamp (manager, device); } /* Removes a device from a device list; returns the start of the new device list */ @@ -439,20 +534,22 @@ static GSList * remove_one_device (NMManager *manager, GSList *list, NMDevice *device, - gboolean quitting, - gboolean force_unmanage) + gboolean quitting) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); if (nm_device_get_managed (device)) { - gboolean unmanage = !quitting; - - /* Don't unmanage active assume-connection-capable devices at shutdown */ - if ( nm_device_interface_can_assume_connection (NM_DEVICE_INTERFACE (device)) - && nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) - unmanage = FALSE; + /* When quitting, we want to leave up interfaces & connections + * that can be taken over again (ie, "assumed") when NM restarts + * so that '/etc/init.d/NetworkManager restart' will not distrupt + * networking for interfaces that support connection assumption. + * All other devices get unmanaged when NM quits so that their + * connections get torn down and the interface is deactivated. + */ - if (unmanage || force_unmanage) + if ( !nm_device_interface_can_assume_connections (NM_DEVICE_INTERFACE (device)) + || (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED) + || !quitting) nm_device_set_managed (device, FALSE, NM_DEVICE_STATE_REASON_REMOVED); } @@ -486,7 +583,7 @@ modem_removed (NMModemManager *modem_manager, /* Otherwise remove the standalone modem */ found = nm_manager_get_device_by_udi (self, nm_modem_get_path (modem)); if (found) - priv->devices = remove_one_device (self, priv->devices, found, FALSE, TRUE); + priv->devices = remove_one_device (self, priv->devices, found, FALSE); } static void @@ -560,19 +657,237 @@ emit_removed (gpointer key, gpointer value, gpointer user_data) } static void -pending_connection_info_destroy (PendingConnectionInfo *info) +nm_manager_pending_activation_remove (NMManager *self, + PendingActivation *pending) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + + priv->pending_activations = g_slist_remove (priv->pending_activations, pending); +} + +static PendingActivation * +pending_activation_new (NMManager *manager, + PolkitAuthority *authority, + DBusGMethodInvocation *context, + const char *device_path, + NMConnectionScope scope, + const char *connection_path, + const char *specific_object_path, + PendingActivationFunc callback) +{ + PendingActivation *pending; + + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (authority != NULL, NULL); + g_return_val_if_fail (context != NULL, NULL); + g_return_val_if_fail (device_path != NULL, NULL); + g_return_val_if_fail (connection_path != NULL, NULL); + + pending = g_slice_new0 (PendingActivation); + pending->manager = manager; + pending->authority = authority; + pending->context = context; + pending->callback = callback; + + pending->device_path = g_strdup (device_path); + pending->scope = scope; + pending->connection_path = g_strdup (connection_path); + + /* "/" is special-cased to NULL to get through D-Bus */ + if (specific_object_path && strcmp (specific_object_path, "/")) + pending->specific_object_path = g_strdup (specific_object_path); + + return pending; +} + +static void +pending_auth_user_done (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + PendingActivation *pending = user_data; + NMAuthCallResult result; + + pending->chain = NULL; + + if (error) { + pending->callback (pending, error); + goto out; + } + + /* Caller has had a chance to obtain authorization, so we only need to + * check for 'yes' here. + */ + result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS)); + if (result != NM_AUTH_CALL_RESULT_YES) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Not authorized to use user connections."); + pending->callback (pending, error); + g_error_free (error); + } else + pending->callback (pending, NULL); + +out: + nm_auth_chain_unref (chain); +} + +static void +pending_auth_net_done (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + PendingActivation *pending = user_data; + NMAuthCallResult result; + + pending->chain = NULL; + + if (error) { + pending->callback (pending, error); + goto out; + } + + /* Caller has had a chance to obtain authorization, so we only need to + * check for 'yes' here. + */ + result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL)); + if (result != NM_AUTH_CALL_RESULT_YES) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Not authorized to control networking."); + pending->callback (pending, error); + g_error_free (error); + goto out; + } + + if (pending->scope == NM_CONNECTION_SCOPE_SYSTEM) { + /* System connection and the user is authorized for that if they have + * the network-control permission. + */ + pending->callback (pending, NULL); + } else { + g_assert (pending->scope == NM_CONNECTION_SCOPE_USER); + + /* User connection, check the 'use-user-connections' permission */ + pending->chain = nm_auth_chain_new (pending->authority, + pending->context, + NULL, + pending_auth_user_done, + pending); + nm_auth_chain_add_call (pending->chain, + NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, + TRUE); + } + +out: + nm_auth_chain_unref (chain); +} + +static gboolean +check_user_authorized (NMDBusManager *dbus_mgr, + DBusGProxy *user_proxy, + DBusGMethodInvocation *context, + NMConnectionScope scope, + gulong *out_sender_uid, + const char **out_error_desc) +{ + g_return_val_if_fail (dbus_mgr != NULL, FALSE); + g_return_val_if_fail (context != NULL, FALSE); + g_return_val_if_fail (out_sender_uid != NULL, FALSE); + g_return_val_if_fail (out_error_desc != NULL, FALSE); + + *out_sender_uid = G_MAXULONG; + + /* Get the UID */ + if (!nm_auth_get_caller_uid (context, dbus_mgr, out_sender_uid, out_error_desc)) + return FALSE; + + /* root gets to do anything */ + if (0 == *out_sender_uid) + return TRUE; + + /* Check whether the UID is authorized for user connections */ + if ( scope == NM_CONNECTION_SCOPE_USER + && !nm_auth_uid_authorized (*out_sender_uid, + dbus_mgr, + user_proxy, + out_error_desc)) + return FALSE; + + return TRUE; +} + +static void +pending_activation_check_authorized (PendingActivation *pending, + NMDBusManager *dbus_mgr, + DBusGProxy *user_proxy) { - if (!info) + const char *error_desc = NULL; + gulong sender_uid = G_MAXULONG; + GError *error; + + g_return_if_fail (pending != NULL); + g_return_if_fail (dbus_mgr != NULL); + + if (!check_user_authorized (dbus_mgr, + user_proxy, + pending->context, + pending->scope, + &sender_uid, + &error_desc)) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + error_desc); + pending->callback (pending, error); + g_error_free (error); return; + } - if (info->timeout_id) - g_source_remove (info->timeout_id); + /* Yay for root */ + if (0 == sender_uid) { + pending->callback (pending, NULL); + return; + } - g_free (info->connection_path); - g_free (info->specific_object_path); - g_free (info->device_path); + /* First check if the user is allowed to use networking at all, giving + * the user a chance to authenticate to gain the permission. + */ + pending->chain = nm_auth_chain_new (pending->authority, + pending->context, + NULL, + pending_auth_net_done, + pending); + g_assert (pending->chain); + nm_auth_chain_add_call (pending->chain, + NM_AUTH_PERMISSION_NETWORK_CONTROL, + TRUE); +} - g_slice_free (PendingConnectionInfo, info); +static void +pending_activation_destroy (PendingActivation *pending, + GError *error, + const char *ac_path) +{ + g_return_if_fail (pending != NULL); + + if (pending->timeout_id) + g_source_remove (pending->timeout_id); + g_free (pending->connection_path); + g_free (pending->specific_object_path); + g_free (pending->device_path); + + if (error) + dbus_g_method_return_error (pending->context, error); + else if (ac_path) + dbus_g_method_return (pending->context, ac_path); + + if (pending->chain) + nm_auth_chain_unref (pending->chain); + + memset (pending, 0, sizeof (PendingActivation)); + g_slice_free (PendingActivation, pending); } static GPtrArray * @@ -632,27 +947,34 @@ remove_connection (NMManager *manager, /*******************************************************************/ static void -user_destroy_connections (NMManager *manager) +user_proxy_cleanup (NMManager *self, gboolean resync_bt) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); if (priv->user_connections) { - g_hash_table_foreach (priv->user_connections, emit_removed, manager); + g_hash_table_foreach (priv->user_connections, emit_removed, self); g_hash_table_remove_all (priv->user_connections); } + priv->user_net_perm = NM_AUTH_CALL_RESULT_UNKNOWN; + priv->user_con_perm = NM_AUTH_CALL_RESULT_UNKNOWN; + if (priv->user_proxy) { g_object_unref (priv->user_proxy); priv->user_proxy = NULL; } + + if (resync_bt) { + /* Resync BT devices since they are generated from connections */ + bluez_manager_resync_devices (self); + } } typedef struct GetSettingsInfo { NMManager *manager; NMConnection *connection; DBusGProxy *proxy; - DBusGProxyCall *call; - GSList **calls; + guint32 *calls; } GetSettingsInfo; static void @@ -664,10 +986,9 @@ free_get_settings_info (gpointer data) * send out the connections-added signal. */ if (info->calls) { - *(info->calls) = g_slist_remove (*(info->calls), info->call); - if (g_slist_length (*(info->calls)) == 0) { - g_slist_free (*(info->calls)); - g_slice_free (GSList, (gpointer) info->calls); + (*info->calls)--; + if (*info->calls == 0) { + g_slice_free (guint32, (gpointer) info->calls); g_signal_emit (info->manager, signals[CONNECTIONS_ADDED], 0, NM_CONNECTION_SCOPE_USER); /* Update the Bluetooth connections for all the new connections */ @@ -838,20 +1159,18 @@ user_connection_updated_cb (DBusGProxy *proxy, } static void -user_internal_new_connection_cb (DBusGProxy *proxy, +user_internal_new_connection_cb (NMManager *manager, const char *path, - NMManager *manager, - GSList **calls) + guint32 *counter) { - struct GetSettingsInfo *info; + GetSettingsInfo *info; DBusGProxy *con_proxy; DBusGConnection *g_connection; - DBusGProxyCall *call; NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr); con_proxy = dbus_g_proxy_new_for_name (g_connection, - dbus_g_proxy_get_bus_name (proxy), + NM_DBUS_SERVICE_USER_SETTINGS, path, NM_DBUS_IFACE_SETTINGS_CONNECTION); if (!con_proxy) { @@ -875,16 +1194,16 @@ user_internal_new_connection_cb (DBusGProxy *proxy, info = g_slice_new0 (GetSettingsInfo); info->manager = g_object_ref (manager); - info->calls = calls; - call = dbus_g_proxy_begin_call (con_proxy, "GetSettings", - user_connection_get_settings_cb, - info, - free_get_settings_info, - G_TYPE_INVALID); - info->call = call; info->proxy = con_proxy; - if (info->calls) - *(info->calls) = g_slist_prepend (*(info->calls), call); + if (counter) { + info->calls = counter; + (*info->calls)++; + } + dbus_g_proxy_begin_call (con_proxy, "GetSettings", + user_connection_get_settings_cb, + info, + free_get_settings_info, + G_TYPE_INVALID); } static void @@ -895,7 +1214,7 @@ user_list_connections_cb (DBusGProxy *proxy, NMManager *manager = NM_MANAGER (user_data); GError *err = NULL; GPtrArray *ops; - GSList **calls = NULL; + guint32 *counter = NULL; int i; if (!dbus_g_proxy_end_call (proxy, call_id, &err, @@ -903,72 +1222,166 @@ user_list_connections_cb (DBusGProxy *proxy, G_TYPE_INVALID)) { nm_log_warn (LOGD_USER_SET, "couldn't retrieve connections: %s", err && err->message ? err->message : "(unknown)"); - g_error_free (err); - goto out; + g_clear_error (&err); + return; } /* Keep track of all calls made here; don't want to emit connection-added for * each one, but emit connections-added when they are all done. */ - calls = g_slice_new0 (GSList *); - + counter = g_slice_new0 (guint32); for (i = 0; i < ops->len; i++) { char *op = g_ptr_array_index (ops, i); - user_internal_new_connection_cb (proxy, op, manager, calls); + user_internal_new_connection_cb (manager, op, counter); g_free (op); } - g_ptr_array_free (ops, TRUE); +} -out: - return; +static void +user_proxy_destroyed_cb (DBusGProxy *proxy, NMManager *self) +{ + nm_log_dbg (LOGD_USER_SET, "Removing user connections..."); + + /* At this point the user proxy is already being disposed */ + NM_MANAGER_GET_PRIVATE (self)->user_proxy = NULL; + + /* User Settings service disappeared; throw away user connections */ + user_proxy_cleanup (self, TRUE); } static void user_new_connection_cb (DBusGProxy *proxy, const char *path, gpointer user_data) { - user_internal_new_connection_cb (proxy, path, NM_MANAGER (user_data), NULL); + user_internal_new_connection_cb (NM_MANAGER (user_data), path, NULL); +} + +static gboolean +user_settings_authorized (NMManager *self, NMAuthChain *chain) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMAuthCallResult old_net_perm = priv->user_net_perm; + NMAuthCallResult old_con_perm = priv->user_con_perm; + + /* If the user could potentially get authorization to use networking and/or + * to use user connections, the user settings service is authorized. + */ + priv->user_net_perm = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL)); + priv->user_con_perm = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS)); + + nm_log_dbg (LOGD_USER_SET, "User connections permissions: net %d, con %d", + priv->user_net_perm, priv->user_con_perm); + + if (old_net_perm != priv->user_net_perm || old_con_perm != priv->user_con_perm) + g_signal_emit (self, signals[USER_PERMISSIONS_CHANGED], 0); + + /* If the user can't control the network they certainly aren't allowed + * to provide user connections. + */ + if ( priv->user_net_perm == NM_AUTH_CALL_RESULT_UNKNOWN + || priv->user_net_perm == NM_AUTH_CALL_RESULT_NO) + return FALSE; + + /* And of course if they aren't allowed to use user connections, they can't + * provide them either. + */ + if ( priv->user_con_perm == NM_AUTH_CALL_RESULT_UNKNOWN + || priv->user_con_perm == NM_AUTH_CALL_RESULT_NO) + return FALSE; + + return TRUE; } static void -user_query_connections (NMManager *manager) +user_proxy_auth_done (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) { - NMManagerPrivate *priv; - DBusGProxyCall *call; - DBusGConnection *g_connection; + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + gboolean authorized = FALSE; - g_return_if_fail (NM_IS_MANAGER (manager)); + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); - priv = NM_MANAGER_GET_PRIVATE (manager); - if (!priv->user_proxy) { - g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr); - priv->user_proxy = dbus_g_proxy_new_for_name (g_connection, - NM_DBUS_SERVICE_USER_SETTINGS, - NM_DBUS_PATH_SETTINGS, - NM_DBUS_IFACE_SETTINGS); - if (!priv->user_proxy) { - nm_log_err (LOGD_USER_SET, "could not init user settings proxy"); - return; - } + if (error) { + nm_log_warn (LOGD_USER_SET, "User connections unavailable: (%d) %s", + error->code, error->message ? error->message : "(unknown)"); + } else + authorized = user_settings_authorized (self, chain); - dbus_g_proxy_add_signal (priv->user_proxy, - "NewConnection", - DBUS_TYPE_G_OBJECT_PATH, - G_TYPE_INVALID); + if (authorized) { + /* If authorized, finish setting up the user settings service proxy */ + nm_log_dbg (LOGD_USER_SET, "Requesting user connections..."); + + authorized = TRUE; + dbus_g_proxy_add_signal (priv->user_proxy, + "NewConnection", + DBUS_TYPE_G_OBJECT_PATH, + G_TYPE_INVALID); dbus_g_proxy_connect_signal (priv->user_proxy, "NewConnection", - G_CALLBACK (user_new_connection_cb), - manager, - NULL); + G_CALLBACK (user_new_connection_cb), + self, + NULL); + + /* Clean up when the user settings proxy goes away */ + g_signal_connect (priv->user_proxy, "destroy", + G_CALLBACK (user_proxy_destroyed_cb), + self); + + /* Request user connections */ + dbus_g_proxy_begin_call (priv->user_proxy, "ListConnections", + user_list_connections_cb, + self, + NULL, + G_TYPE_INVALID); + } else { + /* Otherwise, we ignore the user settings service completely */ + user_proxy_cleanup (self, TRUE); + } + + nm_auth_chain_unref (chain); +} + +static void +user_proxy_init (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + DBusGConnection *bus; + NMAuthChain *chain; + GError *error = NULL; + + g_return_if_fail (self != NULL); + g_return_if_fail (priv->user_proxy == NULL); + + bus = nm_dbus_manager_get_connection (priv->dbus_mgr); + priv->user_proxy = dbus_g_proxy_new_for_name_owner (bus, + NM_DBUS_SERVICE_USER_SETTINGS, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_IFACE_SETTINGS, + &error); + if (!priv->user_proxy) { + nm_log_err (LOGD_USER_SET, "could not init user settings proxy: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + return; } - /* grab connections */ - call = dbus_g_proxy_begin_call (priv->user_proxy, "ListConnections", - user_list_connections_cb, - manager, - NULL, - G_TYPE_INVALID); + /* Kick off some PolicyKit authorization requests to figure out what + * permissions this user settings service has. + */ + chain = nm_auth_chain_new (priv->authority, + NULL, + priv->user_proxy, + user_proxy_auth_done, + self); + priv->auth_chains = g_slist_prepend (priv->auth_chains, chain); + + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, FALSE); } /*******************************************************************/ @@ -1042,6 +1455,7 @@ system_internal_new_connection (NMManager *manager, path = nm_connection_get_path (NM_CONNECTION (connection)); g_hash_table_insert (priv->system_connections, g_strdup (path), g_object_ref (connection)); + g_signal_emit (manager, signals[CONNECTION_ADDED], 0, connection, NM_CONNECTION_SCOPE_SYSTEM); } static void @@ -1150,14 +1564,10 @@ nm_manager_name_owner_changed (NMDBusManager *mgr, gboolean new_owner_good = (new && (strlen (new) > 0)); if (strcmp (name, NM_DBUS_SERVICE_USER_SETTINGS) == 0) { - if (!old_owner_good && new_owner_good) { - /* User Settings service appeared, update stuff */ - user_query_connections (manager); - } else { - /* User Settings service disappeared, throw them away (?) */ - user_destroy_connections (manager); - bluez_manager_resync_devices (manager); - } + if (!old_owner_good && new_owner_good) + user_proxy_init (manager); + else + user_proxy_cleanup (manager, TRUE); } } @@ -1211,50 +1621,40 @@ write_value_to_state_file (const char *filename, return ret; } +static gboolean +radio_enabled_for_rstate (RadioState *rstate) +{ + return rstate->user_enabled && rstate->sw_enabled && rstate->hw_enabled; +} + +static gboolean +radio_enabled_for_type (NMManager *self, RfKillType rtype) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + + return radio_enabled_for_rstate (&priv->radio_states[rtype]); +} + static void -manager_set_radio_enabled (NMManager *manager, - RadioState *rstate, - gboolean enabled) +manager_update_radio_enabled (NMManager *self, RadioState *rstate) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GSList *iter; - GError *error = NULL; /* Do nothing for radio types not yet implemented */ if (!rstate->prop) return; - if (rstate->enabled == enabled) - return; - - /* Can't set wireless enabled if it's disabled in hardware */ - if (!rstate->hw_enabled && enabled) - return; - - rstate->enabled = enabled; - - g_object_notify (G_OBJECT (manager), rstate->prop); - - /* Update enabled key in state file */ - if (priv->state_file) { - if (!write_value_to_state_file (priv->state_file, - "main", rstate->key, - G_TYPE_BOOLEAN, (gpointer) &enabled, - &error)) { - nm_log_warn (LOGD_CORE, "writing to state file %s failed: (%d) %s.", - priv->state_file, - error ? error->code : -1, - (error && error->message) ? error->message : "unknown"); - } - } + g_object_notify (G_OBJECT (self), rstate->prop); /* Don't touch devices if asleep/networking disabled */ - if (manager_sleeping (manager)) + if (manager_sleeping (self)) return; /* enable/disable wireless devices as required */ for (iter = priv->devices; iter; iter = iter->next) { RfKillType devtype = RFKILL_TYPE_UNKNOWN; + gboolean enabled = radio_enabled_for_rstate (rstate); g_object_get (G_OBJECT (iter->data), NM_DEVICE_INTERFACE_RFKILL_TYPE, &devtype, NULL); if (devtype == rstate->rtype) { @@ -1377,6 +1777,21 @@ nm_manager_get_modem_enabled_state (NMManager *self) } static void +update_rstate_from_rfkill (RadioState *rstate, RfKillState rfkill) +{ + if (rfkill == RFKILL_UNBLOCKED) { + rstate->sw_enabled = TRUE; + rstate->hw_enabled = TRUE; + } else if (rfkill == RFKILL_SOFT_BLOCKED) { + rstate->sw_enabled = FALSE; + rstate->hw_enabled = TRUE; + } else if (rfkill == RFKILL_HARD_BLOCKED) { + rstate->sw_enabled = FALSE; + rstate->hw_enabled = FALSE; + } +} + +static void manager_rfkill_update_one_type (NMManager *self, RadioState *rstate, RfKillType rtype) @@ -1385,7 +1800,12 @@ manager_rfkill_update_one_type (NMManager *self, RfKillState udev_state = RFKILL_UNBLOCKED; RfKillState other_state = RFKILL_UNBLOCKED; RfKillState composite; - gboolean new_e = TRUE, new_he = TRUE; + gboolean old_enabled, new_enabled, old_rfkilled, new_rfkilled; + gboolean old_hwe; + + old_enabled = radio_enabled_for_rstate (rstate); + old_rfkilled = rstate->hw_enabled && rstate->sw_enabled; + old_hwe = rstate->hw_enabled; udev_state = nm_udev_manager_get_rfkill_state (priv->udev_mgr, rtype); @@ -1400,38 +1820,31 @@ manager_rfkill_update_one_type (NMManager *self, else composite = RFKILL_UNBLOCKED; - switch (composite) { - case RFKILL_UNBLOCKED: - new_e = TRUE; - new_he = TRUE; - break; - case RFKILL_SOFT_BLOCKED: - new_e = FALSE; - new_he = TRUE; - break; - case RFKILL_HARD_BLOCKED: - new_e = FALSE; - new_he = FALSE; - break; - default: - break; - } + update_rstate_from_rfkill (rstate, composite); if (rstate->desc) { - nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d enabled %d", - rstate->desc, new_he, new_e); + nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d sw-enabled %d", + rstate->desc, rstate->hw_enabled, rstate->sw_enabled); } - if (new_he != rstate->hw_enabled) { + /* Log new killswitch state */ + new_rfkilled = rstate->hw_enabled && rstate->sw_enabled; + if (old_rfkilled != new_rfkilled) { nm_log_info (LOGD_RFKILL, "%s now %s by radio killswitch", rstate->desc, - (new_e && new_he) ? "enabled" : "disabled"); + new_rfkilled ? "enabled" : "disabled"); + } - rstate->hw_enabled = new_he; + /* Send out property changed signal for HW enabled */ + if (rstate->hw_enabled != old_hwe) { if (rstate->hw_prop) g_object_notify (G_OBJECT (self), rstate->hw_prop); } - manager_set_radio_enabled (self, rstate, new_e); + + /* And finally update the actual device radio state itself */ + new_enabled = radio_enabled_for_rstate (rstate); + if (new_enabled != old_enabled) + manager_update_radio_enabled (self, rstate); } static void @@ -1464,6 +1877,172 @@ manager_modem_enabled_changed (NMModem *device, gpointer user_data) nm_manager_rfkill_update (NM_MANAGER (user_data), RFKILL_TYPE_WWAN); } +static GError * +deactivate_disconnect_check_error (GError *auth_error, + NMAuthCallResult result, + const char *detail) +{ + if (auth_error) { + nm_log_dbg (LOGD_CORE, "%s request failed: %s", detail, auth_error->message); + return g_error_new (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "%s request failed: %s", + detail, auth_error->message); + } else if (result != NM_AUTH_CALL_RESULT_YES) { + return g_error_new (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Not authorized to %s user connections", + detail); + } + return NULL; +} + +static void +disconnect_user_auth_done_cb (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GError *ret_error = NULL; + NMAuthCallResult result; + NMDevice *device; + + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + + result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS)); + ret_error = deactivate_disconnect_check_error (error, result, "Disconnect"); + if (!ret_error) { + /* Everything authorized, deactivate the connection */ + device = nm_auth_chain_get_data (chain, "device"); + if (nm_device_interface_disconnect (NM_DEVICE_INTERFACE (device), &ret_error)) + dbus_g_method_return (context); + } + + if (ret_error) + dbus_g_method_return_error (context, ret_error); + g_clear_error (&ret_error); + + nm_auth_chain_unref (chain); +} + +static void +disconnect_net_auth_done_cb (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GError *ret_error = NULL; + NMAuthCallResult result; + NMConnectionScope scope; + NMDevice *device; + + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + + result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL)); + ret_error = deactivate_disconnect_check_error (error, result, "Disconnect"); + if (ret_error) { + dbus_g_method_return_error (context, ret_error); + g_error_free (ret_error); + goto done; + } + + /* If it's a system connection, we're done */ + device = nm_auth_chain_get_data (chain, "device"); + scope = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "scope")); + if (scope == NM_CONNECTION_SCOPE_USER) { + NMAuthChain *user_chain; + + /* It's a user connection, so we need to ensure the caller is + * authorized to manipulate user connections. + */ + user_chain = nm_auth_chain_new (priv->authority, context, NULL, disconnect_user_auth_done_cb, self); + g_assert (user_chain); + priv->auth_chains = g_slist_append (priv->auth_chains, user_chain); + + nm_auth_chain_set_data (user_chain, "device", g_object_ref (device), g_object_unref); + nm_auth_chain_set_data (user_chain, "scope", GUINT_TO_POINTER (scope), NULL); + nm_auth_chain_add_call (user_chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, TRUE); + } else { + if (!nm_device_interface_disconnect (NM_DEVICE_INTERFACE (device), &ret_error)) { + dbus_g_method_return_error (context, ret_error); + g_clear_error (&ret_error); + } else + dbus_g_method_return (context); + } + +done: + nm_auth_chain_unref (chain); +} + +static void +manager_device_disconnect_request (NMDevice *device, + DBusGMethodInvocation *context, + NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMActRequest *req; + NMConnection *connection; + GError *error = NULL; + NMConnectionScope scope; + gulong sender_uid = G_MAXULONG; + const char *error_desc = NULL; + + req = nm_device_get_act_request (device); + if (!req) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "This device is not active"); + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + /* Need to check the caller's permissions and stuff before we can + * deactivate the connection. + */ + scope = nm_connection_get_scope (connection); + if (!check_user_authorized (priv->dbus_mgr, + priv->user_proxy, + context, + scope, + &sender_uid, + &error_desc)) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + error_desc); + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + /* Yay for root */ + if (0 == sender_uid) { + if (!nm_device_interface_disconnect (NM_DEVICE_INTERFACE (device), &error)) { + dbus_g_method_return_error (context, error); + g_clear_error (&error); + } else + dbus_g_method_return (context); + } else { + NMAuthChain *chain; + + /* Otherwise validate the user request */ + chain = nm_auth_chain_new (priv->authority, context, NULL, disconnect_net_auth_done_cb, self); + g_assert (chain); + priv->auth_chains = g_slist_append (priv->auth_chains, chain); + + nm_auth_chain_set_data (chain, "device", g_object_ref (device), g_object_unref); + nm_auth_chain_set_data (chain, "scope", GUINT_TO_POINTER (scope), NULL); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); + } +} + static void add_device (NMManager *self, NMDevice *device) { @@ -1475,7 +2054,7 @@ add_device (NMManager *self, NMDevice *device) NMConnection *existing = NULL; GHashTableIter iter; gpointer value; - gboolean managed = FALSE; + gboolean managed = FALSE, enabled = FALSE; iface = nm_device_get_ip_iface (device); g_assert (iface); @@ -1491,6 +2070,10 @@ add_device (NMManager *self, NMDevice *device) G_CALLBACK (manager_device_state_changed), self); + g_signal_connect (device, NM_DEVICE_INTERFACE_DISCONNECT_REQUEST, + G_CALLBACK (manager_device_disconnect_request), + self); + if (NM_IS_DEVICE_WIFI (device)) { /* Attach to the access-point-added signal so that the manager can fill * non-SSID-broadcasting APs with an SSID. @@ -1510,14 +2093,15 @@ add_device (NMManager *self, NMDevice *device) * then set this device's rfkill state based on the global state. */ nm_manager_rfkill_update (self, RFKILL_TYPE_WLAN); - nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device), - priv->radio_states[RFKILL_TYPE_WLAN].enabled); + enabled = radio_enabled_for_type (self, RFKILL_TYPE_WLAN); + nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device), enabled); } else if (NM_IS_DEVICE_MODEM (device)) { g_signal_connect (device, NM_DEVICE_MODEM_ENABLE_CHANGED, G_CALLBACK (manager_modem_enabled_changed), self); nm_manager_rfkill_update (self, RFKILL_TYPE_WWAN); + enabled = radio_enabled_for_type (self, RFKILL_TYPE_WWAN); /* Until we start respecting WWAN rfkill switches the modem itself * is the source of the enabled/disabled state, so the manager shouldn't * touch it here. @@ -1545,7 +2129,7 @@ add_device (NMManager *self, NMDevice *device) /* Check if we should assume the device's active connection by matching its * config with an existing system connection. */ - if (nm_device_interface_can_assume_connection (NM_DEVICE_INTERFACE (device))) { + if (nm_device_interface_can_assume_connections (NM_DEVICE_INTERFACE (device))) { GSList *connections = NULL; g_hash_table_iter_init (&iter, priv->system_connections); @@ -1705,7 +2289,7 @@ bluez_manager_resync_devices (NMManager *self) priv->devices = keep; while (g_slist_length (gone)) - gone = remove_one_device (self, gone, NM_DEVICE (gone->data), FALSE, TRUE); + gone = remove_one_device (self, gone, NM_DEVICE (gone->data), FALSE); } else { g_slist_free (keep); g_slist_free (gone); @@ -1774,7 +2358,7 @@ bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr, NMDevice *device = NM_DEVICE (iter->data); if (!strcmp (nm_device_get_udi (device), object_path)) { - priv->devices = remove_one_device (self, priv->devices, device, FALSE, TRUE); + priv->devices = remove_one_device (self, priv->devices, device, FALSE); break; } } @@ -1843,7 +2427,7 @@ udev_device_removed_cb (NMUdevManager *manager, ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX"); device = find_device_by_ifindex (self, ifindex); if (device) - priv->devices = remove_one_device (self, priv->devices, device, FALSE, TRUE); + priv->devices = remove_one_device (self, priv->devices, device, FALSE); } static void @@ -2240,25 +2824,21 @@ internal_activate_device (NMManager *manager, static gboolean wait_for_connection_expired (gpointer data) { - NMManager *manager = NM_MANAGER (data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - PendingConnectionInfo *info = priv->pending_connection_info; + PendingActivation *pending = data; GError *error = NULL; - g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (pending != NULL, FALSE); - g_set_error (&error, - NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION, - "%s", "Connection was not provided by any settings service"); - nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate (timeout): (%d) %s", - info->scope, info->connection_path, error->code, error->message); - dbus_g_method_return_error (info->context, error); - g_error_free (error); + nm_log_warn (LOGD_CORE, "connection %s (scope %d) failed to activate (timeout)", + pending->connection_path, pending->scope); - info->timeout_id = 0; - pending_connection_info_destroy (priv->pending_connection_info); - priv->pending_connection_info = NULL; + nm_manager_pending_activation_remove (pending->manager, pending); + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "Connection was not provided by any settings service"); + pending_activation_destroy (pending, error, NULL); + g_error_free (error); return FALSE; } @@ -2274,7 +2854,7 @@ nm_manager_activate_connection (NMManager *manager, NMDevice *device = NULL; NMSettingConnection *s_con; NMVPNConnection *vpn_connection; - const char *path; + const char *path = NULL; g_return_val_if_fail (manager != NULL, NULL); g_return_val_if_fail (connection != NULL, NULL); @@ -2331,11 +2911,13 @@ nm_manager_activate_connection (NMManager *manager, req, device, error); - g_signal_connect (vpn_connection, "manager-get-secrets", - G_CALLBACK (provider_get_secrets), manager); - g_signal_connect (vpn_connection, "manager-cancel-secrets", - G_CALLBACK (provider_cancel_secrets), manager); - path = nm_vpn_connection_get_active_connection_path (vpn_connection); + if (vpn_connection) { + g_signal_connect (vpn_connection, "manager-get-secrets", + G_CALLBACK (provider_get_secrets), manager); + g_signal_connect (vpn_connection, "manager-cancel-secrets", + G_CALLBACK (provider_cancel_secrets), manager); + path = nm_vpn_connection_get_active_connection_path (vpn_connection); + } g_object_unref (vpn_manager); } else { NMDeviceState state; @@ -2353,7 +2935,7 @@ nm_manager_activate_connection (NMManager *manager, if (state < NM_DEVICE_STATE_DISCONNECTED) { g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNMANAGED_DEVICE, - "%s", "Device not managed by NetworkManager"); + "%s", "Device not managed by NetworkManager or unavailable"); return NULL; } @@ -2369,229 +2951,145 @@ nm_manager_activate_connection (NMManager *manager, return path; } +static PendingActivation * +nm_manager_pending_activation_find (NMManager *self, + const char *path, + NMConnectionScope scope) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GSList *iter; + + for (iter = priv->pending_activations; iter; iter = g_slist_next (iter)) { + PendingActivation *pending = iter->data; + + if (!strcmp (pending->connection_path, path) && (pending->scope == scope)) + return pending; + } + return NULL; +} + static void -connection_added_default_handler (NMManager *manager, - NMConnection *connection, - NMConnectionScope scope) +check_pending_ready (NMManager *self, PendingActivation *pending) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - PendingConnectionInfo *info = priv->pending_connection_info; - const char *path; + NMConnection *connection; + const char *path = NULL; GError *error = NULL; - if (!info) + if (!pending->have_connection || !pending->authorized) return; - if (scope != info->scope) - return; + /* Ok, we're authorized and the connection is available */ - if (strcmp (info->connection_path, nm_connection_get_path (connection))) - return; + nm_manager_pending_activation_remove (self, pending); - /* Will destroy below; can't be valid during the initial activation start */ - priv->pending_connection_info = NULL; + connection = nm_manager_get_connection_by_object_path (self, + pending->scope, + pending->connection_path); + if (!connection) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "Connection could not be found."); + goto out; + } - path = nm_manager_activate_connection (manager, + path = nm_manager_activate_connection (self, connection, - info->specific_object_path, - info->device_path, + pending->specific_object_path, + pending->device_path, TRUE, &error); - if (path) { - dbus_g_method_return (info->context, path); - g_object_notify (G_OBJECT (manager), NM_MANAGER_ACTIVE_CONNECTIONS); - } else { - dbus_g_method_return_error (info->context, error); + if (!path) { nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate: (%d) %s", - scope, info->connection_path, error->code, error->message); - g_error_free (error); - } + pending->scope, pending->connection_path, error->code, error->message); + } else + g_object_notify (G_OBJECT (pending->manager), NM_MANAGER_ACTIVE_CONNECTIONS); - pending_connection_info_destroy (info); +out: + pending_activation_destroy (pending, error, path); + g_clear_error (&error); } -static gboolean -is_user_request_authorized (NMManager *manager, - DBusGMethodInvocation *context, - GError **error) +static void +connection_added_default_handler (NMManager *self, + NMConnection *connection, + NMConnectionScope scope) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - DBusConnection *connection; - char *sender = NULL; - gulong sender_uid = G_MAXULONG; - DBusError dbus_error; - char *service_owner = NULL; - const char *service_name; - gulong service_uid = G_MAXULONG; - gboolean success = FALSE; - - /* Ensure the request to activate the user connection came from the - * same session as the user settings service. FIXME: use ConsoleKit - * too. - */ - if (!priv->user_proxy) { - g_set_error (error, NM_MANAGER_ERROR, - NM_MANAGER_ERROR_INVALID_SERVICE, - "%s", "No user settings service available"); - goto out; - } - - sender = dbus_g_method_get_sender (context); - if (!sender) { - g_set_error (error, NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "%s", "Could not determine D-Bus requestor"); - goto out; - } - - connection = nm_dbus_manager_get_dbus_connection (priv->dbus_mgr); - if (!connection) { - g_set_error (error, NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "%s", "Could not get the D-Bus system bus"); - goto out; - } - - dbus_error_init (&dbus_error); - /* FIXME: do this async */ - sender_uid = dbus_bus_get_unix_user (connection, sender, &dbus_error); - if (dbus_error_is_set (&dbus_error)) { - dbus_error_free (&dbus_error); - g_set_error (error, NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "%s", "Could not determine the Unix user ID of the requestor"); - goto out; - } - - /* Let root activate anything. - * FIXME: use a PolicyKit permission instead - */ - if (0 == sender_uid) { - success = TRUE; - goto out; - } - - service_name = dbus_g_proxy_get_bus_name (priv->user_proxy); - if (!service_name) { - g_set_error (error, NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "%s", "Could not determine user settings service name"); - goto out; + PendingActivation *pending; + + pending = nm_manager_pending_activation_find (self, + nm_connection_get_path (connection), + scope); + if (pending) { + pending->have_connection = TRUE; + check_pending_ready (self, pending); } +} - service_owner = nm_dbus_manager_get_name_owner (priv->dbus_mgr, service_name, NULL); - if (!service_owner) { - g_set_error (error, NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "%s", "Could not determine D-Bus owner of the user settings service"); - goto out; - } +static void +activation_auth_done (PendingActivation *pending, GError *error) +{ + if (error) { + nm_manager_pending_activation_remove (pending->manager, pending); + pending_activation_destroy (pending, error, NULL); + return; + } else { + pending->authorized = TRUE; - dbus_error_init (&dbus_error); - /* FIXME: do this async */ - service_uid = dbus_bus_get_unix_user (connection, service_owner, &dbus_error); - if (dbus_error_is_set (&dbus_error)) { - dbus_error_free (&dbus_error); - g_set_error (error, NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "%s", "Could not determine the Unix UID of the sender of the request"); - goto out; - } + /* Now that we're authorized, if the connection hasn't shown up yet, + * start a timer and wait for it. + */ + if (!pending->have_connection && !pending->timeout_id) + pending->timeout_id = g_timeout_add_seconds (5, wait_for_connection_expired, pending); - /* And finally, the actual UID check */ - if (sender_uid != service_uid) { - g_set_error (error, NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "%s", "Requestor UID does not match the UID of the user settings service"); - goto out; + check_pending_ready (pending->manager, pending); } - - success = TRUE; - -out: - g_free (sender); - g_free (service_owner); - return success; } static void -impl_manager_activate_connection (NMManager *manager, +impl_manager_activate_connection (NMManager *self, const char *service_name, const char *connection_path, const char *device_path, const char *specific_object_path, DBusGMethodInvocation *context) { + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMConnectionScope scope = NM_CONNECTION_SCOPE_UNKNOWN; - NMConnection *connection; + PendingActivation *pending; GError *error = NULL; - char *real_sop = NULL; - char *path = NULL; - - if (!strcmp (service_name, NM_DBUS_SERVICE_USER_SETTINGS)) { - if (!is_user_request_authorized (manager, context, &error)) - goto err; + if (!strcmp (service_name, NM_DBUS_SERVICE_USER_SETTINGS)) scope = NM_CONNECTION_SCOPE_USER; - } else if (!strcmp (service_name, NM_DBUS_SERVICE_SYSTEM_SETTINGS)) + else if (!strcmp (service_name, NM_DBUS_SERVICE_SYSTEM_SETTINGS)) scope = NM_CONNECTION_SCOPE_SYSTEM; else { - g_set_error (&error, - NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_SERVICE, - "%s", "Invalid settings service name"); - goto err; - } - - /* "/" is special-cased to NULL to get through D-Bus */ - if (specific_object_path && strcmp (specific_object_path, "/")) - real_sop = g_strdup (specific_object_path); - - connection = nm_manager_get_connection_by_object_path (manager, scope, connection_path); - if (connection) { - path = (char *) nm_manager_activate_connection (manager, - connection, - real_sop, - device_path, - TRUE, - &error); - if (path) { - dbus_g_method_return (context, path); - g_object_notify (G_OBJECT (manager), NM_MANAGER_ACTIVE_CONNECTIONS); - } - } else { - PendingConnectionInfo *info; - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - - if (priv->pending_connection_info) { - pending_connection_info_destroy (priv->pending_connection_info); - priv->pending_connection_info = NULL; - } - - /* Don't have the connection quite yet, probably created by - * the client on-the-fly. Defer the activation until we have it - */ - - info = g_slice_new0 (PendingConnectionInfo); - info->context = context; - info->device_path = g_strdup (device_path); - info->scope = scope; - info->connection_path = g_strdup (connection_path); - info->specific_object_path = g_strdup (real_sop); - info->timeout_id = g_timeout_add_seconds (5, wait_for_connection_expired, manager); - - // FIXME: should probably be per-device, not global to the manager - NM_MANAGER_GET_PRIVATE (manager)->pending_connection_info = info; - } - - err: - if (error) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_INVALID_SERVICE, + "Invalid settings service name"); dbus_g_method_return_error (context, error); nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate: (%d) %s", scope, connection_path, error->code, error->message); g_error_free (error); + return; } - g_free (real_sop); + /* Need to check the caller's permissions and stuff before we can + * activate the connection. + */ + pending = pending_activation_new (self, + priv->authority, + context, + device_path, + scope, + connection_path, + specific_object_path, + activation_auth_done); + priv->pending_activations = g_slist_prepend (priv->pending_activations, pending); + + if (nm_manager_get_connection_by_object_path (self, scope, connection_path)) + pending->have_connection = TRUE; + + pending_activation_check_authorized (pending, priv->dbus_mgr, priv->user_proxy); } gboolean @@ -2642,15 +3140,173 @@ done: return success; } -static gboolean -impl_manager_deactivate_connection (NMManager *manager, - const char *connection_path, - GError **error) +static void +deactivate_user_auth_done_cb (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) { - return nm_manager_deactivate_connection (manager, - connection_path, - NM_DEVICE_STATE_REASON_USER_REQUESTED, - error); + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GError *ret_error = NULL; + NMAuthCallResult result; + + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + + result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS)); + ret_error = deactivate_disconnect_check_error (error, result, "Deactivate"); + if (!ret_error) { + /* Everything authorized, deactivate the connection */ + if (nm_manager_deactivate_connection (self, + nm_auth_chain_get_data (chain, "path"), + NM_DEVICE_STATE_REASON_USER_REQUESTED, + &ret_error)) + dbus_g_method_return (context); + } + + if (ret_error) + dbus_g_method_return_error (context, ret_error); + g_clear_error (&ret_error); + + nm_auth_chain_unref (chain); +} + +static void +deactivate_net_auth_done_cb (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GError *ret_error = NULL; + NMAuthCallResult result; + const char *active_path; + NMConnectionScope scope; + + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + + result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL)); + ret_error = deactivate_disconnect_check_error (error, result, "Deactivate"); + if (ret_error) { + dbus_g_method_return_error (context, ret_error); + g_error_free (ret_error); + goto done; + } + + /* If it's a system connection, we're done */ + active_path = nm_auth_chain_get_data (chain, "path"); + scope = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "scope")); + if (scope == NM_CONNECTION_SCOPE_USER) { + NMAuthChain *user_chain; + + /* It's a user connection, so we need to ensure the caller is + * authorized to manipulate user connections. + */ + user_chain = nm_auth_chain_new (priv->authority, context, NULL, deactivate_user_auth_done_cb, self); + g_assert (user_chain); + priv->auth_chains = g_slist_append (priv->auth_chains, user_chain); + + nm_auth_chain_set_data (user_chain, "path", g_strdup (active_path), g_free); + nm_auth_chain_set_data (user_chain, "scope", GUINT_TO_POINTER (scope), NULL); + nm_auth_chain_add_call (user_chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, TRUE); + } else { + if (!nm_manager_deactivate_connection (self, + active_path, + NM_DEVICE_STATE_REASON_USER_REQUESTED, + &ret_error)) { + dbus_g_method_return_error (context, ret_error); + g_clear_error (&ret_error); + } else + dbus_g_method_return (context); + } + +done: + nm_auth_chain_unref (chain); +} + +static void +impl_manager_deactivate_connection (NMManager *self, + const char *active_path, + DBusGMethodInvocation *context) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMConnection *connection = NULL; + GError *error = NULL; + GSList *iter; + NMAuthChain *chain; + gulong sender_uid = G_MAXULONG; + NMConnectionScope scope; + const char *error_desc = NULL; + + /* Check for device connections first */ + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMActRequest *req; + const char *req_path = NULL; + + req = nm_device_get_act_request (NM_DEVICE (iter->data)); + if (req) + req_path = nm_act_request_get_active_connection_path (req); + + if (req_path && !strcmp (active_path, req_path)) { + connection = nm_act_request_get_connection (req); + break; + } + } + + /* Maybe it's a VPN */ + if (!connection) + connection = nm_vpn_manager_get_connection_for_active (priv->vpn_manager, active_path); + + if (!connection) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, + "The connection was not active."); + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + /* Need to check the caller's permissions and stuff before we can + * deactivate the connection. + */ + scope = nm_connection_get_scope (connection); + if (!check_user_authorized (priv->dbus_mgr, + priv->user_proxy, + context, + scope, + &sender_uid, + &error_desc)) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + error_desc); + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + /* Yay for root */ + if (0 == sender_uid) { + if (!nm_manager_deactivate_connection (self, + active_path, + NM_DEVICE_STATE_REASON_USER_REQUESTED, + &error)) { + dbus_g_method_return_error (context, error); + g_clear_error (&error); + } else + dbus_g_method_return (context); + + return; + } + + /* Otherwise validate the user request */ + chain = nm_auth_chain_new (priv->authority, context, NULL, deactivate_net_auth_done_cb, self); + g_assert (chain); + priv->auth_chains = g_slist_append (priv->auth_chains, chain); + + nm_auth_chain_set_data (chain, "path", g_strdup (active_path), g_free); + nm_auth_chain_set_data (chain, "scope", GUINT_TO_POINTER (scope), NULL); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); } static void @@ -2690,13 +3346,13 @@ do_sleep_wake (NMManager *self) */ for (i = 0; i < RFKILL_TYPE_MAX; i++) { RadioState *rstate = &priv->radio_states[i]; - gboolean enabled = (rstate->hw_enabled && rstate->enabled); + gboolean enabled = radio_enabled_for_rstate (rstate); RfKillType devtype = RFKILL_TYPE_UNKNOWN; if (rstate->desc) { - nm_log_dbg (LOGD_RFKILL, "%s %s devices (hw_enabled %d, enabled %d)", + nm_log_dbg (LOGD_RFKILL, "%s %s devices (hw_enabled %d, sw_enabled %d, user_enabled %d)", enabled ? "enabling" : "disabling", - rstate->desc, rstate->hw_enabled, rstate->enabled); + rstate->desc, rstate->hw_enabled, rstate->sw_enabled, rstate->user_enabled); } g_object_get (G_OBJECT (device), NM_DEVICE_INTERFACE_RFKILL_TYPE, &devtype, NULL); @@ -2719,20 +3375,31 @@ do_sleep_wake (NMManager *self) } static gboolean -impl_manager_sleep (NMManager *self, gboolean do_sleep, GError **error) +return_no_pk_error (PolkitAuthority *authority, + const char *detail, + DBusGMethodInvocation *context) { - NMManagerPrivate *priv; - - g_return_val_if_fail (NM_IS_MANAGER (self), FALSE); + GError *error; - priv = NM_MANAGER_GET_PRIVATE (self); - - if (priv->sleeping == do_sleep) { - g_set_error (error, - NM_MANAGER_ERROR, NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE, - "Already %s", do_sleep ? "asleep" : "awake"); + if (!authority) { + error = g_error_new (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "%s request failed: PolicyKit not initialized", + detail); + dbus_g_method_return_error (context, error); + g_error_free (error); return FALSE; } + return TRUE; +} + +static void +_internal_sleep (NMManager *self, gboolean do_sleep) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + + if (priv->sleeping == do_sleep) + return; nm_log_info (LOGD_SUSPEND, "%s requested (sleeping: %s enabled: %s)", do_sleep ? "sleep" : "wake", @@ -2744,29 +3411,138 @@ impl_manager_sleep (NMManager *self, gboolean do_sleep, GError **error) do_sleep_wake (self); g_object_notify (G_OBJECT (self), NM_MANAGER_SLEEPING); - return TRUE; } -static gboolean -impl_manager_enable (NMManager *self, gboolean enable, GError **error) +#if 0 +static void +sleep_auth_done_cb (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GError *ret_error; + NMAuthCallResult result; + gboolean do_sleep; + + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + + result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_SLEEP_WAKE)); + if (error) { + nm_log_dbg (LOGD_SUSPEND, "Sleep/wake request failed: %s", error->message); + ret_error = g_error_new (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Sleep/wake request failed: %s", + error->message); + dbus_g_method_return_error (context, ret_error); + g_error_free (ret_error); + } else if (result != NM_AUTH_CALL_RESULT_YES) { + ret_error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Not authorized to sleep/wake"); + dbus_g_method_return_error (context, ret_error); + g_error_free (ret_error); + } else { + /* Auth success */ + do_sleep = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "sleep")); + _internal_sleep (self, do_sleep); + dbus_g_method_return (context); + } + + nm_auth_chain_unref (chain); +} +#endif + +static void +impl_manager_sleep (NMManager *self, + gboolean do_sleep, + DBusGMethodInvocation *context) { NMManagerPrivate *priv; + GError *error = NULL; +#if 0 + NMAuthChain *chain; + gulong sender_uid = G_MAXULONG; + const char *error_desc = NULL; +#endif - g_return_val_if_fail (NM_IS_MANAGER (self), FALSE); + g_return_if_fail (NM_IS_MANAGER (self)); priv = NM_MANAGER_GET_PRIVATE (self); - if (priv->net_enabled == enable) { - g_set_error (error, - NM_MANAGER_ERROR, NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED, - "Already %s", enable ? "enabled" : "disabled"); - return FALSE; + if (priv->sleeping == do_sleep) { + error = g_error_new (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE, + "Already %s", do_sleep ? "asleep" : "awake"); + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + /* Unconditionally allow the request. Previously it was polkit protected + * but unfortunately that doesn't work for short-lived processes like + * pm-utils. It uses dbus-send without --print-reply, which quits + * immediately after sending the request, and NM is unable to obtain the + * sender's UID as dbus-send has already dropped off the bus. Thus NM + * fails the request. Instead, don't validate the request, but rely on + * D-Bus permissions to restrict the call to root. + */ + _internal_sleep (self, do_sleep); + dbus_g_method_return (context); + return; + +#if 0 + if (!nm_auth_get_caller_uid (context, priv->dbus_mgr, &sender_uid, &error_desc)) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + error_desc); + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + /* Root doesn't need PK authentication */ + if (0 == sender_uid) { + _internal_sleep (self, do_sleep); + dbus_g_method_return (context); + return; } + if (!return_no_pk_error (priv->authority, "Sleep/wake", context)) + return; + + chain = nm_auth_chain_new (priv->authority, context, NULL, sleep_auth_done_cb, self); + g_assert (chain); + priv->auth_chains = g_slist_append (priv->auth_chains, chain); + + nm_auth_chain_set_data (chain, "sleep", GUINT_TO_POINTER (do_sleep), NULL); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, TRUE); +#endif +} + +static void +upower_sleeping_cb (DBusGProxy *proxy, gpointer user_data) +{ + nm_log_dbg (LOGD_SUSPEND, "Received UPower sleeping signal"); + _internal_sleep (NM_MANAGER (user_data), TRUE); +} + +static void +upower_resuming_cb (DBusGProxy *proxy, gpointer user_data) +{ + nm_log_dbg (LOGD_SUSPEND, "Received UPower resuming signal"); + _internal_sleep (NM_MANAGER (user_data), FALSE); +} + +static void +_internal_enable (NMManager *self, gboolean enable) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GError *err = NULL; + /* Update "NetworkingEnabled" key in state file */ if (priv->state_file) { - GError *err = NULL; - if (!write_value_to_state_file (priv->state_file, "main", "NetworkingEnabled", G_TYPE_BOOLEAN, (gpointer) &enable, @@ -2789,21 +3565,245 @@ impl_manager_enable (NMManager *self, gboolean enable, GError **error) do_sleep_wake (self); g_object_notify (G_OBJECT (self), NM_MANAGER_NETWORKING_ENABLED); - return TRUE; +} + +static void +enable_net_done_cb (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GError *ret_error; + NMAuthCallResult result; + gboolean enable; + + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + + result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK)); + if (error) { + nm_log_dbg (LOGD_CORE, "Enable request failed: %s", error->message); + ret_error = g_error_new (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Enable request failed: %s", + error->message); + dbus_g_method_return_error (context, ret_error); + g_error_free (ret_error); + } else if (result != NM_AUTH_CALL_RESULT_YES) { + ret_error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Not authorized to enable/disable networking"); + dbus_g_method_return_error (context, ret_error); + g_error_free (ret_error); + } else { + /* Auth success */ + enable = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "enable")); + _internal_enable (self, enable); + dbus_g_method_return (context); + } + + nm_auth_chain_unref (chain); +} + +static void +impl_manager_enable (NMManager *self, + gboolean enable, + DBusGMethodInvocation *context) +{ + NMManagerPrivate *priv; + NMAuthChain *chain; + GError *error = NULL; + gulong sender_uid = G_MAXULONG; + const char *error_desc = NULL; + + g_return_if_fail (NM_IS_MANAGER (self)); + + priv = NM_MANAGER_GET_PRIVATE (self); + + if (priv->net_enabled == enable) { + error = g_error_new (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED, + "Already %s", enable ? "enabled" : "disabled"); + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + if (!nm_auth_get_caller_uid (context, priv->dbus_mgr, &sender_uid, &error_desc)) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + error_desc); + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + /* Root doesn't need PK authentication */ + if (0 == sender_uid) { + _internal_enable (self, enable); + dbus_g_method_return (context); + return; + } + + if (!return_no_pk_error (priv->authority, "Enable/disable", context)) + return; + + chain = nm_auth_chain_new (priv->authority, context, NULL, enable_net_done_cb, self); + g_assert (chain); + priv->auth_chains = g_slist_append (priv->auth_chains, chain); + + nm_auth_chain_set_data (chain, "enable", GUINT_TO_POINTER (enable), NULL); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, TRUE); +} + +/* Permissions */ + +static void +user_proxy_permissions_changed_done (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + gboolean authorized = FALSE; + + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + + if (error) { + nm_log_warn (LOGD_USER_SET, "User connections unavailable: (%d) %s", + error->code, error->message ? error->message : "(unknown)"); + } else + authorized = user_settings_authorized (self, chain); + + if (authorized) { + /* User connections are authorized */ + if (!priv->user_proxy) + user_proxy_init (self); + } else + user_proxy_cleanup (self, TRUE); + + nm_auth_chain_unref (chain); +} + +static void +pk_authority_changed_cb (GObject *object, gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMAuthChain *chain; + + /* If the user settings service wasn't previously authorized, we wouldn't + * care about it. But it might be authorized now, so lets check. + */ + if (!priv->user_proxy) + user_proxy_init (self); + else { + /* Otherwise the user settings permissions could have changed so we + * need to recheck them. + */ + chain = nm_auth_chain_new (priv->authority, + NULL, + priv->user_proxy, + user_proxy_permissions_changed_done, + self); + priv->auth_chains = g_slist_prepend (priv->auth_chains, chain); + + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, FALSE); + } + + /* Let clients know they should re-check their authorization */ + g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0); +} + +static void +get_perm_add_result (NMAuthChain *chain, GHashTable *results, const char *permission) +{ + NMAuthCallResult result; + + result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, permission)); + if (result == NM_AUTH_CALL_RESULT_YES) + g_hash_table_insert (results, (char *) permission, "yes"); + else if (result == NM_AUTH_CALL_RESULT_NO) + g_hash_table_insert (results, (char *) permission, "no"); + else if (result == NM_AUTH_CALL_RESULT_AUTH) + g_hash_table_insert (results, (char *) permission, "auth"); + else { + nm_log_dbg (LOGD_CORE, "unknown auth chain result %d", result); + } +} + +static void +get_permissions_done_cb (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GError *ret_error; + GHashTable *results; + + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + if (error) { + nm_log_dbg (LOGD_CORE, "Permissions request failed: %s", error->message); + ret_error = g_error_new (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Permissions request failed: %s", + error->message); + dbus_g_method_return_error (context, ret_error); + g_error_free (ret_error); + } else { + results = g_hash_table_new (g_str_hash, g_str_equal); + get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK); + get_perm_add_result (chain, results, NM_AUTH_PERMISSION_SLEEP_WAKE); + get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI); + get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN); + get_perm_add_result (chain, results, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS); + get_perm_add_result (chain, results, NM_AUTH_PERMISSION_NETWORK_CONTROL); + dbus_g_method_return (context, results); + g_hash_table_destroy (results); + } + + nm_auth_chain_unref (chain); +} + +static void +impl_manager_get_permissions (NMManager *self, + DBusGMethodInvocation *context) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMAuthChain *chain; + + if (!return_no_pk_error (priv->authority, "Permissions", context)) + return; + + chain = nm_auth_chain_new (priv->authority, context, NULL, get_permissions_done_cb, self); + g_assert (chain); + priv->auth_chains = g_slist_append (priv->auth_chains, chain); + + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, FALSE); } /* Legacy 0.6 compatibility interface */ -static gboolean -impl_manager_legacy_sleep (NMManager *manager, GError **error) +static void +impl_manager_legacy_sleep (NMManager *manager, DBusGMethodInvocation *context) { - return impl_manager_sleep (manager, TRUE, error); + return impl_manager_sleep (manager, TRUE, context); } -static gboolean -impl_manager_legacy_wake (NMManager *manager, GError **error) +static void +impl_manager_legacy_wake (NMManager *manager, DBusGMethodInvocation *context) { - return impl_manager_sleep (manager, FALSE, error); + return impl_manager_sleep (manager, FALSE, context); } static gboolean @@ -2836,6 +3836,15 @@ impl_manager_set_logging (NMManager *manager, /* Connections */ +gboolean +nm_manager_auto_user_connections_allowed (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + + return priv->user_net_perm == NM_AUTH_CALL_RESULT_YES + && priv->user_con_perm == NM_AUTH_CALL_RESULT_YES; +} + static int connection_sort (gconstpointer pa, gconstpointer pb) { @@ -2929,34 +3938,21 @@ nm_manager_start (NMManager *self) /* Set initial radio enabled/disabled state */ for (i = 0; i < RFKILL_TYPE_MAX; i++) { RadioState *rstate = &priv->radio_states[i]; - gboolean enabled = TRUE, hw_enabled = TRUE; + RfKillState udev_state; if (!rstate->desc) continue; - switch (nm_udev_manager_get_rfkill_state (priv->udev_mgr, i)) { - case RFKILL_UNBLOCKED: - enabled = TRUE; - hw_enabled = TRUE; - break; - case RFKILL_SOFT_BLOCKED: - enabled = FALSE; - hw_enabled = TRUE; - break; - case RFKILL_HARD_BLOCKED: - enabled = FALSE; - hw_enabled = FALSE; - break; - default: - break; - } + udev_state = nm_udev_manager_get_rfkill_state (priv->udev_mgr, i); + update_rstate_from_rfkill (rstate, udev_state); - rstate->hw_enabled = hw_enabled; - nm_log_info (LOGD_RFKILL, "%s %s by radio killswitch; %s by state file", - rstate->desc, - (rstate->hw_enabled && enabled) ? "enabled" : "disabled", - (rstate->enabled) ? "enabled" : "disabled"); - manager_set_radio_enabled (self, rstate, rstate->enabled && enabled); + if (rstate->desc) { + nm_log_info (LOGD_RFKILL, "%s %s by radio killswitch; %s by state file", + rstate->desc, + (rstate->hw_enabled && rstate->sw_enabled) ? "enabled" : "disabled", + rstate->user_enabled ? "enabled" : "disabled"); + } + manager_update_radio_enabled (self, rstate); } /* Log overall networking status - enabled/disabled */ @@ -2972,12 +3968,226 @@ nm_manager_start (NMManager *self) * bus in nm_manager_name_owner_changed(). */ if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, NM_DBUS_SERVICE_USER_SETTINGS)) - user_query_connections (self); + user_proxy_init (self); nm_udev_manager_query_devices (priv->udev_mgr); bluez_manager_resync_devices (self); } +static gboolean +handle_firmware_changed (gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GSList *iter; + + priv->fw_changed_id = 0; + + if (manager_sleeping (self)) + return FALSE; + + /* Try to re-enable devices with missing firmware */ + for (iter = priv->devices; iter; iter = iter->next) { + NMDevice *candidate = NM_DEVICE (iter->data); + NMDeviceState state = nm_device_get_state (candidate); + + if ( nm_device_get_firmware_missing (candidate) + && (state == NM_DEVICE_STATE_UNAVAILABLE)) { + nm_log_info (LOGD_CORE, "(%s): firmware may now be available", + nm_device_get_iface (candidate)); + + /* Re-set unavailable state to try bringing the device up again */ + nm_device_state_changed (candidate, + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_NONE); + } + } + + return FALSE; +} + +static void +firmware_dir_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + + 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) { + priv->fw_changed_id = g_timeout_add_seconds (4, handle_firmware_changed, self); + nm_log_info (LOGD_CORE, "kernel firmware directory '%s' changed", + KERNEL_FIRMWARE_DIR); + } + break; + default: + break; + } +} + +#define PERM_DENIED_ERROR "org.freedesktop.NetworkManager.PermissionDenied" + +static void +prop_set_auth_done_cb (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + DBusGConnection *bus; + DBusConnection *dbus_connection; + NMAuthCallResult result; + DBusMessage *reply, *request; + const char *permission, *prop; + gboolean set_enabled = TRUE; + + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + + request = nm_auth_chain_get_data (chain, "message"); + permission = nm_auth_chain_get_data (chain, "permission"); + prop = nm_auth_chain_get_data (chain, "prop"); + set_enabled = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "enabled")); + + if (error) { + reply = dbus_message_new_error (request, PERM_DENIED_ERROR, + "Not authorized to perform this operation"); + } else { + /* Caller has had a chance to obtain authorization, so we only need to + * check for 'yes' here. + */ + result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, permission)); + if (result != NM_AUTH_CALL_RESULT_YES) { + reply = dbus_message_new_error (request, PERM_DENIED_ERROR, + "Not authorized to perform this operation"); + } else { + g_object_set (self, prop, set_enabled, NULL); + reply = dbus_message_new_method_return (request); + } + } + + if (reply) { + bus = nm_dbus_manager_get_connection (priv->dbus_mgr); + g_assert (bus); + dbus_connection = dbus_g_connection_get_connection (bus); + g_assert (dbus_connection); + + dbus_connection_send (dbus_connection, reply, NULL); + dbus_message_unref (reply); + } + nm_auth_chain_unref (chain); +} + +static DBusHandlerResult +prop_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + DBusMessageIter iter; + DBusMessageIter sub; + const char *propiface = NULL; + const char *propname = NULL; + const char *sender = NULL; + const char *glib_propname = NULL, *permission = NULL; + DBusError dbus_error; + gulong uid = G_MAXULONG; + DBusMessage *reply = NULL; + gboolean set_enabled = FALSE; + NMAuthChain *chain; + + /* The sole purpose of this function is to validate property accesses + * on the NMManager object since dbus-glib doesn't yet give us this + * functionality. + */ + + if (!dbus_message_is_method_call (message, DBUS_INTERFACE_PROPERTIES, "Set")) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + dbus_message_iter_init (message, &iter); + + /* Get the D-Bus interface of the property to set */ + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + dbus_message_iter_get_basic (&iter, &propiface); + if (!propiface || strcmp (propiface, NM_DBUS_INTERFACE)) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + dbus_message_iter_next (&iter); + + /* Get the property name that's going to be set */ + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + dbus_message_iter_get_basic (&iter, &propname); + dbus_message_iter_next (&iter); + + if (!strcmp (propname, "WirelessEnabled")) { + glib_propname = NM_MANAGER_WIRELESS_ENABLED; + permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI; + } else if (!strcmp (propname, "WwanEnabled")) { + glib_propname = NM_MANAGER_WWAN_ENABLED; + permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN; + } else + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + /* Get the new value for the property */ + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_VARIANT) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + dbus_message_iter_recurse (&iter, &sub); + if (dbus_message_iter_get_arg_type (&sub) != DBUS_TYPE_BOOLEAN) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + dbus_message_iter_get_basic (&sub, &set_enabled); + + sender = dbus_message_get_sender (message); + if (!sender) { + reply = dbus_message_new_error (message, PERM_DENIED_ERROR, + "Could not determine D-Bus requestor"); + goto out; + } + + dbus_error_init (&dbus_error); + uid = dbus_bus_get_unix_user (connection, sender, &dbus_error); + if (dbus_error_is_set (&dbus_error)) { + reply = dbus_message_new_error (message, PERM_DENIED_ERROR, + "Could not determine the user ID of the requestor"); + dbus_error_free (&dbus_error); + goto out; + } + + if (uid > 0) { + /* Otherwise validate the user request */ + chain = nm_auth_chain_new_raw_message (priv->authority, message, prop_set_auth_done_cb, self); + g_assert (chain); + priv->auth_chains = g_slist_append (priv->auth_chains, chain); + nm_auth_chain_set_data (chain, "prop", g_strdup (glib_propname), g_free); + nm_auth_chain_set_data (chain, "permission", g_strdup (permission), g_free); + nm_auth_chain_set_data (chain, "enabled", GUINT_TO_POINTER (set_enabled), NULL); + nm_auth_chain_set_data (chain, "message", dbus_message_ref (message), (GDestroyNotify) dbus_message_unref); + nm_auth_chain_add_call (chain, permission, TRUE); + } else { + /* Yay for root */ + g_object_set (self, glib_propname, set_enabled, NULL); + reply = dbus_message_new_method_return (message); + } + +out: + if (reply) { + dbus_connection_send (connection, reply, NULL); + dbus_message_unref (reply); + } + return DBUS_HANDLER_RESULT_HANDLED; +} + NMManager * nm_manager_get (const char *config_file, const char *plugins, @@ -2990,6 +4200,7 @@ nm_manager_get (const char *config_file, static NMManager *singleton = NULL; NMManagerPrivate *priv; DBusGConnection *bus; + DBusConnection *dbus_connection; if (singleton) return g_object_ref (singleton); @@ -3001,6 +4212,14 @@ nm_manager_get (const char *config_file, bus = nm_dbus_manager_get_connection (priv->dbus_mgr); g_assert (bus); + dbus_connection = dbus_g_connection_get_connection (bus); + g_assert (dbus_connection); + + if (!dbus_connection_add_filter (dbus_connection, prop_filter, singleton, NULL)) { + nm_log_err (LOGD_CORE, "failed to register DBus connection filter"); + g_object_unref (singleton); + return NULL; + } priv->sys_settings = nm_sysconfig_settings_new (config_file, plugins, bus, error); if (!priv->sys_settings) { @@ -3015,8 +4234,8 @@ nm_manager_get (const char *config_file, priv->net_enabled = initial_net_enabled; - priv->radio_states[RFKILL_TYPE_WLAN].enabled = initial_wifi_enabled; - priv->radio_states[RFKILL_TYPE_WWAN].enabled = initial_wwan_enabled; + priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = initial_wifi_enabled; + priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = initial_wwan_enabled; g_signal_connect (priv->sys_settings, "notify::" NM_SYSCONFIG_SETTINGS_UNMANAGED_SPECS, G_CALLBACK (system_unmanaged_devices_changed_cb), singleton); @@ -3068,6 +4287,9 @@ dispose (GObject *object) { NMManager *manager = NM_MANAGER (object); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + GSList *iter; + DBusGConnection *bus; + DBusConnection *dbus_connection; if (priv->disposed) { G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object); @@ -3075,8 +4297,14 @@ dispose (GObject *object) } priv->disposed = TRUE; - pending_connection_info_destroy (priv->pending_connection_info); - priv->pending_connection_info = NULL; + for (iter = priv->pending_activations; iter; iter = g_slist_next (iter)) + pending_activation_destroy ((PendingActivation *) iter->data, NULL, NULL); + g_slist_free (priv->pending_activations); + priv->pending_activations = NULL; + + g_slist_foreach (priv->auth_chains, (GFunc) nm_auth_chain_unref, NULL); + g_slist_free (priv->auth_chains); + g_object_unref (priv->authority); while (g_slist_length (priv->secrets_calls)) free_get_secrets_info ((GetSecretsInfo *) priv->secrets_calls->data); @@ -3085,11 +4313,10 @@ dispose (GObject *object) priv->devices = remove_one_device (manager, priv->devices, NM_DEVICE (priv->devices->data), - TRUE, - FALSE); + TRUE); } - user_destroy_connections (manager); + user_proxy_cleanup (manager, FALSE); g_hash_table_destroy (priv->user_connections); priv->user_connections = NULL; @@ -3122,14 +4349,79 @@ dispose (GObject *object) } g_object_unref (priv->modem_manager); + /* Unregister property filter */ + bus = nm_dbus_manager_get_connection (priv->dbus_mgr); + g_assert (bus); + dbus_connection = dbus_g_connection_get_connection (bus); + g_assert (dbus_connection); + dbus_connection_remove_filter (dbus_connection, prop_filter, manager); g_object_unref (priv->dbus_mgr); + if (priv->bluez_mgr) g_object_unref (priv->bluez_mgr); + if (priv->aipd_proxy) + g_object_unref (priv->aipd_proxy); + + if (priv->upower_proxy) + g_object_unref (priv->upower_proxy); + + if (priv->fw_monitor) { + if (priv->fw_monitor_id) + g_signal_handler_disconnect (priv->fw_monitor, priv->fw_monitor_id); + + if (priv->fw_changed_id) + g_source_remove (priv->fw_changed_id); + + g_file_monitor_cancel (priv->fw_monitor); + g_object_unref (priv->fw_monitor); + } + + if (priv->timestamp_update_id) { + g_source_remove (priv->timestamp_update_id); + priv->timestamp_update_id = 0; + } + G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object); } static void +manager_radio_user_toggled (NMManager *self, + RadioState *rstate, + gboolean enabled) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GError *error = NULL; + gboolean old_enabled, new_enabled; + + if (rstate->desc) { + nm_log_dbg (LOGD_RFKILL, "(%s): setting radio %s by user", + rstate->desc, + enabled ? "enabled" : "disabled"); + } + + /* Update enabled key in state file */ + if (priv->state_file) { + if (!write_value_to_state_file (priv->state_file, + "main", rstate->key, + G_TYPE_BOOLEAN, (gpointer) &enabled, + &error)) { + nm_log_warn (LOGD_CORE, "writing to state file %s failed: (%d) %s.", + priv->state_file, + error ? error->code : -1, + (error && error->message) ? error->message : "unknown"); + g_clear_error (&error); + } + } + + old_enabled = radio_enabled_for_rstate (rstate); + rstate->user_enabled = enabled; + new_enabled = radio_enabled_for_rstate (rstate); + if (new_enabled != old_enabled) + manager_update_radio_enabled (self, rstate); +} + +static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { @@ -3142,14 +4434,14 @@ set_property (GObject *object, guint prop_id, priv->net_enabled = g_value_get_boolean (value); break; case PROP_WIRELESS_ENABLED: - manager_set_radio_enabled (NM_MANAGER (object), - &priv->radio_states[RFKILL_TYPE_WLAN], - g_value_get_boolean (value)); + manager_radio_user_toggled (NM_MANAGER (object), + &priv->radio_states[RFKILL_TYPE_WLAN], + g_value_get_boolean (value)); break; case PROP_WWAN_ENABLED: - manager_set_radio_enabled (NM_MANAGER (object), - &priv->radio_states[RFKILL_TYPE_WWAN], - g_value_get_boolean (value)); + manager_radio_user_toggled (NM_MANAGER (object), + &priv->radio_states[RFKILL_TYPE_WWAN], + g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -3165,6 +4457,9 @@ get_property (GObject *object, guint prop_id, NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); switch (prop_id) { + case PROP_VERSION: + g_value_set_string (value, VERSION); + break; case PROP_STATE: nm_manager_update_state (self); g_value_set_uint (value, priv->state); @@ -3173,13 +4468,13 @@ get_property (GObject *object, guint prop_id, g_value_set_boolean (value, priv->net_enabled); break; case PROP_WIRELESS_ENABLED: - g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WLAN].enabled); + g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WLAN)); break; case PROP_WIRELESS_HARDWARE_ENABLED: g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WLAN].hw_enabled); break; case PROP_WWAN_ENABLED: - g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WWAN].enabled); + g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WWAN)); break; case PROP_WWAN_HARDWARE_ENABLED: g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WWAN].hw_enabled); @@ -3199,17 +4494,41 @@ get_property (GObject *object, guint prop_id, } } +static gboolean +periodic_update_active_connection_timestamps (gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + GPtrArray *active; + int i; + + active = get_active_connections (manager, NULL); + + for (i = 0; i < active->len; i++) { + const char *active_path = g_ptr_array_index (active, i); + NMActRequest *req; + NMDevice *device = NULL; + + req = nm_manager_get_act_request_by_path (manager, active_path, &device); + if (device && nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) + update_active_connection_timestamp (manager, device); + } + + return TRUE; +} + static void nm_manager_init (NMManager *manager) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); DBusGConnection *g_connection; guint id, i; + GFile *file; + GError *error = NULL; /* Initialize rfkill structures and states */ memset (priv->radio_states, 0, sizeof (priv->radio_states)); - priv->radio_states[RFKILL_TYPE_WLAN].enabled = TRUE; + priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = TRUE; priv->radio_states[RFKILL_TYPE_WLAN].key = "WirelessEnabled"; priv->radio_states[RFKILL_TYPE_WLAN].prop = NM_MANAGER_WIRELESS_ENABLED; priv->radio_states[RFKILL_TYPE_WLAN].hw_prop = NM_MANAGER_WIRELESS_HARDWARE_ENABLED; @@ -3217,7 +4536,7 @@ nm_manager_init (NMManager *manager) priv->radio_states[RFKILL_TYPE_WLAN].other_enabled_func = nm_manager_get_ipw_rfkill_state; priv->radio_states[RFKILL_TYPE_WLAN].rtype = RFKILL_TYPE_WLAN; - priv->radio_states[RFKILL_TYPE_WWAN].enabled = TRUE; + priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = TRUE; priv->radio_states[RFKILL_TYPE_WWAN].key = "WWANEnabled"; priv->radio_states[RFKILL_TYPE_WWAN].prop = NM_MANAGER_WWAN_ENABLED; priv->radio_states[RFKILL_TYPE_WWAN].hw_prop = NM_MANAGER_WWAN_HARDWARE_ENABLED; @@ -3225,7 +4544,7 @@ nm_manager_init (NMManager *manager) priv->radio_states[RFKILL_TYPE_WWAN].other_enabled_func = nm_manager_get_modem_enabled_state; priv->radio_states[RFKILL_TYPE_WWAN].rtype = RFKILL_TYPE_WWAN; - priv->radio_states[RFKILL_TYPE_WIMAX].enabled = TRUE; + priv->radio_states[RFKILL_TYPE_WIMAX].user_enabled = TRUE; priv->radio_states[RFKILL_TYPE_WIMAX].key = "WiMAXEnabled"; priv->radio_states[RFKILL_TYPE_WIMAX].prop = NULL; priv->radio_states[RFKILL_TYPE_WIMAX].hw_prop = NULL; @@ -3286,6 +4605,58 @@ nm_manager_init (NMManager *manager) NULL); } else nm_log_warn (LOGD_AUTOIP4, "could not initialize avahi-autoipd D-Bus proxy"); + + /* upower sleep/wake handling */ + priv->upower_proxy = dbus_g_proxy_new_for_name (g_connection, + UPOWER_DBUS_SERVICE, + "/org/freedesktop/UPower", + "org.freedesktop.UPower"); + if (priv->upower_proxy) { + dbus_g_proxy_add_signal (priv->upower_proxy, "Sleeping", G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->upower_proxy, "Sleeping", + G_CALLBACK (upower_sleeping_cb), + manager, NULL); + + dbus_g_proxy_add_signal (priv->upower_proxy, "Resuming", G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->upower_proxy, "Resuming", + G_CALLBACK (upower_resuming_cb), + manager, NULL); + } else + nm_log_warn (LOGD_SUSPEND, "could not initialize UPower D-Bus proxy"); + + priv->authority = polkit_authority_get_sync (NULL, &error); + if (priv->authority) { + priv->auth_changed_id = g_signal_connect (priv->authority, + "changed", + G_CALLBACK (pk_authority_changed_cb), + manager); + } else { + nm_log_warn (LOGD_CORE, "failed to create PolicyKit authority: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + } + + /* Monitor the firmware directory */ + if (strlen (KERNEL_FIRMWARE_DIR)) { + file = g_file_new_for_path (KERNEL_FIRMWARE_DIR "/"); + priv->fw_monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL); + g_object_unref (file); + } + + if (priv->fw_monitor) { + priv->fw_monitor_id = g_signal_connect (priv->fw_monitor, "changed", + G_CALLBACK (firmware_dir_changed), + manager); + nm_log_info (LOGD_CORE, "monitoring kernel firmware directory '%s'.", + KERNEL_FIRMWARE_DIR); + } else { + nm_log_warn (LOGD_CORE, "failed to monitor kernel firmware directory '%s'.", + KERNEL_FIRMWARE_DIR); + } + + /* Update timestamps in active connections */ + priv->timestamp_update_id = g_timeout_add_seconds (300, (GSourceFunc) periodic_update_active_connection_timestamps, manager); } static void @@ -3304,6 +4675,14 @@ nm_manager_class_init (NMManagerClass *manager_class) /* properties */ g_object_class_install_property + (object_class, PROP_VERSION, + g_param_spec_string (NM_MANAGER_VERSION, + "Version", + "NetworkManager version", + NULL, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_STATE, g_param_spec_uint (NM_MANAGER_STATE, "State", @@ -3445,6 +4824,22 @@ nm_manager_class_init (NMManagerClass *manager_class) _nm_marshal_VOID__OBJECT_UINT, G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT); + signals[CHECK_PERMISSIONS] = + g_signal_new ("check-permissions", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[USER_PERMISSIONS_CHANGED] = + g_signal_new ("user-permissions-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + /* StateChange is DEPRECATED */ signals[STATE_CHANGE] = g_signal_new ("state-change", |