diff --git a/src/basic/user-util.h b/src/basic/user-util.h index 5aca6307e0..c82941dd81 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -150,3 +150,8 @@ static inline bool hashed_password_is_locked_or_invalid(const char *password) { /* A password indicating "hey, no password required for login" */ #define PASSWORD_NONE "" + +/* Used by sysusers to indicate that the password should be filled in by firstboot. + * Also see https://github.com/systemd/systemd/pull/24680#pullrequestreview-1439464325. + */ +#define PASSWORD_UNPROVISIONED "!unprovisioned" diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 19d5568854..6b42e64043 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -234,17 +234,72 @@ static int prompt_loop(const char *text, char **l, unsigned percentage, bool (*i } static int should_configure(int dir_fd, const char *filename) { + _cleanup_fclose_ FILE *passwd = NULL, *shadow = NULL; + int r; + assert(dir_fd >= 0); assert(filename); - if (faccessat(dir_fd, filename, F_OK, AT_SYMLINK_NOFOLLOW) < 0) { - if (errno != ENOENT) - return log_error_errno(errno, "Failed to access %s: %m", filename); + if (streq(filename, "passwd") && !arg_force) + /* We may need to do additional checks, so open the file. */ + r = xfopenat(dir_fd, filename, "re", O_NOFOLLOW, &passwd); + else + r = RET_NERRNO(faccessat(dir_fd, filename, F_OK, AT_SYMLINK_NOFOLLOW)); + if (r == -ENOENT) return true; /* missing */ + if (r < 0) + return log_error_errno(r, "Failed to access %s: %m", filename); + if (arg_force) + return true; /* exists, but if --force was given we should still configure the file. */ + + if (!passwd) + return false; + + /* In case of /etc/passwd, do an additional check for the root password field. + * We first check that passwd redirects to shadow, and then we check shadow. + */ + struct passwd *i; + while ((r = fgetpwent_sane(passwd, &i)) > 0) { + if (!streq(i->pw_name, "root")) + continue; + + if (streq_ptr(i->pw_passwd, PASSWORD_SEE_SHADOW)) + break; + log_debug("passwd: root account with non-shadow password found, treating root as configured"); + return false; + } + if (r < 0) + return log_error_errno(r, "Failed to read %s: %m", filename); + if (r == 0) { + log_debug("No root account found in %s, assuming root is not configured.", filename); + return true; } - return arg_force; /* exists, but if --force was given we should still configure the file. */ + r = xfopenat(dir_fd, "shadow", "re", O_NOFOLLOW, &shadow); + if (r == -ENOENT) { + log_debug("No shadow file found, assuming root is not configured."); + return true; /* missing */ + } + if (r < 0) + return log_error_errno(r, "Failed to access shadow: %m"); + + struct spwd *j; + while ((r = fgetspent_sane(shadow, &j)) > 0) { + if (!streq(j->sp_namp, "root")) + continue; + + bool unprovisioned = streq_ptr(j->sp_pwdp, PASSWORD_UNPROVISIONED); + log_debug("Root account found, %s.", + unprovisioned ? "with unprovisioned password, treating root as not configured" : + "treating root as configured"); + return unprovisioned; + } + if (r < 0) + return log_error_errno(r, "Failed to read shadow: %m"); + assert(r == 0); + log_debug("No root account found in shadow, assuming root is not configured."); + return true; } static bool locale_is_installed_bool(const char *name) { diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 5aaaf609b2..12adad516e 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -618,7 +618,6 @@ static int write_temporary_shadow(const char *shadow_path, FILE **ret_tmpfile, c struct spwd n = { .sp_namp = i->name, - .sp_pwdp = (char*) PASSWORD_LOCKED_AND_INVALID, .sp_lstchg = lstchg, .sp_min = -1, .sp_max = -1, @@ -641,6 +640,11 @@ static int write_temporary_shadow(const char *shadow_path, FILE **ret_tmpfile, c if (creds_password) n.sp_pwdp = creds_password; + else if (streq(i->name, "root")) + /* Let firstboot set the password later */ + n.sp_pwdp = (char*) PASSWORD_UNPROVISIONED; + else + n.sp_pwdp = (char*) PASSWORD_LOCKED_AND_INVALID; r = putspent_sane(&n, shadow); if (r < 0) diff --git a/units/systemd-firstboot.service b/units/systemd-firstboot.service index 58f3eeb679..5fee85a287 100644 --- a/units/systemd-firstboot.service +++ b/units/systemd-firstboot.service @@ -16,7 +16,7 @@ ConditionFirstBoot=yes DefaultDependencies=no After=systemd-remount-fs.service -Before=systemd-sysusers.service systemd-vconsole-setup.service sysinit.target first-boot-complete.target +Before=systemd-vconsole-setup.service sysinit.target first-boot-complete.target Wants=first-boot-complete.target Conflicts=shutdown.target Before=shutdown.target