firstboot: Tighten up passwd/shadow handling
There are a lot of edge cases that the current implementation doesn't handle, especially in cases where one of passwd/shadow exists and the other doesn't exist. For example, if --root-password is specified, we will write /etc/shadow but won't add a root entry to /etc/passwd if there is none. To fix some of these issues, we constrain systemd-firstboot to only modify /etc/passwd and /etc/shadow if both do not exist already (or --force) is specified. On top of that, we calculate all necessary information for both passwd and shadow upfront so we can take it all into account when writing the actual files. If no root password options are given --force is specified or both files do not exist, we lock the root account for security purposes.
This commit is contained in:
parent
eced0d2a46
commit
c4a53ebf7a
|
@ -164,9 +164,10 @@
|
||||||
<term><option>--root-password-file=<replaceable>PATH</replaceable></option></term>
|
<term><option>--root-password-file=<replaceable>PATH</replaceable></option></term>
|
||||||
<term><option>--root-password-hashed=<replaceable>HASHED_PASSWORD</replaceable></option></term>
|
<term><option>--root-password-hashed=<replaceable>HASHED_PASSWORD</replaceable></option></term>
|
||||||
|
|
||||||
<listitem><para>Sets the password of the system's root user. This creates a
|
<listitem><para>Sets the password of the system's root user. This creates/modifies the
|
||||||
|
<citerefentry project='die-net'><refentrytitle>passwd</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
|
||||||
<citerefentry project='die-net'><refentrytitle>shadow</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
<citerefentry project='die-net'><refentrytitle>shadow</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||||
file. This setting exists in three forms: <option>--root-password=</option> accepts the password to
|
files. This setting exists in three forms: <option>--root-password=</option> accepts the password to
|
||||||
set directly on the command line, <option>--root-password-file=</option> reads it from a file and
|
set directly on the command line, <option>--root-password-file=</option> reads it from a file and
|
||||||
<option>--root-password-hashed=</option> accepts an already hashed password on the command line. See
|
<option>--root-password-hashed=</option> accepts an already hashed password on the command line. See
|
||||||
<citerefentry project='die-net'><refentrytitle>shadow</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
<citerefentry project='die-net'><refentrytitle>shadow</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||||
|
|
|
@ -606,6 +606,8 @@ static int write_root_passwd(const char *passwd_path, const char *password) {
|
||||||
_cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
|
_cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
assert(password);
|
||||||
|
|
||||||
r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
|
r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -669,6 +671,8 @@ static int write_root_shadow(const char *shadow_path, const char *hashed_passwor
|
||||||
_cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
|
_cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
assert(hashed_password);
|
||||||
|
|
||||||
r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
|
r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -734,70 +738,53 @@ static int write_root_shadow(const char *shadow_path, const char *hashed_passwor
|
||||||
static int process_root_password(void) {
|
static int process_root_password(void) {
|
||||||
_cleanup_close_ int lock = -1;
|
_cleanup_close_ int lock = -1;
|
||||||
struct crypt_data cd = {};
|
struct crypt_data cd = {};
|
||||||
const char *hashed_password;
|
const char *password, *hashed_password;
|
||||||
const char *etc_shadow;
|
const char *etc_passwd, *etc_shadow;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
etc_passwd = prefix_roota(arg_root, "/etc/passwd");
|
||||||
etc_shadow = prefix_roota(arg_root, "/etc/shadow");
|
etc_shadow = prefix_roota(arg_root, "/etc/shadow");
|
||||||
if (laccess(etc_shadow, F_OK) >= 0 && !arg_force)
|
|
||||||
|
/* We only mess with passwd and shadow if both do not exist or --force is specified. These files are
|
||||||
|
* tightly coupled and hence we make sure we have permission from the user to create/modify both
|
||||||
|
* files. */
|
||||||
|
if ((laccess(etc_passwd, F_OK) >= 0 || laccess(etc_shadow, F_OK) >= 0) && !arg_force)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
(void) mkdir_parents(etc_shadow, 0755);
|
(void) mkdir_parents(etc_passwd, 0755);
|
||||||
|
|
||||||
lock = take_etc_passwd_lock(arg_root);
|
lock = take_etc_passwd_lock(arg_root);
|
||||||
if (lock < 0)
|
if (lock < 0)
|
||||||
return log_error_errno(lock, "Failed to take a lock: %m");
|
return log_error_errno(lock, "Failed to take a lock on %s: %m", etc_passwd);
|
||||||
|
|
||||||
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) {
|
if (arg_copy_root_password && arg_root) {
|
||||||
struct spwd *p;
|
struct spwd *p;
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
p = getspnam("root");
|
p = getspnam("root");
|
||||||
if (p || errno != ENOENT) {
|
if (!p)
|
||||||
if (!p) {
|
return log_error_errno(errno_or_else(EIO), "Failed to find shadow entry for root: %m");
|
||||||
if (!errno)
|
|
||||||
errno = EIO;
|
|
||||||
|
|
||||||
return log_error_errno(errno, "Failed to find shadow entry for root: %m");
|
r = free_and_strdup(&arg_root_password, p->sp_pwdp);
|
||||||
}
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
r = write_root_shadow(etc_shadow, p->sp_pwdp);
|
arg_root_password_is_hashed = true;
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to write %s: %m", etc_shadow);
|
|
||||||
|
|
||||||
log_info("%s copied.", etc_shadow);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r = prompt_root_password();
|
r = prompt_root_password();
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (!arg_root_password)
|
if (arg_root_password && arg_root_password_is_hashed) {
|
||||||
return 0;
|
password = "x";
|
||||||
|
|
||||||
if (arg_root_password_is_hashed)
|
|
||||||
hashed_password = arg_root_password;
|
hashed_password = arg_root_password;
|
||||||
else {
|
} else if (arg_root_password) {
|
||||||
_cleanup_free_ char *salt = NULL;
|
_cleanup_free_ char *salt = NULL;
|
||||||
/* hashed_password points inside cd after crypt_r returns so cd has function scope. */
|
/* hashed_password points inside cd after crypt_r returns so cd has function scope. */
|
||||||
|
|
||||||
|
password = "x";
|
||||||
|
|
||||||
r = make_salt(&salt);
|
r = make_salt(&salt);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to get salt: %m");
|
return log_error_errno(r, "Failed to get salt: %m");
|
||||||
|
@ -807,7 +794,16 @@ static int process_root_password(void) {
|
||||||
if (!hashed_password)
|
if (!hashed_password)
|
||||||
return log_error_errno(errno == 0 ? SYNTHETIC_ERRNO(EINVAL) : errno,
|
return log_error_errno(errno == 0 ? SYNTHETIC_ERRNO(EINVAL) : errno,
|
||||||
"Failed to encrypt password: %m");
|
"Failed to encrypt password: %m");
|
||||||
}
|
} else if (arg_delete_root_password)
|
||||||
|
password = hashed_password = "";
|
||||||
|
else
|
||||||
|
password = hashed_password = "!";
|
||||||
|
|
||||||
|
r = write_root_passwd(etc_passwd, password);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to write %s: %m", etc_passwd);
|
||||||
|
|
||||||
|
log_info("%s written", etc_passwd);
|
||||||
|
|
||||||
r = write_root_shadow(etc_shadow, hashed_password);
|
r = write_root_shadow(etc_shadow, hashed_password);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|
Loading…
Reference in a new issue