aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-10-29 00:27:31 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-10-29 00:27:31 +0000
commit34e68c8b42ccbc8b04e6184f4985c3b47e6c6df0 (patch)
tree561bfdec1b2633b25d10e1a8044de3c7184b334c
parentd498850e02861ac4422f9a1e88deb8dec2c34887 (diff)
downloadbusybox-w32-34e68c8b42ccbc8b04e6184f4985c3b47e6c6df0.tar.gz
busybox-w32-34e68c8b42ccbc8b04e6184f4985c3b47e6c6df0.tar.bz2
busybox-w32-34e68c8b42ccbc8b04e6184f4985c3b47e6c6df0.zip
id: coreutils compat by Tito + test script
-rw-r--r--coreutils/id.c290
-rwxr-xr-xcoreutils/id_test.sh244
2 files changed, 406 insertions, 128 deletions
diff --git a/coreutils/id.c b/coreutils/id.c
index aa27ed394..b00532f83 100644
--- a/coreutils/id.c
+++ b/coreutils/id.c
@@ -3,176 +3,210 @@
3 * Mini id implementation for busybox 3 * Mini id implementation for busybox
4 * 4 *
5 * Copyright (C) 2000 by Randolph Chung <tausq@debian.org> 5 * Copyright (C) 2000 by Randolph Chung <tausq@debian.org>
6 * Copyright (C) 2008 by Tito Ragusa <farmatito@tiscali.it>
6 * 7 *
7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 8 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8 */ 9 */
9 10
10/* BB_AUDIT SUSv3 compliant. */ 11/* BB_AUDIT SUSv3 compliant. */
11/* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever length and to 12/* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever
12 * be more similar to GNU id. 13 * length and to be more similar to GNU id.
13 * -Z option support: by Yuichi Nakamura <ynakam@hitachisoft.jp> 14 * -Z option support: by Yuichi Nakamura <ynakam@hitachisoft.jp>
14 * Added -G option Tito Ragusa (C) 2008 for SUSv3. 15 * Added -G option Tito Ragusa (C) 2008 for SUSv3.
15 */ 16 */
16 17
17#include "libbb.h" 18#include "libbb.h"
18 19
19#define PRINT_REAL 1 20enum {
20#define NAME_NOT_NUMBER 2 21 PRINT_REAL = (1 << 0),
21#define JUST_USER 4 22 NAME_NOT_NUMBER = (1 << 1),
22#define JUST_GROUP 8 23 JUST_USER = (1 << 2),
23#define JUST_ALL_GROUPS 16 24 JUST_GROUP = (1 << 3),
25 JUST_ALL_GROUPS = (1 << 4),
24#if ENABLE_SELINUX 26#if ENABLE_SELINUX
25#define JUST_CONTEXT 32 27 JUST_CONTEXT = (1 << 5),
26#endif 28#endif
29};
27 30
28static int printf_full(unsigned id, const char *arg, const char *prefix) 31static int print_common(unsigned id,
32 char* FAST_FUNC bb_getXXXid(char *name, int bufsize, long uid),
33 const char *prefix)
29{ 34{
30 const char *fmt = "%s%u"; 35 const char *name = bb_getXXXid(NULL, 0, id);
31 int status = EXIT_FAILURE;
32 36
33 if (arg) { 37 if (prefix) {
34 fmt = "%s%u(%s)"; 38 printf("%s", prefix);
35 status = EXIT_SUCCESS;
36 } 39 }
37 printf(fmt, prefix, id, arg); 40 if (!(option_mask32 & NAME_NOT_NUMBER) || !name) {
38 return status; 41 printf("%u", id);
42 }
43 if (!option_mask32 || (option_mask32 & NAME_NOT_NUMBER)) {
44 if (name) {
45 printf(option_mask32 ? "%s" : "(%s)", name);
46 } else {
47 /* Don't set error status flag in default mode */
48 if (option_mask32) {
49 if (ENABLE_DESKTOP)
50 bb_error_msg("unknown ID %u", id);
51 return EXIT_FAILURE;
52 }
53 }
54 }
55 return EXIT_SUCCESS;
39} 56}
40 57
41#if (defined(__GLIBC__) && !defined(__UCLIBC__)) 58static int print_group(gid_t id, const char *prefix)
42#define HAVE_getgrouplist 1 59{
43#elif ENABLE_USE_BB_PWD_GRP 60 return print_common(id, bb_getgrgid, prefix);
44#define HAVE_getgrouplist 1 61}
45#else 62
46#define HAVE_getgrouplist 0 63static int print_user(gid_t id, const char *prefix)
47#endif 64{
65 return print_common(id, bb_getpwuid, prefix);
66}
67
68/* On error set *n < 0 and return >= 0
69 * If *n is too small, update it and return < 0
70 * (ok to trash groups[] in both cases)
71 * Otherwise fill in groups[] and return >= 0
72 */
73static int get_groups(const char *username, gid_t rgid, gid_t *groups, int *n)
74{
75 int m;
76
77 if (username) {
78 /* If the user is a member of more than
79 * *n groups, then -1 is returned. Otherwise >= 0.
80 * (and no defined way of detecting errors?!) */
81 m = getgrouplist(username, rgid, groups, n);
82 /* I guess *n < 0 might indicate error. Anyway,
83 * malloc'ing -1 bytes won't be good, so: */
84 //if (*n < 0)
85 // return 0;
86 //return m;
87 //commented here, happens below anyway
88 } else {
89 /* On error -1 is returned, which ends up in *n */
90 int nn = getgroups(*n, groups);
91 /* 0: nn <= *n, groups[] was big enough; -1 otherwise */
92 m = - (nn > *n);
93 *n = nn;
94 }
95 if (*n < 0)
96 return 0; /* error, don't return < 0! */
97 return m;
98}
48 99
49int id_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 100int id_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
50int id_main(int argc UNUSED_PARAM, char **argv) 101int id_main(int argc UNUSED_PARAM, char **argv)
51{ 102{
103 uid_t ruid;
104 gid_t rgid;
105 uid_t euid;
106 gid_t egid;
107 unsigned opt;
108 int i;
109 int status = EXIT_SUCCESS;
110 const char *prefix;
52 const char *username; 111 const char *username;
53 struct passwd *p;
54 uid_t uid;
55 gid_t gid;
56#if HAVE_getgrouplist
57 gid_t *groups;
58 int n;
59#endif
60 unsigned flags;
61 short status;
62#if ENABLE_SELINUX 112#if ENABLE_SELINUX
63 security_context_t scontext; 113 security_context_t scontext = NULL;
64#endif 114#endif
65 /* Don't allow -n -r -nr -ug -rug -nug -rnug */ 115 /* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/
66 /* Don't allow more than one username */ 116 /* Don't allow more than one username */
67 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"); 117 opt_complementary = "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG"
68 flags = getopt32(argv, "rnugG" USE_SELINUX("Z")); 118 USE_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G");
69 username = argv[optind]; 119 opt = getopt32(argv, "rnugG" USE_SELINUX("Z"));
70
71 /* This values could be overwritten later */
72 uid = geteuid();
73 gid = getegid();
74 if (flags & PRINT_REAL) {
75 uid = getuid();
76 gid = getgid();
77 }
78 120
121 username = argv[optind];
79 if (username) { 122 if (username) {
80#if HAVE_getgrouplist 123 struct passwd *p = getpwnam(username);
81 int m; 124 if (!p)
82#endif 125 bb_error_msg_and_die("unknown user %s", username);
83 p = getpwnam(username); 126 euid = ruid = p->pw_uid;
84 /* xuname2uid is needed because it exits on failure */ 127 egid = rgid = p->pw_gid;
85 uid = xuname2uid(username);
86 gid = p->pw_gid; /* in this case PRINT_REAL is the same */
87
88#if HAVE_getgrouplist
89 n = 16;
90 groups = NULL;
91 do {
92 m = n;
93 groups = xrealloc(groups, sizeof(groups[0]) * m);
94 getgrouplist(username, gid, groups, &n); /* GNUism? */
95 } while (n > m);
96#endif
97 } else { 128 } else {
98#if HAVE_getgrouplist 129 egid = getegid();
99 n = getgroups(0, NULL); 130 rgid = getgid();
100 groups = xmalloc(sizeof(groups[0]) * n); 131 euid = geteuid();
101 getgroups(n, groups); 132 ruid = getuid();
102#endif
103 }
104
105 if (flags & JUST_ALL_GROUPS) {
106#if HAVE_getgrouplist
107 while (n--) {
108 if (flags & NAME_NOT_NUMBER)
109 printf("%s", bb_getgrgid(NULL, 0, *groups++));
110 else
111 printf("%u", (unsigned) *groups++);
112 bb_putchar((n > 0) ? ' ' : '\n');
113 }
114#endif
115 /* exit */
116 fflush_stdout_and_exit(EXIT_SUCCESS);
117 } 133 }
118 134 /* JUST_ALL_GROUPS ignores -r PRINT_REAL flag even if man page for */
119 if (flags & (JUST_GROUP | JUST_USER USE_SELINUX(| JUST_CONTEXT))) { 135 /* id says: print the real ID instead of the effective ID, with -ugG */
120 /* JUST_GROUP and JUST_USER are mutually exclusive */ 136 /* in fact in ths case egid is always printed if egid != rgid */
121 if (flags & NAME_NOT_NUMBER) { 137 if (!opt || (opt & JUST_ALL_GROUPS)) {
122 /* bb_getXXXid(-1) exits on failure, puts cannot segfault */ 138 gid_t *groups;
123 puts((flags & JUST_USER) ? bb_getpwuid(NULL, -1, uid) : bb_getgrgid(NULL, -1, gid)); 139 int n;
140
141 if (!opt) {
142 /* Default Mode */
143 status |= print_user(ruid, "uid=");
144 status |= print_group(rgid, " gid=");
145 if (euid != ruid)
146 status |= print_user(euid, " euid=");
147 if (egid != rgid)
148 status |= print_group(egid, " egid=");
124 } else { 149 } else {
125 if (flags & JUST_USER) { 150 /* JUST_ALL_GROUPS */
126 printf("%u\n", (unsigned)uid); 151 status |= print_group(rgid, NULL);
127 } 152 if (egid != rgid)
128 if (flags & JUST_GROUP) { 153 status |= print_group(egid, " ");
129 printf("%u\n", (unsigned)gid); 154 }
155 /* We'd rather try supplying largish buffer than
156 * having get_groups() run twice. That might be slow
157 * (think about "user database in remove SQL server" case) */
158 groups = xmalloc(64 * sizeof(gid_t));
159 n = 64;
160 if (get_groups(username, rgid, groups, &n) < 0) {
161 /* Need bigger buffer after all */
162 groups = xrealloc(groups, n * sizeof(gid_t));
163 get_groups(username, rgid, groups, &n);
164 }
165 if (n > 0) {
166 /* Print the list */
167 prefix = " groups=";
168 for (i = 0; i < n; i++) {
169 if (opt && (groups[i] == rgid || groups[i] == egid))
170 continue;
171 status |= print_group(groups[i], opt ? " " : prefix);
172 prefix = ",";
130 } 173 }
174 if (ENABLE_FEATURE_CLEAN_UP)
175 free(groups);
176 } else if (n < 0) { /* error in get_groups() */
177 if (!ENABLE_DESKTOP)
178 bb_error_msg_and_die("cannot get groups");
179 else
180 return EXIT_FAILURE;
131 } 181 }
132
133#if ENABLE_SELINUX 182#if ENABLE_SELINUX
134 if (flags & JUST_CONTEXT) { 183 if (is_selinux_enabled()) {
135 selinux_or_die(); 184 if (getcon(&scontext) == 0)
136 if (username) { 185 printf(" context=%s", scontext);
137 bb_error_msg_and_die("user name can't be passed with -Z");
138 }
139
140 if (getcon(&scontext)) {
141 bb_error_msg_and_die("can't get process context");
142 }
143 puts(scontext);
144 } 186 }
145#endif 187#endif
146 /* exit */ 188 } else if (opt & PRINT_REAL) {
147 fflush_stdout_and_exit(EXIT_SUCCESS); 189 euid = ruid;
190 egid = rgid;
148 } 191 }
149 192
150 /* Print full info like GNU id */ 193 if (opt & JUST_USER)
151 /* bb_getpwuid(0) doesn't exit on failure (returns NULL) */ 194 status |= print_user(euid, NULL);
152 status = printf_full(uid, bb_getpwuid(NULL, 0, uid), "uid="); 195 else if (opt & JUST_GROUP)
153 status |= printf_full(gid, bb_getgrgid(NULL, 0, gid), " gid="); 196 status |= print_group(egid, NULL);
154#if HAVE_getgrouplist
155 {
156 const char *msg = " groups=";
157 while (n--) {
158 status |= printf_full(*groups, bb_getgrgid(NULL, 0, *groups), msg);
159 msg = ",";
160 groups++;
161 }
162 }
163 /* we leak groups vector... */
164#endif
165
166#if ENABLE_SELINUX 197#if ENABLE_SELINUX
167 if (is_selinux_enabled()) { 198 else if (opt & JUST_CONTEXT) {
168 security_context_t mysid; 199 selinux_or_die();
169 getcon(&mysid); 200 if (username || getcon(&scontext)) {
170 printf(" context=%s", mysid ? mysid : "unknown"); 201 bb_error_msg_and_die("can't get process context%s",
171 if (mysid) /* TODO: maybe freecon(NULL) is harmless? */ 202 username ? " for a different user" : "");
172 freecon(mysid); 203 }
204 fputs(scontext, stdout);
173 } 205 }
206 /* freecon(NULL) seems to be harmless */
207 if (ENABLE_FEATURE_CLEAN_UP)
208 freecon(scontext);
174#endif 209#endif
175
176 bb_putchar('\n'); 210 bb_putchar('\n');
177 fflush_stdout_and_exit(status); 211 fflush_stdout_and_exit(status);
178} 212}
diff --git a/coreutils/id_test.sh b/coreutils/id_test.sh
new file mode 100755
index 000000000..0d65f2ae3
--- /dev/null
+++ b/coreutils/id_test.sh
@@ -0,0 +1,244 @@
1#!/bin/bash
2# Test script for busybox id vs. coreutils id.
3# Needs root privileges for some tests.
4
5cp /usr/bin/id .
6BUSYBOX=./busybox
7ID=./id
8LIST=`awk -F: '{ printf "%s\n", $1 }' /etc/passwd`
9FLAG_USER_EXISTS="no"
10TEST_USER="f583ca884c1d93458fb61ed137ff44f6"
11
12echo "test 1: id [options] nousername"
13rm -f foo bar
14for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
15do
16 #echo "$OPTIONS"
17 $BUSYBOX id $OPTIONS >foo 2>/dev/null
18 RET1=$?
19 $ID $OPTIONS >bar 2>/dev/null
20 RET2=$?
21 if test "$RET1" != "$RET2"; then
22 echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
23 fi
24 diff foo bar
25done
26
27echo "test 2: id [options] username"
28rm -f foo bar
29for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
30do
31 #echo "$OPTIONS"
32 for i in $LIST ; do
33 if test "$i" = "$TEST_USER"; then
34 FLAG_USER_EXISTS="yes"
35 fi
36 $BUSYBOX id $OPTIONS $i >foo 2>/dev/null
37 RET1=$?
38 $ID $OPTIONS $i >bar 2>/dev/null
39 RET2=$?
40 if test "$RET1" != "$RET2"; then
41 echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
42 fi
43 diff foo bar
44 done
45done
46
47if test $FLAG_USER_EXISTS = "yes"; then
48 echo "test 3,4,5,6,7,8,9,10,11,12 skipped because test user $TEST_USER already exists"
49 rm -f foo bar
50 exit 1
51fi
52
53adduser -s /bin/true -g "" -H -D "$TEST_USER" || exit 1
54
55chown $TEST_USER.$TEST_USER $BUSYBOX
56chmod u+s $BUSYBOX 2>&1 /dev/null
57chown $TEST_USER.$TEST_USER $ID
58chmod u+s $ID 2>&1 /dev/null
59
60echo "test 3 setuid, existing user: id [options] no username"
61rm -f foo bar
62for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
63do
64 #echo "$OPTIONS"
65 $BUSYBOX id $OPTIONS >foo 2>/dev/null
66 RET1=$?
67 $ID $OPTIONS >bar 2>/dev/null
68 RET2=$?
69 if test "$RET1" != "$RET2"; then
70 echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
71 fi
72 diff foo bar
73 #done
74done
75
76echo "test 4 setuid, existing user: id [options] username"
77rm -f foo bar
78for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
79do
80 #echo "$OPTIONS"
81 for i in $LIST ; do
82 $BUSYBOX id $OPTIONS $i >foo 2>/dev/null
83 RET1=$?
84 $ID $OPTIONS $i >bar 2>/dev/null
85 RET2=$?
86 if test "$RET1" != "$RET2"; then
87 echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
88 fi
89 diff foo bar
90 done
91done
92
93chown $TEST_USER.$TEST_USER $BUSYBOX
94chmod g+s $BUSYBOX 2>&1 /dev/null
95chown $TEST_USER.$TEST_USER $ID
96chmod g+s $ID 2>&1 /dev/null
97
98echo "test 5 setgid, existing user: id [options] no username"
99rm -f foo bar
100for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
101do
102 #echo "$OPTIONS"
103 $BUSYBOX id $OPTIONS >foo 2>/dev/null
104 RET1=$?
105 $ID $OPTIONS >bar 2>/dev/null
106 RET2=$?
107 if test "$RET1" != "$RET2"; then
108 echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
109 fi
110 diff foo bar
111 #done
112done
113
114echo "test 6 setgid, existing user: id [options] username"
115rm -f foo bar
116for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
117do
118 #echo "$OPTIONS"
119 for i in $LIST ; do
120 $BUSYBOX id $OPTIONS $i >foo 2>/dev/null
121 RET1=$?
122 $ID $OPTIONS $i >bar 2>/dev/null
123 RET2=$?
124 if test "$RET1" != "$RET2"; then
125 echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
126 fi
127 diff foo bar
128 done
129done
130
131chown $TEST_USER.$TEST_USER $BUSYBOX
132chmod u+s,g+s $BUSYBOX 2>&1 /dev/null
133chown $TEST_USER.$TEST_USER $ID
134chmod u+s,g+s $ID 2>&1 /dev/null
135
136echo "test 7 setuid, setgid, existing user: id [options] no username"
137rm -f foo bar
138for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
139do
140 #echo "$OPTIONS"
141 $BUSYBOX id $OPTIONS >foo 2>/dev/null
142 RET1=$?
143 $ID $OPTIONS >bar 2>/dev/null
144 RET2=$?
145 if test "$RET1" != "$RET2"; then
146 echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
147 fi
148 diff foo bar
149 #done
150done
151
152echo "test 8 setuid, setgid, existing user: id [options] username"
153rm -f foo bar
154for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
155do
156 #echo "$OPTIONS"
157 for i in $LIST ; do
158 $BUSYBOX id $OPTIONS $i >foo 2>/dev/null
159 RET1=$?
160 $ID $OPTIONS $i >bar 2>/dev/null
161 RET2=$?
162 if test "$RET1" != "$RET2"; then
163 echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
164 fi
165 diff foo bar
166 done
167done
168
169deluser $TEST_USER || exit 1
170
171echo "test 9 setuid, setgid, not existing user: id [options] no username"
172rm -f foo bar
173for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
174do
175 #echo "$OPTIONS"
176 $BUSYBOX id $OPTIONS >foo 2>/dev/null
177 RET1=$?
178 $ID $OPTIONS >bar 2>/dev/null
179 RET2=$?
180 if test "$RET1" != "$RET2"; then
181 echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
182 fi
183 diff foo bar
184done
185
186echo "test 10 setuid, setgid, not existing user: id [options] username"
187rm -f foo bar
188for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
189do
190 #echo "$OPTIONS"
191 for i in $LIST ; do
192 $BUSYBOX id $OPTIONS $i >foo 2>/dev/null
193 RET1=$?
194 $ID $OPTIONS $i >bar 2>/dev/null
195 RET2=$?
196 if test "$RET1" != "$RET2"; then
197 echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
198 fi
199 diff foo bar
200 done
201done
202
203chown .root $BUSYBOX 2>&1 /dev/null
204chown .root $ID 2>&1 /dev/null
205chmod g+s $BUSYBOX 2>&1 /dev/null
206chmod g+s $ID 2>&1 /dev/null
207
208echo "test 11 setgid, not existing group: id [options] no username"
209rm -f foo bar
210for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
211do
212 #echo "$OPTIONS"
213 $BUSYBOX id $OPTIONS >foo 2>/dev/null
214 RET1=$?
215 $ID $OPTIONS >bar 2>/dev/null
216 RET2=$?
217 if test "$RET1" != "$RET2"; then
218 echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
219 fi
220 diff foo bar
221 #done
222done
223
224echo "test 12 setgid, not existing group: id [options] username"
225rm -f foo bar
226for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
227do
228 #echo "$OPTIONS"
229 for i in $LIST ; do
230 $BUSYBOX id $OPTIONS $i >foo 2>/dev/null
231 RET1=$?
232 $ID $OPTIONS $i >bar 2>/dev/null
233 RET2=$?
234 if test "$RET1" != "$RET2"; then
235 echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
236 fi
237 diff foo bar
238 done
239done
240
241chown root.root $BUSYBOX 2>&1 /dev/null
242chown root.root $ID 2>&1 /dev/null
243rm -f $ID
244rm -f foo bar