Merge pull request #24370 from keszybz/sysusers-equivs

Use /bin/bash for root shell and suppress some warnings from sysusers
This commit is contained in:
Luca Boccassi
2022-08-24 21:35:28 +01:00
committed by GitHub
12 changed files with 261 additions and 233 deletions

View File

@@ -35,23 +35,22 @@
<refsect1>
<title>Description</title>
<para><command>systemd-sysusers</command> creates system users and
groups, based on the file format and location specified in
<para><command>systemd-sysusers</command> creates system users and groups, based on files in the format
described in
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
<para>If invoked with no arguments, it applies all directives from all files
found in the directories specified by
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
When invoked with positional arguments, if option
<option>--replace=<replaceable>PATH</replaceable></option> is specified, arguments
specified on the command line are used instead of the configuration file
<replaceable>PATH</replaceable>. Otherwise, just the configuration specified by
the command line arguments is executed. The string <literal>-</literal> may be
specified instead of a filename to instruct <command>systemd-sysusers</command>
to read the configuration from standard input. If only the basename of a file is
specified, all configuration directories are searched for a matching file and
the file found that has the highest priority is executed.</para>
<para>If invoked with no arguments, it applies all directives from all files found in the directories
specified by
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>. When
invoked with positional arguments, if option <option>--replace=<replaceable>PATH</replaceable></option>
is specified, arguments specified on the command line are used instead of the configuration file
<replaceable>PATH</replaceable>. Otherwise, just the configuration specified by the command line
arguments is executed. The string <literal>-</literal> may be specified instead of a filename to instruct
<command>systemd-sysusers</command> to read the configuration from standard input. If the argument is a
relative path, all configuration directories are searched for a matching file and the file found that has
the highest priority is executed. If the argument is an absolute path, that file is used directly without
searching of the configuration directories.</para>
</refsect1>
<refsect1>

View File

@@ -2108,7 +2108,7 @@ Note that this setting is <emphasis>not</emphasis> influenced by the <varname>Us
<row>
<entry><literal>%s</literal></entry>
<entry>User shell</entry>
<entry>This is the shell of the user running the service manager instance. In case of the system manager this resolves to <literal>/bin/sh</literal>.</entry>
<entry>This is the shell of the user running the service manager instance.</entry>
</row>
<row>
<entry><literal>%S</literal></entry>

View File

@@ -13,6 +13,7 @@
#include "sd-messages.h"
#include "alloc-util.h"
#include "chase-symlinks.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
@@ -136,7 +137,6 @@ char *getusername_malloc(void) {
}
bool is_nologin_shell(const char *shell) {
return PATH_IN_SET(shell,
/* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice
* message and exits. Different distributions place the binary at different places though,
@@ -154,6 +154,21 @@ bool is_nologin_shell(const char *shell) {
"/usr/bin/true");
}
const char* default_root_shell(const char *root) {
/* We want to use the preferred shell, i.e. DEFAULT_USER_SHELL, which usually
* will be /bin/bash. Fall back to /bin/sh if DEFAULT_USER_SHELL is not found,
* or any access errors. */
int r = chase_symlinks(DEFAULT_USER_SHELL, root, CHASE_PREFIX_ROOT, NULL, NULL);
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to look up shell '%s%s%s': %m",
strempty(root), root ? "/" : "", DEFAULT_USER_SHELL);
if (r > 0)
return DEFAULT_USER_SHELL;
return "/bin/sh";
}
static int synthesize_user_creds(
const char **username,
uid_t *uid, gid_t *gid,
@@ -176,13 +191,13 @@ static int synthesize_user_creds(
*home = "/root";
if (shell)
*shell = "/bin/sh";
*shell = default_root_shell(NULL);
return 0;
}
if (synthesize_nobody() &&
STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) {
if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534") &&
synthesize_nobody()) {
*username = NOBODY_USER_NAME;
if (uid)
@@ -326,8 +341,8 @@ int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
return 0;
}
if (synthesize_nobody() &&
STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) {
if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534") &&
synthesize_nobody()) {
*groupname = NOBODY_GROUP_NAME;
if (gid)
@@ -373,8 +388,7 @@ char* uid_to_name(uid_t uid) {
/* Shortcut things to avoid NSS lookups */
if (uid == 0)
return strdup("root");
if (synthesize_nobody() &&
uid == UID_NOBODY)
if (uid == UID_NOBODY && synthesize_nobody())
return strdup(NOBODY_USER_NAME);
if (uid_is_valid(uid)) {
@@ -417,8 +431,7 @@ char* gid_to_name(gid_t gid) {
if (gid == 0)
return strdup("root");
if (synthesize_nobody() &&
gid == GID_NOBODY)
if (gid == GID_NOBODY && synthesize_nobody())
return strdup(NOBODY_GROUP_NAME);
if (gid_is_valid(gid)) {
@@ -556,43 +569,29 @@ int getgroups_alloc(gid_t** gids) {
return ngroups;
}
int get_home_dir(char **_h) {
int get_home_dir(char **ret) {
struct passwd *p;
const char *e;
char *h;
uid_t u;
assert(_h);
assert(ret);
/* Take the user specified one */
e = secure_getenv("HOME");
if (e && path_is_valid(e) && path_is_absolute(e)) {
h = strdup(e);
if (!h)
return -ENOMEM;
*_h = path_simplify(h);
return 0;
}
if (e && path_is_valid(e) && path_is_absolute(e))
goto found;
/* Hardcode home directory for root and nobody to avoid NSS */
u = getuid();
if (u == 0) {
h = strdup("/root");
if (!h)
return -ENOMEM;
*_h = h;
return 0;
e = "/root";
goto found;
}
if (synthesize_nobody() &&
u == UID_NOBODY) {
h = strdup("/");
if (!h)
return -ENOMEM;
*_h = h;
return 0;
if (u == UID_NOBODY && synthesize_nobody()) {
e = "/";
goto found;
}
/* Check the database... */
@@ -600,56 +599,42 @@ int get_home_dir(char **_h) {
p = getpwuid(u);
if (!p)
return errno_or_else(ESRCH);
e = p->pw_dir;
if (!path_is_valid(p->pw_dir) ||
!path_is_absolute(p->pw_dir))
if (!path_is_valid(e) || !path_is_absolute(e))
return -EINVAL;
h = strdup(p->pw_dir);
found:
h = strdup(e);
if (!h)
return -ENOMEM;
*_h = path_simplify(h);
*ret = path_simplify(h);
return 0;
}
int get_shell(char **_s) {
int get_shell(char **ret) {
struct passwd *p;
const char *e;
char *s;
uid_t u;
assert(_s);
assert(ret);
/* Take the user specified one */
e = secure_getenv("SHELL");
if (e && path_is_valid(e) && path_is_absolute(e)) {
s = strdup(e);
if (!s)
return -ENOMEM;
*_s = path_simplify(s);
return 0;
}
if (e && path_is_valid(e) && path_is_absolute(e))
goto found;
/* Hardcode shell for root and nobody to avoid NSS */
u = getuid();
if (u == 0) {
s = strdup("/bin/sh");
if (!s)
return -ENOMEM;
*_s = s;
return 0;
e = default_root_shell(NULL);
goto found;
}
if (synthesize_nobody() &&
u == UID_NOBODY) {
s = strdup(NOLOGIN);
if (!s)
return -ENOMEM;
*_s = s;
return 0;
if (u == UID_NOBODY && synthesize_nobody()) {
e = NOLOGIN;
goto found;
}
/* Check the database... */
@@ -657,16 +642,17 @@ int get_shell(char **_s) {
p = getpwuid(u);
if (!p)
return errno_or_else(ESRCH);
e = p->pw_shell;
if (!path_is_valid(p->pw_shell) ||
!path_is_absolute(p->pw_shell))
if (!path_is_valid(e) || !path_is_absolute(e))
return -EINVAL;
s = strdup(p->pw_shell);
found:
s = strdup(e);
if (!s)
return -ENOMEM;
*_s = path_simplify(s);
*ret = path_simplify(s);
return 0;
}

View File

@@ -55,7 +55,7 @@ int merge_gid_lists(const gid_t *list1, size_t size1, const gid_t *list2, size_t
int getgroups_alloc(gid_t** gids);
int get_home_dir(char **ret);
int get_shell(char **_ret);
int get_shell(char **ret);
int reset_uid_gid(void);
@@ -130,6 +130,7 @@ int putsgent_sane(const struct sgrp *sg, FILE *stream);
#endif
bool is_nologin_shell(const char *shell);
const char* default_root_shell(const char *root);
int is_this_me(const char *username);

View File

@@ -755,7 +755,7 @@ static int write_root_passwd(const char *passwd_path, const char *password, cons
.pw_gid = 0,
.pw_gecos = (char *) "Super User",
.pw_dir = (char *) "/root",
.pw_shell = (char *) (shell ?: "/bin/sh"),
.pw_shell = (char *) (shell ?: default_root_shell(arg_root)),
};
if (errno != ENOENT)

View File

@@ -26,7 +26,7 @@ static const struct passwd root_passwd = {
.pw_gid = 0,
.pw_gecos = (char*) "Super User",
.pw_dir = (char*) "/root",
.pw_shell = (char*) "/bin/sh",
.pw_shell = NULL,
};
static const struct spwd root_spwd = {
@@ -142,24 +142,25 @@ NSS_INITGROUPS_PROTOTYPE(systemd);
static enum nss_status copy_synthesized_passwd(
struct passwd *dest,
const struct passwd *src,
const char *fallback_shell,
char *buffer, size_t buflen,
int *errnop) {
size_t required;
assert(dest);
assert(src);
assert(src->pw_name);
assert(src->pw_passwd);
assert(src->pw_gecos);
assert(src->pw_dir);
assert(src->pw_shell);
required = strlen(src->pw_name) + 1;
required += strlen(src->pw_passwd) + 1;
required += strlen(src->pw_gecos) + 1;
required += strlen(src->pw_dir) + 1;
required += strlen(src->pw_shell) + 1;
const char *shell = ASSERT_PTR(src->pw_shell ?: fallback_shell);
size_t required =
strlen(src->pw_name) + 1 +
strlen(src->pw_passwd) + 1 +
strlen(src->pw_gecos) + 1 +
strlen(src->pw_dir) + 1 +
strlen(shell) + 1;
if (buflen < required) {
*errnop = ERANGE;
@@ -176,7 +177,7 @@ static enum nss_status copy_synthesized_passwd(
dest->pw_gecos = stpcpy(dest->pw_passwd, src->pw_passwd) + 1;
dest->pw_dir = stpcpy(dest->pw_gecos, src->pw_gecos) + 1;
dest->pw_shell = stpcpy(dest->pw_dir, src->pw_dir) + 1;
strcpy(dest->pw_shell, src->pw_shell);
strcpy(dest->pw_shell, shell);
return NSS_STATUS_SUCCESS;
}
@@ -187,15 +188,14 @@ static enum nss_status copy_synthesized_spwd(
char *buffer, size_t buflen,
int *errnop) {
size_t required;
assert(dest);
assert(src);
assert(src->sp_namp);
assert(src->sp_pwdp);
required = strlen(src->sp_namp) + 1;
required += strlen(src->sp_pwdp) + 1;
size_t required =
strlen(src->sp_namp) + 1 +
strlen(src->sp_pwdp) + 1;
if (buflen < required) {
*errnop = ERANGE;
@@ -220,8 +220,6 @@ static enum nss_status copy_synthesized_group(
char *buffer, size_t buflen,
int *errnop) {
size_t required;
assert(dest);
assert(src);
assert(src->gr_name);
@@ -229,9 +227,10 @@ static enum nss_status copy_synthesized_group(
assert(src->gr_mem);
assert(!*src->gr_mem); /* Our synthesized records' gr_mem is always just NULL... */
required = strlen(src->gr_name) + 1;
required += strlen(src->gr_passwd) + 1;
required += sizeof(char*); /* ...but that NULL still needs to be stored into the buffer! */
size_t required =
strlen(src->gr_name) + 1 +
strlen(src->gr_passwd) + 1 +
sizeof(char*); /* ...but that NULL still needs to be stored into the buffer! */
if (buflen < ALIGN(required)) {
*errnop = ERANGE;
@@ -257,15 +256,14 @@ static enum nss_status copy_synthesized_sgrp(
char *buffer, size_t buflen,
int *errnop) {
size_t required;
assert(dest);
assert(src);
assert(src->sg_namp);
assert(src->sg_passwd);
required = strlen(src->sg_namp) + 1;
required += strlen(src->sg_passwd) + 1;
size_t required =
strlen(src->sg_namp) + 1 +
strlen(src->sg_passwd) + 1;
if (buflen < required) {
*errnop = ERANGE;
@@ -310,13 +308,17 @@ enum nss_status _nss_systemd_getpwnam_r(
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
if (streq(name, root_passwd.pw_name))
return copy_synthesized_passwd(pwd, &root_passwd, buffer, buflen, errnop);
return copy_synthesized_passwd(pwd, &root_passwd,
default_root_shell(NULL),
buffer, buflen, errnop);
if (streq(name, nobody_passwd.pw_name)) {
if (!synthesize_nobody())
return NSS_STATUS_NOTFOUND;
return copy_synthesized_passwd(pwd, &nobody_passwd, buffer, buflen, errnop);
return copy_synthesized_passwd(pwd, &nobody_passwd,
NULL,
buffer, buflen, errnop);
}
} else if (STR_IN_SET(name, root_passwd.pw_name, nobody_passwd.pw_name))
@@ -354,13 +356,17 @@ enum nss_status _nss_systemd_getpwuid_r(
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
if (uid == root_passwd.pw_uid)
return copy_synthesized_passwd(pwd, &root_passwd, buffer, buflen, errnop);
return copy_synthesized_passwd(pwd, &root_passwd,
default_root_shell(NULL),
buffer, buflen, errnop);
if (uid == nobody_passwd.pw_uid) {
if (!synthesize_nobody())
return NSS_STATUS_NOTFOUND;
return copy_synthesized_passwd(pwd, &nobody_passwd, buffer, buflen, errnop);
return copy_synthesized_passwd(pwd, &nobody_passwd,
NULL,
buffer, buflen, errnop);
}
} else if (uid == root_passwd.pw_uid || uid == nobody_passwd.pw_uid)

File diff suppressed because it is too large Load Diff

View File

@@ -347,8 +347,8 @@ static void test_get_user_creds_one(const char *id, const char *name, uid_t uid,
}
TEST(get_user_creds) {
test_get_user_creds_one("root", "root", 0, 0, "/root", "/bin/sh");
test_get_user_creds_one("0", "root", 0, 0, "/root", "/bin/sh");
test_get_user_creds_one("root", "root", 0, 0, "/root", DEFAULT_USER_SHELL);
test_get_user_creds_one("0", "root", 0, 0, "/root", DEFAULT_USER_SHELL);
test_get_user_creds_one(NOBODY_USER_NAME, NOBODY_USER_NAME, UID_NOBODY, GID_NOBODY, "/", NOLOGIN);
test_get_user_creds_one("65534", NOBODY_USER_NAME, UID_NOBODY, GID_NOBODY, "/", NOLOGIN);
}

View File

@@ -26,7 +26,7 @@ ExecStart=sh -c 'test %U = $$(id -u)'
ExecStart=sh -c 'test %g = $$(id -gn)'
ExecStart=sh -c 'test %G = $$(id -g)'
ExecStart=test %h = /root
ExecStart=sh -c 'test %s = /bin/sh'
ExecStart=sh -c 'test -x %s'
ExecStart=sh -c 'test %m = $$(cat /etc/machine-id)'
ExecStart=sh -c 'test %b = $$(cat /proc/sys/kernel/random/boot_id | sed -e 's/-//g')'
ExecStart=sh -c 'test %H = $$(uname -n)'

View File

@@ -23,7 +23,7 @@ ExecStart=sh -c 'test %U = $$(id -u)'
ExecStart=sh -c 'test %g = $$(id -gn)'
ExecStart=sh -c 'test %G = $$(id -g)'
ExecStart=test %h = /root
ExecStart=sh -c 'test %s = /bin/sh'
ExecStart=sh -c 'test -x %s'
ExecStart=sh -c 'test %m = $$(cat /etc/machine-id)'
ExecStart=sh -c 'test %b = $$(cat /proc/sys/kernel/random/boot_id | sed -e 's/-//g')'
ExecStart=sh -c 'test %H = $$(uname -n)'

View File

@@ -152,7 +152,7 @@ for f in $(ls -1 $SOURCE/unhappy-*.input | sort -V); do
echo "*** Running test $f"
prepare_testdir ${f%.input}
cp $f $TESTDIR/usr/lib/sysusers.d/test.conf
$SYSUSERS --root=$TESTDIR 2>&1 | tail -n1 > $TESTDIR/err
$SYSUSERS --root=$TESTDIR 2>&1 | tail -n1 | sed -r 's/^[^:]+:[^:]+://' >$TESTDIR/err
if ! diff -u $TESTDIR/err ${f%.*}.expected-err; then
echo "**** Unexpected error output for $f"
cat $TESTDIR/err

View File

@@ -1 +1 @@
Failed to parse UID: '9999999999': Numerical result out of range
Failed to parse UID: '9999999999': Numerical result out of range