// SPDX-License-Identifier: LGPL-2.1+
/*
* 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));
}