1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager -- Network link manager
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Copyright (C) 2010 Red Hat, Inc.
21 #include "nm-default.h"
25 #include "nm-setting-connection.h"
26 #include "nm-auth-utils.h"
27 #include "nm-auth-subject.h"
28 #include "nm-auth-manager.h"
29 #include "nm-session-monitor.h"
36 GDBusMethodInvocation *context;
37 NMAuthSubject *subject;
43 NMAuthChainResultFunc done_func;
49 GCancellable *cancellable;
56 GDestroyNotify destroy;
60 chain_data_new (gpointer data, GDestroyNotify destroy)
64 tmp = g_slice_new (ChainData);
66 tmp->destroy = destroy;
71 chain_data_free (gpointer data)
73 ChainData *tmp = data;
76 tmp->destroy (tmp->data);
77 memset (tmp, 0, sizeof (ChainData));
78 g_slice_free (ChainData, tmp);
82 auth_chain_finish (gpointer user_data)
84 NMAuthChain *self = user_data;
89 /* Ensure we stay alive across the callback */
91 self->done_func (self, self->error, self->context, self->user_data);
92 nm_auth_chain_unref (self);
96 /* Creates the NMAuthSubject automatically */
98 nm_auth_chain_new_context (GDBusMethodInvocation *context,
99 NMAuthChainResultFunc done_func,
102 NMAuthSubject *subject;
105 g_return_val_if_fail (context != NULL, NULL);
107 subject = nm_auth_subject_new_unix_process_from_context (context);
111 chain = nm_auth_chain_new_subject (subject,
115 g_object_unref (subject);
119 /* Requires an NMAuthSubject */
121 nm_auth_chain_new_subject (NMAuthSubject *subject,
122 GDBusMethodInvocation *context,
123 NMAuthChainResultFunc done_func,
128 g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL);
129 g_return_val_if_fail (nm_auth_subject_is_unix_process (subject) || nm_auth_subject_is_internal (subject), NULL);
131 self = g_slice_new0 (NMAuthChain);
133 self->data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, chain_data_free);
134 self->done_func = done_func;
135 self->user_data = user_data;
136 self->context = context ? g_object_ref (context) : NULL;
137 self->subject = g_object_ref (subject);
143 _get_data (NMAuthChain *self, const char *tag)
147 tmp = g_hash_table_lookup (self->data, tag);
148 return tmp ? tmp->data : NULL;
152 nm_auth_chain_get_data (NMAuthChain *self, const char *tag)
154 g_return_val_if_fail (self != NULL, NULL);
155 g_return_val_if_fail (tag != NULL, NULL);
157 return _get_data (self, tag);
161 * nm_auth_chain_steal_data:
162 * @self: A #NMAuthChain.
163 * @tag: A "tag" uniquely identifying the data to steal.
165 * Removes the datum assocated with @tag from the chain's data associations,
166 * without invoking the association's destroy handler. The caller assumes
167 * ownership over the returned value.
169 * Returns: the datum originally associated with @tag
172 nm_auth_chain_steal_data (NMAuthChain *self, const char *tag)
175 gpointer value = NULL;
178 g_return_val_if_fail (self != NULL, NULL);
179 g_return_val_if_fail (tag != NULL, NULL);
181 if (g_hash_table_lookup_extended (self->data, tag, &orig_key, (gpointer)&tmp)) {
182 g_hash_table_steal (self->data, tag);
184 /* Make sure the destroy handler isn't called when freeing */
186 chain_data_free (tmp);
193 nm_auth_chain_set_data (NMAuthChain *self,
196 GDestroyNotify data_destroy)
198 g_return_if_fail (self != NULL);
199 g_return_if_fail (tag != NULL);
202 g_hash_table_remove (self->data, tag);
204 g_hash_table_insert (self->data,
206 chain_data_new (data, data_destroy));
211 nm_auth_chain_get_data_ulong (NMAuthChain *self, const char *tag)
215 g_return_val_if_fail (self != NULL, 0);
216 g_return_val_if_fail (tag != NULL, 0);
218 data = _get_data (self, tag);
219 return data ? *data : 0ul;
223 nm_auth_chain_set_data_ulong (NMAuthChain *self,
229 g_return_if_fail (self != NULL);
230 g_return_if_fail (tag != NULL);
232 ptr = g_malloc (sizeof (*ptr));
234 nm_auth_chain_set_data (self, tag, ptr, g_free);
238 nm_auth_chain_get_subject (NMAuthChain *self)
240 g_return_val_if_fail (self != NULL, NULL);
242 return self->subject;
246 nm_auth_chain_get_result (NMAuthChain *self, const char *permission)
250 g_return_val_if_fail (self != NULL, NM_AUTH_CALL_RESULT_UNKNOWN);
251 g_return_val_if_fail (permission != NULL, NM_AUTH_CALL_RESULT_UNKNOWN);
253 data = _get_data (self, permission);
254 return data ? GPOINTER_TO_UINT (data) : NM_AUTH_CALL_RESULT_UNKNOWN;
258 auth_call_new (NMAuthChain *chain, const char *permission)
262 call = g_slice_new0 (AuthCall);
264 call->permission = g_strdup (permission);
269 auth_call_free (AuthCall *call)
271 g_free (call->permission);
272 g_clear_object (&call->cancellable);
273 g_slice_free (AuthCall, call);
277 auth_call_complete (AuthCall *call)
281 g_return_val_if_fail (call, G_SOURCE_REMOVE);
285 g_return_val_if_fail (self, G_SOURCE_REMOVE);
286 g_return_val_if_fail (g_slist_find (self->calls, call), G_SOURCE_REMOVE);
288 self->calls = g_slist_remove (self->calls, call);
291 g_assert (!self->idle_id && !self->done);
292 self->idle_id = g_idle_add (auth_chain_finish, self);
294 auth_call_free (call);
299 auth_call_cancel (gpointer user_data)
301 AuthCall *call = user_data;
303 if (call->cancellable) {
304 /* we don't free call immediately. Instead we cancel the async operation
305 * and set cancellable to NULL. pk_call_cb() will check for this and
306 * do the final cleanup. */
307 g_cancellable_cancel (call->cancellable);
308 g_clear_object (&call->cancellable);
310 g_source_remove (call->call_idle_id);
311 auth_call_free (call);
317 pk_call_cb (GObject *object, GAsyncResult *result, gpointer user_data)
319 AuthCall *call = user_data;
320 GError *error = NULL;
321 gboolean is_authorized, is_challenge;
323 nm_auth_manager_polkit_authority_check_authorization_finish (NM_AUTH_MANAGER (object),
329 /* If the call is already canceled do nothing */
330 if (!call->cancellable) {
331 nm_log_dbg (LOGD_CORE, "callback already cancelled");
332 g_clear_error (&error);
333 auth_call_free (call);
338 nm_log_warn (LOGD_CORE, "error requesting auth for %s: %s",
339 call->permission, error->message);
341 if (!call->chain->error) {
342 call->chain->error = error;
345 g_clear_error (&error);
347 guint call_result = NM_AUTH_CALL_RESULT_UNKNOWN;
350 /* Caller has the permission */
351 call_result = NM_AUTH_CALL_RESULT_YES;
352 } else if (is_challenge) {
353 /* Caller could authenticate to get the permission */
354 call_result = NM_AUTH_CALL_RESULT_AUTH;
356 call_result = NM_AUTH_CALL_RESULT_NO;
358 nm_auth_chain_set_data (call->chain, call->permission, GUINT_TO_POINTER (call_result), NULL);
361 auth_call_complete (call);
366 nm_auth_chain_add_call (NMAuthChain *self,
367 const char *permission,
368 gboolean allow_interaction)
371 NMAuthManager *auth_manager = nm_auth_manager_get ();
373 g_return_if_fail (self != NULL);
374 g_return_if_fail (permission && *permission);
375 g_return_if_fail (self->subject);
376 g_return_if_fail (nm_auth_subject_is_unix_process (self->subject) || nm_auth_subject_is_internal (self->subject));
377 g_return_if_fail (!self->idle_id && !self->done);
379 call = auth_call_new (self, permission);
380 self->calls = g_slist_append (self->calls, call);
382 if ( nm_auth_subject_is_internal (self->subject)
383 || nm_auth_subject_get_unix_process_uid (self->subject) == 0
384 || !nm_auth_manager_get_polkit_enabled (auth_manager)) {
385 /* Root user or non-polkit always gets the permission */
386 nm_auth_chain_set_data (self, permission, GUINT_TO_POINTER (NM_AUTH_CALL_RESULT_YES), NULL);
387 call->call_idle_id = g_idle_add ((GSourceFunc) auth_call_complete, call);
389 /* Non-root always gets authenticated when using polkit */
391 call->cancellable = g_cancellable_new ();
392 nm_auth_manager_polkit_authority_check_authorization (auth_manager,
400 if (!call->chain->error) {
401 call->chain->error = g_error_new_literal (DBUS_GERROR,
403 "Polkit support is disabled at compile time");
405 call->call_idle_id = g_idle_add ((GSourceFunc) auth_call_complete, call);
411 * nm_auth_chain_unref:
412 * @self: the auth-chain
414 * Unrefs the auth-chain. By unrefing the auth-chain, you also cancel
415 * the receipt of the done-callback. IOW, the callback will not be invoked.
417 * The only exception is, if you call nm_auth_chain_unref() from inside
418 * the callback. In this case, @self stays alive until the callback returns.
421 nm_auth_chain_unref (NMAuthChain *self)
423 g_return_if_fail (self != NULL);
424 g_return_if_fail (self->refcount > 0);
427 if (self->refcount > 0)
431 g_source_remove (self->idle_id);
433 g_object_unref (self->subject);
436 g_object_unref (self->context);
438 g_slist_free_full (self->calls, auth_call_cancel);
440 g_clear_error (&self->error);
441 g_hash_table_destroy (self->data);
443 memset (self, 0, sizeof (NMAuthChain));
444 g_slice_free (NMAuthChain, self);
447 /************ utils **************/
450 nm_auth_is_subject_in_acl (NMConnection *connection,
451 NMAuthSubject *subject,
452 char **out_error_desc)
454 NMSettingConnection *s_con;
455 const char *user = NULL;
458 g_return_val_if_fail (connection != NULL, FALSE);
459 g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), FALSE);
460 g_return_val_if_fail (nm_auth_subject_is_internal (subject) || nm_auth_subject_is_unix_process (subject), FALSE);
462 if (nm_auth_subject_is_internal (subject))
465 uid = nm_auth_subject_get_unix_process_uid (subject);
467 /* Root gets a free pass */
471 if (!nm_session_monitor_uid_to_user (uid, &user)) {
473 *out_error_desc = g_strdup_printf ("Could not determine username for uid %lu", uid);
477 s_con = nm_connection_get_setting_connection (connection);
479 /* This can only happen when called from AddAndActivate, so we know
480 * the user will be authorized when the connection is completed.
485 /* Match the username returned by the session check to a user in the ACL */
486 if (!nm_setting_connection_permissions_user_allowed (s_con, user)) {
488 *out_error_desc = g_strdup_printf ("uid %lu has no permission to perform this operation", uid);