chiark / gitweb /
systemd: use structured logging for unit changes
[elogind.git] / src / core / execute.c
index c0e8f9e01302677ea1fd89016ea0885ce530b90d..e502c2490f1301b4ae5cc78d57d37a523b278a7a 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/fs.h>
 #include <linux/oom.h>
 #include <sys/poll.h>
+#include <linux/seccomp-bpf.h>
 
 #ifdef HAVE_PAM
 #include <security/pam_appl.h>
@@ -60,6 +61,9 @@
 #include "def.h"
 #include "loopback-setup.h"
 #include "path-util.h"
+#include "syscall-list.h"
+#include "sd-id128.h"
+#include "sd-messages.h"
 
 #define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
 
@@ -924,6 +928,59 @@ static void rename_process_from_path(const char *path) {
         rename_process(process_name);
 }
 
+static int apply_seccomp(uint32_t *syscall_filter) {
+        static const struct sock_filter header[] = {
+                VALIDATE_ARCHITECTURE,
+                EXAMINE_SYSCALL
+        };
+        static const struct sock_filter footer[] = {
+                _KILL_PROCESS
+        };
+
+        int i;
+        unsigned n;
+        struct sock_filter *f;
+        struct sock_fprog prog;
+
+        assert(syscall_filter);
+
+        /* First: count the syscalls to check for */
+        for (i = 0, n = 0; i < syscall_max(); i++)
+                if (syscall_filter[i >> 4] & (1 << (i & 31)))
+                        n++;
+
+        /* Second: build the filter program from a header the syscall
+         * matches and the footer */
+        f = alloca(sizeof(struct sock_filter) * (ELEMENTSOF(header) + 2*n + ELEMENTSOF(footer)));
+        memcpy(f, header, sizeof(header));
+
+        for (i = 0, n = 0; i < syscall_max(); i++)
+                if (syscall_filter[i >> 4] & (1 << (i & 31))) {
+                        struct sock_filter item[] = {
+                                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, i, 0, 1),
+                                BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
+                        };
+
+                        assert_cc(ELEMENTSOF(item) == 2);
+
+                        f[ELEMENTSOF(header) + 2*n]  = item[0];
+                        f[ELEMENTSOF(header) + 2*n+1] = item[1];
+
+                        n++;
+                }
+
+        memcpy(f + (ELEMENTSOF(header) + 2*n), footer, sizeof(footer));
+
+        /* Third: install the filter */
+        zero(prog);
+        prog.len = ELEMENTSOF(header) + ELEMENTSOF(footer) + 2*n;
+        prog.filter = f;
+        if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) < 0)
+                return -errno;
+
+        return 0;
+}
+
 int exec_spawn(ExecCommand *command,
                char **argv,
                const ExecContext *context,
@@ -944,7 +1001,7 @@ int exec_spawn(ExecCommand *command,
         int r;
         char *line;
         int socket_fd;
-        char **files_env = NULL;
+        char _cleanup_strv_free_ **files_env = NULL;
 
         assert(command);
         assert(context);
@@ -965,8 +1022,13 @@ int exec_spawn(ExecCommand *command,
         } else
                 socket_fd = -1;
 
-        if ((r = exec_context_load_environment(context, &files_env)) < 0) {
-                log_error("Failed to load environment files: %s", strerror(-r));
+        r = exec_context_load_environment(context, &files_env);
+        if (r < 0) {
+                log_struct(LOG_ERR,
+                           "UNIT=%s", unit_id,
+                           "MESSAGE=Failed to load environment files: %s", strerror(-r),
+                           "ERRNO=%d", -r,
+                           NULL);
                 return r;
         }
 
@@ -974,24 +1036,24 @@ int exec_spawn(ExecCommand *command,
                 argv = command->argv;
 
         line = exec_command_line(argv);
-        if (!line) {
-                r = -ENOMEM;
-                goto fail_parent;
-        }
+        if (!line)
+                return log_oom();
 
-        log_debug("About to execute: %s", line);
+        log_struct(LOG_DEBUG,
+                   "UNIT=%s", unit_id,
+                   "MESSAGE=About to execute %s", line,
+                   NULL);
         free(line);
 
         r = cgroup_bonding_realize_list(cgroup_bondings);
         if (r < 0)
-                goto fail_parent;
+                return r;
 
         cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings);
 
-        if ((pid = fork()) < 0) {
-                r = -errno;
-                goto fail_parent;
-        }
+        pid = fork();
+        if (pid < 0)
+                return -errno;
 
         if (pid == 0) {
                 int i, err;
@@ -999,7 +1061,8 @@ int exec_spawn(ExecCommand *command,
                 const char *username = NULL, *home = NULL;
                 uid_t uid = (uid_t) -1;
                 gid_t gid = (gid_t) -1;
-                char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
+                char _cleanup_strv_free_ **our_env = NULL, **pam_env = NULL,
+                        **final_env = NULL, **final_argv = NULL;
                 unsigned n_env = 0;
                 bool set_access = false;
 
@@ -1182,7 +1245,7 @@ int exec_spawn(ExecCommand *command,
 
                 if (context->user) {
                         username = context->user;
-                        err = get_user_creds(&username, &uid, &gid, &home);
+                        err = get_user_creds(&username, &uid, &gid, &home, NULL);
                         if (err < 0) {
                                 r = EXIT_USER;
                                 goto fail_child;
@@ -1228,7 +1291,7 @@ int exec_spawn(ExecCommand *command,
                 umask(context->umask);
 
 #ifdef HAVE_PAM
-                if (context->pam_name && username) {
+                if (apply_permissions && context->pam_name && username) {
                         err = setup_pam(context->pam_name, username, uid, context->tty_path, &pam_env, fds, n_fds);
                         if (err < 0) {
                                 r = EXIT_PAM;
@@ -1249,7 +1312,7 @@ int exec_spawn(ExecCommand *command,
                 if (strv_length(context->read_write_dirs) > 0 ||
                     strv_length(context->read_only_dirs) > 0 ||
                     strv_length(context->inaccessible_dirs) > 0 ||
-                    context->mount_flags != MS_SHARED ||
+                    context->mount_flags != 0 ||
                     context->private_tmp) {
                         err = setup_namespace(context->read_write_dirs,
                                               context->read_only_dirs,
@@ -1276,8 +1339,7 @@ int exec_spawn(ExecCommand *command,
                                 goto fail_child;
                         }
                 } else {
-
-                        char *d;
+                        char _cleanup_free_ *d = NULL;
 
                         if (asprintf(&d, "%s/%s",
                                      context->root_directory ? context->root_directory : "",
@@ -1289,12 +1351,9 @@ int exec_spawn(ExecCommand *command,
 
                         if (chdir(d) < 0) {
                                 err = -errno;
-                                free(d);
                                 r = EXIT_CHDIR;
                                 goto fail_child;
                         }
-
-                        free(d);
                 }
 
                 /* We repeat the fd closing here, to make sure that
@@ -1355,6 +1414,21 @@ int exec_spawn(ExecCommand *command,
                                         r = EXIT_CAPABILITIES;
                                         goto fail_child;
                                 }
+
+                        if (context->no_new_privileges)
+                                if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
+                                        err = -errno;
+                                        r = EXIT_NO_NEW_PRIVILEGES;
+                                        goto fail_child;
+                                }
+
+                        if (context->syscall_filter) {
+                                err = apply_seccomp(context->syscall_filter);
+                                if (err < 0) {
+                                        r = EXIT_SECCOMP;
+                                        goto fail_child;
+                                }
+                        }
                 }
 
                 if (!(our_env = new0(char*, 7))) {
@@ -1425,21 +1499,24 @@ int exec_spawn(ExecCommand *command,
         fail_child:
                 if (r != 0) {
                         log_open();
-                        log_warning("Failed at step %s spawning %s: %s",
-                                    exit_status_to_string(r, EXIT_STATUS_SYSTEMD),
-                                    command->path, strerror(-err));
+                        log_struct(LOG_ERR, MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
+                                   "EXECUTABLE=%s", command->path,
+                                   "MESSAGE=Failed at step %s spawning %s: %s",
+                                          exit_status_to_string(r, EXIT_STATUS_SYSTEMD),
+                                          command->path, strerror(-err),
+                                   "ERRNO=%d", -err,
+                                   NULL);
+                        log_close();
                 }
 
-                strv_free(our_env);
-                strv_free(final_env);
-                strv_free(pam_env);
-                strv_free(files_env);
-                strv_free(final_argv);
-
                 _exit(r);
         }
 
-        strv_free(files_env);
+        log_struct(LOG_DEBUG,
+                   "UNIT=%s", unit_id,
+                   "MESSAGE=Forked %s as %lu",
+                          command->path, (unsigned long) pid,
+                   NULL);
 
         /* 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
@@ -1449,17 +1526,10 @@ int exec_spawn(ExecCommand *command,
         if (cgroup_bondings)
                 cgroup_bonding_install_list(cgroup_bondings, pid, cgroup_suffix);
 
-        log_debug("Forked %s as %lu", command->path, (unsigned long) pid);
-
         exec_status_start(&command->exec_status, pid);
 
         *ret = pid;
         return 0;
-
-fail_parent:
-        strv_free(files_env);
-
-        return r;
 }
 
 void exec_context_init(ExecContext *c) {
@@ -1470,9 +1540,6 @@ void exec_context_init(ExecContext *c) {
         c->cpu_sched_policy = SCHED_OTHER;
         c->syslog_priority = LOG_DAEMON|LOG_INFO;
         c->syslog_level_prefix = true;
-        c->mount_flags = MS_SHARED;
-        c->kill_signal = SIGTERM;
-        c->send_sigkill = true;
         c->control_group_persistent = -1;
         c->ignore_sigpipe = true;
         c->timer_slack_nsec = (nsec_t) -1;
@@ -1539,6 +1606,9 @@ void exec_context_done(ExecContext *c) {
 
         free(c->utmp_id);
         c->utmp_id = NULL;
+
+        free(c->syscall_filter);
+        c->syscall_filter = NULL;
 }
 
 void exec_command_done(ExecCommand *c) {
@@ -1662,7 +1732,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                 "%sPrivateTmp: %s\n"
                 "%sControlGroupModify: %s\n"
                 "%sControlGroupPersistent: %s\n"
-                "%sPrivateNetwork: %s\n",
+                "%sPrivateNetwork: %s\n"
+                "%sIgnoreSIGPIPE: %s\n",
                 prefix, c->umask,
                 prefix, c->working_directory ? c->working_directory : "/",
                 prefix, c->root_directory ? c->root_directory : "/",
@@ -1670,7 +1741,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                 prefix, yes_no(c->private_tmp),
                 prefix, yes_no(c->control_group_modify),
                 prefix, yes_no(c->control_group_persistent),
-                prefix, yes_no(c->private_network));
+                prefix, yes_no(c->private_network),
+                prefix, yes_no(c->ignore_sigpipe));
 
         STRV_FOREACH(e, c->environment)
                 fprintf(f, "%sEnvironment: %s\n", prefix, *e);
@@ -1821,16 +1893,6 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                 fputs("\n", f);
         }
 
-        fprintf(f,
-                "%sKillMode: %s\n"
-                "%sKillSignal: SIG%s\n"
-                "%sSendSIGKILL: %s\n"
-                "%sIgnoreSIGPIPE: %s\n",
-                prefix, kill_mode_to_string(c->kill_mode),
-                prefix, signal_to_string(c->kill_signal),
-                prefix, yes_no(c->send_sigkill),
-                prefix, yes_no(c->ignore_sigpipe));
-
         if (c->utmp_id)
                 fprintf(f,
                         "%sUtmpIdentifier: %s\n",
@@ -2038,19 +2100,3 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
-
-static const char* const kill_mode_table[_KILL_MODE_MAX] = {
-        [KILL_CONTROL_GROUP] = "control-group",
-        [KILL_PROCESS] = "process",
-        [KILL_NONE] = "none"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode);
-
-static const char* const kill_who_table[_KILL_WHO_MAX] = {
-        [KILL_MAIN] = "main",
-        [KILL_CONTROL] = "control",
-        [KILL_ALL] = "all"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);