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