diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index c70bfa468fd..c452ca59756 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -908,8 +908,7 @@ static int write_root_passwd(int rfd, int etc_fd, const char *password, const ch _cleanup_fclose_ FILE *original = NULL, *passwd = NULL; _cleanup_(unlink_and_freep) char *passwd_tmp = NULL; int r; - - assert(password); + bool found = false; r = fopen_temporary_at_label(etc_fd, "passwd", "passwd", &passwd, &passwd_tmp); if (r < 0) @@ -929,9 +928,11 @@ static int write_root_passwd(int rfd, int etc_fd, const char *password, const ch while ((r = fgetpwent_sane(original, &i)) > 0) { if (streq(i->pw_name, "root")) { - i->pw_passwd = (char *) password; + if (password) + i->pw_passwd = (char *) password; if (shell) i->pw_shell = (char *) shell; + found = true; } r = putpwent_sane(i, passwd); @@ -942,9 +943,15 @@ static int write_root_passwd(int rfd, int etc_fd, const char *password, const ch return r; } else { + r = fchmod(fileno(passwd), 0644); + if (r < 0) + return -errno; + } + + if (!found) { struct passwd root = { .pw_name = (char *) "root", - .pw_passwd = (char *) password, + .pw_passwd = (char *) (password ?: PASSWORD_SEE_SHADOW), .pw_uid = 0, .pw_gid = 0, .pw_gecos = (char *) "Super User", @@ -955,10 +962,6 @@ static int write_root_passwd(int rfd, int etc_fd, const char *password, const ch if (errno != ENOENT) return -errno; - r = fchmod(fileno(passwd), 0644); - if (r < 0) - return -errno; - r = putpwent_sane(&root, passwd); if (r < 0) return r; @@ -979,8 +982,7 @@ static int write_root_shadow(int etc_fd, const char *hashed_password) { _cleanup_fclose_ FILE *original = NULL, *shadow = NULL; _cleanup_(unlink_and_freep) char *shadow_tmp = NULL; int r; - - assert(hashed_password); + bool found = false; r = fopen_temporary_at_label(etc_fd, "shadow", "shadow", &shadow, &shadow_tmp); if (r < 0) @@ -1000,8 +1002,11 @@ static int write_root_shadow(int etc_fd, const char *hashed_password) { 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); + if (hashed_password) { + i->sp_pwdp = (char *) hashed_password; + i->sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); + } + found = true; } r = putspent_sane(i, shadow); @@ -1012,9 +1017,15 @@ static int write_root_shadow(int etc_fd, const char *hashed_password) { return r; } else { + r = fchmod(fileno(shadow), 0000); + if (r < 0) + return -errno; + } + + if (!found) { struct spwd root = { .sp_namp = (char*) "root", - .sp_pwdp = (char *) hashed_password, + .sp_pwdp = (char *) (hashed_password ?: PASSWORD_LOCKED_AND_INVALID), .sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY), .sp_min = -1, .sp_max = -1, @@ -1027,10 +1038,6 @@ static int write_root_shadow(int etc_fd, const char *hashed_password) { 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; @@ -1081,13 +1088,6 @@ static int process_root_account(int rfd) { return 0; } - /* Don't create/modify passwd and shadow if not asked */ - if (!(arg_root_password || arg_prompt_root_password || arg_copy_root_password || arg_delete_root_password || - arg_root_shell || arg_prompt_root_shell || arg_copy_root_shell)) { - log_debug("Initialization of root account was not requested, skipping."); - return 0; - } - r = make_lock_file_at(pfd, ETC_PASSWD_LOCK_FILENAME, LOCK_EX, &lock); if (r < 0) return log_error_errno(r, "Failed to take a lock on /etc/passwd: %m"); @@ -1142,10 +1142,22 @@ static int process_root_account(int rfd) { password = PASSWORD_SEE_SHADOW; hashed_password = _hashed_password; - } else if (arg_delete_root_password) - password = hashed_password = PASSWORD_NONE; - else - password = hashed_password = PASSWORD_LOCKED_AND_INVALID; + } else if (arg_delete_root_password) { + password = PASSWORD_SEE_SHADOW; + hashed_password = PASSWORD_NONE; + } else if (!arg_root_password && arg_prompt_root_password) { + /* If the user was prompted, but no password was supplied, lock the account. */ + password = PASSWORD_SEE_SHADOW; + hashed_password = PASSWORD_LOCKED_AND_INVALID; + } else + /* Leave the password as is. */ + password = hashed_password = NULL; + + /* Don't create/modify passwd and shadow if there's nothing to do. */ + if (!(password || hashed_password || arg_root_shell)) { + log_debug("Initialization of root account was not requested, skipping."); + return 0; + } r = write_root_passwd(rfd, pfd, password, arg_root_shell); if (r < 0) diff --git a/test/units/TEST-74-AUX-UTILS.firstboot.sh b/test/units/TEST-74-AUX-UTILS.firstboot.sh index 7bab009d529..d9e5f594268 100755 --- a/test/units/TEST-74-AUX-UTILS.firstboot.sh +++ b/test/units/TEST-74-AUX-UTILS.firstboot.sh @@ -14,6 +14,7 @@ fi at_exit() { if [[ -n "${ROOT:-}" ]]; then ls -lR "$ROOT" + grep -r . "$ROOT/etc" || : rm -fr "$ROOT" fi @@ -83,15 +84,42 @@ grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id" rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" systemd-firstboot --root="$ROOT" --root-password=foo grep -q "^root:x:0:0:" "$ROOT/etc/passwd" -grep -q "^root:" "$ROOT/etc/shadow" +grep -q "^root:[^!*]" "$ROOT/etc/shadow" rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" echo "foo" >root.passwd systemd-firstboot --root="$ROOT" --root-password-file=root.passwd grep -q "^root:x:0:0:" "$ROOT/etc/passwd" -grep -q "^root:" "$ROOT/etc/shadow" +grep -q "^root:[^!*]" "$ROOT/etc/shadow" rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" root.passwd -# Set the shell together with the password, as firstboot won't touch -# /etc/passwd if it already exists +# Make sure the root password is set if /etc/passwd and /etc/shadow exist but +# don't have a root entry. +touch "$ROOT/etc/passwd" "$ROOT/etc/shadow" +systemd-firstboot --root="$ROOT" --root-password=foo +grep -q "^root:x:0:0:" "$ROOT/etc/passwd" +grep -q "^root:[^!*]" "$ROOT/etc/shadow" +rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" +# If /etc/passwd and /etc/shadow exist, they will only be updated if the shadow +# password is !unprovisioned. +echo "root:x:0:0:root:/root:/bin/sh" >"$ROOT/etc/passwd" +echo "root:!test:::::::" >"$ROOT/etc/shadow" +systemd-firstboot --root="$ROOT" --root-password=foo +grep -q "^root:x:0:0:" "$ROOT/etc/passwd" +grep -q "^root:!test:" "$ROOT/etc/shadow" +rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" +echo "root:x:0:0:root:/root:/bin/sh" >"$ROOT/etc/passwd" +echo "root:!unprovisioned:::::::" >"$ROOT/etc/shadow" +systemd-firstboot --root="$ROOT" --root-password=foo +grep -q "^root:x:0:0:" "$ROOT/etc/passwd" +grep -q "^root:[^!*]" "$ROOT/etc/shadow" +rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" +systemd-firstboot --root="$ROOT" --root-password-hashed="$ROOT_HASHED_PASSWORD1" +grep -q "^root:x:0:0:" "$ROOT/etc/passwd" +grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow" +rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" +systemd-firstboot --root="$ROOT" --root-shell=/bin/fooshell +grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd" +grep -q "^root:!\*:" "$ROOT/etc/shadow" +rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" systemd-firstboot --root="$ROOT" --root-password-hashed="$ROOT_HASHED_PASSWORD1" --root-shell=/bin/fooshell grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd" grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow" @@ -176,8 +204,9 @@ mkdir -p "$ROOT/bin" touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell" # Temporarily disable pipefail to avoid `echo: write error: Broken pipe set +o pipefail -# We can do only limited testing here, since it's all an interactive stuff, -# so --prompt and --prompt-root-password are skipped on purpose +# We can do only limited testing here, since it's all an interactive stuff, so +# --prompt is skipped on purpose and only limited --prompt-root-password +# testing can be done. echo -ne "\nfoo\nbar\n" | systemd-firstboot --root="$ROOT" --prompt-locale grep -q "LANG=foo" "$ROOT$LOCALE_PATH" grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH" @@ -193,6 +222,11 @@ echo -ne "\nEurope/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezon readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$" echo -ne "\nfoobar\n" | systemd-firstboot --root="$ROOT" --prompt-hostname grep -q "foobar" "$ROOT/etc/hostname" +# With no root password provided, a locked account should be created. +systemd-firstboot --root="$ROOT" --prompt-root-password