mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-26 18:10:08 +01:00
We use clang-format for automatic formatting of our source files. Since clang-format is actively maintained software, the actual formatting depends on the used version of clang-format. That is unfortunate and painful, but really unavoidable unless clang-format would be strictly bug-compatible. So the version that we must use is from the current Fedora release, which is also tested by our gitlab-ci. Previously, we were using Fedora 34 with clang-tools-extra-12.0.1-1.fc34.x86_64. As Fedora 35 comes along, we need to update our formatting as Fedora 35 comes with version "13.0.0~rc1-1.fc35". An alternative would be to freeze on version 12, but that has different problems (like, it's cumbersome to rebuild clang 12 on Fedora 35 and it would be cumbersome for our developers which are on Fedora 35 to use a clang that they cannot easily install). The (differently painful) solution is to reformat from time to time, as we switch to a new Fedora (and thus clang) version. Usually we would expect that such a reformatting brings minor changes. But this time, the changes are huge. That is mentioned in the release notes [1] as Makes PointerAligment: Right working with AlignConsecutiveDeclarations. (Fixes https://llvm.org/PR27353) [1] https://releases.llvm.org/13.0.0/tools/clang/docs/ReleaseNotes.html#clang-format
751 lines
29 KiB
C
751 lines
29 KiB
C
/* 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 "libnm-glib-aux/nm-dbus-aux.h"
|
|
#include "nm-errors.h"
|
|
#include "libnm-core-intern/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);
|
|
}
|