1 /* 2 * Authors: Dan Walsh <dwalsh (at) redhat.com> 3 * Authors: Thomas Liu <tliu (at) fedoraproject.org> 4 */ 5 6 #define _GNU_SOURCE 7 #include <signal.h> 8 #include <sys/fsuid.h> 9 #include <sys/stat.h> 10 #include <sys/types.h> 11 #include <sys/wait.h> 12 #include <syslog.h> 13 #include <sys/mount.h> 14 #include <glob.h> 15 #include <pwd.h> 16 #include <sched.h> 17 #include <string.h> 18 #include <stdio.h> 19 #include <regex.h> 20 #include <unistd.h> 21 #include <stdlib.h> 22 #include <cap-ng.h> 23 #include <getopt.h> /* for getopt_long() form of getopt() */ 24 #include <limits.h> 25 #include <stdlib.h> 26 #include <errno.h> 27 #include <fcntl.h> 28 29 #include <selinux/selinux.h> 30 #include <selinux/context.h> /* for context-mangling functions */ 31 #include <dirent.h> 32 33 #ifdef USE_NLS 34 #include <locale.h> /* for setlocale() */ 35 #include <libintl.h> /* for gettext() */ 36 #define _(msgid) gettext (msgid) 37 #else 38 #define _(msgid) (msgid) 39 #endif 40 41 #ifndef MS_REC 42 #define MS_REC 1<<14 43 #endif 44 45 #ifndef MS_SLAVE 46 #define MS_SLAVE 1<<19 47 #endif 48 49 #ifndef PACKAGE 50 #define PACKAGE "policycoreutils" /* the name of this package lang translation */ 51 #endif 52 53 #define BUF_SIZE 1024 54 #define DEFAULT_PATH "/usr/bin:/bin" 55 #define USAGE_STRING _("USAGE: seunshare [ -v ] [ -C ] [ -k ] [ -t tmpdir ] [ -h homedir ] [ -Z CONTEXT ] -- executable [args] ") 56 57 static int verbose = 0; 58 static int child = 0; 59 60 static capng_select_t cap_set = CAPNG_SELECT_CAPS; 61 62 /** 63 * This function will drop all capabilities. 64 */ 65 static int drop_caps(void) 66 { 67 if (capng_have_capabilities(cap_set) == CAPNG_NONE) 68 return 0; 69 capng_clear(cap_set); 70 if (capng_lock() == -1 || capng_apply(cap_set) == -1) { 71 fprintf(stderr, _("Failed to drop all capabilities\n")); 72 return -1; 73 } 74 return 0; 75 } 76 77 /** 78 * This function will drop all privileges. 79 */ 80 static int drop_privs(uid_t uid) 81 { 82 if (drop_caps() == -1 || setresuid(uid, uid, uid) == -1) { 83 fprintf(stderr, _("Failed to drop privileges\n")); 84 return -1; 85 } 86 return 0; 87 } 88 89 /** 90 * If the user sends a siginto to seunshare, kill the child's session 91 */ 92 void handler(int sig) { 93 if (child > 0) kill(-child,sig); 94 } 95 96 /** 97 * Take care of any signal setup. 98 */ 99 static int set_signal_handles(void) 100 { 101 sigset_t empty; 102 103 /* Empty the signal mask in case someone is blocking a signal */ 104 if (sigemptyset(&empty)) { 105 fprintf(stderr, "Unable to obtain empty signal set\n"); 106 return -1; 107 } 108 109 (void)sigprocmask(SIG_SETMASK, &empty, NULL); 110 111 /* Terminate on SIGHUP */ 112 if (signal(SIGHUP, SIG_DFL) == SIG_ERR) { 113 perror("Unable to set SIGHUP handler"); 114 return -1; 115 } 116 117 if (signal(SIGINT, handler) == SIG_ERR) { 118 perror("Unable to set SIGINT handler"); 119 return -1; 120 } 121 122 return 0; 123 } 124 125 #define status_to_retval(status,retval) do { \ 126 if ((status) == -1) \ 127 retval = -1; \ 128 else if (WIFEXITED((status))) \ 129 retval = WEXITSTATUS((status)); \ 130 else if (WIFSIGNALED((status))) \ 131 retval = 128 + WTERMSIG((status)); \ 132 else \ 133 retval = -1; \ 134 } while(0) 135 136 /** 137 * Spawn external command using system() with dropped privileges. 138 * TODO: avoid system() and use exec*() instead 139 */ 140 static int spawn_command(const char *cmd, uid_t uid){ 141 int childpid; 142 int status = -1; 143 144 if (verbose > 1) 145 printf("spawn_command: %s\n", cmd); 146 147 childpid = fork(); 148 if (childpid == -1) { 149 perror(_("Unable to fork")); 150 return status; 151 } 152 153 if (childpid == 0) { 154 if (drop_privs(uid) != 0) exit(-1); 155 156 status = system(cmd); 157 status_to_retval(status, status); 158 exit(status); 159 } 160 161 waitpid(childpid, &status, 0); 162 status_to_retval(status, status); 163 return status; 164 } 165 166 /** 167 * Check file/directory ownership, struct stat * must be passed to the 168 * functions. 169 */ 170 static int check_owner_uid(uid_t uid, const char *file, struct stat *st) { 171 if (S_ISLNK(st->st_mode)) { 172 fprintf(stderr, _("Error: %s must not be a symbolic link\n"), file); 173 return -1; 174 } 175 if (st->st_uid != uid) { 176 fprintf(stderr, _("Error: %s not owned by UID %d\n"), file, uid); 177 return -1; 178 } 179 return 0; 180 } 181 182 static int check_owner_gid(gid_t gid, const char *file, struct stat *st) { 183 if (S_ISLNK(st->st_mode)) { 184 fprintf(stderr, _("Error: %s must not be a symbolic link\n"), file); 185 return -1; 186 } 187 if (st->st_gid != gid) { 188 fprintf(stderr, _("Error: %s not owned by GID %d\n"), file, gid); 189 return -1; 190 } 191 return 0; 192 } 193 194 #define equal_stats(one,two) \ 195 ((one)->st_dev == (two)->st_dev && (one)->st_ino == (two)->st_ino && \ 196 (one)->st_uid == (two)->st_uid && (one)->st_gid == (two)->st_gid && \ 197 (one)->st_mode == (two)->st_mode) 198 199 /** 200 * Sanity check specified directory. Store stat info for future comparison, or 201 * compare with previously saved info to detect replaced directories. 202 * Note: This function does not perform owner checks. 203 */ 204 static int verify_directory(const char *dir, struct stat *st_in, struct stat *st_out) { 205 struct stat sb; 206 207 if (st_out == NULL) st_out = &sb; 208 209 if (lstat(dir, st_out) == -1) { 210 fprintf(stderr, _("Failed to stat %s: %s\n"), dir, strerror(errno)); 211 return -1; 212 } 213 if (! S_ISDIR(st_out->st_mode)) { 214 fprintf(stderr, _("Error: %s is not a directory: %s\n"), dir, strerror(errno)); 215 return -1; 216 } 217 if (st_in && !equal_stats(st_in, st_out)) { 218 fprintf(stderr, _("Error: %s was replaced by a different directory\n"), dir); 219 return -1; 220 } 221 222 return 0; 223 } 224 225 /** 226 * This function checks to see if the shell is known in /etc/shells. 227 * If so, it returns 0. On error or illegal shell, it returns -1. 228 */ 229 static int verify_shell(const char *shell_name) 230 { 231 int rc = -1; 232 const char *buf; 233 234 if (!(shell_name && shell_name[0])) 235 return rc; 236 237 while ((buf = getusershell()) != NULL) { 238 /* ignore comments */ 239 if (*buf == '#') 240 continue; 241 242 /* check the shell skipping newline char */ 243 if (!strcmp(shell_name, buf)) { 244 rc = 0; 245 break; 246 } 247 } 248 endusershell(); 249 return rc; 250 } 251 252 /** 253 * Mount directory and check that we mounted the right directory. 254 */ 255 static int seunshare_mount(const char *src, const char *dst, struct stat *src_st) 256 { 257 int flags = 0; 258 int is_tmp = 0; 259 260 if (verbose) 261 printf(_("Mounting %s on %s\n"), src, dst); 262 263 if (strcmp("/tmp", dst) == 0) { 264 flags = flags | MS_NODEV | MS_NOSUID | MS_NOEXEC; 265 is_tmp = 1; 266 } 267 268 /* mount directory */ 269 if (mount(src, dst, NULL, MS_BIND | flags, NULL) < 0) { 270 fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno)); 271 return -1; 272 } 273 274 /* verify whether we mounted what we expected to mount */ 275 if (verify_directory(dst, src_st, NULL) < 0) return -1; 276 277 /* bind mount /tmp on /var/tmp too */ 278 if (is_tmp) { 279 if (verbose) 280 printf(_("Mounting /tmp on /var/tmp\n")); 281 282 if (mount("/tmp", "/var/tmp", NULL, MS_BIND | flags, NULL) < 0) { 283 fprintf(stderr, _("Failed to mount /tmp on /var/tmp: %s\n"), strerror(errno)); 284 return -1; 285 } 286 } 287 288 return 0; 289 290 } 291 292 /* 293 If path is empy or ends with "/." or "/.. return -1 else return 0; 294 */ 295 static int bad_path(const char *path) { 296 const char *ptr; 297 ptr = path; 298 while (*ptr) ptr++; 299 if (ptr == path) return -1; // ptr null 300 ptr--; 301 if (ptr != path && *ptr == '.') { 302 ptr--; 303 if (*ptr == '/') return -1; // path ends in /. 304 if (*ptr == '.') { 305 if (ptr != path) { 306 ptr--; 307 if (*ptr == '/') return -1; // path ends in /.. 308 } 309 } 310 } 311 return 0; 312 } 313 314 static int rsynccmd(const char * src, const char *dst, char **cmdbuf) 315 { 316 char *buf = NULL; 317 char *newbuf = NULL; 318 glob_t fglob; 319 fglob.gl_offs = 0; 320 int flags = GLOB_PERIOD; 321 unsigned int i = 0; 322 int rc = -1; 323 324 /* match glob for all files in src dir */ 325 if (asprintf(&buf, "%s/*", src) == -1) { 326 fprintf(stderr, "Out of memory\n"); 327 return -1; 328 } 329 330 if (glob(buf, flags, NULL, &fglob) != 0) { 331 free(buf); buf = NULL; 332 return -1; 333 } 334 335 free(buf); buf = NULL; 336 337 for ( i=0; i < fglob.gl_pathc; i++) { 338 const char *path = fglob.gl_pathv[i]; 339 340 if (bad_path(path)) continue; 341 342 if (!buf) { 343 if (asprintf(&newbuf, "\'%s\'", path) == -1) { 344 fprintf(stderr, "Out of memory\n"); 345 goto err; 346 } 347 } else { 348 if (asprintf(&newbuf, "%s \'%s\'", buf, path) == -1) { 349 fprintf(stderr, "Out of memory\n"); 350 goto err; 351 } 352 } 353 354 free(buf); buf = newbuf; 355 newbuf = NULL; 356 } 357 358 if (buf) { 359 if (asprintf(&newbuf, "/usr/bin/rsync -trlHDq %s '%s'", buf, dst) == -1) { 360 fprintf(stderr, "Out of memory\n"); 361 goto err; 362 } 363 *cmdbuf=newbuf; 364 } 365 else { 366 *cmdbuf=NULL; 367 } 368 rc = 0; 369 370 err: 371 free(buf); buf = NULL; 372 globfree(&fglob); 373 return rc; 374 } 375 376 /** 377 * Clean up runtime temporary directory. Returns 0 if no problem was detected, 378 * >0 if some error was detected, but errors here are treated as non-fatal and 379 * left to tmpwatch to finish incomplete cleanup. 380 */ 381 static int cleanup_tmpdir(const char *tmpdir, const char *src, 382 struct passwd *pwd, int copy_content) 383 { 384 char *cmdbuf = NULL; 385 int rc = 0; 386 387 /* rsync files back */ 388 if (copy_content) { 389 if (asprintf(&cmdbuf, "/usr/bin/rsync --exclude=.X11-unix -utrlHDq --delete '%s/' '%s/'", tmpdir, src) == -1) { 390 fprintf(stderr, _("Out of memory\n")); 391 cmdbuf = NULL; 392 rc++; 393 } 394 if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) { 395 fprintf(stderr, _("Failed to copy files from the runtime temporary directory\n")); 396 rc++; 397 } 398 free(cmdbuf); cmdbuf = NULL; 399 } 400 401 /* remove files from the runtime temporary directory */ 402 if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) { 403 fprintf(stderr, _("Out of memory\n")); 404 cmdbuf = NULL; 405 rc++; 406 } 407 /* this may fail if there's root-owned file left in the runtime tmpdir */ 408 if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++; 409 free(cmdbuf); cmdbuf = NULL; 410 411 /* remove runtime temporary directory */ 412 if ((uid_t)setfsuid(0) != 0) { 413 /* setfsuid does not return errror, but this check makes code checkers happy */ 414 rc++; 415 } 416 417 if (rmdir(tmpdir) == -1) 418 fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno)); 419 if ((uid_t)setfsuid(pwd->pw_uid) != 0) { 420 fprintf(stderr, _("unable to switch back to user after clearing tmp dir\n")); 421 rc++; 422 } 423 424 return rc; 425 } 426 427 /** 428 * seunshare will create a tmpdir in /tmp, with root ownership. The parent 429 * process waits for it child to exit to attempt to remove the directory. If 430 * it fails to remove the directory, we will need to rely on tmpreaper/tmpwatch 431 * to clean it up. 432 */ 433 static char *create_tmpdir(const char *src, struct stat *src_st, 434 struct stat *out_st, struct passwd *pwd, security_context_t execcon) 435 { 436 char *tmpdir = NULL; 437 char *cmdbuf = NULL; 438 int fd_t = -1, fd_s = -1; 439 struct stat tmp_st; 440 security_context_t con = NULL; 441 442 /* get selinux context */ 443 if (execcon) { 444 if ((uid_t)setfsuid(pwd->pw_uid) != 0) 445 goto err; 446 447 if ((fd_s = open(src, O_RDONLY)) < 0) { 448 fprintf(stderr, _("Failed to open directory %s: %s\n"), src, strerror(errno)); 449 goto err; 450 } 451 if (fstat(fd_s, &tmp_st) == -1) { 452 fprintf(stderr, _("Failed to stat directory %s: %s\n"), src, strerror(errno)); 453 goto err; 454 } 455 if (!equal_stats(src_st, &tmp_st)) { 456 fprintf(stderr, _("Error: %s was replaced by a different directory\n"), src); 457 goto err; 458 } 459 if (fgetfilecon(fd_s, &con) == -1) { 460 fprintf(stderr, _("Failed to get context of the directory %s: %s\n"), src, strerror(errno)); 461 goto err; 462 } 463 464 /* ok to not reach this if there is an error */ 465 if ((uid_t)setfsuid(0) != pwd->pw_uid) 466 goto err; 467 } 468 469 if (asprintf(&tmpdir, "/tmp/.sandbox-%s-XXXXXX", pwd->pw_name) == -1) { 470 fprintf(stderr, _("Out of memory\n")); 471 tmpdir = NULL; 472 goto err; 473 } 474 if (mkdtemp(tmpdir) == NULL) { 475 fprintf(stderr, _("Failed to create temporary directory: %s\n"), strerror(errno)); 476 goto err; 477 } 478 479 /* temporary directory must be owned by root:user */ 480 if (verify_directory(tmpdir, NULL, out_st) < 0) { 481 goto err; 482 } 483 484 if (check_owner_uid(0, tmpdir, out_st) < 0) 485 goto err; 486 487 if (check_owner_gid(getgid(), tmpdir, out_st) < 0) 488 goto err; 489 490 /* change permissions of the temporary directory */ 491 if ((fd_t = open(tmpdir, O_RDONLY)) < 0) { 492 fprintf(stderr, _("Failed to open directory %s: %s\n"), tmpdir, strerror(errno)); 493 goto err; 494 } 495 if (fstat(fd_t, &tmp_st) == -1) { 496 fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno)); 497 goto err; 498 } 499 if (!equal_stats(out_st, &tmp_st)) { 500 fprintf(stderr, _("Error: %s was replaced by a different directory\n"), tmpdir); 501 goto err; 502 } 503 if (fchmod(fd_t, 01770) == -1) { 504 fprintf(stderr, _("Unable to change mode on %s: %s\n"), tmpdir, strerror(errno)); 505 goto err; 506 } 507 /* re-stat again to pick change mode */ 508 if (fstat(fd_t, out_st) == -1) { 509 fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno)); 510 goto err; 511 } 512 513 /* copy selinux context */ 514 if (execcon) { 515 if (fsetfilecon(fd_t, con) == -1) { 516 fprintf(stderr, _("Failed to set context of the directory %s: %s\n"), tmpdir, strerror(errno)); 517 goto err; 518 } 519 } 520 521 if ((uid_t)setfsuid(pwd->pw_uid) != 0) 522 goto err; 523 524 if (rsynccmd(src, tmpdir, &cmdbuf) < 0) { 525 goto err; 526 } 527 528 /* ok to not reach this if there is an error */ 529 if ((uid_t)setfsuid(0) != pwd->pw_uid) 530 goto err; 531 532 if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) { 533 fprintf(stderr, _("Failed to populate runtime temporary directory\n")); 534 cleanup_tmpdir(tmpdir, src, pwd, 0); 535 goto err; 536 } 537 538 goto good; 539 err: 540 free(tmpdir); tmpdir = NULL; 541 good: 542 free(cmdbuf); cmdbuf = NULL; 543 freecon(con); con = NULL; 544 if (fd_t >= 0) close(fd_t); 545 if (fd_s >= 0) close(fd_s); 546 return tmpdir; 547 } 548 549 #define PROC_BASE "/proc" 550 551 static int 552 killall (security_context_t execcon) 553 { 554 DIR *dir; 555 security_context_t scon; 556 struct dirent *de; 557 pid_t *pid_table, pid, self; 558 int i; 559 int pids, max_pids; 560 int running = 0; 561 self = getpid(); 562 if (!(dir = opendir(PROC_BASE))) { 563 return -1; 564 } 565 max_pids = 256; 566 pid_table = malloc(max_pids * sizeof (pid_t)); 567 if (!pid_table) { 568 (void)closedir(dir); 569 return -1; 570 } 571 pids = 0; 572 context_t con; 573 con = context_new(execcon); 574 const char *mcs = context_range_get(con); 575 printf("mcs=%s\n", mcs); 576 while ((de = readdir (dir)) != NULL) { 577 if (!(pid = (pid_t)atoi(de->d_name)) || pid == self) 578 continue; 579 580 if (pids == max_pids) { 581 pid_t *new_pid_table = realloc(pid_table, 2*pids*sizeof(pid_t)); 582 if (!new_pid_table) { 583 free(pid_table); 584 (void)closedir(dir); 585 return -1; 586 } 587 pid_table = new_pid_table; 588 max_pids *= 2; 589 } 590 pid_table[pids++] = pid; 591 } 592 593 (void)closedir(dir); 594 595 for (i = 0; i < pids; i++) { 596 pid_t id = pid_table[i]; 597 598 if (getpidcon(id, &scon) == 0) { 599 600 context_t pidcon = context_new(scon); 601 /* Attempt to kill remaining processes */ 602 if (strcmp(context_range_get(pidcon), mcs) == 0) 603 kill(id, SIGKILL); 604 605 context_free(pidcon); 606 freecon(scon); 607 } 608 running++; 609 } 610 611 context_free(con); 612 free(pid_table); 613 return running; 614 } 615 616 int main(int argc, char **argv) { 617 int status = -1; 618 security_context_t execcon = NULL; 619 620 int clflag; /* holds codes for command line flags */ 621 int kill_all = 0; 622 623 char *homedir_s = NULL; /* homedir spec'd by user in argv[] */ 624 char *tmpdir_s = NULL; /* tmpdir spec'd by user in argv[] */ 625 char *tmpdir_r = NULL; /* tmpdir created by seunshare */ 626 627 struct stat st_curhomedir; 628 struct stat st_homedir; 629 struct stat st_tmpdir_s; 630 struct stat st_tmpdir_r; 631 632 const struct option long_options[] = { 633 {"homedir", 1, 0, 'h'}, 634 {"tmpdir", 1, 0, 't'}, 635 {"kill", 1, 0, 'k'}, 636 {"verbose", 1, 0, 'v'}, 637 {"context", 1, 0, 'Z'}, 638 {"capabilities", 1, 0, 'C'}, 639 {NULL, 0, 0, 0} 640 }; 641 642 uid_t uid = getuid(); 643 /* 644 if (!uid) { 645 fprintf(stderr, _("Must not be root")); 646 return -1; 647 } 648 */ 649 650 #ifdef USE_NLS 651 setlocale(LC_ALL, ""); 652 bindtextdomain(PACKAGE, LOCALEDIR); 653 textdomain(PACKAGE); 654 #endif 655 656 struct passwd *pwd=getpwuid(uid); 657 if (!pwd) { 658 perror(_("getpwduid failed")); 659 return -1; 660 } 661 662 if (verify_shell(pwd->pw_shell) < 0) { 663 fprintf(stderr, _("Error: User shell is not valid\n")); 664 return -1; 665 } 666 667 while (1) { 668 clflag = getopt_long(argc, argv, "Ccvh:t:Z:", long_options, NULL); 669 if (clflag == -1) 670 break; 671 672 switch (clflag) { 673 case 't': 674 tmpdir_s = optarg; 675 break; 676 case 'k': 677 kill_all = 1; 678 break; 679 case 'h': 680 homedir_s = optarg; 681 break; 682 case 'v': 683 verbose++; 684 break; 685 case 'C': 686 cap_set = CAPNG_SELECT_CAPS; 687 break; 688 case 'Z': 689 execcon = optarg; 690 break; 691 default: 692 fprintf(stderr, "%s\n", USAGE_STRING); 693 return -1; 694 } 695 } 696 697 if (! homedir_s && ! tmpdir_s) { 698 fprintf(stderr, _("Error: tmpdir and/or homedir required\n %s\n"), USAGE_STRING); 699 return -1; 700 } 701 702 if (argc - optind < 1) { 703 fprintf(stderr, _("Error: executable required\n %s\n"), USAGE_STRING); 704 return -1; 705 } 706 707 if (execcon && is_selinux_enabled() != 1) { 708 fprintf(stderr, _("Error: execution context specified, but SELinux is not enabled\n")); 709 return -1; 710 } 711 712 if (set_signal_handles()) 713 return -1; 714 715 /* set fsuid to ruid */ 716 /* Changing fsuid is usually required when user-specified directory is 717 * on an NFS mount. It's also desired to avoid leaking info about 718 * existence of the files not accessible to the user. */ 719 if (((uid_t)setfsuid(uid) != 0) && (errno != 0)) { 720 fprintf(stderr, _("Error: unable to setfsuid %m\n")); 721 722 return -1; 723 } 724 725 /* verify homedir and tmpdir */ 726 if (homedir_s && ( 727 verify_directory(homedir_s, NULL, &st_homedir) < 0 || 728 check_owner_uid(uid, homedir_s, &st_homedir))) return -1; 729 if (tmpdir_s && ( 730 verify_directory(tmpdir_s, NULL, &st_tmpdir_s) < 0 || 731 check_owner_uid(uid, tmpdir_s, &st_tmpdir_s))) return -1; 732 if ((uid_t)setfsuid(0) != uid) return -1; 733 734 /* create runtime tmpdir */ 735 if (tmpdir_s && (tmpdir_r = create_tmpdir(tmpdir_s, &st_tmpdir_s, 736 &st_tmpdir_r, pwd, execcon)) == NULL) { 737 fprintf(stderr, _("Failed to create runtime temporary directory\n")); 738 return -1; 739 } 740 741 /* spawn child process */ 742 child = fork(); 743 if (child == -1) { 744 perror(_("Unable to fork")); 745 goto err; 746 } 747 748 if (child == 0) { 749 char *display = NULL; 750 char *LANG = NULL; 751 char *RUNTIME_DIR = NULL; 752 int rc = -1; 753 char *resolved_path = NULL; 754 755 if (unshare(CLONE_NEWNS) < 0) { 756 perror(_("Failed to unshare")); 757 goto childerr; 758 } 759 760 /* Remount / as SLAVE so that nothing mounted in the namespace 761 shows up in the parent */ 762 if (mount("none", "/", NULL, MS_SLAVE | MS_REC , NULL) < 0) { 763 perror(_("Failed to make / a SLAVE mountpoint\n")); 764 goto childerr; 765 } 766 767 /* assume fsuid==ruid after this point */ 768 if ((uid_t)setfsuid(uid) != 0) goto childerr; 769 770 resolved_path = realpath(pwd->pw_dir,NULL); 771 if (! resolved_path) goto childerr; 772 773 if (verify_directory(resolved_path, NULL, &st_curhomedir) < 0) 774 goto childerr; 775 if (check_owner_uid(uid, resolved_path, &st_curhomedir) < 0) 776 goto childerr; 777 778 /* mount homedir and tmpdir, in this order */ 779 if (homedir_s && seunshare_mount(homedir_s, resolved_path, 780 &st_homedir) != 0) goto childerr; 781 if (tmpdir_s && seunshare_mount(tmpdir_r, "/tmp", 782 &st_tmpdir_r) != 0) goto childerr; 783 784 if (drop_privs(uid) != 0) goto childerr; 785 786 /* construct a new environment */ 787 if ((display = getenv("DISPLAY")) != NULL) { 788 if ((display = strdup(display)) == NULL) { 789 perror(_("Out of memory")); 790 goto childerr; 791 } 792 } 793 794 /* construct a new environment */ 795 if ((LANG = getenv("LANG")) != NULL) { 796 if ((LANG = strdup(LANG)) == NULL) { 797 perror(_("Out of memory")); 798 goto childerr; 799 } 800 } 801 802 if ((RUNTIME_DIR = getenv("XDG_RUNTIME_DIR")) != NULL) { 803 if ((RUNTIME_DIR = strdup(RUNTIME_DIR)) == NULL) { 804 perror(_("Out of memory")); 805 goto childerr; 806 } 807 } 808 809 if ((rc = clearenv()) != 0) { 810 perror(_("Failed to clear environment")); 811 goto childerr; 812 } 813 if (display) 814 rc |= setenv("DISPLAY", display, 1); 815 if (LANG) 816 rc |= setenv("LANG", LANG, 1); 817 if (RUNTIME_DIR) 818 rc |= setenv("XDG_RUNTIME_DIR", RUNTIME_DIR, 1); 819 rc |= setenv("HOME", pwd->pw_dir, 1); 820 rc |= setenv("SHELL", pwd->pw_shell, 1); 821 rc |= setenv("USER", pwd->pw_name, 1); 822 rc |= setenv("LOGNAME", pwd->pw_name, 1); 823 rc |= setenv("PATH", DEFAULT_PATH, 1); 824 if (rc != 0) { 825 fprintf(stderr, _("Failed to construct environment\n")); 826 goto childerr; 827 } 828 829 if (chdir(pwd->pw_dir)) { 830 perror(_("Failed to change dir to homedir")); 831 goto childerr; 832 } 833 setsid(); 834 835 /* selinux context */ 836 if (execcon) { 837 /* try dyntransition, since no_new_privs can interfere 838 * with setexeccon */ 839 if (setcon(execcon) != 0) { 840 /* failed; fall back to setexeccon */ 841 if (setexeccon(execcon) != 0) { 842 fprintf(stderr, _("Could not set exec context to %s. %s\n"), execcon, strerror(errno)); 843 goto childerr; 844 } 845 } 846 } 847 848 execv(argv[optind], argv + optind); 849 fprintf(stderr, _("Failed to execute command %s: %s\n"), argv[optind], strerror(errno)); 850 childerr: 851 free(resolved_path); 852 free(display); 853 free(LANG); 854 free(RUNTIME_DIR); 855 exit(-1); 856 } 857 858 drop_caps(); 859 860 /* parent waits for child exit to do the cleanup */ 861 waitpid(child, &status, 0); 862 status_to_retval(status, status); 863 864 /* Make sure all child processes exit */ 865 kill(-child,SIGTERM); 866 867 if (execcon && kill_all) 868 killall(execcon); 869 870 if (tmpdir_r) cleanup_tmpdir(tmpdir_r, tmpdir_s, pwd, 1); 871 872 err: 873 free(tmpdir_r); 874 return status; 875 } 876