From eabe7d856c243673bbaba3295ce74d72e188596d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 14 Aug 2014 13:34:57 +0200 Subject: [PATCH] auth: rework polkit autorization to use DBUS interface directly This makes NetworkManager independent of development headers and libpolkit-gobject-1.so library. Instead communicate directly with polkit using its DBUS interface. PolicyKit support is now always compiled in. You can control polkit authorization with the configuration option [main] auth-polkit=yes|no If the configure option is omitted, a build time default value is used. This default value can be set with the configure option --enable-polkit. This commit adds a new class NMAuthManager that reimplements the relevant DBUS client parts. It takes source code from the polkit library. https://bugzilla.gnome.org/show_bug.cgi?id=734146 Signed-off-by: Thomas Haller --- configure.ac | 34 +- contrib/fedora/rpm/NetworkManager.spec | 1 - man/NetworkManager.conf.xml.in | 8 + src/Makefile.am | 4 +- src/main.c | 3 + src/nm-active-connection.c | 2 +- src/nm-auth-manager.c | 643 +++++++++++++++++++++++++ src/nm-auth-manager.h | 82 ++++ src/nm-auth-subject.c | 482 ++++++++++++++---- src/nm-auth-subject.h | 47 +- src/nm-auth-utils.c | 318 +++--------- src/nm-auth-utils.h | 23 +- src/nm-config.c | 21 + src/nm-config.h | 1 + src/nm-manager.c | 61 +-- src/settings/nm-agent-manager.c | 36 +- src/settings/nm-secret-agent.c | 17 +- src/settings/nm-settings-connection.c | 18 +- src/settings/nm-settings.c | 20 +- 19 files changed, 1334 insertions(+), 487 deletions(-) create mode 100644 src/nm-auth-manager.c create mode 100644 src/nm-auth-manager.h diff --git a/configure.ac b/configure.ac index 1a1cc28412..93fc135265 100644 --- a/configure.ac +++ b/configure.ac @@ -457,27 +457,21 @@ else fi AM_CONDITIONAL(WITH_TEAMDCTL, test "${enable_teamdctl}" = "yes") -PKG_CHECK_MODULES(POLKIT, [polkit-gobject-1 >= 0.97], [have_polkit=yes],[have_polkit=no]) -AC_ARG_ENABLE(polkit, AS_HELP_STRING([--enable-polkit], [enable PolicyKit support]), - [enable_polkit=${enableval}], [enable_polkit=${have_polkit}]) -if (test "${enable_polkit}" = "yes"); then - if test x"$have_polkit" = x"no"; then - AC_MSG_ERROR(PolicyKit development headers are required) - fi - AC_SUBST(POLKIT_CFLAGS) - AC_SUBST(POLKIT_LIBS) - AC_DEFINE(WITH_POLKIT, 1, [Define if you have PolicyKit support]) +AC_ARG_ENABLE(polkit, AS_HELP_STRING([--enable-polkit], [set default value for auth-polkit configuration option]), + [enable_polkit=${enableval}], [enable_polkit=yes]) +if (test "${enable_polkit}" != "no"); then + enable_polkit=yes + AC_DEFINE(NM_CONFIG_DEFAULT_AUTH_POLKIT, TRUE, [The default value of the auth-polkit configuration option]) + NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT='true' else - AC_DEFINE(WITH_POLKIT, 0, [Define if you have PolicyKit support]) + AC_DEFINE(NM_CONFIG_DEFAULT_AUTH_POLKIT, FALSE, [The default value of the auth-polkit configuration option]) + NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT='false' fi -AM_CONDITIONAL(WITH_POLKIT, test "${enable_polkit}" = "yes") +AC_SUBST(NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT) AC_ARG_ENABLE(modify-system, AS_HELP_STRING([--enable-modify-system], [Allow users to modify system connections])) if test "${enable_modify_system}" = "yes"; then - if ! test "${enable_polkit}" = "yes"; then - AC_MSG_ERROR([--enable-modify-system requires --enable-polkit]) - fi NM_MODIFY_SYSTEM_POLICY="yes" else NM_MODIFY_SYSTEM_POLICY="auth_admin_keep" @@ -967,14 +961,10 @@ echo echo "Platform:" echo " session tracking: $with_session_tracking" echo " suspend/resume: $with_suspend_resume" -if test "${enable_polkit}" = "yes"; then - if test "${enable_modify_system}" = "yes"; then - echo " policykit: yes (permissive modify.system)" - else - echo " policykit: yes (restrictive modify.system)" - fi +if test "${enable_modify_system}" = "yes"; then + echo " policykit: yes (permissive modify.system) (default=${enable_polkit})" else - echo " policykit: no" + echo " policykit: yes (restrictive modify.system) (default=${enable_polkit})" fi echo " selinux: $have_selinux" echo diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec index def31b3602..8d3bc63bfb 100644 --- a/contrib/fedora/rpm/NetworkManager.spec +++ b/contrib/fedora/rpm/NetworkManager.spec @@ -139,7 +139,6 @@ BuildRequires: automake autoconf intltool libtool BuildRequires: ppp = %{ppp_version} BuildRequires: ppp-devel = %{ppp_version} BuildRequires: nss-devel >= 3.11.7 -BuildRequires: polkit-devel BuildRequires: dhclient BuildRequires: readline-devel %if %{regen_docs} diff --git a/man/NetworkManager.conf.xml.in b/man/NetworkManager.conf.xml.in index c017822e3f..06aa403a8b 100644 --- a/man/NetworkManager.conf.xml.in +++ b/man/NetworkManager.conf.xml.in @@ -122,6 +122,14 @@ Copyright (C) 2010 - 2013 Red Hat, Inc. 'true', then NetworkManager will reload connection files any time they changed. + + auth-polkit + Whether the system uses PolicyKit for authorization. + If false, all requests will be allowed. If + true, non-root requests are authorized using PolicyKit. + The default value is @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@. + + dhcp This key sets up what DHCP client diff --git a/src/Makefile.am b/src/Makefile.am index c72c85fefc..95df89be74 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -230,6 +230,8 @@ nm_sources = \ nm-ip6-config.h \ nm-logging.c \ nm-logging.h \ + nm-auth-manager.c \ + nm-auth-manager.h \ nm-auth-subject.c \ nm-auth-subject.h \ nm-auth-utils.c \ @@ -328,7 +330,6 @@ AM_CPPFLAGS += \ $(LIBNL_CFLAGS) \ $(LIBNDP_CFLAGS) \ $(LIBSOUP_CFLAGS) \ - $(POLKIT_CFLAGS) \ $(SYSTEMD_LOGIN_CFLAGS) \ \ -DBINDIR=\"$(bindir)\" \ @@ -365,7 +366,6 @@ libNetworkManager_la_LIBADD = \ $(GLIB_LIBS) \ $(GUDEV_LIBS) \ $(LIBNL_LIBS) \ - $(POLKIT_LIBS) \ $(SYSTEMD_LOGIN_LIBS) \ $(LIBNDP_LIBS) \ $(LIBDL) \ diff --git a/src/main.c b/src/main.c index d41472d4ff..17769587a9 100644 --- a/src/main.c +++ b/src/main.c @@ -56,6 +56,7 @@ #include "nm-session-monitor.h" #include "nm-dispatcher.h" #include "nm-settings.h" +#include "nm-auth-manager.h" #if !defined(NM_DIST_VERSION) # define NM_DIST_VERSION VERSION @@ -592,6 +593,8 @@ main (int argc, char *argv[]) /* Set up platform interaction layer */ nm_linux_platform_setup (); + nm_auth_manager_setup (nm_config_get_auth_polkit (config)); + /* Initialize our DBus service & connection */ dbus_mgr = nm_dbus_manager_get (); g_assert (dbus_mgr != NULL); diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index 888bbd1797..3e532e5225 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -319,7 +319,7 @@ nm_active_connection_get_user_requested (NMActiveConnection *self) { g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), FALSE); - return !nm_auth_subject_get_internal (NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->subject); + return nm_auth_subject_is_unix_process (NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->subject); } NMDevice * diff --git a/src/nm-auth-manager.c b/src/nm-auth-manager.c new file mode 100644 index 0000000000..092248fa5f --- /dev/null +++ b/src/nm-auth-manager.c @@ -0,0 +1,643 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include "nm-auth-manager.h" + +#include "nm-logging.h" + + +#define POLKIT_SERVICE "org.freedesktop.PolicyKit1" +#define POLKIT_OBJECT_PATH "/org/freedesktop/PolicyKit1/Authority" +#define POLKIT_INTERFACE "org.freedesktop.PolicyKit1.Authority" + + +#define _LOG_DEFAULT_DOMAIN LOGD_CORE + +#define _LOG(level, domain, ...) \ + G_STMT_START { \ + if (nm_logging_enabled ((level), (domain))) { \ + char __prefix[30] = "auth"; \ + \ + if ((self) != _instance) \ + g_snprintf (__prefix, sizeof (__prefix), "auth[%p]", (self)); \ + nm_log ((level), (domain), \ + "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } G_STMT_END + +#define _LOGD(...) _LOG (LOGL_DEBUG, _LOG_DEFAULT_DOMAIN, __VA_ARGS__) +#define _LOGI(...) _LOG (LOGL_INFO, _LOG_DEFAULT_DOMAIN, __VA_ARGS__) +#define _LOGW(...) _LOG (LOGL_WARN, _LOG_DEFAULT_DOMAIN, __VA_ARGS__) +#define _LOGE(...) _LOG (LOGL_ERR, _LOG_DEFAULT_DOMAIN, __VA_ARGS__) + + +enum { + PROP_0, + PROP_POLKIT_ENABLED, + + LAST_PROP +}; + +enum { + CHANGED_SIGNAL, + + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = {0}; + +typedef struct { + gboolean polkit_enabled; + guint call_id_counter; + GCancellable *new_proxy_cancellable; + GSList *queued_calls; + GDBusProxy *proxy; +} NMAuthManagerPrivate; + +static NMAuthManager *_instance = NULL; + +G_DEFINE_TYPE (NMAuthManager, nm_auth_manager, G_TYPE_OBJECT) + +#define NM_AUTH_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_AUTH_MANAGER, NMAuthManagerPrivate)) + +GQuark +nm_auth_manager_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-auth-manager-error-quark"); + return quark; +} + +/*****************************************************************************/ + +gboolean +nm_auth_manager_get_polkit_enabled (NMAuthManager *self) +{ + g_return_val_if_fail (NM_IS_AUTH_MANAGER (self), FALSE); + + return NM_AUTH_MANAGER_GET_PRIVATE (self)->polkit_enabled; +} + +/*****************************************************************************/ + +typedef enum { + POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE = 0, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION = (1<<0), +} PolkitCheckAuthorizationFlags; + +typedef struct { + guint call_id; + NMAuthManager *self; + GSimpleAsyncResult *simple; + gchar *cancellation_id; + GVariant *dbus_parameters; + GCancellable *cancellable; +} CheckAuthData; + +static void +_check_auth_data_free (CheckAuthData *data) +{ + if (data->dbus_parameters) + g_variant_unref (data->dbus_parameters); + g_object_unref (data->self); + g_object_unref (data->simple); + g_clear_object (&data->cancellable); + g_free (data->cancellation_id); + g_free (data); +} + +static void +_call_check_authorization_complete_with_error (CheckAuthData *data, + const char *error_message) +{ + NMAuthManager *self = data->self; + GError *error = NULL; + + _LOGD ("call[%u]: CheckAuthorization failed due to internal error: %s", data->call_id, error_message); + g_set_error_literal (&error, NM_AUTH_MANAGER_ERROR, NM_AUTH_MANAGER_ERROR_DBUS_FAILURE, error_message); + g_simple_async_result_set_from_error (data->simple, error); + g_clear_error (&error); + + g_simple_async_result_complete_in_idle (data->simple); + + _check_auth_data_free (data); +} + +static void +cancel_check_authorization_cb (GDBusProxy *proxy, + GAsyncResult *res, + gpointer user_data) +{ + NMAuthManager *self = user_data; + GVariant *value; + GError *error= NULL; + + value = g_dbus_proxy_call_finish (proxy, res, &error); + if (value == NULL) { + _LOGD ("Error cancelling authorization check: %s", error->message); + g_error_free (error); + } else + g_variant_unref (value); + + g_object_unref (self); +} + +typedef struct { + gboolean is_authorized; + gboolean is_challenge; +} CheckAuthorizationResult; + +static void +check_authorization_cb (GDBusProxy *proxy, + GAsyncResult *res, + gpointer user_data) +{ + CheckAuthData *data = user_data; + NMAuthManager *self = data->self; + NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); + GVariant *value; + GError *error = NULL; + + value = g_dbus_proxy_call_finish (proxy, res, &error); + if (value == NULL) { + if (data->cancellation_id != NULL && + (!g_dbus_error_is_remote_error (error) && + error->domain == G_IO_ERROR && + error->code == G_IO_ERROR_CANCELLED)) { + _LOGD ("call[%u]: CheckAuthorization cancelled", data->call_id); + g_dbus_proxy_call (priv->proxy, + "CancelCheckAuthorization", + g_variant_new ("(s)", data->cancellation_id), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + (GAsyncReadyCallback) cancel_check_authorization_cb, + g_object_ref (self)); + } else + _LOGD ("call[%u]: CheckAuthorization failed: %s", data->call_id, error->message); + g_simple_async_result_set_from_error (data->simple, error); + g_error_free (error); + } else { + GVariant *result_value; + CheckAuthorizationResult *result; + + result = g_new0 (CheckAuthorizationResult, 1); + + result_value = g_variant_get_child_value (value, 0); + g_variant_get (result_value, + "(bb@a{ss})", + &result->is_authorized, + &result->is_challenge, + NULL); + g_variant_unref (result_value); + g_variant_unref (value); + + _LOGD ("call[%u]: CheckAuthorization succeeded: (is_authorized=%d, is_challenge=%d)", data->call_id, result->is_authorized, result->is_challenge); + g_simple_async_result_set_op_res_gpointer (data->simple, result, g_free); + } + + g_simple_async_result_complete (data->simple); + + _check_auth_data_free (data); +} + +static void +_call_check_authorization (CheckAuthData *data) +{ + NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (data->self); + + g_dbus_proxy_call (priv->proxy, + "CheckAuthorization", + data->dbus_parameters, + G_DBUS_CALL_FLAGS_NONE, + G_MAXINT, /* no timeout */ + data->cancellable, + (GAsyncReadyCallback) check_authorization_cb, + data); + g_clear_object (&data->cancellable); + data->dbus_parameters = NULL; +} + +void +nm_auth_manager_polkit_authority_check_authorization (NMAuthManager *self, + NMAuthSubject *subject, + const char *action_id, + gboolean allow_user_interaction, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMAuthManagerPrivate *priv; + char subject_buf[64]; + GVariantBuilder builder; + PolkitCheckAuthorizationFlags flags; + GVariant *subject_value; + GVariant *details_value; + CheckAuthData *data; + + g_return_if_fail (NM_IS_AUTH_MANAGER (self)); + g_return_if_fail (NM_IS_AUTH_SUBJECT (subject)); + g_return_if_fail (nm_auth_subject_is_unix_process (subject)); + g_return_if_fail (action_id != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + priv = NM_AUTH_MANAGER_GET_PRIVATE (self); + + g_return_if_fail (priv->polkit_enabled); + + flags = allow_user_interaction + ? POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION + : POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE; + + subject_value = nm_auth_subject_unix_process_to_polkit_gvariant (subject); + g_assert (g_variant_is_floating (subject_value)); + + /* ((PolkitDetails *)NULL) */ + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}")); + details_value = g_variant_builder_end (&builder); + + data = g_new0 (CheckAuthData, 1); + data->call_id = ++priv->call_id_counter; + data->self = g_object_ref (self); + data->simple = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + nm_auth_manager_polkit_authority_check_authorization); + if (cancellable != NULL) { + data->cancellation_id = g_strdup_printf ("cancellation-id-%u", data->call_id); + data->cancellable = g_object_ref (cancellable); + } + + data->dbus_parameters = g_variant_new ("(@(sa{sv})s@a{ss}us)", + subject_value, + action_id, + details_value, + (guint32) flags, + data->cancellation_id != NULL ? data->cancellation_id : ""); + + if (priv->new_proxy_cancellable) { + _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))); + + priv->queued_calls = g_slist_prepend (priv->queued_calls, data); + } else if (!priv->proxy) { + _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))); + + _call_check_authorization_complete_with_error (data, "invalid DBUS proxy"); + } else { + _LOGD ("call[%u]: CheckAuthorization(%s), subject=%s", data->call_id, action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); + + _call_check_authorization (data); + } +} + +gboolean +nm_auth_manager_polkit_authority_check_authorization_finish (NMAuthManager *self, + GAsyncResult *res, + gboolean *out_is_authorized, + gboolean *out_is_challenge, + GError **error) +{ + gboolean success = FALSE; + gboolean is_authorized = FALSE; + gboolean is_challenge = FALSE; + + g_return_val_if_fail (NM_IS_AUTH_MANAGER (self), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) { + CheckAuthorizationResult *result; + + result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)); + is_authorized = !!result->is_authorized; + is_challenge = !!result->is_challenge; + success = TRUE; + } + g_assert ((success && !error) || (!success || error)); + + if (out_is_authorized) + *out_is_authorized = is_authorized; + if (out_is_challenge) + *out_is_challenge = is_challenge; + return success; +} + +/*****************************************************************************/ + +static void +_emit_changed_signal (NMAuthManager *self) +{ + _LOGD ("emit changed signal"); + g_signal_emit_by_name (self, NM_AUTH_MANAGER_SIGNAL_CHANGED); +} + +static void +_log_name_owner (NMAuthManager *self, char **out_name_owner) +{ + NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); + char *name_owner; + + name_owner = g_dbus_proxy_get_name_owner (priv->proxy); + if (name_owner) + _LOGD ("dbus name owner: '%s'", name_owner); + else + _LOGD ("dbus name owner: none"); + + if (out_name_owner) + *out_name_owner = name_owner; + else + g_free (name_owner); +} + +static void +_dbus_on_name_owner_notify_cb (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMAuthManager *self = user_data; + NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); + char *name_owner; + + g_return_if_fail (priv->proxy == (void *) object); + + _log_name_owner (self, &name_owner); + + if (!name_owner) { + /* when the name disappears, we also want to raise a emit signal. + * When it appears, we raise one already. */ + _emit_changed_signal (self); + } + + g_free (name_owner); +} + +static void +_dbus_on_g_signal_cb (GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + NMAuthManager *self = user_data; + NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); + + g_return_if_fail (priv->proxy == proxy); + + _LOGD ("dbus signal: \"%s\"", signal_name ? signal_name : "(null)"); + + if (g_strcmp0 (signal_name, "Changed") == 0) + _emit_changed_signal (self); +} + +static void +_dbus_new_proxy_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NMAuthManager **p_self = user_data; + NMAuthManager *self = NULL; + NMAuthManagerPrivate *priv; + GError *error = NULL; + GDBusProxy *proxy; + CheckAuthData *data; + + proxy = g_dbus_proxy_new_for_bus_finish (res, &error); + + if (!*p_self) { + _LOGD ("_dbus_new_proxy_cb(): manager destroyed before callback finished. Abort"); + g_clear_object (&proxy); + g_clear_error (&error); + g_free (p_self); + return; + } + self = *p_self; + g_object_remove_weak_pointer (G_OBJECT (self), (void **)p_self); + g_free (p_self); + + priv = NM_AUTH_MANAGER_GET_PRIVATE (self); + + g_return_if_fail (priv->new_proxy_cancellable); + g_return_if_fail (!priv->proxy); + + g_clear_object (&priv->new_proxy_cancellable); + + priv->queued_calls = g_slist_reverse (priv->queued_calls); + + priv->proxy = proxy; + if (!priv->proxy) { + _LOGE ("could not get polkit proxy: %s", error->message); + g_clear_error (&error); + + while (priv->queued_calls) { + data = priv->queued_calls->data; + priv->queued_calls = g_slist_remove (priv->queued_calls, data); + + _call_check_authorization_complete_with_error (data, "error creating DBUS proxy"); + } + return; + } + + g_signal_connect (priv->proxy, + "notify::g-name-owner", + G_CALLBACK (_dbus_on_name_owner_notify_cb), + self); + g_signal_connect (priv->proxy, + "g-signal", + G_CALLBACK (_dbus_on_g_signal_cb), + self); + + _log_name_owner (self, NULL); + + while (priv->queued_calls) { + data = priv->queued_calls->data; + priv->queued_calls = g_slist_remove (priv->queued_calls, data); + _LOGD ("call[%u]: CheckAuthorization invoke now", data->call_id); + _call_check_authorization (data); + } + _emit_changed_signal (self); +} + +/*****************************************************************************/ + +NMAuthManager * +nm_auth_manager_get () +{ + g_return_val_if_fail (_instance, NULL); + + return _instance; +} + +NMAuthManager * +nm_auth_manager_setup (gboolean polkit_enabled) +{ + NMAuthManager *self; + + g_return_val_if_fail (!_instance, _instance); + + self = g_object_new (NM_TYPE_AUTH_MANAGER, + NM_AUTH_MANAGER_POLKIT_ENABLED, polkit_enabled, + NULL); + _LOGD ("set instance"); + + return (_instance = self); +} + +/*****************************************************************************/ + +static void +get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_POLKIT_ENABLED: + g_value_set_boolean (value, priv->polkit_enabled); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_POLKIT_ENABLED: + /* construct only */ + priv->polkit_enabled = !!g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_auth_manager_init (NMAuthManager *self) +{ +} + +static void +constructed (GObject *object) +{ + NMAuthManager *self = NM_AUTH_MANAGER (object); + NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); + + G_OBJECT_CLASS (nm_auth_manager_parent_class)->constructed (object); + + _LOGD ("create auth-manager: polkit %s", priv->polkit_enabled ? "enabled" : "disabled"); + + if (priv->polkit_enabled) { + NMAuthManager **p_self; + + priv->new_proxy_cancellable = g_cancellable_new (); + p_self = g_new (NMAuthManager *, 1); + *p_self = self; + g_object_add_weak_pointer (G_OBJECT (self), (void **) p_self); + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + POLKIT_SERVICE, + POLKIT_OBJECT_PATH, + POLKIT_INTERFACE, + priv->new_proxy_cancellable, + _dbus_new_proxy_cb, + p_self); + } +} + + +static void +dispose (GObject *object) +{ + NMAuthManager* self = NM_AUTH_MANAGER (object); + NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); + + _LOGD ("dispose"); + + /* since we take a reference for each queued call, we don't expect to have any queued calls in dispose() */ + g_assert (!priv->queued_calls); + + if (priv->new_proxy_cancellable) { + g_cancellable_cancel (priv->new_proxy_cancellable); + g_clear_object (&priv->new_proxy_cancellable); + } + + if (priv->proxy) { + g_signal_handlers_disconnect_by_func (priv->proxy, _dbus_on_name_owner_notify_cb, self); + g_signal_handlers_disconnect_by_func (priv->proxy, _dbus_on_g_signal_cb, self); + g_clear_object (&priv->proxy); + } + + G_OBJECT_CLASS (nm_auth_manager_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMAuthManager* self = NM_AUTH_MANAGER (object); + + G_OBJECT_CLASS (nm_auth_manager_parent_class)->finalize (object); + + if (self == _instance) { + _instance = NULL; + _LOGD ("unset instance"); + } +} + +static void +nm_auth_manager_class_init (NMAuthManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMAuthManagerPrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->finalize = finalize; + + g_object_class_install_property + (object_class, PROP_POLKIT_ENABLED, + g_param_spec_boolean (NM_AUTH_MANAGER_POLKIT_ENABLED, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + signals[CHANGED_SIGNAL] = g_signal_new (NM_AUTH_MANAGER_SIGNAL_CHANGED, + NM_TYPE_AUTH_MANAGER, + G_SIGNAL_RUN_LAST, + 0, /* class offset */ + NULL, /* accumulator */ + NULL, /* accumulator data */ + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + +} + diff --git a/src/nm-auth-manager.h b/src/nm-auth-manager.h new file mode 100644 index 0000000000..3f5ebc6589 --- /dev/null +++ b/src/nm-auth-manager.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2014 Red Hat, Inc. + */ + +#ifndef NM_AUTH_MANAGER_H +#define NM_AUTH_MANAGER_H + +#include + +#include "nm-auth-subject.h" + +G_BEGIN_DECLS + + +#define NM_TYPE_AUTH_MANAGER (nm_auth_manager_get_type ()) +#define NM_AUTH_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_AUTH_MANAGER, NMAuthManager)) +#define NM_AUTH_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_AUTH_MANAGER, NMAuthManagerClass)) +#define NM_IS_AUTH_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_AUTH_MANAGER)) +#define NM_IS_AUTH_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_AUTH_MANAGER)) +#define NM_AUTH_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_AUTH_MANAGER, NMAuthManagerClass)) + + +#define NM_AUTH_MANAGER_POLKIT_ENABLED "polkit-enabled" + +#define NM_AUTH_MANAGER_SIGNAL_CHANGED "changed" + +#define NM_AUTH_MANAGER_ERROR (nm_auth_manager_error_quark ()) + +typedef enum { + NM_AUTH_MANAGER_ERROR_DBUS_FAILURE = 1, +} NMAuthManagerError; + +typedef struct { + GObject parent; +} NMAuthManager; + +typedef struct { + GObjectClass parent; +} NMAuthManagerClass; + +GType nm_auth_manager_get_type (void); +GQuark nm_auth_manager_error_quark (void); + +NMAuthManager *nm_auth_manager_setup (gboolean polkit_enabled); +NMAuthManager *nm_auth_manager_get (void); + +gboolean nm_auth_manager_get_polkit_enabled (NMAuthManager *self); + +void nm_auth_manager_polkit_authority_check_authorization (NMAuthManager *self, + NMAuthSubject *subject, + const char *action_id, + gboolean allow_user_interaction, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean nm_auth_manager_polkit_authority_check_authorization_finish (NMAuthManager *self, + GAsyncResult *res, + gboolean *out_is_authorized, + gboolean *out_is_challenge, + GError **error); + + +G_END_DECLS + +#endif /* NM_AUTH_MANAGER_H */ + diff --git a/src/nm-auth-subject.c b/src/nm-auth-subject.c index e770af49ec..f982616677 100644 --- a/src/nm-auth-subject.c +++ b/src/nm-auth-subject.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2013 Red Hat, Inc. + * Copyright (C) 2013 - 2014 Red Hat, Inc. */ /** @@ -26,108 +26,262 @@ * makes requests, like process identifier and user UID. */ -#include -#include -#include -#include -#include - -#if WITH_POLKIT -#include -#endif - #include "nm-auth-subject.h" + +#include +#include +#include + #include "nm-dbus-manager.h" +#include "nm-enum-types.h" G_DEFINE_TYPE (NMAuthSubject, nm_auth_subject, G_TYPE_OBJECT) #define NM_AUTH_SUBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_AUTH_SUBJECT, NMAuthSubjectPrivate)) -typedef struct { - gulong pid; - gulong uid; - char *dbus_sender; +enum { + PROP_0, + PROP_SUBJECT_TYPE, + PROP_UNIX_PROCESS_DBUS_SENDER, + PROP_UNIX_PROCESS_PID, + PROP_UNIX_PROCESS_UID, -#if WITH_POLKIT - PolkitSubject *pk_subject; -#endif + PROP_LAST, +}; + +typedef struct { + NMAuthSubjectType subject_type; + struct { + gulong pid; + gulong uid; + guint64 start_time; + char *dbus_sender; + } unix_process; } NMAuthSubjectPrivate; -static NMAuthSubject * -_new_common (DBusGMethodInvocation *context, - DBusConnection *connection, - DBusMessage *message, - gboolean internal) + + +/**************************************************************/ + +/* copied from polkit source (src/polkit/polkitunixprocess.c) + * and adjusted. + */ +static guint64 +get_start_time_for_pid (pid_t pid) { - NMAuthSubject *subject; - NMAuthSubjectPrivate *priv; - NMDBusManager *dbus_mgr; - gboolean success = FALSE; + guint64 start_time; + gchar *filename; + gchar *contents; + size_t length; + gchar **tokens; + guint num_tokens; + gchar *p; + gchar *endp; - g_return_val_if_fail (context || (connection && message) || internal, NULL); - if (internal) - g_return_val_if_fail (context == NULL && connection == NULL && message == NULL, NULL); + start_time = 0; + contents = NULL; - subject = NM_AUTH_SUBJECT (g_object_new (NM_TYPE_AUTH_SUBJECT, NULL)); - priv = NM_AUTH_SUBJECT_GET_PRIVATE (subject); + filename = g_strdup_printf ("/proc/%d/stat", pid); - dbus_mgr = nm_dbus_manager_get (); + if (!g_file_get_contents (filename, &contents, &length, NULL)) + goto out; - if (internal) { - priv->uid = 0; - priv->pid = 0; - return subject; + /* start time is the token at index 19 after the '(process name)' entry - since only this + * field can contain the ')' character, search backwards for this to avoid malicious + * processes trying to fool us + */ + p = strrchr (contents, ')'); + if (p == NULL) + goto out; + p += 2; /* skip ') ' */ + if (p - contents >= (int) length) + goto out; + + tokens = g_strsplit (p, " ", 0); + + num_tokens = g_strv_length (tokens); + + if (num_tokens < 20) + goto out; + + start_time = strtoull (tokens[19], &endp, 10); + if (endp == tokens[19]) + goto out; + + g_strfreev (tokens); + + out: + g_free (filename); + g_free (contents); + + return start_time; +} + +/**************************************************************/ + +#define CHECK_SUBJECT(self, error_value) \ + NMAuthSubjectPrivate *priv; \ + g_return_val_if_fail (NM_IS_AUTH_SUBJECT (self), error_value); \ + priv = NM_AUTH_SUBJECT_GET_PRIVATE (self); \ + +#define CHECK_SUBJECT_TYPED(self, expected_subject_type, error_value) \ + CHECK_SUBJECT (self, error_value); \ + g_return_val_if_fail (priv->subject_type == (expected_subject_type), error_value); + +const char * +nm_auth_subject_to_string (NMAuthSubject *self, char *buf, gsize buf_len) +{ + CHECK_SUBJECT (self, NULL); + + switch (priv->subject_type) { + case NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS: + g_snprintf (buf, buf_len, "unix-process[pid=%lu, uid=%lu, start=%llu]", + (long unsigned) priv->unix_process.pid, + (long unsigned) priv->unix_process.uid, + (long long unsigned) priv->unix_process.start_time); + break; + case NM_AUTH_SUBJECT_TYPE_INTERNAL: + g_strlcat (buf, "internal", buf_len); + break; + default: + g_strlcat (buf, "invalid", buf_len); + break; } + return buf; +} + +/* returns a floating variant */ +GVariant * +nm_auth_subject_unix_process_to_polkit_gvariant (NMAuthSubject *self) +{ + GVariantBuilder builder; + GVariant *dict; + GVariant *ret; + CHECK_SUBJECT_TYPED (self, NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, NULL); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&builder, "{sv}", "pid", + g_variant_new_uint32 (priv->unix_process.pid)); + g_variant_builder_add (&builder, "{sv}", "start-time", + g_variant_new_uint64 (priv->unix_process.start_time)); + g_variant_builder_add (&builder, "{sv}", "uid", + g_variant_new_int32 (priv->unix_process.uid)); + dict = g_variant_builder_end (&builder); + ret = g_variant_new ("(s@a{sv})", "unix-process", dict); + return ret; +} + +NMAuthSubjectType +nm_auth_subject_get_subject_type (NMAuthSubject *subject) +{ + CHECK_SUBJECT (subject, NM_AUTH_SUBJECT_TYPE_INVALID); + + return priv->subject_type; +} + +gboolean +nm_auth_subject_is_internal (NMAuthSubject *subject) +{ + return nm_auth_subject_get_subject_type (subject) == NM_AUTH_SUBJECT_TYPE_INTERNAL; +} + +gboolean +nm_auth_subject_is_unix_process (NMAuthSubject *subject) +{ + return nm_auth_subject_get_subject_type (subject) == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS; +} + +gulong +nm_auth_subject_get_unix_process_pid (NMAuthSubject *subject) +{ + CHECK_SUBJECT_TYPED (subject, NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, G_MAXULONG); + + return priv->unix_process.pid; +} + +gulong +nm_auth_subject_get_unix_process_uid (NMAuthSubject *subject) +{ + CHECK_SUBJECT_TYPED (subject, NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, G_MAXULONG); + + return priv->unix_process.uid; +} + +const char * +nm_auth_subject_get_unix_process_dbus_sender (NMAuthSubject *subject) +{ + CHECK_SUBJECT_TYPED (subject, NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, NULL); + + return priv->unix_process.dbus_sender; +} + +/**************************************************************/ + +static NMAuthSubject * +_new_unix_process (DBusGMethodInvocation *context, + DBusConnection *connection, + DBusMessage *message) +{ + NMAuthSubject *self; + gboolean success = FALSE; + gulong pid = 0, uid = 0; + char *dbus_sender = NULL; + + g_return_val_if_fail (context || (connection && message), NULL); if (context) { - success = nm_dbus_manager_get_caller_info (dbus_mgr, + success = nm_dbus_manager_get_caller_info (nm_dbus_manager_get (), context, - &priv->dbus_sender, - &priv->uid, - &priv->pid); + &dbus_sender, + &uid, + &pid); } else if (message) { - success = nm_dbus_manager_get_caller_info_from_message (dbus_mgr, + success = nm_dbus_manager_get_caller_info_from_message (nm_dbus_manager_get (), connection, message, - &priv->dbus_sender, - &priv->uid, - &priv->pid); + &dbus_sender, + &uid, + &pid); } else g_assert_not_reached (); - if (!success) { - g_object_unref (subject); + if (!success) return NULL; + + g_return_val_if_fail (dbus_sender && *dbus_sender, NULL); + /* polkit glib library stores uid and pid as gint. There might be some + * pitfalls if the id ever happens to be larger then that. Just assert against + * it here. */ + g_return_val_if_fail (uid <= MIN (G_MAXINT, G_MAXINT32), NULL); + g_return_val_if_fail (pid > 0 && pid <= MIN (G_MAXINT, G_MAXINT32), NULL); + + self = NM_AUTH_SUBJECT (g_object_new (NM_TYPE_AUTH_SUBJECT, + NM_AUTH_SUBJECT_SUBJECT_TYPE, NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, + NM_AUTH_SUBJECT_UNIX_PROCESS_DBUS_SENDER, dbus_sender, + NM_AUTH_SUBJECT_UNIX_PROCESS_PID, (gulong) pid, + NM_AUTH_SUBJECT_UNIX_PROCESS_UID, (gulong) uid, + NULL)); + + if (NM_AUTH_SUBJECT_GET_PRIVATE (self)->subject_type != NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS) { + /* this most likely happened because the process is gone (start_time==0). + * Either that is not assert-worthy, or constructed() already asserted. + * Just return NULL. */ + g_clear_object (&self); } - - g_assert (priv->dbus_sender); - g_assert_cmpuint (priv->pid, !=, 0); - -#if WITH_POLKIT - /* FIXME: should we use polkit_unix_session_new() to store the session ID - * of a short-lived process, so that the process can exit but we can still - * ask that user for authorization? - */ - priv->pk_subject = polkit_unix_process_new_for_owner (priv->pid, 0, priv->uid); - if (!priv->pk_subject) - return NULL; -#endif - - return subject; -} - - -NMAuthSubject * -nm_auth_subject_new_from_context (DBusGMethodInvocation *context) -{ - return _new_common (context, NULL, NULL, FALSE); + return self; } NMAuthSubject * -nm_auth_subject_new_from_message (DBusConnection *connection, - DBusMessage *message) +nm_auth_subject_new_unix_process_from_context (DBusGMethodInvocation *context) { - return _new_common (NULL, connection, message, FALSE); + return _new_unix_process (context, NULL, NULL); +} + +NMAuthSubject * +nm_auth_subject_new_unix_process_from_message (DBusConnection *connection, + DBusMessage *message) +{ + return _new_unix_process (NULL, connection, message); } /** @@ -140,53 +294,135 @@ nm_auth_subject_new_from_message (DBusConnection *connection, NMAuthSubject * nm_auth_subject_new_internal (void) { - return _new_common (NULL, NULL, NULL, TRUE); + return NM_AUTH_SUBJECT (g_object_new (NM_TYPE_AUTH_SUBJECT, + NM_AUTH_SUBJECT_SUBJECT_TYPE, NM_AUTH_SUBJECT_TYPE_INTERNAL, + NULL)); } /**************************************************************/ -gulong -nm_auth_subject_get_uid (NMAuthSubject *subject) +static void +get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - return NM_AUTH_SUBJECT_GET_PRIVATE (subject)->uid; + NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SUBJECT_TYPE: + g_value_set_enum (value, priv->subject_type); + break; + case PROP_UNIX_PROCESS_DBUS_SENDER: + g_value_set_string (value, priv->unix_process.dbus_sender); + break; + case PROP_UNIX_PROCESS_PID: + g_value_set_ulong (value, priv->unix_process.pid); + break; + case PROP_UNIX_PROCESS_UID: + g_value_set_ulong (value, priv->unix_process.uid); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } } -gulong -nm_auth_subject_get_pid (NMAuthSubject *subject) +static void +set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - return NM_AUTH_SUBJECT_GET_PRIVATE (subject)->pid; + NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE (object); + NMAuthSubjectType subject_type; + const char *str; + gulong id; + + /* all properties are construct-only */ + switch (prop_id) { + case PROP_SUBJECT_TYPE: + subject_type = g_value_get_enum (value); + g_return_if_fail (subject_type != NM_AUTH_SUBJECT_TYPE_INVALID); + priv->subject_type |= subject_type; + g_return_if_fail (priv->subject_type == subject_type); + break; + case PROP_UNIX_PROCESS_DBUS_SENDER: + if ((str = g_value_get_string (value))) { + priv->subject_type |= NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS; + g_return_if_fail (priv->subject_type == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS); + priv->unix_process.dbus_sender = g_strdup (str); + } + break; + case PROP_UNIX_PROCESS_PID: + if ((id = g_value_get_ulong (value)) != G_MAXULONG) { + priv->subject_type |= NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS; + g_return_if_fail (priv->subject_type == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS); + priv->unix_process.pid = id; + } + break; + case PROP_UNIX_PROCESS_UID: + if ((id = g_value_get_ulong (value)) != G_MAXULONG) { + priv->subject_type |= NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS; + g_return_if_fail (priv->subject_type == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS); + priv->unix_process.uid = id; + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } } -const char * -nm_auth_subject_get_dbus_sender (NMAuthSubject *subject) +static void +_clear_private (NMAuthSubjectPrivate *priv) { - return NM_AUTH_SUBJECT_GET_PRIVATE (subject)->dbus_sender; + priv->subject_type = NM_AUTH_SUBJECT_TYPE_INVALID; + priv->unix_process.pid = G_MAXULONG; + priv->unix_process.uid = G_MAXULONG; + g_clear_pointer (&priv->unix_process.dbus_sender, g_free); } -gboolean -nm_auth_subject_get_internal (NMAuthSubject *subject) -{ - /* internal requests will have no dbus sender */ - return NM_AUTH_SUBJECT_GET_PRIVATE (subject)->dbus_sender ? FALSE : TRUE; -} - -#if WITH_POLKIT -PolkitSubject * -nm_auth_subject_get_polkit_subject (NMAuthSubject *subject) -{ - return NM_AUTH_SUBJECT_GET_PRIVATE (subject)->pk_subject; -} -#endif - -/******************************************************************/ - static void nm_auth_subject_init (NMAuthSubject *self) { + _clear_private (NM_AUTH_SUBJECT_GET_PRIVATE (self)); +} + +static void +constructed (GObject *object) +{ + NMAuthSubject *self = NM_AUTH_SUBJECT (object); NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE (self); - priv->pid = G_MAXULONG; - priv->uid = G_MAXULONG; + /* validate that the created instance. */ + + switch (priv->subject_type) { + case NM_AUTH_SUBJECT_TYPE_INTERNAL: + priv->unix_process.pid = G_MAXULONG; + priv->unix_process.uid = 0; /* internal uses 'root' user */ + return; + case NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS: + /* Ensure pid and uid to be representable as int32. + * DBUS treats them as uint32, polkit library as gint. */ + if (priv->unix_process.pid > MIN (G_MAXINT, G_MAXINT32)) + break; + if (priv->unix_process.uid > MIN (G_MAXINT, G_MAXINT32)) { + /* for uid==-1, libpolkit-gobject-1 detects the user based on the process id. + * Don't bother and require the user id as parameter. */ + break; + } + if (!priv->unix_process.dbus_sender || !*priv->unix_process.dbus_sender) + break; + + priv->unix_process.start_time = get_start_time_for_pid (priv->unix_process.pid); + + if (!priv->unix_process.start_time) { + /* could not detect the process start time. The subject is invalid, but don't + * assert against it. */ + _clear_private (priv); + } + return; + default: + break; + } + + _clear_private (priv); + g_return_if_reached (); } static void @@ -194,12 +430,7 @@ finalize (GObject *object) { NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE (object); - g_free (priv->dbus_sender); - -#if WITH_POLKIT - if (priv->pk_subject) - g_object_unref (priv->pk_subject); -#endif + _clear_private (priv); G_OBJECT_CLASS (nm_auth_subject_parent_class)->finalize (object); } @@ -212,5 +443,42 @@ nm_auth_subject_class_init (NMAuthSubjectClass *config_class) g_type_class_add_private (config_class, sizeof (NMAuthSubjectPrivate)); /* virtual methods */ + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->constructed = constructed; object_class->finalize = finalize; + + g_object_class_install_property + (object_class, PROP_SUBJECT_TYPE, + g_param_spec_enum (NM_AUTH_SUBJECT_SUBJECT_TYPE, "", "", + NM_TYPE_AUTH_SUBJECT_TYPE, + NM_AUTH_SUBJECT_TYPE_INVALID, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, PROP_UNIX_PROCESS_DBUS_SENDER, + g_param_spec_string (NM_AUTH_SUBJECT_UNIX_PROCESS_DBUS_SENDER, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, PROP_UNIX_PROCESS_PID, + g_param_spec_ulong (NM_AUTH_SUBJECT_UNIX_PROCESS_PID, "", "", + 0, G_MAXULONG, G_MAXULONG, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, PROP_UNIX_PROCESS_UID, + g_param_spec_ulong (NM_AUTH_SUBJECT_UNIX_PROCESS_UID, "", "", + 0, G_MAXULONG, G_MAXULONG, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + } diff --git a/src/nm-auth-subject.h b/src/nm-auth-subject.h index 4834005e2e..4e1d83162f 100644 --- a/src/nm-auth-subject.h +++ b/src/nm-auth-subject.h @@ -27,10 +27,6 @@ #include #include -#if WITH_POLKIT -#include -#endif - #include "nm-types.h" #define NM_TYPE_AUTH_SUBJECT (nm_auth_subject_get_type ()) @@ -40,33 +36,52 @@ #define NM_IS_AUTH_SUBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_AUTH_SUBJECT)) #define NM_AUTH_SUBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_AUTH_SUBJECT, NMAuthSubjectClass)) +typedef enum { + NM_AUTH_SUBJECT_TYPE_INVALID = 0, + NM_AUTH_SUBJECT_TYPE_INTERNAL = 1, + NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS = 2, +} NMAuthSubjectType; + +#define NM_AUTH_SUBJECT_SUBJECT_TYPE "subject-type" +#define NM_AUTH_SUBJECT_UNIX_PROCESS_DBUS_SENDER "unix-process-dbus-sender" +#define NM_AUTH_SUBJECT_UNIX_PROCESS_PID "unix-process-pid" +#define NM_AUTH_SUBJECT_UNIX_PROCESS_UID "unix-process-uid" + struct _NMAuthSubject { GObject parent; }; typedef struct { GObjectClass parent; - } NMAuthSubjectClass; GType nm_auth_subject_get_type (void); -NMAuthSubject *nm_auth_subject_new_from_context (DBusGMethodInvocation *context); - -NMAuthSubject *nm_auth_subject_new_from_message (DBusConnection *connection, DBusMessage *message); - NMAuthSubject *nm_auth_subject_new_internal (void); -gulong nm_auth_subject_get_uid (NMAuthSubject *subject); +NMAuthSubject *nm_auth_subject_new_unix_process_from_context (DBusGMethodInvocation *context); -gulong nm_auth_subject_get_pid (NMAuthSubject *subject); +NMAuthSubject *nm_auth_subject_new_unix_process_from_message (DBusConnection *connection, DBusMessage *message); -const char *nm_auth_subject_get_dbus_sender (NMAuthSubject *subject); -gboolean nm_auth_subject_get_internal (NMAuthSubject *subject); +NMAuthSubjectType nm_auth_subject_get_subject_type (NMAuthSubject *subject); -#if WITH_POLKIT -PolkitSubject *nm_auth_subject_get_polkit_subject (NMAuthSubject *subject); -#endif + +gboolean nm_auth_subject_is_internal (NMAuthSubject *subject); + + +gboolean nm_auth_subject_is_unix_process (NMAuthSubject *subject); + +gulong nm_auth_subject_get_unix_process_pid (NMAuthSubject *subject); + +const char *nm_auth_subject_get_unix_process_dbus_sender (NMAuthSubject *subject); + + +gulong nm_auth_subject_get_unix_process_uid (NMAuthSubject *subject); + + +const char *nm_auth_subject_to_string (NMAuthSubject *self, char *buf, gsize buf_len); + +GVariant * nm_auth_subject_unix_process_to_polkit_gvariant (NMAuthSubject *self); #endif /* __NETWORKMANAGER_AUTH_SUBJECT_H__ */ diff --git a/src/nm-auth-utils.c b/src/nm-auth-utils.c index b2d7cb3cbd..0a652507e9 100644 --- a/src/nm-auth-utils.c +++ b/src/nm-auth-utils.c @@ -22,28 +22,21 @@ #include #include -#if WITH_POLKIT -#include -#endif - +#include "gsystem-local-alloc.h" #include "nm-setting-connection.h" #include "nm-auth-utils.h" #include "nm-logging.h" #include "nm-dbus-manager.h" #include "nm-auth-subject.h" +#include "nm-auth-manager.h" #include "nm-session-monitor.h" struct NMAuthChain { guint32 refcount; -#if WITH_POLKIT - PolkitAuthority *authority; -#endif GSList *calls; GHashTable *data; DBusGMethodInvocation *context; - char *owner; - gulong user_uid; NMAuthSubject *subject; GError *error; @@ -90,69 +83,6 @@ auth_chain_finish (gpointer user_data) return FALSE; } -#if WITH_POLKIT -static PolkitAuthority * -pk_authority_get (GError **error) -{ - static PolkitAuthority *authority = NULL; - - if (authority == NULL) - authority = polkit_authority_get_sync (NULL, error); - - /* Yes, ref every time; we want to keep the object alive */ - g_warn_if_fail (authority); - return authority ? g_object_ref (authority) : NULL; -} -#endif - -static NMAuthChain * -_auth_chain_new (NMAuthSubject *subject, - const char *dbus_sender, - gulong user_uid, - DBusGMethodInvocation *context, - NMAuthChainResultFunc done_func, - gpointer user_data) -{ - NMAuthChain *self; - - g_return_val_if_fail (subject || user_uid == 0 || dbus_sender, NULL); - - self = g_malloc0 (sizeof (NMAuthChain)); - self->refcount = 1; -#if WITH_POLKIT - self->authority = pk_authority_get (&self->error); -#endif - self->data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_data); - self->done_func = done_func; - self->user_data = user_data; - self->context = context; - - if (subject) { - self->user_uid = nm_auth_subject_get_uid (subject); - self->subject = g_object_ref (subject); - } else { - self->user_uid = user_uid; - self->owner = g_strdup (dbus_sender); - if (user_uid > 0 && !self->owner) { - /* Need an owner */ - g_warn_if_fail (self->owner); - nm_auth_chain_unref (self); - self = NULL; - } - } - - return self; -} - -NMAuthChain * -nm_auth_chain_new_dbus_sender (const char *dbus_sender, - gulong user_uid, - NMAuthChainResultFunc done_func, - gpointer user_data) -{ - return _auth_chain_new (NULL, dbus_sender, user_uid, NULL, done_func, user_data); -} - /* Creates the NMAuthSubject automatically */ NMAuthChain * nm_auth_chain_new_context (DBusGMethodInvocation *context, @@ -164,7 +94,7 @@ nm_auth_chain_new_context (DBusGMethodInvocation *context, g_return_val_if_fail (context != NULL, NULL); - subject = nm_auth_subject_new_from_context (context); + subject = nm_auth_subject_new_unix_process_from_context (context); if (!subject) return NULL; @@ -183,16 +113,20 @@ nm_auth_chain_new_subject (NMAuthSubject *subject, NMAuthChainResultFunc done_func, gpointer user_data) { - NMAuthChain *chain; + NMAuthChain *self; g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); - chain = _auth_chain_new (subject, NULL, G_MAXULONG, context, done_func, user_data); + g_return_val_if_fail (nm_auth_subject_is_unix_process (subject) || nm_auth_subject_is_internal (subject), NULL); - /* Chains creation from a valid NMAuthSubject cannot fail since the - * subject already has all the necessary auth info. - */ - g_assert (chain); - return chain; + self = g_malloc0 (sizeof (NMAuthChain)); + self->refcount = 1; + self->data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_data); + self->done_func = done_func; + self->user_data = user_data; + self->context = context; + self->subject = g_object_ref (subject); + + return self; } gpointer @@ -364,128 +298,86 @@ auth_call_cancel (gpointer user_data) } } -#if WITH_POLKIT static void pk_call_cb (GObject *object, GAsyncResult *result, gpointer user_data) { AuthCall *call = user_data; - PolkitAuthorizationResult *pk_result; GError *error = NULL; + gboolean is_authorized, is_challenge; - pk_result = polkit_authority_check_authorization_finish ((PolkitAuthority *) object, result, &error); + 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); - g_clear_object (&pk_result); auth_call_free (call); return; } if (error) { - if (!call->chain->error) - call->chain->error = g_error_copy (error); - nm_log_warn (LOGD_CORE, "error requesting auth for %s: (%d) %s", call->permission, error->code, error->message); - g_clear_error (&error); + + if (!call->chain->error) { + call->chain->error = error; + error = NULL; + } else + g_clear_error (&error); } else { guint call_result = NM_AUTH_CALL_RESULT_UNKNOWN; - if (polkit_authorization_result_get_is_authorized (pk_result)) { + if (is_authorized) { /* Caller has the permission */ call_result = NM_AUTH_CALL_RESULT_YES; - } else if (polkit_authorization_result_get_is_challenge (pk_result)) { + } 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; nm_auth_chain_set_data (call->chain, call->permission, GUINT_TO_POINTER (call_result), NULL); - g_object_unref (pk_result); } auth_call_complete (call); } -static void -auth_call_schedule_complete_with_error (AuthCall *call, const char *msg) -{ - if (!call->chain->error) - call->chain->error = g_error_new_literal (DBUS_GERROR, DBUS_GERROR_FAILED, msg); - call->call_idle_id = g_idle_add ((GSourceFunc) auth_call_complete, call); -} - -static gboolean -_add_call_polkit (NMAuthChain *self, - const char *permission, - gboolean allow_interaction) -{ - PolkitSubject *subject; - PolkitCheckAuthorizationFlags flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE; - AuthCall *call; - - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (self->owner || self->subject, FALSE); - g_return_val_if_fail (permission != NULL, FALSE); - - call = auth_call_new (self, permission); - - if (self->authority == NULL) { - /* No polkit, no authorization */ - auth_call_schedule_complete_with_error (call, "PolicyKit not running"); - return FALSE; - } - - if (self->subject) { - subject = g_object_ref (nm_auth_subject_get_polkit_subject (self->subject)); - g_assert (subject); - } else { - g_assert (self->owner); - subject = polkit_system_bus_name_new (self->owner); - if (!subject) { - auth_call_schedule_complete_with_error (call, "Failed to create polkit subject"); - return FALSE; - } - } - - if (allow_interaction) - flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION; - - call->cancellable = g_cancellable_new (); - polkit_authority_check_authorization (self->authority, - subject, - permission, - NULL, - flags, - call->cancellable, - pk_call_cb, - call); - g_object_unref (subject); - return TRUE; -} -#endif - -gboolean +void nm_auth_chain_add_call (NMAuthChain *self, const char *permission, gboolean allow_interaction) { AuthCall *call; + NMAuthManager *auth_manager = nm_auth_manager_get (); - g_return_val_if_fail (self != NULL, FALSE); + g_return_if_fail (self != NULL); + g_return_if_fail (permission && *permission); + g_return_if_fail (self->subject); + g_return_if_fail (nm_auth_subject_is_unix_process (self->subject) || nm_auth_subject_is_internal (self->subject)); -#if WITH_POLKIT - /* Non-root always gets authenticated when using polkit */ - if (self->user_uid > 0) - return _add_call_polkit (self, permission, allow_interaction); -#endif - - /* Root user or non-polkit always gets the permission */ call = auth_call_new (self, 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); - return TRUE; + + 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 */ + 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); + } } void @@ -500,11 +392,6 @@ nm_auth_chain_unref (NMAuthChain *self) if (self->idle_id) g_source_remove (self->idle_id); -#if WITH_POLKIT - if (self->authority) - g_object_unref (self->authority); -#endif - g_free (self->owner); g_object_unref (self->subject); g_slist_free_full (self->calls, auth_call_cancel); @@ -519,17 +406,25 @@ nm_auth_chain_unref (NMAuthChain *self) /************ utils **************/ gboolean -nm_auth_uid_in_acl (NMConnection *connection, - NMSessionMonitor *smon, - gulong uid, - char **out_error_desc) +nm_auth_is_subject_in_acl (NMConnection *connection, + NMSessionMonitor *smon, + NMAuthSubject *subject, + char **out_error_desc) { NMSettingConnection *s_con; const char *user = NULL; GError *local = NULL; + gulong uid; g_return_val_if_fail (connection != NULL, FALSE); g_return_val_if_fail (smon != NULL, 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); + + if (nm_auth_subject_is_internal (subject)) + return TRUE; + + uid = nm_auth_subject_get_unix_process_uid (subject); /* Root gets a free pass */ if (0 == uid) @@ -570,87 +465,4 @@ nm_auth_uid_in_acl (NMConnection *connection, return TRUE; } -typedef struct { - GDestroyNotify changed_callback; - gpointer changed_data; -} PkChangedInfo; - -static GSList *funcs = NULL; - -#if WITH_POLKIT -static void -pk_authority_changed_cb (GObject *object, gpointer unused) -{ - GSList *iter; - - for (iter = funcs; iter; iter = g_slist_next (iter)) { - PkChangedInfo *info = iter->data; - - info->changed_callback (info->changed_data); - } -} -#endif - -void -nm_auth_changed_func_register (GDestroyNotify callback, gpointer callback_data) -{ -#if WITH_POLKIT - PolkitAuthority *authority; - static guint32 changed_id = 0; -#endif - PkChangedInfo *info; - GSList *iter; - gboolean found = FALSE; - -#if WITH_POLKIT - authority = pk_authority_get (NULL); - if (!authority) - return; - - /* Hook up the changed signal the first time a callback is registered */ - if (changed_id == 0) { - changed_id = g_signal_connect (authority, - "changed", - G_CALLBACK (pk_authority_changed_cb), - &funcs); - } -#endif - - /* No duplicates */ - for (iter = funcs; iter; iter = g_slist_next (iter)) { - info = iter->data; - if ((callback == info->changed_callback) && (callback_data == info->changed_data)) { - found = TRUE; - break; - } - } - - g_warn_if_fail (found == FALSE); - if (found == FALSE) { - info = g_malloc0 (sizeof (*info)); - info->changed_callback = callback; - info->changed_data = callback_data; - funcs = g_slist_append (funcs, info); - } - -#if WITH_POLKIT - g_object_unref (authority); -#endif -} - -void -nm_auth_changed_func_unregister (GDestroyNotify callback, gpointer callback_data) -{ - GSList *iter; - - for (iter = funcs; iter; iter = g_slist_next (iter)) { - PkChangedInfo *info = iter->data; - - if ((callback == info->changed_callback) && (callback_data == info->changed_data)) { - g_free (info); - funcs = g_slist_delete_link (funcs, iter); - break; - } - } -} diff --git a/src/nm-auth-utils.h b/src/nm-auth-utils.h index 25f3cd591e..2ab026d012 100644 --- a/src/nm-auth-utils.h +++ b/src/nm-auth-utils.h @@ -54,11 +54,6 @@ typedef void (*NMAuthChainResultFunc) (NMAuthChain *chain, DBusGMethodInvocation *context, gpointer user_data); -NMAuthChain *nm_auth_chain_new_dbus_sender (const char *dbus_sender, - gulong user_uid, - NMAuthChainResultFunc done_func, - gpointer user_data); - NMAuthChain *nm_auth_chain_new_context (DBusGMethodInvocation *context, NMAuthChainResultFunc done_func, gpointer user_data); @@ -86,21 +81,17 @@ gulong nm_auth_chain_get_data_ulong (NMAuthChain *chain, const char *tag); NMAuthCallResult nm_auth_chain_get_result (NMAuthChain *chain, const char *permission); -gboolean nm_auth_chain_add_call (NMAuthChain *chain, - const char *permission, - gboolean allow_interaction); +void nm_auth_chain_add_call (NMAuthChain *chain, + const char *permission, + gboolean allow_interaction); void nm_auth_chain_unref (NMAuthChain *chain); /* Caller must free returned error description */ -gboolean nm_auth_uid_in_acl (NMConnection *connection, - NMSessionMonitor *smon, - gulong uid, - char **out_error_desc); - -void nm_auth_changed_func_register (GDestroyNotify callback, gpointer callback_data); - -void nm_auth_changed_func_unregister (GDestroyNotify callback, gpointer callback_data); +gboolean nm_auth_is_subject_in_acl (NMConnection *connection, + NMSessionMonitor *smon, + NMAuthSubject *subect, + char **out_error_desc); #endif /* __NETWORKMANAGER_MANAGER_AUTH_H__ */ diff --git a/src/nm-config.c b/src/nm-config.c index 3978b56626..2580541a62 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -46,6 +46,7 @@ typedef struct { char **plugins; gboolean monitor_connection_files; + gboolean auth_polkit; char *dhcp_client; char *dns_mode; @@ -142,6 +143,14 @@ nm_config_get_monitor_connection_files (NMConfig *config) return NM_CONFIG_GET_PRIVATE (config)->monitor_connection_files; } +gboolean +nm_config_get_auth_polkit (NMConfig *config) +{ + g_return_val_if_fail (NM_IS_CONFIG (config), NM_CONFIG_DEFAULT_AUTH_POLKIT); + + return NM_CONFIG_GET_PRIVATE (config)->auth_polkit; +} + const char * nm_config_get_dhcp_client (NMConfig *config) { @@ -580,6 +589,16 @@ nm_config_new (GError **error) g_free (value); } + value = g_key_file_get_value (priv->keyfile, "main", "auth-polkit", NULL); + priv->auth_polkit = NM_CONFIG_DEFAULT_AUTH_POLKIT; + if (value) { + if (!_parse_bool_str (value, &priv->auth_polkit)) { + nm_log_warn (LOGD_CORE, "Unrecognized value for main.auth-polkit: %s. Assuming '%s'", value, + NM_CONFIG_DEFAULT_AUTH_POLKIT ? "true" : "false"); + } + g_free (value); + } + priv->dhcp_client = g_key_file_get_value (priv->keyfile, "main", "dhcp", NULL); priv->dns_mode = g_key_file_get_value (priv->keyfile, "main", "dns", NULL); @@ -610,6 +629,8 @@ nm_config_init (NMConfig *config) { NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); + priv->auth_polkit = NM_CONFIG_DEFAULT_AUTH_POLKIT; + priv->keyfile = g_key_file_new (); g_key_file_set_list_separator (priv->keyfile, ','); diff --git a/src/nm-config.h b/src/nm-config.h index 8f4fa1f7af..27da4cbbc6 100644 --- a/src/nm-config.h +++ b/src/nm-config.h @@ -52,6 +52,7 @@ const char *nm_config_get_path (NMConfig *config); const char *nm_config_get_description (NMConfig *config); const char **nm_config_get_plugins (NMConfig *config); gboolean nm_config_get_monitor_connection_files (NMConfig *config); +gboolean nm_config_get_auth_polkit (NMConfig *config); const char *nm_config_get_dhcp_client (NMConfig *config); const char *nm_config_get_dns_mode (NMConfig *config); const char *nm_config_get_log_level (NMConfig *config); diff --git a/src/nm-manager.c b/src/nm-manager.c index 8f5d41f813..db46bf5be8 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -50,6 +50,7 @@ #include "nm-settings.h" #include "nm-settings-connection.h" #include "nm-auth-utils.h" +#include "nm-auth-manager.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-device-factory.h" @@ -1452,7 +1453,7 @@ device_auth_request_cb (NMDevice *device, NMAuthChain *chain; /* Validate the caller */ - subject = nm_auth_subject_new_from_context (context); + subject = nm_auth_subject_new_unix_process_from_context (context); if (!subject) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, @@ -1461,10 +1462,10 @@ device_auth_request_cb (NMDevice *device, } /* Ensure the subject has permissions for this connection */ - if (connection && !nm_auth_uid_in_acl (connection, - nm_session_monitor_get (), - nm_auth_subject_get_uid (subject), - &error_desc)) { + if (connection && !nm_auth_is_subject_in_acl (connection, + nm_session_monitor_get (), + subject, + &error_desc)) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, error_desc); @@ -2662,10 +2663,10 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * existing_connection = nm_device_get_connection (device); subject = nm_active_connection_get_subject (active); if (existing_connection && - !nm_auth_uid_in_acl (existing_connection, - nm_session_monitor_get (), - nm_auth_subject_get_uid (subject), - &error_desc)) { + !nm_auth_is_subject_in_acl (existing_connection, + nm_session_monitor_get (), + subject, + &error_desc)) { g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, @@ -2954,10 +2955,10 @@ nm_manager_activate_connection (NMManager *self, g_return_val_if_fail (*error == NULL, NULL); /* Ensure the subject has permissions for this connection */ - if (!nm_auth_uid_in_acl (connection, - nm_session_monitor_get (), - nm_auth_subject_get_uid (subject), - &error_desc)) { + if (!nm_auth_is_subject_in_acl (connection, + nm_session_monitor_get (), + subject, + &error_desc)) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, @@ -2998,7 +2999,7 @@ validate_activation_request (NMManager *self, g_assert (out_vpn); /* Validate the caller */ - subject = nm_auth_subject_new_from_context (context); + subject = nm_auth_subject_new_unix_process_from_context (context); if (!subject) { g_set_error_literal (error, NM_MANAGER_ERROR, @@ -3008,10 +3009,10 @@ validate_activation_request (NMManager *self, } /* Ensure the subject has permissions for this connection */ - if (!nm_auth_uid_in_acl (connection, - nm_session_monitor_get (), - nm_auth_subject_get_uid (subject), - &error_desc)) { + if (!nm_auth_is_subject_in_acl (connection, + nm_session_monitor_get (), + subject, + &error_desc)) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, @@ -3528,7 +3529,7 @@ impl_manager_deactivate_connection (NMManager *self, } /* Validate the caller */ - subject = nm_auth_subject_new_from_context (context); + subject = nm_auth_subject_new_unix_process_from_context (context); if (!subject) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, @@ -3537,10 +3538,10 @@ impl_manager_deactivate_connection (NMManager *self, } /* Ensure the subject has permissions for this connection */ - if (!nm_auth_uid_in_acl (connection, - nm_session_monitor_get (), - nm_auth_subject_get_uid (subject), - &error_desc)) { + if (!nm_auth_is_subject_in_acl (connection, + nm_session_monitor_get (), + subject, + &error_desc)) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, error_desc); @@ -4434,7 +4435,7 @@ prop_filter (DBusConnection *connection, goto out; } - subject = nm_auth_subject_new_from_message (connection, message); + subject = nm_auth_subject_new_unix_process_from_message (connection, message); if (!subject) { reply = dbus_message_new_error (message, NM_PERM_DENIED_ERROR, "Could not determine request UID."); @@ -4469,7 +4470,7 @@ out: } static void -authority_changed_cb (gpointer user_data) +authority_changed_cb (NMAuthManager *auth_manager, gpointer user_data) { /* Let clients know they should re-check their authorization */ g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0); @@ -4834,7 +4835,11 @@ nm_manager_init (NMManager *manager) G_CALLBACK (resuming_cb), manager); /* Listen for authorization changes */ - nm_auth_changed_func_register (authority_changed_cb, manager); + g_signal_connect (nm_auth_manager_get (), + NM_AUTH_MANAGER_SIGNAL_CHANGED, + G_CALLBACK (authority_changed_cb), + manager); + /* Monitor the firmware directory */ if (strlen (KERNEL_FIRMWARE_DIR)) { @@ -4986,7 +4991,9 @@ dispose (GObject *object) g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_unref); priv->auth_chains = NULL; - nm_auth_changed_func_unregister (authority_changed_cb, manager); + g_signal_handlers_disconnect_by_func (nm_auth_manager_get (), + G_CALLBACK (authority_changed_cb), + manager); /* Remove all devices */ while (priv->devices) diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c index 26692c339f..0de63ffb16 100644 --- a/src/settings/nm-agent-manager.c +++ b/src/settings/nm-agent-manager.c @@ -36,7 +36,7 @@ #include "nm-setting-vpn.h" #include "nm-setting-connection.h" #include "nm-enum-types.h" -#include "nm-auth-subject.h" +#include "nm-auth-manager.h" #include "nm-dbus-manager.h" #include "nm-session-monitor.h" #include "nm-simple-connection.h" @@ -290,14 +290,14 @@ impl_agent_manager_register_with_capabilities (NMAgentManager *self, NMSecretAgent *agent; NMAuthChain *chain; - subject = nm_auth_subject_new_from_context (context); + subject = nm_auth_subject_new_unix_process_from_context (context); if (!subject) { error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, NM_AGENT_MANAGER_ERROR_SENDER_UNKNOWN, "Unable to determine request sender and UID."); goto done; } - sender_uid = nm_auth_subject_get_uid (subject); + sender_uid = nm_auth_subject_get_unix_process_uid (subject); if ( 0 != sender_uid && !nm_session_monitor_uid_has_session (nm_session_monitor_get (), @@ -529,8 +529,8 @@ agent_compare_func (gconstpointer aa, gconstpointer bb, gpointer user_data) gulong a_pid, b_pid, requester; /* Prefer agents in the process the request came from */ - requester = nm_auth_subject_get_pid (req->subject); - if (requester != G_MAXULONG) { + if (nm_auth_subject_is_unix_process (req->subject)) { + requester = nm_auth_subject_get_unix_process_pid (req->subject); a_pid = nm_secret_agent_get_pid (a); b_pid = nm_secret_agent_get_pid (b); @@ -572,11 +572,11 @@ request_add_agent (Request *req, NMSecretAgent *agent) return; /* If the request should filter agents by UID, do that now */ - if (!nm_auth_subject_get_internal (req->subject)) { + if (nm_auth_subject_is_unix_process (req->subject)) { uid_t agent_uid, subject_uid; agent_uid = nm_secret_agent_get_owner_uid (agent); - subject_uid = nm_auth_subject_get_uid (req->subject); + subject_uid = nm_auth_subject_get_unix_process_uid (req->subject); if (agent_uid != subject_uid) { nm_log_dbg (LOGD_AGENTS, "(%s) agent ignored for secrets request %p/%s " "(uid %ld not required %ld)", @@ -713,12 +713,12 @@ static gboolean connection_request_add_agent (Request *parent, NMSecretAgent *agent) { ConnectionRequest *req = (ConnectionRequest *) parent; - uid_t agent_uid = nm_secret_agent_get_owner_uid (agent); + NMAuthSubject *subject = nm_secret_agent_get_subject(agent); /* Ensure the caller's username exists in the connection's permissions, * or that the permissions is empty (ie, visible by everyone). */ - if (!nm_auth_uid_in_acl (req->connection, nm_session_monitor_get (), agent_uid, NULL)) { + if (!nm_auth_is_subject_in_acl (req->connection, nm_session_monitor_get (), subject, NULL)) { nm_log_dbg (LOGD_AGENTS, "(%s) agent ignored for secrets request %p/%s (not in ACL)", nm_secret_agent_get_description (agent), parent, parent->detail); @@ -1454,11 +1454,13 @@ nm_agent_manager_all_agents_have_capability (NMAgentManager *manager, NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (manager); GHashTableIter iter; NMSecretAgent *agent; + gboolean subject_is_unix_process = nm_auth_subject_is_unix_process (subject); + gulong subject_uid = subject_is_unix_process ? nm_auth_subject_get_unix_process_uid (subject) : 0; g_hash_table_iter_init (&iter, priv->agents); while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) { - if ( !nm_auth_subject_get_internal (subject) - && nm_secret_agent_get_owner_uid (agent) != nm_auth_subject_get_uid (subject)) + if ( subject_is_unix_process + && nm_secret_agent_get_owner_uid (agent) != subject_uid) continue; if (!(nm_secret_agent_get_capabilities (agent) & capability)) @@ -1519,9 +1521,8 @@ agent_permissions_changed_done (NMAuthChain *chain, } static void -authority_changed_cb (gpointer user_data) +authority_changed_cb (NMAuthManager *auth_manager, NMAgentManager *self) { - NMAgentManager *self = NM_AGENT_MANAGER (user_data); NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); GHashTableIter iter; NMSecretAgent *agent; @@ -1572,7 +1573,10 @@ nm_agent_manager_get (void) G_CALLBACK (name_owner_changed_cb), singleton); - nm_auth_changed_func_register (authority_changed_cb, singleton); + g_signal_connect (nm_auth_manager_get (), + NM_AUTH_MANAGER_SIGNAL_CHANGED, + G_CALLBACK (authority_changed_cb), + singleton); return singleton; } @@ -1597,7 +1601,9 @@ dispose (GObject *object) if (!priv->disposed) { priv->disposed = TRUE; - nm_auth_changed_func_unregister (authority_changed_cb, NM_AGENT_MANAGER (object)); + g_signal_handlers_disconnect_by_func (nm_auth_manager_get (), + G_CALLBACK (authority_changed_cb), + object); g_slist_free_full (priv->chains, (GDestroyNotify) nm_auth_chain_unref); diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c index a1c6954ee2..3099f4518b 100644 --- a/src/settings/nm-secret-agent.c +++ b/src/settings/nm-secret-agent.c @@ -108,9 +108,9 @@ nm_secret_agent_get_description (NMSecretAgent *agent) priv = NM_SECRET_AGENT_GET_PRIVATE (agent); if (!priv->description) { priv->description = g_strdup_printf ("%s/%s/%lu", - nm_auth_subject_get_dbus_sender (priv->subject), + nm_auth_subject_get_unix_process_dbus_sender (priv->subject), priv->identifier, - nm_auth_subject_get_uid (priv->subject)); + nm_auth_subject_get_unix_process_uid (priv->subject)); } return priv->description; @@ -121,7 +121,7 @@ nm_secret_agent_get_dbus_owner (NMSecretAgent *agent) { g_return_val_if_fail (NM_IS_SECRET_AGENT (agent), NULL); - return nm_auth_subject_get_dbus_sender (NM_SECRET_AGENT_GET_PRIVATE (agent)->subject); + return nm_auth_subject_get_unix_process_dbus_sender (NM_SECRET_AGENT_GET_PRIVATE (agent)->subject); } const char * @@ -137,7 +137,7 @@ nm_secret_agent_get_owner_uid (NMSecretAgent *agent) { g_return_val_if_fail (NM_IS_SECRET_AGENT (agent), G_MAXULONG); - return nm_auth_subject_get_uid (NM_SECRET_AGENT_GET_PRIVATE (agent)->subject); + return nm_auth_subject_get_unix_process_uid (NM_SECRET_AGENT_GET_PRIVATE (agent)->subject); } const char * @@ -153,7 +153,7 @@ nm_secret_agent_get_pid (NMSecretAgent *agent) { g_return_val_if_fail (NM_IS_SECRET_AGENT (agent), G_MAXULONG); - return nm_auth_subject_get_pid (NM_SECRET_AGENT_GET_PRIVATE (agent)->subject); + return nm_auth_subject_get_unix_process_pid (NM_SECRET_AGENT_GET_PRIVATE (agent)->subject); } NMSecretAgentCapabilities @@ -477,9 +477,10 @@ nm_secret_agent_new (DBusGMethodInvocation *context, g_return_val_if_fail (context != NULL, NULL); g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); + g_return_val_if_fail (nm_auth_subject_is_unix_process (subject), NULL); g_return_val_if_fail (identifier != NULL, NULL); - pw = getpwuid (nm_auth_subject_get_uid (subject)); + pw = getpwuid (nm_auth_subject_get_unix_process_uid (subject)); g_return_val_if_fail (pw != NULL, NULL); g_return_val_if_fail (pw->pw_name[0] != '\0', NULL); username = g_strdup (pw->pw_name); @@ -492,13 +493,13 @@ nm_secret_agent_new (DBusGMethodInvocation *context, priv->capabilities = capabilities; priv->subject = g_object_ref (subject); - hash_str = g_strdup_printf ("%16lu%s", nm_auth_subject_get_uid (subject), identifier); + hash_str = g_strdup_printf ("%16lu%s", nm_auth_subject_get_unix_process_uid (subject), identifier); priv->hash = g_str_hash (hash_str); g_free (hash_str); priv->proxy = nm_dbus_manager_new_proxy (nm_dbus_manager_get (), context, - nm_auth_subject_get_dbus_sender (subject), + nm_auth_subject_get_unix_process_dbus_sender (subject), NM_DBUS_PATH_SECRET_AGENT, NM_DBUS_INTERFACE_SECRET_AGENT); g_assert (priv->proxy); diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 9cf90a5bc9..1e0106d07b 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -1029,7 +1029,7 @@ _new_auth_subject (DBusGMethodInvocation *context, GError **error) { NMAuthSubject *subject; - subject = nm_auth_subject_new_from_context (context); + subject = nm_auth_subject_new_unix_process_from_context (context); if (!subject) { g_set_error_literal (error, NM_SETTINGS_ERROR, @@ -1057,10 +1057,10 @@ auth_start (NMSettingsConnection *self, g_return_if_fail (NM_IS_AUTH_SUBJECT (subject)); /* Ensure the caller can view this connection */ - if (!nm_auth_uid_in_acl (NM_CONNECTION (self), - priv->session_monitor, - nm_auth_subject_get_uid (subject), - &error_desc)) { + if (!nm_auth_is_subject_in_acl (NM_CONNECTION (self), + priv->session_monitor, + subject, + &error_desc)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, error_desc); @@ -1414,10 +1414,10 @@ impl_settings_connection_update_helper (NMSettingsConnection *self, * that's sending the update request. You can't make a connection * invisible to yourself. */ - if (!nm_auth_uid_in_acl (tmp ? tmp : NM_CONNECTION (self), - priv->session_monitor, - nm_auth_subject_get_uid (subject), - &error_desc)) { + if (!nm_auth_is_subject_in_acl (tmp ? tmp : NM_CONNECTION (self), + priv->session_monitor, + subject, + &error_desc)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, error_desc); diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 7876a69bf6..65bc241e48 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -288,7 +288,7 @@ impl_settings_get_connection_by_uuid (NMSettings *self, goto error; } - subject = nm_auth_subject_new_from_context (context); + subject = nm_auth_subject_new_unix_process_from_context (context); if (!subject) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, @@ -296,10 +296,10 @@ impl_settings_get_connection_by_uuid (NMSettings *self, goto error; } - if (!nm_auth_uid_in_acl (NM_CONNECTION (connection), - nm_session_monitor_get (), - nm_auth_subject_get_uid (subject), - &error_desc)) { + if (!nm_auth_is_subject_in_acl (NM_CONNECTION (connection), + nm_session_monitor_get (), + subject, + &error_desc)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, error_desc); @@ -1142,7 +1142,7 @@ nm_settings_add_connection_dbus (NMSettings *self, goto done; } - subject = nm_auth_subject_new_from_context (context); + subject = nm_auth_subject_new_unix_process_from_context (context); if (!subject) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, @@ -1153,10 +1153,10 @@ nm_settings_add_connection_dbus (NMSettings *self, /* Ensure the caller's username exists in the connection's permissions, * or that the permissions is empty (ie, visible by everyone). */ - if (!nm_auth_uid_in_acl (connection, - nm_session_monitor_get (), - nm_auth_subject_get_uid (subject), - &error_desc)) { + if (!nm_auth_is_subject_in_acl (connection, + nm_session_monitor_get (), + subject, + &error_desc)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, error_desc);