#include "securebits.h"
#include "cgroup.h"
+/* This assumes there is a 'tty' group */
+#define TTY_MODE 0620
+
static int shift_fds(int fds[], unsigned n_fds) {
int start, restart_from;
if ((nfd = fcntl(fds[i], F_DUPFD, i+3)) < 0)
return -errno;
- assert_se(close_nointr(fds[i]) == 0);
+ close_nointr_nofail(fds[i]);
fds[i] = nfd;
/* Hmm, the fd we wanted isn't free? Then
return 0;
}
-static int flags_fds(int fds[], unsigned n_fds, bool nonblock) {
+static int flags_fds(const int fds[], unsigned n_fds, bool nonblock) {
unsigned i;
int r;
if (fd != nfd) {
r = dup2(fd, nfd) < 0 ? -errno : nfd;
- close_nointr(fd);
+ close_nointr_nofail(fd);
} else
r = nfd;
if (fd != nfd) {
r = dup2(fd, nfd) < 0 ? -errno : nfd;
- close_nointr(fd);
+ close_nointr_nofail(fd);
} else
r = nfd;
i == EXEC_INPUT_TTY_FAIL;
}
-static int setup_input(const ExecContext *context) {
+static int fixup_input(const ExecContext *context, int socket_fd) {
+ assert(context);
+
+ 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);
- switch (context->std_input) {
+ 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");
}
}
+static int chown_terminal(int fd, uid_t uid) {
+ struct stat st;
+
+ assert(fd >= 0);
+
+ /* This might fail. What matters are the results. */
+ fchown(fd, uid, -1);
+ fchmod(fd, TTY_MODE);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (st.st_uid != uid || (st.st_mode & 0777) != TTY_MODE)
+ return -EPERM;
+
+ return 0;
+}
+
static int setup_confirm_stdio(const ExecContext *context,
int *_saved_stdin,
int *_saved_stdout) {
goto fail;
}
+ if (chown_terminal(fd, getuid()) < 0) {
+ r = EXIT_STDIN;
+ goto fail;
+ }
+
if (dup2(fd, STDIN_FILENO) < 0) {
r = EXIT_STDIN;
goto fail;
}
int exec_spawn(ExecCommand *command,
+ char **argv,
const ExecContext *context,
- int *fds, unsigned n_fds,
+ int fds[], unsigned n_fds,
bool apply_permissions,
bool apply_chroot,
bool confirm_spawn,
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);
/* child */
+ reset_all_signal_handlers();
+
if (sigemptyset(&ss) < 0 ||
sigprocmask(SIG_SETMASK, &ss, NULL) < 0) {
r = EXIT_SIGNAL_MASK;
goto fail;
}
- if (setsid() < 0) {
- r = EXIT_SETSID;
- goto fail;
- }
+ if (!context->no_setsid)
+ if (setsid() < 0) {
+ r = EXIT_SETSID;
+ goto fail;
+ }
umask(context->umask);
goto fail;
/* Now ask the question. */
- if (!(line = exec_command_line(command))) {
+ if (!(line = exec_command_line(argv))) {
r = EXIT_MEMORY;
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;
}
r = EXIT_USER;
goto fail;
}
+
+ if (is_terminal_input(context->std_input))
+ if (chown_terminal(STDIN_FILENO, uid) < 0) {
+ r = EXIT_STDIN;
+ goto fail;
+ }
}
if (apply_permissions)
goto fail;
}
- execve(command->path, command->argv, final_env);
+ execve(command->path, argv, final_env);
r = EXIT_EXEC;
fail:
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",
}
const char* exit_status_to_string(ExitStatus status) {
- switch (status) {
+
+ /* We cast to int here, so that -Wenum doesn't complain that
+ * EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */
+
+ switch ((int) status) {
case EXIT_SUCCESS:
return "SUCCESS";
[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);