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 */