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