From: Simon de Vlieger Date: Sat, 4 Jul 2026 11:10:21 +0000 (+0200) Subject: selinux: wire up LabelContext in tmpfiles, firstboot, sysusers X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d652f6dc44bc960bb3583987a186d72a92e67d23;p=thirdparty%2Fsystemd.git selinux: wire up LabelContext in tmpfiles, firstboot, sysusers These three one-shot tools operate on alternate roots via --root/--image but until now created files with host SELinux labels, producing images that fail to boot or run with enforcing mode because every file carries the wrong security context. Create a LabelContext from arg_root at startup and thread it through all labeling call sites so the target image gets labeled according to its own policy. Signed-off-by: Simon de Vlieger --- diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index ed6817a186e..a76b4c755e4 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -94,6 +94,7 @@ static bool arg_reset = false; static ImagePolicy *arg_image_policy = NULL; static bool arg_chrome = true; static bool arg_mute_console = false; +static LabelContext *arg_label_context = NULL; STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep); @@ -107,6 +108,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_root_password, erase_and_freep); STATIC_DESTRUCTOR_REGISTER(arg_root_shell, freep); STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline, freep); STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); +STATIC_DESTRUCTOR_REGISTER(arg_label_context, mac_label_context_freep); static bool welcome_done = false; @@ -408,12 +410,13 @@ static int process_locale(int rfd, sd_varlink **mute_console_link) { locales[i] = NULL; - r = write_env_file( + r = write_env_file_label( pfd, f, /* headers= */ NULL, locales, - WRITE_ENV_FILE_LABEL); + WRITE_ENV_FILE_LABEL, + arg_label_context); if (r < 0) return log_error_errno(r, "Failed to write /etc/locale.conf: %m"); @@ -653,7 +656,7 @@ static int process_timezone(int rfd, sd_varlink **mute_console_link) { if (r < 0) return log_error_errno(r, "Failed to read host's /etc/localtime: %m"); - r = symlinkat_atomic_full(s, pfd, f, SYMLINK_LABEL); + r = symlinkat_atomic_full_label(s, pfd, f, SYMLINK_LABEL, arg_label_context); if (r < 0) return log_error_errno(r, "Failed to create /etc/localtime symlink: %m"); @@ -674,7 +677,7 @@ static int process_timezone(int rfd, sd_varlink **mute_console_link) { if (r < 0) return r; - r = symlinkat_atomic_full(relpath, pfd, f, SYMLINK_LABEL); + r = symlinkat_atomic_full_label(relpath, pfd, f, SYMLINK_LABEL, arg_label_context); if (r < 0) return log_error_errno(r, "Failed to create /etc/localtime symlink: %m"); @@ -779,8 +782,9 @@ static int process_hostname(int rfd, sd_varlink **mute_console_link) { hostname = resolved; } - r = write_string_file_at(pfd, f, hostname, - WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL); + r = write_string_file_full_label(pfd, f, hostname, + WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL, + /* ts= */ NULL, /* label_fn= */ NULL, arg_label_context); if (r < 0) return log_error_errno(r, "Failed to write /etc/hostname: %m"); @@ -812,8 +816,9 @@ static int process_machine_id(int rfd) { return 0; } - r = write_string_file_at(pfd, "machine-id", SD_ID128_TO_STRING(arg_machine_id), - WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL); + r = write_string_file_full_label(pfd, "machine-id", SD_ID128_TO_STRING(arg_machine_id), + WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL, + /* ts= */ NULL, /* label_fn= */ NULL, arg_label_context); if (r < 0) return log_error_errno(r, "Failed to write /etc/machine-id: %m"); @@ -874,11 +879,12 @@ static int process_machine_tags(int rfd) { if (!c) return log_oom(); - r = write_string_file_at( + r = write_string_file_full_label( pfd, "machine-info", c, - WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL); + WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL, + /* ts= */ NULL, /* label_fn= */ NULL, arg_label_context); if (r < 0) return log_error_errno(r, "Failed to write /etc/machine-info: %m"); @@ -1038,7 +1044,7 @@ static int write_root_passwd(int rfd, int etc_fd, const char *password, const ch int r; bool found = false; - r = fopen_temporary_at_label(etc_fd, "passwd", "passwd", &passwd, &passwd_tmp, /* label_context= */ NULL); + r = fopen_temporary_at_label(etc_fd, "passwd", "passwd", &passwd, &passwd_tmp, arg_label_context); if (r < 0) return r; @@ -1109,7 +1115,7 @@ static int write_root_shadow(int etc_fd, const char *hashed_password) { int r; bool found = false; - r = fopen_temporary_at_label(etc_fd, "shadow", "shadow", &shadow, &shadow_tmp, /* label_context= */ NULL); + r = fopen_temporary_at_label(etc_fd, "shadow", "shadow", &shadow, &shadow_tmp, arg_label_context); if (r < 0) return r; @@ -1319,8 +1325,9 @@ static int process_kernel_cmdline(int rfd) { return 0; } - r = write_string_file_at(pfd, "cmdline", arg_kernel_cmdline, - WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL); + r = write_string_file_full_label(pfd, "cmdline", arg_kernel_cmdline, + WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL, + /* ts= */ NULL, /* label_fn= */ NULL, arg_label_context); if (r < 0) return log_error_errno(r, "Failed to write /etc/kernel/cmdline: %m"); @@ -1777,6 +1784,10 @@ static int run(int argc, char *argv[]) { return log_error_errno(errno, "Failed to open %s: %m", empty_to_root(arg_root)); } + r = mac_label_context_new(arg_root, &arg_label_context); + if (r < 0) + return log_error_errno(r, "Failed to initialize label context for root '%s': %m", arg_root); + LOG_SET_PREFIX(arg_image ?: arg_root); DEFER_VOID_CALL(end_marker); DEFER_VOID_CALL(chrome_hide); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 9c52bce66bd..56f3dc3953a 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -132,6 +132,8 @@ typedef struct Context { UGIDAllocationRange login_defs; bool login_defs_need_warning; + + LabelContext *label_context; } Context; static void context_done(Context *c) { @@ -152,6 +154,7 @@ static void context_done(Context *c) { set_free(c->names); uid_range_free(c->uid_range); + mac_label_context_free(c->label_context); } static void maybe_emit_login_defs_warning(Context *c) { @@ -303,7 +306,7 @@ static int load_group_database(Context *c) { return r; } -static int make_backup(const char *target, const char *x) { +static int make_backup(const char *target, const char *x, LabelContext *label_context) { _cleanup_(unlink_and_freep) char *dst_tmp = NULL; _cleanup_fclose_ FILE *dst = NULL; _cleanup_close_ int src = -EBADF; @@ -325,11 +328,13 @@ static int make_backup(const char *target, const char *x) { if (fstat(src, &st) < 0) return -errno; - r = fopen_temporary_label( + r = fopen_temporary_at_label( + AT_FDCWD, target, /* The path for which to the look up the label */ x, /* Where we want the file actually to end up */ &dst, /* The temporary file we write to */ - &dst_tmp); + &dst_tmp, + label_context); if (r < 0) return r; @@ -527,7 +532,7 @@ static int write_temporary_passwd( goto done; } - r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp); + r = fopen_temporary_at_label(AT_FDCWD, passwd_path, passwd_path, &passwd, &passwd_tmp, c->label_context); if (r < 0) return log_debug_errno(r, "Failed to open temporary copy of %s: %m", passwd_path); @@ -655,7 +660,7 @@ static int write_temporary_shadow( goto done; } - r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp); + r = fopen_temporary_at_label(AT_FDCWD, shadow_path, shadow_path, &shadow, &shadow_tmp, c->label_context); if (r < 0) return log_debug_errno(r, "Failed to open temporary copy of %s: %m", shadow_path); @@ -792,7 +797,7 @@ static int write_temporary_group( goto done; } - r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp); + r = fopen_temporary_at_label(AT_FDCWD, group_path, group_path, &group, &group_tmp, c->label_context); if (r < 0) return log_error_errno(r, "Failed to open temporary copy of %s: %m", group_path); @@ -911,7 +916,7 @@ static int write_temporary_gshadow( goto done; } - r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp); + r = fopen_temporary_at_label(AT_FDCWD, gshadow_path, gshadow_path, &gshadow, &gshadow_tmp, c->label_context); if (r < 0) return log_error_errno(r, "Failed to open temporary copy of %s: %m", gshadow_path); @@ -1021,23 +1026,23 @@ static int write_files(Context *c) { /* Make a backup of the old files */ if (group) { - r = make_backup("/etc/group", group_path); + r = make_backup(group_path, group_path, c->label_context); if (r < 0) return log_error_errno(r, "Failed to backup %s: %m", group_path); } if (gshadow) { - r = make_backup("/etc/gshadow", gshadow_path); + r = make_backup(gshadow_path, gshadow_path, c->label_context); if (r < 0) return log_error_errno(r, "Failed to backup %s: %m", gshadow_path); } if (passwd) { - r = make_backup("/etc/passwd", passwd_path); + r = make_backup(passwd_path, passwd_path, c->label_context); if (r < 0) return log_error_errno(r, "Failed to backup %s: %m", passwd_path); } if (shadow) { - r = make_backup("/etc/shadow", shadow_path); + r = make_backup(shadow_path, shadow_path, c->label_context); if (r < 0) return log_error_errno(r, "Failed to backup %s: %m", shadow_path); } @@ -2351,6 +2356,10 @@ static int run(int argc, char *argv[]) { return log_oom(); } + r = mac_label_context_new(arg_root, &c.label_context); + if (r < 0) + return log_error_errno(r, "Failed to initialize label context for root '%s': %m", arg_root); + /* 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(); diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 756c1458e36..d1d7c42ffaf 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -232,6 +232,7 @@ static char *arg_root = NULL; static char *arg_image = NULL; static const char *arg_replace = NULL; static ImagePolicy *arg_image_policy = NULL; +static LabelContext *arg_label_context = NULL; #define MAX_DEPTH 256 @@ -248,6 +249,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_exclude_prefixes, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); +STATIC_DESTRUCTOR_REGISTER(arg_label_context, mac_label_context_freep); static const char *const creation_mode_verb_table[_CREATION_MODE_MAX] = { [CREATION_NORMAL] = "Created", @@ -1062,7 +1064,7 @@ shortcut: } log_debug("Relabelling \"%s\"", path); - return label_fix_full(fd, /* inode_path= */ NULL, /* label_path= */ path, /* flags= */ 0, /* label_context= */ NULL); + return label_fix_full(fd, /* inode_path= */ NULL, /* label_path= */ path, /* flags= */ 0, arg_label_context); } static int path_open_parent_safe(const char *path, bool allow_failure) { @@ -2070,7 +2072,7 @@ static int create_file( return dir_fd; WITH_UMASK(0000) { - mac_selinux_create_file_prepare(path, S_IFREG, /* label_context= */ NULL); + mac_selinux_create_file_prepare(path, S_IFREG, arg_label_context); fd = RET_NERRNO(openat(dir_fd, bn, O_CREAT|O_EXCL|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode)); mac_selinux_create_file_clear(); } @@ -2153,7 +2155,7 @@ static int truncate_file( creation = CREATION_NORMAL; /* Didn't work without O_CREATE, try again with */ WITH_UMASK(0000) { - mac_selinux_create_file_prepare(path, S_IFREG, /* label_context= */ NULL); + mac_selinux_create_file_prepare(path, S_IFREG, arg_label_context); fd = RET_NERRNO(openat(dir_fd, bn, O_CREAT|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode)); mac_selinux_create_file_clear(); } @@ -2314,7 +2316,7 @@ static int create_directory_or_subvolume( log_action("Would create", "Creating", "%s directory \"%s\"", path); if (!arg_dry_run) WITH_UMASK(0000) - r = mkdirat_label(pfd, bn, mode, /* label_context= */ NULL); + r = mkdirat_label(pfd, bn, mode, arg_label_context); } if (arg_dry_run) @@ -2501,7 +2503,7 @@ static int create_device( return dfd; WITH_UMASK(0000) { - mac_selinux_create_file_prepare(i->path, file_type, /* label_context= */ NULL); + mac_selinux_create_file_prepare(i->path, file_type, arg_label_context); r = RET_NERRNO(mknodat(dfd, bn, i->mode | file_type, i->major_minor)); mac_selinux_create_file_clear(); } @@ -2532,7 +2534,7 @@ static int create_device( fd = safe_close(fd); WITH_UMASK(0000) { - mac_selinux_create_file_prepare(i->path, file_type, /* label_context= */ NULL); + mac_selinux_create_file_prepare(i->path, file_type, arg_label_context); r = mknodat_atomic(dfd, bn, i->mode | file_type, i->major_minor); mac_selinux_create_file_clear(); } @@ -2543,7 +2545,7 @@ static int create_device( if (r < 0) return log_error_errno(r, "rm -rf %s failed: %m", i->path); - mac_selinux_create_file_prepare(i->path, file_type, /* label_context= */ NULL); + mac_selinux_create_file_prepare(i->path, file_type, arg_label_context); r = RET_NERRNO(mknodat(dfd, bn, i->mode | file_type, i->major_minor)); mac_selinux_create_file_clear(); } @@ -2611,7 +2613,7 @@ static int create_fifo(Context *c, Item *i) { return pfd; WITH_UMASK(0000) { - mac_selinux_create_file_prepare(i->path, S_IFIFO, /* label_context= */ NULL); + mac_selinux_create_file_prepare(i->path, S_IFIFO, arg_label_context); r = RET_NERRNO(mkfifoat(pfd, bn, i->mode)); mac_selinux_create_file_clear(); } @@ -2636,7 +2638,7 @@ static int create_fifo(Context *c, Item *i) { fd = safe_close(fd); WITH_UMASK(0000) { - mac_selinux_create_file_prepare(i->path, S_IFIFO, /* label_context= */ NULL); + mac_selinux_create_file_prepare(i->path, S_IFIFO, arg_label_context); r = mkfifoat_atomic(pfd, bn, i->mode); mac_selinux_create_file_clear(); } @@ -2645,7 +2647,7 @@ static int create_fifo(Context *c, Item *i) { if (r < 0) return log_error_errno(r, "rm -rf %s failed: %m", i->path); - mac_selinux_create_file_prepare(i->path, S_IFIFO, /* label_context= */ NULL); + mac_selinux_create_file_prepare(i->path, S_IFIFO, arg_label_context); r = RET_NERRNO(mkfifoat(pfd, bn, i->mode)); mac_selinux_create_file_clear(); } @@ -2730,7 +2732,7 @@ static int create_symlink(Context *c, Item *i) { if (pfd < 0) return pfd; - mac_selinux_create_file_prepare(i->path, S_IFLNK, /* label_context= */ NULL); + mac_selinux_create_file_prepare(i->path, S_IFLNK, arg_label_context); r = RET_NERRNO(symlinkat(i->argument, pfd, bn)); mac_selinux_create_file_clear(); @@ -2766,13 +2768,13 @@ static int create_symlink(Context *c, Item *i) { fd = safe_close(fd); - r = symlinkat_atomic_full(i->argument, pfd, bn, SYMLINK_LABEL); + r = symlinkat_atomic_full_label(i->argument, pfd, bn, SYMLINK_LABEL, arg_label_context); if (IN_SET(r, -EISDIR, -EEXIST, -ENOTEMPTY)) { r = rm_rf_child(pfd, bn, REMOVE_PHYSICAL); if (r < 0) return log_error_errno(r, "rm -rf %s failed: %m", i->path); - r = symlinkat_atomic_full(i->argument, pfd, bn, SYMLINK_LABEL); + r = symlinkat_atomic_full_label(i->argument, pfd, bn, SYMLINK_LABEL, arg_label_context); } if (r < 0) return log_error_errno(r, "symlink(%s, %s) failed: %m", i->argument, i->path); @@ -3060,7 +3062,7 @@ static int mkdir_parents_rm_if_wrong_type(mode_t child_mode, const char *path) { if (r == -ENOENT) { if (!arg_dry_run) { WITH_UMASK(0000) - r = mkdirat_label(parent_fd, t, 0755, /* label_context= */ NULL); + r = mkdirat_label(parent_fd, t, 0755, arg_label_context); if (r < 0) { _cleanup_free_ char *parent_name = NULL; @@ -3101,7 +3103,7 @@ static int mkdir_parents_item(Item *i, mode_t child_mode) { } else WITH_UMASK(0000) if (!arg_dry_run) - (void) mkdir_parents_label(i->path, 0755); + (void) mkdirat_parents_label(AT_FDCWD, i->path, 0755, arg_label_context); return 0; } @@ -4967,6 +4969,10 @@ static int run(int argc, char *argv[]) { return log_oom(); } + r = mac_label_context_new(arg_root, &arg_label_context); + if (r < 0) + return log_error_errno(r, "Failed to initialize label context for root '%s': %m", arg_root); + c.items = ordered_hashmap_new(&item_array_hash_ops); c.globs = ordered_hashmap_new(&item_array_hash_ops); if (!c.items || !c.globs)