xref: /illumos-gate/usr/src/cmd/id/id.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 
30 #include <locale.h>
31 #include <stdio.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #include <sys/param.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <project.h>
38 #include <stdlib.h>
39 #include <alloca.h>
40 
41 #define	PWNULL  ((struct passwd *)0)
42 #define	GRNULL  ((struct group *)0)
43 
44 typedef enum TYPE {
45 	UID, EUID, GID, EGID, SGID
46 }	TYPE;
47 
48 typedef enum PRINT {
49 	CURR,		/* Print uid/gid only */
50 	ALLGROUPS,	/* Print all groups */
51 	GROUP,		/* Print only group */
52 	USER		/* Print only uid */
53 }	PRINT;
54 static PRINT mode = CURR;
55 
56 static int usage(void);
57 static void puid(uid_t);
58 static void pgid(gid_t);
59 static void prid(TYPE, uid_t);
60 static int getusergroups(int, gid_t *, char *, gid_t);
61 
62 static int nflag = 0;		/* Output names, not numbers */
63 static int rflag = 0;		/* Output real, not effective IDs */
64 static char stdbuf[BUFSIZ];
65 
66 int
67 main(int argc, char *argv[])
68 {
69 	gid_t *idp;
70 	uid_t uid, euid;
71 	gid_t gid, egid, prgid;
72 	int c, aflag = 0, project_flag = 0;
73 	struct passwd *pwp;
74 	int i, j;
75 	int groupmax = sysconf(_SC_NGROUPS_MAX);
76 	gid_t *groupids = alloca(groupmax * sizeof (gid_t));
77 	struct group *gr;
78 	char *user = NULL;
79 
80 	(void) setlocale(LC_ALL, "");
81 
82 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
83 #define	TEXT_DOMAIN "SYS_TEST"
84 #endif
85 	(void) textdomain(TEXT_DOMAIN);
86 	while ((c = getopt(argc, argv, "Ggunarp")) != EOF) {
87 		switch (c) {
88 			case 'G':
89 				if (mode != CURR)
90 					return (usage());
91 				mode = ALLGROUPS;
92 				break;
93 
94 			case 'g':
95 				if (mode != CURR)
96 					return (usage());
97 				mode = GROUP;
98 				break;
99 
100 			case 'a':
101 				aflag++;
102 				break;
103 
104 			case 'n':
105 				nflag++;
106 				break;
107 
108 			case 'r':
109 				rflag++;
110 				break;
111 
112 			case 'u':
113 				if (mode != CURR)
114 					return (usage());
115 				mode = USER;
116 				break;
117 
118 			case 'p':
119 				if (mode != CURR)
120 					return (usage());
121 				project_flag++;
122 				break;
123 
124 			case '?':
125 				return (usage());
126 		}
127 	}
128 	setbuf(stdout, stdbuf);
129 	argc -= optind-1;
130 	argv += optind-1;
131 
132 	/* -n and -r must be combined with one of -[Ggu] */
133 	/* -r cannot be combined with -G */
134 	/* -a and -p cannot be combined with -[Ggu] */
135 
136 	if ((mode == CURR && (nflag || rflag)) ||
137 		(mode == ALLGROUPS && rflag) ||
138 		(argc != 1 && argc != 2) ||
139 		(mode != CURR && (project_flag || aflag)))
140 		return (usage());
141 	if (argc == 2) {
142 		if ((pwp = getpwnam(argv[1])) == PWNULL) {
143 			(void) fprintf(stderr,
144 				gettext("id: invalid user name: \"%s\"\n"),
145 					argv[1]);
146 			return (1);
147 		}
148 		user = argv[1];
149 		uid = euid = pwp->pw_uid;
150 		prgid = gid = egid = pwp->pw_gid;
151 	} else {
152 		uid = getuid();
153 		gid = getgid();
154 		euid = geteuid();
155 		egid = getegid();
156 	}
157 
158 	if (mode != CURR) {
159 		if (!rflag) {
160 			uid = euid;
161 			gid = egid;
162 		}
163 		if (mode == USER)
164 			puid(uid);
165 		else if (mode == GROUP)
166 			pgid(gid);
167 		else if (mode == ALLGROUPS) {
168 			pgid(gid);
169 			if (user)
170 				i = getusergroups(groupmax, groupids, user,
171 				    prgid);
172 			else
173 				i = getgroups(groupmax, groupids);
174 			if (i == -1)
175 				perror("getgroups");
176 			else if (i > 0) {
177 				for (j = 0; j < i; ++j) {
178 					if ((gid = groupids[j]) == egid)
179 						continue;
180 					(void) putchar(' ');
181 					pgid(gid);
182 				}
183 			}
184 		}
185 		(void) putchar('\n');
186 	} else {
187 		prid(UID, uid);
188 		prid(GID, gid);
189 		if (uid != euid)
190 			prid(EUID, euid);
191 		if (gid != egid)
192 			prid(EGID, egid);
193 
194 		if (aflag) {
195 			if (user)
196 				i = getusergroups(groupmax, groupids, user,
197 				    prgid);
198 			else
199 				i = getgroups(groupmax, groupids);
200 			if (i == -1)
201 				perror("getgroups");
202 			else if (i > 0) {
203 				(void) printf(" groups=");
204 				for (idp = groupids; i--; idp++) {
205 					(void) printf("%u", *idp);
206 					if (gr = getgrgid(*idp))
207 						(void) printf("(%s)",
208 							gr->gr_name);
209 					if (i)
210 						(void) putchar(',');
211 				}
212 			}
213 		}
214 #ifdef XPG4
215 		/*
216 		 * POSIX requires us to show all supplementary groups
217 		 * groups other than the effective group already listed.
218 		 *
219 		 * This differs from -a above, because -a always shows
220 		 * all groups including the effective group in the group=
221 		 * line.
222 		 *
223 		 * It would be simpler if SunOS could just adopt this
224 		 * POSIX behavior, as it is so incredibly close to the
225 		 * the norm already.
226 		 *
227 		 * Then the magic -a flag could just indicate whether or
228 		 * not we are suppressing the effective group id.
229 		 */
230 		else {
231 			if (user)
232 				i = getusergroups(groupmax, groupids, user,
233 				    prgid);
234 			else
235 				i = getgroups(groupmax, groupids);
236 			if (i == -1)
237 				perror("getgroups");
238 			else if (i > 1) {
239 				(void) printf(" groups=");
240 				for (idp = groupids; i--; idp++) {
241 					if (*idp == egid)
242 						continue;
243 					(void) printf("%u", *idp);
244 					if (gr = getgrgid(*idp))
245 						(void) printf("(%s)",
246 							gr->gr_name);
247 					if (i)
248 						(void) putchar(',');
249 				}
250 			}
251 		}
252 #endif
253 		if (project_flag) {
254 			struct project proj;
255 			void *projbuf;
256 			projid_t curprojid = getprojid();
257 
258 			if ((projbuf = malloc(PROJECT_BUFSZ)) == NULL) {
259 				(void) fprintf(stderr, "unable to allocate "
260 				    "memory\n");
261 				return (2);
262 			}
263 
264 			if (user) {
265 				if (getdefaultproj(user, &proj, projbuf,
266 				    PROJECT_BUFSZ) != NULL)
267 					(void) printf(" projid=%d(%s)",
268 					    (int)proj.pj_projid, proj.pj_name);
269 				else
270 					/*
271 					 * This can only happen if project
272 					 * "default" has been removed from
273 					 * /etc/project file or the whole
274 					 * project database file was removed.
275 					 */
276 					(void) printf(" projid=(NONE)");
277 			} else {
278 				if (getprojbyid(curprojid, &proj, projbuf,
279 				    PROJECT_BUFSZ) == NULL)
280 					(void) printf(" projid=%d",
281 					    (int)curprojid);
282 				else
283 					(void) printf(" projid=%d(%s)",
284 					    (int)curprojid, proj.pj_name);
285 			}
286 			free(projbuf);
287 		}
288 		(void) putchar('\n');
289 	}
290 	return (0);
291 }
292 
293 static int
294 usage()
295 {
296 	(void) fprintf(stderr, gettext(
297 	    "Usage: id [-ap] [user]\n"
298 	    "       id -G [-n] [user]\n"
299 	    "       id -g [-nr] [user]\n"
300 	    "       id -u [-nr] [user]\n"));
301 	return (2);
302 }
303 
304 static void
305 puid(uid_t uid)
306 {
307 	struct passwd *pw;
308 
309 	if (nflag && (pw = getpwuid(uid)) != PWNULL)
310 		(void) printf("%s", pw->pw_name);
311 	else
312 		(void) printf("%u", uid);
313 }
314 
315 static void
316 pgid(gid_t gid)
317 {
318 	struct group *gr;
319 
320 	if (nflag && (gr = getgrgid(gid)) != GRNULL)
321 		(void) printf("%s", gr->gr_name);
322 	else
323 		(void) printf("%u", gid);
324 }
325 
326 static void
327 prid(TYPE how, uid_t id)
328 {
329 	char *s;
330 
331 	switch ((int)how) {
332 		case UID:
333 			s = "uid";
334 			break;
335 
336 		case EUID:
337 			s = " euid";
338 			break;
339 
340 		case GID:
341 			s = " gid";
342 			break;
343 
344 		case EGID:
345 			s = " egid";
346 			break;
347 
348 	}
349 	if (s != NULL)
350 		(void) printf("%s=", s);
351 	(void) printf("%u", id);
352 	switch ((int)how) {
353 	case UID:
354 	case EUID:
355 		{
356 			struct passwd *pwp;
357 
358 			if ((pwp = getpwuid(id)) != PWNULL)
359 				(void) printf("(%s)", pwp->pw_name);
360 
361 		}
362 		break;
363 	case GID:
364 	case EGID:
365 		{
366 			struct group *grp;
367 
368 			if ((grp = getgrgid(id)) != GRNULL)
369 				(void) printf("(%s)", grp->gr_name);
370 		}
371 		break;
372 	}
373 }
374 
375 /*
376  * Get the supplementary group affiliation for the user
377  */
378 static int getusergroups(gidsetsize, grouplist, user, prgid)
379 int	gidsetsize;
380 gid_t	*grouplist;
381 char	*user;
382 gid_t	prgid;
383 {
384 	struct group *group;
385 	char **gr_mem;
386 	int ngroups = 0;
387 
388 	setgrent();
389 	while ((ngroups < gidsetsize) && ((group = getgrent()) != NULL))
390 		for (gr_mem = group->gr_mem; *gr_mem; gr_mem++)
391 			if (strcmp(user, *gr_mem) == 0) {
392 				if (gidsetsize)
393 					grouplist[ngroups] = group->gr_gid;
394 				ngroups++;
395 			}
396 	endgrent();
397 	if (gidsetsize && !ngroups)
398 		grouplist[ngroups++] = prgid;
399 	return (ngroups);
400 }
401