diff --git a/introspection/nm-manager-client.xml b/introspection/nm-manager-client.xml index 7a9e311ca7..9ede13e3c0 100644 --- a/introspection/nm-manager-client.xml +++ b/introspection/nm-manager-client.xml @@ -44,6 +44,14 @@ object. dbus-glib generates the same bound function names for D-Bus the methods + + + + + + + + diff --git a/introspection/nm-manager.xml b/introspection/nm-manager.xml index 46db5afd8b..df3a2ab3ef 100644 --- a/introspection/nm-manager.xml +++ b/introspection/nm-manager.xml @@ -116,6 +116,34 @@ + + + + + Returns the permissions a caller has for various authenticated operations + that NetworkManager provides, like Enable/Disable networking, changing + WiFi, WWAN, and WiMAX state, etc. + + + + Dictionary of available permissions and results. Each permission + is represented by a name (ie "org.freedesktop.NetworkManager.Foobar") + and each result is one of the following values: "yes" (the permission + is available), "auth" (the permission is available after a successful + authentication), or "no" (the permission is denied). Clients may use + these values in the UI to indicate the ability to perform certain + operations. + + + + + + + Emitted when system authorization details change, indicating that + clients may wish to recheck permissions with GetPermissions. + + + diff --git a/libnm-glib/libnm-glib.ver b/libnm-glib/libnm-glib.ver index 2b84dace1e..1c4d8f43c7 100644 --- a/libnm-glib/libnm-glib.ver +++ b/libnm-glib/libnm-glib.ver @@ -34,6 +34,7 @@ global: nm_client_get_device_by_path; nm_client_get_devices; nm_client_get_manager_running; + nm_client_get_permission_result; nm_client_get_state; nm_client_get_type; nm_client_networking_get_enabled; diff --git a/libnm-glib/nm-client.c b/libnm-glib/nm-client.c index 00d729a2ed..a1f986f493 100644 --- a/libnm-glib/nm-client.c +++ b/libnm-glib/nm-client.c @@ -58,6 +58,9 @@ typedef struct { GPtrArray *devices; GPtrArray *active_connections; + DBusGProxyCall *perm_call; + GHashTable *permissions; + gboolean have_networking_enabled; gboolean networking_enabled; gboolean wireless_enabled; @@ -84,6 +87,7 @@ enum { enum { DEVICE_ADDED, DEVICE_REMOVED, + PERMISSION_CHANGED, LAST_SIGNAL }; @@ -118,6 +122,8 @@ nm_client_init (NMClient *client) priv->state = NM_STATE_UNKNOWN; + priv->permissions = g_hash_table_new (g_direct_hash, g_direct_equal); + g_signal_connect (client, "notify::" NM_CLIENT_NETWORKING_ENABLED, G_CALLBACK (handle_net_enabled_changed), @@ -284,6 +290,114 @@ register_for_property_changed (NMClient *client) property_changed_info); } +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK "org.freedesktop.NetworkManager.enable-disable-network" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI "org.freedesktop.NetworkManager.enable-disable-wifi" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN "org.freedesktop.NetworkManager.enable-disable-wwan" +#define NM_AUTH_PERMISSION_USE_USER_CONNECTIONS "org.freedesktop.NetworkManager.use-user-connections" + +static NMClientPermission +nm_permission_to_client (const char *nm) +{ + if (!strcmp (nm, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK)) + return NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK; + else if (!strcmp (nm, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI)) + return NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI; + else if (!strcmp (nm, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN)) + return NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN; + else if (!strcmp (nm, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS)) + return NM_CLIENT_PERMISSION_USE_USER_CONNECTIONS; + return NM_CLIENT_PERMISSION_NONE; +} + +static NMClientPermissionResult +nm_permission_result_to_client (const char *nm) +{ + if (!strcmp (nm, "yes")) + return NM_CLIENT_PERMISSION_RESULT_YES; + else if (!strcmp (nm, "no")) + return NM_CLIENT_PERMISSION_RESULT_NO; + else if (!strcmp (nm, "auth")) + return NM_CLIENT_PERMISSION_RESULT_AUTH; + return NM_CLIENT_PERMISSION_RESULT_UNKNOWN; +} + +static void +get_permissions_reply (DBusGProxy *proxy, + GHashTable *permissions, + GError *error, + gpointer user_data) +{ + NMClient *self = NM_CLIENT (user_data); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + GHashTableIter iter; + gpointer key, value; + NMClientPermission perm; + NMClientPermissionResult perm_result; + GList *keys, *keys_iter; + + priv->perm_call = NULL; + + /* get list of old permissions for change notification */ + keys = g_hash_table_get_keys (priv->permissions); + g_hash_table_remove_all (priv->permissions); + + if (!error) { + /* Process new permissions */ + g_hash_table_iter_init (&iter, permissions); + while (g_hash_table_iter_next (&iter, &key, &value)) { + perm = nm_permission_to_client ((const char *) key); + perm_result = nm_permission_result_to_client ((const char *) value); + if (perm) { + g_hash_table_insert (priv->permissions, + GUINT_TO_POINTER (perm), + GUINT_TO_POINTER (perm_result)); + + /* Remove this permission from the list of previous permissions + * we'll be sending NM_CLIENT_PERMISSION_RESULT_UNKNOWN for + * in the change signal since it is still a known permission. + */ + keys = g_list_remove (keys, GUINT_TO_POINTER (perm)); + } + } + } + + /* Signal changes in all updated permissions */ + g_hash_table_iter_init (&iter, priv->permissions); + while (g_hash_table_iter_next (&iter, &key, &value)) { + g_signal_emit (self, signals[PERMISSION_CHANGED], 0, + GPOINTER_TO_UINT (key), + GPOINTER_TO_UINT (value)); + } + + /* And signal changes in all permissions that used to be valid but for + * some reason weren't received in the last request (if any). + */ + for (keys_iter = keys; keys_iter; keys_iter = g_list_next (keys_iter)) { + g_signal_emit (self, signals[PERMISSION_CHANGED], 0, + GPOINTER_TO_UINT (keys_iter->data), + NM_CLIENT_PERMISSION_RESULT_UNKNOWN); + } + g_list_free (keys); +} + +static DBusGProxyCall * +get_permissions (NMClient *self) +{ + return org_freedesktop_NetworkManager_get_permissions_async (NM_CLIENT_GET_PRIVATE (self)->client_proxy, + get_permissions_reply, + self); +} + +static void +client_recheck_permissions (DBusGProxy *proxy, gpointer user_data) +{ + NMClient *self = NM_CLIENT (user_data); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + + if (!priv->perm_call) + priv->perm_call = get_permissions (self); +} + static GObject* constructor (GType type, guint n_construct_params, @@ -324,6 +438,15 @@ constructor (GType type, object, NULL); + /* Permissions */ + dbus_g_proxy_add_signal (priv->client_proxy, "CheckPermissions", G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->client_proxy, + "CheckPermissions", + G_CALLBACK (client_recheck_permissions), + object, + NULL); + priv->perm_call = get_permissions (NM_CLIENT (object)); + priv->bus_proxy = dbus_g_proxy_new_for_name (connection, "org.freedesktop.DBus", "/org/freedesktop/DBus", @@ -381,12 +504,17 @@ dispose (GObject *object) return; } + if (priv->perm_call) + dbus_g_proxy_cancel_call (priv->client_proxy, priv->perm_call); + g_object_unref (priv->client_proxy); g_object_unref (priv->bus_proxy); free_object_array (&priv->devices); free_object_array (&priv->active_connections); + g_hash_table_destroy (priv->permissions); + G_OBJECT_CLASS (nm_client_parent_class)->dispose (object); } @@ -626,6 +754,22 @@ nm_client_class_init (NMClientClass *client_class) g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); + + /** + * NMClient::permission-changed: + * @widget: the client that received the signal + * @permission: a permission from #NMClientPermission + * @result: the permission's result, one of #NMClientPermissionResult + * + * Notifies that a permission has changed + **/ + signals[PERMISSION_CHANGED] = + g_signal_new ("permission-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + _nm_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); } /** @@ -1158,3 +1302,25 @@ nm_client_get_manager_running (NMClient *client) return NM_CLIENT_GET_PRIVATE (client)->manager_running; } +/** + * nm_client_get_permission_result: + * @client: a #NMClient + * @permission: the permission for which to return the result, one of #NMClientPermission + * + * Requests the result of a specific permission, which indicates whether the + * client can or cannot perform the action the permission represents + * + * Returns: the permission's result, one of #NMClientPermissionResult + **/ +NMClientPermissionResult +nm_client_get_permission_result (NMClient *client, NMClientPermission permission) +{ + gpointer result; + + g_return_val_if_fail (NM_IS_CLIENT (client), NM_CLIENT_PERMISSION_RESULT_UNKNOWN); + + result = g_hash_table_lookup (NM_CLIENT_GET_PRIVATE (client)->permissions, + GUINT_TO_POINTER (permission)); + return GPOINTER_TO_UINT (result); +} + diff --git a/libnm-glib/nm-client.h b/libnm-glib/nm-client.h index 6b912e05aa..c67e0d8f84 100644 --- a/libnm-glib/nm-client.h +++ b/libnm-glib/nm-client.h @@ -50,6 +50,25 @@ G_BEGIN_DECLS #define NM_CLIENT_WWAN_HARDWARE_ENABLED "wwan-hardware-enabled" #define NM_CLIENT_ACTIVE_CONNECTIONS "active-connections" +/* Permissions */ +typedef enum { + NM_CLIENT_PERMISSION_NONE = 0, + NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK = 1, + NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI = 2, + NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN = 3, + NM_CLIENT_PERMISSION_USE_USER_CONNECTIONS = 4, + + NM_CLIENT_PERMISSION_LAST = NM_CLIENT_PERMISSION_USE_USER_CONNECTIONS +} NMClientPermission; + +typedef enum { + NM_CLIENT_PERMISSION_RESULT_UNKNOWN = 0, + NM_CLIENT_PERMISSION_RESULT_YES, + NM_CLIENT_PERMISSION_RESULT_AUTH, + NM_CLIENT_PERMISSION_RESULT_NO +} NMClientPermissionResult; + + typedef struct { NMObject parent; } NMClient; @@ -105,6 +124,9 @@ gboolean nm_client_get_manager_running (NMClient *client); const GPtrArray *nm_client_get_active_connections (NMClient *client); void nm_client_sleep (NMClient *client, gboolean sleep); +NMClientPermissionResult nm_client_get_permission_result (NMClient *client, + NMClientPermission permission); + G_END_DECLS #endif /* NM_CLIENT_H */ diff --git a/po/POTFILES.in b/po/POTFILES.in index 4d7faa5873..22e8cf3822 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -11,6 +11,8 @@ libnm-util/crypto.c libnm-util/crypto_gnutls.c libnm-util/crypto_nss.c libnm-util/nm-utils.c +policy/org.freedesktop.network-manager-settings.system.policy.in +policy/org.freedesktop.NetworkManager.policy.in src/nm-netlink-monitor.c src/main.c src/dhcp-manager/nm-dhcp-dhclient.c @@ -19,5 +21,4 @@ src/logging/nm-logging.c src/named-manager/nm-named-manager.c src/system-settings/nm-default-wired-connection.c system-settings/plugins/ifcfg-rh/reader.c -policy/org.freedesktop.network-manager-settings.system.policy.in diff --git a/policy/Makefile.am b/policy/Makefile.am index 409e8edda8..4778ce230a 100644 --- a/policy/Makefile.am +++ b/policy/Makefile.am @@ -1,6 +1,9 @@ polkit_policydir = $(datadir)/polkit-1/actions -dist_polkit_policy_in_files = org.freedesktop.network-manager-settings.system.policy.in +dist_polkit_policy_in_files = \ + org.freedesktop.network-manager-settings.system.policy.in \ + org.freedesktop.NetworkManager.policy.in + dist_polkit_policy_DATA = $(dist_polkit_policy_in_files:.policy.in=.policy) @INTLTOOL_POLICY_RULE@ diff --git a/policy/org.freedesktop.NetworkManager.policy.in b/policy/org.freedesktop.NetworkManager.policy.in new file mode 100644 index 0000000000..fb8654c249 --- /dev/null +++ b/policy/org.freedesktop.NetworkManager.policy.in @@ -0,0 +1,49 @@ + + + + + + NetworkManager + http://www.gnome.org/projects/NetworkManager + nm-icon + + + <_description>Enable or disable system networking + <_message>System policy prevents enabling or disabling system networking + + no + yes + + + + + <_description>Enable or disable WiFi devices + <_message>System policy prevents enabling or disabling WiFi devices + + no + yes + + + + + <_description>Enable or disable mobile broadband devices + <_message>System policy prevents enabling or disabling mobile broadband devices + + no + yes + + + + + <_description>Allow use of user-specific connections + <_message>System policy prevents use of user-specific connections + + no + yes + + + + + diff --git a/src/Makefile.am b/src/Makefile.am index 5d2db6e36d..9a28751a30 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -133,6 +133,8 @@ NetworkManager_SOURCES = \ nm-system.h \ nm-manager.c \ nm-manager.h \ + nm-manager-auth.c \ + nm-manager-auth.h \ nm-netlink-monitor.c \ nm-netlink-monitor.h \ nm-activation-request.c \ @@ -211,6 +213,7 @@ NetworkManager_CPPFLAGS = \ $(GUDEV_CFLAGS) \ $(LIBNL_CFLAGS) \ $(GMODULE_CFLAGS) \ + $(POLKIT_CFLAGS) \ -DG_DISABLE_DEPRECATED \ -DBINDIR=\"$(bindir)\" \ -DSBINDIR=\"$(sbindir)\" \ @@ -242,6 +245,7 @@ NetworkManager_LDADD = \ $(GUDEV_LIBS) \ $(LIBNL_LIBS) \ $(GMODULE_LIBS) \ + $(POLKIT_LIBS) \ $(LIBM) \ $(LIBDL) diff --git a/src/nm-manager-auth.c b/src/nm-manager-auth.c new file mode 100644 index 0000000000..bc04e2544b --- /dev/null +++ b/src/nm-manager-auth.c @@ -0,0 +1,212 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2010 Red Hat, Inc. + */ + +#include "nm-manager-auth.h" +#include "nm-logging.h" + +#include +#include + +struct NMAuthChain { + guint32 refcount; + PolkitAuthority *authority; + GSList *calls; + + DBusGMethodInvocation *context; + GError *error; + + NMAuthChainResultFunc done_func; + NMAuthChainCallFunc call_func; + gpointer user_data; + gpointer user_data2; +}; + +typedef struct { + NMAuthChain *chain; + GCancellable *cancellable; + char *permission; + gboolean disposed; +} PolkitCall; + + +NMAuthChain * +nm_auth_chain_new (PolkitAuthority *authority, + DBusGMethodInvocation *context, + NMAuthChainResultFunc done_func, + NMAuthChainCallFunc call_func, + gpointer user_data, + gpointer user_data2) +{ + NMAuthChain *self; + + self = g_malloc0 (sizeof (NMAuthChain)); + self->refcount = 1; + self->authority = g_object_ref (authority); + self->done_func = done_func; + self->call_func = call_func; + self->context = context; + self->user_data = user_data; + self->user_data2 = user_data2; + return self; +} + +static void +nm_auth_chain_check_done (NMAuthChain *self) +{ + g_return_if_fail (self != NULL); + + if (g_slist_length (self->calls) == 0) { + /* Ensure we say alive across the callback */ + self->refcount++; + self->done_func (self, self->error, self->context, self->user_data, self->user_data2); + nm_auth_chain_unref (self); + } +} + +static void +polkit_call_cancel (PolkitCall *call) +{ + call->disposed = TRUE; + g_cancellable_cancel (call->cancellable); +} + +static void +polkit_call_free (PolkitCall *call) +{ + g_return_if_fail (call != NULL); + + call->disposed = TRUE; + g_free (call->permission); + call->permission = NULL; + call->chain = NULL; + g_object_unref (call->cancellable); + call->cancellable = NULL; + g_free (call); +} + +static void +pk_call_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + PolkitCall *call = user_data; + NMAuthChain *chain; + PolkitAuthorizationResult *pk_result; + GError *error = NULL; + guint call_result = NM_AUTH_CALL_RESULT_UNKNOWN; + + /* If the call is already disposed do nothing */ + if (call->disposed) { + polkit_call_free (call); + return; + } + + chain = call->chain; + chain->calls = g_slist_remove (chain->calls, call); + + pk_result = polkit_authority_check_authorization_finish (chain->authority, + result, + &error); + if (error) { + if (!chain->error) + chain->error = g_error_copy (error); + + nm_log_warn (LOGD_CORE, "error requesting auth for %s: (%d) %s", + call->permission, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + } else { + if (polkit_authorization_result_get_is_authorized (pk_result)) { + /* Caller has the permission */ + call_result = NM_AUTH_CALL_RESULT_YES; + } else if (polkit_authorization_result_get_is_challenge (pk_result)) { + /* Caller could authenticate to get the permission */ + call_result = NM_AUTH_CALL_RESULT_AUTH; + } else + call_result = NM_AUTH_CALL_RESULT_NO; + } + + chain->call_func (chain, call->permission, error, call_result, chain->user_data, chain->user_data2); + nm_auth_chain_check_done (chain); + + g_clear_error (&error); + polkit_call_free (call); + if (pk_result) + g_object_unref (pk_result); +} + +gboolean +nm_auth_chain_add_call (NMAuthChain *self, + const char *permission) +{ + PolkitCall *call; + char *sender; + PolkitSubject *subject; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (self->context != NULL, FALSE); + g_return_val_if_fail (permission != NULL, FALSE); + + sender = dbus_g_method_get_sender (self->context); + subject = polkit_system_bus_name_new (sender); + g_free (sender); + if (!subject) + return FALSE; + + call = g_malloc0 (sizeof (PolkitCall)); + call->chain = self; + call->permission = g_strdup (permission); + call->cancellable = g_cancellable_new (); + + self->calls = g_slist_append (self->calls, call); + + polkit_authority_check_authorization (self->authority, + subject, + permission, + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE, + call->cancellable, + pk_call_cb, + call); + g_object_unref (subject); + return TRUE; +} + +void +nm_auth_chain_unref (NMAuthChain *self) +{ + GSList *iter; + + g_return_if_fail (self != NULL); + + self->refcount--; + if (self->refcount > 0) + return; + + g_object_unref (self->authority); + + for (iter = self->calls; iter; iter = g_slist_next (iter)) + polkit_call_cancel ((PolkitCall *) iter->data); + g_slist_free (self->calls); + + g_clear_error (&self->error); + + memset (self, 0, sizeof (NMAuthChain)); + g_free (self); +} + diff --git a/src/nm-manager-auth.h b/src/nm-manager-auth.h new file mode 100644 index 0000000000..968b28eafe --- /dev/null +++ b/src/nm-manager-auth.h @@ -0,0 +1,69 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2010 Red Hat, Inc. + */ + +#ifndef NM_MANAGER_AUTH_H +#define NM_MANAGER_AUTH_H + +#include +#include +#include + +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK "org.freedesktop.NetworkManager.enable-disable-network" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI "org.freedesktop.NetworkManager.enable-disable-wifi" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN "org.freedesktop.NetworkManager.enable-disable-wwan" +#define NM_AUTH_PERMISSION_USE_USER_CONNECTIONS "org.freedesktop.NetworkManager.use-user-connections" + + +typedef struct NMAuthChain NMAuthChain; + +enum { + NM_AUTH_CALL_RESULT_UNKNOWN, + NM_AUTH_CALL_RESULT_YES, + NM_AUTH_CALL_RESULT_AUTH, + NM_AUTH_CALL_RESULT_NO, +}; + +typedef void (*NMAuthChainResultFunc) (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data, + gpointer user_data2); + +typedef void (*NMAuthChainCallFunc) (NMAuthChain *chain, + const char *permission, + GError *error, + guint result, + gpointer user_data, + gpointer user_data2); + +NMAuthChain *nm_auth_chain_new (PolkitAuthority *authority, + DBusGMethodInvocation *context, + NMAuthChainResultFunc done_func, + NMAuthChainCallFunc call_func, + gpointer user_data, + gpointer user_data2); + +gboolean nm_auth_chain_add_call (NMAuthChain *chain, + const char *permission); + +void nm_auth_chain_unref (NMAuthChain *chain); + +#endif /* NM_MANAGER_AUTH_H */ + diff --git a/src/nm-manager.c b/src/nm-manager.c index 7d46285b35..f37c4a6bad 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -54,6 +54,7 @@ #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" @@ -74,6 +75,9 @@ static gboolean impl_manager_sleep (NMManager *manager, gboolean sleep, GError * static gboolean impl_manager_enable (NMManager *manager, gboolean enable, GError **err); +static void impl_manager_get_permissions (NMManager *manager, + DBusGMethodInvocation *context); + static gboolean impl_manager_set_logging (NMManager *manager, const char *level, const char *domains, @@ -196,6 +200,10 @@ typedef struct { DBusGProxy *aipd_proxy; + PolkitAuthority *authority; + guint auth_changed_id; + GSList *auth_chains; + gboolean disposed; } NMManagerPrivate; @@ -215,6 +223,7 @@ enum { CONNECTION_ADDED, CONNECTION_UPDATED, CONNECTION_REMOVED, + CHECK_PERMISSIONS, LAST_SIGNAL }; @@ -2792,6 +2801,109 @@ impl_manager_enable (NMManager *self, gboolean enable, GError **error) return TRUE; } +/* Permissions */ + +static void +pk_authority_changed_cb (GObject *object, gpointer user_data) +{ + /* Let clients know they should re-check their authorization */ + g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0); +} + +static void +get_permissions_done_cb (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data, + gpointer user_data2) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GHashTable *hash = user_data2; + GError *ret_error; + + 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 { + g_assert (user_data2); + dbus_g_method_return (context, hash); + } + + g_hash_table_destroy (hash); + nm_auth_chain_unref (chain); +} + +static void +get_permissions_call_done_cb (NMAuthChain *chain, + const char *permission, + GError *error, + guint result, + gpointer user_data, + gpointer user_data2) +{ + GHashTable *hash = user_data2; + const char *str_result = NULL; + + if (!error) { + g_assert (result != NM_AUTH_CALL_RESULT_UNKNOWN); + + if (result == NM_AUTH_CALL_RESULT_YES) + str_result = "yes"; + else if (result == NM_AUTH_CALL_RESULT_NO) + str_result = "no"; + else if (result == NM_AUTH_CALL_RESULT_AUTH) + str_result = "auth"; + else { + nm_log_dbg (LOGD_CORE, "unknown auth chain result %d", result); + } + + if (str_result) + g_hash_table_insert (hash, g_strdup (permission), g_strdup (str_result)); + } +} + +static void +impl_manager_get_permissions (NMManager *self, + DBusGMethodInvocation *context) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMAuthChain *chain; + GHashTable *results; + + if (!priv->authority) { + GError *error; + + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Permissions request failed: PolicyKit not initialized"); + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + results = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + chain = nm_auth_chain_new (priv->authority, + context, + get_permissions_done_cb, + get_permissions_call_done_cb, + self, + results); + 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); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS); +} + /* Legacy 0.6 compatibility interface */ static gboolean @@ -3078,6 +3190,10 @@ dispose (GObject *object) pending_connection_info_destroy (priv->pending_connection_info); priv->pending_connection_info = 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); @@ -3286,6 +3402,15 @@ nm_manager_init (NMManager *manager) NULL); } else nm_log_warn (LOGD_AUTOIP4, "could not initialize avahi-autoipd D-Bus proxy"); + + priv->authority = polkit_authority_get (); + 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."); } static void @@ -3445,6 +3570,14 @@ 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); + /* StateChange is DEPRECATED */ signals[STATE_CHANGE] = g_signal_new ("state-change",