#include <sys/stat.h>
#include <grp.h>
#include <pwd.h>
+#include <sys/mount.h>
#include "execute.h"
#include "strv.h"
#include "ioprio.h"
#include "securebits.h"
#include "cgroup.h"
+#include "namespace.h"
/* This assumes there is a 'tty' group */
#define TTY_MODE 0620
i == EXEC_INPUT_TTY_FAIL;
}
-static int setup_input(const ExecContext *context) {
+static int fixup_input(const ExecContext *context, int socket_fd) {
assert(context);
- switch (context->std_input) {
+ if (socket_fd < 0 && context->std_input == EXEC_INPUT_SOCKET)
+ return EXEC_INPUT_NULL;
+
+ return context->std_input;
+}
+
+static int fixup_output(const ExecContext *context, int socket_fd) {
+ assert(context);
+
+ if (socket_fd < 0 && context->std_output == EXEC_OUTPUT_SOCKET)
+ return EXEC_OUTPUT_INHERIT;
+
+ return context->std_output;
+}
+
+static int fixup_error(const ExecContext *context, int socket_fd) {
+ assert(context);
+
+ if (socket_fd < 0 && context->std_error == EXEC_OUTPUT_SOCKET)
+ return EXEC_OUTPUT_INHERIT;
+
+ return context->std_error;
+}
+
+static int setup_input(const ExecContext *context, int socket_fd) {
+ ExecInput i;
+
+ assert(context);
+
+ i = fixup_input(context, socket_fd);
+
+ switch (i) {
case EXEC_INPUT_NULL:
return open_null_as(O_RDONLY, STDIN_FILENO);
if ((fd = acquire_terminal(
tty_path(context),
- context->std_input == EXEC_INPUT_TTY_FAIL,
- context->std_input == EXEC_INPUT_TTY_FORCE)) < 0)
+ i == EXEC_INPUT_TTY_FAIL,
+ i == EXEC_INPUT_TTY_FORCE)) < 0)
return fd;
if (fd != STDIN_FILENO) {
return r;
}
+ case EXEC_INPUT_SOCKET:
+ return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
+
default:
assert_not_reached("Unknown input type");
}
}
-static int setup_output(const ExecContext *context, const char *ident) {
+static int setup_output(const ExecContext *context, int socket_fd, const char *ident) {
+ ExecOutput o;
+ ExecInput i;
+
assert(context);
assert(ident);
+ i = fixup_input(context, socket_fd);
+ o = fixup_output(context, socket_fd);
+
/* This expects the input is already set up */
- switch (context->std_output) {
+ switch (o) {
case EXEC_OUTPUT_INHERIT:
/* If the input is connected to a terminal, inherit that... */
- if (is_terminal_input(context->std_input))
+ if (is_terminal_input(i) || i == EXEC_INPUT_SOCKET)
return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
- return 0;
+ return STDIN_FILENO;
case EXEC_OUTPUT_NULL:
return open_null_as(O_WRONLY, STDOUT_FILENO);
- case EXEC_OUTPUT_TTY: {
- if (is_terminal_input(context->std_input))
+ case EXEC_OUTPUT_TTY:
+ if (is_terminal_input(i))
return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
/* We don't reset the terminal if this is just about output */
return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO);
- }
case EXEC_OUTPUT_SYSLOG:
case EXEC_OUTPUT_KERNEL:
- return connect_logger_as(context, context->std_output, ident, STDOUT_FILENO);
+ return connect_logger_as(context, o, ident, STDOUT_FILENO);
+
+ case EXEC_OUTPUT_SOCKET:
+ assert(socket_fd >= 0);
+ return dup2(socket_fd, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
default:
assert_not_reached("Unknown output type");
}
}
-static int setup_error(const ExecContext *context, const char *ident) {
+static int setup_error(const ExecContext *context, int socket_fd, const char *ident) {
+ ExecOutput o, e;
+ ExecInput i;
+
assert(context);
assert(ident);
+ i = fixup_input(context, socket_fd);
+ o = fixup_output(context, socket_fd);
+ e = fixup_error(context, socket_fd);
+
/* This expects the input and output are already set up */
/* Don't change the stderr file descriptor if we inherit all
* the way and are not on a tty */
- if (context->std_error == EXEC_OUTPUT_INHERIT &&
- context->std_output == EXEC_OUTPUT_INHERIT &&
- !is_terminal_input(context->std_input))
+ if (e == EXEC_OUTPUT_INHERIT &&
+ o == EXEC_OUTPUT_INHERIT &&
+ !is_terminal_input(i))
return STDERR_FILENO;
/* Duplicate form stdout if possible */
- if (context->std_error == context->std_output ||
- context->std_error == EXEC_OUTPUT_INHERIT)
+ if (e == o || e == EXEC_OUTPUT_INHERIT)
return dup2(STDOUT_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
- switch (context->std_error) {
+ switch (e) {
case EXEC_OUTPUT_NULL:
return open_null_as(O_WRONLY, STDERR_FILENO);
case EXEC_OUTPUT_TTY:
- if (is_terminal_input(context->std_input))
+ if (is_terminal_input(i))
return dup2(STDIN_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
/* We don't reset the terminal if this is just about output */
case EXEC_OUTPUT_SYSLOG:
case EXEC_OUTPUT_KERNEL:
- return connect_logger_as(context, context->std_error, ident, STDERR_FILENO);
+ return connect_logger_as(context, e, ident, STDERR_FILENO);
+
+ case EXEC_OUTPUT_SOCKET:
+ assert(socket_fd >= 0);
+ return dup2(socket_fd, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
default:
assert_not_reached("Unknown error type");
return r;
}
-static int restore_conform_stdio(const ExecContext *context,
+static int restore_confirm_stdio(const ExecContext *context,
int *saved_stdin,
int *saved_stdout,
bool *keep_stdin,
}
int exec_spawn(ExecCommand *command,
+ char **argv,
const ExecContext *context,
int fds[], unsigned n_fds,
bool apply_permissions,
pid_t pid;
int r;
char *line;
+ int socket_fd;
assert(command);
assert(context);
assert(ret);
assert(fds || n_fds <= 0);
- if (!(line = exec_command_line(command)))
+ if (context->std_input == EXEC_INPUT_SOCKET ||
+ context->std_output == EXEC_OUTPUT_SOCKET ||
+ context->std_error == EXEC_OUTPUT_SOCKET) {
+
+ if (n_fds != 1)
+ return -EINVAL;
+
+ socket_fd = fds[0];
+
+ fds = NULL;
+ n_fds = 0;
+ } else
+ socket_fd = -1;
+
+ if (!argv)
+ argv = command->argv;
+
+ if (!(line = exec_command_line(argv)))
return -ENOMEM;
log_debug("About to execute: %s", line);
goto fail;
}
- if (setsid() < 0) {
- r = EXIT_SETSID;
- goto fail;
- }
-
- umask(context->umask);
+ if (!context->no_setsid)
+ if (setsid() < 0) {
+ r = EXIT_SETSID;
+ goto fail;
+ }
if (confirm_spawn) {
char response;
goto fail;
/* Now ask the question. */
- if (!(line = exec_command_line(command))) {
+ if (!(line = exec_command_line(argv))) {
r = EXIT_MEMORY;
goto fail;
}
}
/* Release terminal for the question */
- if ((r = restore_conform_stdio(context,
+ if ((r = restore_confirm_stdio(context,
&saved_stdin, &saved_stdout,
&keep_stdin, &keep_stdout)))
goto fail;
}
if (!keep_stdin)
- if (setup_input(context) < 0) {
+ if (setup_input(context, socket_fd) < 0) {
r = EXIT_STDIN;
goto fail;
}
if (!keep_stdout)
- if (setup_output(context, file_name_from_path(command->path)) < 0) {
+ if (setup_output(context, socket_fd, file_name_from_path(command->path)) < 0) {
r = EXIT_STDOUT;
goto fail;
}
- if (setup_error(context, file_name_from_path(command->path)) < 0) {
+ if (setup_error(context, socket_fd, file_name_from_path(command->path)) < 0) {
r = EXIT_STDERR;
goto fail;
}
goto fail;
}
+ 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->private_tmp)
+ if ((r = setup_namespace(
+ context->read_write_dirs,
+ context->read_only_dirs,
+ context->inaccessible_dirs,
+ context->private_tmp,
+ context->mount_flags)) < 0)
+ goto fail;
+
if (context->user) {
username = context->user;
if (get_user_creds(&username, &uid, &gid, &home) < 0) {
goto fail;
}
+ umask(context->umask);
+
if (apply_chroot) {
if (context->root_directory)
if (chroot(context->root_directory) < 0) {
goto fail;
}
- execve(command->path, command->argv, final_env);
+ execve(command->path, argv, final_env);
r = EXIT_EXEC;
fail:
assert(c);
c->umask = 0002;
- c->oom_adjust = 0;
- c->oom_adjust_set = false;
- c->nice = 0;
- c->nice_set = false;
c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
- c->ioprio_set = false;
c->cpu_sched_policy = SCHED_OTHER;
- c->cpu_sched_priority = 0;
- c->cpu_sched_set = false;
- CPU_ZERO(&c->cpu_affinity);
- c->cpu_affinity_set = false;
- c->timer_slack_ns = 0;
- c->timer_slack_ns_set = false;
-
- c->cpu_sched_reset_on_fork = false;
- c->non_blocking = false;
-
- c->std_input = 0;
- c->std_output = 0;
- c->std_error = 0;
c->syslog_priority = LOG_DAEMON|LOG_INFO;
-
- c->secure_bits = 0;
- c->capability_bounding_set_drop = 0;
+ c->mount_flags = MS_SHARED;
}
void exec_context_done(ExecContext *c) {
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;
}
void exec_command_done(ExecCommand *c) {
}
}
+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;
"%sUMask: %04o\n"
"%sWorkingDirectory: %s\n"
"%sRootDirectory: %s\n"
- "%sNonBlocking: %s\n",
+ "%sNonBlocking: %s\n"
+ "%sPrivateTmp: %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->non_blocking),
+ prefix, yes_no(c->private_tmp));
if (c->environment)
for (e = c->environment; *e; e++)
if (c->group)
fprintf(f, "%sGroup: %s", prefix, c->group);
- if (c->supplementary_groups) {
- char **g;
-
+ if (strv_length(c->supplementary_groups) > 0) {
fprintf(f, "%sSupplementaryGroups:", prefix);
+ strv_fprintf(f, c->supplementary_groups);
+ fputs("\n", f);
+ }
- STRV_FOREACH(g, c->supplementary_groups)
- fprintf(f, " %s", *g);
+ 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);
}
}
prefix, s->status);
}
-char *exec_command_line(ExecCommand *c) {
+char *exec_command_line(char **argv) {
size_t k;
char *n, *p, **a;
bool first = true;
- assert(c);
- assert(c->argv);
+ assert(argv);
k = 1;
- STRV_FOREACH(a, c->argv)
+ STRV_FOREACH(a, argv)
k += strlen(*a)+3;
if (!(n = new(char, k)))
return NULL;
p = n;
- STRV_FOREACH(a, c->argv) {
+ STRV_FOREACH(a, argv) {
if (!first)
*(p++) = ' ';
p2 = strappend(prefix, "\t");
prefix2 = p2 ? p2 : prefix;
- cmd = exec_command_line(c);
+ cmd = exec_command_line(c->argv);
fprintf(f,
"%sCommand Line: %s\n",
[EXEC_INPUT_NULL] = "null",
[EXEC_INPUT_TTY] = "tty",
[EXEC_INPUT_TTY_FORCE] = "tty-force",
- [EXEC_INPUT_TTY_FAIL] = "tty-fail"
+ [EXEC_INPUT_TTY_FAIL] = "tty-fail",
+ [EXEC_INPUT_SOCKET] = "socket"
};
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
[EXEC_OUTPUT_NULL] = "null",
[EXEC_OUTPUT_TTY] = "tty",
[EXEC_OUTPUT_SYSLOG] = "syslog",
- [EXEC_OUTPUT_KERNEL] = "kernel"
+ [EXEC_OUTPUT_KERNEL] = "kernel",
+ [EXEC_OUTPUT_SOCKET] = "socket"
};
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);