Merge pull request #13244 from keszybz/allow-dots-in-usernames

Allow dots in usernames
This commit is contained in:
Yu Watanabe 2019-08-29 00:03:19 +09:00 committed by GitHub
commit 4b259b3c63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 146 additions and 35 deletions

View File

@ -620,16 +620,19 @@ int take_etc_passwd_lock(const char *root) {
return fd;
}
bool valid_user_group_name(const char *u) {
bool valid_user_group_name_full(const char *u, bool strict) {
const char *i;
long sz;
/* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition,
* 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules:
*
* - We don't allow any dots (this would break chown syntax which permits dots as user/group name separator)
* - We require that names fit into the appropriate utmp field
* - We don't allow empty user names
* - No dots or digits in the first character
*
* If strict==true, additionally:
* - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator)
*
* Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
*/
@ -642,12 +645,25 @@ bool valid_user_group_name(const char *u) {
u[0] != '_')
return false;
bool warned = false;
for (i = u+1; *i; i++) {
if (!(*i >= 'a' && *i <= 'z') &&
!(*i >= 'A' && *i <= 'Z') &&
!(*i >= '0' && *i <= '9') &&
!IN_SET(*i, '_', '-'))
return false;
if (((*i >= 'a' && *i <= 'z') ||
(*i >= 'A' && *i <= 'Z') ||
(*i >= '0' && *i <= '9') ||
IN_SET(*i, '_', '-')))
continue;
if (*i == '.' && !strict) {
if (!warned) {
log_warning("Bad user or group name \"%s\", accepting for compatibility.", u);
warned = true;
}
continue;
}
return false;
}
sz = sysconf(_SC_LOGIN_NAME_MAX);
@ -662,15 +678,15 @@ bool valid_user_group_name(const char *u) {
return true;
}
bool valid_user_group_name_or_id(const char *u) {
bool valid_user_group_name_or_id_full(const char *u, bool strict) {
/* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right
* range, and not the invalid user ids. */
/* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the
* right range, and not the invalid user ids. */
if (isempty(u))
return false;
if (valid_user_group_name(u))
if (valid_user_group_name_full(u, strict))
return true;
return parse_uid(u, NULL) >= 0;

View File

@ -85,8 +85,20 @@ static inline bool userns_supported(void) {
return access("/proc/self/uid_map", F_OK) >= 0;
}
bool valid_user_group_name(const char *u);
bool valid_user_group_name_or_id(const char *u);
bool valid_user_group_name_full(const char *u, bool strict);
bool valid_user_group_name_or_id_full(const char *u, bool strict);
static inline bool valid_user_group_name(const char *u) {
return valid_user_group_name_full(u, true);
}
static inline bool valid_user_group_name_or_id(const char *u) {
return valid_user_group_name_or_id_full(u, true);
}
static inline bool valid_user_group_name_compat(const char *u) {
return valid_user_group_name_full(u, false);
}
static inline bool valid_user_group_name_or_id_compat(const char *u) {
return valid_user_group_name_or_id_full(u, false);
}
bool valid_gecos(const char *d);
bool valid_home(const char *p);

View File

@ -1182,10 +1182,10 @@ int bus_exec_context_set_transient_property(
flags |= UNIT_PRIVATE;
if (streq(name, "User"))
return bus_set_transient_user(u, name, &c->user, message, flags, error);
return bus_set_transient_user_compat(u, name, &c->user, message, flags, error);
if (streq(name, "Group"))
return bus_set_transient_user(u, name, &c->group, message, flags, error);
return bus_set_transient_user_compat(u, name, &c->group, message, flags, error);
if (streq(name, "TTYPath"))
return bus_set_transient_path(u, name, &c->tty_path, message, flags, error);
@ -1363,10 +1363,10 @@ int bus_exec_context_set_transient_property(
if (r < 0)
return r;
STRV_FOREACH(p, l) {
if (!isempty(*p) && !valid_user_group_name_or_id(*p))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid supplementary group names");
}
STRV_FOREACH(p, l)
if (!isempty(*p) && !valid_user_group_name_or_id_compat(*p))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid supplementary group names");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (strv_isempty(l)) {

View File

@ -277,10 +277,10 @@ static int bus_socket_set_transient_property(
return bus_set_transient_fdname(u, name, &s->fdname, message, flags, error);
if (streq(name, "SocketUser"))
return bus_set_transient_user(u, name, &s->user, message, flags, error);
return bus_set_transient_user_compat(u, name, &s->user, message, flags, error);
if (streq(name, "SocketGroup"))
return bus_set_transient_user(u, name, &s->group, message, flags, error);
return bus_set_transient_user_compat(u, name, &s->group, message, flags, error);
if (streq(name, "BindIPv6Only"))
return bus_set_transient_bind_ipv6_only(u, name, &s->bind_ipv6_only, message, flags, error);

View File

@ -30,7 +30,7 @@ int bus_property_get_triggered_unit(
BUS_DEFINE_SET_TRANSIENT(mode_t, "u", uint32_t, mode_t, "%040o");
BUS_DEFINE_SET_TRANSIENT(unsigned, "u", uint32_t, unsigned, "%" PRIu32);
BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user, valid_user_group_name_or_id);
BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user_compat, valid_user_group_name_or_id_compat);
BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(path, path_is_absolute);
int bus_set_transient_string(

View File

@ -235,7 +235,7 @@ int bus_property_get_triggered_unit(sd_bus *bus, const char *path, const char *i
int bus_set_transient_mode_t(Unit *u, const char *name, mode_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_unsigned(Unit *u, const char *name, unsigned *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_user(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_user_compat(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_path(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_string(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_bool(Unit *u, const char *name, bool *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);

View File

@ -25,9 +25,9 @@ m4_define(`EXEC_CONTEXT_CONFIG_ITEMS',
`$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context)
$1.RootDirectory, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_directory)
$1.RootImage, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_image)
$1.User, config_parse_user_group, 0, offsetof($1, exec_context.user)
$1.Group, config_parse_user_group, 0, offsetof($1, exec_context.group)
$1.SupplementaryGroups, config_parse_user_group_strv, 0, offsetof($1, exec_context.supplementary_groups)
$1.User, config_parse_user_group_compat, 0, offsetof($1, exec_context.user)
$1.Group, config_parse_user_group_compat, 0, offsetof($1, exec_context.group)
$1.SupplementaryGroups, config_parse_user_group_strv_compat, 0, offsetof($1, exec_context.supplementary_groups)
$1.Nice, config_parse_exec_nice, 0, offsetof($1, exec_context)
$1.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof($1, exec_context)
$1.IOSchedulingClass, config_parse_exec_io_class, 0, offsetof($1, exec_context)
@ -365,8 +365,8 @@ Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC
Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command)
Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command)
Socket.TimeoutSec, config_parse_sec_fix_0, 0, offsetof(Socket, timeout_usec)
Socket.SocketUser, config_parse_user_group, 0, offsetof(Socket, user)
Socket.SocketGroup, config_parse_user_group, 0, offsetof(Socket, group)
Socket.SocketUser, config_parse_user_group_compat, 0, offsetof(Socket, user)
Socket.SocketGroup, config_parse_user_group_compat, 0, offsetof(Socket, group)
Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode)
Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode)
Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept)

View File

@ -2002,7 +2002,7 @@ int config_parse_sec_fix_0(
return 0;
}
int config_parse_user_group(
int config_parse_user_group_compat(
const char *unit,
const char *filename,
unsigned line,
@ -2035,7 +2035,7 @@ int config_parse_user_group(
return -ENOEXEC;
}
if (!valid_user_group_name_or_id(k)) {
if (!valid_user_group_name_or_id_compat(k)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
return -ENOEXEC;
}
@ -2043,7 +2043,7 @@ int config_parse_user_group(
return free_and_replace(*user, k);
}
int config_parse_user_group_strv(
int config_parse_user_group_strv_compat(
const char *unit,
const char *filename,
unsigned line,
@ -2089,7 +2089,7 @@ int config_parse_user_group_strv(
return -ENOEXEC;
}
if (!valid_user_group_name_or_id(k)) {
if (!valid_user_group_name_or_id_compat(k)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
return -ENOEXEC;
}

View File

@ -99,8 +99,8 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_utmp_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_working_directory);
CONFIG_PARSER_PROTOTYPE(config_parse_fdname);
CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0);
CONFIG_PARSER_PROTOTYPE(config_parse_user_group);
CONFIG_PARSER_PROTOTYPE(config_parse_user_group_strv);
CONFIG_PARSER_PROTOTYPE(config_parse_user_group_compat);
CONFIG_PARSER_PROTOTYPE(config_parse_user_group_strv_compat);
CONFIG_PARSER_PROTOTYPE(config_parse_restrict_namespaces);
CONFIG_PARSER_PROTOTYPE(config_parse_bind_paths);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_keyring_mode);

View File

@ -61,6 +61,43 @@ static void test_uid_ptr(void) {
assert_se(PTR_TO_UID(UID_TO_PTR(1000)) == 1000);
}
static void test_valid_user_group_name_compat(void) {
log_info("/* %s */", __func__);
assert_se(!valid_user_group_name_compat(NULL));
assert_se(!valid_user_group_name_compat(""));
assert_se(!valid_user_group_name_compat("1"));
assert_se(!valid_user_group_name_compat("65535"));
assert_se(!valid_user_group_name_compat("-1"));
assert_se(!valid_user_group_name_compat("-kkk"));
assert_se(!valid_user_group_name_compat("rööt"));
assert_se(!valid_user_group_name_compat("."));
assert_se(!valid_user_group_name_compat(".eff"));
assert_se(!valid_user_group_name_compat("foo\nbar"));
assert_se(!valid_user_group_name_compat("0123456789012345678901234567890123456789"));
assert_se(!valid_user_group_name_or_id_compat("aaa:bbb"));
assert_se(!valid_user_group_name_compat("."));
assert_se(!valid_user_group_name_compat(".1"));
assert_se(!valid_user_group_name_compat(".65535"));
assert_se(!valid_user_group_name_compat(".-1"));
assert_se(!valid_user_group_name_compat(".-kkk"));
assert_se(!valid_user_group_name_compat(".rööt"));
assert_se(!valid_user_group_name_or_id_compat(".aaa:bbb"));
assert_se(valid_user_group_name_compat("root"));
assert_se(valid_user_group_name_compat("lennart"));
assert_se(valid_user_group_name_compat("LENNART"));
assert_se(valid_user_group_name_compat("_kkk"));
assert_se(valid_user_group_name_compat("kkk-"));
assert_se(valid_user_group_name_compat("kk-k"));
assert_se(valid_user_group_name_compat("eff.eff"));
assert_se(valid_user_group_name_compat("eff."));
assert_se(valid_user_group_name_compat("some5"));
assert_se(!valid_user_group_name_compat("5some"));
assert_se(valid_user_group_name_compat("INNER5NUMBER"));
}
static void test_valid_user_group_name(void) {
log_info("/* %s */", __func__);
@ -72,10 +109,17 @@ static void test_valid_user_group_name(void) {
assert_se(!valid_user_group_name("-kkk"));
assert_se(!valid_user_group_name("rööt"));
assert_se(!valid_user_group_name("."));
assert_se(!valid_user_group_name("eff.eff"));
assert_se(!valid_user_group_name(".eff"));
assert_se(!valid_user_group_name("foo\nbar"));
assert_se(!valid_user_group_name("0123456789012345678901234567890123456789"));
assert_se(!valid_user_group_name_or_id("aaa:bbb"));
assert_se(!valid_user_group_name("."));
assert_se(!valid_user_group_name(".1"));
assert_se(!valid_user_group_name(".65535"));
assert_se(!valid_user_group_name(".-1"));
assert_se(!valid_user_group_name(".-kkk"));
assert_se(!valid_user_group_name(".rööt"));
assert_se(!valid_user_group_name_or_id(".aaa:bbb"));
assert_se(valid_user_group_name("root"));
assert_se(valid_user_group_name("lennart"));
@ -83,12 +127,47 @@ static void test_valid_user_group_name(void) {
assert_se(valid_user_group_name("_kkk"));
assert_se(valid_user_group_name("kkk-"));
assert_se(valid_user_group_name("kk-k"));
assert_se(!valid_user_group_name("eff.eff"));
assert_se(!valid_user_group_name("eff."));
assert_se(valid_user_group_name("some5"));
assert_se(!valid_user_group_name("5some"));
assert_se(valid_user_group_name("INNER5NUMBER"));
}
static void test_valid_user_group_name_or_id_compat(void) {
log_info("/* %s */", __func__);
assert_se(!valid_user_group_name_or_id_compat(NULL));
assert_se(!valid_user_group_name_or_id_compat(""));
assert_se(valid_user_group_name_or_id_compat("0"));
assert_se(valid_user_group_name_or_id_compat("1"));
assert_se(valid_user_group_name_or_id_compat("65534"));
assert_se(!valid_user_group_name_or_id_compat("65535"));
assert_se(valid_user_group_name_or_id_compat("65536"));
assert_se(!valid_user_group_name_or_id_compat("-1"));
assert_se(!valid_user_group_name_or_id_compat("-kkk"));
assert_se(!valid_user_group_name_or_id_compat("rööt"));
assert_se(!valid_user_group_name_or_id_compat("."));
assert_se(!valid_user_group_name_or_id_compat(".eff"));
assert_se(valid_user_group_name_or_id_compat("eff.eff"));
assert_se(valid_user_group_name_or_id_compat("eff."));
assert_se(!valid_user_group_name_or_id_compat("foo\nbar"));
assert_se(!valid_user_group_name_or_id_compat("0123456789012345678901234567890123456789"));
assert_se(!valid_user_group_name_or_id_compat("aaa:bbb"));
assert_se(valid_user_group_name_or_id_compat("root"));
assert_se(valid_user_group_name_or_id_compat("lennart"));
assert_se(valid_user_group_name_or_id_compat("LENNART"));
assert_se(valid_user_group_name_or_id_compat("_kkk"));
assert_se(valid_user_group_name_or_id_compat("kkk-"));
assert_se(valid_user_group_name_or_id_compat("kk-k"));
assert_se(valid_user_group_name_or_id_compat("some5"));
assert_se(!valid_user_group_name_or_id_compat("5some"));
assert_se(valid_user_group_name_or_id_compat("INNER5NUMBER"));
}
static void test_valid_user_group_name_or_id(void) {
log_info("/* %s */", __func__);
@ -103,7 +182,9 @@ static void test_valid_user_group_name_or_id(void) {
assert_se(!valid_user_group_name_or_id("-kkk"));
assert_se(!valid_user_group_name_or_id("rööt"));
assert_se(!valid_user_group_name_or_id("."));
assert_se(!valid_user_group_name_or_id(".eff"));
assert_se(!valid_user_group_name_or_id("eff.eff"));
assert_se(!valid_user_group_name_or_id("eff."));
assert_se(!valid_user_group_name_or_id("foo\nbar"));
assert_se(!valid_user_group_name_or_id("0123456789012345678901234567890123456789"));
assert_se(!valid_user_group_name_or_id("aaa:bbb"));
@ -230,7 +311,9 @@ int main(int argc, char *argv[]) {
test_parse_uid();
test_uid_ptr();
test_valid_user_group_name_compat();
test_valid_user_group_name();
test_valid_user_group_name_or_id_compat();
test_valid_user_group_name_or_id();
test_valid_gecos();
test_valid_home();