mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-26 07:40:08 +01:00
Clang 15 now (correctly) warns about this:
../src/libnm-core-impl/nm-vpn-plugin-info.c:201:40: error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes]
_nm_vpn_plugin_info_get_default_dir_etc()
^
void
../src/libnm-core-impl/nm-vpn-plugin-info.c:213:40: error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes]
_nm_vpn_plugin_info_get_default_dir_lib()
^
void
../src/libnm-core-impl/nm-vpn-plugin-info.c:226:41: error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes]
_nm_vpn_plugin_info_get_default_dir_user()
^
void
../src/libnm-core-impl/nm-vpn-plugin-info.c:315:29: error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes]
nm_vpn_plugin_info_list_load()
^
void
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(void)
|
|
{
|
|
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);
|
|
}
|