]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/execute.c
core: add 'j' in confirmation_spawn to list the jobs that are in progress
[thirdparty/systemd.git] / src / core / execute.c
index 3f053602b595cbbdfb5fde94856080e3c46ece31..6a7ad66a2118ea739f28315168d548dd0b0ee7fd 100644 (file)
@@ -71,7 +71,7 @@
 #include "exit-status.h"
 #include "fd-util.h"
 #include "fileio.h"
-#include "formats-util.h"
+#include "format-util.h"
 #include "fs-util.h"
 #include "glob-util.h"
 #include "io-util.h"
@@ -624,7 +624,7 @@ static int chown_terminal(int fd, uid_t uid) {
         return 0;
 }
 
-static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) {
+static int setup_confirm_stdio(const char *vc, int *_saved_stdin, int *_saved_stdout) {
         _cleanup_close_ int fd = -1, saved_stdin = -1, saved_stdout = -1;
         int r;
 
@@ -639,12 +639,7 @@ static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) {
         if (saved_stdout < 0)
                 return -errno;
 
-        fd = acquire_terminal(
-                        "/dev/console",
-                        false,
-                        false,
-                        false,
-                        DEFAULT_CONFIRM_USEC);
+        fd = acquire_terminal(vc, false, false, false, DEFAULT_CONFIRM_USEC);
         if (fd < 0)
                 return fd;
 
@@ -674,21 +669,27 @@ static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) {
         return 0;
 }
 
-_printf_(1, 2) static int write_confirm_message(const char *format, ...) {
+static void write_confirm_error_fd(int err, int fd) {
+        assert(err < 0);
+
+        if (err == -ETIMEDOUT)
+                dprintf(fd, "Confirmation question timed out, assuming positive response.\n");
+        else {
+                errno = -err;
+                dprintf(fd, "Couldn't ask confirmation: %m, assuming positive response.\n");
+        }
+}
+
+static void write_confirm_error(int err, const char *vc) {
         _cleanup_close_ int fd = -1;
-        va_list ap;
 
-        assert(format);
+        assert(vc);
 
-        fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+        fd = open_terminal(vc, O_WRONLY|O_NOCTTY|O_CLOEXEC);
         if (fd < 0)
-                return fd;
-
-        va_start(ap, format);
-        vdprintf(fd, format, ap);
-        va_end(ap);
+                return;
 
-        return 0;
+        write_confirm_error_fd(err, fd);
 }
 
 static int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) {
@@ -713,22 +714,80 @@ static int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) {
         return r;
 }
 
-static int ask_for_confirmation(char *response, char **argv) {
+enum {
+        CONFIRM_PRETEND_FAILURE = -1,
+        CONFIRM_PRETEND_SUCCESS =  0,
+        CONFIRM_EXECUTE = 1,
+};
+
+static int ask_for_confirmation(const char *vc, Unit *u, const char *cmdline) {
         int saved_stdout = -1, saved_stdin = -1, r;
-        _cleanup_free_ char *line = NULL;
+        _cleanup_free_ char *e = NULL;
+        char c;
 
-        r = setup_confirm_stdio(&saved_stdin, &saved_stdout);
-        if (r < 0)
-                return r;
+        /* For any internal errors, assume a positive response. */
+        r = setup_confirm_stdio(vc, &saved_stdin, &saved_stdout);
+        if (r < 0) {
+                write_confirm_error(r, vc);
+                return CONFIRM_EXECUTE;
+        }
 
-        line = exec_command_line(argv);
-        if (!line)
-                return -ENOMEM;
+        e = ellipsize(cmdline, 60, 100);
+        if (!e) {
+                log_oom();
+                r = CONFIRM_EXECUTE;
+                goto restore_stdio;
+        }
 
-        r = ask_char(response, "yns", "Execute %s? [Yes, No, Skip] ", line);
+        for (;;) {
+                r = ask_char(&c, "yfshiDj", "Execute %s? [y, f, s – h for help] ", e);
+                if (r < 0) {
+                        write_confirm_error_fd(r, STDOUT_FILENO);
+                        r = CONFIRM_EXECUTE;
+                        goto restore_stdio;
+                }
 
-        restore_confirm_stdio(&saved_stdin, &saved_stdout);
+                switch (c) {
+                case 'D':
+                        unit_dump(u, stdout, "  ");
+                        continue; /* ask again */
+                case 'f':
+                        printf("Failing execution.\n");
+                        r = CONFIRM_PRETEND_FAILURE;
+                        break;
+                case 'h':
+                        printf("  D - dump, show the state of the unit\n"
+                               "  f - fail, don't execute the command and pretend it failed\n"
+                               "  h - help\n"
+                               "  i - info, show a short summary of the unit\n"
+                               "  j - jobs, show jobs that are in progress\n"
+                               "  s - skip, don't execute the command and pretend it succeeded\n"
+                               "  y - yes, execute the command\n");
+                        continue; /* ask again */
+                case 'i':
+                        printf("  Description: %s\n"
+                               "  Unit:        %s\n"
+                               "  Command:     %s\n",
+                               u->id, u->description, cmdline);
+                        continue; /* ask again */
+                case 'j':
+                        manager_dump_jobs(u->manager, stdout, "  ");
+                        continue; /* ask again */
+                case 's':
+                        printf("Skipping execution.\n");
+                        r = CONFIRM_PRETEND_SUCCESS;
+                        break;
+                case 'y':
+                        r = CONFIRM_EXECUTE;
+                        break;
+                default:
+                        assert_not_reached("Unhandled choice");
+                }
+                break;
+        }
 
+restore_stdio:
+        restore_confirm_stdio(&saved_stdin, &saved_stdout);
         return r;
 }
 
@@ -773,11 +832,9 @@ static int get_fixed_group(const ExecContext *c, const char **group, gid_t *gid)
         return 0;
 }
 
-static int get_fixed_supplementary_groups(const ExecContext *c,
-                                          const char *user,
-                                          const char *group,
-                                          gid_t gid,
-                                          gid_t **supplementary_gids, int *ngids) {
+static int get_supplementary_groups(const ExecContext *c, const char *user,
+                                    const char *group, gid_t gid,
+                                    gid_t **supplementary_gids, int *ngids) {
         char **i;
         int r, k = 0;
         int ngroups_max;
@@ -790,8 +847,8 @@ static int get_fixed_supplementary_groups(const ExecContext *c,
         /*
          * If user is given, then lookup GID and supplementary groups list.
          * We avoid NSS lookups for gid=0. Also we have to initialize groups
-         * as early as possible so we keep the list of supplementary groups
-         * of the caller.
+         * here and as early as possible so we keep the list of supplementary
+         * groups of the caller.
          */
         if (user && gid_is_valid(gid) && gid != 0) {
                 /* First step, initialize groups from /etc/groups */
@@ -1536,6 +1593,18 @@ static int apply_private_devices(const Unit *u, const ExecContext *c) {
         return seccomp_load_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_RAW_IO, SCMP_ACT_ERRNO(EPERM));
 }
 
+static int apply_restrict_namespaces(Unit *u, const ExecContext *c) {
+        assert(c);
+
+        if (!exec_context_restrict_namespaces_set(c))
+                return 0;
+
+        if (skip_seccomp_unavailable(u, "RestrictNamespaces="))
+                return 0;
+
+        return seccomp_restrict_namespaces(c->restrict_namespaces);
+}
+
 #endif
 
 static void do_idle_pipe_dance(int idle_pipe[4]) {
@@ -1605,7 +1674,7 @@ static int build_environment(
                 if (!joined)
                         return -ENOMEM;
 
-                x = strjoin("LISTEN_FDNAMES=", joined, NULL);
+                x = strjoin("LISTEN_FDNAMES=", joined);
                 if (!x)
                         return -ENOMEM;
                 our_env[n_env++] = x;
@@ -1712,7 +1781,7 @@ static int build_pass_environment(const ExecContext *c, char ***ret) {
                 v = getenv(*i);
                 if (!v)
                         continue;
-                x = strjoin(*i, "=", v, NULL);
+                x = strjoin(*i, "=", v);
                 if (!x)
                         return -ENOMEM;
                 if (!GREEDY_REALLOC(pass_env, n_bufsize, n_env + 2))
@@ -1926,7 +1995,7 @@ static int setup_runtime_directory(
         STRV_FOREACH(rt, context->runtime_directory) {
                 _cleanup_free_ char *p;
 
-                p = strjoin(params->runtime_prefix, "/", *rt, NULL);
+                p = strjoin(params->runtime_prefix, "/", *rt);
                 if (!p)
                         return -ENOMEM;
 
@@ -2002,7 +2071,7 @@ static int compile_read_write_paths(
         STRV_FOREACH(rt, context->runtime_directory) {
                 char *s;
 
-                s = strjoin(params->runtime_prefix, "/", *rt, NULL);
+                s = strjoin(params->runtime_prefix, "/", *rt);
                 if (!s)
                         return -ENOMEM;
 
@@ -2024,6 +2093,7 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context,
         char *tmp = NULL, *var = NULL;
         const char *root_dir = NULL;
         NameSpaceInfo ns_info = {
+                .ignore_protect_paths = false,
                 .private_dev = context->private_devices,
                 .protect_control_groups = context->protect_control_groups,
                 .protect_kernel_tunables = context->protect_kernel_tunables,
@@ -2050,6 +2120,14 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context,
         if (params->flags & EXEC_APPLY_CHROOT)
                 root_dir = context->root_directory;
 
+        /*
+         * If DynamicUser=no and RootDirectory= is set then lets pass a relaxed
+         * sandbox info, otherwise enforce it, don't ignore protected paths and
+         * fail if we are enable to apply the sandbox inside the mount namespace.
+         */
+        if (!context->dynamic_user && root_dir)
+                ns_info.ignore_protect_paths = true;
+
         r = setup_namespace(root_dir, &ns_info, rw,
                             context->read_only_paths,
                             context->inaccessible_paths,
@@ -2185,6 +2263,7 @@ static bool context_has_no_new_privileges(const ExecContext *c) {
         return context_has_address_families(c) || /* we need NNP if we have any form of seccomp and are unprivileged */
                 c->memory_deny_write_execute ||
                 c->restrict_realtime ||
+                exec_context_restrict_namespaces_set(c) ||
                 c->protect_kernel_tunables ||
                 c->protect_kernel_modules ||
                 c->private_devices ||
@@ -2294,22 +2373,24 @@ static int exec_child(
 
         exec_context_tty_reset(context, params);
 
-        if (params->flags & EXEC_CONFIRM_SPAWN) {
-                char response;
+        if (params->confirm_spawn) {
+                const char *vc = params->confirm_spawn;
+                _cleanup_free_ char *cmdline = NULL;
+
+                cmdline = exec_command_line(argv);
+                if (!cmdline) {
+                        *exit_status = EXIT_CONFIRM;
+                        return -ENOMEM;
+                }
 
-                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");
+                r = ask_for_confirmation(vc, unit, cmdline);
+                if (r != CONFIRM_EXECUTE) {
+                        if (r == CONFIRM_PRETEND_SUCCESS) {
+                                *exit_status = EXIT_SUCCESS;
+                                return 0;
+                        }
                         *exit_status = EXIT_CONFIRM;
                         return -ECANCELED;
-                } else if (response == 'n') {
-                        write_confirm_message("Failing execution.\n");
-                        *exit_status = 0;
-                        return 0;
                 }
         }
 
@@ -2347,13 +2428,14 @@ static int exec_child(
                         *exit_status = EXIT_GROUP;
                         return r;
                 }
+        }
 
-                r = get_fixed_supplementary_groups(context, username, groupname,
-                                                   gid, &supplementary_gids, &ngids);
-                if (r < 0) {
-                        *exit_status = EXIT_GROUP;
-                        return r;
-                }
+        /* Initialize user supplementary groups and get SupplementaryGroups= ones */
+        r = get_supplementary_groups(context, username, groupname, gid,
+                                     &supplementary_gids, &ngids);
+        if (r < 0) {
+                *exit_status = EXIT_GROUP;
+                return r;
         }
 
         r = send_user_lookup(unit, user_lookup_fd, uid, gid);
@@ -2765,6 +2847,12 @@ static int exec_child(
                         }
                 }
 
+                r = apply_restrict_namespaces(unit, context);
+                if (r < 0) {
+                        *exit_status = EXIT_SECCOMP;
+                        return r;
+                }
+
                 if (context->protect_kernel_tunables) {
                         r = apply_protect_sysctl(unit, context);
                         if (r < 0) {
@@ -2948,6 +3036,7 @@ void exec_context_init(ExecContext *c) {
         c->personality = PERSONALITY_INVALID;
         c->runtime_directory_mode = 0755;
         c->capability_bounding_set = CAP_ALL;
+        c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
 }
 
 void exec_context_done(ExecContext *c) {
@@ -3005,7 +3094,7 @@ int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_p
         STRV_FOREACH(i, c->runtime_directory) {
                 _cleanup_free_ char *p;
 
-                p = strjoin(runtime_prefix, "/", *i, NULL);
+                p = strjoin(runtime_prefix, "/", *i);
                 if (!p)
                         return -ENOMEM;
 
@@ -3245,6 +3334,7 @@ static void strv_fprintf(FILE *f, char **l) {
 void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
         char **e, **d;
         unsigned i;
+        int r;
 
         assert(c);
         assert(f);
@@ -3525,6 +3615,15 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                 fputc('\n', f);
         }
 
+        if (exec_context_restrict_namespaces_set(c)) {
+                _cleanup_free_ char *s = NULL;
+
+                r = namespace_flag_to_string_many(c->restrict_namespaces, &s);
+                if (r >= 0)
+                        fprintf(f, "%sRestrictNamespaces: %s\n",
+                                prefix, s);
+        }
+
         if (c->syscall_errno > 0)
                 fprintf(f,
                         "%sSystemCallErrorNumber: %s\n",