git: 91eb4d2ba4de - main - chroot: slightly cleanup

From: Kyle Evans <kevans_at_FreeBSD.org>
Date: Sun, 03 Aug 2025 04:15:33 UTC
The branch main has been updated by kevans:

URL: https://cgit.FreeBSD.org/src/commit/?id=91eb4d2ba4def0fe0f56f0a61ad7c503fcab891b

commit 91eb4d2ba4def0fe0f56f0a61ad7c503fcab891b
Author:     Kyle Evans <kevans@FreeBSD.org>
AuthorDate: 2025-08-03 04:15:03 +0000
Commit:     Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2025-08-03 04:15:03 +0000

    chroot: slightly cleanup
    
    Highlights:
     - Pull resolve_user() and resolve_group() out to make the main flow
        a bit easier to read
     - Fix some edge-cases in user/group resolution: you can have fully
        numeric usernames, and they may or may not live within the valid
        ID range.  Switch to just trying to resolve every specified
        group/user as a name, first, with a fallback to converting it to a
        numeric type and trying to resolve it as an ID.
     - Constify locals in main() that don't need to be mutable, re-sort
    
    Reviewed by:    emaste, olce
    Differential Revision:  https://reviews.freebsd.org/D51509
---
 usr.sbin/chroot/chroot.c | 94 ++++++++++++++++++++++++++----------------------
 1 file changed, 51 insertions(+), 43 deletions(-)

diff --git a/usr.sbin/chroot/chroot.c b/usr.sbin/chroot/chroot.c
index c978fc019c95..8a99a9bbf7cb 100644
--- a/usr.sbin/chroot/chroot.c
+++ b/usr.sbin/chroot/chroot.c
@@ -47,17 +47,58 @@
 
 static void usage(void) __dead2;
 
+static gid_t
+resolve_group(const char *group)
+{
+	char *endp;
+	struct group *gp;
+	unsigned long gid;
+
+	gp = getgrnam(group);
+	if (gp != NULL)
+		return (gp->gr_gid);
+
+	/*
+	 * Numeric IDs don't need a trip through the database to check them,
+	 * POSIX seems to think we should generally accept a numeric ID as long
+	 * as it's within the valid range.
+	 */
+	errno = 0;
+	gid = strtoul(group, &endp, 0);
+	if (errno == 0 && *endp == '\0' && (gid_t)gid >= 0 && gid <= GID_MAX)
+		return (gid);
+
+	errx(1, "no such group '%s'", group);
+}
+
+static uid_t
+resolve_user(const char *user)
+{
+	char *endp;
+	struct passwd *pw;
+	unsigned long uid;
+
+	pw = getpwnam(user);
+	if (pw != NULL)
+		return (pw->pw_uid);
+
+	errno = 0;
+	uid = strtoul(user, &endp, 0);
+	if (errno == 0 && *endp == '\0' && (uid_t)uid >= 0 && uid <= UID_MAX)
+		return (uid);
+
+	errx(1, "no such user '%s'", user);
+}
+
 int
 main(int argc, char *argv[])
 {
-	struct group	*gp;
-	struct passwd	*pw;
-	char		*endp, *p, *user, *group, *grouplist;
-	const char	*shell;
+	const char	*group, *p, *shell, *user;
+	char		*grouplist;
+	long		ngroups_max;
 	gid_t		gid, *gidlist;
 	uid_t		uid;
 	int		arg, ch, error, gids;
-	long		ngroups_max;
 	bool		nonprivileged;
 
 	gid = 0;
@@ -95,19 +136,8 @@ main(int argc, char *argv[])
 	if (argc < 1)
 		usage();
 
-	if (group != NULL) {
-		if (isdigit((unsigned char)*group)) {
-			gid = (gid_t)strtoul(group, &endp, 0);
-			if (*endp != '\0')
-				goto getgroup;
-		} else {
- getgroup:
-			if ((gp = getgrnam(group)) != NULL)
-				gid = gp->gr_gid;
-			else
-				errx(1, "no such group `%s'", group);
-		}
-	}
+	if (group != NULL)
+		gid = resolve_group(group);
 
 	ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1;
 	if ((gidlist = malloc(sizeof(gid_t) * ngroups_max)) == NULL)
@@ -122,35 +152,13 @@ main(int argc, char *argv[])
 		if (*p == '\0')
 			continue;
 
-		if (isdigit((unsigned char)*p)) {
-			gidlist[gids] = (gid_t)strtoul(p, &endp, 0);
-			if (*endp != '\0')
-				goto getglist;
-		} else {
- getglist:
-			if ((gp = getgrnam(p)) != NULL)
-				gidlist[gids] = gp->gr_gid;
-			else
-				errx(1, "no such group `%s'", p);
-		}
-		gids++;
+		gidlist[gids++] = resolve_group(p);
 	}
 	if (p != NULL && gids == ngroups_max)
 		errx(1, "too many supplementary groups provided");
 
-	if (user != NULL) {
-		if (isdigit((unsigned char)*user)) {
-			uid = (uid_t)strtoul(user, &endp, 0);
-			if (*endp != '\0')
-				goto getuser;
-		} else {
- getuser:
-			if ((pw = getpwnam(user)) != NULL)
-				uid = pw->pw_uid;
-			else
-				errx(1, "no such user `%s'", user);
-		}
-	}
+	if (user != NULL)
+		uid = resolve_user(user);
 
 	if (nonprivileged) {
 		arg = PROC_NO_NEW_PRIVS_ENABLE;