commit: 15907c2898039d17ce812a712fccabfb8403a5e4
parent 0679a401e9ef76091c43c113828650b362736d16
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Mon, 14 Mar 2022 21:16:28 +0100
bin/id: Handle user argument
Diffstat:
| M | bin/id.c | 166 | ++++++++++++++++++++++++++++++++++++++++++++++++------------------------------- | 
| M | test-bin/id | 21 | +++++++++++++++++++-- | 
2 files changed, 120 insertions(+), 67 deletions(-)
diff --git a/bin/id.c b/bin/id.c
@@ -4,7 +4,6 @@
 
 #define _DEFAULT_SOURCE // for getgrouplist
 
-#include <assert.h>    // assert
 #include <grp.h>       // getgrgid, getgrouplist
 #include <pwd.h>       // getpwuid
 #include <stdbool.h>   // bool
@@ -28,6 +27,12 @@ simple_list_groups(struct passwd *pw)
 
 	// getgrouplist(3) BSD extension might not be in all Unixes
 	int grls = getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups);
+
+	if(grls < 0)
+	{
+		goto failure;
+	}
+
 	for(int i = 0; i < ngroups; i++)
 	{
 		if(name_flag)
@@ -152,8 +157,6 @@ print_gid(char *field, struct group *gr, gid_t gid)
 {
 	if(gr && gr->gr_name)
 	{
-		assert(gr->gr_gid == gid);
-
 		if(name_flag)
 		{
 			return printf("%s=%s", field, gr->gr_name);
@@ -177,8 +180,6 @@ print_uid(char *field, struct passwd *pw, uid_t uid)
 {
 	if(pw && pw->pw_name)
 	{
-		assert(pw->pw_uid == uid);
-
 		if(name_flag)
 		{
 			return printf("%s=%s", field, pw->pw_name);
@@ -197,14 +198,46 @@ print_uid(char *field, struct passwd *pw, uid_t uid)
 	}
 }
 
+void
+safe_getpwuid(uid_t uid, struct passwd *res)
+{
+	struct passwd *pw = getpwuid(uid);
+
+	if(pw != NULL)
+	{
+		*res = *pw;
+	}
+}
+
+enum id_modes
+{
+	ID_NORMAL,
+	ID_GROUPS,
+	ID_GID,
+	ID_UID,
+};
+
+void
+usage()
+{
+	fprintf(stderr, "Usage: id [-Ggu] [-nr] [user]\n");
+}
+
 int
 main(int argc, char *argv[])
 {
 	int ret, c;
-	bool u_flag    = false;
-	bool G_flag    = false;
-	bool g_flag    = false;
-	bool real_flag = false;
+	enum id_modes mode = ID_NORMAL;
+	bool real_flag     = false;
+
+	// geteuid, getuid, getegid, getgid shall always be successful
+	uid_t uid  = getuid();
+	uid_t euid = geteuid();
+	gid_t gid  = getgid();
+	gid_t egid = getegid();
+
+	struct passwd pw  = {.pw_uid = uid, .pw_gid = gid};
+	struct passwd epw = {.pw_uid = euid, .pw_gid = egid};
 
 	/* flawfinder: ignore. Old implementations of getopt should fix themselves */
 	while((c = getopt(argc, argv, ":Ggunr")) != EOF)
@@ -212,39 +245,63 @@ main(int argc, char *argv[])
 		switch(c)
 		{
 		case 'G':
-			G_flag = true;
-			break;
-		case 'n':
-			name_flag = true;
+			mode = ID_GROUPS;
 			break;
 		case 'u':
-			u_flag = true;
+			mode = ID_UID;
 			break;
 		case 'g':
-			g_flag = true;
+			mode = ID_GID;
+			break;
+		case 'n':
+			name_flag = true;
 			break;
 		case 'r':
 			real_flag = true;
 			break;
 		default:
-			assert(false);
+			usage();
+			return 1;
 		}
 	}
 
 	argc -= optind;
 	argv += optind;
 
-	if(g_flag)
+	if(argc == 0)
 	{
-		gid_t gid;
+		safe_getpwuid(uid, &pw);
+		safe_getpwuid(euid, &epw);
+	}
+	else if(argc == 1)
+	{
+		struct passwd *pw_n = getpwnam(argv[0]);
 
-		if(real_flag)
+		if(pw_n == NULL)
 		{
-			gid = getgid();
+			return 1;
 		}
-		else
+		pw  = *pw_n;
+		epw = *pw_n;
+
+		uid  = pw.pw_uid;
+		euid = epw.pw_uid;
+		gid  = pw.pw_gid;
+		egid = epw.pw_gid;
+	}
+	else
+	{
+		usage();
+	}
+
+	struct group *gr  = getgrgid(gid);
+	struct group *egr = getgrgid(egid);
+
+	if(mode == ID_GID)
+	{
+		if(!real_flag)
 		{
-			gid = getegid();
+			gid = egid;
 		}
 
 		if(!name_flag)
@@ -253,11 +310,14 @@ main(int argc, char *argv[])
 		}
 		else
 		{
-			struct group *gr = getgrgid(gid);
-
-			assert(gr->gr_gid == gid);
-
-			ret = printf("%s\n", gr->gr_name);
+			if(gr != NULL)
+			{
+				ret = printf("%s\n", gr->gr_name);
+			}
+			else
+			{
+				return 1;
+			}
 		}
 
 		if(ret < 0)
@@ -268,17 +328,11 @@ main(int argc, char *argv[])
 		return 0;
 	}
 
-	if(u_flag)
+	if(mode == ID_UID)
 	{
-		uid_t uid;
-
-		if(real_flag)
-		{
-			uid = getuid();
-		}
-		else
+		if(!real_flag)
 		{
-			uid = geteuid();
+			uid = euid;
 		}
 
 		if(!name_flag)
@@ -287,11 +341,7 @@ main(int argc, char *argv[])
 		}
 		else
 		{
-			struct passwd *pw = getpwuid(uid);
-
-			assert(pw->pw_uid == uid);
-
-			ret = printf("%s\n", pw->pw_name);
+			ret = printf("%s\n", pw.pw_name);
 		}
 
 		if(ret < 0)
@@ -302,24 +352,16 @@ main(int argc, char *argv[])
 		return 0;
 	}
 
-	// geteuid, getuid, getegid, getgid shall always be successful
-	uid_t uid  = getuid();
-	uid_t euid = geteuid();
-	gid_t gid  = getgid();
-	gid_t egid = getegid();
-
-	struct passwd *pw  = getpwuid(uid);
-	struct passwd *epw = getpwuid(euid);
-	// can return NULL (ie. without /etc/passwd)
-
-	if(G_flag)
+	if(mode == ID_GROUPS)
 	{
-		if(!real_flag)
+		if(real_flag)
 		{
-			pw = epw;
+			ret = simple_list_groups(&pw);
+		} else {
+			ret = simple_list_groups(&epw);
 		}
 
-		if(pw != NULL && simple_list_groups(pw) != 0)
+		if(ret != 0)
 		{
 			goto failure;
 		}
@@ -327,7 +369,7 @@ main(int argc, char *argv[])
 		return 0;
 	}
 
-	ret = print_uid("uid", pw, uid);
+	ret = print_uid("uid", &pw, uid);
 	if(ret < 0)
 	{
 		return 1;
@@ -335,22 +377,19 @@ main(int argc, char *argv[])
 
 	if(euid != uid)
 	{
-		ret = print_uid(" euid", epw, euid);
+		ret = print_uid(" euid", &epw, euid);
 		if(ret < 0)
 		{
 			goto failure;
 		}
 	}
 
-	struct group *gr = getgrgid(gid);
-
 	ret = print_gid(" gid", gr, gid);
 	if(ret < 0)
 	{
 		goto failure;
 	}
 
-	struct group *egr = getgrgid(egid);
 	if(egid != gid)
 	{
 		ret = print_gid(" egid", egr, egid);
@@ -361,12 +400,9 @@ main(int argc, char *argv[])
 		}
 	}
 
-	if(pw != NULL)
+	if(list_groups(&pw) != 0)
 	{
-		if(list_groups(pw) != 0)
-		{
-			goto failure;
-		}
+		goto failure;
 	}
 
 	ret = printf("\n");
diff --git a/test-bin/id b/test-bin/id
@@ -64,9 +64,9 @@ noetc_body() {
 	set -f
 
 	# shellcheck disable=SC2086
-	atf_check -o "inline:uid=$(id -u) gid=$(id -g)\n" -- bwrap ${bwrap_args} ../bin/id
+	atf_check -o "inline:uid=$(id -u) gid=$(id -g) groups=$(id -g)\n" -- bwrap ${bwrap_args} ../bin/id
 	# shellcheck disable=SC2086
-	atf_check -o "inline:uid=$(id -ur) gid=$(id -gr)\n" -- bwrap ${bwrap_args} ../bin/id -r
+	atf_check -o "inline:uid=$(id -ur) gid=$(id -gr) groups=$(id -g)\n" -- bwrap ${bwrap_args} ../bin/id -r
 
 	# shellcheck disable=SC2086
 	atf_check -s exit:1 -- bwrap ${bwrap_args} ../bin/id -n
@@ -103,12 +103,27 @@ nogroup_body() {
 	atf_check -o "inline:$(id -gr)\n" -- bwrap ${bwrap_args} ../bin/id -gr
 }
 
+atf_test_case badarg
+badarg_body() {
+	atf_check -s exit:1 -e 'inline:Usage: id [-Ggu] [-nr] [user]\n' ../bin/id -a
+}
+
+atf_test_case root cleanup
+root_body() {
+	atf_check -o save:root.out ../bin/id root
+	atf_check grep -q "uid=$(id -u root)($(id -un root)) gid=$(id -g root)($(id -gn root)) groups=" root.out
+}
+root_cleanup() {
+	rm -f root.out
+}
+
 atf_init_test_cases() {
 	cd "$(atf_get_srcdir)" || exit 1
 
 	. ../test_functions.sh
 
 	atf_add_test_case devfull
+	atf_add_test_case badarg
 
 	atf_add_test_case noargs
 	atf_add_test_case names
@@ -118,4 +133,6 @@ atf_init_test_cases() {
 
 	atf_add_test_case noetc
 	atf_add_test_case nogroup
+
+	atf_add_test_case root
 }