core: add permissions framework for various operations (rh #585182) (bgo #619323)
authorDan Williams <dcbw@redhat.com>
Sat, 29 May 2010 01:23:00 +0000 (18:23 -0700)
committerDan Williams <dcbw@redhat.com>
Sat, 29 May 2010 01:23:00 +0000 (18:23 -0700)
12 files changed:
introspection/nm-manager-client.xml
introspection/nm-manager.xml
libnm-glib/libnm-glib.ver
libnm-glib/nm-client.c
libnm-glib/nm-client.h
po/POTFILES.in
policy/Makefile.am
policy/org.freedesktop.NetworkManager.policy.in [new file with mode: 0644]
src/Makefile.am
src/nm-manager-auth.c [new file with mode: 0644]
src/nm-manager-auth.h [new file with mode: 0644]
src/nm-manager.c

index 7a9e311..9ede13e 100644 (file)
@@ -44,6 +44,14 @@ object.  dbus-glib generates the same bound function names for D-Bus the methods
       <arg name="enable" type="b" direction="in"/>
     </method>
 
+    <method name="GetPermissions">
+      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_manager_get_permissions"/>
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="permissions" type="a{ss}" direction="out"/>
+    </method>
+
+    <signal name="CheckPermissions"/>
+
     <property name="NetworkingEnabled" type="b" access="read"/>
     <property name="WirelessEnabled" type="b" access="readwrite"/>
     <property name="WirelessHardwareEnabled" type="b" access="read"/>
index 46db5af..df3a2ab 100644 (file)
       </arg>
     </method>
 
+    <method name="GetPermissions">
+      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_manager_get_permissions"/>
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <tp:docstring>
+        Returns the permissions a caller has for various authenticated operations
+        that NetworkManager provides, like Enable/Disable networking, changing
+        WiFi, WWAN, and WiMAX state, etc.
+      </tp:docstring>
+      <arg name="permissions" type="a{ss}" direction="out">
+        <tp:docstring>
+          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.
+        </tp:docstring>
+      </arg>
+    </method>
+
+    <signal name="CheckPermissions">
+      <tp:docstring>
+        Emitted when system authorization details change, indicating that
+        clients may wish to recheck permissions with GetPermissions.
+      </tp:docstring>
+    </signal>
+
     <method name="SetLogging">
       <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_manager_set_logging"/>
       <tp:docstring>
index 2b84dac..1c4d8f4 100644 (file)
@@ -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;
index 00d729a..a1f986f 100644 (file)
@@ -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);
+}
+
index 6b912e0..c67e0d8 100644 (file)
@@ -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 */
index 4d7faa5..22e8cf3 100644 (file)
@@ -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
 
index 409e8ed..4778ce2 100644 (file)
@@ -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 (file)
index 0000000..fb8654c
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+
+<policyconfig>
+
+  <vendor>NetworkManager</vendor>
+  <vendor_url>http://www.gnome.org/projects/NetworkManager</vendor_url>
+  <icon_name>nm-icon</icon_name>
+
+  <action id="org.freedesktop.NetworkManager.enable-disable-network">
+    <_description>Enable or disable system networking</_description>
+    <_message>System policy prevents enabling or disabling system networking</_message>
+    <defaults>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>yes</allow_active>
+    </defaults>
+  </action>
+
+  <action id="org.freedesktop.NetworkManager.enable-disable-wifi">
+    <_description>Enable or disable WiFi devices</_description>
+    <_message>System policy prevents enabling or disabling WiFi devices</_message>
+    <defaults>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>yes</allow_active>
+    </defaults>
+  </action>
+
+  <action id="org.freedesktop.NetworkManager.enable-disable-wwan">
+    <_description>Enable or disable mobile broadband devices</_description>
+    <_message>System policy prevents enabling or disabling mobile broadband devices</_message>
+    <defaults>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>yes</allow_active>
+    </defaults>
+  </action>
+
+  <action id="org.freedesktop.NetworkManager.use-user-connections">
+    <_description>Allow use of user-specific connections</_description>
+    <_message>System policy prevents use of user-specific connections</_message>
+    <defaults>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>yes</allow_active>
+    </defaults>
+  </action>
+
+</policyconfig>
+
index 5d2db6e..9a28751 100644 (file)
@@ -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 (file)
index 0000000..bc04e25
--- /dev/null
@@ -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 <dbus/dbus-glib-lowlevel.h>
+#include <string.h>
+
+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 (file)
index 0000000..968b28e
--- /dev/null
@@ -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 <polkit/polkit.h>
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+
+#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 */
+
index 7d46285..f37c4a6 100644 (file)
@@ -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",