int container_exec(int argc, char **argv) { message(DEBUG, "Called container_exec(%d, **argv)\n", argc); if ( argc <= 1 ) { message(ERROR, "Exec requires a command to run\n"); ABORT(255); } if ( is_exec("/.exec") == 0 ) { argv[0] = strdup("Singularity"); message(VERBOSE, "Found /.exec inside container, exec()'ing...\n"); if ( execv("/.exec", argv) != 0 ) { // Flawfinder: ignore (exec* is necessary) message(ERROR, "Exec of /.exec failed: %s\n", strerror(errno)); ABORT(255); } } else if ( is_exec(argv[1]) == 0 ) { message(VERBOSE, "Exec'ing program: %s\n", argv[1]); if ( execv(argv[1], &argv[1]) != 0 ) { // Flawfinder: ignore (exec* is necessary) message(ERROR, "execv of '%s' failed: %s\n", argv[1], strerror(errno)); ABORT(255); } } else { message(VERBOSE, "Exec'ing program: %s\n", argv[1]); if ( execvp(argv[1], &argv[1]) != 0 ) { // Flawfinder: ignore (exec* is necessary) message(ERROR, "execvp of '%s' failed: %s\n", argv[1], strerror(errno)); ABORT(255); } } message(ERROR, "We should not have reached the end of container_exec\n"); return(-1); }
int mode_passif(t_link *link, char **av, char **env) { char **cmd; char **cmd_two; if (av[1] == NULL) return (FALSE); if (av[2] == NULL) { my_printf("Mode_passif : ./42sh -c string\n"); return (FALSE); } else if (my_strcmp(av[1], "-c") == 0) { cmd = edit_command(link, my_epur_str(av[2])); cmd_two = my_explode(my_epur_str(av[2]), ' '); if (cmd == NULL) my_printf("%s : command not found\n", av[2]); else if (access(cmd_two[0], X_OK) == 0 && is_exec(cmd_two[0])) my_printf("%s : command not found\n", av[2]); else execve(cmd[0], cmd, env); my_printf("%s : command not found\n", av[2]); return (TRUE); } my_printf("Mode_passif : ./42sh -c string\n"); return (FALSE); }
int basic_exec(char **param, t_lst *lst) { pid_t pid; int status; int i; char **env; i = 0; env = lst_to_tab(lst->next); if (is_exec(param) == 0) return (0); if (param[0][0] == '.' || param[0][0] == '/') { if (access(param[0], X_OK) == 0) { pid = fork(); if (pid > 0) waitpid(pid, &status, 0); if (pid == 0) execve(param[0], param, env); return (1); } } return (0); }
int singularity_rootfs_check(void) { singularity_message(DEBUG, "Checking if container has /bin/sh...\n"); if ( ( is_exec(joinpath(joinpath(mount_point, OVERLAY_FINAL), "/bin/sh")) < 0 ) && ( is_link(joinpath(joinpath(mount_point, OVERLAY_FINAL), "/bin/sh")) < 0 ) ) { singularity_message(ERROR, "Container does not have a valid /bin/sh\n"); ABORT(255); } return(0); }
void action_test_do(int argc, char **argv) { singularity_message(VERBOSE, "Exec'ing /.test\n"); if ( is_exec("/.test") == 0 ) { if ( execl("/bin/sh", "test:", "-e", "-x", "/.test", NULL) < 0 ) { // Flawfinder: ignore singularity_message(ERROR, "Failed to execv() /.test: %s\n", strerror(errno)); } } else { singularity_message(INFO, "No test code provided in this container\n"); exit(0); } singularity_message(ERROR, "We should never get here... Grrrrrr!\n"); ABORT(255); }
int container_run(int argc, char **argv) { message(DEBUG, "Called container_run(%d, **argv)\n", argc); if ( is_exec("/.run") == 0 ) { argv[0] = strdup("/.run"); message(VERBOSE, "Found /.run inside container, exec()'ing...\n"); if ( execv("/.run", argv) != 0 ) { // Flawfinder: ignore (exec* is necessary) message(ERROR, "Exec of /.run failed: %s\n", strerror(errno)); ABORT(255); } } else if ( is_exec("/singularity") == 0 ) { argv[0] = strdup("/singularity"); message(VERBOSE, "Found /singularity inside container, exec()'ing...\n"); if ( execv("/singularity", argv) != 0 ) { // Flawfinder: ignore (exec* is necessary) message(ERROR, "Exec of /singularity failed: %s\n", strerror(errno)); ABORT(255); } } else { message(WARNING, "No Singularity runscript found, launching 'shell'\n"); container_shell(argc, argv); } message(ERROR, "We should not have reached the end of container_run()\n"); return(-1); }
int container_shell(int argc, char **argv) { message(DEBUG, "Called container_shell(%d, **argv)\n", argc); if ( is_exec("/.shell") == 0 ) { argv[0] = strdup("Singularity"); message(VERBOSE, "Exec()'ing /.shell\n"); if ( execv("/.shell", argv) != 0 ) { // Flawfinder: ignore (exec* is necessary) message(ERROR, "Exec of /.shell failed: %s\n", strerror(errno)); } } else { argv[0] = strdup("/bin/sh"); message(VERBOSE, "Exec()'ing /bin/sh...\n"); if ( execv("/bin/sh", argv) != 0 ) { // Flawfinder: ignore (exec* is necessary) message(ERROR, "Exec of /bin/sh failed: %s\n", strerror(errno)); } } message(ERROR, "We should not have reached the end of container_shell()\n"); return(-1); }
int main(int argc, char **argv) { int i, cleanupd_fd; struct tempfile *stdout_log, *stderr_log, *singularity_debug; struct image_object image; pid_t child; siginfo_t siginfo; struct stat filestat; singularity_config_init(); singularity_suid_init(); singularity_priv_init(); singularity_registry_init(); singularity_priv_drop(); singularity_runtime_autofs(); singularity_registry_set("UNSHARE_PID", "1"); singularity_registry_set("UNSHARE_IPC", "1"); if ( singularity_registry_get("INSTANCE_BOOT") != NULL ) { singularity_registry_set("CONTAIN", "1"); } singularity_cleanupd(); if ( singularity_registry_get("WRITABLE") != NULL ) { singularity_message(VERBOSE3, "Instantiating writable container image object\n"); image = singularity_image_init(singularity_registry_get("IMAGE"), O_RDWR); } else { singularity_message(VERBOSE3, "Instantiating read only container image object\n"); image = singularity_image_init(singularity_registry_get("IMAGE"), O_RDONLY); } singularity_runtime_ns(SR_NS_ALL); singularity_sessiondir(); singularity_image_mount(&image, CONTAINER_MOUNTDIR); action_ready(); singularity_runtime_overlayfs(); singularity_runtime_mounts(); singularity_runtime_files(); /* After this point, we are running as PID 1 inside PID NS */ singularity_message(DEBUG, "Preparing sinit daemon\n"); singularity_registry_set("ROOTFS", CONTAINER_FINALDIR); singularity_daemon_init(); singularity_message(DEBUG, "Entering chroot environment\n"); singularity_runtime_enter(); singularity_priv_drop_perm(); if ( envclean() != 0 ) { singularity_message(ERROR, "Failed sanitizing the environment\n"); ABORT(255); } if ( chdir("/") < 0 ) { singularity_message(ERROR, "Can't change directory to /\n"); } setsid(); umask(0); cleanupd_fd = atoi(singularity_registry_get("CLEANUPD_FD")); if ( singularity_registry_get("INSTANCE_BOOT") != NULL ) { int pipes[2]; if ( pipe2(pipes, O_CLOEXEC) < 0 ) { singularity_signal_go_ahead(255); return(0); } if ( fork() == 0 ) { /* wait a broken pipe which mean exec success */ struct pollfd pfd; pfd.fd = pipes[0]; pfd.events = POLLRDHUP; close(pipes[1]); while( poll(&pfd, 1, 1000) >= 0 ) { if ( pfd.revents == POLLHUP ) break; } singularity_signal_go_ahead(0); /* wait /sbin/init install signal handler */ usleep(20000); return(0); } else { close(pipes[0]); if ( is_exec("/sbin/init") == 0 ) { argv[1] = NULL; if ( execv("/sbin/init", argv) < 0 ) { // Flawfinder: ignore singularity_message(ERROR, "Exec of /sbin/init failed\n"); } } else { singularity_message(ERROR, "/sbin/init not present in container\n"); } /* send exit status and implicitly kill polling child */ singularity_signal_go_ahead(255); return(0); } } /* set program name */ if ( prctl(PR_SET_NAME, "sinit", 0, 0, 0) < 0 ) { singularity_message(ERROR, "Failed to set program name\n"); ABORT(255); } singularity_install_signal_handler(); /* Close all open fd's that may be present besides daemon info file fd */ singularity_message(DEBUG, "Closing open fd's\n"); for( i = sysconf(_SC_OPEN_MAX); i > 2; i-- ) { if ( i != cleanupd_fd ) { if ( fstat(i, &filestat) == 0 ) { if ( S_ISFIFO(filestat.st_mode) != 0 ) { continue; } } close(i); } } singularity_debug = make_logfile("singularity-debug"); stdout_log = make_logfile("stdout"); stderr_log = make_logfile("stderr"); for( i = 0; i <= 2; i++ ) { close(i); } if ( chdir("/") < 0 ) { singularity_message(ERROR, "Can't change directory to /\n"); } setsid(); umask(0); /* set program name */ if ( prctl(PR_SET_NAME, "sinit", 0, 0, 0) < 0 ) { singularity_message(ERROR, "Failed to set program name\n"); ABORT(255); } child = fork(); if ( child == 0 ) { /* Make standard output and standard error files to log stdout & stderr into */ if ( stdout_log != NULL ) { if ( -1 == dup2(stdout_log->fd, 1) ) { singularity_message(ERROR, "Unable to dup2(): %s\n", strerror(errno)); ABORT(255); } } if ( stderr_log != NULL ) { if ( -1 == dup2(stderr_log->fd, 2) ) { singularity_message(ERROR, "Unable to dup2(): %s\n", strerror(errno)); ABORT(255); } } /* Unblock signals and execute startscript */ singularity_unblock_signals(); if ( is_exec("/.singularity.d/actions/start") == 0 ) { singularity_message(DEBUG, "Exec'ing /.singularity.d/actions/start\n"); if ( execv("/.singularity.d/actions/start", argv) < 0 ) { // Flawfinder: ignore singularity_message(ERROR, "Failed to execv() /.singularity.d/actions/start: %s\n", strerror(errno)); ABORT(CHILD_FAILED); } } else { singularity_message(VERBOSE, "Instance start script not found\n"); kill(1, SIGCONT); } } else if ( child > 0 ) { if ( singularity_debug != NULL ) { if ( -1 == dup2(singularity_debug->fd, 2) ) { singularity_message(ERROR, "Unable to dup2(): %s\n", strerror(errno)); ABORT(255); } } singularity_message(DEBUG, "Waiting for signals\n"); /* send a SIGALRM if start script doesn't send SIGCONT within 1 seconds */ alarm(1); while (1) { if ( singularity_handle_signals(&siginfo) < 0 ) { singularity_signal_go_ahead(255); break; } if ( siginfo.si_signo == SIGCHLD ) { singularity_message(DEBUG, "Child exited\n"); if ( siginfo.si_pid == 2 && siginfo.si_status == CHILD_FAILED ) { singularity_signal_go_ahead(CHILD_FAILED); break; } } else if ( siginfo.si_signo == SIGCONT && siginfo.si_pid == 2 ) { /* start script correctly exec */ singularity_signal_go_ahead(0); started = 1; } else if ( siginfo.si_signo == SIGALRM && started == 0 ) { /* don't receive SIGCONT, start script modified/replaced ? */ singularity_message(ERROR, "Start script doesn't send SIGCONT\n"); singularity_signal_go_ahead(255); break; } } } else { singularity_message(ERROR, "Failed to execute start script\n"); singularity_signal_go_ahead(255); } return(0); }
static int parse_phrase_no_colon(FILE *stream, const char *fullphrase, int def_path_no) { #define FILENAME_LEN 1000 char filename[FILENAME_LEN]; const char *tmp; const char *phrase; int i; int len; D_("ppnc: fp=\"%s\", i=%d\n", fullphrase, def_path_no); if (!*fullphrase) { fputc('@', stream); return 0; /* @@ => @ translation, no looping required */ } /* handle some special phrases */ for (i = 0; STATIC_OVERLAYS[i].opt; ++i) { if (strcmp(fullphrase, STATIC_OVERLAYS[i].opt) == 0) { printf(" **\t\"@%s@\" set by overlay to \"%s\".\n", fullphrase, STATIC_OVERLAYS[i].value); fputs(STATIC_OVERLAYS[i].value, stream); return 0; } } /* if (!path) return; */ if (!(phrase = strrchr(fullphrase, '/'))) phrase = fullphrase; else phrase++; if (phrase != fullphrase) { /* this means fullphrase contains an / */ if (is_exec(fullphrase)) { fputs(fullphrase, stream); return 0; /* found filename in supplied * Path => no looping req'd */ } } /* compare with all overlays */ i = 0; while (overlay[i].opt) { if (strcasecmp(fullphrase, overlay[i].opt) == 0) { printf(" **\t\"@%s@\" set by overlay to \"%s\".\n", fullphrase, overlay[i].value); fputs(overlay[i].value, stream); return 0; } i++; } /* first, search the path */ i = 0; while (path[i]) { memset(filename, 0, sizeof filename); strncpy(filename, path[i], FILENAME_LEN - 2); len = strlen(filename); if (filename[len - 1] != '/') { filename[len] = '/'; filename[len + 1] = '\0'; } strncat(filename, phrase, FILENAME_LEN - 1 - strlen(filename)); if (is_exec(filename)) { fputs(filename, stream); return 0; /* found filename in Path => no looping req'd */ } i++; } /* then, search additional paths */ i = 0; while (additional_paths[i]) { strncpy(filename, additional_paths[i], FILENAME_LEN - 1); strncat(filename, phrase, FILENAME_LEN - 1 - strlen(additional_paths[i])); if (is_exec(filename)) { fputs(filename, stream); return 0; /* found filename in Path => no * looping req'd */ } i++; } /* here we neither found it in the path, nor in the additional paths */ if (def_path_no == -1) { return 1; /* this is from a coloned search: the first * bastard wasn't found, try the next */ } fprintf(stderr, " **\tWARNING: No executable found for \"%s\", ", phrase); if (fullphrase != phrase) { fprintf(stderr, " **\tusing full path \"%s\"\n", fullphrase); fputs(fullphrase, stream); /* return 0; full path specified => no looping req'd */ } else if (at_default_path) { tmp = at_default_paths[def_path_no]; fprintf(stderr, " **\tusing supplied default path \"%s\"\n", tmp); fputs(tmp, stream); if (tmp[strlen(tmp) - 1] != '/') fputc('/', stream); fputs(phrase, stream); if (at_default_paths[def_path_no + 1]) return 1; /* next default path exits, so looping * is required */ /* return 0 */ } else { /* at_default_path == NULL && fullphrase == phrase */ fprintf(stderr, " **\tusing builtin default path \"/usr/sbin/\"\n"); fputs("/usr/sbin/", stream); fputs(phrase, stream); /* no default path(s) so no looping */ } /* exit(1) ??? */ return 0; #if 0 tmp = path; while (1) { i = 0; while (*tmp != ':' && *tmp != '\0' && i < FILENAME_LEN - 1) filename[i++] = *(tmp++); if (*tmp) tmp++; /* skip ':' */ if (filename[i] != '/') filename[i++] = '/'; filename[i] = '\0'; strncat(filename, phrase, FILENAME_LEN - 1 - i); /* TODO: check if executable */ if (is_exec(filename)) { fputs(filename, stream); return 0; /* found filename in Path => no * looping req'd */ } if (!*tmp) { /* here we passed all paths in PATH */ i = 0; while (additional_paths[i]) { strncpy(filename, additional_paths[i], FILENAME_LEN - 1); strncat(filename, phrase, FILENAME_LEN - 1 - strlen(additional_paths[i])); if (is_exec(filename)) { fputs(filename, stream); return 0; /* found filename in * Path => no looping * req'd */ } i++; } if (def_path_no == -1) { return 1; /* this is from a coloned * search: the first bastard * wasn't found, try the next */ } fprintf(stderr, " **\tWARNING: No executable found " "for \"%s\", ", phrase); if (fullphrase != phrase) { fprintf(stderr, " **\tusing full path \"%s\"\n", fullphrase); fputs(fullphrase, stream); /* return 0; full path specified => no * looping req'd */ } else if (at_default_path) { tmp = at_default_paths[def_path_no]; fprintf(stderr, " **\tusing supplied default" " path \"%s\"\n", tmp); fputs(tmp, stream); if (tmp[strlen(tmp) - 1] != '/') fputc('/', stream); fputs(phrase, stream); if (at_default_paths[def_path_no + 1]) return 1; /* next default path * exits, so looping * is required */ /* return 0 */ } else { /* at_default_path == NULL && * fullphrase == phrase */ fprintf(stderr, " **\tusing builtin default " "path \"/usr/sbin/\"\n"); fputs("/usr/sbin/", stream); fputs(phrase, stream); /* no default path(s) so no looping */ } /* exit(1) ??? */ return 0; } } #endif }
int main(int argc, char ** argv) { FILE *containerimage_fp; FILE *loop_fp; FILE *config_fp; char *containerimage; char *containername; char *containerpath; char *username; char *command; char *tmpdir; char *loop_dev_lock; char *loop_dev_cache; char *loop_dev = 0; char *config_path; char *tmp_config_string; char cwd[PATH_MAX]; int cwd_fd; int tmpdirlock_fd; int containerimage_fd; int loop_dev_lock_fd; int gid_list_count; int retval = 0; uid_t uid; gid_t gid; gid_t *gid_list; pid_t namespace_fork_pid = 0; struct passwd *pw; //****************************************************************************// // Init //****************************************************************************// signal(SIGINT, sighandler); signal(SIGKILL, sighandler); signal(SIGQUIT, sighandler); openlog("Singularity", LOG_CONS | LOG_NDELAY, LOG_LOCAL0); // Get all user/group info uid = getuid(); gid = getgid(); gid_list_count = getgroups(0, NULL); gid_list = (gid_t *) malloc(sizeof(gid_t) * gid_list_count); if ( getgroups(gid_list_count, gid_list) < 0 ) { fprintf(stderr, "ABORT: Could not obtain current supplementary group list: %s\n", strerror(errno)); return(255); } pw = getpwuid(uid); // Check to make sure we are installed correctly if ( seteuid(0) < 0 ) { fprintf(stderr, "ABORT: Check installation, must be performed by root.\n"); return(255); } // Lets start off as the calling UID if ( seteuid(uid) < 0 ) { fprintf(stderr, "ABORT: Could not set effective uid to %d: %s\n", uid, strerror(errno)); return(255); } if ( setegid(gid) < 0 ) { fprintf(stderr, "ABORT: Could not set effective gid to %d: %s\n", gid, strerror(errno)); return(255); } username = pw->pw_name; containerimage = getenv("SINGULARITY_IMAGE"); command = getenv("SINGULARITY_COMMAND"); unsetenv("SINGULARITY_COMMAND"); unsetenv("SINGULARITY_EXEC"); config_path = (char *) malloc(strlen(SYSCONFDIR) + 30); snprintf(config_path, strlen(SYSCONFDIR) + 30, "%s/singularity/singularity.conf", SYSCONFDIR); // Figure out where we start if ( (cwd_fd = open(".", O_RDONLY)) < 0 ) { fprintf(stderr, "ABORT: Could not open cwd fd (%s)!\n", strerror(errno)); return(1); } if ( getcwd(cwd, PATH_MAX) == NULL ) { fprintf(stderr, "Could not obtain current directory path: %s\n", strerror(errno)); return(1); } if ( containerimage == NULL ) { fprintf(stderr, "ABORT: SINGULARITY_IMAGE undefined!\n"); return(1); } if ( is_file(containerimage) != 0 ) { fprintf(stderr, "ABORT: Container image path is invalid: %s\n", containerimage); return(1); } if ( is_file(config_path) != 0 ) { fprintf(stderr, "ABORT: Configuration file not found: %s\n", config_path); return(255); } if ( is_owner(config_path, 0) != 0 ) { fprintf(stderr, "ABORT: Configuration file is not owned by root: %s\n", config_path); return(255); } // TODO: Offer option to only run containers owned by root (so root can approve // containers) if ( uid == 0 && is_owner(containerimage, 0) < 0 ) { fprintf(stderr, "ABORT: Root should only run containers that root owns!\n"); return(1); } containername = basename(strdup(containerimage)); tmpdir = strjoin("/tmp/.singularity-", file_id(containerimage)); loop_dev_lock = joinpath(tmpdir, "loop_dev.lock"); loop_dev_cache = joinpath(tmpdir, "loop_dev"); containerpath = (char *) malloc(strlen(tmpdir) + 5); snprintf(containerpath, strlen(tmpdir) + 5, "%s/mnt", tmpdir); syslog(LOG_NOTICE, "User=%s[%d], Command=%s, Container=%s, CWD=%s, Arg1=%s", username, uid, command, containerimage, cwd, argv[1]); //****************************************************************************// // Setup //****************************************************************************// if ( ( config_fp = fopen(config_path, "r") ) == NULL ) { fprintf(stderr, "ERROR: Could not open config file %s: %s\n", config_path, strerror(errno)); return(255); } if ( getenv("SINGULARITY_WRITABLE") == NULL ) { if ( ( containerimage_fp = fopen(containerimage, "r") ) == NULL ) { fprintf(stderr, "ERROR: Could not open image read only %s: %s\n", containerimage, strerror(errno)); return(255); } containerimage_fd = fileno(containerimage_fp); if ( flock(containerimage_fd, LOCK_SH | LOCK_NB) < 0 ) { fprintf(stderr, "ABORT: Image is locked by another process\n"); return(5); } } else { if ( ( containerimage_fp = fopen(containerimage, "r+") ) == NULL ) { fprintf(stderr, "ERROR: Could not open image read/write %s: %s\n", containerimage, strerror(errno)); return(255); } containerimage_fd = fileno(containerimage_fp); if ( flock(containerimage_fd, LOCK_EX | LOCK_NB) < 0 ) { fprintf(stderr, "ABORT: Image is locked by another process\n"); return(5); } } //****************************************************************************// // We are now running with escalated privileges until we exec //****************************************************************************// if ( seteuid(0) < 0 ) { fprintf(stderr, "ABORT: Could not escalate effective user privileges %s\n", strerror(errno)); return(255); } if ( setegid(0) < 0 ) { fprintf(stderr, "ABORT: Could not escalate effective group privileges: %s\n", strerror(errno)); return(255); } if ( s_mkpath(tmpdir, 0755) < 0 ) { fprintf(stderr, "ABORT: Could not create temporary directory %s: %s\n", tmpdir, strerror(errno)); return(255); } if ( is_owner(tmpdir, 0) < 0 ) { fprintf(stderr, "ABORT: Container working directory has wrong ownership: %s\n", tmpdir); syslog(LOG_ERR, "Container working directory has wrong ownership: %s", tmpdir); return(255); } tmpdirlock_fd = open(tmpdir, O_RDONLY); if ( tmpdirlock_fd < 0 ) { fprintf(stderr, "ERROR: Could not obtain file descriptor on %s: %s\n", tmpdir, strerror(errno)); return(255); } if ( flock(tmpdirlock_fd, LOCK_SH | LOCK_NB) < 0 ) { fprintf(stderr, "ERROR: Could not obtain shared lock on %s: %s\n", tmpdir, strerror(errno)); return(255); } if ( ( loop_dev_lock_fd = open(loop_dev_lock, O_CREAT | O_RDWR, 0644) ) < 0 ) { fprintf(stderr, "ERROR: Could not open loop_dev_lock %s: %s\n", loop_dev_lock, strerror(errno)); return(255); } if ( s_mkpath(containerpath, 0755) < 0 ) { fprintf(stderr, "ABORT: Could not create directory %s: %s\n", containerpath, strerror(errno)); return(255); } if ( is_owner(containerpath, 0) < 0 ) { fprintf(stderr, "ABORT: Container directory is not root owned: %s\n", containerpath); syslog(LOG_ERR, "Container directory has wrong ownership: %s", tmpdir); return(255); } if ( flock(loop_dev_lock_fd, LOCK_EX | LOCK_NB) == 0 ) { loop_dev = obtain_loop_dev(); if ( ( loop_fp = fopen(loop_dev, "r+") ) < 0 ) { fprintf(stderr, "ERROR: Failed to open loop device %s: %s\n", loop_dev, strerror(errno)); syslog(LOG_ERR, "Failed to open loop device %s: %s", loop_dev, strerror(errno)); return(255); } if ( associate_loop(containerimage_fp, loop_fp, 1) < 0 ) { fprintf(stderr, "ERROR: Could not associate %s to loop device %s\n", containerimage, loop_dev); syslog(LOG_ERR, "Failed to associate %s to loop device %s", containerimage, loop_dev); return(255); } if ( fileput(loop_dev_cache, loop_dev) < 0 ) { fprintf(stderr, "ERROR: Could not write to loop_dev_cache %s: %s\n", loop_dev_cache, strerror(errno)); return(255); } flock(loop_dev_lock_fd, LOCK_SH | LOCK_NB); } else { flock(loop_dev_lock_fd, LOCK_SH); if ( ( loop_dev = filecat(loop_dev_cache) ) == NULL ) { fprintf(stderr, "ERROR: Could not retrieve loop_dev_cache from %s\n", loop_dev_cache); return(255); } if ( ( loop_fp = fopen(loop_dev, "r") ) < 0 ) { fprintf(stderr, "ERROR: Failed to open loop device %s: %s\n", loop_dev, strerror(errno)); return(255); } } //****************************************************************************// // Management fork //****************************************************************************// namespace_fork_pid = fork(); if ( namespace_fork_pid == 0 ) { //****************************************************************************// // Setup namespaces //****************************************************************************// if ( unshare(CLONE_NEWNS) < 0 ) { fprintf(stderr, "ABORT: Could not virtualize mount namespace: %s\n", strerror(errno)); return(255); } // Privatize the mount namespaces (thank you for the pointer Doug Jacobsen!) if ( mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) < 0 ) { // I am not sure if this error needs to be caught, maybe it will fail // on older kernels? If so, we can fix then. fprintf(stderr, "ABORT: Could not make mountspaces private: %s\n", strerror(errno)); return(255); } #ifdef NS_CLONE_NEWPID if ( getenv("SINGULARITY_NO_NAMESPACE_PID") == NULL ) { unsetenv("SINGULARITY_NO_NAMESPACE_PID"); if ( unshare(CLONE_NEWPID) < 0 ) { fprintf(stderr, "ABORT: Could not virtualize PID namespace: %s\n", strerror(errno)); return(255); } } #else #ifdef NS_CLONE_PID // This is for older legacy CLONE_PID if ( getenv("SINGULARITY_NO_NAMESPACE_PID") == NULL ) { unsetenv("SINGULARITY_NO_NAMESPACE_PID"); if ( unshare(CLONE_PID) < 0 ) { fprintf(stderr, "ABORT: Could not virtualize PID namespace: %s\n", strerror(errno)); return(255); } } #endif #endif #ifdef NS_CLONE_FS if ( getenv("SINGULARITY_NO_NAMESPACE_FS") == NULL ) { unsetenv("SINGULARITY_NO_NAMESPACE_FS"); if ( unshare(CLONE_FS) < 0 ) { fprintf(stderr, "ABORT: Could not virtualize file system namespace: %s\n", strerror(errno)); return(255); } } #endif #ifdef NS_CLONE_FILES if ( getenv("SINGULARITY_NO_NAMESPACE_FILES") == NULL ) { unsetenv("SINGULARITY_NO_NAMESPACE_FILES"); if ( unshare(CLONE_FILES) < 0 ) { fprintf(stderr, "ABORT: Could not virtualize file descriptor namespace: %s\n", strerror(errno)); return(255); } } #endif //****************************************************************************// // Mount image //****************************************************************************// if ( getenv("SINGULARITY_WRITABLE") == NULL ) { unsetenv("SINGULARITY_WRITABLE"); if ( mount_image(loop_dev, containerpath, 0) < 0 ) { fprintf(stderr, "ABORT: exiting...\n"); return(255); } } else { if ( mount_image(loop_dev, containerpath, 1) < 0 ) { fprintf(stderr, "ABORT: exiting...\n"); return(255); } } //****************************************************************************// // Check image //****************************************************************************// if ( is_exec(joinpath(containerpath, "/bin/sh")) < 0 ) { fprintf(stderr, "ERROR: Container image does not have a valid /bin/sh\n"); return(1); } //****************************************************************************// // Bind mounts //****************************************************************************// if ( getenv("SINGULARITY_CONTAIN") == NULL ) { unsetenv("SINGULARITY_CONTAIN"); rewind(config_fp); while ( ( tmp_config_string = config_get_key_value(config_fp, "bind path") ) != NULL ) { if ( ( is_file(tmp_config_string) != 0 ) && ( is_dir(tmp_config_string) != 0 ) ) { fprintf(stderr, "ERROR: Non existant bind source path: '%s'\n", tmp_config_string); continue; } if ( ( is_file(joinpath(containerpath, tmp_config_string)) != 0 ) && ( is_dir(joinpath(containerpath, tmp_config_string)) != 0 ) ) { fprintf(stderr, "WARNING: Non existant bind container destination path: '%s'\n", tmp_config_string); continue; } if ( mount_bind(tmp_config_string, joinpath(containerpath, tmp_config_string), 0) < 0 ) { fprintf(stderr, "ABORTING!\n"); return(255); } } if (is_file(joinpath(containerpath, "/etc/nsswitch.conf")) == 0 ) { if ( is_file(joinpath(SYSCONFDIR, "/singularity/default-nsswitch.conf")) == 0 ) { if ( mount_bind(joinpath(SYSCONFDIR, "/singularity/default-nsswitch.conf"), joinpath(containerpath, "/etc/nsswitch.conf"), 0) != 0 ) { fprintf(stderr, "ABORT: Could not bind /etc/nsswitch.conf\n"); return(255); } } else { fprintf(stderr, "WARNING: Template /etc/nsswitch.conf does not exist: %s\n", joinpath(SYSCONFDIR, "/singularity/default-nsswitch.conf")); } } if ( uid != 0 ) { // If we are root, no need to mess with passwd or group if (is_file(joinpath(containerpath, "/etc/passwd")) == 0 ) { if ( is_file(joinpath(tmpdir, "/passwd")) < 0 ) { if ( build_passwd(joinpath(containerpath, "/etc/passwd"), joinpath(tmpdir, "/passwd")) < 0 ) { fprintf(stderr, "ABORT: Failed creating template password file\n"); return(255); } } if ( mount_bind(joinpath(tmpdir, "/passwd"), joinpath(containerpath, "/etc/passwd"), 0) < 0 ) { fprintf(stderr, "ABORT: Could not bind /etc/passwd\n"); return(255); } } if (is_file(joinpath(containerpath, "/etc/group")) == 0 ) { if ( is_file(joinpath(tmpdir, "/group")) < 0 ) { if ( build_group(joinpath(containerpath, "/etc/group"), joinpath(tmpdir, "/group")) < 0 ) { fprintf(stderr, "ABORT: Failed creating template group file\n"); return(255); } } if ( mount_bind(joinpath(tmpdir, "/group"), joinpath(containerpath, "/etc/group"), 0) < 0 ) { fprintf(stderr, "ABORT: Could not bind /etc/group\n"); return(255); } } } } //****************************************************************************// // Fork child in new namespaces //****************************************************************************// exec_fork_pid = fork(); if ( exec_fork_pid == 0 ) { //****************************************************************************// // Enter the file system //****************************************************************************// if ( chroot(containerpath) < 0 ) { fprintf(stderr, "ABORT: failed enter CONTAINERIMAGE: %s\n", containerpath); return(255); } if ( chdir("/") < 0 ) { fprintf(stderr, "ABORT: Could not chdir after chroot to /: %s\n", strerror(errno)); return(1); } //****************************************************************************// // Setup real mounts within the container //****************************************************************************// rewind(config_fp); if ( config_get_key_bool(config_fp, "mount proc", 1) > 0 ) { if ( is_dir("/proc") == 0 ) { if ( mount("proc", "/proc", "proc", 0, NULL) < 0 ) { fprintf(stderr, "ABORT: Could not mount /proc: %s\n", strerror(errno)); return(255); } } } rewind(config_fp); if ( config_get_key_bool(config_fp, "mount sys", 1) > 0 ) { if ( is_dir("/sys") == 0 ) { if ( mount("sysfs", "/sys", "sysfs", 0, NULL) < 0 ) { fprintf(stderr, "ABORT: Could not mount /sys: %s\n", strerror(errno)); return(255); } } } //****************************************************************************// // Drop all privileges for good //****************************************************************************// if ( setgroups(gid_list_count, gid_list) < 0 ) { fprintf(stderr, "ABOFT: Could not reset supplementary group list: %s\n", strerror(errno)); return(255); } if ( setregid(gid, gid) < 0 ) { fprintf(stderr, "ABORT: Could not dump real and effective group privileges: %s\n", strerror(errno)); return(255); } if ( setreuid(uid, uid) < 0 ) { fprintf(stderr, "ABORT: Could not dump real and effective user privileges: %s\n", strerror(errno)); return(255); } //****************************************************************************// // Setup final environment //****************************************************************************// // After this, we exist only within the container... Let's make it known! if ( setenv("SINGULARITY_CONTAINER", "true", 0) != 0 ) { fprintf(stderr, "ABORT: Could not set SINGULARITY_CONTAINER to 'true'\n"); return(1); } if ( is_dir(cwd) == 0 ) { if ( chdir(cwd) < 0 ) { fprintf(stderr, "ABORT: Could not chdir to: %s: %s\n", cwd, strerror(errno)); return(1); } } else { if ( fchdir(cwd_fd) < 0 ) { fprintf(stderr, "ABORT: Could not fchdir to cwd: %s\n", strerror(errno)); return(1); } } //****************************************************************************// // Execv to container process //****************************************************************************// if ( command == NULL ) { fprintf(stderr, "No command specified, launching 'shell'\n"); command = strdup("shell"); } if ( strcmp(command, "run") == 0 ) { if ( is_exec("/singularity") == 0 ) { argv[0] = strdup("/singularity"); if ( execv("/singularity", argv) != 0 ) { fprintf(stderr, "ABORT: exec of /bin/sh failed: %s\n", strerror(errno)); } } else { fprintf(stderr, "No Singularity runscript found, launching 'shell'\n"); command = strdup("shell"); } } if ( strcmp(command, "exec") == 0 ) { if ( argc <= 1 ) { fprintf(stderr, "ABORT: Exec requires a command to run\n"); return(1); } if ( execvp(argv[1], &argv[1]) != 0 ) { fprintf(stderr, "ABORT: execvp of '%s' failed: %s\n", argv[1], strerror(errno)); return(1); } } if ( strcmp(command, "shell") == 0 ) { char *prompt; prompt = (char *) malloc(strlen(containername) + 16); snprintf(prompt, strlen(containerimage) + 16, "Singularity/%s> ", containername); setenv("PS1", prompt, 1); if ( is_exec("/bin/bash") == 0 ) { char *args[argc+2]; int i; args[0] = strdup("/bin/bash"); args[1] = strdup("--norc"); args[2] = strdup("--noprofile"); for(i=1; i<=argc; i++) { args[i+2] = argv[i]; } if ( execv("/bin/bash", args) != 0 ) { fprintf(stderr, "ABORT: exec of /bin/bash failed: %s\n", strerror(errno)); } } else { argv[0] = strdup("/bin/sh"); if ( execv("/bin/sh", argv) != 0 ) { fprintf(stderr, "ABORT: exec of /bin/sh failed: %s\n", strerror(errno)); } } } // If we get here... we fail on bad command fprintf(stderr, "ABORT: Unrecognized Singularity command: %s\n", command); return(1); //****************************************************************************// // Outer child waits for inner child //****************************************************************************// } else if ( exec_fork_pid > 0 ) { int tmpstatus; strncpy(argv[0], "Singularity: exec", strlen(argv[0])); if ( seteuid(uid) < 0 ) { fprintf(stderr, "ABORT: Could not set effective user privileges to %d: %s\n", uid, strerror(errno)); return(255); } waitpid(exec_fork_pid, &tmpstatus, 0); retval = WEXITSTATUS(tmpstatus); } else { fprintf(stderr, "ABORT: Could not fork namespace process: %s\n", strerror(errno)); return(255); } return(retval); } else if ( namespace_fork_pid > 0 ) { int tmpstatus; strncpy(argv[0], "Singularity: namespace", strlen(argv[0])); if ( seteuid(uid) < 0 ) { fprintf(stderr, "ABORT: Could not set effective user privileges to %d: %s\n", uid, strerror(errno)); return(255); } waitpid(namespace_fork_pid, &tmpstatus, 0); retval = WEXITSTATUS(tmpstatus); } else { fprintf(stderr, "ABORT: Could not fork management process: %s\n", strerror(errno)); return(255); } //****************************************************************************// // Final wrap up before exiting //****************************************************************************// if ( close(cwd_fd) < 0 ) { fprintf(stderr, "ERROR: Could not close cwd_fd: %s\n", strerror(errno)); retval++; } if ( flock(tmpdirlock_fd, LOCK_EX | LOCK_NB) == 0 ) { close(tmpdirlock_fd); if ( seteuid(0) < 0 ) { fprintf(stderr, "ABORT: Could not re-escalate effective user privileges: %s\n", strerror(errno)); return(255); } if ( s_rmdir(tmpdir) < 0 ) { fprintf(stderr, "WARNING: Could not remove all files in %s: %s\n", tmpdir, strerror(errno)); } // Dissociate loops from here Just in case autoflush didn't work. (void)disassociate_loop(loop_fp); if ( seteuid(uid) < 0 ) { fprintf(stderr, "ABORT: Could not drop effective user privileges: %s\n", strerror(errno)); return(255); } } else { // printf("Not removing tmpdir, lock still\n"); } close(containerimage_fd); close(tmpdirlock_fd); free(loop_dev_lock); free(containerpath); free(tmpdir); closelog(); return(retval); }