execute.c revision 2a836ca9703689ba05513c98fb293b91a186a9ac
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer/***
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer This file is part of systemd.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer Copyright 2010 Lennart Poettering
0fe15dc8ddddeb39a5cad1f4f4afa25fa074a5d1Evgeny Vereshchagin
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
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.
c50a4525aa8151b180d5a325e88c5f3812e66c36Martin Pitt
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/>.
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier***/
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <errno.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <fcntl.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <glob.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <grp.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <poll.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <signal.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <string.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <sys/personality.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <sys/prctl.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <sys/socket.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <sys/stat.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <sys/un.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <unistd.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <utmpx.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#ifdef HAVE_PAM
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <security/pam_appl.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#endif
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#ifdef HAVE_SELINUX
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <selinux/selinux.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#endif
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#ifdef HAVE_SECCOMP
b6f0c419e38a960873fe68bf8f89bbb0268eed02Harald Hoyer#include <seccomp.h>
b6f0c419e38a960873fe68bf8f89bbb0268eed02Harald Hoyer#endif
b6f0c419e38a960873fe68bf8f89bbb0268eed02Harald Hoyer
b6f0c419e38a960873fe68bf8f89bbb0268eed02Harald Hoyer#ifdef HAVE_APPARMOR
b6f0c419e38a960873fe68bf8f89bbb0268eed02Harald Hoyer#include <sys/apparmor.h>
b6f0c419e38a960873fe68bf8f89bbb0268eed02Harald Hoyer#endif
b6f0c419e38a960873fe68bf8f89bbb0268eed02Harald Hoyer
b6f0c419e38a960873fe68bf8f89bbb0268eed02Harald Hoyer#include "sd-messages.h"
61fea35e14d84144e6e2122f5cd247f9c7e6245eEvgeny Vereshchagin
61fea35e14d84144e6e2122f5cd247f9c7e6245eEvgeny Vereshchagin#include "af-list.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "alloc-util.h"
61fea35e14d84144e6e2122f5cd247f9c7e6245eEvgeny Vereshchagin#ifdef HAVE_APPARMOR
61fea35e14d84144e6e2122f5cd247f9c7e6245eEvgeny Vereshchagin#include "apparmor-util.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#endif
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "async.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "barrier.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "bus-endpoint.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "cap-list.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "capability-util.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "def.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "env-util.h"
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt#include "errno-list.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "execute.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "exit-status.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "fd-util.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "fileio.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "formats-util.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "fs-util.h"
dbf43a42b8bb66d53c7cbab05f104c28097f811eDaniel Mack#include "glob-util.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "io-util.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "ioprio.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "log.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "macro.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "missing.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "mkdir.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "namespace.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "parse-util.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "path-util.h"
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include "process-util.h"
dbf43a42b8bb66d53c7cbab05f104c28097f811eDaniel Mack#include "rlimit-util.h"
dbf43a42b8bb66d53c7cbab05f104c28097f811eDaniel Mack#include "rm-rf.h"
dbf43a42b8bb66d53c7cbab05f104c28097f811eDaniel Mack#ifdef HAVE_SECCOMP
dbf43a42b8bb66d53c7cbab05f104c28097f811eDaniel Mack#include "seccomp-util.h"
8a8332f77e61d41f3bb28b8f929ed41e0ffaf721Zbigniew Jędrzejewski-Szmek#endif
8a8332f77e61d41f3bb28b8f929ed41e0ffaf721Zbigniew Jędrzejewski-Szmek#include "securebits.h"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#include "selinux-util.h"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#include "signal-util.h"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#include "smack-util.h"
8a8332f77e61d41f3bb28b8f929ed41e0ffaf721Zbigniew Jędrzejewski-Szmek#include "string-table.h"
24be78d72b931b0175f08cee12fd23d631c024bfEvgeny Vereshchagin#include "string-util.h"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#include "strv.h"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#include "syslog-util.h"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#include "terminal-util.h"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#include "unit.h"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#include "user-util.h"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#include "util.h"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#include "utmp-wtmp.h"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier/* This assumes there is a 'tty' group */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#define TTY_MODE 0620
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#define SNDBUF_SIZE (8*1024*1024)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int shift_fds(int fds[], unsigned n_fds) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier int start, restart_from;
9974ff63b182e67bf3d3d9262e2bfa84f0a1378bEvgeny Vereshchagin
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (n_fds <= 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return 0;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* Modifies the fds array! (sorts it) */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(fds);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier start = 0;
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin for (;;) {
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin int i;
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin restart_from = -1;
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin for (i = start; i < (int) n_fds; i++) {
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin int nfd;
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin /* Already at right index? */
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin if (fds[i] == i+3)
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin continue;
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin nfd = fcntl(fds[i], F_DUPFD, i + 3);
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin if (nfd < 0)
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin return -errno;
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin safe_close(fds[i]);
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin fds[i] = nfd;
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin
cb2f9d3f296bc80b55f09880d61dfdf47fc98212Evgeny Vereshchagin /* Hmm, the fd we wanted isn't free? Then
cb2f9d3f296bc80b55f09880d61dfdf47fc98212Evgeny Vereshchagin * let's remember that and try again from here */
cb2f9d3f296bc80b55f09880d61dfdf47fc98212Evgeny Vereshchagin if (nfd != i+3 && restart_from < 0)
cb2f9d3f296bc80b55f09880d61dfdf47fc98212Evgeny Vereshchagin restart_from = i;
cb2f9d3f296bc80b55f09880d61dfdf47fc98212Evgeny Vereshchagin }
cb2f9d3f296bc80b55f09880d61dfdf47fc98212Evgeny Vereshchagin
cb2f9d3f296bc80b55f09880d61dfdf47fc98212Evgeny Vereshchagin if (restart_from < 0)
cb2f9d3f296bc80b55f09880d61dfdf47fc98212Evgeny Vereshchagin break;
cb2f9d3f296bc80b55f09880d61dfdf47fc98212Evgeny Vereshchagin
cb2f9d3f296bc80b55f09880d61dfdf47fc98212Evgeny Vereshchagin start = restart_from;
cb2f9d3f296bc80b55f09880d61dfdf47fc98212Evgeny Vereshchagin }
9974ff63b182e67bf3d3d9262e2bfa84f0a1378bEvgeny Vereshchagin
9974ff63b182e67bf3d3d9262e2bfa84f0a1378bEvgeny Vereshchagin return 0;
9974ff63b182e67bf3d3d9262e2bfa84f0a1378bEvgeny Vereshchagin}
9974ff63b182e67bf3d3d9262e2bfa84f0a1378bEvgeny Vereshchagin
9974ff63b182e67bf3d3d9262e2bfa84f0a1378bEvgeny Vereshchaginstatic int flags_fds(const int fds[], unsigned n_fds, bool nonblock) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier unsigned i;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier int r;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (n_fds <= 0)
ac289ce3f5eb3f13806f7c631c6b23cee18b26daEvgeny Vereshchagin return 0;
ac289ce3f5eb3f13806f7c631c6b23cee18b26daEvgeny Vereshchagin
ac289ce3f5eb3f13806f7c631c6b23cee18b26daEvgeny Vereshchagin assert(fds);
ac289ce3f5eb3f13806f7c631c6b23cee18b26daEvgeny Vereshchagin
ac289ce3f5eb3f13806f7c631c6b23cee18b26daEvgeny Vereshchagin /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */
ac289ce3f5eb3f13806f7c631c6b23cee18b26daEvgeny Vereshchagin
ac289ce3f5eb3f13806f7c631c6b23cee18b26daEvgeny Vereshchagin for (i = 0; i < n_fds; i++) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = fd_nonblock(fds[i], nonblock);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (r < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return r;
8a8332f77e61d41f3bb28b8f929ed41e0ffaf721Zbigniew Jędrzejewski-Szmek
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
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = fd_cloexec(fds[i], false);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (r < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return r;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier }
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return 0;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier}
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier_pure_ static const char *tty_path(const ExecContext *context) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(context);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
739d81ddd005fae2bb82edce5b8a6173c7c48b34Zbigniew Jędrzejewski-Szmek if (context->tty_path)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return context->tty_path;
1b1eae69ce52ef6c89a1200e8d3758549b291991Daniel Mack
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return "/dev/console";
739d81ddd005fae2bb82edce5b8a6173c7c48b34Zbigniew Jędrzejewski-Szmek}
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
edbced8a151c1b7ded685e2ec644950d2adec5f5Harald Hoyerstatic void exec_context_tty_reset(const ExecContext *context) {
1b1eae69ce52ef6c89a1200e8d3758549b291991Daniel Mack assert(context);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (context->tty_vhangup)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier terminal_vhangup(tty_path(context));
739d81ddd005fae2bb82edce5b8a6173c7c48b34Zbigniew Jędrzejewski-Szmek
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (context->tty_reset)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier reset_terminal(tty_path(context));
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (context->tty_vt_disallocate && context->tty_path)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier vt_disallocate(context->tty_path);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier}
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic bool is_terminal_output(ExecOutput o) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier o == EXEC_OUTPUT_TTY ||
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE ||
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier o == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier}
5a613464fa15c0960b418322f592a24b9e0f7455Evgeny Vereshchagin
5a613464fa15c0960b418322f592a24b9e0f7455Evgeny Vereshchaginstatic int open_null_as(int flags, int nfd) {
5a613464fa15c0960b418322f592a24b9e0f7455Evgeny Vereshchagin int fd, r;
5a613464fa15c0960b418322f592a24b9e0f7455Evgeny Vereshchagin
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(nfd >= 0);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier fd = open("/dev/null", flags|O_NOCTTY);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (fd < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return -errno;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (fd != nfd) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = dup2(fd, nfd) < 0 ? -errno : nfd;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier safe_close(fd);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier } else
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = nfd;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return r;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier}
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchagin
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchaginstatic int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchagin union sockaddr_union sa = {
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchagin .un.sun_family = AF_UNIX,
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchagin .un.sun_path = "/run/systemd/journal/stdout",
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchagin };
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchagin uid_t olduid = UID_INVALID;
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchagin gid_t oldgid = GID_INVALID;
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchagin int r;
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchagin
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchagin if (gid != GID_INVALID) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier oldgid = getgid();
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = setegid(gid);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (r < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return -errno;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier }
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (uid != UID_INVALID) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier olduid = getuid();
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = seteuid(uid);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (r < 0) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = -errno;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier goto restore_gid;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier }
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier }
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (r < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = -errno;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
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 Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (uid != UID_INVALID)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier (void) seteuid(olduid);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier restore_gid:
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (gid != GID_INVALID)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier (void) setegid(oldgid);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return r;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier}
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
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) {
bf3a947cb44f31359bba313e0252cbcc0dc95b03Evgeny Vereshchagin int fd, r;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(context);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(output < _EXEC_OUTPUT_MAX);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(ident);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(nfd >= 0);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier fd = socket(AF_UNIX, SOCK_STREAM, 0);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (fd < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return -errno;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = connect_journal_socket(fd, uid, gid);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (r < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return r;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (shutdown(fd, SHUT_RD) < 0) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier safe_close(fd);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return -errno;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier }
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier fd_inc_sndbuf(fd, SNDBUF_SIZE);
4be4833ece2856e0cacc09f8f8b2c02b320751faMartin Pitt
7d023341c765c205068e33d23d63a4000ec211dfMartin Pitt dprintf(fd,
7d023341c765c205068e33d23d63a4000ec211dfMartin Pitt "%s\n"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier "%s\n"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier "%i\n"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier "%i\n"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier "%i\n"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier "%i\n"
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier "%i\n",
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier context->syslog_identifier ? context->syslog_identifier : ident,
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier unit_id,
cffae62bcb6912fbaf1b7b282d9d170c9d308897Martin Pitt context->syslog_priority,
99877b7e3782a51b31bf191825f0335500f52fe5Harald Hoyer !!context->syslog_level_prefix,
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 Chevalier is_terminal_output(output));
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt if (fd != nfd) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = dup2(fd, nfd) < 0 ? -errno : nfd;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier safe_close(fd);
e63b61be5350dbe92ea12e1eeb96dde251ed9292Evgeny Vereshchagin } else
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = nfd;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return r;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier}
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int open_terminal_as(const char *path, mode_t mode, int nfd) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier int fd, r;
0fe15dc8ddddeb39a5cad1f4f4afa25fa074a5d1Evgeny Vereshchagin
0fe15dc8ddddeb39a5cad1f4f4afa25fa074a5d1Evgeny Vereshchagin assert(path);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(nfd >= 0);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier fd = open_terminal(path, mode | O_NOCTTY);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (fd < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return fd;
0fe15dc8ddddeb39a5cad1f4f4afa25fa074a5d1Evgeny Vereshchagin
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (fd != nfd) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = dup2(fd, nfd) < 0 ? -errno : nfd;
417491f122b346a31cf8dc406c4f9195a5900cecEvgeny Vereshchagin safe_close(fd);
417491f122b346a31cf8dc406c4f9195a5900cecEvgeny Vereshchagin } else
417491f122b346a31cf8dc406c4f9195a5900cecEvgeny Vereshchagin r = nfd;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return r;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier}
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic bool is_terminal_input(ExecInput i) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier i == EXEC_INPUT_TTY ||
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier i == EXEC_INPUT_TTY_FORCE ||
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier i == EXEC_INPUT_TTY_FAIL;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier}
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (is_terminal_input(std_input) && !apply_tty_stdin)
25b47f96d9601ff566257b2a31bfb5f4bd25d661Marko Myllynen return EXEC_INPUT_NULL;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return EXEC_INPUT_NULL;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return std_input;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier}
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int fixup_output(ExecOutput std_output, int socket_fd) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (std_output == EXEC_OUTPUT_SOCKET && socket_fd < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return EXEC_OUTPUT_INHERIT;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return std_output;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier}
53d90f9582f96208b3674da823ad1a3d2c3b1aa4Martin Pitt
5c404f1ab8e96efedb983806443ca982a1b2a372Evgeny Vereshchaginstatic int setup_input(
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier const ExecContext *context,
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier const ExecParameters *params,
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier int socket_fd) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier ExecInput i;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(context);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(params);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (params->stdin_fd >= 0) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (dup2(params->stdin_fd, STDIN_FILENO) < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return -errno;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
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
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer return STDIN_FILENO;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier }
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer switch (i) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer case EXEC_INPUT_NULL:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return open_null_as(O_RDONLY, STDIN_FILENO);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer case EXEC_INPUT_TTY:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer case EXEC_INPUT_TTY_FORCE:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer case EXEC_INPUT_TTY_FAIL: {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int fd, r;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer fd = acquire_terminal(tty_path(context),
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer i == EXEC_INPUT_TTY_FAIL,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer i == EXEC_INPUT_TTY_FORCE,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer false,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer USEC_INFINITY);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (fd < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return fd;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (fd != STDIN_FILENO) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dup2(fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer safe_close(fd);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer } else
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = STDIN_FILENO;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return r;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer case EXEC_INPUT_SOCKET:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer default:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_not_reached("Unknown input type");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer}
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int setup_output(
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer Unit *unit,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const ExecContext *context,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const ExecParameters *params,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int fileno,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int socket_fd,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *ident,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer uid_t uid, gid_t gid) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer ExecOutput o;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer ExecInput i;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int r;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(unit);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(context);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(params);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(ident);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (fileno == STDOUT_FILENO && params->stdout_fd >= 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (dup2(params->stdout_fd, STDOUT_FILENO) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return STDOUT_FILENO;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (fileno == STDERR_FILENO && params->stderr_fd >= 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (dup2(params->stderr_fd, STDERR_FILENO) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return STDERR_FILENO;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (fileno == STDERR_FILENO) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer ExecOutput e;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer e = fixup_output(context->std_error, socket_fd);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* This expects the input and output are already set up */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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 if (e == EXEC_OUTPUT_INHERIT &&
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer o == EXEC_OUTPUT_INHERIT &&
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer i == EXEC_INPUT_NULL &&
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer !is_terminal_input(context->std_input) &&
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer getppid () != 1)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return fileno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer o = e;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* If the input is connected to anything that's not a /dev/null, inherit that... */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (i != EXEC_INPUT_NULL)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (getppid() != 1)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return fileno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* We need to open /dev/null here anew, to get the right access mode. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return open_null_as(O_WRONLY, fileno);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer switch (o) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer case EXEC_OUTPUT_NULL:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return open_null_as(O_WRONLY, fileno);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer case EXEC_OUTPUT_TTY:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (is_terminal_input(i))
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer case EXEC_OUTPUT_SYSLOG:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer case EXEC_OUTPUT_KMSG:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer case EXEC_OUTPUT_KMSG_AND_CONSOLE:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer case EXEC_OUTPUT_JOURNAL:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
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 r = open_null_as(O_WRONLY, fileno);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return r;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer case EXEC_OUTPUT_SOCKET:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(socket_fd >= 0);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return dup2(socket_fd, fileno) < 0 ? -errno : fileno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer default:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_not_reached("Unknown error type");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer}
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int chown_terminal(int fd, uid_t uid) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer struct stat st;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(fd >= 0);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* This might fail. What matters are the results. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer (void) fchown(fd, uid, -1);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer (void) fchmod(fd, TTY_MODE);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (fstat(fd, &st) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (st.st_uid != uid || (st.st_mode & 0777) != TTY_MODE)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -EPERM;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return 0;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer}
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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 int r;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(_saved_stdin);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(_saved_stdout);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer saved_stdin = fcntl(STDIN_FILENO, F_DUPFD, 3);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (saved_stdin < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (saved_stdout < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer fd = acquire_terminal(
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer "/dev/console",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer false,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer false,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer false,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer DEFAULT_CONFIRM_USEC);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (fd < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return fd;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = chown_terminal(fd, getuid());
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return r;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = reset_terminal_fd(fd, true);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return r;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (dup2(fd, STDIN_FILENO) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (dup2(fd, STDOUT_FILENO) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (fd >= 2)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer safe_close(fd);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer fd = -1;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer *_saved_stdin = saved_stdin;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer *_saved_stdout = saved_stdout;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer saved_stdin = saved_stdout = -1;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return 0;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer}
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer_printf_(1, 2) static int write_confirm_message(const char *format, ...) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer _cleanup_close_ int fd = -1;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer va_list ap;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(format);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (fd < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return fd;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer va_start(ap, format);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer vdprintf(fd, format, ap);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer va_end(ap);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return 0;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer}
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int r = 0;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(saved_stdin);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(saved_stdout);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer release_terminal();
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (*saved_stdin >= 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (dup2(*saved_stdin, STDIN_FILENO) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (*saved_stdout >= 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (dup2(*saved_stdout, STDOUT_FILENO) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer *saved_stdin = safe_close(*saved_stdin);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer *saved_stdout = safe_close(*saved_stdout);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return r;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer}
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int ask_for_confirmation(char *response, char **argv) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int saved_stdout = -1, saved_stdin = -1, r;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer _cleanup_free_ char *line = NULL;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = setup_confirm_stdio(&saved_stdin, &saved_stdout);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return r;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer line = exec_command_line(argv);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (!line)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -ENOMEM;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = ask_char(response, "yns", "Execute %s? [Yes, No, Skip] ", line);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer restore_confirm_stdio(&saved_stdin, &saved_stdout);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return r;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer}
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int enforce_groups(const ExecContext *context, const char *username, gid_t gid) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer bool keep_groups = false;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int r;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(context);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Lookup and set GID and supplementary group list. Here too
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * we avoid NSS lookups for gid=0. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (context->group || username) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* First step, initialize groups from /etc/groups */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (username && gid != 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (initgroups(username, gid) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer keep_groups = true;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Second step, set our gids */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (setresgid(gid, gid, gid) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (context->supplementary_groups) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int ngroups_max, k;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer gid_t *gids;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer char **i;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Final step, initialize any manually set supplementary groups */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_se((ngroups_max = (int) sysconf(_SC_NGROUPS_MAX)) > 0);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (!(gids = new(gid_t, ngroups_max)))
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -ENOMEM;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (keep_groups) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer k = getgroups(ngroups_max, gids);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (k < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer free(gids);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer } else
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer k = 0;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer STRV_FOREACH(i, context->supplementary_groups) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *g;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (k >= ngroups_max) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer free(gids);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -E2BIG;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer g = *i;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = get_group_creds(&g, gids+k);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer free(gids);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return r;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer k++;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (setgroups(k, gids) < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer free(gids);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer free(gids);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return 0;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer}
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int enforce_user(const ExecContext *context, uid_t uid) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(context);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Sets (but doesn't lookup) the uid and make sure we keep the
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * capabilities while doing so. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (context->capabilities || context->capability_ambient_set != 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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 if (uid != 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int sb = context->secure_bits | 1<<SECURE_KEEP_CAPS;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (prctl(PR_GET_SECUREBITS) != sb)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (prctl(PR_SET_SECUREBITS, sb) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Second step: set the capabilities. This will reduce
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * the capabilities to the minimum we need. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (context->capabilities) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer _cleanup_cap_free_ cap_t d = NULL;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer static const cap_value_t bits[] = {
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 };
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer d = cap_dup(context->capabilities);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (!d)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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 return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (cap_set_proc(d) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Third step: actually set the uids */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (setresuid(uid, uid, uid) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return -errno;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return 0;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer}
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer#ifdef HAVE_PAM
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int null_conv(
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int num_msg,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const struct pam_message **msg,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer struct pam_response **resp,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer void *appdata_ptr) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* We don't support conversations */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return PAM_CONV_ERR;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer}
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int setup_pam(
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *name,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *user,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer uid_t uid,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *tty,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer char ***pam_env,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int fds[], unsigned n_fds) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer static const struct pam_conv conv = {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer .conv = null_conv,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer .appdata_ptr = NULL
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer };
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer pam_handle_t *handle = NULL;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer sigset_t old_ss;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int pam_code = PAM_SUCCESS;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int err = 0;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer char **e = NULL;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer bool close_session = false;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer pid_t pam_pid = 0, parent_pid;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int flags = 0;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(name);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(user);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(pam_env);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer err = barrier_create(&barrier);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (err < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer goto fail;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (log_get_max_level() < LOG_DEBUG)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer flags |= PAM_SILENT;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer pam_code = pam_start(name, user, &conv, &handle);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (pam_code != PAM_SUCCESS) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer handle = NULL;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer goto fail;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (tty) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer pam_code = pam_set_item(handle, PAM_TTY, tty);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (pam_code != PAM_SUCCESS)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer goto fail;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer pam_code = pam_acct_mgmt(handle, flags);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (pam_code != PAM_SUCCESS)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer goto fail;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer pam_code = pam_open_session(handle, flags);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (pam_code != PAM_SUCCESS)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer goto fail;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer close_session = true;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer e = pam_getenvlist(handle);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (!e) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer pam_code = PAM_BUF_ERR;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer goto fail;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Block SIGTERM, so that we know that it won't get lost in
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * the child */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_se(sigprocmask_many(SIG_BLOCK, &old_ss, SIGTERM, -1) >= 0);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer parent_pid = getpid();
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer pam_pid = fork();
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (pam_pid < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer goto fail;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (pam_pid == 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int sig;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int r = EXIT_PAM;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* The child's job is to reset the PAM session on
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * termination */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer barrier_set_role(&barrier, BARRIER_CHILD);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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 rename_process("(sd-pam)");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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 close_many(fds, n_fds);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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 if (setresuid(uid, uid, uid) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_error_errno(r, "Error: Failed to setresuid() in sd-pam: %m");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer (void) ignore_signals(SIGPIPE, -1);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer goto child_finish;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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 barrier_place(&barrier);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Check if our parent process might already have
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * died? */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (getppid() == parent_pid) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer sigset_t ss;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_se(sigemptyset(&ss) >= 0);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_se(sigaddset(&ss, SIGTERM) >= 0);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer for (;;) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (sigwait(&ss, &sig) < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (errno == EINTR)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer continue;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer goto child_finish;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer }
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer assert(sig == SIGTERM);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer break;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer }
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer }
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* If our parent died we'll end the session */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (getppid() != parent_pid) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer pam_code = pam_close_session(handle, flags);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (pam_code != PAM_SUCCESS)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer goto child_finish;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer }
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = 0;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer child_finish:
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer pam_end(handle, pam_code | flags);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer _exit(r);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer }
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer barrier_set_role(&barrier, BARRIER_PARENT);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
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 handle = NULL;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* Unblock SIGTERM again in the parent */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer assert_se(sigprocmask(SIG_SETMASK, &old_ss, NULL) >= 0);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
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 closelog();
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
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 if (!barrier_place_and_sync(&barrier))
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer log_error("PAM initialization failed");
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer *pam_env = e;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer e = NULL;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer return 0;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerfail:
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (pam_code != PAM_SUCCESS) {
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 } else {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer err = log_error_errno(err < 0 ? err : errno, "PAM failed: %m");
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer }
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (handle) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (close_session)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer pam_code = pam_close_session(handle, flags);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer pam_end(handle, pam_code | flags);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer }
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer strv_free(e);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer closelog();
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer return err;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer}
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer#endif
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerstatic void rename_process_from_path(const char *path) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer char process_name[11];
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer const char *p;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer size_t l;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
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
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer p = basename(path);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (isempty(p)) {
c53158818d8cdaf46b3f1b5299b9bda118a1043fThomas Hindoe Paaboel Andersen rename_process("(...)");
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer return;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer }
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer l = strlen(p);
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 Hoyer l = 8;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer }
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer process_name[0] = '(';
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer memcpy(process_name+1, p, l);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer process_name[1+l] = ')';
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer process_name[1+l+1] = 0;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer rename_process(process_name);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer}
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer#ifdef HAVE_SECCOMP
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerstatic int apply_seccomp(const ExecContext *c) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer uint32_t negative_action, action;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer scmp_filter_ctx *seccomp;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer Iterator i;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer void *id;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer int r;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer assert(c);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer negative_action = c->syscall_errno == 0 ? SCMP_ACT_KILL : SCMP_ACT_ERRNO(c->syscall_errno);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer seccomp = seccomp_init(c->syscall_whitelist ? negative_action : SCMP_ACT_ALLOW);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (!seccomp)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer return -ENOMEM;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (c->syscall_archs) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer SET_FOREACH(id, c->syscall_archs, i) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (r == -EEXIST)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer continue;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (r < 0)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer goto finish;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer }
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer } else {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = seccomp_add_secondary_archs(seccomp);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (r < 0)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer goto finish;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer }
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer action = c->syscall_whitelist ? SCMP_ACT_ALLOW : negative_action;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer SET_FOREACH(id, c->syscall_filter, i) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = seccomp_rule_add(seccomp, action, PTR_TO_INT(id) - 1, 0);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (r < 0)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer goto finish;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer }
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (r < 0)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer goto finish;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = seccomp_load(seccomp);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerfinish:
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer seccomp_release(seccomp);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer return r;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer}
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerstatic int apply_address_families(const ExecContext *c) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer scmp_filter_ctx *seccomp;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer Iterator i;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer int r;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer assert(c);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer seccomp = seccomp_init(SCMP_ACT_ALLOW);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (!seccomp)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer return -ENOMEM;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = seccomp_add_secondary_archs(seccomp);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (r < 0)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer goto finish;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (c->address_families_whitelist) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer int af, first = 0, last = 0;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer void *afp;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
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
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer SET_FOREACH(afp, c->address_families, i) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer af = PTR_TO_INT(afp);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (af <= 0 || af >= af_max())
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer continue;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (first == 0 || af < first)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer first = af;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (last == 0 || af > last)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer last = af;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer }
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer assert((first == 0) == (last == 0));
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (first == 0) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* No entries in the valid range, block everything */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = seccomp_rule_add(
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer seccomp,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer SCMP_ACT_ERRNO(EPROTONOSUPPORT),
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer SCMP_SYS(socket),
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer 0);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (r < 0)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer goto finish;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer } else {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* Block everything below the first entry */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = seccomp_rule_add(
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer seccomp,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer SCMP_ACT_ERRNO(EPROTONOSUPPORT),
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer SCMP_SYS(socket),
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer 1,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer SCMP_A0(SCMP_CMP_LT, first));
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (r < 0)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer goto finish;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* Block everything above the last entry */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = seccomp_rule_add(
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer seccomp,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer SCMP_ACT_ERRNO(EPROTONOSUPPORT),
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer SCMP_SYS(socket),
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer 1,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer SCMP_A0(SCMP_CMP_GT, last));
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (r < 0)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer goto finish;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Block everything between the first and last
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * entry */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer for (af = 1; af < af_max(); af++) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (set_contains(c->address_families, INT_TO_PTR(af)))
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer continue;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = seccomp_rule_add(
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer seccomp,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer SCMP_ACT_ERRNO(EPROTONOSUPPORT),
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer SCMP_SYS(socket),
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer 1,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer SCMP_A0(SCMP_CMP_EQ, af));
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer goto finish;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer } else {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer void *af;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
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
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer SET_FOREACH(af, c->address_families, i) {
1ecf6a2b4960229ad1d06c591b4776ddf065e834Harald Hoyer
2c393ed7611a586ef5665aa62453ec57c0f5fef6Evgeny Vereshchagin r = seccomp_rule_add(
1ecf6a2b4960229ad1d06c591b4776ddf065e834Harald Hoyer seccomp,
1ecf6a2b4960229ad1d06c591b4776ddf065e834Harald Hoyer SCMP_ACT_ERRNO(EPROTONOSUPPORT),
1ecf6a2b4960229ad1d06c591b4776ddf065e834Harald Hoyer SCMP_SYS(socket),
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer 1,
33a5e20ffaa2cbb2853f14265566bac66a7f9026Harald Hoyer SCMP_A0(SCMP_CMP_EQ, PTR_TO_INT(af)));
33a5e20ffaa2cbb2853f14265566bac66a7f9026Harald Hoyer if (r < 0)
33a5e20ffaa2cbb2853f14265566bac66a7f9026Harald Hoyer goto finish;
33a5e20ffaa2cbb2853f14265566bac66a7f9026Harald Hoyer }
33a5e20ffaa2cbb2853f14265566bac66a7f9026Harald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer goto finish;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = seccomp_load(seccomp);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerfinish:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer seccomp_release(seccomp);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return r;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier}
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer#endif
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic void do_idle_pipe_dance(int idle_pipe[4]) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(idle_pipe);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer idle_pipe[1] = safe_close(idle_pipe[1]);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer idle_pipe[2] = safe_close(idle_pipe[2]);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (idle_pipe[0] >= 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int r;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (idle_pipe[3] >= 0 && r == 0 /* timeout */) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer ssize_t n;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Signal systemd that we are bored and want to continue. */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer n = write(idle_pipe[3], "x", 1);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (n > 0)
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 }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer idle_pipe[0] = safe_close(idle_pipe[0]);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer }
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer idle_pipe[3] = safe_close(idle_pipe[3]);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer}
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int build_environment(
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const ExecContext *c,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer unsigned n_fds,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer char ** fd_names,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer usec_t watchdog_usec,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *home,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *username,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *shell,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer char ***ret) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer _cleanup_strv_free_ char **our_env = NULL;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer unsigned n_env = 0;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer char *x;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(c);
assert(ret);
our_env = new0(char*, 11);
if (!our_env)
return -ENOMEM;
if (n_fds > 0) {
_cleanup_free_ char *joined = NULL;
if (asprintf(&x, "LISTEN_PID="PID_FMT, getpid()) < 0)
return -ENOMEM;
our_env[n_env++] = x;
if (asprintf(&x, "LISTEN_FDS=%u", n_fds) < 0)
return -ENOMEM;
our_env[n_env++] = x;
joined = strv_join(fd_names, ":");
if (!joined)
return -ENOMEM;
x = strjoin("LISTEN_FDNAMES=", joined, NULL);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
}
if (watchdog_usec > 0) {
if (asprintf(&x, "WATCHDOG_PID="PID_FMT, getpid()) < 0)
return -ENOMEM;
our_env[n_env++] = x;
if (asprintf(&x, "WATCHDOG_USEC="USEC_FMT, watchdog_usec) < 0)
return -ENOMEM;
our_env[n_env++] = x;
}
if (home) {
x = strappend("HOME=", home);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
}
if (username) {
x = strappend("LOGNAME=", username);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
x = strappend("USER=", username);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
}
if (shell) {
x = strappend("SHELL=", shell);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
}
if (is_terminal_input(c->std_input) ||
c->std_output == EXEC_OUTPUT_TTY ||
c->std_error == EXEC_OUTPUT_TTY ||
c->tty_path) {
x = strdup(default_term_for_tty(tty_path(c)));
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
}
our_env[n_env++] = NULL;
assert(n_env <= 11);
*ret = our_env;
our_env = NULL;
return 0;
}
static int build_pass_environment(const ExecContext *c, char ***ret) {
_cleanup_strv_free_ char **pass_env = NULL;
size_t n_env = 0, n_bufsize = 0;
char **i;
STRV_FOREACH(i, c->pass_environment) {
_cleanup_free_ char *x = NULL;
char *v;
v = getenv(*i);
if (!v)
continue;
x = strjoin(*i, "=", v, NULL);
if (!x)
return -ENOMEM;
if (!GREEDY_REALLOC(pass_env, n_bufsize, n_env + 2))
return -ENOMEM;
pass_env[n_env++] = x;
pass_env[n_env] = NULL;
x = NULL;
}
*ret = pass_env;
pass_env = NULL;
return 0;
}
static bool exec_needs_mount_namespace(
const ExecContext *context,
const ExecParameters *params,
ExecRuntime *runtime) {
assert(context);
assert(params);
if (!strv_isempty(context->read_write_dirs) ||
!strv_isempty(context->read_only_dirs) ||
!strv_isempty(context->inaccessible_dirs))
return true;
if (context->mount_flags != 0)
return true;
if (context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir))
return true;
if (params->bus_endpoint_path)
return true;
if (context->private_devices ||
context->protect_system != PROTECT_SYSTEM_NO ||
context->protect_home != PROTECT_HOME_NO)
return true;
return false;
}
static int close_remaining_fds(
const ExecParameters *params,
ExecRuntime *runtime,
int socket_fd,
int *fds, unsigned n_fds) {
unsigned n_dont_close = 0;
int dont_close[n_fds + 7];
assert(params);
if (params->stdin_fd >= 0)
dont_close[n_dont_close++] = params->stdin_fd;
if (params->stdout_fd >= 0)
dont_close[n_dont_close++] = params->stdout_fd;
if (params->stderr_fd >= 0)
dont_close[n_dont_close++] = params->stderr_fd;
if (socket_fd >= 0)
dont_close[n_dont_close++] = socket_fd;
if (n_fds > 0) {
memcpy(dont_close + n_dont_close, fds, sizeof(int) * n_fds);
n_dont_close += n_fds;
}
if (params->bus_endpoint_fd >= 0)
dont_close[n_dont_close++] = params->bus_endpoint_fd;
if (runtime) {
if (runtime->netns_storage_socket[0] >= 0)
dont_close[n_dont_close++] = runtime->netns_storage_socket[0];
if (runtime->netns_storage_socket[1] >= 0)
dont_close[n_dont_close++] = runtime->netns_storage_socket[1];
}
return close_all_fds(dont_close, n_dont_close);
}
static int exec_child(
Unit *unit,
ExecCommand *command,
const ExecContext *context,
const ExecParameters *params,
ExecRuntime *runtime,
char **argv,
int socket_fd,
int *fds, unsigned n_fds,
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;
_cleanup_free_ char *mac_selinux_context_net = NULL;
const char *username = NULL, *home = NULL, *shell = NULL, *wd;
uid_t uid = UID_INVALID;
gid_t gid = GID_INVALID;
int i, r;
bool needs_mount_namespace;
assert(unit);
assert(command);
assert(context);
assert(params);
assert(exit_status);
rename_process_from_path(command->path);
/* We reset exactly these signals, since they are the
* only ones we set to SIG_IGN in the main daemon. All
* others we leave untouched because we set them to
* SIG_DFL or a valid handler initially, both of which
* will be demoted to SIG_DFL. */
(void) default_signals(SIGNALS_CRASH_HANDLER,
SIGNALS_IGNORE, -1);
if (context->ignore_sigpipe)
(void) ignore_signals(SIGPIPE, -1);
r = reset_signal_mask();
if (r < 0) {
*exit_status = EXIT_SIGNAL_MASK;
return r;
}
if (params->idle_pipe)
do_idle_pipe_dance(params->idle_pipe);
/* Close sockets very early to make sure we don't
* block init reexecution because it cannot bind its
* sockets */
log_forget_fds();
r = close_remaining_fds(params, runtime, socket_fd, fds, n_fds);
if (r < 0) {
*exit_status = EXIT_FDS;
return r;
}
if (!context->same_pgrp)
if (setsid() < 0) {
*exit_status = EXIT_SETSID;
return -errno;
}
exec_context_tty_reset(context);
if (params->confirm_spawn) {
char response;
r = ask_for_confirmation(&response, argv);
if (r == -ETIMEDOUT)
write_confirm_message("Confirmation question timed out, assuming positive response.\n");
else if (r < 0)
write_confirm_message("Couldn't ask confirmation question, assuming positive response: %s\n", strerror(-r));
else if (response == 's') {
write_confirm_message("Skipping execution.\n");
*exit_status = EXIT_CONFIRM;
return -ECANCELED;
} else if (response == 'n') {
write_confirm_message("Failing execution.\n");
*exit_status = 0;
return 0;
}
}
if (context->user) {
username = context->user;
r = get_user_creds(&username, &uid, &gid, &home, &shell);
if (r < 0) {
*exit_status = EXIT_USER;
return r;
}
}
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)
(void) fd_nonblock(socket_fd, false);
r = setup_input(context, params, socket_fd);
if (r < 0) {
*exit_status = EXIT_STDIN;
return r;
}
r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, basename(command->path), uid, gid);
if (r < 0) {
*exit_status = EXIT_STDOUT;
return r;
}
r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, basename(command->path), uid, gid);
if (r < 0) {
*exit_status = EXIT_STDERR;
return r;
}
if (params->cgroup_path) {
r = cg_attach_everywhere(params->cgroup_supported, params->cgroup_path, 0, NULL, NULL);
if (r < 0) {
*exit_status = EXIT_CGROUP;
return r;
}
}
if (context->oom_score_adjust_set) {
char t[DECIMAL_STR_MAX(context->oom_score_adjust)];
/* When we can't make this change due to EPERM, then
* let's silently skip over it. User namespaces
* prohibit write access to this file, and we
* shouldn't trip up over that. */
sprintf(t, "%i", context->oom_score_adjust);
r = write_string_file("/proc/self/oom_score_adj", t, 0);
if (r == -EPERM || r == -EACCES) {
log_open();
log_unit_debug_errno(unit, r, "Failed to adjust OOM setting, assuming containerized execution, ignoring: %m");
log_close();
} else if (r < 0) {
*exit_status = EXIT_OOM_ADJUST;
return -errno;
}
}
if (context->nice_set)
if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) {
*exit_status = EXIT_NICE;
return -errno;
}
if (context->cpu_sched_set) {
struct sched_param param = {
.sched_priority = context->cpu_sched_priority,
};
r = sched_setscheduler(0,
context->cpu_sched_policy |
(context->cpu_sched_reset_on_fork ?
SCHED_RESET_ON_FORK : 0),
&param);
if (r < 0) {
*exit_status = EXIT_SETSCHEDULER;
return -errno;
}
}
if (context->cpuset)
if (sched_setaffinity(0, CPU_ALLOC_SIZE(context->cpuset_ncpus), context->cpuset) < 0) {
*exit_status = EXIT_CPUAFFINITY;
return -errno;
}
if (context->ioprio_set)
if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) {
*exit_status = EXIT_IOPRIO;
return -errno;
}
if (context->timer_slack_nsec != NSEC_INFINITY)
if (prctl(PR_SET_TIMERSLACK, context->timer_slack_nsec) < 0) {
*exit_status = EXIT_TIMERSLACK;
return -errno;
}
if (context->personality != PERSONALITY_INVALID)
if (personality(context->personality) < 0) {
*exit_status = EXIT_PERSONALITY;
return -errno;
}
if (context->utmp_id)
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);
if (r < 0) {
*exit_status = EXIT_STDIN;
return r;
}
}
if (params->bus_endpoint_fd >= 0 && context->bus_endpoint) {
uid_t ep_uid = (uid == UID_INVALID) ? 0 : uid;
r = bus_kernel_set_endpoint_policy(params->bus_endpoint_fd, ep_uid, context->bus_endpoint);
if (r < 0) {
*exit_status = EXIT_BUS_ENDPOINT;
return r;
}
}
/* If delegation is enabled we'll pass ownership of the cgroup
* (but only in systemd's own controller hierarchy!) to the
* user of the new process. */
if (params->cgroup_path && context->user && params->cgroup_delegate) {
r = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, 0644, uid, gid);
if (r < 0) {
*exit_status = EXIT_CGROUP;
return r;
}
r = cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, 0755, uid, gid);
if (r < 0) {
*exit_status = EXIT_CGROUP;
return r;
}
}
if (!strv_isempty(context->runtime_directory) && params->runtime_prefix) {
char **rt;
STRV_FOREACH(rt, context->runtime_directory) {
_cleanup_free_ char *p;
p = strjoin(params->runtime_prefix, "/", *rt, NULL);
if (!p) {
*exit_status = EXIT_RUNTIME_DIRECTORY;
return -ENOMEM;
}
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;
}
}
}
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;
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 (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);
if (r < 0) {
*exit_status = EXIT_NETWORK;
return r;
}
}
needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime);
if (needs_mount_namespace) {
char *tmp = NULL, *var = NULL;
/* The runtime struct only contains the parent
* of the private /tmp, which is
* non-accessible to world users. Inside of it
* there's a /tmp that is sticky, and that's
* the one we want to use here. */
if (context->private_tmp && runtime) {
if (runtime->tmp_dir)
tmp = strjoina(runtime->tmp_dir, "/tmp");
if (runtime->var_tmp_dir)
var = strjoina(runtime->var_tmp_dir, "/tmp");
}
r = setup_namespace(
params->apply_chroot ? context->root_directory : NULL,
context->read_write_dirs,
context->read_only_dirs,
context->inaccessible_dirs,
tmp,
var,
params->bus_endpoint_path,
context->private_devices,
context->protect_home,
context->protect_system,
context->mount_flags);
/* If we couldn't set up the namespace this is
* probably due to a missing capability. In this case,
* silently proceeed. */
if (r == -EPERM || r == -EACCES) {
log_open();
log_unit_debug_errno(unit, r, "Failed to set up namespace, assuming containerized execution, ignoring: %m");
log_close();
} else if (r < 0) {
*exit_status = EXIT_NAMESPACE;
return r;
}
}
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) {
*exit_status = EXIT_CHROOT;
return -errno;
}
if (chdir(wd) < 0 &&
!context->working_directory_missing_ok) {
*exit_status = EXIT_CHDIR;
return -errno;
}
} else {
const char *d;
d = strjoina(strempty(context->root_directory), "/", strempty(wd));
if (chdir(d) < 0 &&
!context->working_directory_missing_ok) {
*exit_status = EXIT_CHDIR;
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);
if (r < 0) {
*exit_status = EXIT_SELINUX_CONTEXT;
return r;
}
}
#endif
/* We repeat the fd closing here, to make sure that
* nothing is leaked from the PAM modules. Note that
* we are more aggressive this time since socket_fd
* and the netns fds we don't need anymore. The custom
* endpoint fd was needed to upload the policy and can
* now be closed as well. */
r = close_all_fds(fds, n_fds);
if (r >= 0)
r = shift_fds(fds, n_fds);
if (r >= 0)
r = flags_fds(fds, n_fds, context->non_blocking);
if (r < 0) {
*exit_status = EXIT_FDS;
return r;
}
if (params->apply_permissions) {
int secure_bits = context->secure_bits;
for (i = 0; i < _RLIMIT_MAX; i++) {
if (!context->rlimit[i])
continue;
if (setrlimit_closest(i, context->rlimit[i]) < 0) {
*exit_status = EXIT_LIMITS;
return -errno;
}
}
if (!cap_test_all(context->capability_bounding_set)) {
r = capability_bounding_set_drop(context->capability_bounding_set, false);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
return r;
}
}
/* This is done before enforce_user, but ambient set
* does not survive over setresuid() if keep_caps is not set. */
if (context->capability_ambient_set != 0) {
r = capability_ambient_set_apply(context->capability_ambient_set, true);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
return r;
}
if (context->capabilities) {
/* The capabilities in ambient set need to be also in the inherited
* set. If they aren't, trying to get them will fail. Add the ambient
* set inherited capabilities to the capability set in the context.
* This is needed because if capabilities are set (using "Capabilities="
* keyword), they will override whatever we set now. */
r = capability_update_inherited_set(context->capabilities, context->capability_ambient_set);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
return r;
}
}
}
if (context->user) {
r = enforce_user(context, uid);
if (r < 0) {
*exit_status = EXIT_USER;
return r;
}
if (context->capability_ambient_set != 0) {
/* Fix the ambient capabilities after user change. */
r = capability_ambient_set_apply(context->capability_ambient_set, false);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
return r;
}
/* If we were asked to change user and ambient capabilities
* were requested, we had to add keep-caps to the securebits
* so that we would maintain the inherited capability set
* through the setresuid(). Make sure that the bit is added
* also to the context secure_bits so that we don't try to
* drop the bit away next. */
secure_bits |= 1<<SECURE_KEEP_CAPS;
}
}
/* PR_GET_SECUREBITS is not privileged, while
* PR_SET_SECUREBITS is. So to suppress
* potential EPERMs we'll try not to call
* PR_SET_SECUREBITS unless necessary. */
if (prctl(PR_GET_SECUREBITS) != secure_bits)
if (prctl(PR_SET_SECUREBITS, secure_bits) < 0) {
*exit_status = EXIT_SECUREBITS;
return -errno;
}
if (context->capabilities)
if (cap_set_proc(context->capabilities) < 0) {
*exit_status = EXIT_CAPABILITIES;
return -errno;
}
if (context->no_new_privileges)
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
*exit_status = EXIT_NO_NEW_PRIVILEGES;
return -errno;
}
#ifdef HAVE_SECCOMP
if (context->address_families_whitelist ||
!set_isempty(context->address_families)) {
r = apply_address_families(context);
if (r < 0) {
*exit_status = EXIT_ADDRESS_FAMILIES;
return r;
}
}
if (context->syscall_whitelist ||
!set_isempty(context->syscall_filter) ||
!set_isempty(context->syscall_archs)) {
r = apply_seccomp(context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
return r;
}
}
#endif
#ifdef HAVE_SELINUX
if (mac_selinux_use()) {
char *exec_context = mac_selinux_context_net ?: context->selinux_context;
if (exec_context) {
r = setexeccon(exec_context);
if (r < 0) {
*exit_status = EXIT_SELINUX_CONTEXT;
return r;
}
}
}
#endif
#ifdef HAVE_APPARMOR
if (context->apparmor_profile && mac_apparmor_use()) {
r = aa_change_onexec(context->apparmor_profile);
if (r < 0 && !context->apparmor_profile_ignore) {
*exit_status = EXIT_APPARMOR_PROFILE;
return -errno;
}
}
#endif
}
r = build_environment(context, n_fds, params->fd_names, params->watchdog_usec, home, username, shell, &our_env);
if (r < 0) {
*exit_status = EXIT_MEMORY;
return r;
}
r = build_pass_environment(context, &pass_env);
if (r < 0) {
*exit_status = EXIT_MEMORY;
return r;
}
final_env = strv_env_merge(6,
params->environment,
our_env,
pass_env,
context->environment,
files_env,
pam_env,
NULL);
if (!final_env) {
*exit_status = EXIT_MEMORY;
return -ENOMEM;
}
final_argv = replace_env_argv(argv, final_env);
if (!final_argv) {
*exit_status = EXIT_MEMORY;
return -ENOMEM;
}
final_env = strv_env_clean(final_env);
if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
_cleanup_free_ char *line;
line = exec_command_line(final_argv);
if (line) {
log_open();
log_struct(LOG_DEBUG,
LOG_UNIT_ID(unit),
"EXECUTABLE=%s", command->path,
LOG_UNIT_MESSAGE(unit, "Executing: %s", line),
NULL);
log_close();
}
}
execve(command->path, final_argv, final_env);
*exit_status = EXIT_EXEC;
return -errno;
}
int exec_spawn(Unit *unit,
ExecCommand *command,
const ExecContext *context,
const ExecParameters *params,
ExecRuntime *runtime,
pid_t *ret) {
_cleanup_strv_free_ char **files_env = NULL;
int *fds = NULL; unsigned n_fds = 0;
_cleanup_free_ char *line = NULL;
int socket_fd, r;
char **argv;
pid_t pid;
assert(unit);
assert(command);
assert(context);
assert(ret);
assert(params);
assert(params->fds || params->n_fds <= 0);
if (context->std_input == EXEC_INPUT_SOCKET ||
context->std_output == EXEC_OUTPUT_SOCKET ||
context->std_error == EXEC_OUTPUT_SOCKET) {
if (params->n_fds != 1) {
log_unit_error(unit, "Got more than one socket.");
return -EINVAL;
}
socket_fd = params->fds[0];
} else {
socket_fd = -1;
fds = params->fds;
n_fds = params->n_fds;
}
r = exec_context_load_environment(unit, context, &files_env);
if (r < 0)
return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
argv = params->argv ?: command->argv;
line = exec_command_line(argv);
if (!line)
return log_oom();
log_struct(LOG_DEBUG,
LOG_UNIT_ID(unit),
LOG_UNIT_MESSAGE(unit, "About to execute: %s", line),
"EXECUTABLE=%s", command->path,
NULL);
pid = fork();
if (pid < 0)
return log_unit_error_errno(unit, errno, "Failed to fork: %m");
if (pid == 0) {
int exit_status;
r = exec_child(unit,
command,
context,
params,
runtime,
argv,
socket_fd,
fds, n_fds,
files_env,
&exit_status);
if (r < 0) {
log_open();
log_struct_errno(LOG_ERR, r,
LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
LOG_UNIT_ID(unit),
LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m",
exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD),
command->path),
"EXECUTABLE=%s", command->path,
NULL);
}
_exit(exit_status);
}
log_unit_debug(unit, "Forked %s as "PID_FMT, command->path, pid);
/* We add the new process to the cgroup both in the child (so
* that we can be sure that no user code is ever executed
* outside of the cgroup) and in the parent (so that we can be
* sure that when we kill the cgroup the process will be
* killed too). */
if (params->cgroup_path)
(void) cg_attach(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, pid);
exec_status_start(&command->exec_status, pid);
*ret = pid;
return 0;
}
void exec_context_init(ExecContext *c) {
assert(c);
c->umask = 0022;
c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
c->cpu_sched_policy = SCHED_OTHER;
c->syslog_priority = LOG_DAEMON|LOG_INFO;
c->syslog_level_prefix = true;
c->ignore_sigpipe = true;
c->timer_slack_nsec = NSEC_INFINITY;
c->personality = PERSONALITY_INVALID;
c->runtime_directory_mode = 0755;
c->capability_bounding_set = CAP_ALL;
}
void exec_context_done(ExecContext *c) {
unsigned l;
assert(c);
c->environment = strv_free(c->environment);
c->environment_files = strv_free(c->environment_files);
c->pass_environment = strv_free(c->pass_environment);
for (l = 0; l < ELEMENTSOF(c->rlimit); l++)
c->rlimit[l] = mfree(c->rlimit[l]);
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);
c->supplementary_groups = strv_free(c->supplementary_groups);
c->pam_name = mfree(c->pam_name);
if (c->capabilities) {
cap_free(c->capabilities);
c->capabilities = 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);
c->utmp_id = mfree(c->utmp_id);
c->selinux_context = mfree(c->selinux_context);
c->apparmor_profile = mfree(c->apparmor_profile);
c->syscall_filter = set_free(c->syscall_filter);
c->syscall_archs = set_free(c->syscall_archs);
c->address_families = set_free(c->address_families);
c->runtime_directory = strv_free(c->runtime_directory);
bus_endpoint_free(c->bus_endpoint);
c->bus_endpoint = NULL;
}
int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_prefix) {
char **i;
assert(c);
if (!runtime_prefix)
return 0;
STRV_FOREACH(i, c->runtime_directory) {
_cleanup_free_ char *p;
p = strjoin(runtime_prefix, "/", *i, NULL);
if (!p)
return -ENOMEM;
/* We execute this synchronously, since we need to be
* sure this is gone when we start the service
* next. */
(void) rm_rf(p, REMOVE_ROOT);
}
return 0;
}
void exec_command_done(ExecCommand *c) {
assert(c);
c->path = mfree(c->path);
c->argv = strv_free(c->argv);
}
void exec_command_done_array(ExecCommand *c, unsigned n) {
unsigned i;
for (i = 0; i < n; i++)
exec_command_done(c+i);
}
ExecCommand* exec_command_free_list(ExecCommand *c) {
ExecCommand *i;
while ((i = c)) {
LIST_REMOVE(command, c, i);
exec_command_done(i);
free(i);
}
return NULL;
}
void exec_command_free_array(ExecCommand **c, unsigned n) {
unsigned i;
for (i = 0; i < n; i++)
c[i] = exec_command_free_list(c[i]);
}
typedef struct InvalidEnvInfo {
Unit *unit;
const char *path;
} InvalidEnvInfo;
static void invalid_env(const char *p, void *userdata) {
InvalidEnvInfo *info = userdata;
log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
}
int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) {
char **i, **r = NULL;
assert(c);
assert(l);
STRV_FOREACH(i, c->environment_files) {
char *fn;
int k;
bool ignore = false;
char **p;
_cleanup_globfree_ glob_t pglob = {};
int count, n;
fn = *i;
if (fn[0] == '-') {
ignore = true;
fn ++;
}
if (!path_is_absolute(fn)) {
if (ignore)
continue;
strv_free(r);
return -EINVAL;
}
/* Filename supports globbing, take all matching files */
errno = 0;
if (glob(fn, 0, NULL, &pglob) != 0) {
if (ignore)
continue;
strv_free(r);
return errno > 0 ? -errno : -EINVAL;
}
count = pglob.gl_pathc;
if (count == 0) {
if (ignore)
continue;
strv_free(r);
return -EINVAL;
}
for (n = 0; n < count; n++) {
k = load_env_file(NULL, pglob.gl_pathv[n], NULL, &p);
if (k < 0) {
if (ignore)
continue;
strv_free(r);
return k;
}
/* Log invalid environment variables with filename */
if (p) {
InvalidEnvInfo info = {
.unit = unit,
.path = pglob.gl_pathv[n]
};
p = strv_env_clean_with_callback(p, invalid_env, &info);
}
if (r == NULL)
r = p;
else {
char **m;
m = strv_env_merge(2, r, p);
strv_free(r);
strv_free(p);
if (!m)
return -ENOMEM;
r = m;
}
}
}
*l = r;
return 0;
}
static bool tty_may_match_dev_console(const char *tty) {
_cleanup_free_ char *active = NULL;
char *console;
if (startswith(tty, "/dev/"))
tty += 5;
/* trivial identity? */
if (streq(tty, "console"))
return true;
console = resolve_dev_console(&active);
/* if we could not resolve, assume it may */
if (!console)
return true;
/* "tty0" means the active VC, so it may be the same sometimes */
return streq(console, tty) || (streq(console, "tty0") && tty_is_vc(tty));
}
bool exec_context_may_touch_console(ExecContext *ec) {
return (ec->tty_reset || ec->tty_vhangup || ec->tty_vt_disallocate ||
is_terminal_input(ec->std_input) ||
is_terminal_output(ec->std_output) ||
is_terminal_output(ec->std_error)) &&
tty_may_match_dev_console(tty_path(ec));
}
static void strv_fprintf(FILE *f, char **l) {
char **g;
assert(f);
STRV_FOREACH(g, l)
fprintf(f, " %s", *g);
}
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
char **e, **d;
unsigned i;
assert(c);
assert(f);
prefix = strempty(prefix);
fprintf(f,
"%sUMask: %04o\n"
"%sWorkingDirectory: %s\n"
"%sRootDirectory: %s\n"
"%sNonBlocking: %s\n"
"%sPrivateTmp: %s\n"
"%sPrivateNetwork: %s\n"
"%sPrivateDevices: %s\n"
"%sProtectHome: %s\n"
"%sProtectSystem: %s\n"
"%sIgnoreSIGPIPE: %s\n",
prefix, c->umask,
prefix, c->working_directory ? c->working_directory : "/",
prefix, c->root_directory ? c->root_directory : "/",
prefix, yes_no(c->non_blocking),
prefix, yes_no(c->private_tmp),
prefix, yes_no(c->private_network),
prefix, yes_no(c->private_devices),
prefix, protect_home_to_string(c->protect_home),
prefix, protect_system_to_string(c->protect_system),
prefix, yes_no(c->ignore_sigpipe));
STRV_FOREACH(e, c->environment)
fprintf(f, "%sEnvironment: %s\n", prefix, *e);
STRV_FOREACH(e, c->environment_files)
fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
STRV_FOREACH(e, c->pass_environment)
fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
fprintf(f, "%sRuntimeDirectoryMode: %04o\n", prefix, c->runtime_directory_mode);
STRV_FOREACH(d, c->runtime_directory)
fprintf(f, "%sRuntimeDirectory: %s\n", prefix, *d);
if (c->nice_set)
fprintf(f,
"%sNice: %i\n",
prefix, c->nice);
if (c->oom_score_adjust_set)
fprintf(f,
"%sOOMScoreAdjust: %i\n",
prefix, c->oom_score_adjust);
for (i = 0; i < RLIM_NLIMITS; i++)
if (c->rlimit[i]) {
fprintf(f, "%s%s: " RLIM_FMT "\n",
prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
fprintf(f, "%s%sSoft: " RLIM_FMT "\n",
prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
}
if (c->ioprio_set) {
_cleanup_free_ char *class_str = NULL;
ioprio_class_to_string_alloc(IOPRIO_PRIO_CLASS(c->ioprio), &class_str);
fprintf(f,
"%sIOSchedulingClass: %s\n"
"%sIOPriority: %i\n",
prefix, strna(class_str),
prefix, (int) IOPRIO_PRIO_DATA(c->ioprio));
}
if (c->cpu_sched_set) {
_cleanup_free_ char *policy_str = NULL;
sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
fprintf(f,
"%sCPUSchedulingPolicy: %s\n"
"%sCPUSchedulingPriority: %i\n"
"%sCPUSchedulingResetOnFork: %s\n",
prefix, strna(policy_str),
prefix, c->cpu_sched_priority,
prefix, yes_no(c->cpu_sched_reset_on_fork));
}
if (c->cpuset) {
fprintf(f, "%sCPUAffinity:", prefix);
for (i = 0; i < c->cpuset_ncpus; i++)
if (CPU_ISSET_S(i, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset))
fprintf(f, " %u", i);
fputs("\n", f);
}
if (c->timer_slack_nsec != NSEC_INFINITY)
fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec);
fprintf(f,
"%sStandardInput: %s\n"
"%sStandardOutput: %s\n"
"%sStandardError: %s\n",
prefix, exec_input_to_string(c->std_input),
prefix, exec_output_to_string(c->std_output),
prefix, exec_output_to_string(c->std_error));
if (c->tty_path)
fprintf(f,
"%sTTYPath: %s\n"
"%sTTYReset: %s\n"
"%sTTYVHangup: %s\n"
"%sTTYVTDisallocate: %s\n",
prefix, c->tty_path,
prefix, yes_no(c->tty_reset),
prefix, yes_no(c->tty_vhangup),
prefix, yes_no(c->tty_vt_disallocate));
if (c->std_output == EXEC_OUTPUT_SYSLOG ||
c->std_output == EXEC_OUTPUT_KMSG ||
c->std_output == EXEC_OUTPUT_JOURNAL ||
c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE ||
c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
c->std_output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE ||
c->std_error == EXEC_OUTPUT_SYSLOG ||
c->std_error == EXEC_OUTPUT_KMSG ||
c->std_error == EXEC_OUTPUT_JOURNAL ||
c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE ||
c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
c->std_error == EXEC_OUTPUT_JOURNAL_AND_CONSOLE) {
_cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
fprintf(f,
"%sSyslogFacility: %s\n"
"%sSyslogLevel: %s\n",
prefix, strna(fac_str),
prefix, strna(lvl_str));
}
if (c->capabilities) {
_cleanup_cap_free_charp_ char *t;
t = cap_to_text(c->capabilities, NULL);
if (t)
fprintf(f, "%sCapabilities: %s\n", prefix, t);
}
if (c->secure_bits)
fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n",
prefix,
(c->secure_bits & 1<<SECURE_KEEP_CAPS) ? " keep-caps" : "",
(c->secure_bits & 1<<SECURE_KEEP_CAPS_LOCKED) ? " keep-caps-locked" : "",
(c->secure_bits & 1<<SECURE_NO_SETUID_FIXUP) ? " no-setuid-fixup" : "",
(c->secure_bits & 1<<SECURE_NO_SETUID_FIXUP_LOCKED) ? " no-setuid-fixup-locked" : "",
(c->secure_bits & 1<<SECURE_NOROOT) ? " noroot" : "",
(c->secure_bits & 1<<SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
if (c->capability_bounding_set != CAP_ALL) {
unsigned long l;
fprintf(f, "%sCapabilityBoundingSet:", prefix);
for (l = 0; l <= cap_last_cap(); l++)
if (c->capability_bounding_set & (UINT64_C(1) << l))
fprintf(f, " %s", strna(capability_to_name(l)));
fputs("\n", f);
}
if (c->capability_ambient_set != 0) {
unsigned long l;
fprintf(f, "%sAmbientCapabilities:", prefix);
for (l = 0; l <= cap_last_cap(); l++)
if (c->capability_ambient_set & (UINT64_C(1) << l))
fprintf(f, " %s", strna(capability_to_name(l)));
fputs("\n", f);
}
if (c->user)
fprintf(f, "%sUser: %s\n", prefix, c->user);
if (c->group)
fprintf(f, "%sGroup: %s\n", prefix, c->group);
if (strv_length(c->supplementary_groups) > 0) {
fprintf(f, "%sSupplementaryGroups:", prefix);
strv_fprintf(f, c->supplementary_groups);
fputs("\n", f);
}
if (c->pam_name)
fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
if (strv_length(c->read_write_dirs) > 0) {
fprintf(f, "%sReadWriteDirs:", prefix);
strv_fprintf(f, c->read_write_dirs);
fputs("\n", f);
}
if (strv_length(c->read_only_dirs) > 0) {
fprintf(f, "%sReadOnlyDirs:", prefix);
strv_fprintf(f, c->read_only_dirs);
fputs("\n", f);
}
if (strv_length(c->inaccessible_dirs) > 0) {
fprintf(f, "%sInaccessibleDirs:", prefix);
strv_fprintf(f, c->inaccessible_dirs);
fputs("\n", f);
}
if (c->utmp_id)
fprintf(f,
"%sUtmpIdentifier: %s\n",
prefix, c->utmp_id);
if (c->selinux_context)
fprintf(f,
"%sSELinuxContext: %s%s\n",
prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
if (c->personality != PERSONALITY_INVALID)
fprintf(f,
"%sPersonality: %s\n",
prefix, strna(personality_to_string(c->personality)));
if (c->syscall_filter) {
#ifdef HAVE_SECCOMP
Iterator j;
void *id;
bool first = true;
#endif
fprintf(f,
"%sSystemCallFilter: ",
prefix);
if (!c->syscall_whitelist)
fputc('~', f);
#ifdef HAVE_SECCOMP
SET_FOREACH(id, c->syscall_filter, j) {
_cleanup_free_ char *name = NULL;
if (first)
first = false;
else
fputc(' ', f);
name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
fputs(strna(name), f);
}
#endif
fputc('\n', f);
}
if (c->syscall_archs) {
#ifdef HAVE_SECCOMP
Iterator j;
void *id;
#endif
fprintf(f,
"%sSystemCallArchitectures:",
prefix);
#ifdef HAVE_SECCOMP
SET_FOREACH(id, c->syscall_archs, j)
fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1)));
#endif
fputc('\n', f);
}
if (c->syscall_errno > 0)
fprintf(f,
"%sSystemCallErrorNumber: %s\n",
prefix, strna(errno_to_name(c->syscall_errno)));
if (c->apparmor_profile)
fprintf(f,
"%sAppArmorProfile: %s%s\n",
prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
}
bool exec_context_maintains_privileges(ExecContext *c) {
assert(c);
/* Returns true if the process forked off would run run under
* an unchanged UID or as root. */
if (!c->user)
return true;
if (streq(c->user, "root") || streq(c->user, "0"))
return true;
return false;
}
void exec_status_start(ExecStatus *s, pid_t pid) {
assert(s);
zero(*s);
s->pid = pid;
dual_timestamp_get(&s->start_timestamp);
}
void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status) {
assert(s);
if (s->pid && s->pid != pid)
zero(*s);
s->pid = pid;
dual_timestamp_get(&s->exit_timestamp);
s->code = code;
s->status = status;
if (context) {
if (context->utmp_id)
utmp_put_dead_process(context->utmp_id, pid, code, status);
exec_context_tty_reset(context);
}
}
void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) {
char buf[FORMAT_TIMESTAMP_MAX];
assert(s);
assert(f);
if (s->pid <= 0)
return;
prefix = strempty(prefix);
fprintf(f,
"%sPID: "PID_FMT"\n",
prefix, s->pid);
if (s->start_timestamp.realtime > 0)
fprintf(f,
"%sStart Timestamp: %s\n",
prefix, format_timestamp(buf, sizeof(buf), s->start_timestamp.realtime));
if (s->exit_timestamp.realtime > 0)
fprintf(f,
"%sExit Timestamp: %s\n"
"%sExit Code: %s\n"
"%sExit Status: %i\n",
prefix, format_timestamp(buf, sizeof(buf), s->exit_timestamp.realtime),
prefix, sigchld_code_to_string(s->code),
prefix, s->status);
}
char *exec_command_line(char **argv) {
size_t k;
char *n, *p, **a;
bool first = true;
assert(argv);
k = 1;
STRV_FOREACH(a, argv)
k += strlen(*a)+3;
if (!(n = new(char, k)))
return NULL;
p = n;
STRV_FOREACH(a, argv) {
if (!first)
*(p++) = ' ';
else
first = false;
if (strpbrk(*a, WHITESPACE)) {
*(p++) = '\'';
p = stpcpy(p, *a);
*(p++) = '\'';
} else
p = stpcpy(p, *a);
}
*p = 0;
/* FIXME: this doesn't really handle arguments that have
* spaces and ticks in them */
return n;
}
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
_cleanup_free_ char *cmd = NULL;
const char *prefix2;
assert(c);
assert(f);
prefix = strempty(prefix);
prefix2 = strjoina(prefix, "\t");
cmd = exec_command_line(c->argv);
fprintf(f,
"%sCommand Line: %s\n",
prefix, cmd ? cmd : strerror(ENOMEM));
exec_status_dump(&c->exec_status, f, prefix2);
}
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
assert(f);
prefix = strempty(prefix);
LIST_FOREACH(command, c, c)
exec_command_dump(c, f, prefix);
}
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
ExecCommand *end;
assert(l);
assert(e);
if (*l) {
/* It's kind of important, that we keep the order here */
LIST_FIND_TAIL(command, *l, end);
LIST_INSERT_AFTER(command, *l, end, e);
} else
*l = e;
}
int exec_command_set(ExecCommand *c, const char *path, ...) {
va_list ap;
char **l, *p;
assert(c);
assert(path);
va_start(ap, path);
l = strv_new_ap(path, ap);
va_end(ap);
if (!l)
return -ENOMEM;
p = strdup(path);
if (!p) {
strv_free(l);
return -ENOMEM;
}
free(c->path);
c->path = p;
strv_free(c->argv);
c->argv = l;
return 0;
}
int exec_command_append(ExecCommand *c, const char *path, ...) {
_cleanup_strv_free_ char **l = NULL;
va_list ap;
int r;
assert(c);
assert(path);
va_start(ap, path);
l = strv_new_ap(path, ap);
va_end(ap);
if (!l)
return -ENOMEM;
r = strv_extend_strv(&c->argv, l, false);
if (r < 0)
return r;
return 0;
}
static int exec_runtime_allocate(ExecRuntime **rt) {
if (*rt)
return 0;
*rt = new0(ExecRuntime, 1);
if (!*rt)
return -ENOMEM;
(*rt)->n_ref = 1;
(*rt)->netns_storage_socket[0] = (*rt)->netns_storage_socket[1] = -1;
return 0;
}
int exec_runtime_make(ExecRuntime **rt, ExecContext *c, const char *id) {
int r;
assert(rt);
assert(c);
assert(id);
if (*rt)
return 1;
if (!c->private_network && !c->private_tmp)
return 0;
r = exec_runtime_allocate(rt);
if (r < 0)
return r;
if (c->private_network && (*rt)->netns_storage_socket[0] < 0) {
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, (*rt)->netns_storage_socket) < 0)
return -errno;
}
if (c->private_tmp && !(*rt)->tmp_dir) {
r = setup_tmp_dirs(id, &(*rt)->tmp_dir, &(*rt)->var_tmp_dir);
if (r < 0)
return r;
}
return 1;
}
ExecRuntime *exec_runtime_ref(ExecRuntime *r) {
assert(r);
assert(r->n_ref > 0);
r->n_ref++;
return r;
}
ExecRuntime *exec_runtime_unref(ExecRuntime *r) {
if (!r)
return NULL;
assert(r->n_ref > 0);
r->n_ref--;
if (r->n_ref > 0)
return NULL;
free(r->tmp_dir);
free(r->var_tmp_dir);
safe_close_pair(r->netns_storage_socket);
free(r);
return NULL;
}
int exec_runtime_serialize(Unit *u, ExecRuntime *rt, FILE *f, FDSet *fds) {
assert(u);
assert(f);
assert(fds);
if (!rt)
return 0;
if (rt->tmp_dir)
unit_serialize_item(u, f, "tmp-dir", rt->tmp_dir);
if (rt->var_tmp_dir)
unit_serialize_item(u, f, "var-tmp-dir", rt->var_tmp_dir);
if (rt->netns_storage_socket[0] >= 0) {
int copy;
copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
if (copy < 0)
return copy;
unit_serialize_item_format(u, f, "netns-socket-0", "%i", copy);
}
if (rt->netns_storage_socket[1] >= 0) {
int copy;
copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
if (copy < 0)
return copy;
unit_serialize_item_format(u, f, "netns-socket-1", "%i", copy);
}
return 0;
}
int exec_runtime_deserialize_item(Unit *u, ExecRuntime **rt, const char *key, const char *value, FDSet *fds) {
int r;
assert(rt);
assert(key);
assert(value);
if (streq(key, "tmp-dir")) {
char *copy;
r = exec_runtime_allocate(rt);
if (r < 0)
return log_oom();
copy = strdup(value);
if (!copy)
return log_oom();
free((*rt)->tmp_dir);
(*rt)->tmp_dir = copy;
} else if (streq(key, "var-tmp-dir")) {
char *copy;
r = exec_runtime_allocate(rt);
if (r < 0)
return log_oom();
copy = strdup(value);
if (!copy)
return log_oom();
free((*rt)->var_tmp_dir);
(*rt)->var_tmp_dir = copy;
} else if (streq(key, "netns-socket-0")) {
int fd;
r = exec_runtime_allocate(rt);
if (r < 0)
return log_oom();
if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse netns socket value: %s", value);
else {
safe_close((*rt)->netns_storage_socket[0]);
(*rt)->netns_storage_socket[0] = fdset_remove(fds, fd);
}
} else if (streq(key, "netns-socket-1")) {
int fd;
r = exec_runtime_allocate(rt);
if (r < 0)
return log_oom();
if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse netns socket value: %s", value);
else {
safe_close((*rt)->netns_storage_socket[1]);
(*rt)->netns_storage_socket[1] = fdset_remove(fds, fd);
}
} else
return 0;
return 1;
}
static void *remove_tmpdir_thread(void *p) {
_cleanup_free_ char *path = p;
(void) rm_rf(path, REMOVE_ROOT|REMOVE_PHYSICAL);
return NULL;
}
void exec_runtime_destroy(ExecRuntime *rt) {
int r;
if (!rt)
return;
/* If there are multiple users of this, let's leave the stuff around */
if (rt->n_ref > 1)
return;
if (rt->tmp_dir) {
log_debug("Spawning thread to nuke %s", rt->tmp_dir);
r = asynchronous_job(remove_tmpdir_thread, rt->tmp_dir);
if (r < 0) {
log_warning_errno(r, "Failed to nuke %s: %m", rt->tmp_dir);
free(rt->tmp_dir);
}
rt->tmp_dir = NULL;
}
if (rt->var_tmp_dir) {
log_debug("Spawning thread to nuke %s", rt->var_tmp_dir);
r = asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir);
if (r < 0) {
log_warning_errno(r, "Failed to nuke %s: %m", rt->var_tmp_dir);
free(rt->var_tmp_dir);
}
rt->var_tmp_dir = NULL;
}
safe_close_pair(rt->netns_storage_socket);
}
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
[EXEC_INPUT_NULL] = "null",
[EXEC_INPUT_TTY] = "tty",
[EXEC_INPUT_TTY_FORCE] = "tty-force",
[EXEC_INPUT_TTY_FAIL] = "tty-fail",
[EXEC_INPUT_SOCKET] = "socket"
};
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
[EXEC_OUTPUT_INHERIT] = "inherit",
[EXEC_OUTPUT_NULL] = "null",
[EXEC_OUTPUT_TTY] = "tty",
[EXEC_OUTPUT_SYSLOG] = "syslog",
[EXEC_OUTPUT_SYSLOG_AND_CONSOLE] = "syslog+console",
[EXEC_OUTPUT_KMSG] = "kmsg",
[EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console",
[EXEC_OUTPUT_JOURNAL] = "journal",
[EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
[EXEC_OUTPUT_SOCKET] = "socket"
};
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);