execute.c revision fdbbadbd0d13d3296b9aa4273aaeecd9ba6b82d1
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering This file is part of systemd.
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering Copyright 2010 Lennart Poettering
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering systemd is free software; you can redistribute it and/or modify it
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering under the terms of the GNU Lesser General Public License as published by
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering (at your option) any later version.
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering systemd is distributed in the hope that it will be useful, but
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering Lesser General Public License for more details.
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering You should have received a copy of the GNU Lesser General Public License
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering/* This assumes there is a 'tty' group */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poetteringstatic int shift_fds(int fds[], unsigned n_fds) {
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* Modifies the fds array! (sorts it) */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* Already at right index? */
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering /* Hmm, the fd we wanted isn't free? Then
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering * let's remember that and try again from here */
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poetteringstatic int flags_fds(const int fds[], unsigned n_fds, bool nonblock) {
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering for (i = 0; i < n_fds; i++) {
5892a914d173e4b968d2a14fbf717373dee3999aDaniel Mack /* We unconditionally drop FD_CLOEXEC from the fds,
5892a914d173e4b968d2a14fbf717373dee3999aDaniel Mack * since after all we want to pass these fds to our
5892a914d173e4b968d2a14fbf717373dee3999aDaniel Mack * children */
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering_pure_ static const char *tty_path(const ExecContext *context) {
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poetteringstatic void exec_context_tty_reset(const ExecContext *context) {
79008bddf679a5e0900369950eb346c9fa687107Lennart Poettering if (context->tty_vt_disallocate && context->tty_path)
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poetteringstatic bool is_terminal_output(ExecOutput o) {
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poetteringstatic int open_null_as(int flags, int nfd) {
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poetteringstatic int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering .un.sun_path = "/run/systemd/journal/stdout",
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering /* If we fail to restore the uid or gid, things will likely
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering fail later on. This should only happen if an LSM interferes. */
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poetteringstatic int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd, uid_t uid, gid_t gid) {
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering r = connect_journal_socket(fd, uid, gid);
8f077bf94e129fa1b6f0159e3140c4326f1066cfZbigniew Jędrzejewski-Szmek fd_inc_sndbuf(fd, SNDBUF_SIZE);
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering context->syslog_identifier ? context->syslog_identifier : ident,
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poetteringstatic int open_terminal_as(const char *path, mode_t mode, int nfd) {
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering fd = open_terminal(path, mode | O_NOCTTY);
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poetteringstatic bool is_terminal_input(ExecInput i) {
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poetteringstatic int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) {
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering if (is_terminal_input(std_input) && !apply_tty_stdin)
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0)
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poetteringstatic int fixup_output(ExecOutput std_output, int socket_fd) {
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering if (std_output == EXEC_OUTPUT_SOCKET && socket_fd < 0)
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering if (dup2(params->stdin_fd, STDIN_FILENO) < 0)
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* Try to make this the controlling tty, if it is a tty, and reset it */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering (void) ioctl(STDIN_FILENO, TIOCSCTTY, context->std_input == EXEC_INPUT_TTY_FORCE);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering (void) reset_terminal_fd(STDIN_FILENO, true);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering return open_null_as(O_RDONLY, STDIN_FILENO);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering r = dup2(fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering assert_not_reached("Unknown input type");
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering const char *ident,
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering if (fileno == STDOUT_FILENO && params->stdout_fd >= 0) {
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering if (dup2(params->stdout_fd, STDOUT_FILENO) < 0)
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering if (fileno == STDERR_FILENO && params->stderr_fd >= 0) {
31938a8560a664c32a9d72f1fc2d4347b232e6e9Michal Schmidt if (dup2(params->stderr_fd, STDERR_FILENO) < 0)
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin);
31938a8560a664c32a9d72f1fc2d4347b232e6e9Michal Schmidt o = fixup_output(context->std_output, socket_fd);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering e = fixup_output(context->std_error, socket_fd);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* This expects the input and output are already set up */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* Don't change the stderr file descriptor if we inherit all
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering * the way and are not on a tty */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering !is_terminal_input(context->std_input) &&
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* Duplicate from stdout if possible */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering if (e == o || e == EXEC_OUTPUT_INHERIT)
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering return dup2(STDOUT_FILENO, fileno) < 0 ? -errno : fileno;
5892a914d173e4b968d2a14fbf717373dee3999aDaniel Mack } else if (o == EXEC_OUTPUT_INHERIT) {
31938a8560a664c32a9d72f1fc2d4347b232e6e9Michal Schmidt /* If input got downgraded, inherit the original value */
5892a914d173e4b968d2a14fbf717373dee3999aDaniel Mack if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input))
5892a914d173e4b968d2a14fbf717373dee3999aDaniel Mack return open_terminal_as(tty_path(context), O_WRONLY, fileno);
5892a914d173e4b968d2a14fbf717373dee3999aDaniel Mack /* If the input is connected to anything that's not a /dev/null, inherit that... */
5892a914d173e4b968d2a14fbf717373dee3999aDaniel Mack return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* We need to open /dev/null here anew, to get the right access mode. */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* We don't reset the terminal if this is just about output */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering return open_terminal_as(tty_path(context), O_WRONLY, fileno);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering r = connect_logger_as(context, o, ident, unit->id, fileno, uid, gid);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering log_unit_error_errno(unit, r, "Failed to connect %s to the journal socket, ignoring: %m", fileno == STDOUT_FILENO ? "stdout" : "stderr");
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering return dup2(socket_fd, fileno) < 0 ? -errno : fileno;
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering assert_not_reached("Unknown error type");
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poetteringstatic int chown_terminal(int fd, uid_t uid) {
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering /* This might fail. What matters are the results. */
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering if (st.st_uid != uid || (st.st_mode & 0777) != TTY_MODE)
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poetteringstatic int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) {
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering _cleanup_close_ int fd = -1, saved_stdin = -1, saved_stdout = -1;
16ac401407959cbc62312e61c2dd76dbc3a0793bLennart Poettering saved_stdin = fcntl(STDIN_FILENO, F_DUPFD, 3);
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3);
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering_printf_(1, 2) static int write_confirm_message(const char *format, ...) {
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poetteringstatic int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) {
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering if (dup2(*saved_stdin, STDIN_FILENO) < 0)
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering if (dup2(*saved_stdout, STDOUT_FILENO) < 0)
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poetteringstatic int ask_for_confirmation(char *response, char **argv) {
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering int saved_stdout = -1, saved_stdin = -1, r;
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering r = setup_confirm_stdio(&saved_stdin, &saved_stdout);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering r = ask_char(response, "yns", "Execute %s? [Yes, No, Skip] ", line);
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering restore_confirm_stdio(&saved_stdin, &saved_stdout);
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poetteringstatic int enforce_groups(const ExecContext *context, const char *username, gid_t gid) {
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering /* Lookup and set GID and supplementary group list. Here too
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering * we avoid NSS lookups for gid=0. */
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering /* First step, initialize groups from /etc/groups */
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering /* Second step, set our gids */
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering /* Final step, initialize any manually set supplementary groups */
79008bddf679a5e0900369950eb346c9fa687107Lennart Poettering assert_se((ngroups_max = (int) sysconf(_SC_NGROUPS_MAX)) > 0);
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering STRV_FOREACH(i, context->supplementary_groups) {
79008bddf679a5e0900369950eb346c9fa687107Lennart Poettering const char *g;
e24e415e5f3af2fe86d2be9a1f1a2e8d5f8c96bfDaniel Mackstatic int enforce_user(const ExecContext *context, uid_t uid) {
bd5f920f1288c0d4d488629fadf067f709227030Lennart Poettering /* Sets (but doesn't lookup) the uid and make sure we keep the
bd5f920f1288c0d4d488629fadf067f709227030Lennart Poettering * capabilities while doing so. */
bd5f920f1288c0d4d488629fadf067f709227030Lennart Poettering CAP_SETUID, /* Necessary so that we can run setresuid() below */
bd5f920f1288c0d4d488629fadf067f709227030Lennart Poettering CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
dcc2fc01fa850e9ee36c549dc2691e7e5c71bebfLennart Poettering /* First step: If we need to keep capabilities but
dcc2fc01fa850e9ee36c549dc2691e7e5c71bebfLennart Poettering * drop privileges we need to make sure we keep our
bd5f920f1288c0d4d488629fadf067f709227030Lennart Poettering * caps, while we drop privileges. */
dcc2fc01fa850e9ee36c549dc2691e7e5c71bebfLennart Poettering int sb = context->secure_bits | 1<<SECURE_KEEP_CAPS;
bd5f920f1288c0d4d488629fadf067f709227030Lennart Poettering /* Second step: set the capabilities. This will reduce
bd5f920f1288c0d4d488629fadf067f709227030Lennart Poettering * the capabilities to the minimum we need. */
bd5f920f1288c0d4d488629fadf067f709227030Lennart Poettering if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
bd5f920f1288c0d4d488629fadf067f709227030Lennart Poettering cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0)
a9c8343e83ec09f80a76930573b2592f97ae4283Daniel Mack /* Third step: actually set the uids */
bd5f920f1288c0d4d488629fadf067f709227030Lennart Poettering /* At this point we should have all necessary capabilities but
bd5f920f1288c0d4d488629fadf067f709227030Lennart Poettering are otherwise a normal user. However, the caps might got
79008bddf679a5e0900369950eb346c9fa687107Lennart Poettering corrupted due to the setresuid() so we need clean them up
bd5f920f1288c0d4d488629fadf067f709227030Lennart Poettering later. This is done outside of this call. */
bd5f920f1288c0d4d488629fadf067f709227030Lennart Poettering /* We don't support conversations */
bd5f920f1288c0d4d488629fadf067f709227030Lennart Poettering _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering /* We set up PAM in the parent process, then fork. The child
79008bddf679a5e0900369950eb346c9fa687107Lennart Poettering * will then stay around until killed via PR_GET_PDEATHSIG or
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering * systemd via the cgroup logic. It will then remove the PAM
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering * session again. The parent process will exec() the actual
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering * daemon. We do things this way to ensure that the main PID
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering * of the daemon is the one we initially fork()ed. */
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering pam_code = pam_start(name, user, &conv, &handle);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering pam_code = pam_set_item(handle, PAM_TTY, tty);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering pam_code = pam_open_session(handle, flags);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* Block SIGTERM, so that we know that it won't get lost in
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering * the child */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering assert_se(sigprocmask_many(SIG_BLOCK, &old_ss, SIGTERM, -1) >= 0);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* The child's job is to reset the PAM session on
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering * termination */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering barrier_set_role(&barrier, BARRIER_CHILD);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* This string must fit in 10 chars (i.e. the length
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering * of "/sbin/init"), to look pretty in /bin/ps */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* Make sure we don't keep open the passed fds in this
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering child. We assume that otherwise only those fds are
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering open here that have been opened by PAM. */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* Drop privileges - we don't need any to pam_close_session
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering * and this will make PR_SET_PDEATHSIG work in most cases.
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering * If this fails, ignore the error - but expect sd-pam threads
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering * to fail to exit normally */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering log_error_errno(r, "Error: Failed to setresuid() in sd-pam: %m");
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* Wait until our parent died. This will only work if
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering * the above setresuid() succeeds, otherwise the kernel
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering * will not allow unprivileged parents kill their privileged
79008bddf679a5e0900369950eb346c9fa687107Lennart Poettering * children this way. We rely on the control groups kill logic
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering * to do the rest for us. */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
79008bddf679a5e0900369950eb346c9fa687107Lennart Poettering /* Tell the parent that our setup is done. This is especially
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering * important regarding dropping privileges. Otherwise, unit
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering * setup might race against our setresuid(2) call. */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* Check if our parent process might already have
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering /* If our parent died we'll end the session */
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering pam_code = pam_close_session(handle, flags);
2f671520ebade4877cbf6aca3572a5f8c4e1871dLennart Poettering barrier_set_role(&barrier, BARRIER_PARENT);
2f671520ebade4877cbf6aca3572a5f8c4e1871dLennart Poettering /* If the child was forked off successfully it will do all the
2f671520ebade4877cbf6aca3572a5f8c4e1871dLennart Poettering * cleanups, so forget about the handle here. */
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering /* Unblock SIGTERM again in the parent */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering assert_se(sigprocmask(SIG_SETMASK, &old_ss, NULL) >= 0);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* We close the log explicitly here, since the PAM modules
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering * might have opened it, but we don't want this fd around. */
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering /* Synchronously wait for the child to initialize. We don't care for
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering * errors as we cannot recover. However, warn loudly if it happens. */
0faacd470dfbd24f4c6504da6f04213aa05f9d19Lennart Poettering log_error("PAM failed: %s", pam_strerror(handle, pam_code));
0faacd470dfbd24f4c6504da6f04213aa05f9d19Lennart Poettering err = -EPERM; /* PAM errors do not map to errno */
0faacd470dfbd24f4c6504da6f04213aa05f9d19Lennart Poettering err = log_error_errno(err < 0 ? err : errno, "PAM failed: %m");
0faacd470dfbd24f4c6504da6f04213aa05f9d19Lennart Poettering pam_code = pam_close_session(handle, flags);
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poetteringstatic void rename_process_from_path(const char *path) {
a4152e3fe28b53b8919cc404dd7eca7ead1bf9bdLennart Poettering const char *p;
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering /* This resulting string must fit in 10 chars (i.e. the length
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering * of "/sbin/init") to look pretty in /bin/ps */
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering /* The end of the process name is usually more
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering * interesting, since the first bit might just be
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering * "systemd-" */
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering p = p + l - 8;
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poetteringstatic int apply_seccomp(const ExecContext *c) {
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering negative_action = c->syscall_errno == 0 ? SCMP_ACT_KILL : SCMP_ACT_ERRNO(c->syscall_errno);
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering seccomp = seccomp_init(c->syscall_whitelist ? negative_action : SCMP_ACT_ALLOW);
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1);
e821075a23fdfa3ca7738fc30bb2d4c430fe10c0Lennart Poettering r = seccomp_add_secondary_archs(seccomp);
goto finish;
goto finish;
Iterator i;
assert(c);
if (!seccomp)
return -ENOMEM;
goto finish;
if (c->address_families_whitelist) {
void *afp;
if (first == 0) {
r = seccomp_rule_add(
goto finish;
r = seccomp_rule_add(
goto finish;
r = seccomp_rule_add(
goto finish;
r = seccomp_rule_add(
goto finish;
void *af;
r = seccomp_rule_add(
goto finish;
goto finish;
if (idle_pipe[0] >= 0) {
ssize_t n;
static int build_environment(
const ExecContext *c,
unsigned n_fds,
char ** fd_names,
const char *home,
const char *username,
const char *shell,
char ***ret) {
unsigned n_env = 0;
assert(c);
if (!our_env)
return -ENOMEM;
if (n_fds > 0) {
return -ENOMEM;
return -ENOMEM;
if (!joined)
return -ENOMEM;
return -ENOMEM;
if (watchdog_usec > 0) {
return -ENOMEM;
return -ENOMEM;
if (home) {
return -ENOMEM;
if (username) {
return -ENOMEM;
return -ENOMEM;
if (shell) {
return -ENOMEM;
c->tty_path) {
return -ENOMEM;
v = getenv(*i);
return -ENOMEM;
return -ENOMEM;
x = NULL;
static bool exec_needs_mount_namespace(
static int close_remaining_fds(
int socket_fd,
unsigned n_dont_close = 0;
if (socket_fd >= 0)
if (n_fds > 0) {
if (runtime) {
static int exec_child(
char **argv,
int socket_fd,
char **files_env,
int *exit_status) {
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
bool needs_mount_namespace;
r = reset_signal_mask();
if (setsid() < 0) {
return -errno;
char response;
if (r == -ETIMEDOUT)
write_confirm_message("Couldn't ask confirmation question, assuming positive response: %s\n", strerror(-r));
return -ECANCELED;
*exit_status = 0;
if (socket_fd >= 0)
r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, basename(command->path), uid, gid);
r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, basename(command->path), uid, gid);
log_open();
log_unit_debug_errno(unit, r, "Failed to adjust OOM setting, assuming containerized execution, ignoring: %m");
log_close();
return -errno;
return -errno;
r = sched_setscheduler(0,
SCHED_RESET_ON_FORK : 0),
¶m);
return -errno;
return -errno;
return -errno;
return -errno;
return -errno;
char **rt;
_cleanup_free_ char *p;
return -ENOMEM;
#ifdef HAVE_SMACK
#ifdef SMACK_DEFAULT_PROCESS_LABEL
#ifdef HAVE_PAM
if (needs_mount_namespace) {
r = setup_namespace(
tmp,
var,
log_open();
log_unit_debug_errno(unit, r, "Failed to set up namespace, assuming containerized execution, ignoring: %m");
log_close();
return -errno;
return -errno;
if (chdir(d) < 0 &&
return -errno;
#ifdef HAVE_SELINUX
if (params->apply_permissions && mac_selinux_use() && params->selinux_context_net && socket_fd >= 0) {
r = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net);
for (i = 0; i < _RLIMIT_MAX; i++) {
return -errno;
return -errno;
return -errno;
return -errno;
#ifdef HAVE_SECCOMP
#ifdef HAVE_SELINUX
if (mac_selinux_use()) {
if (exec_context) {
#ifdef HAVE_APPARMOR
return -errno;
r = build_environment(context, n_fds, params->fd_names, params->watchdog_usec, home, username, shell, &our_env);
NULL);
if (!final_env) {
return -ENOMEM;
if (!final_argv) {
return -ENOMEM;
if (line) {
log_open();
NULL);
log_close();
return -errno;
int socket_fd, r;
char **argv;
return -EINVAL;
if (!line)
return log_oom();
NULL);
if (pid < 0)
if (pid == 0) {
int exit_status;
argv,
&exit_status);
log_open();
NULL);
assert(c);
c->syslog_level_prefix = true;
c->ignore_sigpipe = true;
assert(c);
if (c->capabilities) {
if (c->cpuset)
assert(c);
if (!runtime_prefix)
_cleanup_free_ char *p;
return -ENOMEM;
assert(c);
exec_command_done(c+i);
ExecCommand *i;
free(i);
return NULL;
c[i] = exec_command_free_list(c[i]);
typedef struct InvalidEnvInfo {
const char *path;
char **i, **r = NULL;
assert(c);
assert(l);
char *fn;
bool ignore = false;
int count, n;
fn = *i;
ignore = true;
fn ++;
if (ignore)
strv_free(r);
return -EINVAL;
errno = 0;
if (ignore)
strv_free(r);
if (count == 0) {
if (ignore)
strv_free(r);
return -EINVAL;
for (n = 0; n < count; n++) {
if (ignore)
strv_free(r);
if (r == NULL)
strv_free(r);
strv_free(p);
return -ENOMEM;
char *console;
if (!console)
assert(f);
STRV_FOREACH(g, l)
assert(c);
assert(f);
fprintf(f,
if (c->nice_set)
fprintf(f,
if (c->oom_score_adjust_set)
fprintf(f,
for (i = 0; i < RLIM_NLIMITS; i++)
if (c->rlimit[i])
if (c->ioprio_set) {
fprintf(f,
if (c->cpu_sched_set) {
fprintf(f,
if (c->cpuset) {
for (i = 0; i < c->cpuset_ncpus; i++)
fprintf(f,
if (c->tty_path)
fprintf(f,
fprintf(f,
if (c->capabilities) {
_cleanup_cap_free_charp_ char *t;
if (c->secure_bits)
if (c->capability_bounding_set_drop) {
for (l = 0; l <= cap_last_cap(); l++)
if (c->user)
if (c->group)
if (c->pam_name)
if (c->utmp_id)
fprintf(f,
if (c->selinux_context)
fprintf(f,
fprintf(f,
if (c->syscall_filter) {
#ifdef HAVE_SECCOMP
Iterator j;
void *id;
bool first = true;
fprintf(f,
prefix);
if (!c->syscall_whitelist)
#ifdef HAVE_SECCOMP
if (first)
first = false;
if (c->syscall_archs) {
#ifdef HAVE_SECCOMP
Iterator j;
void *id;
fprintf(f,
prefix);
#ifdef HAVE_SECCOMP
if (c->syscall_errno != 0)
fprintf(f,
if (c->apparmor_profile)
fprintf(f,
assert(c);
if (!c->user)
assert(s);
zero(*s);
assert(s);
zero(*s);
if (context) {
assert(s);
assert(f);
if (s->pid <= 0)
fprintf(f,
fprintf(f,
fprintf(f,
size_t k;
bool first = true;
if (!(n = new(char, k)))
return NULL;
if (!first)
first = false;
p = stpcpy(p, *a);
p = stpcpy(p, *a);
const char *prefix2;
assert(c);
assert(f);
fprintf(f,
assert(f);
assert(l);
assert(e);
assert(c);
return -ENOMEM;
strv_free(l);
return -ENOMEM;
c->path = p;
c->argv = l;
assert(c);
return -ENOMEM;
if (*rt)
if (!*rt)
return -ENOMEM;
assert(c);
if (*rt)
return -errno;
assert(r);
r->n_ref++;
return NULL;
r->n_ref--;
if (r->n_ref > 0)
return NULL;
free(r);
return NULL;
assert(u);
assert(f);
if (!rt)
int copy;
if (copy < 0)
return copy;
int copy;
if (copy < 0)
return copy;
int exec_runtime_deserialize_item(Unit *u, ExecRuntime **rt, const char *key, const char *value, FDSet *fds) {
char *copy;
return log_oom();
if (!copy)
return log_oom();
char *copy;
return log_oom();
if (!copy)
return log_oom();
int fd;
return log_oom();
int fd;
return log_oom();
static void *remove_tmpdir_thread(void *p) {
return NULL;
if (!rt)