diff options
author | Dan Williams <dcbw@redhat.com> | 2010-06-03 23:20:11 -0700 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2010-06-03 23:20:11 -0700 |
commit | 25e758c77041bec8014a4f28333dd9ccd559487e (patch) | |
tree | 0710cda231674fdceebee619229a0a038442b097 | |
parent | 6cbe50ffbd861fc705035fd17f6a88eb58377890 (diff) | |
download | NetworkManager-25e758c77041bec8014a4f28333dd9ccd559487e.tar.gz |
core: PolicyKit protect connection activation
-rw-r--r-- | src/nm-manager.c | 452 |
1 files changed, 276 insertions, 176 deletions
diff --git a/src/nm-manager.c b/src/nm-manager.c index ac7eb2ea65..7560b81afb 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -61,11 +61,11 @@ 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, @@ -147,18 +147,27 @@ static NMDevice *nm_manager_get_device_by_udi (NMManager *manager, const char *u #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; - /* More than one caller may have requested this connection */ - GSList *contexts; + 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; -} PendingActivation; +}; typedef struct { gboolean enabled; @@ -587,33 +596,178 @@ nm_manager_pending_activation_remove (NMManager *self, 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) + const char *specific_object_path, + PendingActivationFunc callback) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); 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); - pending->specific_object_path = g_strdup (specific_object_path); - priv->pending_activations = g_slist_append (priv->pending_activations, pending); + /* "/" 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_activation_add_caller (PendingActivation *pending, - DBusGMethodInvocation *context) +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 void +pending_activation_check_authorized (PendingActivation *pending, + NMDBusManager *dbus_mgr, + DBusGProxy *user_proxy) { - /* Ensure no dupes */ - if (!g_slist_find (pending->contexts, context)) - pending->contexts = g_slist_prepend (pending->contexts, context); + 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); + g_return_if_fail (user_proxy != NULL); + + /* Get the UID */ + if (!nm_auth_get_caller_uid (pending->context, dbus_mgr, &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; + } + + /* root gets to do anything */ + if (0 == sender_uid) { + pending->callback (pending, NULL); + return; + } + + /* Check whether the UID is authorized for user connections */ + if ( pending->scope == NM_CONNECTION_SCOPE_USER + && !nm_auth_uid_authorized (sender_uid, + dbus_mgr, + user_proxy, + &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; + } + + /* 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); + nm_auth_chain_add_call (pending->chain, + NM_AUTH_PERMISSION_NETWORK_CONTROL, + TRUE); } static void @@ -621,8 +775,6 @@ pending_activation_destroy (PendingActivation *pending, GError *error, const char *ac_path) { - GSList *iter; - g_return_if_fail (pending != NULL); if (pending->timeout_id) @@ -631,16 +783,13 @@ pending_activation_destroy (PendingActivation *pending, g_free (pending->specific_object_path); g_free (pending->device_path); - /* Send a reply (either error or the ActiveConnection path) if needed */ - for (iter = pending->contexts; iter; iter = g_slist_next (iter)) { - DBusGMethodInvocation *context = iter->data; + if (error) + dbus_g_method_return_error (pending->context, error); + else if (ac_path) + dbus_g_method_return (pending->context, ac_path); - if (error) - dbus_g_method_return_error (context, error); - else if (ac_path) - dbus_g_method_return (context, ac_path); - } - g_slist_free (pending->contexts); + if (pending->chain) + nm_auth_chain_unref (pending->chain); memset (pending, 0, sizeof (PendingActivation)); g_slice_free (PendingActivation, pending); @@ -1016,10 +1165,33 @@ user_proxy_destroyed_cb (DBusGProxy *proxy, NMManager *self) user_proxy_cleanup (self, TRUE); } +typedef struct { + DBusGProxy *proxy; + char *path; + NMManager *manager; +} Foo; + +static gboolean +blah (gpointer user_data) +{ + Foo *f = user_data; + + user_internal_new_connection_cb (f->proxy, f->path, f->manager, NULL); + g_free (f->path); + g_free (f); + return FALSE; +} + 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); + Foo *f = g_malloc0 (sizeof (Foo)); + + f->proxy = proxy; + f->path = g_strdup (path); + f->manager = NM_MANAGER (user_data); + + g_timeout_add_seconds (6, blah, f); } static gboolean @@ -2415,7 +2587,7 @@ wait_for_connection_expired (gpointer data) g_return_val_if_fail (pending != NULL, FALSE); nm_log_warn (LOGD_CORE, "connection %s (scope %d) failed to activate (timeout)", - pending->scope, pending->connection_path); + pending->connection_path, pending->scope); nm_manager_pending_activation_remove (pending->manager, pending); @@ -2552,24 +2724,30 @@ nm_manager_pending_activation_find (NMManager *self, } static void -connection_added_default_handler (NMManager *manager, - NMConnection *connection, - NMConnectionScope scope) +check_pending_ready (NMManager *self, PendingActivation *pending) { - PendingActivation *pending; + NMConnection *connection; const char *path = NULL; GError *error = NULL; - pending = nm_manager_pending_activation_find (manager, - nm_connection_get_path (connection), - scope); - if (!pending) + if (!pending->have_connection || !pending->authorized) return; - /* Will destroy below; can't be valid during the initial activation start */ - nm_manager_pending_activation_remove (manager, pending); + /* Ok, we're authorized and the connection is available */ - path = nm_manager_activate_connection (manager, + nm_manager_pending_activation_remove (self, pending); + + 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 (self, connection, pending->specific_object_path, pending->device_path, @@ -2577,174 +2755,96 @@ connection_added_default_handler (NMManager *manager, &error); if (!path) { nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate: (%d) %s", - scope, pending->connection_path, error->code, error->message); + pending->scope, pending->connection_path, error->code, error->message); } else - g_object_notify (G_OBJECT (manager), NM_MANAGER_ACTIVE_CONNECTIONS); + g_object_notify (G_OBJECT (pending->manager), NM_MANAGER_ACTIVE_CONNECTIONS); +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; - gulong sender_uid = G_MAXULONG; - DBusError dbus_error; - char *service_owner = NULL; - const char *service_name; - gulong service_uid = G_MAXULONG; - gboolean success = FALSE; - const char *error_desc = NULL; - - /* 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; - } - - if (!nm_auth_get_caller_uid (context, priv->dbus_mgr, &sender_uid, &error_desc)) { - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); - goto out; - } - - /* Let root activate anything */ - if (0 == sender_uid) { - success = TRUE; - goto out; - } + PendingActivation *pending; - 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; + 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; - connection = nm_dbus_manager_get_dbus_connection (priv->dbus_mgr); - if (!connection) { - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Could not get the D-Bus system bus"); - 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); - 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; + check_pending_ready (pending->manager, 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"); - } else - success = TRUE; - -out: - 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 { - PendingActivation *pending; - - /* Don't have the connection quite yet, probably created by - * the client on-the-fly. Defer the activation until we have it. - */ - - pending = nm_manager_pending_activation_find (manager, connection_path, scope); - if (!pending) { - pending = pending_activation_new (manager, device_path, scope, connection_path, real_sop); - /* Add a timeout waiting for the connection to be added */ - pending->timeout_id = g_timeout_add_seconds (5, wait_for_connection_expired, pending); - } - pending_activation_add_caller (pending, context); - } - - 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 |