execute.c revision cd972d6911b0e1403e0a11408be732fc3a513f2b
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/***
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen This file is part of systemd.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen Copyright 2010 Lennart Poettering
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen systemd is free software; you can redistribute it and/or modify it
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen under the terms of the GNU Lesser General Public License as published by
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen (at your option) any later version.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen systemd is distributed in the hope that it will be useful, but
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen Lesser General Public License for more details.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen You should have received a copy of the GNU Lesser General Public License
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen***/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <errno.h>
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include <fcntl.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <unistd.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <string.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <signal.h>
b5efdb8af40ea759a1ea584c1bc44ecc81dd00ceLennart Poettering#include <sys/socket.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <sys/un.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <sys/prctl.h>
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include <sys/stat.h>
3ffd4af22052963e7a29431721ee204e634bea75Lennart Poettering#include <grp.h>
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include <poll.h>
f4f15635ec05293ffcc83a5b39f624bbabbd8fd0Lennart Poettering#include <glob.h>
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include <sys/personality.h>
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#ifdef HAVE_PAM
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering#include <security/pam_appl.h>
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#endif
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#ifdef HAVE_SELINUX
8b43440b7ef4b81c69c31de7ff820dc07a780254Lennart Poettering#include <selinux/selinux.h>
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#endif
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#ifdef HAVE_SECCOMP
ee104e11e303499a637c5cd8157bd12ad5cc116dLennart Poettering#include <seccomp.h>
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#endif
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#ifdef HAVE_APPARMOR
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <sys/apparmor.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#endif
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "execute.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "strv.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "macro.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "capability.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "util.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "log.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "sd-messages.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "ioprio.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "securebits.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "namespace.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "exit-status.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "missing.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "utmp-wtmp.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "def.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "path-util.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "env-util.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "fileio.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "unit.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "async.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "selinux-util.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "errno-list.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "af-list.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "mkdir.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "smack-util.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "bus-endpoint.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "cap-list.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#ifdef HAVE_APPARMOR
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "apparmor-util.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#endif
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#ifdef HAVE_SECCOMP
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "seccomp-util.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#endif
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/* This assumes there is a 'tty' group */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#define TTY_MODE 0620
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#define SNDBUF_SIZE (8*1024*1024)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int shift_fds(int fds[], unsigned n_fds) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int start, restart_from;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (n_fds <= 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Modifies the fds array! (sorts it) */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(fds);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen start = 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen for (;;) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int i;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen restart_from = -1;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen for (i = start; i < (int) n_fds; i++) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int nfd;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Already at right index? */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fds[i] == i+3)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen continue;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if ((nfd = fcntl(fds[i], F_DUPFD, i+3)) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen safe_close(fds[i]);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fds[i] = nfd;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Hmm, the fd we wanted isn't free? Then
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * let's remember that and try again from here */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (nfd != i+3 && restart_from < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen restart_from = i;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (restart_from < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen break;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen start = restart_from;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int flags_fds(const int fds[], unsigned n_fds, bool nonblock) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen unsigned i;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (n_fds <= 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(fds);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen for (i = 0; i < n_fds; i++) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if ((r = fd_nonblock(fds[i], nonblock)) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* We unconditionally drop FD_CLOEXEC from the fds,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * since after all we want to pass these fds to our
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * children */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if ((r = fd_cloexec(fds[i], false)) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen_pure_ static const char *tty_path(const ExecContext *context) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(context);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (context->tty_path)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return context->tty_path;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return "/dev/console";
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic void exec_context_tty_reset(const ExecContext *context) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(context);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (context->tty_vhangup)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen terminal_vhangup(tty_path(context));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (context->tty_reset)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen reset_terminal(tty_path(context));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (context->tty_vt_disallocate && context->tty_path)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen vt_disallocate(context->tty_path);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic bool is_terminal_output(ExecOutput o) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen o == EXEC_OUTPUT_TTY ||
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE ||
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen o == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int open_null_as(int flags, int nfd) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int fd, r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(nfd >= 0);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fd = open("/dev/null", flags|O_NOCTTY);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fd < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fd != nfd) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = dup2(fd, nfd) < 0 ? -errno : nfd;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen safe_close(fd);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen } else
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering r = nfd;
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen union sockaddr_union sa = {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen .un.sun_family = AF_UNIX,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen .un.sun_path = "/run/systemd/journal/stdout",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen };
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen uid_t olduid = UID_INVALID;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen gid_t oldgid = GID_INVALID;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (gid != GID_INVALID) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen oldgid = getgid();
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = setegid(gid);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (uid != UID_INVALID) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen olduid = getuid();
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = seteuid(uid);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto restore_gid;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* If we fail to restore the uid or gid, things will likely
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fail later on. This should only happen if an LSM interferes. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (uid != UID_INVALID)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen (void) seteuid(olduid);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen restore_gid:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (gid != GID_INVALID)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen (void) setegid(oldgid);
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd, uid_t uid, gid_t gid) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int fd, r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(context);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(output < _EXEC_OUTPUT_MAX);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(ident);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(nfd >= 0);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fd = socket(AF_UNIX, SOCK_STREAM, 0);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fd < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = connect_journal_socket(fd, uid, gid);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (shutdown(fd, SHUT_RD) < 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen safe_close(fd);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fd_inc_sndbuf(fd, SNDBUF_SIZE);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen dprintf(fd,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "%s\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "%s\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "%i\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "%i\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "%i\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "%i\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "%i\n",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen context->syslog_identifier ? context->syslog_identifier : ident,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen unit_id,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen context->syslog_priority,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen !!context->syslog_level_prefix,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen is_terminal_output(output));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fd != nfd) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = dup2(fd, nfd) < 0 ? -errno : nfd;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen safe_close(fd);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen } else
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = nfd;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int open_terminal_as(const char *path, mode_t mode, int nfd) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int fd, r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(path);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(nfd >= 0);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if ((fd = open_terminal(path, mode | O_NOCTTY)) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return fd;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fd != nfd) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = dup2(fd, nfd) < 0 ? -errno : nfd;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen safe_close(fd);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen } else
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = nfd;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic bool is_terminal_input(ExecInput i) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen i == EXEC_INPUT_TTY ||
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen i == EXEC_INPUT_TTY_FORCE ||
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen i == EXEC_INPUT_TTY_FAIL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (is_terminal_input(std_input) && !apply_tty_stdin)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return EXEC_INPUT_NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return EXEC_INPUT_NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return std_input;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int fixup_output(ExecOutput std_output, int socket_fd) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (std_output == EXEC_OUTPUT_SOCKET && socket_fd < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return EXEC_OUTPUT_INHERIT;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return std_output;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int setup_input(const ExecContext *context, int socket_fd, bool apply_tty_stdin) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen ExecInput i;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(context);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen switch (i) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen case EXEC_INPUT_NULL:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return open_null_as(O_RDONLY, STDIN_FILENO);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen case EXEC_INPUT_TTY:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen case EXEC_INPUT_TTY_FORCE:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen case EXEC_INPUT_TTY_FAIL: {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int fd, r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fd = acquire_terminal(tty_path(context),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen i == EXEC_INPUT_TTY_FAIL,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen i == EXEC_INPUT_TTY_FORCE,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen false,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen USEC_INFINITY);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fd < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return fd;
401cb61499f446c7c1579a160eeef435afd525fdTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fd != STDIN_FILENO) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = dup2(fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen safe_close(fd);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen } else
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = STDIN_FILENO;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen case EXEC_INPUT_SOCKET:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen default:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert_not_reached("Unknown input type");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int setup_output(const ExecContext *context, int fileno, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin, uid_t uid, gid_t gid) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen ExecOutput o;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen ExecInput i;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(context);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(ident);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen o = fixup_output(context->std_output, socket_fd);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fileno == STDERR_FILENO) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen ExecOutput e;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen e = fixup_output(context->std_error, socket_fd);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* This expects the input and output are already set up */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Don't change the stderr file descriptor if we inherit all
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * the way and are not on a tty */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (e == EXEC_OUTPUT_INHERIT &&
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen o == EXEC_OUTPUT_INHERIT &&
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen i == EXEC_INPUT_NULL &&
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen !is_terminal_input(context->std_input) &&
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen getppid () != 1)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return fileno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Duplicate from stdout if possible */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (e == o || e == EXEC_OUTPUT_INHERIT)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return dup2(STDOUT_FILENO, fileno) < 0 ? -errno : fileno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen o = e;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen } else if (o == EXEC_OUTPUT_INHERIT) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* If input got downgraded, inherit the original value */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input))
4df4fd1127df4b70f78d952a37a51a8c69e3243fTom Gundersen return open_terminal_as(tty_path(context), O_WRONLY, fileno);
4df4fd1127df4b70f78d952a37a51a8c69e3243fTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* If the input is connected to anything that's not a /dev/null, inherit that... */
4df4fd1127df4b70f78d952a37a51a8c69e3243fTom Gundersen if (i != EXEC_INPUT_NULL)
de9b34b6d4250056ae2c483cf22844880504bcccTom Gundersen return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
de9b34b6d4250056ae2c483cf22844880504bcccTom Gundersen /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
de9b34b6d4250056ae2c483cf22844880504bcccTom Gundersen if (getppid() != 1)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return fileno;
4df4fd1127df4b70f78d952a37a51a8c69e3243fTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* We need to open /dev/null here anew, to get the right access mode. */
4df4fd1127df4b70f78d952a37a51a8c69e3243fTom Gundersen return open_null_as(O_WRONLY, fileno);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
4df4fd1127df4b70f78d952a37a51a8c69e3243fTom Gundersen switch (o) {
4df4fd1127df4b70f78d952a37a51a8c69e3243fTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen case EXEC_OUTPUT_NULL:
4df4fd1127df4b70f78d952a37a51a8c69e3243fTom Gundersen return open_null_as(O_WRONLY, fileno);
de9b34b6d4250056ae2c483cf22844880504bcccTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen case EXEC_OUTPUT_TTY:
de9b34b6d4250056ae2c483cf22844880504bcccTom Gundersen if (is_terminal_input(i))
de9b34b6d4250056ae2c483cf22844880504bcccTom Gundersen return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
4df4fd1127df4b70f78d952a37a51a8c69e3243fTom Gundersen /* We don't reset the terminal if this is just about output */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return open_terminal_as(tty_path(context), O_WRONLY, fileno);
4df4fd1127df4b70f78d952a37a51a8c69e3243fTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen case EXEC_OUTPUT_SYSLOG:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen case EXEC_OUTPUT_KMSG:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen case EXEC_OUTPUT_KMSG_AND_CONSOLE:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen case EXEC_OUTPUT_JOURNAL:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = connect_logger_as(context, o, ident, unit_id, fileno, uid, gid);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_unit_struct(unit_id,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen LOG_ERR,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen LOG_MESSAGE("Failed to connect %s of %s to the journal socket: %s",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fileno == STDOUT_FILENO ? "stdout" : "stderr",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen unit_id, strerror(-r)),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen LOG_ERRNO(-r),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = open_null_as(O_WRONLY, fileno);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen case EXEC_OUTPUT_SOCKET:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(socket_fd >= 0);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return dup2(socket_fd, fileno) < 0 ? -errno : fileno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen default:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert_not_reached("Unknown error type");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int chown_terminal(int fd, uid_t uid) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen struct stat st;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(fd >= 0);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* This might fail. What matters are the results. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen (void) fchown(fd, uid, -1);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen (void) fchmod(fd, TTY_MODE);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fstat(fd, &st) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (st.st_uid != uid || (st.st_mode & 0777) != TTY_MODE)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -EPERM;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int setup_confirm_stdio(int *_saved_stdin,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int *_saved_stdout) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int fd = -1, saved_stdin, saved_stdout = -1, r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(_saved_stdin);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(_saved_stdout);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen saved_stdin = fcntl(STDIN_FILENO, F_DUPFD, 3);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (saved_stdin < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (saved_stdout < 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto fail;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fd = acquire_terminal(
401cb61499f446c7c1579a160eeef435afd525fdTom Gundersen "/dev/console",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen false,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen false,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen false,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen DEFAULT_CONFIRM_USEC);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fd < 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = fd;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto fail;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = chown_terminal(fd, getuid());
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto fail;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (dup2(fd, STDIN_FILENO) < 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto fail;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (dup2(fd, STDOUT_FILENO) < 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto fail;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fd >= 2)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen safe_close(fd);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen *_saved_stdin = saved_stdin;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen *_saved_stdout = saved_stdout;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenfail:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen safe_close(saved_stdout);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen safe_close(saved_stdin);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen safe_close(fd);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen_printf_(1, 2) static int write_confirm_message(const char *format, ...) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_close_ int fd = -1;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen va_list ap;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(format);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fd < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return fd;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen va_start(ap, format);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen vdprintf(fd, format, ap);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen va_end(ap);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int restore_confirm_stdio(int *saved_stdin,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int *saved_stdout) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int r = 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(saved_stdin);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(saved_stdout);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen release_terminal();
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (*saved_stdin >= 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (dup2(*saved_stdin, STDIN_FILENO) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (*saved_stdout >= 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (dup2(*saved_stdout, STDOUT_FILENO) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen safe_close(*saved_stdin);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen safe_close(*saved_stdout);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int ask_for_confirmation(char *response, char **argv) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int saved_stdout = -1, saved_stdin = -1, r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_free_ char *line = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = setup_confirm_stdio(&saved_stdin, &saved_stdout);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen line = exec_command_line(argv);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!line)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -ENOMEM;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = ask_char(response, "yns", "Execute %s? [Yes, No, Skip] ", line);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen restore_confirm_stdio(&saved_stdin, &saved_stdout);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int enforce_groups(const ExecContext *context, const char *username, gid_t gid) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen bool keep_groups = false;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(context);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Lookup and set GID and supplementary group list. Here too
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * we avoid NSS lookups for gid=0. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (context->group || username) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (context->group) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *g = context->group;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if ((r = get_group_creds(&g, &gid)) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* First step, initialize groups from /etc/groups */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (username && gid != 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (initgroups(username, gid) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen keep_groups = true;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Second step, set our gids */
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen if (setresgid(gid, gid, gid) < 0)
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen return -errno;
d854ba50a82f28b776c670d27156f0e9881fde8aMartin Pitt }
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (context->supplementary_groups) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int ngroups_max, k;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen gid_t *gids;
aa20f49a1c5816e6e7e97f2e2ba209be47f3c0a3Tom Gundersen char **i;
aa20f49a1c5816e6e7e97f2e2ba209be47f3c0a3Tom Gundersen
aa20f49a1c5816e6e7e97f2e2ba209be47f3c0a3Tom Gundersen /* Final step, initialize any manually set supplementary groups */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert_se((ngroups_max = (int) sysconf(_SC_NGROUPS_MAX)) > 0);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!(gids = new(gid_t, ngroups_max)))
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -ENOMEM;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (keep_groups) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if ((k = getgroups(ngroups_max, gids)) < 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen free(gids);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen } else
d854ba50a82f28b776c670d27156f0e9881fde8aMartin Pitt k = 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen STRV_FOREACH(i, context->supplementary_groups) {
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen const char *g;
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen if (k >= ngroups_max) {
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen free(gids);
d854ba50a82f28b776c670d27156f0e9881fde8aMartin Pitt return -E2BIG;
d854ba50a82f28b776c670d27156f0e9881fde8aMartin Pitt }
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen
d854ba50a82f28b776c670d27156f0e9881fde8aMartin Pitt g = *i;
d854ba50a82f28b776c670d27156f0e9881fde8aMartin Pitt r = get_group_creds(&g, gids+k);
d854ba50a82f28b776c670d27156f0e9881fde8aMartin Pitt if (r < 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen free(gids);
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen return r;
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen }
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen k++;
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen }
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen if (setgroups(k, gids) < 0) {
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen free(gids);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen free(gids);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int enforce_user(const ExecContext *context, uid_t uid) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(context);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
aa20f49a1c5816e6e7e97f2e2ba209be47f3c0a3Tom Gundersen /* Sets (but doesn't lookup) the uid and make sure we keep the
aa20f49a1c5816e6e7e97f2e2ba209be47f3c0a3Tom Gundersen * capabilities while doing so. */
aa20f49a1c5816e6e7e97f2e2ba209be47f3c0a3Tom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (context->capabilities) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_cap_free_ cap_t d = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen static const cap_value_t bits[] = {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen CAP_SETUID, /* Necessary so that we can run setresuid() below */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen };
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* First step: If we need to keep capabilities but
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * drop privileges we need to make sure we keep our
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * caps, while we drop privileges. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (uid != 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int sb = context->secure_bits | 1<<SECURE_KEEP_CAPS;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (prctl(PR_GET_SECUREBITS) != sb)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (prctl(PR_SET_SECUREBITS, sb) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Second step: set the capabilities. This will reduce
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * the capabilities to the minimum we need. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen d = cap_dup(context->capabilities);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!d)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (cap_set_proc(d) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Third step: actually set the uids */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (setresuid(uid, uid, uid) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* At this point we should have all necessary capabilities but
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen are otherwise a normal user. However, the caps might got
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen corrupted due to the setresuid() so we need clean them up
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen later. This is done outside of this call. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#ifdef HAVE_PAM
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int null_conv(
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int num_msg,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const struct pam_message **msg,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen struct pam_response **resp,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen void *appdata_ptr) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* We don't support conversations */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return PAM_CONV_ERR;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int setup_pam(
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *name,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *user,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen uid_t uid,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *tty,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen char ***pam_env,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int fds[], unsigned n_fds) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen static const struct pam_conv conv = {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen .conv = null_conv,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen .appdata_ptr = NULL
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen };
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen pam_handle_t *handle = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen sigset_t ss, old_ss;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int pam_code = PAM_SUCCESS;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int err;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen char **e = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen bool close_session = false;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen pid_t pam_pid = 0, parent_pid;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int flags = 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(name);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(user);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(pam_env);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* We set up PAM in the parent process, then fork. The child
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * will then stay around until killed via PR_GET_PDEATHSIG or
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * systemd via the cgroup logic. It will then remove the PAM
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * session again. The parent process will exec() the actual
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * daemon. We do things this way to ensure that the main PID
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * of the daemon is the one we initially fork()ed. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (log_get_max_level() < LOG_DEBUG)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen flags |= PAM_SILENT;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen pam_code = pam_start(name, user, &conv, &handle);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (pam_code != PAM_SUCCESS) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen handle = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto fail;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (tty) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen pam_code = pam_set_item(handle, PAM_TTY, tty);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (pam_code != PAM_SUCCESS)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto fail;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen pam_code = pam_acct_mgmt(handle, flags);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (pam_code != PAM_SUCCESS)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto fail;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen pam_code = pam_open_session(handle, flags);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (pam_code != PAM_SUCCESS)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto fail;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen close_session = true;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen e = pam_getenvlist(handle);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!e) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen pam_code = PAM_BUF_ERR;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto fail;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Block SIGTERM, so that we know that it won't get lost in
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * the child */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (sigemptyset(&ss) < 0 ||
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen sigaddset(&ss, SIGTERM) < 0 ||
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen sigprocmask(SIG_BLOCK, &ss, &old_ss) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto fail;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen parent_pid = getpid();
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen pam_pid = fork();
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (pam_pid < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto fail;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (pam_pid == 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int sig;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int r = EXIT_PAM;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* The child's job is to reset the PAM session on
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * termination */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* This string must fit in 10 chars (i.e. the length
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * of "/sbin/init"), to look pretty in /bin/ps */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen rename_process("(sd-pam)");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Make sure we don't keep open the passed fds in this
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen child. We assume that otherwise only those fds are
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen open here that have been opened by PAM. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen close_many(fds, n_fds);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Drop privileges - we don't need any to pam_close_session
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * and this will make PR_SET_PDEATHSIG work in most cases.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * If this fails, ignore the error - but expect sd-pam threads
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * to fail to exit normally */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (setresuid(uid, uid, uid) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error_errno(r, "Error: Failed to setresuid() in sd-pam: %m");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Wait until our parent died. This will only work if
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * the above setresuid() succeeds, otherwise the kernel
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * will not allow unprivileged parents kill their privileged
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * children this way. We rely on the control groups kill logic
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * to do the rest for us. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto child_finish;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Check if our parent process might already have
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * died? */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (getppid() == parent_pid) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen for (;;) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (sigwait(&ss, &sig) < 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (errno == EINTR)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen continue;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto child_finish;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(sig == SIGTERM);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen break;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* If our parent died we'll end the session */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (getppid() != parent_pid) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen pam_code = pam_close_session(handle, flags);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (pam_code != PAM_SUCCESS)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto child_finish;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen child_finish:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen pam_end(handle, pam_code | flags);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _exit(r);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* If the child was forked off successfully it will do all the
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * cleanups, so forget about the handle here. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen handle = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Unblock SIGTERM again in the parent */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (sigprocmask(SIG_SETMASK, &old_ss, NULL) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto fail;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* We close the log explicitly here, since the PAM modules
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * might have opened it, but we don't want this fd around. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen closelog();
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen *pam_env = e;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen e = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenfail:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (pam_code != PAM_SUCCESS) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("PAM failed: %s", pam_strerror(handle, pam_code));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen err = -EPERM; /* PAM errors do not map to errno */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen } else {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error_errno(errno, "PAM failed: %m");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen err = -errno;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (handle) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (close_session)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen pam_code = pam_close_session(handle, flags);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen pam_end(handle, pam_code | flags);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen strv_free(e);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen closelog();
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (pam_pid > 1) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen kill(pam_pid, SIGTERM);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen kill(pam_pid, SIGCONT);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return err;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#endif
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic void rename_process_from_path(const char *path) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen char process_name[11];
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *p;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen size_t l;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* This resulting string must fit in 10 chars (i.e. the length
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * of "/sbin/init") to look pretty in /bin/ps */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen p = basename(path);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (isempty(p)) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen rename_process("(...)");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen l = strlen(p);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (l > 8) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* The end of the process name is usually more
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * interesting, since the first bit might just be
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * "systemd-" */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen p = p + l - 8;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen l = 8;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen process_name[0] = '(';
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen memcpy(process_name+1, p, l);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen process_name[1+l] = ')';
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen process_name[1+l+1] = 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen rename_process(process_name);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#ifdef HAVE_SECCOMP
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int apply_seccomp(const ExecContext *c) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen uint32_t negative_action, action;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen scmp_filter_ctx *seccomp;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen Iterator i;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen void *id;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(c);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen negative_action = c->syscall_errno == 0 ? SCMP_ACT_KILL : SCMP_ACT_ERRNO(c->syscall_errno);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen seccomp = seccomp_init(c->syscall_whitelist ? negative_action : SCMP_ACT_ALLOW);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!seccomp)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -ENOMEM;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (c->syscall_archs) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SET_FOREACH(id, c->syscall_archs, i) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r == -EEXIST)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen continue;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto finish;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen } else {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = seccomp_add_secondary_archs(seccomp);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto finish;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen action = c->syscall_whitelist ? SCMP_ACT_ALLOW : negative_action;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SET_FOREACH(id, c->syscall_filter, i) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = seccomp_rule_add(seccomp, action, PTR_TO_INT(id) - 1, 0);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto finish;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto finish;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = seccomp_load(seccomp);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenfinish:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen seccomp_release(seccomp);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int apply_address_families(const ExecContext *c) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen scmp_filter_ctx *seccomp;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen Iterator i;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int r;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(c);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen seccomp = seccomp_init(SCMP_ACT_ALLOW);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!seccomp)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -ENOMEM;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = seccomp_add_secondary_archs(seccomp);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto finish;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (c->address_families_whitelist) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int af, first = 0, last = 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen void *afp;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* If this is a whitelist, we first block the address
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * families that are out of range and then everything
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * that is not in the set. First, we find the lowest
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * and highest address family in the set. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SET_FOREACH(afp, c->address_families, i) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen af = PTR_TO_INT(afp);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (af <= 0 || af >= af_max())
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen continue;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (first == 0 || af < first)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen first = af;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (last == 0 || af > last)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen last = af;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert((first == 0) == (last == 0));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (first == 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* No entries in the valid range, block everything */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = seccomp_rule_add(
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen seccomp,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SCMP_ACT_ERRNO(EPROTONOSUPPORT),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SCMP_SYS(socket),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen 0);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto finish;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen } else {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Block everything below the first entry */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = seccomp_rule_add(
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen seccomp,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SCMP_ACT_ERRNO(EPROTONOSUPPORT),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SCMP_SYS(socket),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen 1,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SCMP_A0(SCMP_CMP_LT, first));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto finish;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Block everything above the last entry */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = seccomp_rule_add(
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering seccomp,
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering SCMP_ACT_ERRNO(EPROTONOSUPPORT),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SCMP_SYS(socket),
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering 1,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SCMP_A0(SCMP_CMP_GT, last));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto finish;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Block everything between the first and last
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * entry */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen for (af = 1; af < af_max(); af++) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (set_contains(c->address_families, INT_TO_PTR(af)))
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen continue;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = seccomp_rule_add(
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen seccomp,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SCMP_ACT_ERRNO(EPROTONOSUPPORT),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SCMP_SYS(socket),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen 1,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SCMP_A0(SCMP_CMP_EQ, af));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen goto finish;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
107f2e2526d476c6cc9b81a690391c111027d641Tom Gundersen
107f2e2526d476c6cc9b81a690391c111027d641Tom Gundersen } else {
107f2e2526d476c6cc9b81a690391c111027d641Tom Gundersen void *af;
107f2e2526d476c6cc9b81a690391c111027d641Tom Gundersen
107f2e2526d476c6cc9b81a690391c111027d641Tom Gundersen /* If this is a blacklist, then generate one rule for
107f2e2526d476c6cc9b81a690391c111027d641Tom Gundersen * each address family that are then combined in OR
* checks. */
SET_FOREACH(af, c->address_families, i) {
r = seccomp_rule_add(
seccomp,
SCMP_ACT_ERRNO(EPROTONOSUPPORT),
SCMP_SYS(socket),
1,
SCMP_A0(SCMP_CMP_EQ, PTR_TO_INT(af)));
if (r < 0)
goto finish;
}
}
r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
if (r < 0)
goto finish;
r = seccomp_load(seccomp);
finish:
seccomp_release(seccomp);
return r;
}
#endif
static void do_idle_pipe_dance(int idle_pipe[4]) {
assert(idle_pipe);
safe_close(idle_pipe[1]);
safe_close(idle_pipe[2]);
if (idle_pipe[0] >= 0) {
int r;
r = fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC);
if (idle_pipe[3] >= 0 && r == 0 /* timeout */) {
/* Signal systemd that we are bored and want to continue. */
r = write(idle_pipe[3], "x", 1);
if (r > 0)
/* Wait for systemd to react to the signal above. */
fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC);
}
safe_close(idle_pipe[0]);
}
safe_close(idle_pipe[3]);
}
static int build_environment(
const ExecContext *c,
unsigned n_fds,
usec_t watchdog_usec,
const char *home,
const char *username,
const char *shell,
char ***ret) {
_cleanup_strv_free_ char **our_env = NULL;
unsigned n_env = 0;
char *x;
assert(c);
assert(ret);
our_env = new0(char*, 10);
if (!our_env)
return -ENOMEM;
if (n_fds > 0) {
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;
}
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 <= 10);
*ret = our_env;
our_env = NULL;
return 0;
}
static int exec_child(
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, **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;
unsigned n_dont_close = 0;
int dont_close[n_fds + 4];
uid_t uid = UID_INVALID;
gid_t gid = GID_INVALID;
int i, r;
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. */
default_signals(SIGNALS_CRASH_HANDLER,
SIGNALS_IGNORE, -1);
if (context->ignore_sigpipe)
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();
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];
}
r = close_all_fds(dont_close, n_dont_close);
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 a socket is connected to STDIN/STDOUT/STDERR, we
* must sure to drop O_NONBLOCK */
if (socket_fd >= 0)
fd_nonblock(socket_fd, false);
r = setup_input(context, socket_fd, params->apply_tty_stdin);
if (r < 0) {
*exit_status = EXIT_STDIN;
return r;
}
r = setup_output(context, STDOUT_FILENO, socket_fd, basename(command->path), params->unit_id, params->apply_tty_stdin, uid, gid);
if (r < 0) {
*exit_status = EXIT_STDOUT;
return r;
}
r = setup_output(context, STDERR_FILENO, socket_fd, basename(command->path), params->unit_id, params->apply_tty_stdin, 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);
if (r == -EPERM || r == -EACCES) {
log_open();
log_unit_debug_errno(params->unit_id, 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 != 0xffffffffUL)
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);
if (context->user && is_terminal_input(context->std_input)) {
r = chown_terminal(STDIN_FILENO, uid);
if (r < 0) {
*exit_status = EXIT_STDIN;
return r;
}
}
#ifdef ENABLE_KDBUS
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;
}
}
#endif
/* 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_safe_label(p, context->runtime_directory_mode, uid, gid);
if (r < 0) {
*exit_status = EXIT_RUNTIME_DIRECTORY;
return r;
}
}
}
if (params->apply_permissions) {
r = enforce_groups(context, username, gid);
if (r < 0) {
*exit_status = EXIT_GROUP;
return r;
}
}
umask(context->umask);
#ifdef HAVE_PAM
if (params->apply_permissions && context->pam_name && username) {
r = setup_pam(context->pam_name, username, uid, context->tty_path, &pam_env, fds, n_fds);
if (r < 0) {
*exit_status = EXIT_PAM;
return r;
}
}
#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;
}
}
if (!strv_isempty(context->read_write_dirs) ||
!strv_isempty(context->read_only_dirs) ||
!strv_isempty(context->inaccessible_dirs) ||
context->mount_flags != 0 ||
(context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir)) ||
params->bus_endpoint_path ||
context->private_devices ||
context->protect_system != PROTECT_SYSTEM_NO ||
context->protect_home != PROTECT_HOME_NO) {
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(
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(params->unit_id, r, "Failed to set up namespace, assuming containerized execution, ignoring: %m");
log_close();
} else if (r < 0) {
*exit_status = EXIT_NAMESPACE;
return r;
}
}
if (params->apply_chroot) {
if (context->root_directory)
if (chroot(context->root_directory) < 0) {
*exit_status = EXIT_CHROOT;
return -errno;
}
if (chdir(context->working_directory ?: "/") < 0 &&
!context->working_directory_missing_ok) {
*exit_status = EXIT_CHDIR;
return -errno;
}
} else {
_cleanup_free_ char *d = NULL;
if (asprintf(&d, "%s/%s",
context->root_directory ?: "",
context->working_directory ?: "") < 0) {
*exit_status = EXIT_MEMORY;
return -ENOMEM;
}
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) {
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 (context->capability_bounding_set_drop) {
r = capability_bounding_set_drop(context->capability_bounding_set_drop, false);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
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;
}
}
#endif
if (context->user) {
r = enforce_user(context, uid);
if (r < 0) {
*exit_status = EXIT_USER;
return r;
}
}
/* 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) != context->secure_bits)
if (prctl(PR_SET_SECUREBITS, context->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->watchdog_usec, home, username, shell, &our_env);
if (r < 0) {
*exit_status = EXIT_MEMORY;
return r;
}
final_env = strv_env_merge(5,
params->environment,
our_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_unit_struct(params->unit_id,
LOG_DEBUG,
"EXECUTABLE=%s", command->path,
LOG_MESSAGE("Executing: %s", line),
NULL);
log_close();
}
}
execve(command->path, final_argv, final_env);
*exit_status = EXIT_EXEC;
return -errno;
}
int exec_spawn(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(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(params->unit_id, "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(context, params->unit_id, &files_env);
if (r < 0)
return log_unit_error_errno(params->unit_id, r, "Failed to load environment files: %m");
argv = params->argv ?: command->argv;
line = exec_command_line(argv);
if (!line)
return log_oom();
log_unit_struct(params->unit_id,
LOG_DEBUG,
"EXECUTABLE=%s", command->path,
LOG_MESSAGE("About to execute: %s", line),
NULL);
pid = fork();
if (pid < 0)
return log_unit_error_errno(params->unit_id, r, "Failed to fork: %m");
if (pid == 0) {
int exit_status;
r = exec_child(command,
context,
params,
runtime,
argv,
socket_fd,
fds, n_fds,
files_env,
&exit_status);
if (r < 0) {
log_open();
log_unit_struct(params->unit_id,
LOG_ERR,
LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
"EXECUTABLE=%s", command->path,
LOG_MESSAGE("Failed at step %s spawning %s: %s",
exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD),
command->path, strerror(-r)),
LOG_ERRNO(r),
NULL);
}
_exit(exit_status);
}
log_unit_debug(params->unit_id, "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)
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 = 0xffffffffUL;
c->runtime_directory_mode = 0755;
}
void exec_context_done(ExecContext *c) {
unsigned l;
assert(c);
strv_free(c->environment);
c->environment = NULL;
strv_free(c->environment_files);
c->environment_files = NULL;
for (l = 0; l < ELEMENTSOF(c->rlimit); l++) {
free(c->rlimit[l]);
c->rlimit[l] = NULL;
}
free(c->working_directory);
c->working_directory = NULL;
free(c->root_directory);
c->root_directory = NULL;
free(c->tty_path);
c->tty_path = NULL;
free(c->syslog_identifier);
c->syslog_identifier = NULL;
free(c->user);
c->user = NULL;
free(c->group);
c->group = NULL;
strv_free(c->supplementary_groups);
c->supplementary_groups = NULL;
free(c->pam_name);
c->pam_name = NULL;
if (c->capabilities) {
cap_free(c->capabilities);
c->capabilities = NULL;
}
strv_free(c->read_only_dirs);
c->read_only_dirs = NULL;
strv_free(c->read_write_dirs);
c->read_write_dirs = NULL;
strv_free(c->inaccessible_dirs);
c->inaccessible_dirs = NULL;
if (c->cpuset)
CPU_FREE(c->cpuset);
free(c->utmp_id);
c->utmp_id = NULL;
free(c->selinux_context);
c->selinux_context = NULL;
free(c->apparmor_profile);
c->apparmor_profile = NULL;
set_free(c->syscall_filter);
c->syscall_filter = NULL;
set_free(c->syscall_archs);
c->syscall_archs = NULL;
set_free(c->address_families);
c->address_families = NULL;
strv_free(c->runtime_directory);
c->runtime_directory = NULL;
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. */
rm_rf(p, false, true, false);
}
return 0;
}
void exec_command_done(ExecCommand *c) {
assert(c);
free(c->path);
c->path = NULL;
strv_free(c->argv);
c->argv = NULL;
}
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 {
const char *unit_id;
const char *path;
} InvalidEnvInfo;
static void invalid_env(const char *p, void *userdata) {
InvalidEnvInfo *info = userdata;
log_unit_error(info->unit_id, "Ignoring invalid environment assignment '%s': %s", p, info->path);
}
int exec_context_load_environment(const ExecContext *c, const char *unit_id, 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 ? -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_id = unit_id,
.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;
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);
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);
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_drop) {
unsigned long l;
fprintf(f, "%sCapabilityBoundingSet:", prefix);
for (l = 0; l <= cap_last_cap(); l++)
if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) 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 != 0xffffffffUL)
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);
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) {
free(r->tmp_dir);
free(r->var_tmp_dir);
safe_close_pair(r->netns_storage_socket);
free(r);
}
return NULL;
}
int exec_runtime_serialize(ExecRuntime *rt, Unit *u, 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(ExecRuntime **rt, Unit *u, 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 r;
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 r;
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 r;
if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd))
log_unit_debug(u->id, "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 r;
if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd))
log_unit_debug(u->id, "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;
rm_rf_dangerous(path, false, true, false);
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);