From: Alejandro Colomar Date: Mon, 23 Dec 2024 10:06:33 +0000 (+0100) Subject: lib/chkname.c, src/: Strictly disallow really bad names X-Git-Tag: 4.19.0~15 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=25aea7422615;p=thirdparty%2Fshadow.git lib/chkname.c, src/: Strictly disallow really bad names Some names are bad, and some names are really bad. '--badname' should only allow the mildly bad ones, which we can handle. Some names are too bad, and it's not possible to deal with them. Reject them unconditionally. - A leading '-' is too dangerous. It breaks things like execve(2), and almost every command. - Spaces are used for delimiting lists of users and groups. - '"' is special in many languages, including the shell. Having it in user names would be unnecessarily dangerous. - '#' is used for delimiting comments in several of our config files. Having it in usernames could result in incorrect configuration files. - "'" is special in many languages, including the shell. Having it in user names would be unnecessarily dangerous. - ',' is used for delimiting lists of users and groups. - '/' is used for delimiting files, and thus could result in incorrect handling of users and groups. - ':' is the main delimiter in /etc/shadow and /etc/passwd. - ';' is special in many languages, including the shell. Having it in user names would be unnecessarily dangerous. There are other characters that we should disallow, but they need more research to make sure we don't introduce regressions. This set should be less problematic. Acked-by: Tobias Stoeckmann Reviewed-by: Iker Pedrosa Reviewed-by: Chris Hofstaedtler Cc: Marc 'Zugschlus' Haber Cc: Serge Hallyn Signed-off-by: Alejandro Colomar --- diff --git a/lib/chkname.c b/lib/chkname.c index 6abd34fff..0abee4d29 100644 --- a/lib/chkname.c +++ b/lib/chkname.c @@ -13,7 +13,8 @@ * true - OK * false - bad name * errors: - * EINVAL Invalid name characters or sequences + * EINVAL Invalid name + * EILSEQ Invalid name character sequence (acceptable with --badname) * EOVERFLOW Name longer than maximum size */ @@ -27,12 +28,15 @@ #include #include #include +#include #include #include "defines.h" #include "chkname.h" +#include "string/ctype/strchrisascii/strchriscntrl.h" #include "string/ctype/strisascii/strisdigit.h" #include "string/strcmp/streq.h" +#include "string/strcmp/strcaseeq.h" #ifndef LOGIN_NAME_MAX @@ -59,6 +63,18 @@ login_name_max_size(void) static bool is_valid_name(const char *name) { + if (streq(name, "") + || streq(name, ".") + || streq(name, "..") + || strspn(name, "-") + || strpbrk(name, " \"#',/:;") + || strchriscntrl(name) + || strisdigit(name)) + { + errno = EINVAL; + return false; + } + if (allow_bad_names) { return true; } @@ -69,25 +85,15 @@ is_valid_name(const char *name) * * as a non-POSIX, extension, allow "$" as the last char for * sake of Samba 3.x "add machine script" - * - * Also do not allow fully numeric names or just "." or "..". */ - if (strisdigit(name)) { - errno = EINVAL; - return false; - } - - if (streq(name, "") || - streq(name, ".") || - streq(name, "..") || - !((*name >= 'a' && *name <= 'z') || + if (!((*name >= 'a' && *name <= 'z') || (*name >= 'A' && *name <= 'Z') || (*name >= '0' && *name <= '9') || *name == '_' || *name == '.')) { - errno = EINVAL; + errno = EILSEQ; return false; } @@ -101,7 +107,7 @@ is_valid_name(const char *name) streq(name, "$") )) { - errno = EINVAL; + errno = EILSEQ; return false; } } diff --git a/src/newusers.c b/src/newusers.c index 8af65b7a2..e9353fdc0 100644 --- a/src/newusers.c +++ b/src/newusers.c @@ -398,7 +398,7 @@ static int add_user (const char *name, uid_t uid, gid_t gid) /* Check if this is a valid user name */ if (!is_valid_user_name(name)) { - if (errno == EINVAL) { + if (errno == EILSEQ) { fprintf(stderr, _("%s: invalid user name '%s': use --badname to ignore\n"), Prog, name); diff --git a/src/pwck.c b/src/pwck.c index 0e7dfe7c9..c35f03e69 100644 --- a/src/pwck.c +++ b/src/pwck.c @@ -492,7 +492,7 @@ static void check_pw_file(bool *errors, bool *changed, const struct option_flags */ if (!is_valid_user_name(pwd->pw_name)) { - if (errno == EINVAL) { + if (errno == EILSEQ) { printf(_("invalid user name '%s': use --badname to ignore\n"), pwd->pw_name); } else { diff --git a/src/useradd.c b/src/useradd.c index a71a049da..899efe3c8 100644 --- a/src/useradd.c +++ b/src/useradd.c @@ -1498,7 +1498,7 @@ static void process_flags (int argc, char **argv, struct option_flags *flags) user_name = argv[optind]; if (!is_valid_user_name(user_name)) { - if (errno == EINVAL) { + if (errno == EILSEQ) { fprintf(stderr, _("%s: invalid user name '%s': use --badname to ignore\n"), Prog, user_name); diff --git a/src/usermod.c b/src/usermod.c index 2418514dd..e8c9da668 100644 --- a/src/usermod.c +++ b/src/usermod.c @@ -1137,7 +1137,7 @@ process_flags(int argc, char **argv, struct option_flags *flags) /*@notreached@*/break; case 'l': if (!is_valid_user_name(optarg)) { - if (errno == EINVAL) { + if (errno == EILSEQ) { fprintf(stderr, _("%s: invalid user name '%s': use --badname to ignore\n"), Prog, optarg);