summary refs log tree commit diff
path: root/src/core/nm-auth-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/nm-auth-manager.c')
-rw-r--r--src/core/nm-auth-manager.c751
1 files changed, 751 insertions, 0 deletions
diff --git a/src/core/nm-auth-manager.c b/src/core/nm-auth-manager.c
new file mode 100644
index 00000000..9470838c
--- /dev/null
+++ b/src/core/nm-auth-manager.c
@@ -0,0 +1,751 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#include "src/core/nm-default-daemon.h"
+
+#include "nm-auth-manager.h"
+
+#include "c-list/src/c-list.h"
+#include "nm-glib-aux/nm-dbus-aux.h"
+#include "nm-errors.h"
+#include "nm-core-internal.h"
+#include "nm-dbus-manager.h"
+#include "NetworkManagerUtils.h"
+
+#define POLKIT_SERVICE     "org.freedesktop.PolicyKit1"
+#define POLKIT_OBJECT_PATH "/org/freedesktop/PolicyKit1/Authority"
+#define POLKIT_INTERFACE   "org.freedesktop.PolicyKit1.Authority"
+
+#define CANCELLATION_ID_PREFIX  "cancellation-id-"
+#define CANCELLATION_TIMEOUT_MS 5000
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_POLKIT_ENABLED, );
+
+enum {
+    CHANGED_SIGNAL,
+    LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+typedef struct {
+    CList            calls_lst_head;
+    GDBusConnection *dbus_connection;
+    GCancellable *   main_cancellable;
+    char *           name_owner;
+    guint64          call_numid_counter;
+    guint            changed_id;
+    guint            name_owner_changed_id;
+    bool             disposing : 1;
+    bool             shutting_down : 1;
+    bool             got_name_owner : 1;
+    NMAuthPolkitMode auth_polkit_mode : 3;
+} NMAuthManagerPrivate;
+
+struct _NMAuthManager {
+    GObject              parent;
+    NMAuthManagerPrivate _priv;
+};
+
+struct _NMAuthManagerClass {
+    GObjectClass parent;
+};
+
+G_DEFINE_TYPE(NMAuthManager, nm_auth_manager, G_TYPE_OBJECT)
+
+#define NM_AUTH_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMAuthManager, NM_IS_AUTH_MANAGER)
+
+NM_DEFINE_SINGLETON_REGISTER(NMAuthManager);
+
+/*****************************************************************************/
+
+#define _NMLOG_PREFIX_NAME "auth"
+#define _NMLOG_DOMAIN      LOGD_CORE
+#define _NMLOG(level, ...)                                       \
+    G_STMT_START                                                 \
+    {                                                            \
+        if (nm_logging_enabled((level), (_NMLOG_DOMAIN))) {      \
+            char __prefix[30] = _NMLOG_PREFIX_NAME;              \
+                                                                 \
+            if ((self) != singleton_instance)                    \
+                g_snprintf(__prefix,                             \
+                           sizeof(__prefix),                     \
+                           ""_NMLOG_PREFIX_NAME                  \
+                           "[%p]",                               \
+                           (self));                              \
+            _nm_log((level),                                     \
+                    (_NMLOG_DOMAIN),                             \
+                    0,                                           \
+                    NULL,                                        \
+                    NULL,                                        \
+                    "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__),   \
+                    __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+        }                                                        \
+    }                                                            \
+    G_STMT_END
+
+#define _NMLOG2(level, call_id, ...)                                                        \
+    G_STMT_START                                                                            \
+    {                                                                                       \
+        if (nm_logging_enabled((level), (_NMLOG_DOMAIN))) {                                 \
+            NMAuthManagerCallId *_call_id     = (call_id);                                  \
+            char                 __prefix[30] = _NMLOG_PREFIX_NAME;                         \
+                                                                                            \
+            if (_call_id->self != singleton_instance)                                       \
+                g_snprintf(__prefix,                                                        \
+                           sizeof(__prefix),                                                \
+                           ""_NMLOG_PREFIX_NAME                                             \
+                           "[%p]",                                                          \
+                           _call_id->self);                                                 \
+            _nm_log((level),                                                                \
+                    (_NMLOG_DOMAIN),                                                        \
+                    0,                                                                      \
+                    NULL,                                                                   \
+                    NULL,                                                                   \
+                    "%s: call[%" G_GUINT64_FORMAT "]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                    __prefix,                                                               \
+                    _call_id->call_numid _NM_UTILS_MACRO_REST(__VA_ARGS__));                \
+        }                                                                                   \
+    }                                                                                       \
+    G_STMT_END
+
+/*****************************************************************************/
+
+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)->dbus_connection != NULL;
+}
+
+/*****************************************************************************/
+
+static void
+_emit_changed_signal(NMAuthManager *self)
+{
+    g_signal_emit(self, signals[CHANGED_SIGNAL], 0);
+}
+
+typedef enum {
+    POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE                   = 0,
+    POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION = (1 << 0),
+} PolkitCheckAuthorizationFlags;
+
+struct _NMAuthManagerCallId {
+    CList                                   calls_lst;
+    NMAuthManager *                         self;
+    GCancellable *                          dbus_cancellable;
+    NMAuthManagerCheckAuthorizationCallback callback;
+    gpointer                                user_data;
+    guint64                                 call_numid;
+    guint                                   idle_id;
+    bool                                    idle_is_authorized : 1;
+};
+
+#define cancellation_id_to_str_a(call_numid)                     \
+    nm_sprintf_bufa(NM_STRLEN(CANCELLATION_ID_PREFIX) + 60,      \
+                    CANCELLATION_ID_PREFIX "%" G_GUINT64_FORMAT, \
+                    (call_numid))
+
+static void
+_call_id_free(NMAuthManagerCallId *call_id)
+{
+    c_list_unlink(&call_id->calls_lst);
+    nm_clear_g_source(&call_id->idle_id);
+
+    if (call_id->dbus_cancellable) {
+        /* we have a pending D-Bus call. We keep the call-id instance alive
+         * for _call_check_authorize_cb() */
+        g_cancellable_cancel(call_id->dbus_cancellable);
+        return;
+    }
+
+    g_object_unref(call_id->self);
+    g_slice_free(NMAuthManagerCallId, call_id);
+}
+
+static void
+_call_id_invoke_callback(NMAuthManagerCallId *call_id,
+                         gboolean             is_authorized,
+                         gboolean             is_challenge,
+                         GError *             error)
+{
+    c_list_unlink(&call_id->calls_lst);
+
+    call_id
+        ->callback(call_id->self, call_id, is_authorized, is_challenge, error, call_id->user_data);
+    _call_id_free(call_id);
+}
+
+static void
+cancel_check_authorization_cb(GObject *source, GAsyncResult *res, gpointer user_data)
+{
+    NMAuthManagerCallId *call_id     = user_data;
+    gs_unref_variant GVariant *value = NULL;
+    gs_free_error GError *error      = NULL;
+
+    value = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error);
+    if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        _LOG2T(call_id, "cancel request was cancelled");
+    else if (error)
+        _LOG2T(call_id, "cancel request failed: %s", error->message);
+    else
+        _LOG2T(call_id, "cancel request succeeded");
+
+    _call_id_free(call_id);
+}
+
+static void
+_call_check_authorize_cb(GObject *proxy, GAsyncResult *res, gpointer user_data)
+{
+    NMAuthManagerCallId * call_id = user_data;
+    NMAuthManager *       self;
+    NMAuthManagerPrivate *priv;
+    gs_unref_variant GVariant *value    = NULL;
+    gs_free_error GError *error         = NULL;
+    gboolean              is_authorized = FALSE;
+    gboolean              is_challenge  = FALSE;
+
+    /* we need to clear the cancelable, to signal for _call_id_free() that we
+     * are not in a pending call.
+     *
+     * Note how _call_id_free() kept call-id alive, even if the request was
+     * already cancelled. */
+    g_clear_object(&call_id->dbus_cancellable);
+
+    self = call_id->self;
+    priv = NM_AUTH_MANAGER_GET_PRIVATE(self);
+
+    value = g_dbus_connection_call_finish(G_DBUS_CONNECTION(proxy), res, &error);
+
+    if (nm_utils_error_is_cancelled(error)) {
+        /* call_id was cancelled externally, but _call_id_free() kept call_id
+         * alive (and it has still the reference on @self. */
+
+        if (!priv->main_cancellable) {
+            /* we do a forced shutdown. There is no more time for cancelling... */
+            _call_id_free(call_id);
+
+            /* this shouldn't really happen, because:
+             * nm_auth_manager_check_authorization() only scheduled the D-Bus request at a time when
+             * main_cancellable was still set. It means, somebody called force-shutdown
+             * after call-id was schedule.
+             * force-shutdown should only be called after:
+             *   - cancel all pending requests
+             *   - give enough time to cancel the request and schedule a D-Bus call
+             *     to CancelCheckAuthorization (below), before issuing force-shutdown. */
+            g_return_if_reached();
+        }
+
+        g_dbus_connection_call(priv->dbus_connection,
+                               POLKIT_SERVICE,
+                               POLKIT_OBJECT_PATH,
+                               POLKIT_INTERFACE,
+                               "CancelCheckAuthorization",
+                               g_variant_new("(s)", cancellation_id_to_str_a(call_id->call_numid)),
+                               G_VARIANT_TYPE("()"),
+                               G_DBUS_CALL_FLAGS_NONE,
+                               CANCELLATION_TIMEOUT_MS,
+                               priv->main_cancellable,
+                               cancel_check_authorization_cb,
+                               call_id);
+        return;
+    }
+
+    if (!error) {
+        g_variant_get(value, "((bb@a{ss}))", &is_authorized, &is_challenge, NULL);
+        _LOG2T(call_id, "completed: authorized=%d, challenge=%d", is_authorized, is_challenge);
+    } else
+        _LOG2T(call_id, "completed: failed: %s", error->message);
+
+    _call_id_invoke_callback(call_id, is_authorized, is_challenge, error);
+}
+
+static gboolean
+_call_on_idle(gpointer user_data)
+{
+    NMAuthManagerCallId *call_id = user_data;
+    gboolean             is_authorized;
+    gboolean             is_challenge = FALSE;
+
+    is_authorized    = call_id->idle_is_authorized;
+    call_id->idle_id = 0;
+
+    _LOG2T(call_id,
+           "completed: authorized=%d, challenge=%d (simulated)",
+           is_authorized,
+           is_challenge);
+
+    _call_id_invoke_callback(call_id, is_authorized, is_challenge, NULL);
+    return G_SOURCE_REMOVE;
+}
+
+/*
+ * @callback must never be invoked synchronously.
+ *
+ * @callback is always invoked exactly once, and never synchronously.
+ * You may cancel the invocation with nm_auth_manager_check_authorization_cancel(),
+ * but: you may only do so exactly once, and only before @callback is
+ * invoked. Even if you cancel the request, @callback will still be invoked
+ * (synchronously, during the _cancel() callback).
+ *
+ * The request keeps @self alive (it needs to do so, because when cancelling a
+ * request we might need to do an additional CancelCheckAuthorization call, for
+ * which @self must be live long enough).
+ */
+NMAuthManagerCallId *
+nm_auth_manager_check_authorization(NMAuthManager *                         self,
+                                    NMAuthSubject *                         subject,
+                                    const char *                            action_id,
+                                    gboolean                                allow_user_interaction,
+                                    NMAuthManagerCheckAuthorizationCallback callback,
+                                    gpointer                                user_data)
+{
+    NMAuthManagerPrivate *        priv;
+    PolkitCheckAuthorizationFlags flags;
+    char                          subject_buf[64];
+    NMAuthManagerCallId *         call_id;
+
+    g_return_val_if_fail(NM_IS_AUTH_MANAGER(self), NULL);
+    g_return_val_if_fail(NM_IN_SET(nm_auth_subject_get_subject_type(subject),
+                                   NM_AUTH_SUBJECT_TYPE_INTERNAL,
+                                   NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS),
+                         NULL);
+    g_return_val_if_fail(action_id, NULL);
+
+    priv = NM_AUTH_MANAGER_GET_PRIVATE(self);
+
+    g_return_val_if_fail(!priv->disposing, NULL);
+    g_return_val_if_fail(!priv->shutting_down, NULL);
+
+    flags = allow_user_interaction ? POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION
+                                   : POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE;
+
+    call_id  = g_slice_new(NMAuthManagerCallId);
+    *call_id = (NMAuthManagerCallId){
+        .self               = g_object_ref(self),
+        .callback           = callback,
+        .user_data          = user_data,
+        .call_numid         = ++priv->call_numid_counter,
+        .idle_is_authorized = TRUE,
+    };
+    c_list_link_tail(&priv->calls_lst_head, &call_id->calls_lst);
+
+    if (nm_auth_subject_get_subject_type(subject) == NM_AUTH_SUBJECT_TYPE_INTERNAL) {
+        _LOG2T(call_id,
+               "CheckAuthorization(%s), subject=%s (succeeding for internal request)",
+               action_id,
+               nm_auth_subject_to_string(subject, subject_buf, sizeof(subject_buf)));
+        call_id->idle_id = g_idle_add(_call_on_idle, call_id);
+    } else if (nm_auth_subject_get_unix_process_uid(subject) == 0) {
+        _LOG2T(call_id,
+               "CheckAuthorization(%s), subject=%s (succeeding for root)",
+               action_id,
+               nm_auth_subject_to_string(subject, subject_buf, sizeof(subject_buf)));
+        call_id->idle_id = g_idle_add(_call_on_idle, call_id);
+    } else if (priv->auth_polkit_mode != NM_AUTH_POLKIT_MODE_USE_POLKIT) {
+        _LOG2T(call_id,
+               "CheckAuthorization(%s), subject=%s (PolicyKit disabled and always %s authorization "
+               "to non-root user)",
+               action_id,
+               nm_auth_subject_to_string(subject, subject_buf, sizeof(subject_buf)),
+               priv->auth_polkit_mode == NM_AUTH_POLKIT_MODE_ALLOW_ALL ? "grant" : "deny");
+        call_id->idle_is_authorized = (priv->auth_polkit_mode == NM_AUTH_POLKIT_MODE_ALLOW_ALL);
+        call_id->idle_id            = g_idle_add(_call_on_idle, call_id);
+    } else {
+        GVariant *      parameters;
+        GVariantBuilder builder;
+        GVariant *      subject_value;
+        GVariant *      details_value;
+
+        subject_value = nm_auth_subject_unix_to_polkit_gvariant(subject);
+        nm_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);
+
+        parameters = g_variant_new("(@(sa{sv})s@a{ss}us)",
+                                   subject_value,
+                                   action_id,
+                                   details_value,
+                                   (guint32) flags,
+                                   cancellation_id_to_str_a(call_id->call_numid));
+
+        _LOG2T(call_id,
+               "CheckAuthorization(%s), subject=%s",
+               action_id,
+               nm_auth_subject_to_string(subject, subject_buf, sizeof(subject_buf)));
+
+        call_id->dbus_cancellable = g_cancellable_new();
+
+        nm_assert(priv->main_cancellable);
+
+        g_dbus_connection_call(priv->dbus_connection,
+                               POLKIT_SERVICE,
+                               POLKIT_OBJECT_PATH,
+                               POLKIT_INTERFACE,
+                               "CheckAuthorization",
+                               parameters,
+                               G_VARIANT_TYPE("((bba{ss}))"),
+                               G_DBUS_CALL_FLAGS_NONE,
+                               G_MAXINT, /* no timeout */
+                               call_id->dbus_cancellable,
+                               _call_check_authorize_cb,
+                               call_id);
+    }
+
+    return call_id;
+}
+
+void
+nm_auth_manager_check_authorization_cancel(NMAuthManagerCallId *call_id)
+{
+    NMAuthManager *self;
+    gs_free_error GError *error = NULL;
+
+    g_return_if_fail(call_id);
+
+    self = call_id->self;
+
+    g_return_if_fail(NM_IS_AUTH_MANAGER(self));
+    g_return_if_fail(!c_list_is_empty(&call_id->calls_lst));
+
+    nm_assert(
+        c_list_contains(&NM_AUTH_MANAGER_GET_PRIVATE(self)->calls_lst_head, &call_id->calls_lst));
+
+    nm_utils_error_set_cancelled(&error, FALSE, "NMAuthManager");
+    _LOG2T(call_id, "completed: failed due to call cancelled");
+    _call_id_invoke_callback(call_id, FALSE, FALSE, error);
+}
+
+/*****************************************************************************/
+
+static void
+changed_signal_cb(GDBusConnection *connection,
+                  const char *     sender_name,
+                  const char *     object_path,
+                  const char *     interface_name,
+                  const char *     signal_name,
+                  GVariant *       parameters,
+                  gpointer         user_data)
+{
+    NMAuthManager *       self = user_data;
+    NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE(self);
+    gboolean              valid_sender;
+
+    nm_assert(nm_streq0(signal_name, "Changed"));
+
+    valid_sender = nm_streq0(priv->name_owner, sender_name);
+
+    _LOGD("dbus-signal: \"Changed\" notification%s", valid_sender ? "" : " (ignore)");
+
+    if (valid_sender)
+        _emit_changed_signal(self);
+}
+
+static void
+_name_owner_changed(NMAuthManager *self, const char *name_owner, gboolean is_initial)
+{
+    NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE(self);
+    gboolean              is_changed;
+    gs_free char *        old_name_owner = NULL;
+
+    if (is_initial)
+        priv->got_name_owner = TRUE;
+    else {
+        if (!priv->got_name_owner)
+            return;
+    }
+
+    name_owner = nm_str_not_empty(name_owner);
+
+    is_changed = !nm_streq0(priv->name_owner, name_owner);
+    if (is_changed) {
+        old_name_owner   = g_steal_pointer(&priv->name_owner);
+        priv->name_owner = g_strdup(name_owner);
+    } else {
+        if (!is_initial)
+            return;
+    }
+
+    if (!priv->name_owner) {
+        if (is_initial)
+            _LOGT("name-owner: polkit not running");
+        else
+            _LOGT("name-owner: polkit stopped (was %s)", old_name_owner);
+    } else {
+        if (is_initial)
+            _LOGT("name-owner: polkit is running (now %s)", priv->name_owner);
+        else if (old_name_owner)
+            _LOGT("name-owner: polkit restarted (now %s, was %s)",
+                  priv->name_owner,
+                  old_name_owner);
+        else
+            _LOGT("name-owner: polkit started (now %s)", priv->name_owner);
+    }
+
+    if (priv->name_owner)
+        _emit_changed_signal(self);
+}
+
+static void
+_name_owner_changed_cb(GDBusConnection *connection,
+                       const char *     sender_name,
+                       const char *     object_path,
+                       const char *     interface_name,
+                       const char *     signal_name,
+                       GVariant *       parameters,
+                       gpointer         user_data)
+{
+    NMAuthManager *self = user_data;
+    const char *   new_owner;
+
+    if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)")))
+        return;
+
+    g_variant_get(parameters, "(&s&s&s)", NULL, NULL, &new_owner);
+
+    _name_owner_changed(self, new_owner, FALSE);
+}
+
+static void
+_name_owner_get_cb(const char *name_owner, GError *error, gpointer user_data)
+{
+    if (!nm_utils_error_is_cancelled(error))
+        _name_owner_changed(user_data, name_owner, TRUE);
+}
+
+/*****************************************************************************/
+
+NMAuthManager *
+nm_auth_manager_get()
+{
+    g_return_val_if_fail(singleton_instance, NULL);
+
+    return singleton_instance;
+}
+
+void
+nm_auth_manager_force_shutdown(NMAuthManager *self)
+{
+    NMAuthManagerPrivate *priv;
+
+    g_return_if_fail(NM_IS_AUTH_MANAGER(self));
+
+    priv = NM_AUTH_MANAGER_GET_PRIVATE(self);
+
+    /* FIXME(shutdown): ensure we properly call this API during shutdown as
+     * described next. */
+
+    /* while we have pending requests (NMAuthManagerCallId), the instance
+     * is kept alive.
+     *
+     * Even if the caller cancels all pending call-ids, we still need to keep
+     * a reference to self, in order to handle pending CancelCheckAuthorization
+     * requests.
+     *
+     * To do a coordinated shutdown, do the following:
+     * - cancel all pending NMAuthManagerCallId requests.
+     * - ensure everybody unrefs the NMAuthManager instance. If by that, the instance
+     *   gets destroyed, the shutdown already completed successfully.
+     * - Otherwise, the object is kept alive by pending CancelCheckAuthorization requests.
+     *   wait a certain timeout (1 second) for all requests to complete (by watching
+     *   for destruction of NMAuthManager).
+     * - if that doesn't happen within timeout, issue nm_auth_manager_force_shutdown() and
+     *   wait longer. After that, soon the instance should be destroyed and you
+     *   did a successful shutdown.
+     * - if the instance was still not destroyed within a short timeout, you leaked
+     *   resources. You cannot properly shutdown.
+     */
+
+    priv->shutting_down = TRUE;
+    nm_clear_g_cancellable(&priv->main_cancellable);
+}
+
+/*****************************************************************************/
+
+static void
+set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+    NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE(object);
+    int                   v_int;
+
+    switch (prop_id) {
+    case PROP_POLKIT_ENABLED:
+        /* construct-only */
+        v_int = g_value_get_int(value);
+        g_return_if_fail(NM_IN_SET(v_int,
+                                   NM_AUTH_POLKIT_MODE_ROOT_ONLY,
+                                   NM_AUTH_POLKIT_MODE_ALLOW_ALL,
+                                   NM_AUTH_POLKIT_MODE_USE_POLKIT));
+        priv->auth_polkit_mode = v_int;
+        nm_assert(priv->auth_polkit_mode == v_int);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
+}
+
+/*****************************************************************************/
+
+static void
+nm_auth_manager_init(NMAuthManager *self)
+{
+    NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE(self);
+
+    c_list_init(&priv->calls_lst_head);
+    priv->auth_polkit_mode = NM_AUTH_POLKIT_MODE_ROOT_ONLY;
+}
+
+static void
+constructed(GObject *object)
+{
+    NMAuthManager *       self = NM_AUTH_MANAGER(object);
+    NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE(self);
+    NMLogLevel            logl = LOGL_DEBUG;
+    const char *          create_message;
+
+    G_OBJECT_CLASS(nm_auth_manager_parent_class)->constructed(object);
+
+    if (priv->auth_polkit_mode != NM_AUTH_POLKIT_MODE_USE_POLKIT) {
+        if (priv->auth_polkit_mode == NM_AUTH_POLKIT_MODE_ROOT_ONLY)
+            create_message = "polkit disabled, root-only";
+        else
+            create_message = "polkit disabled, allow-all";
+        goto out;
+    }
+
+    priv->dbus_connection = nm_g_object_ref(NM_MAIN_DBUS_CONNECTION_GET);
+
+    if (!priv->dbus_connection) {
+        /* This warrants an info level message. */
+        logl = LOGL_INFO;
+        create_message =
+            "D-Bus connection not available. Polkit is disabled and only root will be authorized.";
+        priv->auth_polkit_mode = NM_AUTH_POLKIT_MODE_ROOT_ONLY;
+        goto out;
+    }
+
+    priv->main_cancellable = g_cancellable_new();
+
+    priv->name_owner_changed_id =
+        nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection,
+                                                               POLKIT_SERVICE,
+                                                               _name_owner_changed_cb,
+                                                               self,
+                                                               NULL);
+
+    priv->changed_id = g_dbus_connection_signal_subscribe(priv->dbus_connection,
+                                                          POLKIT_SERVICE,
+                                                          POLKIT_INTERFACE,
+                                                          "Changed",
+                                                          POLKIT_OBJECT_PATH,
+                                                          NULL,
+                                                          G_DBUS_SIGNAL_FLAGS_NONE,
+                                                          changed_signal_cb,
+                                                          self,
+                                                          NULL);
+
+    nm_dbus_connection_call_get_name_owner(priv->dbus_connection,
+                                           POLKIT_SERVICE,
+                                           -1,
+                                           priv->main_cancellable,
+                                           _name_owner_get_cb,
+                                           self);
+
+    create_message = "polkit enabled";
+
+out:
+    _NMLOG(logl, "create auth-manager: %s", create_message);
+}
+
+NMAuthManager *
+nm_auth_manager_setup(NMAuthPolkitMode auth_polkit_mode)
+{
+    NMAuthManager *self;
+
+    g_return_val_if_fail(!singleton_instance, singleton_instance);
+    nm_assert(NM_IN_SET(auth_polkit_mode,
+                        NM_AUTH_POLKIT_MODE_ROOT_ONLY,
+                        NM_AUTH_POLKIT_MODE_ALLOW_ALL,
+                        NM_AUTH_POLKIT_MODE_USE_POLKIT));
+
+    self = g_object_new(NM_TYPE_AUTH_MANAGER,
+                        NM_AUTH_MANAGER_POLKIT_ENABLED,
+                        (int) auth_polkit_mode,
+                        NULL);
+    _LOGD("set instance");
+
+    singleton_instance = self;
+    nm_singleton_instance_register();
+
+    nm_log_dbg(LOGD_CORE,
+               "setup %s singleton (" NM_HASH_OBFUSCATE_PTR_FMT ")",
+               "NMAuthManager",
+               NM_HASH_OBFUSCATE_PTR(singleton_instance));
+
+    return self;
+}
+
+static void
+dispose(GObject *object)
+{
+    NMAuthManager *       self = NM_AUTH_MANAGER(object);
+    NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE(self);
+
+    _LOGD("dispose");
+
+    nm_assert(c_list_is_empty(&priv->calls_lst_head));
+
+    priv->disposing = TRUE;
+
+    nm_clear_g_cancellable(&priv->main_cancellable);
+
+    nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id);
+
+    nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->changed_id);
+
+    G_OBJECT_CLASS(nm_auth_manager_parent_class)->dispose(object);
+
+    g_clear_object(&priv->dbus_connection);
+
+    nm_clear_g_free(&priv->name_owner);
+}
+
+static void
+nm_auth_manager_class_init(NMAuthManagerClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+    object_class->set_property = set_property;
+    object_class->constructed  = constructed;
+    object_class->dispose      = dispose;
+
+    obj_properties[PROP_POLKIT_ENABLED] =
+        g_param_spec_int(NM_AUTH_MANAGER_POLKIT_ENABLED,
+                         "",
+                         "",
+                         NM_AUTH_POLKIT_MODE_ROOT_ONLY,
+                         NM_AUTH_POLKIT_MODE_USE_POLKIT,
+                         NM_AUTH_POLKIT_MODE_USE_POLKIT,
+                         G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+    signals[CHANGED_SIGNAL] = g_signal_new(NM_AUTH_MANAGER_SIGNAL_CHANGED,
+                                           NM_TYPE_AUTH_MANAGER,
+                                           G_SIGNAL_RUN_LAST,
+                                           0,
+                                           NULL,
+                                           NULL,
+                                           g_cclosure_marshal_VOID__VOID,
+                                           G_TYPE_NONE,
+                                           0);
+}