From 649916d3561ac41b41bfc5be6297a78847509abe Mon Sep 17 00:00:00 2001 From: David Michael Date: Thu, 30 Jan 2020 14:04:14 -0500 Subject: [PATCH] sysusers: support creating users with a specific primary group This extends the "uid:gid" syntax for "u" lines so that a group name can be given instead of a GID. This requires that the group is either queued for creation by sysusers, or it is already defined on the system. Closes #14340 --- man/sysusers.d.xml | 11 ++-- src/sysusers/sysusers.c | 63 +++++++++++++------ test/TEST-21-SYSUSERS/test-13.expected-group | 5 ++ test/TEST-21-SYSUSERS/test-13.expected-passwd | 5 ++ test/TEST-21-SYSUSERS/test-13.input | 13 ++++ test/TEST-21-SYSUSERS/test-14.expected-group | 1 + test/TEST-21-SYSUSERS/test-14.expected-passwd | 1 + test/TEST-21-SYSUSERS/test-14.initial-group | 1 + test/TEST-21-SYSUSERS/test-14.input | 4 ++ test/TEST-21-SYSUSERS/test.sh | 2 + test/TEST-21-SYSUSERS/unhappy-3.expected-err | 1 + test/TEST-21-SYSUSERS/unhappy-3.input | 4 ++ 12 files changed, 88 insertions(+), 23 deletions(-) create mode 100644 test/TEST-21-SYSUSERS/test-13.expected-group create mode 100644 test/TEST-21-SYSUSERS/test-13.expected-passwd create mode 100644 test/TEST-21-SYSUSERS/test-13.input create mode 100644 test/TEST-21-SYSUSERS/test-14.expected-group create mode 100644 test/TEST-21-SYSUSERS/test-14.expected-passwd create mode 100644 test/TEST-21-SYSUSERS/test-14.initial-group create mode 100644 test/TEST-21-SYSUSERS/test-14.input create mode 100644 test/TEST-21-SYSUSERS/unhappy-3.expected-err create mode 100644 test/TEST-21-SYSUSERS/unhappy-3.input diff --git a/man/sysusers.d.xml b/man/sysusers.d.xml index 2e93715be6..72d8f62399 100644 --- a/man/sysusers.d.xml +++ b/man/sysusers.d.xml @@ -101,8 +101,8 @@ u root 0 "Superuser" /root /bin/zshu Create a system user and group of the specified name should they not exist yet. The user's primary group will be set to the group - bearing the same name. The account will be created disabled, so that logins - are not allowed. + bearing the same name unless the ID field specifies it. The account will be + created disabled, so that logins are not allowed. @@ -166,9 +166,10 @@ u root 0 "Superuser" /root /bin/zshuid:gid is also supported to - allow creating user and group pairs with different numeric UID and GID values. The group with the indicated GID must get created explicitly before or it must already exist. Specifying - for the UID in this syntax - is also supported. + The syntaxes uid:gid and + uid:groupname are supported to + allow creating users with specific primary groups. The given group must be created explicitly, or it + must already exist. Specifying - for the UID in these syntaxes is also supported. For m lines, this field should contain diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 08a2df707b..2771fd959f 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -39,6 +39,7 @@ typedef struct Item { ItemType type; char *name; + char *group_name; char *uid_path; char *gid_path; char *description; @@ -1085,18 +1086,15 @@ static int gid_is_ok(gid_t gid) { return 1; } -static int add_group(Item *i) { +static int get_gid_by_name(const char *name, gid_t *gid) { void *z; - int r; - assert(i); + assert(gid); /* Check the database directly */ - z = hashmap_get(database_by_groupname, i->name); + z = hashmap_get(database_by_groupname, name); if (z) { - log_debug("Group %s already exists.", i->name); - i->gid = PTR_TO_GID(z); - i->gid_set = true; + *gid = PTR_TO_GID(z); return 0; } @@ -1105,15 +1103,30 @@ static int add_group(Item *i) { struct group *g; errno = 0; - g = getgrnam(i->name); + g = getgrnam(name); if (g) { - log_debug("Group %s already exists.", i->name); - i->gid = g->gr_gid; - i->gid_set = true; + *gid = g->gr_gid; return 0; } if (!IN_SET(errno, 0, ENOENT)) - return log_error_errno(errno, "Failed to check if group %s already exists: %m", i->name); + return log_error_errno(errno, "Failed to check if group %s already exists: %m", name); + } + + return -ENOENT; +} + +static int add_group(Item *i) { + int r; + + assert(i); + + r = get_gid_by_name(i->name, &i->gid); + if (r != -ENOENT) { + if (r < 0) + return r; + log_debug("Group %s already exists.", i->name); + i->gid_set = true; + return 0; } /* Try to use the suggested numeric gid */ @@ -1214,14 +1227,22 @@ static int process_item(Item *i) { case ADD_USER: { Item *j; - j = ordered_hashmap_get(groups, i->name); + j = ordered_hashmap_get(groups, i->group_name ?: i->name); if (j && j->todo_group) { - /* When the group with the same name is already in queue, + /* When a group with the target name is already in queue, * use the information about the group and do not create * duplicated group entry. */ i->gid_set = j->gid_set; i->gid = j->gid; i->id_set_strict = true; + } else if (i->group_name) { + /* When a group name was given instead of a GID and it's + * not in queue, then it must already exist. */ + r = get_gid_by_name(i->group_name, &i->gid); + if (r < 0) + return log_error_errno(r, "Group %s not found.", i->group_name); + i->gid_set = true; + i->id_set_strict = true; } else { r = add_group(i); if (r < 0) @@ -1244,6 +1265,7 @@ static Item* item_free(Item *i) { return NULL; free(i->name); + free(i->group_name); free(i->uid_path); free(i->gid_path); free(i->description); @@ -1560,10 +1582,15 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { _cleanup_free_ char *uid = NULL, *gid = NULL; if (split_pair(resolved_id, ":", &uid, &gid) == 0) { r = parse_gid(gid, &i->gid); - if (r < 0) - return log_error_errno(r, "Failed to parse GID: '%s': %m", id); - i->gid_set = true; - i->id_set_strict = true; + if (r < 0) { + if (valid_user_group_name(gid)) + i->group_name = TAKE_PTR(gid); + else + return log_error_errno(r, "Failed to parse GID: '%s': %m", id); + } else { + i->gid_set = true; + i->id_set_strict = true; + } free_and_replace(resolved_id, uid); } if (!streq(resolved_id, "-")) { diff --git a/test/TEST-21-SYSUSERS/test-13.expected-group b/test/TEST-21-SYSUSERS/test-13.expected-group new file mode 100644 index 0000000000..c78ea54bd3 --- /dev/null +++ b/test/TEST-21-SYSUSERS/test-13.expected-group @@ -0,0 +1,5 @@ +hoge:x:300: +baz:x:302: +yyy:x:SYSTEM_GID_MAX: +foo:x:301: +ccc:x:305: diff --git a/test/TEST-21-SYSUSERS/test-13.expected-passwd b/test/TEST-21-SYSUSERS/test-13.expected-passwd new file mode 100644 index 0000000000..ffc20a8193 --- /dev/null +++ b/test/TEST-21-SYSUSERS/test-13.expected-passwd @@ -0,0 +1,5 @@ +foo:x:301:301::/:NOLOGIN +aaa:x:303:302::/:NOLOGIN +bbb:x:304:302::/:NOLOGIN +ccc:x:305:305::/:NOLOGIN +zzz:x:306:SYSTEM_GID_MAX::/:NOLOGIN diff --git a/test/TEST-21-SYSUSERS/test-13.input b/test/TEST-21-SYSUSERS/test-13.input new file mode 100644 index 0000000000..bad2f09505 --- /dev/null +++ b/test/TEST-21-SYSUSERS/test-13.input @@ -0,0 +1,13 @@ +# Ensure that the semantic for the uid:groupname syntax is correct +# +#Type Name ID GECOS HOMEDIR +g hoge 300 - - +u foo 301 - - + +g baz 302 - - +u aaa 303:baz - - +u bbb 304:baz - - +u ccc 305 - - + +g yyy - +u zzz 306:yyy diff --git a/test/TEST-21-SYSUSERS/test-14.expected-group b/test/TEST-21-SYSUSERS/test-14.expected-group new file mode 100644 index 0000000000..2e619bc5cd --- /dev/null +++ b/test/TEST-21-SYSUSERS/test-14.expected-group @@ -0,0 +1 @@ +pre:x:987: diff --git a/test/TEST-21-SYSUSERS/test-14.expected-passwd b/test/TEST-21-SYSUSERS/test-14.expected-passwd new file mode 100644 index 0000000000..62ed4f50af --- /dev/null +++ b/test/TEST-21-SYSUSERS/test-14.expected-passwd @@ -0,0 +1 @@ +aaa:x:SYSTEM_UID_MAX:987::/:NOLOGIN diff --git a/test/TEST-21-SYSUSERS/test-14.initial-group b/test/TEST-21-SYSUSERS/test-14.initial-group new file mode 100644 index 0000000000..2e619bc5cd --- /dev/null +++ b/test/TEST-21-SYSUSERS/test-14.initial-group @@ -0,0 +1 @@ +pre:x:987: diff --git a/test/TEST-21-SYSUSERS/test-14.input b/test/TEST-21-SYSUSERS/test-14.input new file mode 100644 index 0000000000..0a11a2e325 --- /dev/null +++ b/test/TEST-21-SYSUSERS/test-14.input @@ -0,0 +1,4 @@ +# Ensure that a preexisting system group can be used as primary +# +#Type Name ID GECOS HOMEDIR +u aaa -:pre diff --git a/test/TEST-21-SYSUSERS/test.sh b/test/TEST-21-SYSUSERS/test.sh index add16ea19f..aed921e39e 100755 --- a/test/TEST-21-SYSUSERS/test.sh +++ b/test/TEST-21-SYSUSERS/test.sh @@ -23,6 +23,7 @@ preprocess() { # get this value from config.h, however the autopkgtest fails with # it SYSTEM_UID_MAX=$(awk 'BEGIN { uid=999 } /^\s*SYS_UID_MAX\s+/ { uid=$2 } END { print uid }' /etc/login.defs) + SYSTEM_GID_MAX=$(awk 'BEGIN { gid=999 } /^\s*SYS_GID_MAX\s+/ { gid=$2 } END { print gid }' /etc/login.defs) # we can't rely on config.h to get the nologin path, as autopkgtest # uses pre-compiled binaries, so extract it from the systemd-sysusers @@ -30,6 +31,7 @@ preprocess() { NOLOGIN=$(strings $(type -p systemd-sysusers) | grep nologin) sed -e "s/SYSTEM_UID_MAX/${SYSTEM_UID_MAX}/g" \ + -e "s/SYSTEM_GID_MAX/${SYSTEM_GID_MAX}/g" \ -e "s#NOLOGIN#${NOLOGIN}#g" "$in" } diff --git a/test/TEST-21-SYSUSERS/unhappy-3.expected-err b/test/TEST-21-SYSUSERS/unhappy-3.expected-err new file mode 100644 index 0000000000..d55b366372 --- /dev/null +++ b/test/TEST-21-SYSUSERS/unhappy-3.expected-err @@ -0,0 +1 @@ +Group g1 not found. diff --git a/test/TEST-21-SYSUSERS/unhappy-3.input b/test/TEST-21-SYSUSERS/unhappy-3.input new file mode 100644 index 0000000000..64e60dd606 --- /dev/null +++ b/test/TEST-21-SYSUSERS/unhappy-3.input @@ -0,0 +1,4 @@ +# Ensure it is not allowed to create groups implicitly in the uid:groupname syntax +# +#Type Name ID GECOS HOMEDIR +u u1 100:g1 -