xref: /aosp_15_r20/external/libcap/progs/capsh.c (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1 /*
2  * Copyright (c) 2008-11,16,19,2020 Andrew G. Morgan <[email protected]>
3  *
4  * This is a multifunction shell wrapper tool that can be used to
5  * launch capable files in various ways with a variety of settings. It
6  * also supports some testing modes, which are used extensively as
7  * part of the libcap build system.
8  *
9  * The --print option can be used as a quick test whether various
10  * capability manipulations work as expected (or not).
11  */
12 
13 #ifndef _DEFAULT_SOURCE
14 #define _DEFAULT_SOURCE
15 #endif
16 
17 #ifndef _GNU_SOURCE
18 #define _GNU_SOURCE
19 #endif
20 
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <sys/types.h>
25 #include <pwd.h>
26 #include <grp.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <sys/capability.h>
30 #include <sys/prctl.h>
31 #include <sys/securebits.h>
32 #include <sys/wait.h>
33 #include <unistd.h>
34 
35 #ifndef SHELL
36 #define SHELL "/bin/bash"
37 #endif /* ndef SHELL */
38 
39 #include "./capshdoc.h"
40 
41 #define MAX_GROUPS       100   /* max number of supplementary groups for user */
42 
43 /* parse a non-negative integer with some error handling */
nonneg_uint(const char * text,const char * prefix,int * ok)44 static unsigned long nonneg_uint(const char *text, const char *prefix, int *ok)
45 {
46     char *remains;
47     unsigned long value;
48     ssize_t len = strlen(text);
49 
50     if (len == 0 || *text == '-') {
51 	goto fail;
52     }
53     value = strtoul(text, &remains, 0);
54     if (*remains) {
55 	goto fail;
56     }
57     if (ok != NULL) {
58 	*ok = 1;
59     }
60     return value;
61 
62 fail:
63     if (ok == NULL) {
64 	fprintf(stderr, "%s: want non-negative integer, got \"%s\"\n",
65 		prefix, text);
66 	exit(1);
67     }
68     *ok = 0;
69     return 0;
70 }
71 
binary(unsigned long value)72 static char *binary(unsigned long value)
73 {
74     static char string[8*sizeof(unsigned long) + 1];
75     unsigned i;
76 
77     i = sizeof(string);
78     string[--i] = '\0';
79     do {
80 	string[--i] = (value & 1) ? '1' : '0';
81 	value >>= 1;
82     } while ((i > 0) && value);
83     return string + i;
84 }
85 
display_prctl_set(const char * name,int (* fn)(cap_value_t))86 static void display_prctl_set(const char *name, int (*fn)(cap_value_t))
87 {
88     unsigned cap;
89     const char *sep;
90     int set;
91 
92     printf("%s set =", name);
93     for (sep = "", cap=0; (set = fn(cap)) >= 0; cap++) {
94 	char *ptr;
95 	if (!set) {
96 	    continue;
97 	}
98 
99 	ptr = cap_to_name(cap);
100 	if (ptr == NULL) {
101 	    printf("%s%u", sep, cap);
102 	} else {
103 	    printf("%s%s", sep, ptr);
104 	    cap_free(ptr);
105 	}
106 	sep = ",";
107     }
108     if (!cap) {
109 	printf(" <unsupported>\n");
110     } else {
111 	printf("\n");
112     }
113 }
114 
display_current(void)115 static void display_current(void)
116 {
117     cap_t all;
118     char *text;
119 
120     all = cap_get_proc();
121     if (all == NULL) {
122 	perror("failed to get process capabilities");
123 	exit(1);
124     }
125     text = cap_to_text(all, NULL);
126     printf("Current: %s\n", text);
127     cap_free(text);
128     cap_free(all);
129 }
130 
display_current_iab(void)131 static void display_current_iab(void)
132 {
133     cap_iab_t iab;
134     char *text;
135 
136     iab = cap_iab_get_proc();
137     if (iab == NULL) {
138 	perror("failed to get IAB for process");
139 	exit(1);
140     }
141     text = cap_iab_to_text(iab);
142     if (text == NULL) {
143 	perror("failed to obtain text for IAB");
144 	cap_free(iab);
145 	exit(1);
146     }
147     printf("Current IAB: %s\n", text);
148     cap_free(text);
149     cap_free(iab);
150 }
151 
152 /* arg_print displays the current capability state of the process */
arg_print(void)153 static void arg_print(void)
154 {
155     long set;
156     int status, j;
157     const char *sep;
158     struct group *g;
159     gid_t groups[MAX_GROUPS], gid;
160     uid_t uid, euid;
161     struct passwd *u, *eu;
162 
163     display_current();
164     display_prctl_set("Bounding", cap_get_bound);
165     display_prctl_set("Ambient", cap_get_ambient);
166     display_current_iab();
167 
168     set = cap_get_secbits();
169     if (set >= 0) {
170 	const char *b = binary(set);  /* verilog convention for binary string */
171 	printf("Securebits: 0%lo/0x%lx/%u'b%s (no-new-privs=%d)\n", set, set,
172 	       (unsigned) strlen(b), b,
173 	       prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0));
174 	printf(" secure-noroot: %s (%s)\n",
175 	       (set & SECBIT_NOROOT) ? "yes":"no",
176 	       (set & SECBIT_NOROOT_LOCKED) ? "locked":"unlocked");
177 	printf(" secure-no-suid-fixup: %s (%s)\n",
178 	       (set & SECBIT_NO_SETUID_FIXUP) ? "yes":"no",
179 	       (set & SECBIT_NO_SETUID_FIXUP_LOCKED) ? "locked":"unlocked");
180 	printf(" secure-keep-caps: %s (%s)\n",
181 	       (set & SECBIT_KEEP_CAPS) ? "yes":"no",
182 	       (set & SECBIT_KEEP_CAPS_LOCKED) ? "locked":"unlocked");
183 	if (CAP_AMBIENT_SUPPORTED()) {
184 	    printf(" secure-no-ambient-raise: %s (%s)\n",
185 		   (set & SECBIT_NO_CAP_AMBIENT_RAISE) ? "yes":"no",
186 		   (set & SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED) ?
187 		   "locked":"unlocked");
188 	}
189     } else {
190 	printf("[Securebits ABI not supported]\n");
191 	set = prctl(PR_GET_KEEPCAPS);
192 	if (set >= 0) {
193 	    printf(" prctl-keep-caps: %s (locking not supported)\n",
194 		   set ? "yes":"no");
195 	} else {
196 	    printf("[Keepcaps ABI not supported]\n");
197 	}
198     }
199     uid = getuid();
200     u = getpwuid(uid);
201     euid = geteuid();
202     eu = getpwuid(euid);
203     printf("uid=%u(%s) euid=%u(%s)\n", uid, u ? u->pw_name : "???", euid, eu ? eu->pw_name : "???");
204     gid = getgid();
205     g = getgrgid(gid);
206     printf("gid=%u(%s)\n", gid, g ? g->gr_name : "???");
207     printf("groups=");
208     status = getgroups(MAX_GROUPS, groups);
209     sep = "";
210     for (j=0; j < status; j++) {
211 	g = getgrgid(groups[j]);
212 	printf("%s%u(%s)", sep, groups[j], g ? g->gr_name : "???");
213 	sep = ",";
214     }
215     printf("\n");
216     cap_mode_t mode = cap_get_mode();
217     printf("Guessed mode: %s (%d)\n", cap_mode_name(mode), mode);
218 }
219 
220 static const cap_value_t raise_setpcap[1] = { CAP_SETPCAP };
221 static const cap_value_t raise_chroot[1] = { CAP_SYS_CHROOT };
222 
will_need_setpcap(int strict)223 static cap_t will_need_setpcap(int strict)
224 {
225     cap_flag_value_t enabled;
226     cap_t raised = NULL;
227 
228     if (strict) {
229 	return NULL;
230     }
231 
232     raised = cap_get_proc();
233     if (raised == NULL) {
234 	perror("Capabilities not available");
235 	exit(1);
236     }
237     if (cap_get_flag(raised, CAP_SETPCAP, CAP_EFFECTIVE, &enabled) != 0) {
238 	perror("Unable to check CAP_EFFECTIVE CAP_SETPCAP value");
239 	exit(1);
240     }
241     if (enabled != CAP_SET) {
242 	cap_set_flag(raised, CAP_EFFECTIVE, 1, raise_setpcap, CAP_SET);
243     } else {
244 	/* no need to raise - since already raised */
245 	cap_free(raised);
246 	raised = NULL;
247     }
248     return raised;
249 }
250 
push_pcap(int strict,cap_t * orig_p,cap_t * raised_for_setpcap_p)251 static void push_pcap(int strict, cap_t *orig_p, cap_t *raised_for_setpcap_p)
252 {
253     *orig_p = cap_get_proc();
254     if (NULL == *orig_p) {
255 	perror("Capabilities not available");
256 	exit(1);
257     }
258     *raised_for_setpcap_p = will_need_setpcap(strict);
259 }
260 
pop_pcap(cap_t orig,cap_t raised_for_setpcap)261 static void pop_pcap(cap_t orig, cap_t raised_for_setpcap)
262 {
263     cap_free(raised_for_setpcap);
264     cap_free(orig);
265 }
266 
arg_drop(int strict,const char * arg_names)267 static void arg_drop(int strict, const char *arg_names)
268 {
269     char *ptr;
270     cap_t orig, raised_for_setpcap;
271     char *names;
272 
273     push_pcap(strict, &orig, &raised_for_setpcap);
274     if (strcmp("all", arg_names) == 0) {
275 	unsigned j = 0;
276 	while (CAP_IS_SUPPORTED(j)) {
277 	    int status;
278 	    if (raised_for_setpcap != NULL &&
279 		cap_set_proc(raised_for_setpcap) != 0) {
280 		perror("unable to raise CAP_SETPCAP for BSET changes");
281 		exit(1);
282 	    }
283 	    status = cap_drop_bound(j);
284 	    if (raised_for_setpcap != NULL && cap_set_proc(orig) != 0) {
285 		perror("unable to lower CAP_SETPCAP post BSET change");
286 		exit(1);
287 	    }
288 	    if (status != 0) {
289 		char *name_ptr;
290 
291 		name_ptr = cap_to_name(j);
292 		fprintf(stderr, "Unable to drop bounding capability [%s]\n",
293 			name_ptr);
294 		cap_free(name_ptr);
295 		exit(1);
296 	    }
297 	    j++;
298 	}
299 	pop_pcap(orig, raised_for_setpcap);
300 	return;
301     }
302 
303     names = strdup(arg_names);
304     if (NULL == names) {
305 	fprintf(stderr, "failed to allocate names\n");
306 	exit(1);
307     }
308     for (ptr = names; (ptr = strtok(ptr, ",")); ptr = NULL) {
309 	/* find name for token */
310 	cap_value_t cap;
311 	int status;
312 
313 	if (cap_from_name(ptr, &cap) != 0) {
314 	    fprintf(stderr, "capability [%s] is unknown to libcap\n", ptr);
315 	    exit(1);
316 	}
317 	if (raised_for_setpcap != NULL &&
318 	    cap_set_proc(raised_for_setpcap) != 0) {
319 	    perror("unable to raise CAP_SETPCAP for BSET changes");
320 	    exit(1);
321 	}
322 	status = cap_drop_bound(cap);
323 	if (raised_for_setpcap != NULL && cap_set_proc(orig) != 0) {
324 	    perror("unable to lower CAP_SETPCAP post BSET change");
325 	    exit(1);
326 	}
327 	if (status != 0) {
328 	    fprintf(stderr, "failed to drop [%s=%u]\n", ptr, cap);
329 	    exit(1);
330 	}
331     }
332     pop_pcap(orig, raised_for_setpcap);
333     free(names);
334 }
335 
arg_change_amb(const char * arg_names,cap_flag_value_t set)336 static void arg_change_amb(const char *arg_names, cap_flag_value_t set)
337 {
338     char *ptr;
339     cap_t orig;
340     char *names;
341 
342     orig = cap_get_proc();
343     if (strcmp("all", arg_names) == 0) {
344 	unsigned j = 0;
345 	while (CAP_IS_SUPPORTED(j)) {
346 	    int status;
347 	    status = cap_set_ambient(j, set);
348 	    if (status != 0) {
349 		char *name_ptr;
350 
351 		name_ptr = cap_to_name(j);
352 		fprintf(stderr, "Unable to %s ambient capability [%s]\n",
353 			set == CAP_CLEAR ? "clear":"raise", name_ptr);
354 		cap_free(name_ptr);
355 		exit(1);
356 	    }
357 	    j++;
358 	}
359 	cap_free(orig);
360 	return;
361     }
362 
363     names = strdup(arg_names);
364     if (NULL == names) {
365 	fprintf(stderr, "failed to allocate names\n");
366 	exit(1);
367     }
368     for (ptr = names; (ptr = strtok(ptr, ",")); ptr = NULL) {
369 	/* find name for token */
370 	cap_value_t cap;
371 	int status;
372 
373 	if (cap_from_name(ptr, &cap) != 0) {
374 	    fprintf(stderr, "capability [%s] is unknown to libcap\n", ptr);
375 	    exit(1);
376 	}
377 	status = cap_set_ambient(cap, set);
378 	if (status != 0) {
379 	    fprintf(stderr, "failed to %s ambient [%s=%u]\n",
380 		    set == CAP_CLEAR ? "clear":"raise", ptr, cap);
381 	    exit(1);
382 	}
383     }
384     cap_free(orig);
385     free(names);
386 }
387 
388 /*
389  * find_self locates and returns the full pathname of the named binary
390  * that is running. Importantly, it looks in the context of the
391  * prevailing CHROOT. Further, it does not fail over to invoking a
392  * shell if the target binary looks like something other than a
393  * executable. If an executable is not found, the function terminates
394  * the program with an error.
395  */
find_self(const char * arg0)396 static char *find_self(const char *arg0)
397 {
398     int i, status=1;
399     char *p = NULL, *parts, *dir, *scratch;
400     const char *path;
401 
402     for (i = strlen(arg0)-1; i >= 0 && arg0[i] != '/'; i--);
403     if (i >= 0) {
404         return strdup(arg0);
405     }
406 
407     path = getenv("PATH");
408     if (path == NULL) {
409         fprintf(stderr, "no PATH environment variable found for re-execing\n");
410 	exit(1);
411     }
412 
413     parts = strdup(path);
414     if (parts == NULL) {
415         fprintf(stderr, "insufficient memory for parts of path\n");
416 	exit(1);
417     }
418 
419     scratch = malloc(2+strlen(path)+strlen(arg0));
420     if (scratch == NULL) {
421         fprintf(stderr, "insufficient memory for path building\n");
422 	goto free_parts;
423     }
424 
425     for (p = parts; (dir = strtok(p, ":")); p = NULL) {
426         sprintf(scratch, "%s/%s", dir, arg0);
427 	if (access(scratch, X_OK) == 0) {
428 	    status = 0;
429 	    break;
430 	}
431     }
432     if (status) {
433 	fprintf(stderr, "unable to find executable '%s' in PATH\n", arg0);
434 	free(scratch);
435     }
436 
437 free_parts:
438     free(parts);
439     if (status) {
440 	exit(status);
441     }
442     return scratch;
443 }
444 
safe_sysconf(int name)445 static long safe_sysconf(int name)
446 {
447     long ans = sysconf(name);
448     if (ans <= 0) {
449 	fprintf(stderr, "sysconf(%d) returned a non-positive number: %ld\n", name, ans);
450 	exit(1);
451     }
452     return ans;
453 }
454 
describe(cap_value_t cap)455 static void describe(cap_value_t cap) {
456     int j;
457     const char **lines = explanations[cap];
458     char *name = cap_to_name(cap);
459     if (cap < cap_max_bits()) {
460 	printf("%s (%d)", name, cap);
461     } else {
462 	printf("<reserved for> %s (%d)", name, cap);
463     }
464     cap_free(name);
465     printf(" [/proc/self/status:CapXXX: 0x%016llx]\n\n", 1ULL<<cap);
466     for (j=0; lines[j]; j++) {
467 	printf("    %s\n", lines[j]);
468     }
469 }
470 
471 __attribute__ ((noreturn))
do_launch(char * args[],char * envp[])472 static void do_launch(char *args[], char *envp[])
473 {
474     cap_launch_t lau;
475     pid_t child;
476     int ret, result;
477 
478     lau = cap_new_launcher(args[0], (void *) args, (void *) envp);
479     if (lau == NULL) {
480 	perror("failed to create launcher");
481 	exit(1);
482     }
483     child = cap_launch(lau, NULL);
484     if (child <= 0) {
485 	perror("child failed to start");
486 	exit(1);
487     }
488     cap_free(lau);
489     ret = waitpid(child, &result, 0);
490     if (ret != child) {
491 	fprintf(stderr, "failed to wait for PID=%d, result=%x: ",
492 		child, result);
493 	perror("");
494 	exit(1);
495     }
496     if (WIFEXITED(result)) {
497 	exit(WEXITSTATUS(result));
498     }
499     if (WIFSIGNALED(result)) {
500 	fprintf(stderr, "child PID=%d terminated by signo=%d\n",
501 		child, WTERMSIG(result));
502 	exit(1);
503     }
504     fprintf(stderr, "child PID=%d generated result=%0x\n", child, result);
505     exit(1);
506 }
507 
main(int argc,char * argv[],char * envp[])508 int main(int argc, char *argv[], char *envp[])
509 {
510     pid_t child = 0;
511     unsigned i;
512     int strict = 0, quiet_start = 0, dont_set_env = 0;
513     const char *shell = SHELL;
514 
515     for (i=1; i<argc; ++i) {
516 	if (!strcmp("--quiet", argv[i])) {
517 	    quiet_start = 1;
518 	    continue;
519 	}
520 	if (i == 1) {
521 	    char *temp_name = cap_to_name(cap_max_bits() - 1);
522 	    if (temp_name == NULL) {
523 		perror("obtaining highest capability name");
524 		exit(1);
525 	    }
526 	    if (temp_name[0] != 'c') {
527 		printf("WARNING: libcap needs an update"
528 		       " (cap=%d should have a name).\n",
529 		       cap_max_bits() - 1);
530 	    }
531 	    cap_free(temp_name);
532 	}
533 	if (!strncmp("--drop=", argv[i], 7)) {
534 	    arg_drop(strict, argv[i]+7);
535 	} else if (!strncmp("--dropped=", argv[i], 10)) {
536 	    cap_value_t cap;
537 	    if (cap_from_name(argv[i]+10, &cap) < 0) {
538 		fprintf(stderr, "cap[%s] not recognized by library\n",
539 			argv[i] + 10);
540 		exit(1);
541 	    }
542 	    if (cap_get_bound(cap) > 0) {
543 		fprintf(stderr, "cap[%s] raised in bounding vector\n",
544 			argv[i]+10);
545 		exit(1);
546 	    }
547 	} else if (!strcmp("--has-ambient", argv[i])) {
548 	    if (!CAP_AMBIENT_SUPPORTED()) {
549 		perror("ambient set not supported");
550 		exit(1);
551 	    }
552 	} else if (!strncmp("--addamb=", argv[i], 9)) {
553 	    arg_change_amb(argv[i]+9, CAP_SET);
554 	} else if (!strncmp("--delamb=", argv[i], 9)) {
555 	    arg_change_amb(argv[i]+9, CAP_CLEAR);
556 	} else if (!strncmp("--noamb", argv[i], 7)) {
557 	    if (cap_reset_ambient() != 0) {
558 		perror("failed to reset ambient set");
559 		exit(1);
560 	    }
561 	} else if (!strcmp("--noenv", argv[i])) {
562 	    dont_set_env = 1;
563 	} else if (!strncmp("--inh=", argv[i], 6)) {
564 	    cap_t all, raised_for_setpcap;
565 	    char *text;
566 	    char *ptr;
567 
568 	    push_pcap(strict, &all, &raised_for_setpcap);
569 	    if (cap_clear_flag(all, CAP_INHERITABLE) != 0) {
570 		perror("libcap:cap_clear_flag() internal error");
571 		exit(1);
572 	    }
573 	    text = cap_to_text(all, NULL);
574 	    cap_free(all);
575 	    if (text == NULL) {
576 		perror("Fatal error concerning process capabilities");
577 		exit(1);
578 	    }
579 	    ptr = malloc(10 + strlen(argv[i]+6) + strlen(text));
580 	    if (ptr == NULL) {
581 		perror("Out of memory for inh set");
582 		exit(1);
583 	    }
584 	    if (argv[i][6] && strcmp("none", argv[i]+6)) {
585 		sprintf(ptr, "%s %s+i", text, argv[i]+6);
586 	    } else {
587 		strcpy(ptr, text);
588 	    }
589 	    cap_free(text);
590 
591 	    all = cap_from_text(ptr);
592 	    if (all == NULL) {
593 		perror("Fatal error internalizing capabilities");
594 		exit(1);
595 	    }
596 	    free(ptr);
597 
598 	    if (raised_for_setpcap != NULL) {
599 		/*
600 		 * This is only for the case that pP does not contain
601 		 * the requested change to pI.. Failing here is not
602 		 * indicative of the cap_set_proc(all) failing (always).
603 		 */
604 		(void) cap_set_proc(raised_for_setpcap);
605 		cap_free(raised_for_setpcap);
606 		raised_for_setpcap = NULL;
607 	    }
608 
609 	    if (cap_set_proc(all) != 0) {
610 		perror("Unable to set inheritable capabilities");
611 		exit(1);
612 	    }
613 	    cap_free(all);
614 	} else if (!strcmp("--strict", argv[i])) {
615 	    strict = !strict;
616 	} else if (!strncmp("--caps=", argv[i], 7)) {
617 	    cap_t all, raised_for_setpcap;
618 
619 	    raised_for_setpcap = will_need_setpcap(strict);
620 	    all = cap_from_text(argv[i]+7);
621 	    if (all == NULL) {
622 		fprintf(stderr, "unable to interpret [%s]\n", argv[i]);
623 		exit(1);
624 	    }
625 	    if (raised_for_setpcap != NULL) {
626 		/*
627 		 * This is actually only for the case that pP does not
628 		 * contain the requested change to pI.. Failing here
629 		 * is not always indicative of the cap_set_proc(all)
630 		 * failing.
631 		 */
632 		(void) cap_set_proc(raised_for_setpcap);
633 		cap_free(raised_for_setpcap);
634 		raised_for_setpcap = NULL;
635 	    }
636 	    if (cap_set_proc(all) != 0) {
637 		fprintf(stderr, "Unable to set capabilities [%s]\n", argv[i]);
638 		exit(1);
639 	    }
640 	    /*
641 	     * Since status is based on orig, we don't want to restore
642 	     * the previous value of 'all' again here!
643 	     */
644 	    cap_free(all);
645 	} else if (!strcmp("--modes", argv[i])) {
646 	    cap_mode_t c;
647 	    printf("Supported modes:");
648 	    for (c = 1; ; c++) {
649 		const char *m = cap_mode_name(c);
650 		if (strcmp("UNKNOWN", m) == 0) {
651 		    break;
652 		}
653 		printf(" %s", m);
654 	    }
655 	    printf("\n");
656 	} else if (!strncmp("--mode", argv[i], 6)) {
657 	    if (argv[i][6] == '=') {
658 		const char *target = argv[i]+7;
659 		cap_mode_t c;
660 		int found = 0;
661 		for (c = 1; ; c++) {
662 		    const char *m = cap_mode_name(c);
663 		    if (!strcmp("UNKNOWN", m)) {
664 			found = 0;
665 			break;
666 		    }
667 		    if (!strcmp(m, target)) {
668 			found = 1;
669 			break;
670 		    }
671 		}
672 		if (!found) {
673 		    printf("unsupported mode: %s\n", target);
674 		    exit(1);
675 		}
676 		int ret = cap_set_mode(c);
677 		if (ret != 0) {
678 		    printf("failed to set mode [%s]: %s\n",
679 			   target, strerror(errno));
680 		    exit(1);
681 		}
682 	    } else if (argv[i][6]) {
683 		printf("unrecognized command [%s]\n", argv[i]);
684 		goto usage;
685 	    } else {
686 		cap_mode_t m = cap_get_mode();
687 		printf("Mode: %s\n", cap_mode_name(m));
688 	    }
689 	} else if (!strncmp("--inmode=", argv[i], 9)) {
690 	    const char *target = argv[i]+9;
691 	    cap_mode_t c = cap_get_mode();
692 	    const char *m = cap_mode_name(c);
693 	    if (strcmp(m, target)) {
694 		printf("mismatched mode got=%s want=%s\n", m, target);
695 		exit(1);
696 	    }
697 	} else if (!strncmp("--keep=", argv[i], 7)) {
698 	    unsigned value;
699 	    int set;
700 
701 	    value = nonneg_uint(argv[i]+7, "invalid --keep value", NULL);
702 	    set = prctl(PR_SET_KEEPCAPS, value);
703 	    if (set < 0) {
704 		fprintf(stderr, "prctl(PR_SET_KEEPCAPS, %u) failed: %s\n",
705 			value, strerror(errno));
706 		exit(1);
707 	    }
708 	} else if (!strncmp("--chroot=", argv[i], 9)) {
709 	    int status;
710 	    cap_t orig, raised_for_chroot;
711 
712 	    orig = cap_get_proc();
713 	    if (orig == NULL) {
714 		perror("Capabilities not available");
715 		exit(1);
716 	    }
717 
718 	    raised_for_chroot = cap_dup(orig);
719 	    if (raised_for_chroot == NULL) {
720 		perror("Unable to duplicate capabilities");
721 		exit(1);
722 	    }
723 
724 	    if (cap_set_flag(raised_for_chroot, CAP_EFFECTIVE, 1, raise_chroot,
725 			     CAP_SET) != 0) {
726 		perror("unable to select CAP_SET_SYS_CHROOT");
727 		exit(1);
728 	    }
729 
730 	    if (cap_set_proc(raised_for_chroot) != 0) {
731 		perror("unable to raise CAP_SYS_CHROOT");
732 		exit(1);
733 	    }
734 	    cap_free(raised_for_chroot);
735 
736 	    status = chroot(argv[i]+9);
737 	    if (cap_set_proc(orig) != 0) {
738 		perror("unable to lower CAP_SYS_CHROOT");
739 		exit(1);
740 	    }
741 	    /*
742 	     * Given we are now in a new directory tree, its good practice
743 	     * to start off in a sane location
744 	     */
745 	    if (status == 0) {
746 		status = chdir("/");
747 	    }
748 
749 	    cap_free(orig);
750 
751 	    if (status != 0) {
752 		fprintf(stderr, "Unable to chroot/chdir to [%s]", argv[i]+9);
753 		exit(1);
754 	    }
755 	} else if (!strncmp("--secbits=", argv[i], 10)) {
756 	    unsigned value;
757 	    int status;
758 	    value = nonneg_uint(argv[i]+10, "invalid --secbits value", NULL);
759 	    status = cap_set_secbits(value);
760 	    if (status < 0) {
761 		fprintf(stderr, "failed to set securebits to 0%o/0x%x\n",
762 			value, value);
763 		exit(1);
764 	    }
765 	} else if (!strncmp("--forkfor=", argv[i], 10)) {
766 	    unsigned value;
767 	    if (child != 0) {
768 		fprintf(stderr, "already forked\n");
769 		exit(1);
770 	    }
771 	    value = nonneg_uint(argv[i]+10, "invalid --forkfor value", NULL);
772 	    if (value == 0) {
773 		fprintf(stderr, "require non-zero --forkfor value\n");
774 		goto usage;
775 	    }
776 	    child = fork();
777 	    if (child < 0) {
778 		perror("unable to fork()");
779 	    } else if (!child) {
780 		sleep(value);
781 		exit(0);
782 	    }
783 	} else if (!strncmp("--killit=", argv[i], 9)) {
784 	    int retval, status;
785 	    pid_t result;
786 	    unsigned value;
787 
788 	    value = nonneg_uint(argv[i]+9, "invalid --killit signo value",
789 				NULL);
790 	    if (!child) {
791 		fprintf(stderr, "no forked process to kill\n");
792 		exit(1);
793 	    }
794 	    retval = kill(child, value);
795 	    if (retval != 0) {
796 		perror("Unable to kill child process");
797 		exit(1);
798 	    }
799 	    result = waitpid(child, &status, 0);
800 	    if (result != child) {
801 		fprintf(stderr, "waitpid didn't match child: %u != %u\n",
802 			child, result);
803 		exit(1);
804 	    }
805 	    if (WTERMSIG(status) != value) {
806 		fprintf(stderr, "child terminated with odd signal (%d != %d)\n"
807 			, value, WTERMSIG(status));
808 		exit(1);
809 	    }
810 	    child = 0;
811 	} else if (!strncmp("--uid=", argv[i], 6)) {
812 	    unsigned value;
813 	    int status;
814 
815 	    value = nonneg_uint(argv[i]+6, "invalid --uid value", NULL);
816 	    status = setuid(value);
817 	    if (status < 0) {
818 		fprintf(stderr, "Failed to set uid=%u: %s\n",
819 			value, strerror(errno));
820 		exit(1);
821 	    }
822 	} else if (!strncmp("--cap-uid=", argv[i], 10)) {
823 	    unsigned value;
824 	    int status;
825 
826 	    value = nonneg_uint(argv[i]+10, "invalid --cap-uid value", NULL);
827 	    status = cap_setuid(value);
828 	    if (status < 0) {
829 		fprintf(stderr, "Failed to cap_setuid(%u): %s\n",
830 			value, strerror(errno));
831 		exit(1);
832 	    }
833 	} else if (!strncmp("--gid=", argv[i], 6)) {
834 	    unsigned value;
835 	    int status;
836 
837 	    value = nonneg_uint(argv[i]+6, "invalid --gid value", NULL);
838 	    status = setgid(value);
839 	    if (status < 0) {
840 		fprintf(stderr, "Failed to set gid=%u: %s\n",
841 			value, strerror(errno));
842 		exit(1);
843 	    }
844         } else if (!strncmp("--groups=", argv[i], 9)) {
845 	  char *ptr, *buf;
846 	  long length, max_groups;
847 	  gid_t *group_list;
848 	  int g_count;
849 
850 	  length = safe_sysconf(_SC_GETGR_R_SIZE_MAX);
851 	  buf = calloc(1, length);
852 	  if (NULL == buf) {
853 	    fprintf(stderr, "No memory for [%s] operation\n", argv[i]);
854 	    exit(1);
855 	  }
856 
857 	  max_groups = safe_sysconf(_SC_NGROUPS_MAX);
858 	  group_list = calloc(max_groups, sizeof(gid_t));
859 	  if (NULL == group_list) {
860 	    fprintf(stderr, "No memory for gid list\n");
861 	    exit(1);
862 	  }
863 
864 	  g_count = 0;
865 	  for (ptr = argv[i] + 9; (ptr = strtok(ptr, ","));
866 	       ptr = NULL, g_count++) {
867 	    if (max_groups <= g_count) {
868 	      fprintf(stderr, "Too many groups specified (%d)\n", g_count);
869 	      exit(1);
870 	    }
871 	    if (!isdigit(*ptr)) {
872 	      struct group *g, grp;
873 	      if (getgrnam_r(ptr, &grp, buf, length, &g) || NULL == g) {
874 		fprintf(stderr, "Failed to identify gid for group [%s]\n", ptr);
875 		exit(1);
876 	      }
877 	      group_list[g_count] = g->gr_gid;
878 	    } else {
879 	      group_list[g_count] = strtoul(ptr, NULL, 0);
880 	    }
881 	  }
882 	  free(buf);
883 	  if (setgroups(g_count, group_list) != 0) {
884 	    fprintf(stderr, "Failed to setgroups.\n");
885 	    exit(1);
886 	  }
887 	  free(group_list);
888 	} else if (!strncmp("--user=", argv[i], 7)) {
889 	    struct passwd *pwd;
890 	    const char *user;
891 	    gid_t groups[MAX_GROUPS];
892 	    int status, ngroups;
893 
894 	    user = argv[i] + 7;
895 	    pwd = getpwnam(user);
896 	    if (pwd == NULL) {
897 	      fprintf(stderr, "User [%s] not known\n", user);
898 	      exit(1);
899 	    }
900 	    ngroups = MAX_GROUPS;
901 	    status = getgrouplist(user, pwd->pw_gid, groups, &ngroups);
902 	    if (status < 1) {
903 	      perror("Unable to get group list for user");
904 	      exit(1);
905 	    }
906 	    status = cap_setgroups(pwd->pw_gid, ngroups, groups);
907 	    if (status != 0) {
908 		perror("Unable to set group list for user");
909 		exit(1);
910 	    }
911 	    status = cap_setuid(pwd->pw_uid);
912 	    if (status < 0) {
913 		fprintf(stderr, "Failed to set uid=%u(user=%s): %s\n",
914 			pwd->pw_uid, user, strerror(errno));
915 		exit(1);
916 	    }
917 	    if (!dont_set_env) {
918 		/*
919 		 * not setting this confuses bash at start up, but use
920 		 * --noenv to preserve the HOME etc values instead.
921 		 */
922 		if (setenv("HOME", pwd->pw_dir, 1) != 0) {
923 		    perror("unable to set HOME");
924 		    exit(1);
925 		}
926 		if (setenv("USER", user, 1) != 0) {
927 		    perror("unable to set USER");
928 		    exit(1);
929 		}
930 	    }
931 	} else if (!strncmp("--decode=", argv[i], 9)) {
932 	    unsigned long long value;
933 	    unsigned cap;
934 	    const char *sep = "";
935 
936 	    /* Note, if capabilities become longer than 64-bits we'll need
937 	       to fixup the following code.. */
938 	    value = strtoull(argv[i]+9, NULL, 16);
939 	    printf("0x%016llx=", value);
940 
941 	    for (cap=0; (cap < 64) && (value >> cap); ++cap) {
942 		if (value & (1ULL << cap)) {
943 		    char *ptr;
944 
945 		    ptr = cap_to_name(cap);
946 		    if (ptr != NULL) {
947 			printf("%s%s", sep, ptr);
948 			cap_free(ptr);
949 		    } else {
950 			printf("%s%u", sep, cap);
951 		    }
952 		    sep = ",";
953 		}
954 	    }
955 	    printf("\n");
956         } else if (!strncmp("--supports=", argv[i], 11)) {
957 	    cap_value_t cap;
958 
959 	    if (cap_from_name(argv[i] + 11, &cap) < 0) {
960 		fprintf(stderr, "cap[%s] not recognized by library\n",
961 			argv[i] + 11);
962 		exit(1);
963 	    }
964 	    if (!CAP_IS_SUPPORTED(cap)) {
965 		fprintf(stderr, "cap[%s=%d] not supported by kernel\n",
966 			argv[i] + 11, cap);
967 		exit(1);
968 	    }
969 	} else if (!strcmp("--print", argv[i])) {
970 	    arg_print();
971 	} else if ((!strcmp("--", argv[i])) || (!strcmp("==", argv[i]))
972 		   || (!strcmp("-+", argv[i])) ||  (!strcmp("=+", argv[i]))) {
973 	    int launch = argv[i][1] == '+';
974 	    if (argv[i][0] == '=') {
975 		if (quiet_start) {
976 		    argv[i--] = strdup("--quiet");
977 		}
978 	        argv[i] = find_self(argv[0]);
979 	    } else {
980 	        argv[i] = strdup(shell);
981 	    }
982 	    argv[argc] = NULL;
983 	    /* Two ways to chain load - use cap_launch() or execve() */
984 	    if (launch) {
985 		do_launch(argv+i, envp);
986 	    }
987 	    execve(argv[i], argv+i, envp);
988 	    fprintf(stderr, "execve '%s' failed!\n", argv[i]);
989 	    free(argv[i]);
990 	    exit(1);
991 	} else if (!strncmp("--shell=", argv[i], 8)) {
992 	    shell = argv[i]+8;
993 	} else if (!strncmp("--has-p=", argv[i], 8)) {
994 	    cap_value_t cap;
995 	    cap_flag_value_t enabled;
996 	    cap_t orig;
997 
998 	    if (cap_from_name(argv[i]+8, &cap) < 0) {
999 		fprintf(stderr, "cap[%s] not recognized by library\n",
1000 			argv[i] + 8);
1001 		exit(1);
1002 	    }
1003 	    orig = cap_get_proc();
1004 	    if (orig == NULL) {
1005 		perror("failed to get process capabilities");
1006 		exit(1);
1007 	    }
1008 	    if (cap_get_flag(orig, cap, CAP_PERMITTED, &enabled) || !enabled) {
1009 		fprintf(stderr, "cap[%s] not permitted\n", argv[i]+8);
1010 		exit(1);
1011 	    }
1012 	    cap_free(orig);
1013 	} else if (!strncmp("--has-i=", argv[i], 8)) {
1014 	    cap_value_t cap;
1015 	    cap_flag_value_t enabled;
1016 	    cap_t orig;
1017 
1018 	    if (cap_from_name(argv[i]+8, &cap) < 0) {
1019 		fprintf(stderr, "cap[%s] not recognized by library\n",
1020 			argv[i] + 8);
1021 		exit(1);
1022 	    }
1023 	    orig = cap_get_proc();
1024 	    if (orig == NULL) {
1025 		perror("failed to get process capabilities");
1026 		exit(1);
1027 	    }
1028 	    if (cap_get_flag(orig, cap, CAP_INHERITABLE, &enabled)
1029 		|| !enabled) {
1030 		fprintf(stderr, "cap[%s] not inheritable\n", argv[i]+8);
1031 		exit(1);
1032 	    }
1033 	    cap_free(orig);
1034 	} else if (!strncmp("--has-a=", argv[i], 8)) {
1035 	    cap_value_t cap;
1036 	    if (cap_from_name(argv[i]+8, &cap) < 0) {
1037 		fprintf(stderr, "cap[%s] not recognized by library\n",
1038 			argv[i] + 8);
1039 		exit(1);
1040 	    }
1041 	    if (!cap_get_ambient(cap)) {
1042 		fprintf(stderr, "cap[%s] not in ambient vector\n", argv[i]+8);
1043 		exit(1);
1044 	    }
1045 	} else if (!strncmp("--has-b=", argv[i], 8)) {
1046 	    cap_value_t cap;
1047 	    if (cap_from_name(argv[i]+8, &cap) < 0) {
1048 		fprintf(stderr, "cap[%s] not recognized by library\n",
1049 			argv[i] + 8);
1050 		exit(1);
1051 	    }
1052 	    if (!cap_get_bound(cap)) {
1053 		fprintf(stderr, "cap[%s] not in bounding vector\n", argv[i]+8);
1054 		exit(1);
1055 	    }
1056 	} else if (!strncmp("--is-uid=", argv[i], 9)) {
1057 	    unsigned value;
1058 	    uid_t uid;
1059 	    value = nonneg_uint(argv[i]+9, "invalid --is-uid value", NULL);
1060 	    uid = getuid();
1061 	    if (uid != value) {
1062 		fprintf(stderr, "uid: got=%d, want=%d\n", uid, value);
1063 		exit(1);
1064 	    }
1065 	} else if (!strncmp("--is-gid=", argv[i], 9)) {
1066 	    unsigned value;
1067 	    gid_t gid;
1068 	    value = nonneg_uint(argv[i]+9, "invalid --is-gid value", NULL);
1069 	    gid = getgid();
1070 	    if (gid != value) {
1071 		fprintf(stderr, "gid: got=%d, want=%d\n", gid, value);
1072 		exit(1);
1073 	    }
1074 	} else if (!strncmp("--iab=", argv[i], 6)) {
1075 	    cap_iab_t iab = cap_iab_from_text(argv[i]+6);
1076 	    if (iab == NULL) {
1077 		fprintf(stderr, "iab: '%s' malformed\n", argv[i]+6);
1078 		exit(1);
1079 	    }
1080 	    if (cap_iab_set_proc(iab)) {
1081 		perror("unable to set IAB tuple");
1082 		exit(1);
1083 	    }
1084 	    cap_free(iab);
1085 	} else if (!strcmp("--no-new-privs", argv[i])) {
1086 	    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0, 0) != 0) {
1087 		perror("unable to set no-new-privs");
1088 		exit(1);
1089 	    }
1090 	} else if (!strcmp("--has-no-new-privs", argv[i])) {
1091 	    if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0) != 1) {
1092 		fprintf(stderr, "no-new-privs not set\n");
1093 		exit(1);
1094 	    }
1095 	} else if (!strcmp("--license", argv[i])) {
1096 	    printf(
1097 		"%s see License file for details.\n"
1098 		"Copyright (c) 2008-11,16,19-21 Andrew G. Morgan"
1099 		" <[email protected]>\n", argv[0]);
1100 	    exit(0);
1101 	} else if (!strncmp("--explain=", argv[i], 10)) {
1102 	    cap_value_t cap;
1103 	    if (cap_from_name(argv[i]+10, &cap) != 0) {
1104 		fprintf(stderr, "unrecognised value '%s'\n", argv[i]+10);
1105 		exit(1);
1106 	    }
1107 	    if (cap < 0) {
1108 		fprintf(stderr, "negative capability (%d) invalid\n", cap);
1109 		exit(1);
1110 	    }
1111 	    if (cap < capsh_doc_limit) {
1112 		describe(cap);
1113 		continue;
1114 	    }
1115 	    if (cap < cap_max_bits()) {
1116 		printf("<unnamed in libcap> (%d)", cap);
1117 	    } else {
1118 		printf("<unsupported> (%d)", cap);
1119 	    }
1120 	    printf(" [/proc/self/status:CapXXX: 0x%016llx]\n", 1ULL<<cap);
1121 	} else if (!strncmp("--suggest=", argv[i], 10)) {
1122 	    cap_value_t cap;
1123 	    int hits = 0;
1124 	    for (cap=0; cap < capsh_doc_limit; cap++) {
1125 		const char **lines = explanations[cap];
1126 		int j;
1127 		char *name = cap_to_name(cap);
1128 		if (name == NULL) {
1129 		    perror("invalid named cap");
1130 		    exit(1);
1131 		}
1132 		char *match = strcasestr(name, argv[i]+10);
1133 		cap_free(name);
1134 		if (match != NULL) {
1135 		    if (hits++) {
1136 			printf("\n");
1137 		    }
1138 		    describe(cap);
1139 		    continue;
1140 		}
1141 		for (j=0; lines[j]; j++) {
1142 		    if (strcasestr(lines[j], argv[i]+10) != NULL) {
1143 			if (hits++) {
1144 			    printf("\n");
1145 			}
1146 			describe(cap);
1147 			break;
1148 		    }
1149 		}
1150 	    }
1151 	} else if (strcmp("--current", argv[i]) == 0) {
1152 	    display_current();
1153 	    display_current_iab();
1154 	} else {
1155 	usage:
1156 	    printf("usage: %s [args ...]\n"
1157 		   "  --addamb=xxx   add xxx,... capabilities to ambient set\n"
1158 		   "  --cap-uid=<n>  use libcap cap_setuid() to change uid\n"
1159 		   "  --caps=xxx     set caps as per cap_from_text()\n"
1160 		   "  --chroot=path  chroot(2) to this path\n"
1161 		   "  --current      show current caps and IAB vectors\n"
1162 		   "  --decode=xxx   decode a hex string to a list of caps\n"
1163 		   "  --delamb=xxx   remove xxx,... capabilities from ambient\n"
1164 		   "  --drop=xxx     drop xxx,... caps from bounding set\n"
1165 		   "  --explain=xxx  explain what capability xxx permits\n"
1166 		   "  --forkfor=<n>  fork and make child sleep for <n> sec\n"
1167 		   "  --gid=<n>      set gid to <n> (hint: id <username>)\n"
1168 		   "  --groups=g,... set the supplemental groups\n"
1169 		   "  --has-a=xxx    exit 1 if capability xxx not ambient\n"
1170 		   "  --has-b=xxx    exit 1 if capability xxx not dropped\n"
1171 		   "  --has-ambient  exit 1 unless ambient vector supported\n"
1172 		   "  --has-i=xxx    exit 1 if capability xxx not inheritable\n"
1173 		   "  --has-p=xxx    exit 1 if capability xxx not permitted\n"
1174 		   "  --has-no-new-privs  exit 1 if privs not limited\n"
1175 		   "  --help, -h     this message (or try 'man capsh')\n"
1176 		   "  --iab=...      use cap_iab_from_text() to set iab\n"
1177 		   "  --inh=xxx      set xxx,.. inheritable set\n"
1178 		   "  --inmode=<xxx> exit 1 if current mode is not <xxx>\n"
1179 		   "  --is-uid=<n>   exit 1 if uid != <n>\n"
1180 		   "  --is-gid=<n>   exit 1 if gid != <n>\n"
1181 		   "  --keep=<n>     set keep-capability bit to <n>\n"
1182 		   "  --killit=<n>   send signal(n) to child\n"
1183 		   "  --license      display license info\n"
1184 		   "  --mode         display current libcap mode\n"
1185 		   "  --mode=<xxx>   set libcap mode to <xxx>\n"
1186 		   "  --modes        list libcap named modes\n"
1187 		   "  --no-new-privs set sticky process privilege limiter\n"
1188 		   "  --noamb        reset (drop) all ambient capabilities\n"
1189 		   "  --noenv        no fixup of env vars (for --user)\n"
1190 		   "  --print        display capability relevant state\n"
1191 		   "  --quiet        if first argument skip max cap check\n"
1192 		   "  --secbits=<n>  write a new value for securebits\n"
1193 		   "  --shell=/xx/yy use /xx/yy instead of " SHELL " for --\n"
1194 		   "  --strict       toggle --caps, --drop and --inh fixups\n"
1195 		   "  --suggest=text search cap descriptions for text\n"
1196 		   "  --supports=xxx exit 1 if capability xxx unsupported\n"
1197 		   "  --uid=<n>      set uid to <n> (hint: id <username>)\n"
1198                    "  --user=<name>  set uid,gid and groups to that of user\n"
1199 		   "  ==             re-exec(capsh) with args as for --\n"
1200 		   "  =+             cap_launch capsh with args as for -+\n"
1201 		   "  --             remaining arguments are for " SHELL "\n"
1202 		   "  -+             cap_launch " SHELL " with remaining args\n"
1203 		   "                 (without -- [%s] will simply exit(0))\n",
1204 		   argv[0], argv[0]);
1205 	    if (strcmp("--help", argv[1]) && strcmp("-h", argv[1])) {
1206 		exit(1);
1207 	    }
1208 	    exit(0);
1209 	}
1210     }
1211 
1212     exit(0);
1213 }
1214