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) 2014 Red Hat, Inc.
21 #include "nm-default.h"
23 #include "nm-auth-manager.h"
25 #include "nm-errors.h"
26 #include "nm-core-internal.h"
27 #include "NetworkManagerUtils.h"
29 #define POLKIT_SERVICE "org.freedesktop.PolicyKit1"
30 #define POLKIT_OBJECT_PATH "/org/freedesktop/PolicyKit1/Authority"
31 #define POLKIT_INTERFACE "org.freedesktop.PolicyKit1.Authority"
34 #define _NMLOG_PREFIX_NAME "auth"
35 #define _NMLOG_DOMAIN LOGD_CORE
36 #define _NMLOG(level, ...) \
38 if (nm_logging_enabled ((level), (_NMLOG_DOMAIN))) { \
39 char __prefix[30] = _NMLOG_PREFIX_NAME; \
41 if ((self) != singleton_instance) \
42 g_snprintf (__prefix, sizeof (__prefix), ""_NMLOG_PREFIX_NAME"[%p]", (self)); \
43 _nm_log ((level), (_NMLOG_DOMAIN), 0, \
44 "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
45 __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
62 static guint signals[LAST_SIGNAL] = {0};
65 gboolean polkit_enabled;
67 guint call_id_counter;
68 GCancellable *new_proxy_cancellable;
72 } NMAuthManagerPrivate;
74 NM_DEFINE_SINGLETON_REGISTER (NMAuthManager);
76 G_DEFINE_TYPE (NMAuthManager, nm_auth_manager, G_TYPE_OBJECT)
78 #define NM_AUTH_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_AUTH_MANAGER, NMAuthManagerPrivate))
80 /*****************************************************************************/
83 nm_auth_manager_get_polkit_enabled (NMAuthManager *self)
85 g_return_val_if_fail (NM_IS_AUTH_MANAGER (self), FALSE);
87 return NM_AUTH_MANAGER_GET_PRIVATE (self)->polkit_enabled;
90 /*****************************************************************************/
95 POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE = 0,
96 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION = (1<<0),
97 } PolkitCheckAuthorizationFlags;
102 GSimpleAsyncResult *simple;
103 gchar *cancellation_id;
104 GVariant *dbus_parameters;
105 GCancellable *cancellable;
109 _check_auth_data_free (CheckAuthData *data)
111 if (data->dbus_parameters)
112 g_variant_unref (data->dbus_parameters);
113 g_object_unref (data->self);
114 g_object_unref (data->simple);
115 g_clear_object (&data->cancellable);
116 g_free (data->cancellation_id);
121 _call_check_authorization_complete_with_error (CheckAuthData *data,
122 const char *error_message)
124 NMAuthManager *self = data->self;
126 _LOGD ("call[%u]: CheckAuthorization failed due to internal error: %s", data->call_id, error_message);
127 g_simple_async_result_set_error (data->simple,
129 NM_MANAGER_ERROR_FAILED,
130 "Authorization check failed: %s",
133 g_simple_async_result_complete_in_idle (data->simple);
135 _check_auth_data_free (data);
139 cancel_check_authorization_cb (GDBusProxy *proxy,
143 NMAuthManager *self = user_data;
147 value = g_dbus_proxy_call_finish (proxy, res, &error);
149 g_dbus_error_strip_remote_error (error);
150 _LOGD ("Error cancelling authorization check: %s", error->message);
151 g_error_free (error);
153 g_variant_unref (value);
155 g_object_unref (self);
159 gboolean is_authorized;
160 gboolean is_challenge;
161 } CheckAuthorizationResult;
164 check_authorization_cb (GDBusProxy *proxy,
168 CheckAuthData *data = user_data;
169 NMAuthManager *self = data->self;
170 NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
172 GError *error = NULL;
174 value = _nm_dbus_proxy_call_finish (proxy, res, G_VARIANT_TYPE ("((bba{ss}))"), &error);
176 if (data->cancellation_id != NULL &&
177 ( g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)
178 && !g_dbus_error_is_remote_error (error))) {
179 _LOGD ("call[%u]: CheckAuthorization cancelled", data->call_id);
180 g_dbus_proxy_call (priv->proxy,
181 "CancelCheckAuthorization",
182 g_variant_new ("(s)", data->cancellation_id),
183 G_DBUS_CALL_FLAGS_NONE,
185 NULL, /* GCancellable */
186 (GAsyncReadyCallback) cancel_check_authorization_cb,
187 g_object_ref (self));
189 _LOGD ("call[%u]: CheckAuthorization failed: %s", data->call_id, error->message);
190 g_dbus_error_strip_remote_error (error);
191 g_simple_async_result_set_error (data->simple,
193 NM_MANAGER_ERROR_FAILED,
194 "Authorization check failed: %s",
196 g_error_free (error);
198 CheckAuthorizationResult *result;
200 result = g_new0 (CheckAuthorizationResult, 1);
202 g_variant_get (value,
204 &result->is_authorized,
205 &result->is_challenge,
207 g_variant_unref (value);
209 _LOGD ("call[%u]: CheckAuthorization succeeded: (is_authorized=%d, is_challenge=%d)", data->call_id, result->is_authorized, result->is_challenge);
210 g_simple_async_result_set_op_res_gpointer (data->simple, result, g_free);
213 g_simple_async_result_complete (data->simple);
215 _check_auth_data_free (data);
219 _call_check_authorization (CheckAuthData *data)
221 NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (data->self);
223 g_dbus_proxy_call (priv->proxy,
224 "CheckAuthorization",
225 data->dbus_parameters,
226 G_DBUS_CALL_FLAGS_NONE,
227 G_MAXINT, /* no timeout */
229 (GAsyncReadyCallback) check_authorization_cb,
231 g_clear_object (&data->cancellable);
232 data->dbus_parameters = NULL;
236 nm_auth_manager_polkit_authority_check_authorization (NMAuthManager *self,
237 NMAuthSubject *subject,
238 const char *action_id,
239 gboolean allow_user_interaction,
240 GCancellable *cancellable,
241 GAsyncReadyCallback callback,
244 NMAuthManagerPrivate *priv;
245 char subject_buf[64];
246 GVariantBuilder builder;
247 PolkitCheckAuthorizationFlags flags;
248 GVariant *subject_value;
249 GVariant *details_value;
252 g_return_if_fail (NM_IS_AUTH_MANAGER (self));
253 g_return_if_fail (NM_IS_AUTH_SUBJECT (subject));
254 g_return_if_fail (nm_auth_subject_is_unix_process (subject));
255 g_return_if_fail (action_id != NULL);
256 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
258 priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
260 g_return_if_fail (priv->polkit_enabled);
262 flags = allow_user_interaction
263 ? POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION
264 : POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE;
266 subject_value = nm_auth_subject_unix_process_to_polkit_gvariant (subject);
267 g_assert (g_variant_is_floating (subject_value));
269 /* ((PolkitDetails *)NULL) */
270 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
271 details_value = g_variant_builder_end (&builder);
273 data = g_new0 (CheckAuthData, 1);
274 data->call_id = ++priv->call_id_counter;
275 data->self = g_object_ref (self);
276 data->simple = g_simple_async_result_new (G_OBJECT (self),
279 nm_auth_manager_polkit_authority_check_authorization);
280 if (cancellable != NULL) {
281 data->cancellation_id = g_strdup_printf ("cancellation-id-%u", data->call_id);
282 data->cancellable = g_object_ref (cancellable);
285 data->dbus_parameters = g_variant_new ("(@(sa{sv})s@a{ss}us)",
290 data->cancellation_id != NULL ? data->cancellation_id : "");
292 if (priv->new_proxy_cancellable) {
293 _LOGD ("call[%u]: CheckAuthorization(%s), subject=%s (wait for proxy)", data->call_id, action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
295 priv->queued_calls = g_slist_prepend (priv->queued_calls, data);
296 } else if (!priv->proxy) {
297 _LOGD ("call[%u]: CheckAuthorization(%s), subject=%s (fails due to invalid DBUS proxy)", data->call_id, action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
299 _call_check_authorization_complete_with_error (data, "invalid DBUS proxy");
301 _LOGD ("call[%u]: CheckAuthorization(%s), subject=%s", data->call_id, action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
303 _call_check_authorization (data);
308 nm_auth_manager_polkit_authority_check_authorization_finish (NMAuthManager *self,
310 gboolean *out_is_authorized,
311 gboolean *out_is_challenge,
314 gboolean success = FALSE;
315 gboolean is_authorized = FALSE;
316 gboolean is_challenge = FALSE;
318 g_return_val_if_fail (NM_IS_AUTH_MANAGER (self), FALSE);
319 g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), FALSE);
320 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
322 if (!g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) {
323 CheckAuthorizationResult *result;
325 result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
326 is_authorized = !!result->is_authorized;
327 is_challenge = !!result->is_challenge;
330 g_assert ((success && !error) || (!success || error));
332 if (out_is_authorized)
333 *out_is_authorized = is_authorized;
334 if (out_is_challenge)
335 *out_is_challenge = is_challenge;
339 /*****************************************************************************/
342 _emit_changed_signal (NMAuthManager *self)
344 _LOGD ("emit changed signal");
345 g_signal_emit_by_name (self, NM_AUTH_MANAGER_SIGNAL_CHANGED);
349 _log_name_owner (NMAuthManager *self, char **out_name_owner)
351 NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
354 name_owner = g_dbus_proxy_get_name_owner (priv->proxy);
356 _LOGD ("dbus name owner: '%s'", name_owner);
358 _LOGD ("dbus name owner: none");
361 *out_name_owner = name_owner;
367 _dbus_on_name_owner_notify_cb (GObject *object,
371 NMAuthManager *self = user_data;
372 NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
375 g_return_if_fail (priv->proxy == (void *) object);
377 _log_name_owner (self, &name_owner);
380 /* when the name disappears, we also want to raise a emit signal.
381 * When it appears, we raise one already. */
382 _emit_changed_signal (self);
389 _dbus_on_changed_signal_cb (GDBusProxy *proxy,
392 NMAuthManager *self = user_data;
393 NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
395 g_return_if_fail (priv->proxy == proxy);
397 _LOGD ("dbus signal: \"Changed\"");
398 _emit_changed_signal (self);
402 _dbus_new_proxy_cb (GObject *source_object,
406 NMAuthManager **p_self = user_data;
407 NMAuthManager *self = NULL;
408 NMAuthManagerPrivate *priv;
409 GError *error = NULL;
413 proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
416 _LOGD ("_dbus_new_proxy_cb(): manager destroyed before callback finished. Abort");
417 g_clear_object (&proxy);
418 g_clear_error (&error);
423 g_object_remove_weak_pointer (G_OBJECT (self), (void **)p_self);
426 priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
428 g_return_if_fail (priv->new_proxy_cancellable);
429 g_return_if_fail (!priv->proxy);
431 g_clear_object (&priv->new_proxy_cancellable);
433 priv->queued_calls = g_slist_reverse (priv->queued_calls);
437 _LOGE ("could not get polkit proxy: %s", error->message);
438 g_clear_error (&error);
440 while (priv->queued_calls) {
441 data = priv->queued_calls->data;
442 priv->queued_calls = g_slist_remove (priv->queued_calls, data);
444 _call_check_authorization_complete_with_error (data, "error creating DBUS proxy");
449 g_signal_connect (priv->proxy,
450 "notify::g-name-owner",
451 G_CALLBACK (_dbus_on_name_owner_notify_cb),
453 _nm_dbus_signal_connect (priv->proxy, "Changed", NULL,
454 G_CALLBACK (_dbus_on_changed_signal_cb),
457 _log_name_owner (self, NULL);
459 while (priv->queued_calls) {
460 data = priv->queued_calls->data;
461 priv->queued_calls = g_slist_remove (priv->queued_calls, data);
462 _LOGD ("call[%u]: CheckAuthorization invoke now", data->call_id);
463 _call_check_authorization (data);
465 _emit_changed_signal (self);
470 /*****************************************************************************/
473 nm_auth_manager_get ()
475 g_return_val_if_fail (singleton_instance, NULL);
477 return singleton_instance;
481 nm_auth_manager_setup (gboolean polkit_enabled)
485 g_return_val_if_fail (!singleton_instance, singleton_instance);
487 self = g_object_new (NM_TYPE_AUTH_MANAGER,
488 NM_AUTH_MANAGER_POLKIT_ENABLED, polkit_enabled,
490 _LOGD ("set instance");
492 singleton_instance = self;
493 nm_singleton_instance_register ();
495 nm_log_dbg (LOGD_CORE, "setup %s singleton (%p)", "NMAuthManager", singleton_instance);
500 /*****************************************************************************/
503 get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
505 NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (object);
508 case PROP_POLKIT_ENABLED:
509 g_value_set_boolean (value, priv->polkit_enabled);
512 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
518 set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
520 NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (object);
523 case PROP_POLKIT_ENABLED:
525 priv->polkit_enabled = !!g_value_get_boolean (value);
528 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
534 nm_auth_manager_init (NMAuthManager *self)
539 constructed (GObject *object)
541 NMAuthManager *self = NM_AUTH_MANAGER (object);
542 NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
544 G_OBJECT_CLASS (nm_auth_manager_parent_class)->constructed (object);
547 _LOGD ("create auth-manager: polkit %s", priv->polkit_enabled ? "enabled" : "disabled");
549 if (priv->polkit_enabled) {
550 NMAuthManager **p_self;
552 priv->new_proxy_cancellable = g_cancellable_new ();
553 p_self = g_new (NMAuthManager *, 1);
555 g_object_add_weak_pointer (G_OBJECT (self), (void **) p_self);
556 g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
557 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
562 priv->new_proxy_cancellable,
567 if (priv->polkit_enabled)
568 _LOGW ("create auth-manager: polkit disabled at compile time. All authentication requests will fail");
570 _LOGD ("create auth-manager: polkit disabled at compile time");
576 dispose (GObject *object)
578 NMAuthManager* self = NM_AUTH_MANAGER (object);
580 NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
586 /* since we take a reference for each queued call, we don't expect to have any queued calls in dispose() */
587 g_assert (!priv->queued_calls);
589 if (priv->new_proxy_cancellable) {
590 g_cancellable_cancel (priv->new_proxy_cancellable);
591 g_clear_object (&priv->new_proxy_cancellable);
595 g_signal_handlers_disconnect_by_data (priv->proxy, self);
596 g_clear_object (&priv->proxy);
600 G_OBJECT_CLASS (nm_auth_manager_parent_class)->dispose (object);
604 nm_auth_manager_class_init (NMAuthManagerClass *klass)
606 GObjectClass *object_class = G_OBJECT_CLASS (klass);
608 g_type_class_add_private (klass, sizeof (NMAuthManagerPrivate));
610 object_class->get_property = get_property;
611 object_class->set_property = set_property;
612 object_class->constructed = constructed;
613 object_class->dispose = dispose;
615 g_object_class_install_property
616 (object_class, PROP_POLKIT_ENABLED,
617 g_param_spec_boolean (NM_AUTH_MANAGER_POLKIT_ENABLED, "", "",
620 G_PARAM_CONSTRUCT_ONLY |
621 G_PARAM_STATIC_STRINGS));
623 signals[CHANGED_SIGNAL] = g_signal_new (NM_AUTH_MANAGER_SIGNAL_CHANGED,
624 NM_TYPE_AUTH_MANAGER,
626 0, /* class offset */
627 NULL, /* accumulator */
628 NULL, /* accumulator data */
629 g_cclosure_marshal_VOID__VOID,