/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2013 - 2014 Red Hat, Inc.
*/
/**
* SECTION:nm-auth-subject
* @short_description: Encapsulates authentication information about a requestor
*
* #NMAuthSubject encpasulates identifying information about an entity that
* makes requests, like process identifier and user UID.
*/
#include "nm-default.h"
#include "nm-auth-subject.h"
#include <stdlib.h>
enum {
PROP_0,
PROP_SUBJECT_TYPE,
PROP_UNIX_PROCESS_DBUS_SENDER,
PROP_UNIX_PROCESS_PID,
PROP_UNIX_PROCESS_UID,
PROP_UNIX_SESSION_ID,
PROP_LAST,
};
typedef struct {
NMAuthSubjectType subject_type;
struct {
gulong pid;
gulong uid;
guint64 start_time;
char * dbus_sender;
} unix_process;
struct {
char *id;
} unix_session;
} NMAuthSubjectPrivate;
struct _NMAuthSubject {
GObject parent;
NMAuthSubjectPrivate _priv;
};
struct _NMAuthSubjectClass {
GObjectClass parent;
};
G_DEFINE_TYPE(NMAuthSubject, nm_auth_subject, G_TYPE_OBJECT)
#define NM_AUTH_SUBJECT_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMAuthSubject, NM_IS_AUTH_SUBJECT)
/*****************************************************************************/
#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]",
(unsigned long) priv->unix_process.pid,
(unsigned long) priv->unix_process.uid,
(unsigned long long) priv->unix_process.start_time);
break;
case NM_AUTH_SUBJECT_TYPE_INTERNAL:
g_strlcpy(buf, "internal", buf_len);
break;
case NM_AUTH_SUBJECT_TYPE_UNIX_SESSION:
g_snprintf(buf, buf_len, "unix-session[id=%s]", priv->unix_session.id);
break;
default:
g_strlcpy(buf, "invalid", buf_len);
break;
}
return buf;
}
/* returns a floating variant */
GVariant *
nm_auth_subject_unix_to_polkit_gvariant(NMAuthSubject *self)
{
GVariantBuilder builder;
CHECK_SUBJECT(self, NULL);
switch (priv->subject_type) {
case NM_AUTH_SUBJECT_TYPE_UNIX_SESSION:
g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
g_variant_builder_add(&builder,
"{sv}",
"session-id",
g_variant_new_string(priv->unix_session.id));
return g_variant_new("(sa{sv})", "unix-session", &builder);
case NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS:
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));
return g_variant_new("(sa{sv})", "unix-process", &builder);
default:
g_return_val_if_reached(NULL);
}
}
NMAuthSubjectType
nm_auth_subject_get_subject_type(NMAuthSubject *subject)
{
CHECK_SUBJECT(subject, NM_AUTH_SUBJECT_TYPE_INVALID);
return priv->subject_type;
}
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;
}
const char *
nm_auth_subject_get_unix_session_id(NMAuthSubject *subject)
{
CHECK_SUBJECT_TYPED(subject, NM_AUTH_SUBJECT_TYPE_UNIX_SESSION, NULL);
return priv->unix_session.id;
}
/*****************************************************************************/
/**
* nm_auth_subject_new_internal():
*
* Creates a new auth subject representing the NetworkManager process itself.
*
* Returns: the new #NMAuthSubject
*/
NMAuthSubject *
nm_auth_subject_new_internal(void)
{
return NM_AUTH_SUBJECT(g_object_new(NM_TYPE_AUTH_SUBJECT,
NM_AUTH_SUBJECT_SUBJECT_TYPE,
(int) NM_AUTH_SUBJECT_TYPE_INTERNAL,
NULL));
}
/**
* nm_auth_subject_new_unix_session():
*
* Creates a new auth subject representing a given unix session.
*
* Returns: the new #NMAuthSubject
*/
NMAuthSubject *
nm_auth_subject_new_unix_session(const char *session_id)
{
return NM_AUTH_SUBJECT(g_object_new(NM_TYPE_AUTH_SUBJECT,
NM_AUTH_SUBJECT_SUBJECT_TYPE,
(int) NM_AUTH_SUBJECT_TYPE_UNIX_SESSION,
NM_AUTH_SUBJECT_UNIX_SESSION_ID,
session_id,
NULL));
}
/**
* nm_auth_subject_new_unix_process():
*
* Creates a new auth subject representing a given unix process.
*
* Returns: the new #NMAuthSubject
*/
NMAuthSubject *
nm_auth_subject_new_unix_process(const char *dbus_sender, gulong pid, gulong uid)
{
return NM_AUTH_SUBJECT(g_object_new(NM_TYPE_AUTH_SUBJECT,
NM_AUTH_SUBJECT_SUBJECT_TYPE,
(int) NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS,
NM_AUTH_SUBJECT_UNIX_PROCESS_DBUS_SENDER,
dbus_sender,
NM_AUTH_SUBJECT_UNIX_PROCESS_PID,
pid,
NM_AUTH_SUBJECT_UNIX_PROCESS_UID,
uid,
NULL));
}
/**
* nm_auth_subject_new_unix_process_self():
*
* Creates a new auth subject representing the current executing process.
*
* Returns: the new #NMAuthSubject
*/
NMAuthSubject *
nm_auth_subject_new_unix_process_self(void)
{
return nm_auth_subject_new_unix_process(NULL, getpid(), getuid());
}
/*****************************************************************************/
static void
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE(object);
switch (prop_id) {
case PROP_SUBJECT_TYPE:
g_value_set_int(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;
case PROP_UNIX_SESSION_ID:
g_value_set_string(value, priv->unix_session.id);
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)
{
NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE(object);
NMAuthSubjectType subject_type;
int i;
const char * str;
gulong id;
switch (prop_id) {
case PROP_SUBJECT_TYPE:
/* construct-only */
i = g_value_get_int(value);
g_return_if_fail(NM_IN_SET(i,
(int) NM_AUTH_SUBJECT_TYPE_INTERNAL,
(int) NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS,
(int) NM_AUTH_SUBJECT_TYPE_UNIX_SESSION));
subject_type = i;
priv->subject_type |= subject_type;
g_return_if_fail(priv->subject_type == subject_type);
break;
case PROP_UNIX_PROCESS_DBUS_SENDER:
/* construct-only */
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:
/* construct-only */
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:
/* construct-only */
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;
case PROP_UNIX_SESSION_ID:
/* construct-only */
if ((str = g_value_get_string(value))) {
priv->subject_type |= NM_AUTH_SUBJECT_TYPE_UNIX_SESSION;
g_return_if_fail(priv->subject_type == NM_AUTH_SUBJECT_TYPE_UNIX_SESSION);
priv->unix_session.id = g_strdup(str);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void
_clear_private(NMAuthSubject *self)
{
NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE(self);
priv->subject_type = NM_AUTH_SUBJECT_TYPE_INVALID;
priv->unix_process.pid = G_MAXULONG;
priv->unix_process.uid = G_MAXULONG;
nm_clear_g_free(&priv->unix_process.dbus_sender);
nm_clear_g_free(&priv->unix_session.id);
}
static void
nm_auth_subject_init(NMAuthSubject *self)
{
_clear_private(self);
}
static void
constructed(GObject *object)
{
NMAuthSubject * self = NM_AUTH_SUBJECT(object);
NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE(self);
/* 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 int. */
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;
}
priv->unix_process.start_time =
nm_utils_get_start_time_for_pid(priv->unix_process.pid, NULL, NULL);
if (!priv->unix_process.start_time) {
/* Is the process already gone? Then fail creation of the auth subject
* by clearing the type. */
if (kill(priv->unix_process.pid, 0) != 0)
_clear_private(self);
/* Otherwise, although we didn't detect a start_time, the process is still around.
* That could be due to procfs mounted with hidepid. So just accept the request.
*
* Polkit on the other side, will accept 0 and try to lookup /proc/$PID/stat
* itself (and if it fails to do so, assume a start-time of 0 and proceed).
* The only combination that would fail here, is when NM is able to read the
* start-time, but polkit is not. */
}
return;
case NM_AUTH_SUBJECT_TYPE_UNIX_SESSION:
return;
default:
break;
}
_clear_private(self);
g_return_if_reached();
}
static void
finalize(GObject *object)
{
_clear_private((NMAuthSubject *) object);
G_OBJECT_CLASS(nm_auth_subject_parent_class)->finalize(object);
}
static void
nm_auth_subject_class_init(NMAuthSubjectClass *config_class)
{
GObjectClass *object_class = G_OBJECT_CLASS(config_class);
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_int(NM_AUTH_SUBJECT_SUBJECT_TYPE,
"",
"",
NM_AUTH_SUBJECT_TYPE_INVALID,
NM_AUTH_SUBJECT_TYPE_UNIX_SESSION,
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));
g_object_class_install_property(
object_class,
PROP_UNIX_SESSION_ID,
g_param_spec_string(NM_AUTH_SUBJECT_UNIX_SESSION_ID,
"",
"",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}