execute.c revision 2a836ca9703689ba05513c98fb293b91a186a9ac
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer This file is part of systemd.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer Copyright 2010 Lennart Poettering
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer systemd is free software; you can redistribute it and/or modify it
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer under the terms of the GNU Lesser General Public License as published by
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer the Free Software Foundation; either version 2.1 of the License, or
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt (at your option) any later version.
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt systemd is distributed in the hope that it will be useful, but
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt WITHOUT ANY WARRANTY; without even the implied warranty of
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1c36b4a73b876258fbe01fbe9bc9b750b7dcc9ceEvgeny Vereshchagin Lesser General Public License for more details.
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier You should have received a copy of the GNU Lesser General Public License
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier along with systemd; If not, see <http://www.gnu.org/licenses/>.
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier/* This assumes there is a 'tty' group */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int shift_fds(int fds[], unsigned n_fds) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* Modifies the fds array! (sorts it) */
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin /* Already at right index? */
cb2f9d3f296bc80b55f09880d61dfdf47fc98212Evgeny Vereshchagin /* Hmm, the fd we wanted isn't free? Then
cb2f9d3f296bc80b55f09880d61dfdf47fc98212Evgeny Vereshchagin * let's remember that and try again from here */
9974ff63b182e67bf3d3d9262e2bfa84f0a1378bEvgeny Vereshchaginstatic int flags_fds(const int fds[], unsigned n_fds, bool nonblock) {
ac289ce3f5eb3f13806f7c631c6b23cee18b26daEvgeny Vereshchagin /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */
ac289ce3f5eb3f13806f7c631c6b23cee18b26daEvgeny Vereshchagin for (i = 0; i < n_fds; i++) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* We unconditionally drop FD_CLOEXEC from the fds,
2375607039517c88df51ef16ddbb624ec1c10654Kay Sievers * since after all we want to pass these fds to our
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier * children */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier_pure_ static const char *tty_path(const ExecContext *context) {
edbced8a151c1b7ded685e2ec644950d2adec5f5Harald Hoyerstatic void exec_context_tty_reset(const ExecContext *context) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (context->tty_vt_disallocate && context->tty_path)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic bool is_terminal_output(ExecOutput o) {
5a613464fa15c0960b418322f592a24b9e0f7455Evgeny Vereshchaginstatic int open_null_as(int flags, int nfd) {
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchaginstatic int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchagin .un.sun_path = "/run/systemd/journal/stdout",
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* If we fail to restore the uid or gid, things will likely
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier fail later on. This should only happen if an LSM interferes. */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd, uid_t uid, gid_t gid) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier context->syslog_identifier ? context->syslog_identifier : ident,
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int open_terminal_as(const char *path, mode_t mode, int nfd) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (is_terminal_input(std_input) && !apply_tty_stdin)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int fixup_output(ExecOutput std_output, int socket_fd) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (std_output == EXEC_OUTPUT_SOCKET && socket_fd < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (dup2(params->stdin_fd, STDIN_FILENO) < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* Try to make this the controlling tty, if it is a tty, and reset it */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier (void) ioctl(STDIN_FILENO, TIOCSCTTY, context->std_input == EXEC_INPUT_TTY_FORCE);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier (void) reset_terminal_fd(STDIN_FILENO, true);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer switch (i) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dup2(fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *ident,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (fileno == STDOUT_FILENO && params->stdout_fd >= 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (dup2(params->stdout_fd, STDOUT_FILENO) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (fileno == STDERR_FILENO && params->stderr_fd >= 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (dup2(params->stderr_fd, STDERR_FILENO) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer o = fixup_output(context->std_output, socket_fd);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer e = fixup_output(context->std_error, socket_fd);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* This expects the input and output are already set up */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Don't change the stderr file descriptor if we inherit all
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * the way and are not on a tty */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Duplicate from stdout if possible */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (e == o || e == EXEC_OUTPUT_INHERIT)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return dup2(STDOUT_FILENO, fileno) < 0 ? -errno : fileno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer } else if (o == EXEC_OUTPUT_INHERIT) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* If input got downgraded, inherit the original value */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input))
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return open_terminal_as(tty_path(context), O_WRONLY, fileno);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* If the input is connected to anything that's not a /dev/null, inherit that... */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* We need to open /dev/null here anew, to get the right access mode. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer switch (o) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* We don't reset the terminal if this is just about output */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return open_terminal_as(tty_path(context), O_WRONLY, fileno);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = connect_logger_as(context, o, ident, unit->id, fileno, uid, gid);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_unit_error_errno(unit, r, "Failed to connect %s to the journal socket, ignoring: %m", fileno == STDOUT_FILENO ? "stdout" : "stderr");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return dup2(socket_fd, fileno) < 0 ? -errno : fileno;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* This might fail. What matters are the results. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (st.st_uid != uid || (st.st_mode & 0777) != TTY_MODE)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer _cleanup_close_ int fd = -1, saved_stdin = -1, saved_stdout = -1;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer_printf_(1, 2) static int write_confirm_message(const char *format, ...) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int ask_for_confirmation(char *response, char **argv) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = setup_confirm_stdio(&saved_stdin, &saved_stdout);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = ask_char(response, "yns", "Execute %s? [Yes, No, Skip] ", line);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer restore_confirm_stdio(&saved_stdin, &saved_stdout);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int enforce_groups(const ExecContext *context, const char *username, gid_t gid) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Lookup and set GID and supplementary group list. Here too
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * we avoid NSS lookups for gid=0. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* First step, initialize groups from /etc/groups */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Second step, set our gids */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Final step, initialize any manually set supplementary groups */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_se((ngroups_max = (int) sysconf(_SC_NGROUPS_MAX)) > 0);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (k < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer STRV_FOREACH(i, context->supplementary_groups) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *g;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int enforce_user(const ExecContext *context, uid_t uid) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Sets (but doesn't lookup) the uid and make sure we keep the
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * capabilities while doing so. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (context->capabilities || context->capability_ambient_set != 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* First step: If we need to keep capabilities but
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * drop privileges we need to make sure we keep our
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * caps, while we drop privileges. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int sb = context->secure_bits | 1<<SECURE_KEEP_CAPS;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Second step: set the capabilities. This will reduce
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * the capabilities to the minimum we need. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer CAP_SETUID, /* Necessary so that we can run setresuid() below */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Third step: actually set the uids */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* At this point we should have all necessary capabilities but
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer are otherwise a normal user. However, the caps might got
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer corrupted due to the setresuid() so we need clean them up
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer later. This is done outside of this call. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* We don't support conversations */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *name,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *user,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *tty,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* We set up PAM in the parent process, then fork. The child
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * will then stay around until killed via PR_GET_PDEATHSIG or
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * systemd via the cgroup logic. It will then remove the PAM
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * session again. The parent process will exec() the actual
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * daemon. We do things this way to ensure that the main PID
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * of the daemon is the one we initially fork()ed. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer pam_code = pam_start(name, user, &conv, &handle);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Block SIGTERM, so that we know that it won't get lost in
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * the child */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_se(sigprocmask_many(SIG_BLOCK, &old_ss, SIGTERM, -1) >= 0);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* The child's job is to reset the PAM session on
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * termination */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* This string must fit in 10 chars (i.e. the length
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * of "/sbin/init"), to look pretty in /bin/ps */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Make sure we don't keep open the passed fds in this
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer child. We assume that otherwise only those fds are
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer open here that have been opened by PAM. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Drop privileges - we don't need any to pam_close_session
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * and this will make PR_SET_PDEATHSIG work in most cases.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * If this fails, ignore the error - but expect sd-pam threads
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * to fail to exit normally */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_error_errno(r, "Error: Failed to setresuid() in sd-pam: %m");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Wait until our parent died. This will only work if
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * the above setresuid() succeeds, otherwise the kernel
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * will not allow unprivileged parents kill their privileged
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * children this way. We rely on the control groups kill logic
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * to do the rest for us. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Tell the parent that our setup is done. This is especially
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * important regarding dropping privileges. Otherwise, unit
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * setup might race against our setresuid(2) call. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Check if our parent process might already have
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* If our parent died we'll end the session */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* If the child was forked off successfully it will do all the
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer * cleanups, so forget about the handle here. */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* Unblock SIGTERM again in the parent */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer assert_se(sigprocmask(SIG_SETMASK, &old_ss, NULL) >= 0);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* We close the log explicitly here, since the PAM modules
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer * might have opened it, but we don't want this fd around. */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* Synchronously wait for the child to initialize. We don't care for
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer * errors as we cannot recover. However, warn loudly if it happens. */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer log_error("PAM failed: %s", pam_strerror(handle, pam_code));
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer err = -EPERM; /* PAM errors do not map to errno */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer err = log_error_errno(err < 0 ? err : errno, "PAM failed: %m");
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerstatic void rename_process_from_path(const char *path) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer const char *p;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* This resulting string must fit in 10 chars (i.e. the length
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer * of "/sbin/init") to look pretty in /bin/ps */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (l > 8) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* The end of the process name is usually more
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer * interesting, since the first bit might just be
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer * "systemd-" */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer p = p + l - 8;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerstatic int apply_seccomp(const ExecContext *c) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer negative_action = c->syscall_errno == 0 ? SCMP_ACT_KILL : SCMP_ACT_ERRNO(c->syscall_errno);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer seccomp = seccomp_init(c->syscall_whitelist ? negative_action : SCMP_ACT_ALLOW);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer action = c->syscall_whitelist ? SCMP_ACT_ALLOW : negative_action;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = seccomp_rule_add(seccomp, action, PTR_TO_INT(id) - 1, 0);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerstatic int apply_address_families(const ExecContext *c) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* If this is a whitelist, we first block the address
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer * families that are out of range and then everything
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer * that is not in the set. First, we find the lowest
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer * and highest address family in the set. */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* No entries in the valid range, block everything */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* Block everything below the first entry */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* Block everything above the last entry */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Block everything between the first and last
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (set_contains(c->address_families, INT_TO_PTR(af)))
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* If this is a blacklist, then generate one rule for
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * each address family that are then combined in OR
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * checks. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic void do_idle_pipe_dance(int idle_pipe[4]) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (idle_pipe[0] >= 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (idle_pipe[3] >= 0 && r == 0 /* timeout */) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Signal systemd that we are bored and want to continue. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Wait for systemd to react to the signal above. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *home,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer unsigned n_env = 0;
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)
for (l = 0; l <= cap_last_cap(); l++)
if (c->capability_ambient_set != 0) {
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)