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