diff --git a/introspection/nm-settings-system.xml b/introspection/nm-settings-system.xml
index ff98719f8f..598274c0d1 100644
--- a/introspection/nm-settings-system.xml
+++ b/introspection/nm-settings-system.xml
@@ -39,6 +39,43 @@
+
+
+ Emitted when system authorization details change, indicating that clients may wish to recheck permissions with GetPermissions.
+
+
+
+
+
+ Returns a bitfield indicating certain operations the caller is permitted to perform. Some of these operations may require authorization by the user.
+
+
+
+
+
+ A bitfield of permitted operations. Some of these operations may require the user to authorize via password entry or other means.
+
+
+
+
+
+
+ No permissions.
+
+
+ Can modify/add/delete connections.
+
+
+ Can share connections via a encrypted user-created WiFi network.
+
+
+ Can share connections via a open/unencrypted user-created WiFi network.
+
+
+ Can modify the persistent system hostname.
+
+
+
diff --git a/libnm-glib/libnm_glib.ver b/libnm-glib/libnm_glib.ver
index f1ea6f0395..342f713dcc 100644
--- a/libnm-glib/libnm_glib.ver
+++ b/libnm-glib/libnm_glib.ver
@@ -135,6 +135,7 @@ global:
nm_settings_service_get_type;
nm_settings_system_interface_get_type;
nm_settings_system_interface_add_connection;
+ nm_settings_system_interface_get_permissions;
nm_settings_system_interface_save_hostname;
nm_settings_error_quark;
nm_settings_get_type;
diff --git a/libnm-glib/nm-remote-settings-system.c b/libnm-glib/nm-remote-settings-system.c
index fb9b0e2abc..34a810d2e2 100644
--- a/libnm-glib/nm-remote-settings-system.c
+++ b/libnm-glib/nm-remote-settings-system.c
@@ -105,9 +105,53 @@ save_hostname (NMSettingsSystemInterface *settings,
NMSettingsSystemSaveHostnameFunc callback,
gpointer user_data)
{
+ // FIXME: implement using get_permissions as a template
return FALSE;
}
+typedef struct {
+ NMSettingsSystemInterface *settings;
+ NMSettingsSystemGetPermissionsFunc callback;
+ gpointer callback_data;
+} GetPermissionsInfo;
+
+static void
+get_permissions_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ GetPermissionsInfo *info = user_data;
+ NMSettingsSystemPermission permissions = NM_SETTINGS_SYSTEM_PERMISSION_NONE;
+ GError *error = NULL;
+
+ dbus_g_proxy_end_call (proxy, call, &error,
+ G_TYPE_UINT, &permissions,
+ G_TYPE_INVALID);
+ info->callback (info->settings, permissions, error, info->callback_data);
+ g_clear_error (&error);
+}
+
+static gboolean
+get_permissions (NMSettingsSystemInterface *settings,
+ NMSettingsSystemGetPermissionsFunc callback,
+ gpointer user_data)
+{
+ NMRemoteSettingsSystemPrivate *priv = NM_REMOTE_SETTINGS_SYSTEM_GET_PRIVATE (settings);
+ GetPermissionsInfo *info;
+
+ info = g_malloc0 (sizeof (GetPermissionsInfo));
+ info->settings = settings;
+ info->callback = callback;
+ info->callback_data = user_data;
+
+ dbus_g_proxy_begin_call (priv->proxy, "GetPermissions",
+ get_permissions_cb,
+ info,
+ g_free,
+ G_TYPE_INVALID);
+ return TRUE;
+}
+
/****************************************************************/
static void
@@ -115,6 +159,7 @@ settings_system_interface_init (NMSettingsSystemInterface *klass)
{
/* interface implementation */
klass->save_hostname = save_hostname;
+ klass->get_permissions = get_permissions;
}
/**
diff --git a/libnm-glib/nm-settings-system-interface.c b/libnm-glib/nm-settings-system-interface.c
index 07114c0fc0..8403793b69 100644
--- a/libnm-glib/nm-settings-system-interface.c
+++ b/libnm-glib/nm-settings-system-interface.c
@@ -56,9 +56,33 @@ nm_settings_system_interface_save_hostname (NMSettingsSystemInterface *settings,
return FALSE;
}
+/**
+ * nm_settings_system_interface_get_permissions:
+ * @settings: a object implementing %NMSettingsSystemInterface
+ * @callback: callback to be called when the permissions operation completes
+ * @user_data: caller-specific data passed to @callback
+ *
+ * Requests an indication of the operations the caller is permitted to perform
+ * including those that may require authorization.
+ **/
+gboolean
+nm_settings_system_interface_get_permissions (NMSettingsSystemInterface *settings,
+ NMSettingsSystemGetPermissionsFunc callback,
+ gpointer user_data)
+{
+ g_return_val_if_fail (settings != NULL, FALSE);
+ g_return_val_if_fail (NM_IS_SETTINGS_SYSTEM_INTERFACE (settings), FALSE);
+ g_return_val_if_fail (callback != NULL, FALSE);
+
+ if (NM_SETTINGS_SYSTEM_INTERFACE_GET_INTERFACE (settings)->get_permissions)
+ return NM_SETTINGS_SYSTEM_INTERFACE_GET_INTERFACE (settings)->get_permissions (settings, callback, user_data);
+ return FALSE;
+}
+
static void
nm_settings_system_interface_init (gpointer g_iface)
{
+ GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
static gboolean initialized = FALSE;
if (initialized)
@@ -81,6 +105,15 @@ nm_settings_system_interface_init (gpointer g_iface)
FALSE,
G_PARAM_READABLE));
+ /* Signals */
+ g_signal_new (NM_SETTINGS_SYSTEM_INTERFACE_CHECK_PERMISSIONS,
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMSettingsSystemInterface, check_permissions),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
initialized = TRUE;
}
diff --git a/libnm-glib/nm-settings-system-interface.h b/libnm-glib/nm-settings-system-interface.h
index 6f535aaa6a..b4ea39e30b 100644
--- a/libnm-glib/nm-settings-system-interface.h
+++ b/libnm-glib/nm-settings-system-interface.h
@@ -26,6 +26,14 @@
#include "NetworkManager.h"
+typedef enum {
+ NM_SETTINGS_SYSTEM_PERMISSION_NONE = 0x0,
+ NM_SETTINGS_SYSTEM_PERMISSION_CONNECTION_MODIFY = 0x1,
+ NM_SETTINGS_SYSTEM_PERMISSION_WIFI_SHARE_PROTECTED = 0x2,
+ NM_SETTINGS_SYSTEM_PERMISSION_WIFI_SHARE_OPEN = 0x4,
+ NM_SETTINGS_SYSTEM_PERMISSION_HOSTNAME_MODIFY = 0x8
+} NMSettingsSystemPermission;
+
#define NM_TYPE_SETTINGS_SYSTEM_INTERFACE (nm_settings_system_interface_get_type ())
#define NM_SETTINGS_SYSTEM_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS_SYSTEM_INTERFACE, NMSettingsSystemInterface))
#define NM_IS_SETTINGS_SYSTEM_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTINGS_SYSTEM_INTERFACE))
@@ -34,6 +42,8 @@
#define NM_SETTINGS_SYSTEM_INTERFACE_HOSTNAME "hostname"
#define NM_SETTINGS_SYSTEM_INTERFACE_CAN_MODIFY "can-modify"
+#define NM_SETTINGS_SYSTEM_INTERFACE_CHECK_PERMISSIONS "check-permissions"
+
typedef enum {
NM_SETTINGS_SYSTEM_INTERFACE_PROP_FIRST = 0x1000,
@@ -49,6 +59,11 @@ typedef void (*NMSettingsSystemSaveHostnameFunc) (NMSettingsSystemInterface *set
GError *error,
gpointer user_data);
+typedef void (*NMSettingsSystemGetPermissionsFunc) (NMSettingsSystemInterface *settings,
+ NMSettingsSystemPermission permissions,
+ GError *error,
+ gpointer user_data);
+
struct _NMSettingsSystemInterface {
GTypeInterface g_iface;
@@ -57,6 +72,13 @@ struct _NMSettingsSystemInterface {
const char *hostname,
NMSettingsSystemSaveHostnameFunc callback,
gpointer user_data);
+
+ gboolean (*get_permissions) (NMSettingsSystemInterface *settings,
+ NMSettingsSystemGetPermissionsFunc callback,
+ gpointer user_data);
+
+ /* Signals */
+ void (*check_permissions) (NMSettingsSystemInterface *settings);
};
GType nm_settings_system_interface_get_type (void);
@@ -66,4 +88,8 @@ gboolean nm_settings_system_interface_save_hostname (NMSettingsSystemInterface *
NMSettingsSystemSaveHostnameFunc callback,
gpointer user_data);
+gboolean nm_settings_system_interface_get_permissions (NMSettingsSystemInterface *settings,
+ NMSettingsSystemGetPermissionsFunc callback,
+ gpointer user_data);
+
#endif /* NM_SETTINGS_SYSTEM_INTERFACE_H */
diff --git a/policy/org.freedesktop.network-manager-settings.system.policy.in b/policy/org.freedesktop.network-manager-settings.system.policy.in
index 8b24a23b13..620e3a6a58 100644
--- a/policy/org.freedesktop.network-manager-settings.system.policy.in
+++ b/policy/org.freedesktop.network-manager-settings.system.policy.in
@@ -18,4 +18,31 @@
+
+ <_description>Modify persistent system hostname
+ <_message>System policy prevents modification of the persistent system hostname
+
+ no
+ auth_admin_keep
+
+
+
+
+ <_description>Connection sharing via a protected WiFi network
+ <_message>System policy prevents sharing connections via a protected WiFi network
+
+ no
+ yes
+
+
+
+
+ <_description>Connection sharing via an open WiFi network
+ <_message>System policy prevents sharing connections via an open WiFi network
+
+ no
+ yes
+
+
+
diff --git a/src/system-settings/nm-polkit-helpers.h b/src/system-settings/nm-polkit-helpers.h
index af7cd7a95c..c26fcc2c9b 100644
--- a/src/system-settings/nm-polkit-helpers.h
+++ b/src/system-settings/nm-polkit-helpers.h
@@ -24,6 +24,9 @@
#include
-#define NM_SYSCONFIG_POLICY_ACTION "org.freedesktop.network-manager-settings.system.modify"
+#define NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY "org.freedesktop.network-manager-settings.system.modify"
+#define NM_SYSCONFIG_POLICY_ACTION_WIFI_SHARE_PROTECTED "org.freedesktop.network-manager-settings.system.wifi.share.protected"
+#define NM_SYSCONFIG_POLICY_ACTION_WIFI_SHARE_OPEN "org.freedesktop.network-manager-settings.system.wifi.share.open"
+#define NM_SYSCONFIG_POLICY_ACTION_HOSTNAME_MODIFY "org.freedesktop.network-manager-settings.system.hostname.modify"
#endif /* NM_POLKIT_HELPERS_H */
diff --git a/src/system-settings/nm-sysconfig-connection.c b/src/system-settings/nm-sysconfig-connection.c
index b711c232b0..8be95f5a90 100644
--- a/src/system-settings/nm-sysconfig-connection.c
+++ b/src/system-settings/nm-sysconfig-connection.c
@@ -296,7 +296,7 @@ dbus_update (NMExportedConnection *exported,
g_assert (call);
polkit_authority_check_authorization (priv->authority,
call->subject,
- NM_SYSCONFIG_POLICY_ACTION,
+ NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY,
NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
call->cancellable,
@@ -371,7 +371,7 @@ dbus_delete (NMExportedConnection *exported,
g_assert (call);
polkit_authority_check_authorization (priv->authority,
call->subject,
- NM_SYSCONFIG_POLICY_ACTION,
+ NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY,
NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
call->cancellable,
@@ -453,7 +453,7 @@ dbus_get_secrets (NMExportedConnection *exported,
g_assert (call);
polkit_authority_check_authorization (priv->authority,
call->subject,
- NM_SYSCONFIG_POLICY_ACTION,
+ NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY,
NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
call->cancellable,
diff --git a/src/system-settings/nm-sysconfig-settings.c b/src/system-settings/nm-sysconfig-settings.c
index f07ab8da85..6f925de135 100644
--- a/src/system-settings/nm-sysconfig-settings.c
+++ b/src/system-settings/nm-sysconfig-settings.c
@@ -70,6 +70,9 @@ static void impl_settings_save_hostname (NMSysconfigSettings *self,
const char *hostname,
DBusGMethodInvocation *context);
+static void impl_settings_get_permissions (NMSysconfigSettings *self,
+ DBusGMethodInvocation *context);
+
#include "nm-settings-system-glue.h"
static void unmanaged_specs_changed (NMSystemConfigInterface *config, gpointer user_data);
@@ -513,15 +516,19 @@ typedef struct {
NMSysconfigSettings *self;
DBusGMethodInvocation *context;
PolkitSubject *subject;
- GCancellable *cancellable;
NMConnection *connection;
NMSettingsAddConnectionFunc callback;
gpointer callback_data;
char *hostname;
+
+ NMSettingsSystemPermission permissions;
+ guint32 permissions_calls;
} PolkitCall;
+#include "nm-dbus-manager.h"
+
static PolkitCall *
polkit_call_new (NMSysconfigSettings *self,
DBusGMethodInvocation *context,
@@ -547,7 +554,6 @@ polkit_call_new (NMSysconfigSettings *self,
}
if (hostname)
call->hostname = g_strdup (hostname);
- call->cancellable = g_cancellable_new ();
sender = dbus_g_method_get_sender (context);
call->subject = polkit_system_bus_name_new (sender);
@@ -563,7 +569,6 @@ polkit_call_free (PolkitCall *call)
g_object_unref (call->connection);
g_free (call->hostname);
g_object_unref (call->subject);
- g_object_unref (call->cancellable);
g_free (call);
}
@@ -670,10 +675,10 @@ add_connection (NMSettingsService *service,
g_assert (call);
polkit_authority_check_authorization (priv->authority,
call->subject,
- NM_SYSCONFIG_POLICY_ACTION,
+ NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY,
NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
- call->cancellable,
+ NULL,
pk_add_cb,
call);
}
@@ -757,14 +762,169 @@ impl_settings_save_hostname (NMSysconfigSettings *self,
g_assert (call);
polkit_authority_check_authorization (priv->authority,
call->subject,
- NM_SYSCONFIG_POLICY_ACTION,
+ NM_SYSCONFIG_POLICY_ACTION_HOSTNAME_MODIFY,
NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
- call->cancellable,
+ NULL,
pk_hostname_cb,
call);
}
+static void
+pk_authority_changed_cb (GObject *object, gpointer user_data)
+{
+ /* Let clients know they should re-check their authorization */
+ g_signal_emit_by_name (NM_SYSCONFIG_SETTINGS (user_data),
+ NM_SETTINGS_SYSTEM_INTERFACE_CHECK_PERMISSIONS);
+}
+
+typedef struct {
+ PolkitCall *pk_call;
+ const char *pk_action;
+ NMSettingsSystemPermission permission;
+} PermissionsCall;
+
+static void
+permission_call_done (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+ PermissionsCall *call = user_data;
+ PolkitCall *pk_call = call->pk_call;
+ NMSysconfigSettings *self = pk_call->self;
+ NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+ PolkitAuthorizationResult *pk_result;
+ GError *error = NULL;
+
+ pk_result = polkit_authority_check_authorization_finish (priv->authority,
+ result,
+ &error);
+ /* Some random error happened */
+ if (error) {
+ g_warning ("%s.%d (%s): error checking '%s' permission: (%d) %s",
+ __FILE__, __LINE__, __func__,
+ call->pk_action,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ if (error)
+ g_error_free (error);
+ } else {
+ /* If the caller is authorized, or the caller could authorize via a
+ * challenge, then authorization is possible. Otherwise, caller is out of
+ * luck.
+ */
+ if ( polkit_authorization_result_get_is_authorized (pk_result)
+ || polkit_authorization_result_get_is_challenge (pk_result))
+ pk_call->permissions |= call->permission;
+ }
+
+ g_object_unref (pk_result);
+
+ pk_call->permissions_calls--;
+ if (pk_call->permissions_calls == 0) {
+ /* All the permissions calls are done, return the full permissions
+ * bitfield back to the user.
+ */
+ dbus_g_method_return (pk_call->context, pk_call->permissions);
+
+ polkit_call_free (pk_call);
+ }
+ memset (call, 0, sizeof (PermissionsCall));
+ g_free (call);
+}
+
+static void
+start_permission_check (NMSysconfigSettings *self,
+ PolkitCall *pk_call,
+ const char *pk_action,
+ NMSettingsSystemPermission permission)
+{
+ NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+ PermissionsCall *call;
+
+ g_return_if_fail (pk_call != NULL);
+ g_return_if_fail (pk_action != NULL);
+ g_return_if_fail (permission != NM_SETTINGS_SYSTEM_PERMISSION_NONE);
+
+ call = g_malloc0 (sizeof (PermissionsCall));
+ call->pk_call = pk_call;
+ call->pk_action = pk_action;
+ call->permission = permission;
+
+ pk_call->permissions_calls++;
+
+ polkit_authority_check_authorization (priv->authority,
+ pk_call->subject,
+ pk_action,
+ NULL,
+ 0,
+ NULL,
+ permission_call_done,
+ call);
+}
+
+static void
+impl_settings_get_permissions (NMSysconfigSettings *self,
+ DBusGMethodInvocation *context)
+{
+ PolkitCall *call;
+
+ call = polkit_call_new (self, context, NULL, NULL, NULL, FALSE);
+ g_assert (call);
+
+ /* Start checks for the various permissions */
+
+ /* Only check for connection-modify if one of our plugins supports it. */
+ if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)) {
+ start_permission_check (self, call,
+ NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY,
+ NM_SETTINGS_SYSTEM_PERMISSION_CONNECTION_MODIFY);
+ }
+
+ /* Only check for hostname-modify if one of our plugins supports it. */
+ if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME)) {
+ start_permission_check (self, call,
+ NM_SYSCONFIG_POLICY_ACTION_HOSTNAME_MODIFY,
+ NM_SETTINGS_SYSTEM_PERMISSION_HOSTNAME_MODIFY);
+ }
+
+ // FIXME: hook these into plugin permissions like the modify permissions */
+ start_permission_check (self, call,
+ NM_SYSCONFIG_POLICY_ACTION_WIFI_SHARE_OPEN,
+ NM_SETTINGS_SYSTEM_PERMISSION_WIFI_SHARE_OPEN);
+ start_permission_check (self, call,
+ NM_SYSCONFIG_POLICY_ACTION_WIFI_SHARE_PROTECTED,
+ NM_SETTINGS_SYSTEM_PERMISSION_WIFI_SHARE_PROTECTED);
+}
+
+static gboolean
+get_permissions (NMSettingsSystemInterface *settings,
+ NMSettingsSystemGetPermissionsFunc callback,
+ gpointer user_data)
+{
+ NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (settings);
+ NMSettingsSystemPermission permissions = NM_SETTINGS_SYSTEM_PERMISSION_NONE;
+
+ /* Local caller (ie, NM) gets full permissions by default because it doesn't
+ * need authorization. However, permissions are still subject to plugin's
+ * restrictions. i.e. if no plugins support connection-modify, then even
+ * the local caller won't get that permission.
+ */
+
+ /* Only check for connection-modify if one of our plugins supports it. */
+ if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS))
+ permissions |= NM_SETTINGS_SYSTEM_PERMISSION_CONNECTION_MODIFY;
+
+ /* Only check for hostname-modify if one of our plugins supports it. */
+ if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME))
+ permissions |= NM_SETTINGS_SYSTEM_PERMISSION_HOSTNAME_MODIFY;
+
+ // FIXME: hook these into plugin permissions like the modify permissions */
+ permissions |= NM_SETTINGS_SYSTEM_PERMISSION_WIFI_SHARE_OPEN;
+ permissions |= NM_SETTINGS_SYSTEM_PERMISSION_WIFI_SHARE_PROTECTED;
+
+ callback (settings, permissions, NULL, user_data);
+ return TRUE;
+}
+
static gboolean
have_connection_for_device (NMSysconfigSettings *self, GByteArray *mac)
{
@@ -1115,6 +1275,8 @@ finalize (GObject *object)
static void
settings_system_interface_init (NMSettingsSystemInterface *iface)
{
+ iface->get_permissions = get_permissions;
+
dbus_g_object_type_install_info (G_TYPE_FROM_INTERFACE (iface),
&dbus_glib_nm_settings_system_object_info);
}
@@ -1206,7 +1368,9 @@ nm_sysconfig_settings_init (NMSysconfigSettings *self)
priv->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
priv->authority = polkit_authority_get ();
- if (!priv->authority)
+ if (priv->authority)
+ g_signal_connect (priv->authority, "changed", G_CALLBACK (pk_authority_changed_cb), self);
+ else
g_warning ("%s: failed to create PolicyKit authority.", __func__);
/* Grab hostname on startup and use that if no plugins provide one */