WIP: NMSysconfig: enforce access controls

This commit is contained in:
Daniel Gnoutcheff
2010-08-15 03:25:58 -04:00
parent b0fb908e18
commit 6ebc201636
4 changed files with 562 additions and 291 deletions

View File

@@ -33,6 +33,7 @@
Get the settings maps describing this object.
</tp:docstring>
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_sysconfig_connection_get_settings"/>
<annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
<arg name="settings" type="a{sa{sv}}" direction="out" tp:type="String_String_Variant_Map_Map">
<tp:docstring>
The nested settings maps describing this object.
@@ -53,7 +54,13 @@
<signal name="Removed">
<tp:docstring>
Emitted when this connection has been deleted/removed. After receipt of this signal, the object no longer exists.
Emitted when this connection is no longer available. This happens when the connection is deleted or if it is no longer accessable by any of the system's logged in users. After receipt of this signal, the object no longer exists.
</tp:docstring>
</signal>
<signal name="CheckPermissions">
<tp:docstring>
Emitted when a client's permission to access this connection may have changed. Generally, clients should re-test their ability to access this connecton whenever this signal is emmited.
</tp:docstring>
</signal>

View File

@@ -22,16 +22,18 @@
#include <NetworkManager.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <nm-setting-connection.h>
#include <nm-utils.h>
#include "nm-sysconfig-connection.h"
#include "nm-session-manager.h"
#include "nm-dbus-manager.h"
#include "nm-system-config-error.h"
#include "nm-dbus-glib-types.h"
#include "nm-polkit-helpers.h"
#include "nm-logging.h"
static gboolean impl_sysconfig_connection_get_settings (NMSysconfigConnection *connection,
GHashTable **settings,
GError **error);
static void impl_sysconfig_connection_get_settings (NMSysconfigConnection *connection,
DBusGMethodInvocation *context);
static void impl_sysconfig_connection_update (NMSysconfigConnection *connection,
GHashTable *new_settings,
@@ -56,7 +58,10 @@ G_DEFINE_TYPE (NMSysconfigConnection, nm_sysconfig_connection, NM_TYPE_CONNECTIO
enum {
UPDATED,
CHECK_PERMISSIONS,
REMOVED,
PURGED,
UNHIDDEN,
LAST_SIGNAL
};
@@ -64,12 +69,220 @@ static guint signals[LAST_SIGNAL] = { 0 };
typedef struct {
PolkitAuthority *authority;
GSList *pk_calls;
GSList *pending_auths; /* List of PendingAuth structs*/
NMConnection *secrets;
gboolean visible; /* Is this connection is visible by some session? */
GHashTable *access_list; /* Sessions that may access this connection. */
} NMSysconfigConnectionPrivate;
/**************************************************************/
/* Returns true iff the given session should be permitted to access this
* connection. Used to update the access list */
static gboolean
session_allowed (NMSysconfigConnection *connection,
NMSessionInfo *session)
{
NMSettingConnection *setting_connection = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (connection), NM_TYPE_SETTING_CONNECTION);
GSList *permissions_entries;
char *session_user;
GSList *session_groups;
GSList *p_iter;
gboolean allowed = FALSE;
g_object_get (setting_connection, NM_SETTING_CONNECTION_PERMISSIONS, &permissions_entries, NULL);
/* If permissions list is empty, the connection is world-accessible. */
if (permissions_entries == NULL) {
allowed = TRUE;
goto out;
}
/* The "default session" can only access world-accessible connections. */
if (nm_session_info_is_default_session (session)) {
allowed = FALSE;
goto out;
}
session_user = nm_session_info_get_unix_user (session);
session_groups = nm_session_info_get_unix_groups (session);
for (p_iter = permissions_entries; p_iter != NULL; p_iter = p_iter->next) {
char **p_comps = g_strsplit ((char *)p_iter->data, ":", 3);
char *type = p_comps[0];
char *name = p_comps[1];
if (g_str_equal (type, "user")) {
if (g_str_equal (session_user, name)) {
allowed = TRUE;
goto out;
}
}
else if (g_str_equal (type, "group")) {
GSList *g_iter;
for (g_iter = session_groups; g_iter != NULL; g_iter = g_iter->next ) {
char *group_name = (char *) g_iter->data;
if (g_str_equal (group_name, name)) {
allowed = TRUE;
goto out;
}
}
}
else {
nm_log_err (LOGD_SYS_SET,
"connection %s: failed to parse permissions entry '%s'",
nm_setting_connection_get_id (setting_connection),
(char *) p_iter->data);
}
}
out:
nm_utils_slist_free (permissions_entries, g_free);
return allowed;
}
static void
set_visibility (NMSysconfigConnection *self, gboolean new_visibility)
{
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
DBusGConnection *connection = nm_dbus_manager_get_connection (nm_dbus_manager_get());
const char *dbus_path = nm_connection_get_path (NM_CONNECTION (self));
if (new_visibility && !priv->visible) {
priv->visible = TRUE;
dbus_g_connection_register_g_object (connection, dbus_path, G_OBJECT (self));
g_signal_emit (self, signals[UNHIDDEN], 0);
}
else
if (!new_visibility && priv->visible) {
priv->visible = FALSE;
g_signal_emit (self, signals[REMOVED], 0);
dbus_g_connection_unregister_g_object (connection, G_OBJECT (self));
}
}
/* One of the sessions on our access list has been removed. */
static void
session_removed_cb (NMSessionInfo *removed_session,
gpointer user_data)
{
NMSysconfigConnection *connection = NM_SYSCONFIG_CONNECTION (user_data);
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (connection);
char *removed_session_id = nm_session_info_get_id (removed_session);
g_hash_table_remove (priv->access_list, removed_session_id);
if (g_hash_table_size (priv->access_list) == 0) {
set_visibility (connection, FALSE);
} else {
g_signal_emit (connection, signals[CHECK_PERMISSIONS], 0);
}
}
/* The session manager reports that a new session has been added. */
static void
session_added_cb (NMSessionManager *mananager,
NMSessionInfo *session,
gpointer user_data)
{
NMSysconfigConnection *connection = NM_SYSCONFIG_CONNECTION (user_data);
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (connection);
if (session_allowed (connection, session)) {
g_object_ref (session);
g_signal_connect (session, NM_SESSION_INFO_REMOVED,
G_CALLBACK(session_removed_cb), connection);
g_hash_table_insert (priv->access_list, nm_session_info_get_id (session), session);
g_signal_emit (connection, signals[CHECK_PERMISSIONS], 0);
set_visibility (connection, TRUE);
}
}
/* Our permissions property may have been changed. Update the access list. */
static void
update_access_list (NMSysconfigConnection *connection)
{
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (connection);
NMSessionManager *session_manager = nm_session_manager_get();
GSList *sessions = nm_session_manager_get_sessions (session_manager);
GSList *iter;
if (!priv->access_list)
return;
g_hash_table_remove_all (priv->access_list);
for (iter = sessions; iter != NULL; iter = iter->next) {
NMSessionInfo *session = (NMSessionInfo *) iter->data;
if (session_allowed (connection, iter->data)) {
g_object_ref (session);
g_signal_connect (session, NM_SESSION_INFO_REMOVED,
G_CALLBACK(session_removed_cb), connection);
g_hash_table_insert (priv->access_list, nm_session_info_get_id (session), session);
}
}
if (g_hash_table_size (priv->access_list) == 0) {
set_visibility (connection, FALSE);
} else {
g_signal_emit (connection, signals[CHECK_PERMISSIONS], 0);
}
}
gboolean
nm_sysconfig_connection_is_visible (NMSysconfigConnection *connection)
{
g_return_val_if_fail (NM_SYSCONFIG_CONNECTION (connection), FALSE);
return NM_SYSCONFIG_CONNECTION_GET_PRIVATE (connection)->visible;
}
static void
prepend_slist (gpointer key, gpointer value, gpointer user_data)
{
GSList **sessions = (GSList **) user_data;
*sessions = g_slist_prepend (*sessions, value);
}
GSList *
nm_sysconfig_connection_get_session_access_list (NMSysconfigConnection *connection)
{
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (connection);
GSList *sessions = NULL;
if (priv->access_list)
g_hash_table_foreach (priv->access_list, prepend_slist, &sessions);
return sessions;
}
gboolean
nm_sysconfig_connection_is_accessible_by_session (NMSysconfigConnection *connection,
NMSessionInfo *session)
{
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (connection);
NMSessionInfo *stored_session = NULL;
g_return_val_if_fail (NM_IS_SYSCONFIG_CONNECTION (connection), FALSE);
g_return_val_if_fail (NM_IS_SESSION_INFO (session), FALSE);
if (!priv->access_list)
return FALSE;
stored_session = g_hash_table_lookup (priv->access_list,
nm_session_info_get_id (session));
if (stored_session == session)
return TRUE;
return FALSE;
}
/**************************************************************/
/* Update the settings of this connection to match that of 'new', taking care to
* make a private copy of secrets. */
gboolean
@@ -98,6 +311,8 @@ nm_sysconfig_connection_replace_settings (NMSysconfigConnection *self,
g_object_unref (priv->secrets);
priv->secrets = nm_connection_duplicate (NM_CONNECTION (self));
update_access_list (self);
success = TRUE;
}
g_hash_table_destroy (new_settings);
@@ -254,7 +469,8 @@ do_delete (NMSysconfigConnection *connection,
gpointer user_data)
{
g_object_ref (connection);
g_signal_emit (connection, signals[REMOVED], 0);
set_visibility (connection, FALSE);
g_signal_emit (connection, signals[PURGED], 0);
callback (connection, NULL, user_data);
g_object_unref (connection);
}
@@ -388,28 +604,158 @@ get_secrets (NMSysconfigConnection *connection,
g_hash_table_destroy (settings);
}
/**************************************************************/
/**** User authorization **************************************/
static gboolean
impl_sysconfig_connection_get_settings (NMSysconfigConnection *self,
GHashTable **settings,
GError **error)
typedef void (*AuthCallback) (NMSysconfigConnection *connection,
DBusGMethodInvocation *context,
GError *error,
gpointer data);
typedef struct {
NMSysconfigConnection *self;
DBusGMethodInvocation *context;
PolkitSubject *subject;
GCancellable *cancellable;
gboolean check_modify;
gboolean disposed;
AuthCallback callback;
gpointer callback_data;
} PendingAuth;
static void
auth_finish (PendingAuth *info, GError *error)
{
NMConnection *no_secrets;
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (info->self);
priv->pending_auths = g_slist_remove (priv->pending_auths, info);
/* Secrets should *never* be returned by the GetSettings method, they
* get returned by the GetSecrets method which can be better
* protected against leakage of secrets to unprivileged callers.
*/
no_secrets = nm_connection_duplicate (NM_CONNECTION (self));
g_assert (no_secrets);
nm_connection_clear_secrets (no_secrets);
*settings = nm_connection_to_hash (no_secrets);
g_assert (*settings);
g_object_unref (no_secrets);
return *settings ? TRUE : FALSE;
info->callback (info->self, info->context, error, info->callback_data);
if (info->subject)
g_object_unref (info->subject);
if (info->cancellable)
g_object_unref (info->cancellable);
g_slice_free (PendingAuth, info);
}
static void
auth_pk_cb (GObject *object, GAsyncResult *result, gpointer user_data)
{
PendingAuth *info = user_data;
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (info->self);
PolkitAuthorizationResult *pk_result;
GError *error = NULL;
if (info->disposed) {
error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
NM_SYSCONFIG_SETTINGS_ERROR_GENERAL,
"Request was canceled.");
auth_finish (info, error);
g_error_free (error);
return;
}
pk_result = polkit_authority_check_authorization_finish (priv->authority,
result,
&error);
if (error) {
auth_finish (info, error);
g_error_free (error);
return;
}
if (!polkit_authorization_result_get_is_authorized (pk_result)) {
error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED,
"Insufficient privileges.");
auth_finish (info, error);
g_error_free (error);
goto out;
}
auth_finish (info, NULL);
out:
g_object_unref (pk_result);
}
static void
auth_get_session_cb (NMSessionInfo *session,
GError *session_error,
gpointer user_data)
{
PendingAuth *info = user_data;
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (info->self);
GError *error = NULL;
if (info->disposed || !session) {
error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
NM_SYSCONFIG_SETTINGS_ERROR_GENERAL,
"Request was canceled.");
auth_finish (info, error);
g_error_free (error);
return;
}
if (!nm_sysconfig_connection_is_accessible_by_session (info->self, session)) {
error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
NM_SYSCONFIG_SETTINGS_ERROR_PERMISSION_DENIED,
"Caller's session is not authorized to access connection.");
auth_finish (info, error);
g_error_free (error);
return;
}
if (!info->check_modify) {
auth_finish (info, NULL);
} else {
char *sender = dbus_g_method_get_sender (info->context);
info->subject = polkit_system_bus_name_new (sender);
info->cancellable = g_cancellable_new();
g_free (sender);
polkit_authority_check_authorization (priv->authority,
info->subject,
NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY,
NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
info->cancellable,
auth_pk_cb,
info);
}
}
static void
auth_start (NMSysconfigConnection *self,
DBusGMethodInvocation *context,
gboolean check_modify,
AuthCallback callback,
gpointer callback_data)
{
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
PendingAuth *info = g_slice_new (PendingAuth);
info->self = self;
info->context = context;
info->subject = NULL;
info->cancellable = NULL;
info->check_modify = check_modify;
info->disposed = FALSE;
info->callback_data = callback_data;
priv->pending_auths = g_slist_prepend (priv->pending_auths, info);
nm_session_manager_get_session_of_caller (nm_session_manager_get(),
context,
auth_get_session_cb,
info);
}
/**** DBus method handlers ************************************/
static gboolean
check_writable (NMConnection *connection, GError **error)
{
@@ -443,65 +789,41 @@ check_writable (NMConnection *connection, GError **error)
return TRUE;
}
typedef struct {
NMSysconfigConnection *self;
DBusGMethodInvocation *context;
PolkitSubject *subject;
GCancellable *cancellable;
gboolean disposed;
/* Update */
NMConnection *connection;
/* Secrets */
char *setting_name;
char **hints;
gboolean request_new;
} PolkitCall;
static PolkitCall *
polkit_call_new (NMSysconfigConnection *self,
DBusGMethodInvocation *context,
NMConnection *connection,
const char *setting_name,
const char **hints,
gboolean request_new)
static void
get_settings_auth_cb (NMSysconfigConnection *self,
DBusGMethodInvocation *context,
GError *error,
gpointer data)
{
PolkitCall *call;
char *sender;
NMConnection *no_secrets;
GHashTable *settings;
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (context != NULL, NULL);
if (error) {
dbus_g_method_return_error (context, error);
return;
}
call = g_malloc0 (sizeof (PolkitCall));
call->self = self;
call->context = context;
call->cancellable = g_cancellable_new ();
call->connection = connection;
call->setting_name = g_strdup (setting_name);
if (hints)
call->hints = g_strdupv ((char **) hints);
call->request_new = request_new;
/* Secrets should *never* be returned by the GetSettings method, they
* get returned by the GetSecrets method which can be better
* protected against leakage of secrets to unprivileged callers.
*/
no_secrets = nm_connection_duplicate (NM_CONNECTION (self));
g_assert (no_secrets);
nm_connection_clear_secrets (no_secrets);
settings = nm_connection_to_hash (no_secrets);
g_assert (settings);
sender = dbus_g_method_get_sender (context);
call->subject = polkit_system_bus_name_new (sender);
g_free (sender);
return call;
dbus_g_method_return (context, settings);
g_object_unref (no_secrets);
g_object_unref (settings);
}
static void
polkit_call_free (PolkitCall *call)
impl_sysconfig_connection_get_settings (NMSysconfigConnection *self,
DBusGMethodInvocation *context)
{
if (call->connection)
g_object_unref (call->connection);
g_free (call->setting_name);
if (call->hints)
g_strfreev (call->hints);
g_object_unref (call->subject);
g_object_unref (call->cancellable);
g_free (call);
auth_start (self, context, FALSE, get_settings_auth_cb, NULL);
}
static void
@@ -509,70 +831,35 @@ con_update_cb (NMSysconfigConnection *connection,
GError *error,
gpointer user_data)
{
PolkitCall *call = user_data;
DBusGMethodInvocation *context = user_data;
if (error)
dbus_g_method_return_error (call->context, error);
dbus_g_method_return_error (context, error);
else
dbus_g_method_return (call->context);
polkit_call_free (call);
dbus_g_method_return (context);
}
static void
pk_update_cb (GObject *object, GAsyncResult *result, gpointer user_data)
update_auth_cb (NMSysconfigConnection *self,
DBusGMethodInvocation *context,
GError *error,
gpointer data)
{
PolkitCall *call = user_data;
NMSysconfigConnection *self = call->self;
NMSysconfigConnectionPrivate *priv;
PolkitAuthorizationResult *pk_result;
GError *error = NULL;
NMConnection *new_settings = data;
/* If our NMSysconfigConnection is already gone, do nothing */
if (call->disposed) {
error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
NM_SYSCONFIG_SETTINGS_ERROR_GENERAL,
"Request was canceled.");
dbus_g_method_return_error (call->context, error);
g_error_free (error);
polkit_call_free (call);
return;
}
priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
priv->pk_calls = g_slist_remove (priv->pk_calls, call);
pk_result = polkit_authority_check_authorization_finish (priv->authority,
result,
&error);
/* Some random error happened */
if (error) {
dbus_g_method_return_error (call->context, error);
g_error_free (error);
polkit_call_free (call);
return;
}
/* Caller didn't successfully authenticate */
if (!polkit_authorization_result_get_is_authorized (pk_result)) {
error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED,
"Insufficient privileges.");
dbus_g_method_return_error (call->context, error);
g_error_free (error);
polkit_call_free (call);
dbus_g_method_return_error (context, error);
goto out;
}
/* Update and commit our settings. */
nm_sysconfig_connection_replace_and_commit (self,
call->connection,
new_settings,
con_update_cb,
call);
context);
out:
g_object_unref (pk_result);
g_object_unref (new_settings);
}
static void
@@ -580,8 +867,6 @@ impl_sysconfig_connection_update (NMSysconfigConnection *self,
GHashTable *new_settings,
DBusGMethodInvocation *context)
{
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
PolkitCall *call;
NMConnection *tmp;
GError *error = NULL;
@@ -604,17 +889,7 @@ impl_sysconfig_connection_update (NMSysconfigConnection *self,
return;
}
call = polkit_call_new (self, context, tmp, NULL, NULL, FALSE);
g_assert (call);
polkit_authority_check_authorization (priv->authority,
call->subject,
NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY,
NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
call->cancellable,
pk_update_cb,
call);
priv->pk_calls = g_slist_prepend (priv->pk_calls, call);
auth_start (self, context, TRUE, update_auth_cb, tmp);
}
static void
@@ -622,75 +897,32 @@ con_delete_cb (NMSysconfigConnection *connection,
GError *error,
gpointer user_data)
{
PolkitCall *call = user_data;
DBusGMethodInvocation *context = user_data;
if (error)
dbus_g_method_return_error (call->context, error);
dbus_g_method_return_error (context, error);
else
dbus_g_method_return (call->context);
polkit_call_free (call);
dbus_g_method_return (context);
}
static void
pk_delete_cb (GObject *object, GAsyncResult *result, gpointer user_data)
delete_auth_cb (NMSysconfigConnection *self,
DBusGMethodInvocation *context,
GError *error,
gpointer data)
{
PolkitCall *call = user_data;
NMSysconfigConnection *self = call->self;
NMSysconfigConnectionPrivate *priv;
PolkitAuthorizationResult *pk_result;
GError *error = NULL;
/* If our NMSysconfigConnection is already gone, do nothing */
if (call->disposed) {
error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
NM_SYSCONFIG_SETTINGS_ERROR_GENERAL,
"Request was canceled.");
dbus_g_method_return_error (call->context, error);
g_error_free (error);
polkit_call_free (call);
return;
}
priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
priv->pk_calls = g_slist_remove (priv->pk_calls, call);
pk_result = polkit_authority_check_authorization_finish (priv->authority,
result,
&error);
/* Some random error happened */
if (error) {
dbus_g_method_return_error (call->context, error);
g_error_free (error);
polkit_call_free (call);
dbus_g_method_return_error (context, error);
return;
}
/* Caller didn't successfully authenticate */
if (!polkit_authorization_result_get_is_authorized (pk_result)) {
error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED,
"Insufficient privileges.");
dbus_g_method_return_error (call->context, error);
g_error_free (error);
polkit_call_free (call);
goto out;
}
/* Caller is authenticated, now we can finally try to delete */
nm_sysconfig_connection_delete (self, con_delete_cb, call);
out:
g_object_unref (pk_result);
nm_sysconfig_connection_delete (self, con_delete_cb, context);
}
static void
impl_sysconfig_connection_delete (NMSysconfigConnection *self,
DBusGMethodInvocation *context)
{
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
PolkitCall *call;
GError *error = NULL;
if (!check_writable (NM_CONNECTION (self), &error)) {
@@ -699,91 +931,53 @@ impl_sysconfig_connection_delete (NMSysconfigConnection *self,
return;
}
call = polkit_call_new (self, context, NULL, NULL, NULL, FALSE);
g_assert (call);
polkit_authority_check_authorization (priv->authority,
call->subject,
NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY,
NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
call->cancellable,
pk_delete_cb,
call);
priv->pk_calls = g_slist_prepend (priv->pk_calls, call);
auth_start (self, context, TRUE, delete_auth_cb, NULL);
}
typedef struct {
char *setting_name;
char **hints;
gboolean request_new;
} GetSecretsInfo;
static void
con_secrets_cb (NMSysconfigConnection *connection,
GHashTable *secrets,
GError *error,
gpointer user_data)
{
PolkitCall *call = user_data;
DBusGMethodInvocation *context = user_data;
if (error)
dbus_g_method_return_error (call->context, error);
dbus_g_method_return_error (context, error);
else
dbus_g_method_return (call->context, secrets);
polkit_call_free (call);
dbus_g_method_return (context, secrets);
}
static void
pk_secrets_cb (GObject *object, GAsyncResult *result, gpointer user_data)
secrets_auth_cb (NMSysconfigConnection *self,
DBusGMethodInvocation *context,
GError *error,
gpointer data)
{
PolkitCall *call = user_data;
NMSysconfigConnection *self = call->self;
NMSysconfigConnectionPrivate *priv;
PolkitAuthorizationResult *pk_result;
GError *error = NULL;
GetSecretsInfo *info = data;
/* If our NMSysconfigConnection is already gone, do nothing */
if (call->disposed) {
error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
NM_SYSCONFIG_SETTINGS_ERROR_GENERAL,
"Request was canceled.");
dbus_g_method_return_error (call->context, error);
g_error_free (error);
polkit_call_free (call);
return;
}
priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
priv->pk_calls = g_slist_remove (priv->pk_calls, call);
pk_result = polkit_authority_check_authorization_finish (priv->authority,
result,
&error);
/* Some random error happened */
if (error) {
dbus_g_method_return_error (call->context, error);
g_error_free (error);
polkit_call_free (call);
return;
}
/* Caller didn't successfully authenticate */
if (!polkit_authorization_result_get_is_authorized (pk_result)) {
error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED,
"Insufficient privileges.");
dbus_g_method_return_error (call->context, error);
g_error_free (error);
polkit_call_free (call);
dbus_g_method_return_error (context, error);
goto out;
}
/* Caller is authenticated, now we can finally try to update */
nm_sysconfig_connection_get_secrets (self,
call->setting_name,
(const char **) call->hints,
call->request_new,
info->setting_name,
(const char **) info->hints,
info->request_new,
con_secrets_cb,
call);
context);
out:
g_object_unref (pk_result);
g_free (info->setting_name);
g_strfreev (info->hints);
g_slice_free (GetSecretsInfo, info);
}
static void
@@ -793,20 +987,12 @@ impl_sysconfig_connection_get_secrets (NMSysconfigConnection *self,
gboolean request_new,
DBusGMethodInvocation *context)
{
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
PolkitCall *call;
GetSecretsInfo *info = g_slice_new (GetSecretsInfo);
info->setting_name = g_strdup (setting_name);
info->hints = g_strdupv ((char **) hints);
info->request_new = request_new;
call = polkit_call_new (self, context, NULL, setting_name, hints, request_new);
g_assert (call);
polkit_authority_check_authorization (priv->authority,
call->subject,
NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY,
NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
call->cancellable,
pk_secrets_cb,
call);
priv->pk_calls = g_slist_prepend (priv->pk_calls, call);
auth_start (self, context, TRUE, secrets_auth_cb, info);
}
/**************************************************************/
@@ -815,11 +1001,34 @@ static void
nm_sysconfig_connection_init (NMSysconfigConnection *self)
{
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
NMSessionManager *session_manager;
static guint32 dbus_counter = 0;
char *dbus_path;
priv->authority = polkit_authority_get ();
if (!priv->authority) {
nm_log_err (LOGD_SYS_SET, "%s: error creating PolicyKit authority");
nm_log_err (LOGD_SYS_SET, "%s:error creating PolicyKit authority");
}
dbus_path = g_strdup_printf ("%s/%u", NM_DBUS_PATH_SETTINGS, dbus_counter++);
nm_connection_set_path (NM_CONNECTION (self), dbus_path);
g_free (dbus_path);
priv->visible = FALSE;
priv->access_list = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
session_manager = nm_session_manager_get();
g_signal_connect (session_manager, NM_SESSION_MANAGER_SESSION_ADDED,
G_CALLBACK (session_added_cb), self);
}
static void
access_list_dispose (gpointer key, gpointer value, gpointer user_data)
{
NMSysconfigConnection *connection = (NMSysconfigConnection *) user_data;
NMSessionInfo *session = (NMSessionInfo *) value;
g_signal_handlers_disconnect_by_func (session, session_removed_cb, connection);
}
static void
@@ -827,20 +1036,27 @@ dispose (GObject *object)
{
NMSysconfigConnection *self = NM_SYSCONFIG_CONNECTION (object);
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
NMSessionManager *session_manager = nm_session_manager_get ();
GSList *iter;
if (priv->secrets)
g_object_unref (priv->secrets);
/* Cancel PolicyKit requests */
for (iter = priv->pk_calls; iter; iter = g_slist_next (iter)) {
PolkitCall *call = iter->data;
for (iter = priv->pending_auths; iter; iter = g_slist_next (iter)) {
PendingAuth *call = iter->data;
call->disposed = TRUE;
g_cancellable_cancel (call->cancellable);
}
g_slist_free (priv->pk_calls);
priv->pk_calls = NULL;
g_slist_free (priv->pending_auths);
priv->pending_auths = NULL;
set_visibility (self, FALSE);
g_signal_handlers_disconnect_by_func (session_manager, session_added_cb, self);
g_hash_table_foreach (priv->access_list, access_list_dispose, self);
g_hash_table_unref (priv->access_list);
priv->access_list = NULL;
G_OBJECT_CLASS (nm_sysconfig_connection_parent_class)->dispose (object);
}
@@ -877,6 +1093,24 @@ nm_sysconfig_connection_class_init (NMSysconfigConnectionClass *class)
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[PURGED] =
g_signal_new (NM_SYSCONFIG_CONNECTION_PURGED,
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[UNHIDDEN] =
g_signal_new (NM_SYSCONFIG_CONNECTION_UNHIDDEN,
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (class),
&dbus_glib_nm_sysconfig_connection_object_info);

View File

@@ -23,6 +23,7 @@
#include <nm-connection.h>
#include <dbus/dbus-glib.h>
#include "nm-session-info.h"
G_BEGIN_DECLS
@@ -35,6 +36,8 @@ G_BEGIN_DECLS
#define NM_SYSCONFIG_CONNECTION_UPDATED "updated"
#define NM_SYSCONFIG_CONNECTION_REMOVED "removed"
#define NM_SYSCONFIG_CONNECTION_PURGED "purged"
#define NM_SYSCONFIG_CONNECTION_UNHIDDEN "unhidden"
typedef struct _NMSysconfigConnection NMSysconfigConnection;
@@ -102,6 +105,14 @@ void nm_sysconfig_connection_get_secrets (NMSysconfigConnection *connection,
NMSysconfigConnectionGetSecretsFunc callback,
gpointer user_data);
gboolean nm_sysconfig_connection_is_visible (NMSysconfigConnection *connection);
gboolean nm_sysconfig_connection_is_accessible_by_session (NMSysconfigConnection *connection,
NMSessionInfo *session);
GSList * nm_sysconfig_connection_get_session_access_list (NMSysconfigConnection *connection);
G_END_DECLS
#endif /* NM_SYSCONFIG_CONNECTION_H */

View File

@@ -112,7 +112,8 @@ typedef struct {
GSList *plugins;
gboolean connections_loaded;
GHashTable *connections;
GHashTable *visible_connections;
GHashTable *all_connections;
GSList *unmanaged_specs;
} NMSysconfigSettingsPrivate;
@@ -186,7 +187,7 @@ nm_sysconfig_settings_list_connections (NMSysconfigSettings *settings)
load_connections (settings);
g_hash_table_iter_init (&iter, priv->connections);
g_hash_table_iter_init (&iter, priv->visible_connections);
while (g_hash_table_iter_next (&iter, &key, NULL))
list = g_slist_prepend (list, NM_SYSCONFIG_CONNECTION (key));
return g_slist_reverse (list);
@@ -536,28 +537,28 @@ connection_removed (NMSysconfigConnection *connection,
{
NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (user_data);
g_hash_table_remove (priv->connections, connection);
g_hash_table_remove (priv->visible_connections, connection);
}
static void
export_connection (NMSysconfigConnection *connection)
connection_unhidden (NMSysconfigConnection *connection,
gpointer user_data)
{
DBusGConnection *bus;
static guint32 ec_counter = 0;
char *path;
NMSysconfigSettings *settings = user_data;
NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (settings);
g_return_if_fail (connection != NULL);
g_return_if_fail (NM_IS_SYSCONFIG_CONNECTION (connection));
g_hash_table_insert (priv->visible_connections,
connection, GINT_TO_POINTER (1));
g_signal_emit (settings, signals[NEW_CONNECTION], 0, connection);
}
/* Don't allow exporting twice */
g_return_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL);
static void
connection_purged (NMSysconfigConnection *connection,
gpointer user_data)
{
NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (user_data);
path = g_strdup_printf ("%s/%u", NM_DBUS_PATH_SETTINGS, ec_counter++);
nm_connection_set_path (NM_CONNECTION (connection), path);
bus = nm_dbus_manager_get_connection (nm_dbus_manager_get ());
dbus_g_connection_register_g_object (bus, path, G_OBJECT (connection));
g_free (path);
g_hash_table_remove (priv->all_connections, connection);
}
static void
@@ -570,22 +571,33 @@ claim_connection (NMSysconfigSettings *self,
g_return_if_fail (NM_IS_SYSCONFIG_SETTINGS (self));
g_return_if_fail (NM_IS_SYSCONFIG_CONNECTION (connection));
if (g_hash_table_lookup (priv->connections, connection))
if (g_hash_table_lookup (priv->all_connections, connection))
/* A plugin is lying to us. */
return;
g_hash_table_insert (priv->connections, g_object_ref (connection), GINT_TO_POINTER (1));
g_hash_table_insert (priv->all_connections, g_object_ref (connection), GINT_TO_POINTER (1));
g_signal_connect (connection,
NM_SYSCONFIG_CONNECTION_REMOVED,
G_CALLBACK (connection_removed),
self);
g_signal_connect (connection,
NM_SYSCONFIG_CONNECTION_PURGED,
G_CALLBACK (connection_purged),
self);
g_signal_connect (connection,
NM_SYSCONFIG_CONNECTION_UNHIDDEN,
G_CALLBACK (connection_unhidden),
self);
if (do_export) {
export_connection (connection);
if (nm_sysconfig_connection_is_visible (connection)) {
g_hash_table_insert (priv->visible_connections, connection, GINT_TO_POINTER (1));
g_signal_emit (self, signals[NEW_CONNECTION], 0, connection);
}
}
// TODO it seems that this is only ever used to remove a
// NMDefaultWiredConnection, and it probably needs to stay that way. So this
// *needs* a better name!
static void
remove_connection (NMSysconfigSettings *self,
NMSysconfigConnection *connection,
@@ -593,9 +605,14 @@ remove_connection (NMSysconfigSettings *self,
{
NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
if (g_hash_table_lookup (priv->connections, connection)) {
if (g_hash_table_lookup (priv->visible_connections, connection)) {
g_signal_emit_by_name (G_OBJECT (connection), NM_SYSCONFIG_CONNECTION_REMOVED);
g_hash_table_remove (priv->connections, connection);
g_hash_table_remove (priv->visible_connections, connection);
}
if (g_hash_table_lookup (priv->all_connections, connection)) {
g_signal_emit_by_name (G_OBJECT (connection), NM_SYSCONFIG_CONNECTION_PURGED);
g_hash_table_remove (priv->all_connections, connection);
}
}
@@ -1056,7 +1073,7 @@ have_connection_for_device (NMSysconfigSettings *self, GByteArray *mac)
g_return_val_if_fail (mac != NULL, FALSE);
/* Find a wired connection locked to the given MAC address, if any */
g_hash_table_iter_init (&iter, priv->connections);
g_hash_table_iter_init (&iter, priv->visible_connections);
while (g_hash_table_iter_next (&iter, &key, NULL)) {
NMConnection *connection = NM_CONNECTION (key);
const char *connection_type;
@@ -1442,7 +1459,8 @@ finalize (GObject *object)
NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (object);
NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
g_hash_table_destroy (priv->connections);
g_hash_table_destroy (priv->visible_connections);
g_hash_table_destroy (priv->all_connections);
clear_unmanaged_specs (self);
@@ -1584,7 +1602,8 @@ nm_sysconfig_settings_init (NMSysconfigSettings *self)
{
NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
priv->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
priv->visible_connections = g_hash_table_new (g_direct_hash, g_direct_equal);
priv->all_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
priv->authority = polkit_authority_get ();
if (priv->authority) {