diff options
| author | Michael Biebl <biebl@debian.org> | 2018-04-23 18:00:21 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2018-04-23 18:00:21 +0200 |
| commit | f60117b41d5433be1b4a96d82cd11d0c3dce9b63 (patch) | |
| tree | 2dd55c4ab0fdcbe9cddb26adb4a554b1a45c73c8 /src/nm-auth-utils.c | |
| parent | 7e9ff09fcb2366b383b7ebbec80d2f6fff117290 (diff) | |
New upstream version 1.11.3 upstream/1.11.3
Diffstat (limited to 'src/nm-auth-utils.c')
| -rw-r--r-- | src/nm-auth-utils.c | 489 |
1 files changed, 227 insertions, 262 deletions
diff --git a/src/nm-auth-utils.c b/src/nm-auth-utils.c index f1aff430..b41f6efa 100644 --- a/src/nm-auth-utils.c +++ b/src/nm-auth-utils.c @@ -24,48 +24,83 @@ #include <string.h> -#include "nm-utils/nm-hash-utils.h" +#include "nm-utils/nm-c-list.h" + #include "nm-setting-connection.h" #include "nm-auth-subject.h" #include "nm-auth-manager.h" #include "nm-session-monitor.h" +/*****************************************************************************/ + struct NMAuthChain { - guint32 refcount; - GSList *calls; - GHashTable *data; + GHashTable *data_hash; + + CList auth_call_lst_head; GDBusMethodInvocation *context; NMAuthSubject *subject; - GError *error; - - guint idle_id; - gboolean done; NMAuthChainResultFunc done_func; gpointer user_data; + + guint32 refcount; + + bool done:1; }; typedef struct { + CList auth_call_lst; NMAuthChain *chain; - GCancellable *cancellable; + NMAuthManagerCallId *call_id; char *permission; - guint call_idle_id; } AuthCall; +/*****************************************************************************/ + +static void +_ASSERT_call (AuthCall *call) +{ + nm_assert (call); + nm_assert (call->chain); + nm_assert (nm_c_list_contains_entry (&call->chain->auth_call_lst_head, call, auth_call_lst)); +} + +/*****************************************************************************/ + +static void +auth_call_free (AuthCall *call) +{ + if (call->call_id) + nm_auth_manager_check_authorization_cancel (call->call_id); + c_list_unlink_stale (&call->auth_call_lst); + g_free (call->permission); + g_slice_free (AuthCall, call); +} + +/*****************************************************************************/ + typedef struct { + + /* must be the first field. */ + const char *tag; + gpointer data; GDestroyNotify destroy; + char tag_data[]; } ChainData; static ChainData * -chain_data_new (gpointer data, GDestroyNotify destroy) +chain_data_new (const char *tag, gpointer data, GDestroyNotify destroy) { ChainData *tmp; + gsize l = strlen (tag); - tmp = g_slice_new (ChainData); + tmp = g_malloc (sizeof (ChainData) + l + 1); + tmp->tag = &tmp->tag_data[0]; tmp->data = data; tmp->destroy = destroy; + memcpy (&tmp->tag_data[0], tag, l + 1); return tmp; } @@ -76,69 +111,7 @@ chain_data_free (gpointer data) if (tmp->destroy) tmp->destroy (tmp->data); - memset (tmp, 0, sizeof (ChainData)); - g_slice_free (ChainData, tmp); -} - -static gboolean -auth_chain_finish (gpointer user_data) -{ - NMAuthChain *self = user_data; - - self->idle_id = 0; - self->done = TRUE; - - /* Ensure we stay alive across the callback */ - self->refcount++; - self->done_func (self, self->error, self->context, self->user_data); - nm_auth_chain_unref (self); - return FALSE; -} - -/* Creates the NMAuthSubject automatically */ -NMAuthChain * -nm_auth_chain_new_context (GDBusMethodInvocation *context, - NMAuthChainResultFunc done_func, - gpointer user_data) -{ - NMAuthSubject *subject; - NMAuthChain *chain; - - g_return_val_if_fail (context != NULL, NULL); - - subject = nm_auth_subject_new_unix_process_from_context (context); - if (!subject) - return NULL; - - chain = nm_auth_chain_new_subject (subject, - context, - done_func, - user_data); - g_object_unref (subject); - return chain; -} - -/* Requires an NMAuthSubject */ -NMAuthChain * -nm_auth_chain_new_subject (NMAuthSubject *subject, - GDBusMethodInvocation *context, - NMAuthChainResultFunc done_func, - gpointer user_data) -{ - NMAuthChain *self; - - g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); - g_return_val_if_fail (nm_auth_subject_is_unix_process (subject) || nm_auth_subject_is_internal (subject), NULL); - - self = g_slice_new0 (NMAuthChain); - self->refcount = 1; - self->data = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, chain_data_free); - self->done_func = done_func; - self->user_data = user_data; - self->context = context ? g_object_ref (context) : NULL; - self->subject = g_object_ref (subject); - - return self; + g_free (tmp); } static gpointer @@ -146,15 +119,17 @@ _get_data (NMAuthChain *self, const char *tag) { ChainData *tmp; - tmp = g_hash_table_lookup (self->data, tag); + if (!self->data_hash) + return NULL; + tmp = g_hash_table_lookup (self->data_hash, &tag); return tmp ? tmp->data : NULL; } gpointer nm_auth_chain_get_data (NMAuthChain *self, const char *tag) { - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (tag != NULL, NULL); + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (tag, NULL); return _get_data (self, tag); } @@ -175,19 +150,22 @@ nm_auth_chain_steal_data (NMAuthChain *self, const char *tag) { ChainData *tmp; gpointer value = NULL; - void *orig_key; - - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (tag != NULL, NULL); - - if (g_hash_table_lookup_extended (self->data, tag, &orig_key, (gpointer)&tmp)) { - g_hash_table_steal (self->data, tag); - value = tmp->data; - /* Make sure the destroy handler isn't called when freeing */ - tmp->destroy = NULL; - chain_data_free (tmp); - g_free (orig_key); - } + + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (tag, NULL); + + if (!self->data_hash) + return NULL; + + tmp = g_hash_table_lookup (self->data_hash, &tag); + if (!tmp) + return NULL; + + value = tmp->data; + + /* Make sure the destroy handler isn't called when freeing */ + tmp->destroy = NULL; + g_hash_table_remove (self->data_hash, tmp); return value; } @@ -197,165 +175,105 @@ nm_auth_chain_set_data (NMAuthChain *self, gpointer data, GDestroyNotify data_destroy) { - g_return_if_fail (self != NULL); - g_return_if_fail (tag != NULL); - - if (data == NULL) - g_hash_table_remove (self->data, tag); - else { - g_hash_table_insert (self->data, - g_strdup (tag), - chain_data_new (data, data_destroy)); + g_return_if_fail (self); + g_return_if_fail (tag); + + if (data == NULL) { + if (self->data_hash) + g_hash_table_remove (self->data_hash, &tag); + } else { + if (!self->data_hash) { + self->data_hash = g_hash_table_new_full (nm_pstr_hash, nm_pstr_equal, + NULL, chain_data_free); + } + g_hash_table_add (self->data_hash, + chain_data_new (tag, data, data_destroy)); } } -gulong -nm_auth_chain_get_data_ulong (NMAuthChain *self, const char *tag) -{ - gulong *data; +/*****************************************************************************/ - g_return_val_if_fail (self != NULL, 0); - g_return_val_if_fail (tag != NULL, 0); - - data = _get_data (self, tag); - return data ? *data : 0ul; -} - -void -nm_auth_chain_set_data_ulong (NMAuthChain *self, - const char *tag, - gulong data) +NMAuthCallResult +nm_auth_chain_get_result (NMAuthChain *self, const char *permission) { - gulong *ptr; + gpointer data; - g_return_if_fail (self != NULL); - g_return_if_fail (tag != NULL); + g_return_val_if_fail (self, NM_AUTH_CALL_RESULT_UNKNOWN); + g_return_val_if_fail (permission, NM_AUTH_CALL_RESULT_UNKNOWN); - ptr = g_malloc (sizeof (*ptr)); - *ptr = data; - nm_auth_chain_set_data (self, tag, ptr, g_free); + data = _get_data (self, permission); + return data ? GPOINTER_TO_UINT (data) : NM_AUTH_CALL_RESULT_UNKNOWN; } NMAuthSubject * nm_auth_chain_get_subject (NMAuthChain *self) { - g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (self, NULL); return self->subject; } -NMAuthCallResult -nm_auth_chain_get_result (NMAuthChain *self, const char *permission) -{ - gpointer data; - - g_return_val_if_fail (self != NULL, NM_AUTH_CALL_RESULT_UNKNOWN); - g_return_val_if_fail (permission != NULL, NM_AUTH_CALL_RESULT_UNKNOWN); +/*****************************************************************************/ - data = _get_data (self, permission); - return data ? GPOINTER_TO_UINT (data) : NM_AUTH_CALL_RESULT_UNKNOWN; -} - -static AuthCall * -auth_call_new (NMAuthChain *chain, const char *permission) +static gboolean +auth_chain_finish (NMAuthChain *self) { - AuthCall *call; + self->done = TRUE; - call = g_slice_new0 (AuthCall); - call->chain = chain; - call->permission = g_strdup (permission); - return call; + /* Ensure we stay alive across the callback */ + nm_assert (self->refcount == 1); + self->refcount++; + self->done_func (self, NULL, self->context, self->user_data); + nm_assert (NM_IN_SET (self->refcount, 1, 2)); + nm_auth_chain_destroy (self); + return FALSE; } static void -auth_call_free (AuthCall *call) -{ - g_free (call->permission); - g_clear_object (&call->cancellable); - g_slice_free (AuthCall, call); -} - -static gboolean auth_call_complete (AuthCall *call) { NMAuthChain *self; - g_return_val_if_fail (call, G_SOURCE_REMOVE); + _ASSERT_call (call); self = call->chain; - g_return_val_if_fail (self, G_SOURCE_REMOVE); - g_return_val_if_fail (g_slist_find (self->calls, call), G_SOURCE_REMOVE); - - self->calls = g_slist_remove (self->calls, call); + nm_assert (!self->done); - if (!self->calls) { - g_assert (!self->idle_id && !self->done); - self->idle_id = g_idle_add (auth_chain_finish, self); - } auth_call_free (call); - return FALSE; -} -static void -auth_call_cancel (gpointer user_data) -{ - AuthCall *call = user_data; - - if (nm_clear_g_cancellable (&call->cancellable)) { - /* we don't free call immediately. Instead we cancel the async operation - * and set cancellable to NULL. pk_call_cb() will check for this and - * do the final cleanup. */ - } else { - g_source_remove (call->call_idle_id); - auth_call_free (call); + if (c_list_is_empty (&self->auth_call_lst_head)) { + /* we are on an idle-handler or a clean call-stack (non-reentrant). */ + auth_chain_finish (self); } } -#if WITH_POLKIT static void -pk_call_cb (GObject *object, GAsyncResult *result, gpointer user_data) +pk_call_cb (NMAuthManager *auth_manager, + NMAuthManagerCallId *call_id, + gboolean is_authorized, + gboolean is_challenge, + GError *error, + gpointer user_data) { - AuthCall *call = user_data; - GError *error = NULL; - gboolean is_authorized = FALSE, is_challenge = FALSE; - guint call_result = NM_AUTH_CALL_RESULT_UNKNOWN; - - nm_auth_manager_polkit_authority_check_authorization_finish (NM_AUTH_MANAGER (object), - result, - &is_authorized, - &is_challenge, - &error); - - /* If the call is already canceled do nothing */ - if (!call->cancellable) { - nm_log_dbg (LOGD_CORE, "callback already cancelled"); - g_clear_error (&error); - auth_call_free (call); + AuthCall *call; + NMAuthCallResult call_result; + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; - } - if (error) { - /* Don't ruin the chain. Just leave the result unknown. */ - nm_log_warn (LOGD_CORE, "error requesting auth for %s: %s", - call->permission, error->message); - g_clear_error (&error); - } else { - if (is_authorized) { - /* Caller has the permission */ - call_result = NM_AUTH_CALL_RESULT_YES; - } else if (is_challenge) { - /* Caller could authenticate to get the permission */ - call_result = NM_AUTH_CALL_RESULT_AUTH; - } else - call_result = NM_AUTH_CALL_RESULT_NO; - } + call = user_data; + + nm_assert (call->call_id == call_id); + + call->call_id = NULL; + + call_result = nm_auth_call_result_eval (is_authorized, is_challenge, error); nm_auth_chain_set_data (call->chain, call->permission, GUINT_TO_POINTER (call_result), NULL); auth_call_complete (call); } -#endif void nm_auth_chain_add_call (NMAuthChain *self, @@ -365,81 +283,109 @@ nm_auth_chain_add_call (NMAuthChain *self, AuthCall *call; NMAuthManager *auth_manager = nm_auth_manager_get (); - g_return_if_fail (self != NULL); - g_return_if_fail (permission && *permission); + g_return_if_fail (self); g_return_if_fail (self->subject); + g_return_if_fail (!self->done); + g_return_if_fail (permission && *permission); g_return_if_fail (nm_auth_subject_is_unix_process (self->subject) || nm_auth_subject_is_internal (self->subject)); - g_return_if_fail (!self->idle_id && !self->done); - call = auth_call_new (self, permission); - self->calls = g_slist_append (self->calls, call); + call = g_slice_new0 (AuthCall); + call->chain = self; + call->permission = g_strdup (permission); + c_list_link_tail (&self->auth_call_lst_head, &call->auth_call_lst); + call->call_id = nm_auth_manager_check_authorization (auth_manager, + self->subject, + permission, + allow_interaction, + pk_call_cb, + call); +} - if ( nm_auth_subject_is_internal (self->subject) - || nm_auth_subject_get_unix_process_uid (self->subject) == 0 - || !nm_auth_manager_get_polkit_enabled (auth_manager)) { - /* Root user or non-polkit always gets the permission */ - nm_auth_chain_set_data (self, permission, GUINT_TO_POINTER (NM_AUTH_CALL_RESULT_YES), NULL); - call->call_idle_id = g_idle_add ((GSourceFunc) auth_call_complete, call); - } else { - /* Non-root always gets authenticated when using polkit */ -#if WITH_POLKIT - call->cancellable = g_cancellable_new (); - nm_auth_manager_polkit_authority_check_authorization (auth_manager, - self->subject, - permission, - allow_interaction, - call->cancellable, - pk_call_cb, - call); -#else - if (!call->chain->error) { - call->chain->error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "Polkit support is disabled at compile time"); - } - call->call_idle_id = g_idle_add ((GSourceFunc) auth_call_complete, call); -#endif - } +/*****************************************************************************/ + +/* Creates the NMAuthSubject automatically */ +NMAuthChain * +nm_auth_chain_new_context (GDBusMethodInvocation *context, + NMAuthChainResultFunc done_func, + gpointer user_data) +{ + NMAuthSubject *subject; + NMAuthChain *chain; + + g_return_val_if_fail (context, NULL); + + subject = nm_auth_subject_new_unix_process_from_context (context); + if (!subject) + return NULL; + + chain = nm_auth_chain_new_subject (subject, + context, + done_func, + user_data); + g_object_unref (subject); + return chain; +} + +/* Requires an NMAuthSubject */ +NMAuthChain * +nm_auth_chain_new_subject (NMAuthSubject *subject, + GDBusMethodInvocation *context, + NMAuthChainResultFunc done_func, + gpointer user_data) +{ + NMAuthChain *self; + + g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); + nm_assert (nm_auth_subject_is_unix_process (subject) || nm_auth_subject_is_internal (subject)); + + self = g_slice_new0 (NMAuthChain); + c_list_init (&self->auth_call_lst_head); + self->refcount = 1; + self->done_func = done_func; + self->user_data = user_data; + self->context = context ? g_object_ref (context) : NULL; + self->subject = g_object_ref (subject); + return self; } /** - * nm_auth_chain_unref: + * nm_auth_chain_destroy: * @self: the auth-chain * - * Unrefs the auth-chain. By unrefing the auth-chain, you also cancel + * Destroys the auth-chain. By destroying the auth-chain, you also cancel * the receipt of the done-callback. IOW, the callback will not be invoked. * - * The only exception is, if you call nm_auth_chain_unref() from inside + * The only exception is, if may call nm_auth_chain_destroy() from inside * the callback. In this case, @self stays alive until the callback returns. + * + * Note that you might only destroy an auth-chain exactly once, and never + * after the callback was handled. */ void -nm_auth_chain_unref (NMAuthChain *self) +nm_auth_chain_destroy (NMAuthChain *self) { - g_return_if_fail (self != NULL); - g_return_if_fail (self->refcount > 0); - - self->refcount--; - if (self->refcount > 0) - return; + AuthCall *call; - if (self->idle_id) - g_source_remove (self->idle_id); + g_return_if_fail (self); + g_return_if_fail (NM_IN_SET (self->refcount, 1, 2)); - g_object_unref (self->subject); + if (--self->refcount > 0) + return; - if (self->context) - g_object_unref (self->context); + nm_clear_g_object (&self->subject); + nm_clear_g_object (&self->context); - g_slist_free_full (self->calls, auth_call_cancel); + while ((call = c_list_first_entry (&self->auth_call_lst_head, AuthCall, auth_call_lst))) + auth_call_free (call); - g_clear_error (&self->error); - g_hash_table_destroy (self->data); + nm_clear_pointer (&self->data_hash, g_hash_table_destroy); - memset (self, 0, sizeof (NMAuthChain)); g_slice_free (NMAuthChain, self); } -/************ utils **************/ +/****************************************************************************** + * utils + *****************************************************************************/ gboolean nm_auth_is_subject_in_acl (NMConnection *connection, @@ -450,7 +396,7 @@ nm_auth_is_subject_in_acl (NMConnection *connection, const char *user = NULL; gulong uid; - g_return_val_if_fail (connection != NULL, FALSE); + g_return_val_if_fail (connection, FALSE); g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), FALSE); g_return_val_if_fail (nm_auth_subject_is_internal (subject) || nm_auth_subject_is_unix_process (subject), FALSE); @@ -464,8 +410,8 @@ nm_auth_is_subject_in_acl (NMConnection *connection, return TRUE; if (!nm_session_monitor_uid_to_user (uid, &user)) { - if (out_error_desc) - *out_error_desc = g_strdup_printf ("Could not determine username for uid %lu", uid); + NM_SET_OUT (out_error_desc, + g_strdup_printf ("Could not determine username for uid %lu", uid)); return FALSE; } @@ -479,12 +425,31 @@ nm_auth_is_subject_in_acl (NMConnection *connection, /* Match the username returned by the session check to a user in the ACL */ if (!nm_setting_connection_permissions_user_allowed (s_con, user)) { - if (out_error_desc) - *out_error_desc = g_strdup_printf ("uid %lu has no permission to perform this operation", uid); + NM_SET_OUT (out_error_desc, + g_strdup_printf ("uid %lu has no permission to perform this operation", uid)); return FALSE; } return TRUE; } +gboolean +nm_auth_is_subject_in_acl_set_error (NMConnection *connection, + NMAuthSubject *subject, + GQuark err_domain, + int err_code, + GError **error) +{ + char *error_desc = NULL; + + nm_assert (!error || !*error); + + if (nm_auth_is_subject_in_acl (connection, + subject, + error ? &error_desc : NULL)) + return TRUE; + g_set_error_literal (error, err_domain, err_code, error_desc); + g_free (error_desc); + return FALSE; +} |