1 /* 2 * Copyright (c) 2008-11 Andrew G. Morgan <morgan (at) kernel.org> 3 * 4 * This is a simple 'bash' wrapper program that can be used to 5 * raise and lower both the bset and pI capabilities before invoking 6 * /bin/bash (hardcoded right now). 7 * 8 * The --print option can be used as a quick test whether various 9 * capability manipulations work as expected (or not). 10 */ 11 12 #include <stdio.h> 13 #include <string.h> 14 #include <stdlib.h> 15 #include <sys/prctl.h> 16 #include <sys/types.h> 17 #include <unistd.h> 18 #include <pwd.h> 19 #include <grp.h> 20 #include <errno.h> 21 #include <ctype.h> 22 #include <sys/capability.h> 23 #include <sys/securebits.h> 24 #include <sys/wait.h> 25 #include <sys/prctl.h> 26 27 #define MAX_GROUPS 100 /* max number of supplementary groups for user */ 28 29 static const cap_value_t raise_setpcap[1] = { CAP_SETPCAP }; 30 static const cap_value_t raise_chroot[1] = { CAP_SYS_CHROOT }; 31 32 static char *binary(unsigned long value) 33 { 34 static char string[8*sizeof(unsigned long) + 1]; 35 unsigned i; 36 37 i = sizeof(string); 38 string[--i] = '\0'; 39 do { 40 string[--i] = (value & 1) ? '1' : '0'; 41 value >>= 1; 42 } while ((i > 0) && value); 43 return string + i; 44 } 45 46 int main(int argc, char *argv[], char *envp[]) 47 { 48 pid_t child; 49 unsigned i; 50 51 child = 0; 52 53 for (i=1; i<argc; ++i) { 54 if (!memcmp("--drop=", argv[i], 4)) { 55 char *ptr; 56 cap_t orig, raised_for_setpcap; 57 58 /* 59 * We need to do this here because --inh=XXX may have reset 60 * orig and it isn't until we are within the --drop code that 61 * we know what the prevailing (orig) pI value is. 62 */ 63 orig = cap_get_proc(); 64 if (orig == NULL) { 65 perror("Capabilities not available"); 66 exit(1); 67 } 68 69 raised_for_setpcap = cap_dup(orig); 70 if (raised_for_setpcap == NULL) { 71 fprintf(stderr, "BSET modification requires CAP_SETPCAP\n"); 72 exit(1); 73 } 74 75 if (cap_set_flag(raised_for_setpcap, CAP_EFFECTIVE, 1, 76 raise_setpcap, CAP_SET) != 0) { 77 perror("unable to select CAP_SETPCAP"); 78 exit(1); 79 } 80 81 if (strcmp("all", argv[i]+7) == 0) { 82 unsigned j = 0; 83 while (CAP_IS_SUPPORTED(j)) { 84 if (cap_drop_bound(j) != 0) { 85 char *name_ptr; 86 87 name_ptr = cap_to_name(j); 88 fprintf(stderr, 89 "Unable to drop bounding capability [%s]\n", 90 name_ptr); 91 cap_free(name_ptr); 92 exit(1); 93 } 94 j++; 95 } 96 } else { 97 for (ptr = argv[i]+7; (ptr = strtok(ptr, ",")); ptr = NULL) { 98 /* find name for token */ 99 cap_value_t cap; 100 int status; 101 102 if (cap_from_name(ptr, &cap) != 0) { 103 fprintf(stderr, 104 "capability [%s] is unknown to libcap\n", 105 ptr); 106 exit(1); 107 } 108 if (cap_set_proc(raised_for_setpcap) != 0) { 109 perror("unable to raise CAP_SETPCAP for BSET changes"); 110 exit(1); 111 } 112 status = prctl(PR_CAPBSET_DROP, cap); 113 if (cap_set_proc(orig) != 0) { 114 perror("unable to lower CAP_SETPCAP post BSET change"); 115 exit(1); 116 } 117 if (status) { 118 fprintf(stderr, "failed to drop [%s=%u]\n", ptr, cap); 119 exit(1); 120 } 121 } 122 } 123 cap_free(raised_for_setpcap); 124 cap_free(orig); 125 } else if (!memcmp("--inh=", argv[i], 6)) { 126 cap_t all, raised_for_setpcap; 127 char *text; 128 char *ptr; 129 130 all = cap_get_proc(); 131 if (all == NULL) { 132 perror("Capabilities not available"); 133 exit(1); 134 } 135 if (cap_clear_flag(all, CAP_INHERITABLE) != 0) { 136 perror("libcap:cap_clear_flag() internal error"); 137 exit(1); 138 } 139 140 raised_for_setpcap = cap_dup(all); 141 if ((raised_for_setpcap != NULL) 142 && (cap_set_flag(raised_for_setpcap, CAP_EFFECTIVE, 1, 143 raise_setpcap, CAP_SET) != 0)) { 144 cap_free(raised_for_setpcap); 145 raised_for_setpcap = NULL; 146 } 147 148 text = cap_to_text(all, NULL); 149 cap_free(all); 150 if (text == NULL) { 151 perror("Fatal error concerning process capabilities"); 152 exit(1); 153 } 154 ptr = malloc(10 + strlen(argv[i]+6) + strlen(text)); 155 if (ptr == NULL) { 156 perror("Out of memory for inh set"); 157 exit(1); 158 } 159 if (argv[i][6] && strcmp("none", argv[i]+6)) { 160 sprintf(ptr, "%s %s+i", text, argv[i]+6); 161 } else { 162 strcpy(ptr, text); 163 } 164 165 all = cap_from_text(ptr); 166 if (all == NULL) { 167 perror("Fatal error internalizing capabilities"); 168 exit(1); 169 } 170 cap_free(text); 171 free(ptr); 172 173 if (raised_for_setpcap != NULL) { 174 /* 175 * This is only for the case that pP does not contain 176 * the requested change to pI.. Failing here is not 177 * indicative of the cap_set_proc(all) failing (always). 178 */ 179 (void) cap_set_proc(raised_for_setpcap); 180 cap_free(raised_for_setpcap); 181 raised_for_setpcap = NULL; 182 } 183 184 if (cap_set_proc(all) != 0) { 185 perror("Unable to set inheritable capabilities"); 186 exit(1); 187 } 188 /* 189 * Since status is based on orig, we don't want to restore 190 * the previous value of 'all' again here! 191 */ 192 193 cap_free(all); 194 } else if (!memcmp("--caps=", argv[i], 7)) { 195 cap_t all, raised_for_setpcap; 196 197 raised_for_setpcap = cap_get_proc(); 198 if (raised_for_setpcap == NULL) { 199 perror("Capabilities not available"); 200 exit(1); 201 } 202 203 if ((raised_for_setpcap != NULL) 204 && (cap_set_flag(raised_for_setpcap, CAP_EFFECTIVE, 1, 205 raise_setpcap, CAP_SET) != 0)) { 206 cap_free(raised_for_setpcap); 207 raised_for_setpcap = NULL; 208 } 209 210 all = cap_from_text(argv[i]+7); 211 if (all == NULL) { 212 fprintf(stderr, "unable to interpret [%s]\n", argv[i]); 213 exit(1); 214 } 215 216 if (raised_for_setpcap != NULL) { 217 /* 218 * This is only for the case that pP does not contain 219 * the requested change to pI.. Failing here is not 220 * indicative of the cap_set_proc(all) failing (always). 221 */ 222 (void) cap_set_proc(raised_for_setpcap); 223 cap_free(raised_for_setpcap); 224 raised_for_setpcap = NULL; 225 } 226 227 if (cap_set_proc(all) != 0) { 228 fprintf(stderr, "Unable to set capabilities [%s]\n", argv[i]); 229 exit(1); 230 } 231 /* 232 * Since status is based on orig, we don't want to restore 233 * the previous value of 'all' again here! 234 */ 235 236 cap_free(all); 237 } else if (!memcmp("--keep=", argv[i], 7)) { 238 unsigned value; 239 int set; 240 241 value = strtoul(argv[i]+7, NULL, 0); 242 set = prctl(PR_SET_KEEPCAPS, value); 243 if (set < 0) { 244 fprintf(stderr, "prctl(PR_SET_KEEPCAPS, %u) failed: %s\n", 245 value, strerror(errno)); 246 exit(1); 247 } 248 } else if (!memcmp("--chroot=", argv[i], 9)) { 249 int status; 250 cap_t orig, raised_for_chroot; 251 252 orig = cap_get_proc(); 253 if (orig == NULL) { 254 perror("Capabilities not available"); 255 exit(1); 256 } 257 258 raised_for_chroot = cap_dup(orig); 259 if (raised_for_chroot == NULL) { 260 perror("Unable to duplicate capabilities"); 261 exit(1); 262 } 263 264 if (cap_set_flag(raised_for_chroot, CAP_EFFECTIVE, 1, raise_chroot, 265 CAP_SET) != 0) { 266 perror("unable to select CAP_SET_SYS_CHROOT"); 267 exit(1); 268 } 269 270 if (cap_set_proc(raised_for_chroot) != 0) { 271 perror("unable to raise CAP_SYS_CHROOT"); 272 exit(1); 273 } 274 cap_free(raised_for_chroot); 275 276 status = chroot(argv[i]+9); 277 if (cap_set_proc(orig) != 0) { 278 perror("unable to lower CAP_SYS_CHROOT"); 279 exit(1); 280 } 281 /* 282 * Given we are now in a new directory tree, its good practice 283 * to start off in a sane location 284 */ 285 status = chdir("/"); 286 287 cap_free(orig); 288 289 if (status != 0) { 290 fprintf(stderr, "Unable to chroot/chdir to [%s]", argv[i]+9); 291 exit(1); 292 } 293 } else if (!memcmp("--secbits=", argv[i], 10)) { 294 unsigned value; 295 int status; 296 297 value = strtoul(argv[i]+10, NULL, 0); 298 status = prctl(PR_SET_SECUREBITS, value); 299 if (status < 0) { 300 fprintf(stderr, "failed to set securebits to 0%o/0x%x\n", 301 value, value); 302 exit(1); 303 } 304 } else if (!memcmp("--forkfor=", argv[i], 10)) { 305 unsigned value; 306 307 value = strtoul(argv[i]+10, NULL, 0); 308 if (value == 0) { 309 goto usage; 310 } 311 child = fork(); 312 if (child < 0) { 313 perror("unable to fork()"); 314 } else if (!child) { 315 sleep(value); 316 exit(0); 317 } 318 } else if (!memcmp("--killit=", argv[i], 9)) { 319 int retval, status; 320 pid_t result; 321 unsigned value; 322 323 value = strtoul(argv[i]+9, NULL, 0); 324 if (!child) { 325 fprintf(stderr, "no forked process to kill\n"); 326 exit(1); 327 } 328 retval = kill(child, value); 329 if (retval != 0) { 330 perror("Unable to kill child process"); 331 exit(1); 332 } 333 result = waitpid(child, &status, 0); 334 if (result != child) { 335 fprintf(stderr, "waitpid didn't match child: %u != %u\n", 336 child, result); 337 exit(1); 338 } 339 if (WTERMSIG(status) != value) { 340 fprintf(stderr, "child terminated with odd signal (%d != %d)\n" 341 , value, WTERMSIG(status)); 342 exit(1); 343 } 344 } else if (!memcmp("--uid=", argv[i], 6)) { 345 unsigned value; 346 int status; 347 348 value = strtoul(argv[i]+6, NULL, 0); 349 status = setuid(value); 350 if (status < 0) { 351 fprintf(stderr, "Failed to set uid=%u: %s\n", 352 value, strerror(errno)); 353 exit(1); 354 } 355 } else if (!memcmp("--gid=", argv[i], 6)) { 356 unsigned value; 357 int status; 358 359 value = strtoul(argv[i]+6, NULL, 0); 360 status = setgid(value); 361 if (status < 0) { 362 fprintf(stderr, "Failed to set gid=%u: %s\n", 363 value, strerror(errno)); 364 exit(1); 365 } 366 } else if (!memcmp("--groups=", argv[i], 9)) { 367 char *ptr, *buf; 368 long length, max_groups; 369 gid_t *group_list; 370 int g_count; 371 372 length = sysconf(_SC_GETGR_R_SIZE_MAX); 373 buf = calloc(1, length); 374 if (NULL == buf) { 375 fprintf(stderr, "No memory for [%s] operation\n", argv[i]); 376 exit(1); 377 } 378 379 max_groups = sysconf(_SC_NGROUPS_MAX); 380 group_list = calloc(max_groups, sizeof(gid_t)); 381 if (NULL == group_list) { 382 fprintf(stderr, "No memory for gid list\n"); 383 exit(1); 384 } 385 386 g_count = 0; 387 for (ptr = argv[i] + 9; (ptr = strtok(ptr, ",")); 388 ptr = NULL, g_count++) { 389 if (max_groups <= g_count) { 390 fprintf(stderr, "Too many groups specified (%d)\n", g_count); 391 exit(1); 392 } 393 if (!isdigit(*ptr)) { 394 struct group *g, grp; 395 getgrnam_r(ptr, &grp, buf, length, &g); 396 if (NULL == g) { 397 fprintf(stderr, "Failed to identify gid for group [%s]\n", ptr); 398 exit(1); 399 } 400 group_list[g_count] = g->gr_gid; 401 } else { 402 group_list[g_count] = strtoul(ptr, NULL, 0); 403 } 404 } 405 free(buf); 406 if (setgroups(g_count, group_list) != 0) { 407 fprintf(stderr, "Failed to setgroups.\n"); 408 exit(1); 409 } 410 free(group_list); 411 } else if (!memcmp("--user=", argv[i], 7)) { 412 struct passwd *pwd; 413 const char *user; 414 gid_t groups[MAX_GROUPS]; 415 int status, ngroups; 416 417 user = argv[i] + 7; 418 pwd = getpwnam(user); 419 if (pwd == NULL) { 420 fprintf(stderr, "User [%s] not known\n", user); 421 exit(1); 422 } 423 ngroups = MAX_GROUPS; 424 status = getgrouplist(user, pwd->pw_gid, groups, &ngroups); 425 if (status < 1) { 426 perror("Unable to get group list for user"); 427 exit(1); 428 } 429 status = setgroups(ngroups, groups); 430 if (status != 0) { 431 perror("Unable to set group list for user"); 432 exit(1); 433 } 434 status = setgid(pwd->pw_gid); 435 if (status < 0) { 436 fprintf(stderr, "Failed to set gid=%u(user=%s): %s\n", 437 pwd->pw_gid, user, strerror(errno)); 438 exit(1); 439 } 440 status = setuid(pwd->pw_uid); 441 if (status < 0) { 442 fprintf(stderr, "Failed to set uid=%u(user=%s): %s\n", 443 pwd->pw_uid, user, strerror(errno)); 444 exit(1); 445 } 446 } else if (!memcmp("--decode=", argv[i], 9)) { 447 unsigned long long value; 448 unsigned cap; 449 const char *sep = ""; 450 451 /* Note, if capabilities become longer than 64-bits we'll need 452 to fixup the following code.. */ 453 value = strtoull(argv[i]+9, NULL, 16); 454 printf("0x%016llx=", value); 455 456 for (cap=0; (cap < 64) && (value >> cap); ++cap) { 457 if (value & (1ULL << cap)) { 458 char *ptr; 459 460 ptr = cap_to_name(cap); 461 if (ptr != NULL) { 462 printf("%s%s", sep, ptr); 463 cap_free(ptr); 464 } else { 465 printf("%s%u", sep, cap); 466 } 467 sep = ","; 468 } 469 } 470 printf("\n"); 471 } else if (!memcmp("--supports=", argv[i], 11)) { 472 cap_value_t cap; 473 474 if (cap_from_name(argv[i] + 11, &cap) < 0) { 475 fprintf(stderr, "cap[%s] not recognized by library\n", 476 argv[i] + 11); 477 exit(1); 478 } 479 if (!CAP_IS_SUPPORTED(cap)) { 480 fprintf(stderr, "cap[%s=%d] not supported by kernel\n", 481 argv[i] + 11, cap); 482 exit(1); 483 } 484 } else if (!strcmp("--print", argv[i])) { 485 unsigned cap; 486 int set, status, j; 487 cap_t all; 488 char *text; 489 const char *sep; 490 struct group *g; 491 gid_t groups[MAX_GROUPS], gid; 492 uid_t uid; 493 struct passwd *u; 494 495 all = cap_get_proc(); 496 text = cap_to_text(all, NULL); 497 printf("Current: %s\n", text); 498 cap_free(text); 499 cap_free(all); 500 501 printf("Bounding set ="); 502 sep = ""; 503 for (cap=0; (set = cap_get_bound(cap)) >= 0; cap++) { 504 char *ptr; 505 if (!set) { 506 continue; 507 } 508 509 ptr = cap_to_name(cap); 510 if (ptr == NULL) { 511 printf("%s%u", sep, cap); 512 } else { 513 printf("%s%s", sep, ptr); 514 cap_free(ptr); 515 } 516 sep = ","; 517 } 518 printf("\n"); 519 set = prctl(PR_GET_SECUREBITS); 520 if (set >= 0) { 521 const char *b; 522 b = binary(set); /* use verilog convention for binary string */ 523 printf("Securebits: 0%o/0x%x/%u'b%s\n", set, set, 524 (unsigned) strlen(b), b); 525 printf(" secure-noroot: %s (%s)\n", 526 (set & 1) ? "yes":"no", 527 (set & 2) ? "locked":"unlocked"); 528 printf(" secure-no-suid-fixup: %s (%s)\n", 529 (set & 4) ? "yes":"no", 530 (set & 8) ? "locked":"unlocked"); 531 printf(" secure-keep-caps: %s (%s)\n", 532 (set & 16) ? "yes":"no", 533 (set & 32) ? "locked":"unlocked"); 534 } else { 535 printf("[Securebits ABI not supported]\n"); 536 set = prctl(PR_GET_KEEPCAPS); 537 if (set >= 0) { 538 printf(" prctl-keep-caps: %s (locking not supported)\n", 539 set ? "yes":"no"); 540 } else { 541 printf("[Keepcaps ABI not supported]\n"); 542 } 543 } 544 uid = getuid(); 545 u = getpwuid(uid); 546 printf("uid=%u(%s)\n", getuid(), u ? u->pw_name : "???"); 547 gid = getgid(); 548 g = getgrgid(gid); 549 printf("gid=%u(%s)\n", gid, g ? g->gr_name : "???"); 550 printf("groups="); 551 status = getgroups(MAX_GROUPS, groups); 552 sep = ""; 553 for (j=0; j < status; j++) { 554 g = getgrgid(groups[j]); 555 printf("%s%u(%s)", sep, groups[j], g ? g->gr_name : "???"); 556 sep = ","; 557 } 558 printf("\n"); 559 } else if ((!strcmp("--", argv[i])) || (!strcmp("==", argv[i]))) { 560 argv[i] = strdup(argv[i][0] == '-' ? "/bin/bash" : argv[0]); 561 argv[argc] = NULL; 562 execve(argv[i], argv+i, envp); 563 fprintf(stderr, "execve /bin/bash failed!\n"); 564 exit(1); 565 } else { 566 usage: 567 printf("usage: %s [args ...]\n" 568 " --help this message (or try 'man capsh')\n" 569 " --print display capability relevant state\n" 570 " --decode=xxx decode a hex string to a list of caps\n" 571 " --supports=xxx exit 1 if capability xxx unsupported\n" 572 " --drop=xxx remove xxx,.. capabilities from bset\n" 573 " --caps=xxx set caps as per cap_from_text()\n" 574 " --inh=xxx set xxx,.. inheritiable set\n" 575 " --secbits=<n> write a new value for securebits\n" 576 " --keep=<n> set keep-capabability bit to <n>\n" 577 " --uid=<n> set uid to <n> (hint: id <username>)\n" 578 " --gid=<n> set gid to <n> (hint: id <username>)\n" 579 " --groups=g,... set the supplemental groups\n" 580 " --user=<name> set uid,gid and groups to that of user\n" 581 " --chroot=path chroot(2) to this path\n" 582 " --killit=<n> send signal(n) to child\n" 583 " --forkfor=<n> fork and make child sleep for <n> sec\n" 584 " == re-exec(capsh) with args as for --\n" 585 " -- remaing arguments are for /bin/bash\n" 586 " (without -- [%s] will simply exit(0))\n", 587 argv[0], argv[0]); 588 589 exit(strcmp("--help", argv[i]) != 0); 590 } 591 } 592 593 exit(0); 594 } 595