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
This commit is contained in:
David Michael 2020-01-30 14:04:14 -05:00 committed by Lennart Poettering
parent 4e3132d6d6
commit 649916d356
12 changed files with 88 additions and 23 deletions

View File

@ -101,8 +101,8 @@ u root 0 "Superuser" /root /bin/zsh</pro
<term><varname>u</varname></term>
<listitem><para>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.</para></listitem>
bearing the same name unless the ID field specifies it. The account will be
created disabled, so that logins are not allowed.</para></listitem>
</varlistentry>
<varlistentry>
@ -166,9 +166,10 @@ u root 0 "Superuser" /root /bin/zsh</pro
path's owner/group. This is useful to create users whose UID/GID
match the owners of pre-existing files (such as SUID or SGID
binaries).
The syntax <literal><replaceable>uid</replaceable>:<replaceable>gid</replaceable></literal> 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 <literal>-</literal> for the UID in this syntax
is also supported.
The syntaxes <literal><replaceable>uid</replaceable>:<replaceable>gid</replaceable></literal> and
<literal><replaceable>uid</replaceable>:<replaceable>groupname</replaceable></literal> are supported to
allow creating users with specific primary groups. The given group must be created explicitly, or it
must already exist. Specifying <literal>-</literal> for the UID in these syntaxes is also supported.
</para>
<para>For <varname>m</varname> lines, this field should contain

View File

@ -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, "-")) {

View File

@ -0,0 +1,5 @@
hoge:x:300:
baz:x:302:
yyy:x:SYSTEM_GID_MAX:
foo:x:301:
ccc:x:305:

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
pre:x:987:

View File

@ -0,0 +1 @@
aaa:x:SYSTEM_UID_MAX:987::/:NOLOGIN

View File

@ -0,0 +1 @@
pre:x:987:

View File

@ -0,0 +1,4 @@
# Ensure that a preexisting system group can be used as primary
#
#Type Name ID GECOS HOMEDIR
u aaa -:pre

View File

@ -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"
}

View File

@ -0,0 +1 @@
Group g1 not found.

View File

@ -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 -