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>
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 */
/*
* 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
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);
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;
}
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,
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 */
"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);
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;
}
/**