sysusers: allow uid:gid in sysusers.conf files
authorMichael Vogt <mvo@ubuntu.com>
Tue, 23 Jan 2018 18:53:08 +0000 (19:53 +0100)
committerMichael Vogt <mvo@ubuntu.com>
Thu, 25 Jan 2018 11:50:37 +0000 (12:50 +0100)
This PR allows to write sysuser.conf lines like:
```
u games 5:60 -
```
This will create an a "games" user with uid 5 and games group with
gid 60. This is arguable ugly, however it is required to represent
certain configurations like the default passwd file on Debian and
Ubuntu.

When the ":" syntax is used and there is a group with the given
gid already then no new group is created. This allows writing the
following:
```
g unrelated 60
u games 5:60 -
```
which will create a "games" user with the uid 5 and the primary
gid 60. No group games is created here (might be useful for [1]).

[1] https://pagure.io/packaging-committee/issue/442

man/sysusers.d.xml
src/sysusers/sysusers.c

index 38b749c..a4083ad 100644 (file)
@@ -191,7 +191,10 @@ u      root   0               "Superuser"           /root</programlisting>
       in the file system. In this case, the UID/GID is read from the
       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).</para>
+      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. If a group with the indicated GID already exists it is not created.
+      </para>
 
       <para>For <varname>m</varname> lines, this field should contain
       the group name to add to a user to.</para>
index d800945..1845b80 100644 (file)
@@ -64,6 +64,7 @@ typedef struct Item {
         uid_t uid;
 
         bool gid_set:1;
+        bool gid_existing_ok:1;
         bool uid_set:1;
 
         bool todo_user:1;
@@ -1100,6 +1101,8 @@ static int add_group(Item *i) {
                         return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
                 if (r == 0) {
                         log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
+                        if (i->gid_existing_ok)
+                                return 0;
                         i->gid_set = false;
                 }
         }
@@ -1551,11 +1554,18 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
 
                                 path_kill_slashes(i->uid_path);
                         } else {
-                                r = parse_uid(resolved_id, &i->uid);
-                                if (r < 0) {
-                                        log_error("Failed to parse UID: %s", id);
-                                        return -EBADMSG;
+                                _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->gid_existing_ok = true;
+                                        free_and_replace(resolved_id, uid);
                                 }
+                                r = parse_uid(resolved_id, &i->uid);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse UID: '%s': %m", id);
 
                                 i->uid_set = true;
                         }
@@ -1602,10 +1612,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                                 path_kill_slashes(i->gid_path);
                         } else {
                                 r = parse_gid(resolved_id, &i->gid);
-                                if (r < 0) {
-                                        log_error("Failed to parse GID: %s", id);
-                                        return -EBADMSG;
-                                }
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
 
                                 i->gid_set = true;
                         }