#include #include #include #include #include #define _GNU_SOURCE #include #include #include #include #include #include #include #include /* for getopt_long() form of getopt() */ #include #include /* for context-mangling functions */ /** * This function will drop the capabilities so that we are left * only with access to the audit system and the ability to raise * CAP_SYS_ADMIN, CAP_DAC_OVERRIDE, CAP_FOWNER and CAP_CHOWN, * before invoking pam_namespace. These capabilities are needed * for performing bind mounts/unmounts and to create potential new * instance directories with appropriate DAC attributes. If the * user is root, we leave the capabilities alone since they already * should have access to the audit netlink socket and should have * the ability to create/mount/unmount instance directories. * * Returns zero on success, non-zero otherwise */ #ifdef DROP static int drop_capabilities(void) { int rc = 0; cap_t new_caps; cap_value_t cap_list[] = { CAP_SYS_ADMIN, CAP_FOWNER, CAP_CHOWN, CAP_DAC_OVERRIDE }; if (!getuid()) return 0; /* Non-root caller, suid root path */ new_caps = cap_init(); if (!new_caps) { fprintf(stderr, "Error initializing capabilities, aborting.\n"); return -1; } rc |= cap_set_flag(new_caps, CAP_PERMITTED, 6, cap_list, CAP_SET); rc |= cap_set_flag(new_caps, CAP_EFFECTIVE, 6, cap_list, CAP_SET); if (rc) { fprintf(stderr, "Error setting capabilities, aborting\n"); goto out; } /* Ensure that caps are dropped after setuid call */ if ((rc = prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) < 0)) { fprintf(stderr, "Error resetting KEEPCAPS, aborting\n"); goto out; } /* We should still have root's caps, so drop most capabilities now */ if ((rc = cap_set_proc(new_caps))) { fprintf(stderr, "Error dropping capabilities, aborting\n"); goto out; } out: if (cap_free(new_caps)) fprintf(stderr, "Error freeing caps\n"); return rc; } #endif #define DEFAULT_PATH "/usr/bin:/bin" /** * Take care of any signal setup */ static int set_signal_handles() { sigset_t empty; /* Empty the signal mask in case someone is blocking a signal */ if (sigemptyset(&empty)) { fprintf(stderr, "Unable to obtain empty signal set\n"); return -1; } (void)sigprocmask(SIG_SETMASK, &empty, NULL); /* Terminate on SIGHUP. */ if (signal(SIGHUP, SIG_DFL) == SIG_ERR) { fprintf(stderr, "Unable to set SIGHUP handler\n"); return -1; } return 0; } #define USAGE_STRING "USAGE: seunshare [ -t tmpdir ] [ -h homedir ] -- CONTEXT executable [args] " int main(int argc, char **argv) { int rc; struct passwd *pwd=getpwuid(getuid()); security_context_t scontext; #ifdef DROP if (drop_capabilities()) return -1; #endif int flag_index; /* flag index in argv[] */ int clflag; /* holds codes for command line flags */ char *tmpdir_s = NULL; /* tmpdir spec'd by user in argv[] */ char *homedir_s = NULL; /* homedir spec'd by user in argv[] */ const struct option long_options[] = { {"homedir", 1, 0, 'h'}, {"tmpdir", 1, 0, 't'}, {NULL, 0, 0, 0} }; while (1) { clflag = getopt_long(argc, argv, "h:t:", long_options, &flag_index); if (clflag == -1) break; switch (clflag) { case 't': tmpdir_s = optarg; break; case 'h': homedir_s = optarg; break; default: fprintf(stderr, "%s\n", USAGE_STRING); return -1; } } if (! homedir_s && ! tmpdir_s) { fprintf(stderr, "Error: tmpdir and/or homedir required \n" "%s\n", USAGE_STRING); return -1; } if (argc - optind < 2) { fprintf(stderr, "Error: executable required \n" "%s\n", USAGE_STRING); return -1; } scontext = argv[optind++]; int i; for (i=optind; i< argc; i++) printf("%s ", argv[i]); printf("\n"); if (set_signal_handles()) return -1; if (unshare(CLONE_NEWNS) < 0) { perror(argv[0]); return -1; } if (homedir_s && mount(homedir_s, pwd->pw_dir, NULL, MS_BIND, NULL) < 0) { perror("mount HOMEDIR"); goto out; } printf("mounting tmpdir_s %s\n", tmpdir_s); if (tmpdir_s && mount(tmpdir_s, "/tmp", NULL, MS_BIND, NULL) < 0) { perror("mount /tmp"); goto out; } int child = fork(); if (!child) { /* Construct a new environment */ char *display = strdup(getenv("DISPLAY")); if (!display) { fprintf(stderr, "Out of memory\n"); exit(-1); } if ((rc = clearenv())) { fprintf(stderr, "Unable to clear environment\n"); exit(-1); } if (setexeccon(scontext)) { fprintf(stderr, "Could not set exec context to %s.\n", scontext); exit(-1); } rc |= setenv("DISPLAY", display, 1); rc |= setenv("HOME", pwd->pw_dir, 1); rc |= setenv("SHELL", pwd->pw_shell, 1); rc |= setenv("USER", pwd->pw_name, 1); rc |= setenv("LOGNAME", pwd->pw_name, 1); rc |= setenv("PATH", DEFAULT_PATH, 1); chdir(pwd->pw_dir); execv(argv[optind], argv + optind); perror("execv"); exit(-1); } else { int status=0; printf("parent child = %d\n", child); waitpid(child, &status, 0); printf("Status = %d\n", status); } out: if (tmpdir_s && umount("/tmp") < 0) { perror("umount /tmp"); } if (homedir_s && umount(pwd->pw_dir) < 0) { perror("umount homedir"); } return 0; }