From 1fbc95d3881390e0e86af07e9c9fece32377535d Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 21 May 2020 20:31:55 +0200 Subject: [PATCH 1/9] firstboot: Don't check twice if /etc/shadow exists --- src/firstboot/firstboot.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index f9fdf7b1d7..760014588e 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -533,7 +533,7 @@ static int process_machine_id(void) { } static int prompt_root_password(void) { - const char *msg1, *msg2, *etc_shadow; + const char *msg1, *msg2; int r; if (arg_root_password) @@ -542,10 +542,6 @@ static int prompt_root_password(void) { if (!arg_prompt_root_password) return 0; - etc_shadow = prefix_roota(arg_root, "/etc/shadow"); - if (laccess(etc_shadow, F_OK) >= 0) - return 0; - print_welcome(); putchar('\n'); From 494735f3d067bdeb97a5a98d09f9e2c4af705272 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 22 May 2020 20:30:24 +0200 Subject: [PATCH 2/9] sysusers: Move sync_rights and rename_and_apply_smack to basic --- src/basic/fileio.c | 22 ++++++++++++++++++++++ src/basic/fileio.h | 4 ++++ src/sysusers/sysusers.c | 22 ---------------------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 00dce02064..ccdbfe895f 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -1187,3 +1187,25 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c filename, st->st_mode & 07777); return 0; } + +int sync_rights(FILE *from, const char *to) { + struct stat st; + + if (fstat(fileno(from), &st) < 0) + return -errno; + + return chmod_and_chown_unsafe(to, st.st_mode & 07777, st.st_uid, st.st_gid); +} + +int rename_and_apply_smack(const char *from, const char *to) { + int r = 0; + if (rename(from, to) < 0) + return -errno; + +#ifdef SMACK_RUN_LABEL + r = mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL); + if (r < 0) + return r; +#endif + return r; +} diff --git a/src/basic/fileio.h b/src/basic/fileio.h index e6062121a3..9dc3f18796 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -106,3 +106,7 @@ static inline int read_nul_string(FILE *f, size_t limit, char **ret) { int safe_fgetc(FILE *f, char *ret); int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line); + +int sync_rights(FILE *from, const char *to); + +int rename_and_apply_smack(const char *temp_path, const char *dest_path); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 847aebc516..3d68dc2e0f 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -345,28 +345,6 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) { } #endif -static int sync_rights(FILE *from, const char *to) { - struct stat st; - - if (fstat(fileno(from), &st) < 0) - return -errno; - - return chmod_and_chown_unsafe(to, st.st_mode & 07777, st.st_uid, st.st_gid); -} - -static int rename_and_apply_smack(const char *temp_path, const char *dest_path) { - int r = 0; - if (rename(temp_path, dest_path) < 0) - return -errno; - -#ifdef SMACK_RUN_LABEL - r = mac_smack_apply(dest_path, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL); - if (r < 0) - return r; -#endif - return r; -} - static const char* default_shell(uid_t uid) { return uid == 0 ? "/bin/sh" : NOLOGIN; } From 90c81688fffde5b1d7bf9fa972db145096fae010 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 26 May 2020 20:00:45 +0200 Subject: [PATCH 3/9] fileio: Refactor sync_rights to take fds as arguments --- src/basic/fileio.c | 6 +++--- src/basic/fileio.h | 2 +- src/sysusers/sysusers.c | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/basic/fileio.c b/src/basic/fileio.c index ccdbfe895f..6abe5417a6 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -1188,13 +1188,13 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c return 0; } -int sync_rights(FILE *from, const char *to) { +int sync_rights(int from, int to) { struct stat st; - if (fstat(fileno(from), &st) < 0) + if (fstat(from, &st) < 0) return -errno; - return chmod_and_chown_unsafe(to, st.st_mode & 07777, st.st_uid, st.st_gid); + return fchmod_and_chown(to, st.st_mode & 07777, st.st_uid, st.st_gid); } int rename_and_apply_smack(const char *from, const char *to) { diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 9dc3f18796..03678d827a 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -107,6 +107,6 @@ int safe_fgetc(FILE *f, char *ret); int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line); -int sync_rights(FILE *from, const char *to); +int sync_rights(int from, int to); int rename_and_apply_smack(const char *temp_path, const char *dest_path); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 3d68dc2e0f..2cb8172103 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -367,7 +367,7 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char original = fopen(passwd_path, "re"); if (original) { - r = sync_rights(original, passwd_tmp); + r = sync_rights(fileno(original), fileno(passwd)); if (r < 0) return r; @@ -469,7 +469,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char original = fopen(shadow_path, "re"); if (original) { - r = sync_rights(original, shadow_tmp); + r = sync_rights(fileno(original), fileno(shadow)); if (r < 0) return r; @@ -566,7 +566,7 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char ** original = fopen(group_path, "re"); if (original) { - r = sync_rights(original, group_tmp); + r = sync_rights(fileno(original), fileno(group)); if (r < 0) return r; @@ -665,7 +665,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch if (original) { struct sgrp *sg; - r = sync_rights(original, gshadow_tmp); + r = sync_rights(fileno(original), fileno(gshadow)); if (r < 0) return r; From 2da3dc69e7e11e9d4fe03c1b33c6a99f74405a6c Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 26 May 2020 19:49:26 +0200 Subject: [PATCH 4/9] fileio: Rename rename_and_apply_smack to rename_and_apply_smack_floor_label. --- src/basic/fileio.c | 2 +- src/basic/fileio.h | 2 +- src/sysusers/sysusers.c | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 6abe5417a6..f2f1e1139f 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -1197,7 +1197,7 @@ int sync_rights(int from, int to) { return fchmod_and_chown(to, st.st_mode & 07777, st.st_uid, st.st_gid); } -int rename_and_apply_smack(const char *from, const char *to) { +int rename_and_apply_smack_floor_label(const char *from, const char *to) { int r = 0; if (rename(from, to) < 0) return -errno; diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 03678d827a..e2830b7963 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -109,4 +109,4 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c int sync_rights(int from, int to); -int rename_and_apply_smack(const char *temp_path, const char *dest_path); +int rename_and_apply_smack_floor_label(const char *temp_path, const char *dest_path); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 2cb8172103..6fbd5985d3 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -772,14 +772,14 @@ static int write_files(void) { /* And make the new files count */ if (group) { - r = rename_and_apply_smack(group_tmp, group_path); + r = rename_and_apply_smack_floor_label(group_tmp, group_path); if (r < 0) return r; group_tmp = mfree(group_tmp); } if (gshadow) { - r = rename_and_apply_smack(gshadow_tmp, gshadow_path); + r = rename_and_apply_smack_floor_label(gshadow_tmp, gshadow_path); if (r < 0) return r; @@ -787,14 +787,14 @@ static int write_files(void) { } if (passwd) { - r = rename_and_apply_smack(passwd_tmp, passwd_path); + r = rename_and_apply_smack_floor_label(passwd_tmp, passwd_path); if (r < 0) return r; passwd_tmp = mfree(passwd_tmp); } if (shadow) { - r = rename_and_apply_smack(shadow_tmp, shadow_path); + r = rename_and_apply_smack_floor_label(shadow_tmp, shadow_path); if (r < 0) return r; From b4909a3fd0ea5556e50c1f4704cbd8d3a2a99667 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 22 May 2020 20:31:16 +0200 Subject: [PATCH 5/9] firstboot: Add --force option --- man/systemd-firstboot.xml | 9 +++ src/firstboot/firstboot.c | 121 ++++++++++++++++++++++++++------------ 2 files changed, 93 insertions(+), 37 deletions(-) diff --git a/man/systemd-firstboot.xml b/man/systemd-firstboot.xml index 0eb212341f..552268ea52 100644 --- a/man/systemd-firstboot.xml +++ b/man/systemd-firstboot.xml @@ -221,6 +221,15 @@ . + + + + systemd-firstboot doesn't modify existing files unless + is specified. For modifications to /etc/passwd and + /etc/shadow, systemd-firstboot only modifies the entry of the + root user instead of overwriting the entire file. + + diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 760014588e..ff275b15df 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -30,6 +30,7 @@ #include "strv.h" #include "terminal-util.h" #include "time-util.h" +#include "tmpfile-util-label.h" #include "umask-util.h" #include "user-util.h" @@ -50,6 +51,7 @@ static bool arg_copy_locale = false; static bool arg_copy_keymap = false; static bool arg_copy_timezone = false; static bool arg_copy_root_password = false; +static bool arg_force = false; STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_locale, freep); @@ -273,7 +275,7 @@ static int process_locale(void) { int r; etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf"); - if (laccess(etc_localeconf, F_OK) >= 0) + if (laccess(etc_localeconf, F_OK) >= 0 && !arg_force) return 0; if (arg_copy_locale && arg_root) { @@ -340,7 +342,7 @@ static int process_keymap(void) { int r; etc_vconsoleconf = prefix_roota(arg_root, "/etc/vconsole.conf"); - if (laccess(etc_vconsoleconf, F_OK) >= 0) + if (laccess(etc_vconsoleconf, F_OK) >= 0 && !arg_force) return 0; if (arg_copy_keymap && arg_root) { @@ -412,7 +414,7 @@ static int process_timezone(void) { int r; etc_localtime = prefix_roota(arg_root, "/etc/localtime"); - if (laccess(etc_localtime, F_OK) >= 0) + if (laccess(etc_localtime, F_OK) >= 0 && !arg_force) return 0; if (arg_copy_timezone && arg_root) { @@ -492,7 +494,7 @@ static int process_hostname(void) { int r; etc_hostname = prefix_roota(arg_root, "/etc/hostname"); - if (laccess(etc_hostname, F_OK) >= 0) + if (laccess(etc_hostname, F_OK) >= 0 && !arg_force) return 0; r = prompt_hostname(); @@ -503,7 +505,8 @@ static int process_hostname(void) { return 0; r = write_string_file(etc_hostname, arg_hostname, - WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755); + WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 | + (arg_force ? WRITE_STRING_FILE_ATOMIC : 0)); if (r < 0) return log_error_errno(r, "Failed to write %s: %m", etc_hostname); @@ -517,14 +520,15 @@ static int process_machine_id(void) { int r; etc_machine_id = prefix_roota(arg_root, "/etc/machine-id"); - if (laccess(etc_machine_id, F_OK) >= 0) + if (laccess(etc_machine_id, F_OK) >= 0 && !arg_force) return 0; if (sd_id128_is_null(arg_machine_id)) return 0; r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id), - WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755); + WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 | + (arg_force ? WRITE_STRING_FILE_ATOMIC : 0)); if (r < 0) return log_error_errno(r, "Failed to write machine id: %m"); @@ -582,45 +586,83 @@ static int prompt_root_password(void) { return 0; } -static int write_root_shadow(const char *path, const struct spwd *p) { - _cleanup_fclose_ FILE *f = NULL; +static int write_root_shadow(const char *shadow_path, const char *hashed_password) { + _cleanup_fclose_ FILE *original = NULL, *shadow = NULL; + _cleanup_(unlink_and_freep) char *shadow_tmp = NULL; int r; - assert(path); - assert(p); - - RUN_WITH_UMASK(0777) - f = fopen(path, "wex"); - if (!f) - return -errno; - - r = putspent_sane(p, f); + r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp); if (r < 0) return r; - return fflush_sync_and_check(f); + original = fopen(shadow_path, "re"); + if (original) { + struct spwd *i; + + r = sync_rights(fileno(original), fileno(shadow)); + if (r < 0) + return r; + + while ((r = fgetspent_sane(original, &i)) > 0) { + + if (streq(i->sp_namp, "root")) { + i->sp_pwdp = (char *) hashed_password; + i->sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); + } + + r = putspent_sane(i, shadow); + if (r < 0) + return r; + } + if (r < 0) + return r; + + } else { + struct spwd root = { + .sp_namp = (char*) "root", + .sp_pwdp = (char *) hashed_password, + .sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY), + .sp_min = -1, + .sp_max = -1, + .sp_warn = -1, + .sp_inact = -1, + .sp_expire = -1, + .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ + }; + + if (errno != ENOENT) + return -errno; + + r = fchmod(fileno(shadow), 0000); + if (r < 0) + return -errno; + + r = putspent_sane(&root, shadow); + if (r < 0) + return r; + } + + r = fflush_sync_and_check(shadow); + if (r < 0) + return r; + + r = rename_and_apply_smack_floor_label(shadow_tmp, shadow_path); + if (r < 0) + return r; + + return 0; } static int process_root_password(void) { - - struct spwd item = { - .sp_namp = (char*) "root", - .sp_min = -1, - .sp_max = -1, - .sp_warn = -1, - .sp_inact = -1, - .sp_expire = -1, - .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ - }; _cleanup_free_ char *salt = NULL; _cleanup_close_ int lock = -1; struct crypt_data cd = {}; - + const char *hashed_password; const char *etc_shadow; int r; etc_shadow = prefix_roota(arg_root, "/etc/shadow"); - if (laccess(etc_shadow, F_OK) >= 0) + if (laccess(etc_shadow, F_OK) >= 0 && !arg_force) return 0; (void) mkdir_parents(etc_shadow, 0755); @@ -642,7 +684,7 @@ static int process_root_password(void) { return log_error_errno(errno, "Failed to find shadow entry for root: %m"); } - r = write_root_shadow(etc_shadow, p); + r = write_root_shadow(etc_shadow, p->sp_pwdp); if (r < 0) return log_error_errno(r, "Failed to write %s: %m", etc_shadow); @@ -663,14 +705,12 @@ static int process_root_password(void) { return log_error_errno(r, "Failed to get salt: %m"); errno = 0; - item.sp_pwdp = crypt_r(arg_root_password, salt, &cd); - if (!item.sp_pwdp) + hashed_password = crypt_r(arg_root_password, salt, &cd); + if (!hashed_password) return log_error_errno(errno == 0 ? SYNTHETIC_ERRNO(EINVAL) : errno, "Failed to encrypt password: %m"); - item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); - - r = write_root_shadow(etc_shadow, &item); + r = write_root_shadow(etc_shadow, hashed_password); if (r < 0) return log_error_errno(r, "Failed to write %s: %m", etc_shadow); @@ -711,6 +751,7 @@ static int help(void) { " --copy-root-password Copy root password from host\n" " --copy Copy locale, keymap, timezone, root password\n" " --setup-machine-id Generate a new random machine ID\n" + " --force Overwrite existing files\n" "\nSee the %s for details.\n" , program_invocation_short_name , link @@ -744,6 +785,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_COPY_TIMEZONE, ARG_COPY_ROOT_PASSWORD, ARG_SETUP_MACHINE_ID, + ARG_FORCE, }; static const struct option options[] = { @@ -770,6 +812,7 @@ static int parse_argv(int argc, char *argv[]) { { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE }, { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD }, { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID }, + { "force", no_argument, NULL, ARG_FORCE }, {} }; @@ -916,6 +959,10 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_FORCE: + arg_force = true; + break; + case '?': return -EINVAL; From 4926ceaff3bfcedac9f5c59abf4a848e22aa6be3 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Sat, 23 May 2020 21:35:21 +0200 Subject: [PATCH 6/9] firstboot: Add --delete-root-password option --- man/systemd-firstboot.xml | 8 ++++ src/firstboot/firstboot.c | 90 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/man/systemd-firstboot.xml b/man/systemd-firstboot.xml index 552268ea52..cf87249c4d 100644 --- a/man/systemd-firstboot.xml +++ b/man/systemd-firstboot.xml @@ -230,6 +230,14 @@ root user instead of overwriting the entire file. + + + + Removes the password of the system's root user, enabling login as root without a + password unless the root account is locked. Note that this is extremely insecure and hence this + option should not be used lightly. + + diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index ff275b15df..b9b7512805 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -52,6 +52,7 @@ static bool arg_copy_keymap = false; static bool arg_copy_timezone = false; static bool arg_copy_root_password = false; static bool arg_force = false; +static bool arg_delete_root_password = false; STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_locale, freep); @@ -586,6 +587,69 @@ static int prompt_root_password(void) { return 0; } +static int write_root_passwd(const char *passwd_path, const char *password) { + _cleanup_fclose_ FILE *original = NULL, *passwd = NULL; + _cleanup_(unlink_and_freep) char *passwd_tmp = NULL; + int r; + + r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp); + if (r < 0) + return r; + + original = fopen(passwd_path, "re"); + if (original) { + struct passwd *i; + + r = sync_rights(fileno(original), fileno(passwd)); + if (r < 0) + return r; + + while ((r = fgetpwent_sane(original, &i)) > 0) { + + if (streq(i->pw_name, "root")) + i->pw_passwd = (char *) password; + + r = putpwent_sane(i, passwd); + if (r < 0) + return r; + } + if (r < 0) + return r; + + } else { + struct passwd root = { + .pw_name = (char *) "root", + .pw_passwd = (char *) password, + .pw_uid = 0, + .pw_gid = 0, + .pw_gecos = (char *) "Super User", + .pw_dir = (char *) "/root", + .pw_shell = (char *) "/bin/sh", + }; + + if (errno != ENOENT) + return -errno; + + r = fchmod(fileno(passwd), 0000); + if (r < 0) + return -errno; + + r = putpwent_sane(&root, passwd); + if (r < 0) + return r; + } + + r = fflush_sync_and_check(passwd); + if (r < 0) + return r; + + r = rename_and_apply_smack_floor_label(passwd_tmp, passwd_path); + if (r < 0) + return r; + + return 0; +} + static int write_root_shadow(const char *shadow_path, const char *hashed_password) { _cleanup_fclose_ FILE *original = NULL, *shadow = NULL; _cleanup_(unlink_and_freep) char *shadow_tmp = NULL; @@ -671,6 +735,21 @@ static int process_root_password(void) { if (lock < 0) return log_error_errno(lock, "Failed to take a lock: %m"); + if (arg_delete_root_password) { + const char *etc_passwd; + + /* Mixing alloca() and other stuff that touches the stack in one expression is not portable. */ + etc_passwd = prefix_roota(arg_root, "/etc/passwd"); + + r = write_root_passwd(etc_passwd, ""); + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_passwd); + + log_info("%s written", etc_passwd); + + return 0; + } + if (arg_copy_root_password && arg_root) { struct spwd *p; @@ -752,6 +831,7 @@ static int help(void) { " --copy Copy locale, keymap, timezone, root password\n" " --setup-machine-id Generate a new random machine ID\n" " --force Overwrite existing files\n" + " --delete-root-password Delete root password\n" "\nSee the %s for details.\n" , program_invocation_short_name , link @@ -786,6 +866,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_COPY_ROOT_PASSWORD, ARG_SETUP_MACHINE_ID, ARG_FORCE, + ARG_DELETE_ROOT_PASSWORD, }; static const struct option options[] = { @@ -813,6 +894,7 @@ static int parse_argv(int argc, char *argv[]) { { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD }, { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID }, { "force", no_argument, NULL, ARG_FORCE }, + { "delete-root-password", no_argument, NULL, ARG_DELETE_ROOT_PASSWORD }, {} }; @@ -963,6 +1045,10 @@ static int parse_argv(int argc, char *argv[]) { arg_force = true; break; + case ARG_DELETE_ROOT_PASSWORD: + arg_delete_root_password = true; + break; + case '?': return -EINVAL; @@ -978,6 +1064,10 @@ static int parse_argv(int argc, char *argv[]) { if (arg_locale_messages && !locale_is_ok(arg_locale_messages)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale_messages); + if (arg_delete_root_password && (arg_copy_root_password || arg_root_password || arg_prompt_root_password)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "--delete-root-password cannot be combined with other root password options"); + return 1; } From 97a1a1103c037ce7a67afaae689e0457d653066f Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 25 May 2020 17:50:28 +0200 Subject: [PATCH 7/9] Remove systemd-firstboot --force entry from TODO --- TODO | 2 -- 1 file changed, 2 deletions(-) diff --git a/TODO b/TODO index 37323e3a19..624ae2aafd 100644 --- a/TODO +++ b/TODO @@ -256,8 +256,6 @@ Features: * systemd-firstboot: teach it dissector magic, so that you can point it to some disk image and it will just set everything in it all behind the scenes. -* systemd-firstboot: add --force mode that replaces existing configuration. - * We should probably replace /var/log/README, /etc/rc.d/README with symlinks that are linked to these places instead of copied. After all they are constant vendor data. From 676339a1910a35a31f79d7bb00a6f603f18f4aa8 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 25 May 2020 19:24:07 +0200 Subject: [PATCH 8/9] firstboot: Add --root-password-hashed option --- man/systemd-firstboot.xml | 20 +++--- src/firstboot/firstboot.c | 139 ++++++++++++++++++++++---------------- 2 files changed, 90 insertions(+), 69 deletions(-) diff --git a/man/systemd-firstboot.xml b/man/systemd-firstboot.xml index cf87249c4d..88d90e405a 100644 --- a/man/systemd-firstboot.xml +++ b/man/systemd-firstboot.xml @@ -150,18 +150,18 @@ + - Sets the password of the system's root user. - This creates a + Sets the password of the system's root user. This creates a shadow5 - file. This setting exists in two forms: - accepts the password to set - directly on the command line, and - reads it from a file. - Note that it is not recommended to specify passwords on the - command line, as other users might be able to see them simply - by invoking - ps1. + file. This setting exists in three forms: accepts the password to + set directly on the command line, reads it from a file and + accepts an already hashed password on the command line. See + shadow5 + for more information on the format of the hashed password. Note that it is not recommended to specify + plaintext passwords on the command line, as other users might be able to see them simply by invoking + ps1. + diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index b9b7512805..b3a416af18 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -53,6 +53,7 @@ static bool arg_copy_timezone = false; static bool arg_copy_root_password = false; static bool arg_force = false; static bool arg_delete_root_password = false; +static bool arg_root_password_is_hashed = false; STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_locale, freep); @@ -718,7 +719,6 @@ static int write_root_shadow(const char *shadow_path, const char *hashed_passwor } static int process_root_password(void) { - _cleanup_free_ char *salt = NULL; _cleanup_close_ int lock = -1; struct crypt_data cd = {}; const char *hashed_password; @@ -779,15 +779,22 @@ static int process_root_password(void) { if (!arg_root_password) return 0; - r = make_salt(&salt); - if (r < 0) - return log_error_errno(r, "Failed to get salt: %m"); + if (arg_root_password_is_hashed) + hashed_password = arg_root_password; + else { + _cleanup_free_ char *salt = NULL; + /* hashed_password points inside cd after crypt_r returns so cd has function scope. */ - errno = 0; - hashed_password = crypt_r(arg_root_password, salt, &cd); - if (!hashed_password) - return log_error_errno(errno == 0 ? SYNTHETIC_ERRNO(EINVAL) : errno, - "Failed to encrypt password: %m"); + r = make_salt(&salt); + if (r < 0) + return log_error_errno(r, "Failed to get salt: %m"); + + errno = 0; + hashed_password = crypt_r(arg_root_password, salt, &cd); + if (!hashed_password) + return log_error_errno(errno == 0 ? SYNTHETIC_ERRNO(EINVAL) : errno, + "Failed to encrypt password: %m"); + } r = write_root_shadow(etc_shadow, hashed_password); if (r < 0) @@ -807,31 +814,32 @@ static int help(void) { printf("%s [OPTIONS...]\n\n" "Configures basic settings of the system.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --root=PATH Operate on an alternate filesystem root\n" - " --locale=LOCALE Set primary locale (LANG=)\n" - " --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n" - " --keymap=KEYMAP Set keymap\n" - " --timezone=TIMEZONE Set timezone\n" - " --hostname=NAME Set hostname\n" - " --machine-ID=ID Set machine ID\n" - " --root-password=PASSWORD Set root password\n" - " --root-password-file=FILE Set root password from file\n" - " --prompt-locale Prompt the user for locale settings\n" - " --prompt-keymap Prompt the user for keymap settings\n" - " --prompt-timezone Prompt the user for timezone\n" - " --prompt-hostname Prompt the user for hostname\n" - " --prompt-root-password Prompt the user for root password\n" - " --prompt Prompt for all of the above\n" - " --copy-locale Copy locale from host\n" - " --copy-keymap Copy keymap from host\n" - " --copy-timezone Copy timezone from host\n" - " --copy-root-password Copy root password from host\n" - " --copy Copy locale, keymap, timezone, root password\n" - " --setup-machine-id Generate a new random machine ID\n" - " --force Overwrite existing files\n" - " --delete-root-password Delete root password\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --root=PATH Operate on an alternate filesystem root\n" + " --locale=LOCALE Set primary locale (LANG=)\n" + " --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n" + " --keymap=KEYMAP Set keymap\n" + " --timezone=TIMEZONE Set timezone\n" + " --hostname=NAME Set hostname\n" + " --machine-ID=ID Set machine ID\n" + " --root-password=PASSWORD Set root password from plaintext password\n" + " --root-password-file=FILE Set root password from file\n" + " --root-password-hashed=HASHED_PASSWORD Set root password from hashed password\n" + " --prompt-locale Prompt the user for locale settings\n" + " --prompt-keymap Prompt the user for keymap settings\n" + " --prompt-timezone Prompt the user for timezone\n" + " --prompt-hostname Prompt the user for hostname\n" + " --prompt-root-password Prompt the user for root password\n" + " --prompt Prompt for all of the above\n" + " --copy-locale Copy locale from host\n" + " --copy-keymap Copy keymap from host\n" + " --copy-timezone Copy timezone from host\n" + " --copy-root-password Copy root password from host\n" + " --copy Copy locale, keymap, timezone, root password\n" + " --setup-machine-id Generate a new random machine ID\n" + " --force Overwrite existing files\n" + " --delete-root-password Delete root password\n" "\nSee the %s for details.\n" , program_invocation_short_name , link @@ -853,6 +861,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_MACHINE_ID, ARG_ROOT_PASSWORD, ARG_ROOT_PASSWORD_FILE, + ARG_ROOT_PASSWORD_HASHED, ARG_PROMPT, ARG_PROMPT_LOCALE, ARG_PROMPT_KEYMAP, @@ -870,31 +879,32 @@ static int parse_argv(int argc, char *argv[]) { }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "root", required_argument, NULL, ARG_ROOT }, - { "locale", required_argument, NULL, ARG_LOCALE }, - { "locale-messages", required_argument, NULL, ARG_LOCALE_MESSAGES }, - { "keymap", required_argument, NULL, ARG_KEYMAP }, - { "timezone", required_argument, NULL, ARG_TIMEZONE }, - { "hostname", required_argument, NULL, ARG_HOSTNAME }, - { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, - { "root-password", required_argument, NULL, ARG_ROOT_PASSWORD }, - { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE }, - { "prompt", no_argument, NULL, ARG_PROMPT }, - { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE }, - { "prompt-keymap", no_argument, NULL, ARG_PROMPT_KEYMAP }, - { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE }, - { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME }, - { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD }, - { "copy", no_argument, NULL, ARG_COPY }, - { "copy-locale", no_argument, NULL, ARG_COPY_LOCALE }, - { "copy-keymap", no_argument, NULL, ARG_COPY_KEYMAP }, - { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE }, - { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD }, - { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID }, - { "force", no_argument, NULL, ARG_FORCE }, - { "delete-root-password", no_argument, NULL, ARG_DELETE_ROOT_PASSWORD }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "root", required_argument, NULL, ARG_ROOT }, + { "locale", required_argument, NULL, ARG_LOCALE }, + { "locale-messages", required_argument, NULL, ARG_LOCALE_MESSAGES }, + { "keymap", required_argument, NULL, ARG_KEYMAP }, + { "timezone", required_argument, NULL, ARG_TIMEZONE }, + { "hostname", required_argument, NULL, ARG_HOSTNAME }, + { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, + { "root-password", required_argument, NULL, ARG_ROOT_PASSWORD }, + { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE }, + { "root-password-hashed", required_argument, NULL, ARG_ROOT_PASSWORD_HASHED }, + { "prompt", no_argument, NULL, ARG_PROMPT }, + { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE }, + { "prompt-keymap", no_argument, NULL, ARG_PROMPT_KEYMAP }, + { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE }, + { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME }, + { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD }, + { "copy", no_argument, NULL, ARG_COPY }, + { "copy-locale", no_argument, NULL, ARG_COPY_LOCALE }, + { "copy-keymap", no_argument, NULL, ARG_COPY_KEYMAP }, + { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE }, + { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD }, + { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID }, + { "force", no_argument, NULL, ARG_FORCE }, + { "delete-root-password", no_argument, NULL, ARG_DELETE_ROOT_PASSWORD }, {} }; @@ -959,6 +969,8 @@ static int parse_argv(int argc, char *argv[]) { r = free_and_strdup(&arg_root_password, optarg); if (r < 0) return log_oom(); + + arg_root_password_is_hashed = false; break; case ARG_ROOT_PASSWORD_FILE: @@ -968,6 +980,15 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to read %s: %m", optarg); + arg_root_password_is_hashed = false; + break; + + case ARG_ROOT_PASSWORD_HASHED: + r = free_and_strdup(&arg_root_password, optarg); + if (r < 0) + return log_oom(); + + arg_root_password_is_hashed = true; break; case ARG_HOSTNAME: From a5925354bba982da2385dde69330b09848a3897b Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 25 May 2020 21:39:51 +0200 Subject: [PATCH 9/9] firstboot: Add --kernel-command-line option --- man/systemd-firstboot.xml | 9 +++++++++ src/firstboot/firstboot.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/man/systemd-firstboot.xml b/man/systemd-firstboot.xml index 88d90e405a..81e62484fd 100644 --- a/man/systemd-firstboot.xml +++ b/man/systemd-firstboot.xml @@ -164,6 +164,15 @@ + + + + Sets the system's kernel command line. This controls the + /etc/kernel/cmdline file which is used by + kernel-install8. + + + diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index b3a416af18..0c3ef3e2a7 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -42,6 +42,7 @@ static char *arg_timezone = NULL; static char *arg_hostname = NULL; static sd_id128_t arg_machine_id = {}; static char *arg_root_password = NULL; +static char *arg_kernel_cmdline = NULL; static bool arg_prompt_locale = false; static bool arg_prompt_keymap = false; static bool arg_prompt_timezone = false; @@ -804,6 +805,27 @@ static int process_root_password(void) { return 0; } +static int process_kernel_cmdline(void) { + const char *etc_kernel_cmdline; + int r; + + etc_kernel_cmdline = prefix_roota(arg_root, "/etc/kernel/cmdline"); + if (laccess(etc_kernel_cmdline, F_OK) >= 0 && !arg_force) + return 0; + + if (!arg_kernel_cmdline) + return 0; + + r = write_string_file(etc_kernel_cmdline, arg_kernel_cmdline, + WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 | + (arg_force ? WRITE_STRING_FILE_ATOMIC : 0)); + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_kernel_cmdline); + + log_info("%s written.", etc_kernel_cmdline); + return 0; +} + static int help(void) { _cleanup_free_ char *link = NULL; int r; @@ -862,6 +884,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_ROOT_PASSWORD, ARG_ROOT_PASSWORD_FILE, ARG_ROOT_PASSWORD_HASHED, + ARG_KERNEL_COMMAND_LINE, ARG_PROMPT, ARG_PROMPT_LOCALE, ARG_PROMPT_KEYMAP, @@ -891,6 +914,7 @@ static int parse_argv(int argc, char *argv[]) { { "root-password", required_argument, NULL, ARG_ROOT_PASSWORD }, { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE }, { "root-password-hashed", required_argument, NULL, ARG_ROOT_PASSWORD_HASHED }, + { "kernel-command-line", required_argument, NULL, ARG_KERNEL_COMMAND_LINE }, { "prompt", no_argument, NULL, ARG_PROMPT }, { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE }, { "prompt-keymap", no_argument, NULL, ARG_PROMPT_KEYMAP }, @@ -1010,6 +1034,13 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_KERNEL_COMMAND_LINE: + r = free_and_strdup(&arg_kernel_cmdline, optarg); + if (r < 0) + return log_oom(); + + break; + case ARG_PROMPT: arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true; break; @@ -1134,6 +1165,10 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; + r = process_kernel_cmdline(); + if (r < 0) + return r; + return 0; }