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#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <errno.h>
29#include <stdio.h>
30#include <stdio_ext.h>
31#include <stdlib.h>
32#include <unistd.h>
33#include <fcntl.h>
34#include <string.h>
35#include <limits.h>
36#include <sys/types.h>
37#include <pwd.h>
38#include <grp.h>
39#include <libproc.h>
40
41extern int _getgroupsbymember(const char *, gid_t[], int, int);
42
43static int look(char *);
44static int perr(char *);
45
46static void usage(void);
47static void initcred(void);
48
49static char *command;
50static char *procname;
51
52static char *user;
53static char *group;
54static char *grplst;
55static char *login;
56
57static boolean_t all = B_FALSE;
58static boolean_t doset = B_FALSE;
59static int ngrp = -1;
60static gid_t *groups;
61static long ngroups_max;
62
63static uid_t uid = (uid_t)-1;
64static gid_t gid = (gid_t)-1;
65
66int
67main(int argc, char **argv)
68{
69	int rc = 0;
70	int c;
71	struct rlimit rlim;
72
73	if ((command = strrchr(argv[0], '/')) != NULL)
74		command++;
75	else
76		command = argv[0];
77
78	if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) < 0)
79		return (perr("sysconf(_SC_NGROUPS_MAX)"));
80
81	opterr = 0;
82
83	while ((c = getopt(argc, argv, "au:g:l:G:")) != EOF) {
84		switch (c) {
85		case 'a':
86			all = B_TRUE;
87			break;
88		case 'u':
89			user = optarg;
90			doset = B_TRUE;
91			break;
92		case 'g':
93			group = optarg;
94			doset = B_TRUE;
95			break;
96		case 'G':
97			grplst = optarg;
98			doset = B_TRUE;
99			break;
100		case 'l':
101			login = optarg;
102			doset = B_TRUE;
103			break;
104		default:
105			usage();
106			/*NOTREACHED*/
107		}
108	}
109	if (login != NULL && (user != NULL || group != NULL || grplst != NULL))
110		usage();
111
112	if (all && doset)
113		usage();
114
115	argc -= optind;
116	argv += optind;
117
118	if (argc == 0)
119		usage();
120
121	if (doset)
122		initcred();
123
124	/*
125	 * Make sure we'll have enough file descriptors to handle a target
126	 * that has many many mappings.
127	 */
128	if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
129		rlim.rlim_cur = rlim.rlim_max;
130		(void) setrlimit(RLIMIT_NOFILE, &rlim);
131		(void) enable_extended_FILE_stdio(-1, -1);
132	}
133
134	while (argc-- > 0)
135		rc += look(*argv++);
136
137	return (rc > 255 ? 255 : rc);
138}
139
140static void
141credupdate(prcred_t *pcr)
142{
143	if (uid != (uid_t)-1)
144		pcr->pr_euid = pcr->pr_ruid = pcr->pr_suid = uid;
145	if (gid != (gid_t)-1)
146		pcr->pr_egid = pcr->pr_rgid = pcr->pr_sgid = gid;
147	if (ngrp >= 0) {
148
149		pcr->pr_ngroups = ngrp;
150
151		(void) memcpy(pcr->pr_groups, groups, ngrp * sizeof (gid_t));
152	}
153}
154
155static int
156look(char *arg)
157{
158	struct ps_prochandle *Pr;
159	static prcred_t *prcred = NULL;
160	int gcode;
161
162	procname = arg;		/* for perr() */
163
164	if (prcred == NULL) {
165		prcred = malloc(sizeof (prcred_t) +
166			(ngroups_max - 1) * sizeof (gid_t));
167		if (prcred == NULL) {
168			(void) perr("malloc");
169			exit(1);
170		}
171	}
172
173	if ((Pr = proc_arg_grab(arg, doset ? PR_ARG_PIDS : PR_ARG_ANY,
174	    PGRAB_RETAIN | PGRAB_FORCE | (doset ? 0 : PGRAB_RDONLY) |
175	    PGRAB_NOSTOP, &gcode)) == NULL) {
176		(void) fprintf(stderr, "%s: cannot examine %s: %s\n",
177		    command, arg, Pgrab_error(gcode));
178		return (1);
179	}
180
181	if (Pcred(Pr, prcred, ngroups_max) == -1) {
182		(void) perr("getcred");
183		Prelease(Pr, 0);
184		return (1);
185	}
186
187	if (doset) {
188		credupdate(prcred);
189		if (Psetcred(Pr, prcred) != 0) {
190			(void) perr("setcred");
191			Prelease(Pr, 0);
192			return (1);
193		}
194		Prelease(Pr, 0);
195		return (0);
196	}
197
198	if (Pstate(Pr) == PS_DEAD)
199		(void) printf("core of %d:\t", (int)Pstatus(Pr)->pr_pid);
200	else
201		(void) printf("%d:\t", (int)Pstatus(Pr)->pr_pid);
202
203	if (!all &&
204	    prcred->pr_euid == prcred->pr_ruid &&
205	    prcred->pr_ruid == prcred->pr_suid)
206		(void) printf("e/r/suid=%u  ", prcred->pr_euid);
207	else
208		(void) printf("euid=%u ruid=%u suid=%u  ",
209			prcred->pr_euid, prcred->pr_ruid, prcred->pr_suid);
210
211	if (!all &&
212	    prcred->pr_egid == prcred->pr_rgid &&
213	    prcred->pr_rgid == prcred->pr_sgid)
214		(void) printf("e/r/sgid=%u\n", prcred->pr_egid);
215	else
216		(void) printf("egid=%u rgid=%u sgid=%u\n",
217			prcred->pr_egid, prcred->pr_rgid, prcred->pr_sgid);
218
219	if (prcred->pr_ngroups != 0 &&
220	    (all || prcred->pr_ngroups != 1 ||
221	    prcred->pr_groups[0] != prcred->pr_rgid)) {
222		int i;
223
224		(void) printf("\tgroups:");
225		for (i = 0; i < prcred->pr_ngroups; i++)
226			(void) printf(" %u", prcred->pr_groups[i]);
227		(void) printf("\n");
228	}
229
230	Prelease(Pr, 0);
231	return (0);
232}
233
234static int
235perr(char *s)
236{
237	if (s)
238		(void) fprintf(stderr, "%s: ", procname);
239	else
240		s = procname;
241	perror(s);
242	return (1);
243}
244
245static void
246usage(void)
247{
248	(void) fprintf(stderr, "usage:\t%s [-a] { pid | core } ...\n"
249	    "\t%s [-u user] [-g group] [-G groups] pid ...\n"
250	    "\t%s -l login pid ...\n"
251	    "  (report or modify process credentials)\n",
252	    command, command, command);
253	exit(2);
254}
255
256
257static uint32_t
258str2id(const char *str)
259{
260	unsigned long res;
261	char *p;
262
263	errno = 0;
264	res = strtoul(str, &p, 0);
265	if (p == str || *p != '\0' || errno != 0)
266		return ((uint32_t)-1);
267	else
268		return ((uint32_t)res);
269}
270
271static gid_t
272str2gid(const char *grnam)
273{
274	struct group *grp = getgrnam(grnam);
275	gid_t res;
276
277	if (grp == NULL) {
278		res = (gid_t)str2id(grnam);
279		if (res == (gid_t)-1) {
280			(void) fprintf(stderr, "%s: %s: unknown group"
281			    " or bad gid\n",
282			    command, grnam);
283			exit(1);
284		}
285	} else {
286		res = grp->gr_gid;
287	}
288	return (res);
289}
290
291static void
292initcred(void)
293{
294	struct passwd *pwd;
295
296	if ((groups = malloc(ngroups_max * sizeof (gid_t))) == NULL) {
297		(void) perr("malloc");
298		exit(1);
299	}
300
301	if (login != NULL) {
302		pwd = getpwnam(login);
303
304		if (pwd == NULL) {
305			(void) fprintf(stderr, "%s: %s: unknown user\n",
306			    command, login);
307			exit(1);
308		}
309		uid = pwd->pw_uid;
310		gid = pwd->pw_gid;
311
312		groups[0] = gid;
313
314		ngrp = _getgroupsbymember(login, groups, (int)ngroups_max, 1);
315	}
316
317	if (user != NULL) {
318		pwd = getpwnam(user);
319		if (pwd == NULL) {
320			uid = (uid_t)str2id(user);
321			if (uid == (uid_t)-1) {
322				(void) fprintf(stderr, "%s: %s: unknown user"
323				    " or bad uid\n",
324				    command, user);
325				exit(1);
326			}
327		} else {
328			uid = pwd->pw_uid;
329		}
330	}
331
332	if (group != NULL)
333		gid = str2gid(group);
334
335	if (grplst != NULL) {
336		char *cgrp;
337
338		ngrp = 0;
339
340		while ((cgrp = strtok(grplst, ",")) != NULL) {
341
342			if (ngrp >= ngroups_max) {
343				(void) fprintf(stderr, "%s: Too many groups\n",
344				    command);
345				exit(1);
346			}
347			groups[ngrp++] = str2gid(cgrp);
348
349			/* For iterations of strtok */
350			grplst = NULL;
351		}
352	}
353}
354