static NORETURN void * spawn_thread_func (void *param) { UNUSED (param); while (true) { do_sem_op (SEM_LAUNCH_START, -1); do_spawn (); } }
static ret_t sem_adquire (int sem_ref, int sem_num) { return do_sem_op (sem_ref, sem_num, -1); }
static void do_spawn (void) { int n; int size; uid_t uid; gid_t gid; int env_inherit; pid_t child; int envs = 0; int log_stderr = 0; char *interpreter = NULL; char *log_file = NULL; char *uid_str = NULL; char *chroot_dir = NULL; char **envp = NULL; char *p = spawn_shared; const char *argv[] = {"sh", "-c", NULL, NULL}; #define CHECK_MARK(val) \ if ((*(int *)p) != val) { \ goto cleanup; \ } else { \ p += sizeof(int); \ } #define ALIGN4(buf) while ((long)p & 0x3) p++; /* Read the shared memory */ /* 1.- Interpreter */ CHECK_MARK (0xF0); size = *((int *)p); p += sizeof(int); if (size <= 0) { goto cleanup; } interpreter = malloc (sizeof("exec ") + size); if (interpreter == NULL) { goto cleanup; } strncpy (interpreter, "exec ", 5); strncpy (interpreter + 5, p, size + 1); p += size + 1; ALIGN4 (p); /* 2.- UID & GID */ CHECK_MARK (0xF1); size = *((int *)p); if (size > 0) { uid_str = strdup (p + sizeof(int)); } p += sizeof(int) + size + 1; ALIGN4 (p); memcpy (&uid, p, sizeof(uid_t)); p += sizeof(uid_t); memcpy (&gid, p, sizeof(gid_t)); p += sizeof(gid_t); /* 3.- Chroot directory */ CHECK_MARK (0xF2); size = *((int *) p); p += sizeof(int); if (size > 0) { chroot_dir = malloc(size + 1); memcpy(chroot_dir, p, size + 1); } p += size + 1; ALIGN4 (p); /* 4.- Environment */ CHECK_MARK (0xF3); env_inherit = *((int *)p); p += sizeof(int); envs = *((int *)p); p += sizeof(int); envp = malloc (sizeof(char *) * (envs + 1)); if (envp == NULL) { goto cleanup; } envp[envs] = NULL; for (n=0; n<envs; n++) { char *e; size = *((int *)p); p += sizeof(int); e = malloc (size + 1); if (e == NULL) { goto cleanup; } memcpy (e, p, size); e[size] = '\0'; envp[n] = e; p += size + 1; ALIGN4 (p); } /* 5.- Error log */ CHECK_MARK (0xF4); size = *((int *)p); p += sizeof(int); if (size > 0) { if (! strncmp (p, "stderr", 6)) { log_stderr = 1; } else if (! strncmp (p, "file,", 5)) { log_file = p+5; } p += (size + 1); ALIGN4 (p); } /* 6.- PID: it's -1 now */ CHECK_MARK (0xF5); n = *((int *)p); if (n > 0) { kill (n, SIGTERM); *p = -1; } /* Spawn */ child = fork(); switch (child) { case 0: { int i; struct sigaction sig_action; /* Reset signal handlers */ sig_action.sa_handler = SIG_DFL; sig_action.sa_flags = 0; sigemptyset (&sig_action.sa_mask); for (i=0 ; i < NSIG ; i++) { sigaction (i, &sig_action, NULL); } /* Logging */ if (log_file) { int fd; fd = open (log_file, O_WRONLY | O_APPEND | O_CREAT, 0600); if (fd < 0) { PRINT_ERROR ("(warning) Couldn't open '%s' for writing..\n", log_file); } close (STDOUT_FILENO); close (STDERR_FILENO); dup2 (fd, STDOUT_FILENO); dup2 (fd, STDERR_FILENO); } else if (log_stderr) { /* do nothing */ } else { int tmp_fd; tmp_fd = open ("/dev/null", O_WRONLY); close (STDOUT_FILENO); close (STDERR_FILENO); dup2 (tmp_fd, STDOUT_FILENO); dup2 (tmp_fd, STDERR_FILENO); } /* Change root */ if (chroot_dir) { int re = chroot(chroot_dir); if (re < 0) { PRINT_ERROR ("(critial) Couldn't chroot to %s\n", chroot_dir); exit (1); } } /* Change user & group */ if (uid_str != NULL) { n = initgroups (uid_str, gid); if (n == -1) { PRINT_ERROR ("(warning) initgroups failed User=%s, GID=%d\n", uid_str, gid); } } if ((int)gid != -1) { n = setgid (gid); if (n != 0) { PRINT_ERROR ("(warning) Couldn't set GID=%d\n", gid); } } if ((int)uid != -1) { n = setuid (uid); if (n != 0) { PRINT_ERROR ("(warning) Couldn't set UID=%d\n", uid); } } /* Clean the shared memory */ size = (p - spawn_shared) - sizeof(int); memset (spawn_shared, 0, size); /* Execute the interpreter */ argv[2] = interpreter; if (env_inherit) { do { execv ("/bin/sh", (char **)argv); } while (errno == EINTR); } else { do { execve ("/bin/sh", (char **)argv, envp); } while (errno == EINTR); } PRINT_MSG ("(critical) Couldn't spawn: sh -c %s\n", interpreter); exit (1); } case -1: /* Error */ PRINT_MSG ("(critical) Couldn't fork(): %s\n", strerror(errno)); goto cleanup; default: break; } /* Return the PID */ memcpy (p, (char *)&child, sizeof(int)); printf ("PID %d: launched '/bin/sh -c %s' with uid=%d, gid=%d, chroot=%s, env=%s\n", child, interpreter, uid, gid, chroot_dir, env_inherit ? "inherited":"custom"); cleanup: /* Unlock worker */ do_sem_op (SEM_LAUNCH_READY, 1); /* Clean up */ free (uid_str); free (interpreter); free (chroot_dir); if (envp != NULL) { for (n=0; n<envs; n++) { free (envp[n]); } free (envp); } }
static ret_t sem_unlock (int sem_ref, int sem_num) { return do_sem_op (sem_ref, sem_num, 1); }