mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-09 15:50:26 +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
487 lines
16 KiB
C
487 lines
16 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* Copyright (C) 2015 Red Hat, Inc.
|
|
*/
|
|
|
|
#include "src/core/nm-default-daemon.h"
|
|
|
|
#include "nm-audit-manager.h"
|
|
|
|
#if HAVE_LIBAUDIT
|
|
#include <libaudit.h>
|
|
#endif
|
|
|
|
#define NM_VALUE_TYPE_DEFINE_FUNCTIONS
|
|
|
|
#include "libnm-core-aux-intern/nm-auth-subject.h"
|
|
#include "libnm-glib-aux/nm-str-buf.h"
|
|
#include "libnm-glib-aux/nm-value-type.h"
|
|
#include "nm-config.h"
|
|
#include "nm-dbus-manager.h"
|
|
#include "settings/nm-settings-connection.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef enum _nm_packed {
|
|
BACKEND_LOG = (1 << 0),
|
|
BACKEND_AUDITD = (1 << 1),
|
|
_BACKEND_LAST,
|
|
BACKEND_ALL = ((_BACKEND_LAST - 1) << 1) - 1,
|
|
} AuditBackend;
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
AuditBackend backends;
|
|
bool need_encoding;
|
|
NMValueType value_type;
|
|
NMValueTypUnion value;
|
|
} AuditField;
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
NMConfig *config;
|
|
int auditd_fd;
|
|
} NMAuditManagerPrivate;
|
|
|
|
struct _NMAuditManager {
|
|
GObject parent;
|
|
#if HAVE_LIBAUDIT
|
|
NMAuditManagerPrivate _priv;
|
|
#endif
|
|
};
|
|
|
|
struct _NMAuditManagerClass {
|
|
GObjectClass parent;
|
|
};
|
|
|
|
G_DEFINE_TYPE(NMAuditManager, nm_audit_manager, G_TYPE_OBJECT)
|
|
|
|
#define NM_AUDIT_MANAGER_GET_PRIVATE(self) \
|
|
_NM_GET_PRIVATE(self, NMAuditManager, NM_IS_AUDIT_MANAGER)
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define AUDIT_LOG_LEVEL LOGL_INFO
|
|
|
|
#define _NMLOG_PREFIX_NAME "audit"
|
|
#define _NMLOG(level, domain, ...) \
|
|
G_STMT_START \
|
|
{ \
|
|
nm_log((level), \
|
|
(domain), \
|
|
NULL, \
|
|
NULL, \
|
|
"%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
|
|
_NMLOG_PREFIX_NAME ": " _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
|
|
} \
|
|
G_STMT_END
|
|
|
|
/*****************************************************************************/
|
|
|
|
NM_DEFINE_SINGLETON_GETTER(NMAuditManager, nm_audit_manager_get, NM_TYPE_AUDIT_MANAGER);
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_audit_field_init_string(AuditField *field,
|
|
const char *name,
|
|
const char *str,
|
|
gboolean need_encoding,
|
|
AuditBackend backends)
|
|
{
|
|
*field = (AuditField){
|
|
.name = name,
|
|
.need_encoding = need_encoding,
|
|
.backends = backends,
|
|
.value_type = NM_VALUE_TYPE_STRING,
|
|
.value.v_string = str,
|
|
};
|
|
}
|
|
|
|
static void
|
|
_audit_field_init_uint64(AuditField *field, const char *name, guint64 val, AuditBackend backends)
|
|
{
|
|
*field = (AuditField){
|
|
.name = name,
|
|
.backends = backends,
|
|
.value_type = NM_VALUE_TYPE_UINT64,
|
|
.value.v_uint64 = val,
|
|
};
|
|
}
|
|
|
|
static const char *
|
|
build_message(NMStrBuf *strbuf, AuditBackend backend, GPtrArray *fields)
|
|
{
|
|
guint i;
|
|
|
|
if (strbuf->len == 0) {
|
|
/* preallocate a large buffer... */
|
|
nm_str_buf_maybe_expand(strbuf, NM_UTILS_GET_NEXT_REALLOC_SIZE_232, FALSE);
|
|
} else
|
|
nm_str_buf_reset(strbuf);
|
|
|
|
for (i = 0; i < fields->len; i++) {
|
|
const AuditField *field = fields->pdata[i];
|
|
|
|
if (!NM_FLAGS_ANY(field->backends, backend))
|
|
continue;
|
|
|
|
nm_str_buf_append_required_delimiter(strbuf, ' ');
|
|
|
|
if (field->value_type == NM_VALUE_TYPE_STRING) {
|
|
const char *str = field->value.v_string;
|
|
|
|
#if HAVE_LIBAUDIT
|
|
if (backend == BACKEND_AUDITD) {
|
|
if (field->need_encoding) {
|
|
gs_free char *value = NULL;
|
|
|
|
value = audit_encode_nv_string(field->name, str, 0);
|
|
nm_str_buf_append(strbuf, value);
|
|
} else
|
|
nm_str_buf_append_printf(strbuf, "%s=%s", field->name, str);
|
|
continue;
|
|
}
|
|
#endif /* HAVE_LIBAUDIT */
|
|
|
|
nm_str_buf_append_printf(strbuf, "%s=\"%s\"", field->name, str);
|
|
continue;
|
|
}
|
|
|
|
if (field->value_type == NM_VALUE_TYPE_UINT64) {
|
|
nm_str_buf_append_printf(strbuf,
|
|
"%s=%" G_GUINT64_FORMAT,
|
|
field->name,
|
|
field->value.v_uint64);
|
|
continue;
|
|
}
|
|
|
|
g_return_val_if_reached(NULL);
|
|
}
|
|
|
|
return nm_str_buf_get_str(strbuf);
|
|
}
|
|
|
|
static void
|
|
nm_audit_log(NMAuditManager *self,
|
|
GPtrArray *fields,
|
|
const char *file,
|
|
guint line,
|
|
const char *func,
|
|
gboolean success)
|
|
{
|
|
nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT(0, FALSE);
|
|
#if HAVE_LIBAUDIT
|
|
NMAuditManagerPrivate *priv;
|
|
#endif
|
|
|
|
g_return_if_fail(NM_IS_AUDIT_MANAGER(self));
|
|
|
|
#if HAVE_LIBAUDIT
|
|
priv = NM_AUDIT_MANAGER_GET_PRIVATE(self);
|
|
|
|
if (priv->auditd_fd >= 0) {
|
|
audit_log_user_message(priv->auditd_fd,
|
|
AUDIT_USYS_CONFIG,
|
|
build_message(&strbuf, BACKEND_AUDITD, fields),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
success);
|
|
}
|
|
#endif
|
|
|
|
if (nm_logging_enabled(AUDIT_LOG_LEVEL, LOGD_AUDIT)) {
|
|
_nm_log_full(file,
|
|
line,
|
|
func,
|
|
!(NM_THREAD_SAFE_ON_MAIN_THREAD),
|
|
AUDIT_LOG_LEVEL,
|
|
LOGD_AUDIT,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
"%s%s",
|
|
_NMLOG_PREFIX_NAME ": ",
|
|
build_message(&strbuf, BACKEND_LOG, fields));
|
|
}
|
|
}
|
|
|
|
static void
|
|
_audit_log_helper(NMAuditManager *self,
|
|
GPtrArray *fields,
|
|
const char *file,
|
|
guint line,
|
|
const char *func,
|
|
const char *op,
|
|
gboolean result,
|
|
gpointer subject_context,
|
|
const char *reason)
|
|
{
|
|
AuditField op_field;
|
|
AuditField pid_field;
|
|
AuditField uid_field;
|
|
AuditField result_field;
|
|
AuditField reason_field;
|
|
gulong pid;
|
|
gulong uid;
|
|
NMAuthSubject *subject = NULL;
|
|
gs_unref_object NMAuthSubject *subject_free = NULL;
|
|
|
|
_audit_field_init_string(&op_field, "op", op, FALSE, BACKEND_ALL);
|
|
g_ptr_array_insert(fields, 0, &op_field);
|
|
|
|
if (subject_context) {
|
|
if (NM_IS_AUTH_SUBJECT(subject_context))
|
|
subject = subject_context;
|
|
else if (G_IS_DBUS_METHOD_INVOCATION(subject_context)) {
|
|
GDBusMethodInvocation *context = subject_context;
|
|
|
|
subject = subject_free = nm_dbus_manager_new_auth_subject_from_context(context);
|
|
} else
|
|
g_warn_if_reached();
|
|
}
|
|
if (subject && nm_auth_subject_get_subject_type(subject) == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS) {
|
|
pid = nm_auth_subject_get_unix_process_pid(subject);
|
|
uid = nm_auth_subject_get_unix_process_uid(subject);
|
|
if (pid != G_MAXULONG) {
|
|
_audit_field_init_uint64(&pid_field, "pid", pid, BACKEND_ALL);
|
|
g_ptr_array_add(fields, &pid_field);
|
|
}
|
|
if (uid != G_MAXULONG) {
|
|
_audit_field_init_uint64(&uid_field, "uid", uid, BACKEND_ALL);
|
|
g_ptr_array_add(fields, &uid_field);
|
|
}
|
|
}
|
|
|
|
_audit_field_init_string(&result_field,
|
|
"result",
|
|
result ? "success" : "fail",
|
|
FALSE,
|
|
BACKEND_ALL);
|
|
g_ptr_array_add(fields, &result_field);
|
|
|
|
if (reason) {
|
|
_audit_field_init_string(&reason_field, "reason", reason, FALSE, BACKEND_LOG);
|
|
g_ptr_array_add(fields, &reason_field);
|
|
}
|
|
|
|
nm_audit_log(self, fields, file, line, func, result);
|
|
}
|
|
|
|
gboolean
|
|
nm_audit_manager_audit_enabled(NMAuditManager *self)
|
|
{
|
|
#if HAVE_LIBAUDIT
|
|
NMAuditManagerPrivate *priv = NM_AUDIT_MANAGER_GET_PRIVATE(self);
|
|
|
|
if (priv->auditd_fd >= 0)
|
|
return TRUE;
|
|
#endif
|
|
|
|
return nm_logging_enabled(AUDIT_LOG_LEVEL, LOGD_AUDIT);
|
|
}
|
|
|
|
void
|
|
_nm_audit_manager_log_connection_op(NMAuditManager *self,
|
|
const char *file,
|
|
guint line,
|
|
const char *func,
|
|
const char *op,
|
|
NMSettingsConnection *connection,
|
|
gboolean result,
|
|
const char *args,
|
|
gpointer subject_context,
|
|
const char *reason)
|
|
{
|
|
gs_unref_ptrarray GPtrArray *fields = NULL;
|
|
AuditField uuid_field;
|
|
AuditField name_field;
|
|
AuditField args_field;
|
|
|
|
g_return_if_fail(op);
|
|
|
|
fields = g_ptr_array_new();
|
|
|
|
if (connection) {
|
|
_audit_field_init_string(&uuid_field,
|
|
"uuid",
|
|
nm_settings_connection_get_uuid(connection),
|
|
FALSE,
|
|
BACKEND_ALL);
|
|
g_ptr_array_add(fields, &uuid_field);
|
|
|
|
_audit_field_init_string(&name_field,
|
|
"name",
|
|
nm_settings_connection_get_id(connection),
|
|
TRUE,
|
|
BACKEND_ALL);
|
|
g_ptr_array_add(fields, &name_field);
|
|
}
|
|
|
|
if (args) {
|
|
_audit_field_init_string(&args_field, "args", args, FALSE, BACKEND_ALL);
|
|
g_ptr_array_add(fields, &args_field);
|
|
}
|
|
|
|
_audit_log_helper(self, fields, file, line, func, op, result, subject_context, reason);
|
|
}
|
|
|
|
void
|
|
_nm_audit_manager_log_generic_op(NMAuditManager *self,
|
|
const char *file,
|
|
guint line,
|
|
const char *func,
|
|
const char *op,
|
|
const char *arg,
|
|
gboolean result,
|
|
gpointer subject_context,
|
|
const char *reason)
|
|
{
|
|
gs_unref_ptrarray GPtrArray *fields = NULL;
|
|
AuditField arg_field;
|
|
|
|
g_return_if_fail(op);
|
|
g_return_if_fail(arg);
|
|
|
|
fields = g_ptr_array_new();
|
|
|
|
_audit_field_init_string(&arg_field, "arg", arg, TRUE, BACKEND_ALL);
|
|
g_ptr_array_add(fields, &arg_field);
|
|
|
|
_audit_log_helper(self, fields, file, line, func, op, result, subject_context, reason);
|
|
}
|
|
|
|
void
|
|
_nm_audit_manager_log_device_op(NMAuditManager *self,
|
|
const char *file,
|
|
guint line,
|
|
const char *func,
|
|
const char *op,
|
|
NMDevice *device,
|
|
gboolean result,
|
|
const char *args,
|
|
gpointer subject_context,
|
|
const char *reason)
|
|
{
|
|
gs_unref_ptrarray GPtrArray *fields = NULL;
|
|
AuditField interface_field;
|
|
AuditField ifindex_field;
|
|
AuditField args_field;
|
|
int ifindex;
|
|
|
|
g_return_if_fail(op);
|
|
g_return_if_fail(device);
|
|
|
|
fields = g_ptr_array_new();
|
|
|
|
_audit_field_init_string(&interface_field,
|
|
"interface",
|
|
nm_device_get_ip_iface(device),
|
|
TRUE,
|
|
BACKEND_ALL);
|
|
g_ptr_array_add(fields, &interface_field);
|
|
|
|
ifindex = nm_device_get_ip_ifindex(device);
|
|
if (ifindex > 0) {
|
|
_audit_field_init_uint64(&ifindex_field, "ifindex", ifindex, BACKEND_ALL);
|
|
g_ptr_array_add(fields, &ifindex_field);
|
|
}
|
|
|
|
if (args) {
|
|
_audit_field_init_string(&args_field, "args", args, FALSE, BACKEND_ALL);
|
|
g_ptr_array_add(fields, &args_field);
|
|
}
|
|
|
|
_audit_log_helper(self, fields, file, line, func, op, result, subject_context, reason);
|
|
}
|
|
|
|
#if HAVE_LIBAUDIT
|
|
static void
|
|
init_auditd(NMAuditManager *self)
|
|
{
|
|
NMAuditManagerPrivate *priv = NM_AUDIT_MANAGER_GET_PRIVATE(self);
|
|
NMConfigData *data = nm_config_get_data(priv->config);
|
|
int errsv;
|
|
|
|
if (nm_config_data_get_value_boolean(data,
|
|
NM_CONFIG_KEYFILE_GROUP_LOGGING,
|
|
NM_CONFIG_KEYFILE_KEY_LOGGING_AUDIT,
|
|
NM_CONFIG_DEFAULT_LOGGING_AUDIT_BOOL)) {
|
|
if (priv->auditd_fd < 0) {
|
|
priv->auditd_fd = audit_open();
|
|
if (priv->auditd_fd < 0) {
|
|
errsv = errno;
|
|
_LOGE(LOGD_CORE, "failed to open auditd socket: %s", nm_strerror_native(errsv));
|
|
} else
|
|
_LOGD(LOGD_CORE, "socket created");
|
|
}
|
|
} else {
|
|
if (priv->auditd_fd >= 0) {
|
|
audit_close(priv->auditd_fd);
|
|
priv->auditd_fd = -1;
|
|
_LOGD(LOGD_CORE, "socket closed");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
config_changed_cb(NMConfig *config,
|
|
NMConfigData *config_data,
|
|
NMConfigChangeFlags changes,
|
|
NMConfigData *old_data,
|
|
NMAuditManager *self)
|
|
{
|
|
if (NM_FLAGS_HAS(changes, NM_CONFIG_CHANGE_VALUES))
|
|
init_auditd(self);
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
nm_audit_manager_init(NMAuditManager *self)
|
|
{
|
|
#if HAVE_LIBAUDIT
|
|
NMAuditManagerPrivate *priv = NM_AUDIT_MANAGER_GET_PRIVATE(self);
|
|
|
|
priv->config = g_object_ref(nm_config_get());
|
|
g_signal_connect(G_OBJECT(priv->config),
|
|
NM_CONFIG_SIGNAL_CONFIG_CHANGED,
|
|
G_CALLBACK(config_changed_cb),
|
|
self);
|
|
priv->auditd_fd = -1;
|
|
|
|
init_auditd(self);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
dispose(GObject *object)
|
|
{
|
|
#if HAVE_LIBAUDIT
|
|
NMAuditManager *self = NM_AUDIT_MANAGER(object);
|
|
NMAuditManagerPrivate *priv = NM_AUDIT_MANAGER_GET_PRIVATE(self);
|
|
|
|
if (priv->config) {
|
|
g_signal_handlers_disconnect_by_func(priv->config, config_changed_cb, self);
|
|
g_clear_object(&priv->config);
|
|
}
|
|
|
|
if (priv->auditd_fd >= 0) {
|
|
audit_close(priv->auditd_fd);
|
|
priv->auditd_fd = -1;
|
|
}
|
|
#endif
|
|
|
|
G_OBJECT_CLASS(nm_audit_manager_parent_class)->dispose(object);
|
|
}
|
|
|
|
static void
|
|
nm_audit_manager_class_init(NMAuditManagerClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
|
|
|
object_class->dispose = dispose;
|
|
}
|