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