]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
pwdutils: extend ul_getuserpw_str() and ul_getgrp_str() to return ID
authorKarel Zak <kzak@redhat.com>
Mon, 23 Mar 2026 11:13:11 +0000 (12:13 +0100)
committerKarel Zak <kzak@redhat.com>
Mon, 23 Mar 2026 11:13:11 +0000 (12:13 +0100)
Add an optional result parameter to ul_getuserpw_str() and
ul_getgrp_str() to return the parsed UID/GID even when getpwuid() or
getgrgid() returns NULL (i.e., the numeric ID has no entry in the
passwd/group database).

This avoids the need for callers to re-parse the string when they only
need the numeric ID.

Also zero errno before calling getpwnam()/getgrnam() to prevent stale
errno from the preceding ul_strtou64() call from leaking to callers.

This fixes unshare --map-user/--map-group regression introduced in
commit 0a7fb8061 where a valid numeric UID/GID without a passwd/group
entry would cause a failure instead of being used directly.

Addresses: https://github.com/util-linux/util-linux/pull/4134
Signed-off-by: Karel Zak <kzak@redhat.com>
include/pwdutils.h
lib/pwdutils.c
login-utils/chfn.c
login-utils/login.c
login-utils/su-common.c
sys-utils/unshare.c

index 321b2ac59665a559b68b1f374cc3b127e8e9a2b7..dd852e1a4d5ce0300a80fc0700771147d2bab565 100644 (file)
@@ -13,8 +13,8 @@ extern struct passwd *xgetpwuid(uid_t uid, char **pwdbuf);
 extern struct passwd *xgetuserpw(const char *str, char **pwdbuf);
 extern struct group *xgetgroup(const char *str, char **pwdbuf);
 extern char *xgetlogin(void);
-extern struct group *ul_getgrp_str(const char *str);
-extern struct passwd *ul_getuserpw_str(const char *str);
+extern struct group *ul_getgrp_str(const char *str, gid_t *gid);
+extern struct passwd *ul_getuserpw_str(const char *str, uid_t *uid);
 
 #endif /* UTIL_LINUX_PWDUTILS_H */
 
index c5c4a76dfdecfa5d3bc2210cb379d2761c92b298..1c08eecc9b604f5ced0b5f5f1182f30d030f76b9 100644 (file)
@@ -163,40 +163,68 @@ char *xgetlogin(void)
 
 /*
  * Return a pointer to a `struct group` for a matching group name or GID.
+ *
+ * If @result is not NULL, it will always be set to the parsed GID on success
+ * or (gid_t) -1 on failure.
  */
-struct group *ul_getgrp_str(const char *str)
+struct group *ul_getgrp_str(const char *str, gid_t *result)
 {
         int rc;
-        uint64_t gid;
+        uint64_t num;
+        struct group *gr;
 
-        rc = ul_strtou64(str, &gid, 10);
+        if (result)
+                *result = (gid_t) -1;
+
+        rc = ul_strtou64(str, &num, 10);
         if (rc == -ERANGE)
                 return NULL;
-        if (rc == -EINVAL)
-                return getgrnam(str);
-        if (gid > MAX_OF_UINT_TYPE(gid_t))
+        if (rc == -EINVAL) {
+                errno = 0;
+                gr = getgrnam(str);
+                if (gr && result)
+                        *result = gr->gr_gid;
+                return gr;
+        }
+        if (num > MAX_OF_UINT_TYPE(gid_t))
                 return NULL;
 
-        return getgrgid((gid_t)gid);
+        if (result)
+                *result = (gid_t) num;
+        return getgrgid((gid_t) num);
 }
 
 /*
  * Return a pointer to a `struct passwd` for a matching username or UID.
+ *
+ * If @result is not NULL, it will always be set to the parsed UID on success
+ * or (uid_t) -1 on failure.
  */
-struct passwd *ul_getuserpw_str(const char *str)
+struct passwd *ul_getuserpw_str(const char *str, uid_t *result)
 {
         int rc;
-        uint64_t uid;
+        uint64_t num;
+        struct passwd *pw;
+
+        if (result)
+                *result = (uid_t) -1;
 
-        rc = ul_strtou64(str, &uid, 10);
+        rc = ul_strtou64(str, &num, 10);
         if (rc == -ERANGE)
                 return NULL;
-        if (rc == -EINVAL)
-                return getpwnam(str);
-        if (uid > MAX_OF_UINT_TYPE(uid_t))
+        if (rc == -EINVAL) {
+                errno = 0;
+                pw = getpwnam(str);
+                if (pw && result)
+                        *result = pw->pw_uid;
+                return pw;
+        }
+        if (num > MAX_OF_UINT_TYPE(uid_t))
                 return NULL;
 
-        return getpwuid((uid_t)uid);
+        if (result)
+                *result = (uid_t) num;
+        return getpwuid((uid_t) num);
 }
 
 #ifdef TEST_PROGRAM
index 2a0f599d1437cc3efa1b3875b46bd3e6d3905c78..e0f4a0dd050baf02d4b63d37a033851a37916a1d 100644 (file)
@@ -448,7 +448,7 @@ int main(int argc, char **argv)
                        errx(EXIT_FAILURE, _("your user %d does not exist"),
                             uid);
        } else {
-               ctl.pw = ul_getuserpw_str(ctl.username);
+               ctl.pw = ul_getuserpw_str(ctl.username, NULL);
                if (!ctl.pw)
                        errx(EXIT_FAILURE, _("user \"%s\" does not exist"),
                             ctl.username);
index 167975df03189339125438292242af01e6db5baa..72b6fc27f269fe1dc53289ccf5f247881ea8cb51 100644 (file)
@@ -496,7 +496,7 @@ static void chown_tty(struct login_context *cxt)
 
        grname = getlogindefs_str("TTYGROUP", TTYGRPNAME);
        if (grname && *grname) {
-               struct group *gr = ul_getgrp_str(grname);
+               struct group *gr = ul_getgrp_str(grname, NULL);
                if (gr)
                        gid = gr->gr_gid;
        }
@@ -659,7 +659,7 @@ static void log_audit(struct login_context *cxt, int status)
        if (audit_fd == -1)
                return;
        if (!pwd && cxt->username)
-               pwd = ul_getuserpw_str(cxt->username);
+               pwd = ul_getuserpw_str(cxt->username, NULL);
 
        ignore_result( audit_log_acct_message(audit_fd,
                                              AUDIT_USER_LOGIN,
index d51fca09ebe60ec72740fc0ebde902aecb775370..ca01110cd08fbb4cb13ca6ee75540e25828099a8 100644 (file)
@@ -264,7 +264,7 @@ static void chownmod_pty(struct su_context *su)
        const char *grname = getlogindefs_str("TTYGROUP", TTYGRPNAME);
 
        if (grname && *grname) {
-               struct group *gr = ul_getgrp_str(grname);
+               struct group *gr = ul_getgrp_str(grname, NULL);
                if (gr) /* group by name */
                        gid = gr->gr_gid;
                else    /* group by ID */
@@ -967,7 +967,7 @@ static gid_t add_supp_group(const char *name, gid_t **groups, size_t *ngroups)
                        "specifying more than %d supplemental groups is not possible",
                        NGROUPS_MAX - 1), NGROUPS_MAX - 1);
 
-       gr = ul_getgrp_str(name);
+       gr = ul_getgrp_str(name, NULL);
        if (!gr)
                errx(EXIT_FAILURE, _("group %s does not exist"), name);
 
index a4d28892d3addb6a3d1f097864023e0064b8c1f4..9a7c9f617dd8ec3b8a91d3537a07a36103084ac1 100644 (file)
@@ -325,22 +325,20 @@ static pid_t bind_ns_files_from_child(int *fd)
 
 static uid_t get_user(const char *s)
 {
-       struct passwd *pw;
+       uid_t uid;
 
-       pw = ul_getuserpw_str(s);
-       if (!pw)
+       if (!ul_getuserpw_str(s, &uid) && uid == (uid_t) -1)
                errx(EXIT_FAILURE, _("failed to parse uid '%s'"), s);
-       return pw->pw_uid;
+       return uid;
 }
 
 static gid_t get_group(const char *s)
 {
-       struct group *gr;
+       gid_t gid;
 
-       gr = ul_getgrp_str(s);
-       if (!gr)
+       if (!ul_getgrp_str(s, &gid) && gid == (gid_t) -1)
                errx(EXIT_FAILURE, _("failed to parse gid '%s'"), s);
-       return gr->gr_gid;
+       return gid;
 }
 
 /**