Hi to all,
this is a full rewrote of the id applet rather than a patch.
My goal was to add euid egid handling to the applet without
increasing its size.
While at it I fixed also some differences in exit codes vs GNU id
when an id to name translation fails,
added some error messages when ENABLE_DESKTOP is set
and added some clean up code when ENABLE_FEATURE_CLEAN_UP is set.

Bloat o meter says:
 scripts/bloat-o-meter busybox_old busybox_unstripped
function                                             old     new   delta
print_common                                           -     123    +123
get_groups                                             -      37     +37
print_user                                             -      12     +12
print_group                                            -      12     +12
status                                               120     124      +4
flags                                                  -       4      +4
printf_full                                           44       -     -44
id_main                                              539     400    -139
------------------------------------------------------------------------------
(add/remove: 5/1 grow/shrink: 1/1 up/down: 192/-183)            Total: 9 bytes

the selinux code if enable was shrinked also:
[EMAIL PROTECTED] busybox_1]# scripts/bloat-o-meter busybox_old 
busybox_unstripped
function                                             old     new   delta
id_main                                              535     525     -10
.rodata                                           120583  120568     -15
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-25)             Total: -25 bytes

The code was extensively tested with the attached script and seems to work
correctly. The selinux code was tested in a virtualboxed fedora FC9 and seems
to work, but the selinux people on the list may take a look at it.
The test script maybe could be ported by some list member with better
scripting ablities to the testsuite, the main problem probably is that it
requires root privileges to prepare the environment for some tests.

Attached you will find the test script, the patch and a drop in
replacement to allow easier inspection of the code.

Hints, critics and imrovements are always welcome.

Please apply if you like it.

Ciao,
Tito

PS: Denys I removed the #if HAVE_getgrouplist as it seems to me
      that now we have getgrouplist in busybox libpwd, in gnu libc and also
      in uclibc (svn?).
PPS: is the G (*(struct globals*) stuff needed in this applet?

Attachment: id_test.sh
Description: application/shellscript

/* vi: set sw=4 ts=4: */
/*
 * Mini id implementation for busybox
 *
 * Copyright (C) 2000 by Randolph Chung <[EMAIL PROTECTED]>
 * Copyright (C) 2008 by Tito Ragusa <[EMAIL PROTECTED]>
 *
 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
 */

/* BB_AUDIT SUSv3 compliant. */
/* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever
 * length and to be more similar to GNU id.
 * -Z option support: by Yuichi Nakamura <[EMAIL PROTECTED]>
 * Added -G option Tito Ragusa (C) 2008 for SUSv3.
 */

#include "libbb.h"

enum {
	PRINT_REAL      = (1 << 0),
	NAME_NOT_NUMBER = (1 << 1),
	JUST_USER       = (1 << 2),
	JUST_GROUP      = (1 << 3),
	JUST_ALL_GROUPS = (1 << 4),
#if ENABLE_SELINUX
	JUST_CONTEXT    = (1 << 5),
#endif
};

#define DEFAULT_MODE	!flags

/* Globals */
static int status = EXIT_SUCCESS;
static unsigned flags;

static void  print_common(unsigned id,
			char* FAST_FUNC bb_getXXXid(char *name, int bufsize, long uid),
			const char *prefix)
{
	const char *name  = bb_getXXXid(NULL, 0, id);

	if (prefix) {
		printf("%s", prefix);
	}
	if (DEFAULT_MODE || !(flags & NAME_NOT_NUMBER) || !name) {
		printf("%u", id);
	}
	if (DEFAULT_MODE || flags & NAME_NOT_NUMBER) {
		if (name) {
			printf((DEFAULT_MODE) ? "(%s)" : "%s", name);
		} else {
			/* Don't set error status flag in default mode */
			if (flags) {
				if (ENABLE_DESKTOP)
					bb_error_msg("unknown ID %u", id);
				status |= EXIT_FAILURE;
			}
		}
	}
}

static void print_group(gid_t id, const char *prefix)
{
	print_common(id, bb_getgrgid, prefix);
}

static void print_user(gid_t id, const char *prefix)
{
	print_common(id, bb_getpwuid, prefix);
}

static int get_groups(const char *username, gid_t rgid, gid_t *groups, int *n)
{
	/* Fill the group list */
	if (username) {
		/* If  the  user  is  a member of more than */
		/* n groups,  then -1 is returned */
		return getgrouplist(username, rgid, groups, n);
	}
	/* On error, -1 is returned: possible errors EFAULT, EINVAL */
	return *n = getgroups(*n, groups);
}

int id_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int id_main(int argc UNUSED_PARAM, char **argv)
{
	struct passwd *p;
	uid_t ruid;
	gid_t rgid;
	uid_t euid;
	gid_t egid;
	gid_t *groups = NULL;
	int n = 0;
	int i;
	const char *prefix;
	const char *username;
#if ENABLE_SELINUX
	security_context_t scontext = NULL;
#endif
	/* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/
	/* Don't allow more than one username */
	opt_complementary =  "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG"
			 USE_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G");
	flags = getopt32(argv, "rnugG" USE_SELINUX("Z"));

	egid = getegid();
	rgid = getgid();
	euid = geteuid();
	ruid = getuid();
	username = argv[optind];
	if (username) {
		p = getpwnam(username);
		xuname2uid(username);
		euid = ruid = p->pw_uid;
		egid = rgid = p->pw_gid;
	}
	/* JUST_ALL_GROUPS ignores -r PRINT_REAL flag even if man page for */
	/* id says: print the real ID instead of the effective ID, with -ugG */
	/* in fact in ths case egid is always printed if egid != rgid */
	if (DEFAULT_MODE || flags & JUST_ALL_GROUPS) {
		if (DEFAULT_MODE) {
			/* Default Mode */
			print_user(ruid, "uid=");
			print_group(rgid, " gid=");
			if (euid != ruid)
				print_user(euid, " euid=");
			if (egid != rgid)
				print_group(egid, " egid=");
		} else {
			/* JUST_ALL_GROUPS */
			print_group(rgid, NULL);
			if (egid != rgid)
				print_group(egid, " ");
		}
		/* Get the number of supplementary groups */
		/* Here -1 could be returned  if getgrouplist is used as n is 0 */
		/* but there is no need to check for it */
		get_groups(username, rgid, groups, &n);
		/* Are there other groups ? */
		if (n > 0) {
			/* Malloc memory for the group list */
			groups = xmalloc(sizeof(gid_t) * n);
			if (get_groups(username, rgid, groups, &n) != -1) {
				/* Print the list */
				prefix = " groups=";
				for (i = 0; i < n; i++) {
					if (flags && (groups[i] == rgid || groups[i] == egid))
						continue;
					print_group(groups[i], (flags) ? " " : prefix);
					prefix = ",";
				}
				if (ENABLE_FEATURE_CLEAN_UP)
					free(groups);
			} else {
				if (ENABLE_DESKTOP)
					bb_error_msg_and_die("cannot get groups");
				else
					exit(EXIT_FAILURE);
			}
#if ENABLE_SELINUX
			if (is_selinux_enabled()) {
				if (getcon(&scontext) == 0)
					printf(" context=%s", scontext);
			}
#endif
		}
	} else if (flags & PRINT_REAL) {
		euid = ruid;
		egid = rgid;
	}

	if (flags & JUST_USER)
		print_user(euid, NULL);
	else if (flags & JUST_GROUP)
		print_group(egid, NULL);
#if ENABLE_SELINUX
	else if (flags & JUST_CONTEXT) {
		selinux_or_die();
		if (username || getcon(&scontext)) {
			bb_error_msg_and_die("can't get process context%s",
						username ? " for a different user" : "");
		}
		fputs(scontext, stdout);
	}
	/* freecon(NULL) seems to be harmless */
	if (ENABLE_FEATURE_CLEAN_UP)
		freecon(scontext);
#endif
	bb_putchar('\n');
	fflush_stdout_and_exit(status);
}
--- coreutils/id_orig.c	2008-09-18 09:01:25.000000000 +0200
+++ coreutils/id.c	2008-10-23 20:59:30.000000000 +0200
@@ -3,176 +3,190 @@
  * Mini id implementation for busybox
  *
  * Copyright (C) 2000 by Randolph Chung <[EMAIL PROTECTED]>
+ * Copyright (C) 2008 by Tito Ragusa <[EMAIL PROTECTED]>
  *
  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  */
 
 /* BB_AUDIT SUSv3 compliant. */
-/* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever length and to
- * be more similar to GNU id.
+/* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever
+ * length and to be more similar to GNU id.
  * -Z option support: by Yuichi Nakamura <[EMAIL PROTECTED]>
  * Added -G option Tito Ragusa (C) 2008 for SUSv3.
  */
 
 #include "libbb.h"
 
-#define PRINT_REAL        1
-#define NAME_NOT_NUMBER   2
-#define JUST_USER         4
-#define JUST_GROUP        8
-#define JUST_ALL_GROUPS  16
+enum {
+	PRINT_REAL      = (1 << 0),
+	NAME_NOT_NUMBER = (1 << 1),
+	JUST_USER       = (1 << 2),
+	JUST_GROUP      = (1 << 3),
+	JUST_ALL_GROUPS = (1 << 4),
 #if ENABLE_SELINUX
-#define JUST_CONTEXT     32
+	JUST_CONTEXT    = (1 << 5),
 #endif
+};
 
-static int printf_full(unsigned id, const char *arg, const char *prefix)
+#define DEFAULT_MODE	!flags
+
+/* Globals */
+static int status = EXIT_SUCCESS;
+static unsigned flags;
+
+static void  print_common(unsigned id,
+			char* FAST_FUNC bb_getXXXid(char *name, int bufsize, long uid),
+			const char *prefix)
 {
-	const char *fmt = "%s%u";
-	int status = EXIT_FAILURE;
+	const char *name  = bb_getXXXid(NULL, 0, id);
 
-	if (arg) {
-		fmt = "%s%u(%s)";
-		status = EXIT_SUCCESS;
+	if (prefix) {
+		printf("%s", prefix);
+	}
+	if (DEFAULT_MODE || !(flags & NAME_NOT_NUMBER) || !name) {
+		printf("%u", id);
+	}
+	if (DEFAULT_MODE || flags & NAME_NOT_NUMBER) {
+		if (name) {
+			printf((DEFAULT_MODE) ? "(%s)" : "%s", name);
+		} else {
+			/* Don't set error status flag in default mode */
+			if (flags) {
+				if (ENABLE_DESKTOP)
+					bb_error_msg("unknown ID %u", id);
+				status |= EXIT_FAILURE;
+			}
+		}
 	}
-	printf(fmt, prefix, id, arg);
-	return status;
 }
 
-#if (defined(__GLIBC__) && !defined(__UCLIBC__))
-#define HAVE_getgrouplist 1
-#elif ENABLE_USE_BB_PWD_GRP
-#define HAVE_getgrouplist 1
-#else
-#define HAVE_getgrouplist 0
-#endif
+static void print_group(gid_t id, const char *prefix)
+{
+	print_common(id, bb_getgrgid, prefix);
+}
+
+static void print_user(gid_t id, const char *prefix)
+{
+	print_common(id, bb_getpwuid, prefix);
+}
+
+static int get_groups(const char *username, gid_t rgid, gid_t *groups, int *n)
+{
+	/* Fill the group list */
+	if (username) {
+		/* If  the  user  is  a member of more than */
+		/* n groups,  then -1 is returned */
+		return getgrouplist(username, rgid, groups, n);
+	}
+	/* On error, -1 is returned: possible errors EFAULT, EINVAL */
+	return *n = getgroups(*n, groups);
+}
 
 int id_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int id_main(int argc UNUSED_PARAM, char **argv)
 {
-	const char *username;
 	struct passwd *p;
-	uid_t uid;
-	gid_t gid;
-#if HAVE_getgrouplist
-	gid_t *groups;
-	int n;
-#endif
-	unsigned flags;
-	short status;
+	uid_t ruid;
+	gid_t rgid;
+	uid_t euid;
+	gid_t egid;
+	gid_t *groups = NULL;
+	int n = 0;
+	int i;
+	const char *prefix;
+	const char *username;
 #if ENABLE_SELINUX
-	security_context_t scontext;
+	security_context_t scontext = NULL;
 #endif
-	/* Don't allow -n -r -nr -ug -rug -nug -rnug */
+	/* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/
 	/* Don't allow more than one username */
-	opt_complementary = "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG" USE_SELINUX(":u--Z:Z--u:g--Z:Z--g");
+	opt_complementary =  "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG"
+			 USE_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G");
 	flags = getopt32(argv, "rnugG" USE_SELINUX("Z"));
-	username = argv[optind];
-
-	/* This values could be overwritten later */
-	uid = geteuid();
-	gid = getegid();
-	if (flags & PRINT_REAL) {
-		uid = getuid();
-		gid = getgid();
-	}
 
+	egid = getegid();
+	rgid = getgid();
+	euid = geteuid();
+	ruid = getuid();
+	username = argv[optind];
 	if (username) {
-#if HAVE_getgrouplist
-		int m;
-#endif
 		p = getpwnam(username);
-		/* xuname2uid is needed because it exits on failure */
-		uid = xuname2uid(username);
-		gid = p->pw_gid; /* in this case PRINT_REAL is the same */
-
-#if HAVE_getgrouplist
-		n = 16;
-		groups = NULL;
-		do {
-			m = n;
-			groups = xrealloc(groups, sizeof(groups[0]) * m);
-			getgrouplist(username, gid, groups, &n); /* GNUism? */
-		} while (n > m);
-#endif
-	} else {
-#if HAVE_getgrouplist
-		n = getgroups(0, NULL);
-		groups = xmalloc(sizeof(groups[0]) * n);
-		getgroups(n, groups);
-#endif
-	}
-
-	if (flags & JUST_ALL_GROUPS) {
-#if HAVE_getgrouplist
-		while (n--) {
-			if (flags & NAME_NOT_NUMBER)
-				printf("%s", bb_getgrgid(NULL, 0, *groups++));
-			else
-				printf("%u", (unsigned) *groups++);
-			bb_putchar((n > 0) ? ' ' : '\n');
-		}
-#endif
-		/* exit */
-		fflush_stdout_and_exit(EXIT_SUCCESS);
-	}
-
-	if (flags & (JUST_GROUP | JUST_USER USE_SELINUX(| JUST_CONTEXT))) {
-		/* JUST_GROUP and JUST_USER are mutually exclusive */
-		if (flags & NAME_NOT_NUMBER) {
-			/* bb_getXXXid(-1) exits on failure, puts cannot segfault */
-			puts((flags & JUST_USER) ? bb_getpwuid(NULL, -1, uid) : bb_getgrgid(NULL, -1, gid));
+		xuname2uid(username);
+		euid = ruid = p->pw_uid;
+		egid = rgid = p->pw_gid;
+	}
+	/* JUST_ALL_GROUPS ignores -r PRINT_REAL flag even if man page for */
+	/* id says: print the real ID instead of the effective ID, with -ugG */
+	/* in fact in ths case egid is always printed if egid != rgid */
+	if (DEFAULT_MODE || flags & JUST_ALL_GROUPS) {
+		if (DEFAULT_MODE) {
+			/* Default Mode */
+			print_user(ruid, "uid=");
+			print_group(rgid, " gid=");
+			if (euid != ruid)
+				print_user(euid, " euid=");
+			if (egid != rgid)
+				print_group(egid, " egid=");
 		} else {
-			if (flags & JUST_USER) {
-				printf("%u\n", (unsigned)uid);
-			}
-			if (flags & JUST_GROUP) {
-				printf("%u\n", (unsigned)gid);
-			}
+			/* JUST_ALL_GROUPS */
+			print_group(rgid, NULL);
+			if (egid != rgid)
+				print_group(egid, " ");
 		}
-
-#if ENABLE_SELINUX
-		if (flags & JUST_CONTEXT) {
-			selinux_or_die();
-			if (username) {
-				bb_error_msg_and_die("user name can't be passed with -Z");
+		/* Get the number of supplementary groups */
+		/* Here -1 could be returned  if getgrouplist is used as n is 0 */
+		/* but there is no need to check for it */
+		get_groups(username, rgid, groups, &n);
+		/* Are there other groups ? */
+		if (n > 0) {
+			/* Malloc memory for the group list */
+			groups = xmalloc(sizeof(gid_t) * n);
+			if (get_groups(username, rgid, groups, &n) != -1) {
+				/* Print the list */
+				prefix = " groups=";
+				for (i = 0; i < n; i++) {
+					if (flags && (groups[i] == rgid || groups[i] == egid))
+						continue;
+					print_group(groups[i], (flags) ? " " : prefix);
+					prefix = ",";
+				}
+				if (ENABLE_FEATURE_CLEAN_UP)
+					free(groups);
+			} else {
+				if (ENABLE_DESKTOP)
+					bb_error_msg_and_die("cannot get groups");
+				else
+					exit(EXIT_FAILURE);
 			}
-
-			if (getcon(&scontext)) {
-				bb_error_msg_and_die("can't get process context");
+#if ENABLE_SELINUX
+			if (is_selinux_enabled()) {
+				if (getcon(&scontext) == 0)
+					printf(" context=%s", scontext);
 			}
-			puts(scontext);
-		}
 #endif
-		/* exit */
-		fflush_stdout_and_exit(EXIT_SUCCESS);
-	}
-
-	/* Print full info like GNU id */
-	/* bb_getpwuid(0) doesn't exit on failure (returns NULL) */
-	status = printf_full(uid, bb_getpwuid(NULL, 0, uid), "uid=");
-	status |= printf_full(gid, bb_getgrgid(NULL, 0, gid), " gid=");
-#if HAVE_getgrouplist
-	{
-		const char *msg = " groups=";
-		while (n--) {
-			status |= printf_full(*groups, bb_getgrgid(NULL, 0, *groups), msg);
-			msg = ",";
-			groups++;
 		}
+	} else if (flags & PRINT_REAL) {
+		euid = ruid;
+		egid = rgid;
 	}
-	/* we leak groups vector... */
-#endif
 
+	if (flags & JUST_USER)
+		print_user(euid, NULL);
+	else if (flags & JUST_GROUP)
+		print_group(egid, NULL);
 #if ENABLE_SELINUX
-	if (is_selinux_enabled()) {
-		security_context_t mysid;
-		getcon(&mysid);
-		printf(" context=%s", mysid ? mysid : "unknown");
-		if (mysid) /* TODO: maybe freecon(NULL) is harmless? */
-			freecon(mysid);
+	else if (flags & JUST_CONTEXT) {
+		selinux_or_die();
+		if (username || getcon(&scontext)) {
+			bb_error_msg_and_die("can't get process context%s",
+						username ? " for a different user" : "");
+		}
+		fputs(scontext, stdout);
 	}
+	/* freecon(NULL) seems to be harmless */
+	if (ENABLE_FEATURE_CLEAN_UP)
+		freecon(scontext);
 #endif
-
 	bb_putchar('\n');
 	fflush_stdout_and_exit(status);
 }
_______________________________________________
busybox mailing list
[email protected]
http://busybox.net/cgi-bin/mailman/listinfo/busybox

Reply via email to