From d0973df8633edfe039c8040d1773f7e89c7dd2f7 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Jan 17 2025 13:10:24 +0000 Subject: Backport patch to emit audit events from systemd-sysusers A second patch is backported to avoid conflict and also because it seems reasoanble to backport. --- diff --git a/0001-update-utmp-do-not-give-up-if-the-first-attempt-at-c.patch b/0001-update-utmp-do-not-give-up-if-the-first-attempt-at-c.patch new file mode 100644 index 0000000..76c2be1 --- /dev/null +++ b/0001-update-utmp-do-not-give-up-if-the-first-attempt-at-c.patch @@ -0,0 +1,75 @@ +From 8ed12b37afea9ccc36789aad2cef0d60eb6c5073 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 18 Dec 2024 22:27:29 +0900 +Subject: [PATCH 1/2] update-utmp: do not give up if the first attempt at + connecting bus failed +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Otherwise, the program exits with failure if the first attempt in run() failed: +``` +Dec 18 20:27:37 systemd-update-utmp[254]: Bus n/a: changing state UNSET → OPENING +Dec 18 20:27:37 systemd-update-utmp[254]: sd-bus: starting bus by connecting to /run/systemd/private... +Dec 18 20:27:37 systemd-update-utmp[254]: Bus n/a: changing state OPENING → CLOSED +Dec 18 20:27:37 systemd-update-utmp[254]: Failed to get D-Bus connection: Connection refused +``` + +(cherry picked from commit 85d040dabd2cc67c89b7ed6157429b8f6f2240f4) +--- + src/update-utmp/update-utmp.c | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +diff --git a/src/update-utmp/update-utmp.c b/src/update-utmp/update-utmp.c +index e40843cf35..a10e6d478a 100644 +--- a/src/update-utmp/update-utmp.c ++++ b/src/update-utmp/update-utmp.c +@@ -53,6 +53,12 @@ static int get_startup_monotonic_time(Context *c, usec_t *ret) { + assert(c); + assert(ret); + ++ if (!c->bus) { ++ r = bus_connect_system_systemd(&c->bus); ++ if (r < 0) ++ return log_warning_errno(r, "Failed to get D-Bus connection, ignoring: %m"); ++ } ++ + r = bus_get_property_trivial( + c->bus, + bus_systemd_mgr, +@@ -94,10 +100,13 @@ static int get_current_runlevel(Context *c) { + UINT64_C(100) * USEC_PER_MSEC + + random_u64_range(UINT64_C(1900) * USEC_PER_MSEC * n_attempts / MAX_ATTEMPTS); + (void) usleep_safe(usec); ++ } + ++ if (!c->bus) { + r = bus_connect_system_systemd(&c->bus); + if (r == -ECONNREFUSED && n_attempts < 64) { +- log_debug_errno(r, "Failed to reconnect to system bus, retrying after a slight delay: %m"); ++ log_debug_errno(r, "Failed to %s to system bus, retrying after a slight delay: %m", ++ n_attempts <= 1 ? "connect" : "reconnect"); + continue; + } + if (r < 0) +@@ -251,7 +260,6 @@ static int run(int argc, char *argv[]) { + .audit_fd = -EBADF, + #endif + }; +- int r; + + log_setup(); + +@@ -264,9 +272,6 @@ static int run(int argc, char *argv[]) { + log_full_errno(IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT) ? LOG_DEBUG : LOG_WARNING, + errno, "Failed to connect to audit log, ignoring: %m"); + #endif +- r = bus_connect_system_systemd(&c.bus); +- if (r < 0) +- return log_error_errno(r, "Failed to get D-Bus connection: %m"); + + return dispatch_verb(argc, argv, verbs, &c); + } +-- +2.47.1 + diff --git a/0002-sysusers-emit-audit-events-for-user-and-group-creati.patch b/0002-sysusers-emit-audit-events-for-user-and-group-creati.patch new file mode 100644 index 0000000..d442f5a --- /dev/null +++ b/0002-sysusers-emit-audit-events-for-user-and-group-creati.patch @@ -0,0 +1,287 @@ +From 398049208b4aae5f2a9f0d4914dee6ab6e101118 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 10 Jan 2025 15:35:13 +0100 +Subject: [PATCH 2/2] sysusers: emit audit events for user and group creation + +Background: Fedora/RHEL are switching to sysusers.d metadata for creation of +users and groups for system users defined by packages +(https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers). +Packages carry sysusers files. During package installation, rpm calls an +program to execute on this config. This program may either be +/usr/lib/rpm/sysusers.sh which calls useradd/groupadd, or +/usr/bin/systemd-sysusers. To match the functionality provided by +useradd/groupadd from the shadow-utils project, systemd-sysusers must emit +audit events so that it provides a drop-in replacement. + +systemd-sysuers will emit audit events AUDIT_ADD_USER/AUDIT_ADD_GROUP when +adding users and groups. The operation "names" are copied from shadow-utils in +Fedora (which has a patch to change them from the upstream version), so the +format of the events that is generated on success should be identical. + +The helper code is shared between sysusers and utmp-wtmp. I changed the +audit_fd variable to be unconditional. This way we can avoid ugly iffdefery +every time the variable would be used. The cost is that 4 bytes of unused +storage might be present. This is negligible, and the compiler might even be +able to optimize that away if it inlines things. +--- + src/basic/audit-util.h | 33 +++++++++++++++++++++ + src/sysusers/meson.build | 2 ++ + src/sysusers/sysusers.c | 56 +++++++++++++++++++++++++++++++++++ + src/update-utmp/update-utmp.c | 23 ++------------ + 4 files changed, 94 insertions(+), 20 deletions(-) + +diff --git a/src/basic/audit-util.h b/src/basic/audit-util.h +index 9a74e4f102..d8ecf14f69 100644 +--- a/src/basic/audit-util.h ++++ b/src/basic/audit-util.h +@@ -1,10 +1,16 @@ + /* SPDX-License-Identifier: LGPL-2.1-or-later */ + #pragma once + ++#if HAVE_AUDIT ++# include ++#endif ++ + #include + #include + #include + ++#include "errno-util.h" ++#include "log.h" + #include "pidref.h" + + #define AUDIT_SESSION_INVALID UINT32_MAX +@@ -17,3 +23,30 @@ bool use_audit(void); + static inline bool audit_session_is_valid(uint32_t id) { + return id > 0 && id != AUDIT_SESSION_INVALID; + } ++ ++/* The wrappers for audit_open() and audit_close() are inline functions so that we don't get a spurious ++ * linkage to libaudit in libbasic, but we also don't need to create a separate source file for two very ++ * short functions. */ ++ ++static inline int close_audit_fd(int fd) { ++#if HAVE_AUDIT ++ if (fd >= 0) ++ audit_close(fd); ++#else ++ assert(fd < 0); ++#endif ++ return -EBADF; ++} ++ ++static inline int open_audit_fd_or_warn(void) { ++ int fd = -EBADF; ++ ++#if HAVE_AUDIT ++ /* If the kernel lacks netlink or audit support, don't worry about it. */ ++ fd = audit_open(); ++ if (fd < 0) ++ return log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, ++ errno, "Failed to connect to audit log, ignoring: %m"); ++#endif ++ return fd; ++} +diff --git a/src/sysusers/meson.build b/src/sysusers/meson.build +index 123ff41d3f..c968f55110 100644 +--- a/src/sysusers/meson.build ++++ b/src/sysusers/meson.build +@@ -9,6 +9,7 @@ executables += [ + 'name' : 'systemd-sysusers', + 'public' : true, + 'sources' : files('sysusers.c'), ++ 'dependencies' : libaudit, + }, + executable_template + { + 'name' : 'systemd-sysusers.standalone', +@@ -20,6 +21,7 @@ executables += [ + libshared_static, + libsystemd_static, + ], ++ 'dependencies' : libaudit, + 'build_by_default' : have_standalone_binaries, + 'install' : have_standalone_binaries, + }, +diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c +index 44253483db..84eb9fc0c3 100644 +--- a/src/sysusers/sysusers.c ++++ b/src/sysusers/sysusers.c +@@ -3,6 +3,7 @@ + #include + + #include "alloc-util.h" ++#include "audit-util.h" + #include "build.h" + #include "chase.h" + #include "conf-files.h" +@@ -106,6 +107,8 @@ STATIC_DESTRUCTOR_REGISTER(arg_image, freep); + STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); + + typedef struct Context { ++ int audit_fd; ++ + OrderedHashmap *users, *groups; + OrderedHashmap *todo_uids, *todo_gids; + OrderedHashmap *members; +@@ -126,6 +129,8 @@ typedef struct Context { + static void context_done(Context *c) { + assert(c); + ++ c->audit_fd = close_audit_fd(c->audit_fd); ++ + ordered_hashmap_free(c->groups); + ordered_hashmap_free(c->users); + ordered_hashmap_free(c->members); +@@ -163,6 +168,48 @@ static void maybe_emit_login_defs_warning(Context *c) { + c->login_defs_need_warning = false; + } + ++static void log_audit_accounts(Context *c, ItemType what) { ++#if HAVE_AUDIT ++ assert(c); ++ assert(IN_SET(what, ADD_USER, ADD_GROUP)); ++ ++ if (arg_dry_run || c->audit_fd < 0) ++ return; ++ ++ Item *i; ++ int type = what == ADD_USER ? AUDIT_ADD_USER : AUDIT_ADD_GROUP; ++ const char *op = what == ADD_USER ? "adding-user" : "adding-group"; ++ ++ /* Notes: ++ * ++ * The op must not contain whitespace. The format with a dash matches what Fedora shadow-utils uses. ++ * ++ * We send id == -1, even though we know the number, in particular on success. This is because if we ++ * send the id, the generated audit message will not contain the name. The name seems more useful ++ * than the number, hence send just the name: ++ * ++ * type=ADD_USER msg=audit(01/10/2025 16:02:00.639:3854) : ++ * pid=3846380 uid=root auid=zbyszek ses=2 msg='op=adding-user id=unknown(952) exe=systemd-sysusers ... res=success' ++ * vs. ++ * type=ADD_USER msg=audit(01/10/2025 16:03:15.457:3908) : ++ * pid=3846607 uid=root auid=zbyszek ses=2 msg='op=adding-user acct=foo5 exe=systemd-sysusers ... res=success' ++ */ ++ ++ ORDERED_HASHMAP_FOREACH(i, what == ADD_USER ? c->todo_uids : c->todo_gids) ++ audit_log_acct_message( ++ c->audit_fd, ++ type, ++ program_invocation_short_name, ++ op, ++ i->name, ++ /* id= */ (unsigned) -1, ++ /* host= */ NULL, ++ /* addr= */ NULL, ++ /* tty= */ NULL, ++ /* success= */ 1); ++#endif ++} ++ + static int load_user_database(Context *c) { + _cleanup_fclose_ FILE *f = NULL; + const char *passwd_path; +@@ -971,6 +1018,8 @@ static int write_files(Context *c) { + group_tmp, group_path); + group_tmp = mfree(group_tmp); + } ++ /* OK, we have written the group entries successfully */ ++ log_audit_accounts(c, ADD_GROUP); + if (gshadow) { + r = rename_and_apply_smack_floor_label(gshadow_tmp, gshadow_path); + if (r < 0) +@@ -988,6 +1037,8 @@ static int write_files(Context *c) { + + passwd_tmp = mfree(passwd_tmp); + } ++ /* OK, we have written the user entries successfully */ ++ log_audit_accounts(c, ADD_USER); + if (shadow) { + r = rename_and_apply_smack_floor_label(shadow_tmp, shadow_path); + if (r < 0) +@@ -2232,6 +2283,7 @@ static int run(int argc, char *argv[]) { + #endif + _cleanup_close_ int lock = -EBADF; + _cleanup_(context_done) Context c = { ++ .audit_fd = -EBADF, + .search_uid = UID_INVALID, + }; + +@@ -2281,6 +2333,10 @@ static int run(int argc, char *argv[]) { + assert(!arg_image); + #endif + ++ /* Prepare to emit audit events, but only if we're operating on the host system. */ ++ if (!arg_root) ++ c.audit_fd = open_audit_fd_or_warn(); ++ + /* If command line arguments are specified along with --replace, read all configuration files and + * insert the positional arguments at the specified place. Otherwise, if command line arguments are + * specified, execute just them, and finally, without --replace= or any positional arguments, just +diff --git a/src/update-utmp/update-utmp.c b/src/update-utmp/update-utmp.c +index a10e6d478a..6df9414063 100644 +--- a/src/update-utmp/update-utmp.c ++++ b/src/update-utmp/update-utmp.c +@@ -5,12 +5,9 @@ + #include + #include + +-#if HAVE_AUDIT +-#include +-#endif +- + #include "sd-bus.h" + ++#include "audit-util.h" + #include "alloc-util.h" + #include "bus-error.h" + #include "bus-locator.h" +@@ -30,20 +27,14 @@ + + typedef struct Context { + sd_bus *bus; +-#if HAVE_AUDIT + int audit_fd; +-#endif + } Context; + + static void context_clear(Context *c) { + assert(c); + + c->bus = sd_bus_flush_close_unref(c->bus); +-#if HAVE_AUDIT +- if (c->audit_fd >= 0) +- audit_close(c->audit_fd); +- c->audit_fd = -EBADF; +-#endif ++ c->audit_fd = close_audit_fd(c->audit_fd); + } + + static int get_startup_monotonic_time(Context *c, usec_t *ret) { +@@ -256,22 +247,14 @@ static int run(int argc, char *argv[]) { + }; + + _cleanup_(context_clear) Context c = { +-#if HAVE_AUDIT + .audit_fd = -EBADF, +-#endif + }; + + log_setup(); + + umask(0022); + +-#if HAVE_AUDIT +- /* If the kernel lacks netlink or audit support, don't worry about it. */ +- c.audit_fd = audit_open(); +- if (c.audit_fd < 0) +- log_full_errno(IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT) ? LOG_DEBUG : LOG_WARNING, +- errno, "Failed to connect to audit log, ignoring: %m"); +-#endif ++ c.audit_fd = open_audit_fd_or_warn(); + + return dispatch_verb(argc, argv, verbs, &c); + } +-- +2.47.1 + diff --git a/systemd.spec b/systemd.spec index c513a1c..8b7735e 100644 --- a/systemd.spec +++ b/systemd.spec @@ -118,6 +118,11 @@ Patch: https://github.com/systemd/systemd/pull/26494.patch # https://github.com/coreos/fedora-coreos-tracker/issues/1857 Patch: 0001-Revert-units-use-PrivateTmp-disconnected-instead-of-.patch +# Backport of sysusers audit support for +# https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers. +Patch: 0001-update-utmp-do-not-give-up-if-the-first-attempt-at-c.patch +Patch: 0002-sysusers-emit-audit-events-for-user-and-group-creati.patch + # Those are downstream-only patches, but we don't want them in packit builds: # https://bugzilla.redhat.com/show_bug.cgi?id=2251843 Patch: https://github.com/systemd/systemd/pull/30846.patch