diff options
Diffstat (limited to 'src/core/execute.c')
-rw-r--r-- | src/core/execute.c | 261 |
1 files changed, 140 insertions, 121 deletions
diff --git a/src/core/execute.c b/src/core/execute.c index 21721dc240..137a176c18 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -31,6 +31,7 @@ #include <grp.h> #include <poll.h> #include <glob.h> +#include <utmpx.h> #include <sys/personality.h> #ifdef HAVE_PAM @@ -49,6 +50,7 @@ #include <sys/apparmor.h> #endif +#include "barrier.h" #include "sd-messages.h" #include "rm-rf.h" #include "strv.h" @@ -121,7 +123,8 @@ static int shift_fds(int fds[], unsigned n_fds) { if (fds[i] == i+3) continue; - if ((nfd = fcntl(fds[i], F_DUPFD, i+3)) < 0) + nfd = fcntl(fds[i], F_DUPFD, i + 3); + if (nfd < 0) return -errno; safe_close(fds[i]); @@ -155,14 +158,16 @@ static int flags_fds(const int fds[], unsigned n_fds, bool nonblock) { for (i = 0; i < n_fds; i++) { - if ((r = fd_nonblock(fds[i], nonblock)) < 0) + r = fd_nonblock(fds[i], nonblock); + if (r < 0) return r; /* We unconditionally drop FD_CLOEXEC from the fds, * since after all we want to pass these fds to our * children */ - if ((r = fd_cloexec(fds[i], false)) < 0) + r = fd_cloexec(fds[i], false); + if (r < 0) return r; } @@ -314,7 +319,8 @@ static int open_terminal_as(const char *path, mode_t mode, int nfd) { assert(path); assert(nfd >= 0); - if ((fd = open_terminal(path, mode | O_NOCTTY)) < 0) + fd = open_terminal(path, mode | O_NOCTTY); + if (fd < 0) return fd; if (fd != nfd) { @@ -624,14 +630,6 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_ * we avoid NSS lookups for gid=0. */ if (context->group || username) { - - if (context->group) { - const char *g = context->group; - - if ((r = get_group_creds(&g, &gid)) < 0) - return r; - } - /* First step, initialize groups from /etc/groups */ if (username && gid != 0) { if (initgroups(username, gid) < 0) @@ -657,7 +655,8 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_ return -ENOMEM; if (keep_groups) { - if ((k = getgroups(ngroups_max, gids)) < 0) { + k = getgroups(ngroups_max, gids); + if (k < 0) { free(gids); return -errno; } @@ -770,10 +769,11 @@ static int setup_pam( .appdata_ptr = NULL }; + _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL; pam_handle_t *handle = NULL; sigset_t old_ss; int pam_code = PAM_SUCCESS; - int err; + int err = 0; char **e = NULL; bool close_session = false; pid_t pam_pid = 0, parent_pid; @@ -790,6 +790,10 @@ static int setup_pam( * daemon. We do things this way to ensure that the main PID * of the daemon is the one we initially fork()ed. */ + err = barrier_create(&barrier); + if (err < 0) + goto fail; + if (log_get_max_level() < LOG_DEBUG) flags |= PAM_SILENT; @@ -838,6 +842,7 @@ static int setup_pam( /* The child's job is to reset the PAM session on * termination */ + barrier_set_role(&barrier, BARRIER_CHILD); /* This string must fit in 10 chars (i.e. the length * of "/sbin/init"), to look pretty in /bin/ps */ @@ -865,6 +870,11 @@ static int setup_pam( if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) goto child_finish; + /* Tell the parent that our setup is done. This is especially + * important regarding dropping privileges. Otherwise, unit + * setup might race against our setresuid(2) call. */ + barrier_place(&barrier); + /* Check if our parent process might already have * died? */ if (getppid() == parent_pid) { @@ -900,6 +910,8 @@ static int setup_pam( _exit(r); } + barrier_set_role(&barrier, BARRIER_PARENT); + /* If the child was forked off successfully it will do all the * cleanups, so forget about the handle here. */ handle = NULL; @@ -911,6 +923,11 @@ static int setup_pam( * might have opened it, but we don't want this fd around. */ closelog(); + /* Synchronously wait for the child to initialize. We don't care for + * errors as we cannot recover. However, warn loudly if it happens. */ + if (!barrier_place_and_sync(&barrier)) + log_error("PAM initialization failed"); + *pam_env = e; e = NULL; @@ -921,8 +938,7 @@ fail: log_error("PAM failed: %s", pam_strerror(handle, pam_code)); err = -EPERM; /* PAM errors do not map to errno */ } else { - log_error_errno(errno, "PAM failed: %m"); - err = -errno; + err = log_error_errno(err < 0 ? err : errno, "PAM failed: %m"); } if (handle) { @@ -1154,8 +1170,8 @@ static void do_idle_pipe_dance(int idle_pipe[4]) { assert(idle_pipe); - safe_close(idle_pipe[1]); - safe_close(idle_pipe[2]); + idle_pipe[1] = safe_close(idle_pipe[1]); + idle_pipe[2] = safe_close(idle_pipe[2]); if (idle_pipe[0] >= 0) { int r; @@ -1163,18 +1179,20 @@ static void do_idle_pipe_dance(int idle_pipe[4]) { r = fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC); if (idle_pipe[3] >= 0 && r == 0 /* timeout */) { + ssize_t n; + /* Signal systemd that we are bored and want to continue. */ - r = write(idle_pipe[3], "x", 1); - if (r > 0) + n = write(idle_pipe[3], "x", 1); + if (n > 0) /* Wait for systemd to react to the signal above. */ fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC); } - safe_close(idle_pipe[0]); + idle_pipe[0] = safe_close(idle_pipe[0]); } - safe_close(idle_pipe[3]); + idle_pipe[3] = safe_close(idle_pipe[3]); } static int build_environment( @@ -1307,7 +1325,7 @@ static int exec_child( _cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL; _cleanup_free_ char *mac_selinux_context_net = NULL; - const char *username = NULL, *home = NULL, *shell = NULL; + const char *username = NULL, *home = NULL, *shell = NULL, *wd; unsigned n_dont_close = 0; int dont_close[n_fds + 4]; uid_t uid = UID_INVALID; @@ -1406,6 +1424,17 @@ static int exec_child( } } + if (context->group) { + const char *g = context->group; + + r = get_group_creds(&g, &gid); + if (r < 0) { + *exit_status = EXIT_GROUP; + return r; + } + } + + /* If a socket is connected to STDIN/STDOUT/STDERR, we * must sure to drop O_NONBLOCK */ if (socket_fd >= 0) @@ -1504,7 +1533,11 @@ static int exec_child( } if (context->utmp_id) - utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path); + utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path, + context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS : + context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS : + USER_PROCESS, + username ? "root" : context->user); if (context->user && is_terminal_input(context->std_input)) { r = chown_terminal(STDIN_FILENO, uid); @@ -1554,7 +1587,13 @@ static int exec_child( return -ENOMEM; } - r = mkdir_safe_label(p, context->runtime_directory_mode, uid, gid); + r = mkdir_p_label(p, context->runtime_directory_mode); + if (r < 0) { + *exit_status = EXIT_RUNTIME_DIRECTORY; + return r; + } + + r = chmod_and_chown(p, context->runtime_directory_mode, uid, gid); if (r < 0) { *exit_status = EXIT_RUNTIME_DIRECTORY; return r; @@ -1562,25 +1601,50 @@ static int exec_child( } } + umask(context->umask); + if (params->apply_permissions) { r = enforce_groups(context, username, gid); if (r < 0) { *exit_status = EXIT_GROUP; return r; } - } +#ifdef HAVE_SMACK + if (context->smack_process_label) { + r = mac_smack_apply_pid(0, context->smack_process_label); + if (r < 0) { + *exit_status = EXIT_SMACK_PROCESS_LABEL; + return r; + } + } +#ifdef SMACK_DEFAULT_PROCESS_LABEL + else { + _cleanup_free_ char *exec_label = NULL; - umask(context->umask); + r = mac_smack_read(command->path, SMACK_ATTR_EXEC, &exec_label); + if (r < 0 && r != -ENODATA && r != -EOPNOTSUPP) { + *exit_status = EXIT_SMACK_PROCESS_LABEL; + return r; + } + r = mac_smack_apply_pid(0, exec_label ? : SMACK_DEFAULT_PROCESS_LABEL); + if (r < 0) { + *exit_status = EXIT_SMACK_PROCESS_LABEL; + return r; + } + } +#endif +#endif #ifdef HAVE_PAM - if (params->apply_permissions && context->pam_name && username) { - r = setup_pam(context->pam_name, username, uid, context->tty_path, &pam_env, fds, n_fds); - if (r < 0) { - *exit_status = EXIT_PAM; - return r; + if (context->pam_name && username) { + r = setup_pam(context->pam_name, username, uid, context->tty_path, &pam_env, fds, n_fds); + if (r < 0) { + *exit_status = EXIT_PAM; + return r; + } } - } #endif + } if (context->private_network && runtime && runtime->netns_storage_socket[0] >= 0) { r = setup_netns(runtime->netns_storage_socket); @@ -1634,6 +1698,13 @@ static int exec_child( } } + if (context->working_directory_home) + wd = home; + else if (context->working_directory) + wd = context->working_directory; + else + wd = "/"; + if (params->apply_chroot) { if (!needs_mount_namespace && context->root_directory) if (chroot(context->root_directory) < 0) { @@ -1641,21 +1712,15 @@ static int exec_child( return -errno; } - if (chdir(context->working_directory ?: "/") < 0 && + if (chdir(wd) < 0 && !context->working_directory_missing_ok) { *exit_status = EXIT_CHDIR; return -errno; } } else { - _cleanup_free_ char *d = NULL; - - if (asprintf(&d, "%s/%s", - context->root_directory ?: "", - context->working_directory ?: "") < 0) { - *exit_status = EXIT_MEMORY; - return -ENOMEM; - } + const char *d; + d = strjoina(strempty(context->root_directory), "/", strempty(wd)); if (chdir(d) < 0 && !context->working_directory_missing_ok) { *exit_status = EXIT_CHDIR; @@ -1709,25 +1774,6 @@ static int exec_child( } } -#ifdef HAVE_SMACK - if (context->smack_process_label) { - r = mac_smack_apply_pid(0, context->smack_process_label); - if (r < 0) { - *exit_status = EXIT_SMACK_PROCESS_LABEL; - return r; - } - } -#ifdef SMACK_DEFAULT_PROCESS_LABEL - else { - r = mac_smack_apply_pid(0, SMACK_DEFAULT_PROCESS_LABEL); - if (r < 0) { - *exit_status = EXIT_SMACK_PROCESS_LABEL; - return r; - } - } -#endif -#endif - if (context->user) { r = enforce_user(context, uid); if (r < 0) { @@ -1968,77 +2014,44 @@ void exec_context_done(ExecContext *c) { assert(c); - strv_free(c->environment); - c->environment = NULL; - - strv_free(c->environment_files); - c->environment_files = NULL; - - for (l = 0; l < ELEMENTSOF(c->rlimit); l++) { - free(c->rlimit[l]); - c->rlimit[l] = NULL; - } - - free(c->working_directory); - c->working_directory = NULL; - free(c->root_directory); - c->root_directory = NULL; - - free(c->tty_path); - c->tty_path = NULL; + c->environment = strv_free(c->environment); + c->environment_files = strv_free(c->environment_files); - free(c->syslog_identifier); - c->syslog_identifier = NULL; + for (l = 0; l < ELEMENTSOF(c->rlimit); l++) + c->rlimit[l] = mfree(c->rlimit[l]); - free(c->user); - c->user = NULL; + c->working_directory = mfree(c->working_directory); + c->root_directory = mfree(c->root_directory); + c->tty_path = mfree(c->tty_path); + c->syslog_identifier = mfree(c->syslog_identifier); + c->user = mfree(c->user); + c->group = mfree(c->group); - free(c->group); - c->group = NULL; + c->supplementary_groups = strv_free(c->supplementary_groups); - strv_free(c->supplementary_groups); - c->supplementary_groups = NULL; - - free(c->pam_name); - c->pam_name = NULL; + c->pam_name = mfree(c->pam_name); if (c->capabilities) { cap_free(c->capabilities); c->capabilities = NULL; } - strv_free(c->read_only_dirs); - c->read_only_dirs = NULL; - - strv_free(c->read_write_dirs); - c->read_write_dirs = NULL; - - strv_free(c->inaccessible_dirs); - c->inaccessible_dirs = NULL; + c->read_only_dirs = strv_free(c->read_only_dirs); + c->read_write_dirs = strv_free(c->read_write_dirs); + c->inaccessible_dirs = strv_free(c->inaccessible_dirs); if (c->cpuset) CPU_FREE(c->cpuset); - free(c->utmp_id); - c->utmp_id = NULL; - - free(c->selinux_context); - c->selinux_context = NULL; - - free(c->apparmor_profile); - c->apparmor_profile = NULL; - - set_free(c->syscall_filter); - c->syscall_filter = NULL; + c->utmp_id = mfree(c->utmp_id); + c->selinux_context = mfree(c->selinux_context); + c->apparmor_profile = mfree(c->apparmor_profile); - set_free(c->syscall_archs); - c->syscall_archs = NULL; + c->syscall_filter = set_free(c->syscall_filter); + c->syscall_archs = set_free(c->syscall_archs); + c->address_families = set_free(c->address_families); - set_free(c->address_families); - c->address_families = NULL; - - strv_free(c->runtime_directory); - c->runtime_directory = NULL; + c->runtime_directory = strv_free(c->runtime_directory); bus_endpoint_free(c->bus_endpoint); c->bus_endpoint = NULL; @@ -2071,11 +2084,9 @@ int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_p void exec_command_done(ExecCommand *c) { assert(c); - free(c->path); - c->path = NULL; + c->path = mfree(c->path); - strv_free(c->argv); - c->argv = NULL; + c->argv = strv_free(c->argv); } void exec_command_done_array(ExecCommand *c, unsigned n) { @@ -2203,7 +2214,7 @@ int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) { static bool tty_may_match_dev_console(const char *tty) { _cleanup_free_ char *active = NULL; - char *console; + char *console; if (startswith(tty, "/dev/")) tty += 5; @@ -2954,3 +2965,11 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput); + +static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = { + [EXEC_UTMP_INIT] = "init", + [EXEC_UTMP_LOGIN] = "login", + [EXEC_UTMP_USER] = "user", +}; + +DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode); |