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