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",