diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2022-08-19 16:43:45 +0200 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2022-08-24 10:02:46 +0200 |
commit | 8a7adccbdb23ae6fee82840ef41d17d5e568a8f2 (patch) | |
tree | 43c2e7748cf432e98c640b2984385a70960db36e | |
parent | 5f465fda4ec9f1e70a1bb993944ea92b2469b0db (diff) | |
download | systemd-8a7adccbdb23ae6fee82840ef41d17d5e568a8f2.tar.gz |
various: try to use DEFAULT_USER_SHELL for root too
/bin/sh as a shell is punishing. There is no good reason to make
the occasional root login unpleasant.
Since /bin/sh is usually /bin/bash in compat mode, i.e. if one is
available, the other will be too, /bin/bash is almost as good as a default.
But to avoid a regression in the situation where /bin/bash (or
DEFAULT_USER_SHELL) is not installed, we check with access() and fall back
to /bin/sh. This should make this change in behaviour less risky.
(FWIW, e.g. Fedora/RHEL use /bin/bash as default for root.)
This is a follow-up of sorts for 53350c7bbade8c5f357aa3d1029ef9b2208ea675,
which added the default-user-shell option, but most likely with the idea
of using /bin/bash less ;)
Fixes #24369.
-rw-r--r-- | man/systemd.unit.xml | 2 | ||||
-rw-r--r-- | src/basic/user-util.c | 21 | ||||
-rw-r--r-- | src/basic/user-util.h | 1 | ||||
-rw-r--r-- | src/firstboot/firstboot.c | 2 | ||||
-rw-r--r-- | src/nss-systemd/nss-systemd.c | 60 | ||||
-rw-r--r-- | src/sysusers/sysusers.c | 2 | ||||
-rw-r--r-- | src/test/test-user-util.c | 4 | ||||
-rw-r--r-- | test/test-execute/exec-specifier.service | 2 | ||||
-rw-r--r-- | test/test-execute/exec-specifier@.service | 2 |
9 files changed, 59 insertions, 37 deletions
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index ea95ba8869..c4c6be98a5 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -2118,7 +2118,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> diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 16185332f9..37ccb667b2 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -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,7 +191,7 @@ static int synthesize_user_creds( *home = "/root"; if (shell) - *shell = "/bin/sh"; + *shell = default_root_shell(NULL); return 0; } @@ -635,7 +650,7 @@ int get_shell(char **_s) { /* Hardcode shell for root and nobody to avoid NSS */ u = getuid(); if (u == 0) { - s = strdup("/bin/sh"); + s = strdup(default_root_shell(NULL)); if (!s) return -ENOMEM; diff --git a/src/basic/user-util.h b/src/basic/user-util.h index e1692c4f66..c8f0758541 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -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); diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 9169129a68..fd9954b54d 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -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) diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index e24828450f..75d749e736 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -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) diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 5eb67ea084..393d2cc0fc 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -398,7 +398,7 @@ static const char* pick_shell(const Item *i) { if (i->shell) return i->shell; if (i->uid_set && i->uid == 0) - return "/bin/sh"; + return default_root_shell(arg_root); return NOLOGIN; } diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c index 907de54eaa..48d9b1e0fb 100644 --- a/src/test/test-user-util.c +++ b/src/test/test-user-util.c @@ -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); } diff --git a/test/test-execute/exec-specifier.service b/test/test-execute/exec-specifier.service index ae8b2428bc..321d0e338a 100644 --- a/test/test-execute/exec-specifier.service +++ b/test/test-execute/exec-specifier.service @@ -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)' diff --git a/test/test-execute/exec-specifier@.service b/test/test-execute/exec-specifier@.service index 5e30efce4c..46c8503f1d 100644 --- a/test/test-execute/exec-specifier@.service +++ b/test/test-execute/exec-specifier@.service @@ -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)' |