#include "mkdir.h"
#include "apparmor-util.h"
#include "smack-util.h"
-#include "bus-kernel.h"
+#include "bus-endpoint.h"
#include "label.h"
+#include "cap-list.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
fds[i] = nfd;
/* Hmm, the fd we wanted isn't free? Then
- * let's remember that and try again from here*/
+ * let's remember that and try again from here */
if (nfd != i+3 && restart_from < 0)
restart_from = i;
}
return r;
}
-static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd) {
- int fd, r;
+static int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/journal/stdout",
};
+ uid_t olduid = UID_INVALID;
+ gid_t oldgid = GID_INVALID;
+ int r;
+
+ if (gid != GID_INVALID) {
+ oldgid = getgid();
+
+ r = setegid(gid);
+ if (r < 0)
+ return -errno;
+ }
+
+ if (uid != UID_INVALID) {
+ olduid = getuid();
+
+ r = seteuid(uid);
+ if (r < 0) {
+ r = -errno;
+ goto restore_gid;
+ }
+ }
+
+ r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
+ if (r < 0)
+ r = -errno;
+
+ /* If we fail to restore the uid or gid, things will likely
+ fail later on. This should only happen if an LSM interferes. */
+
+ if (uid != UID_INVALID)
+ (void) seteuid(olduid);
+
+ restore_gid:
+ if (gid != GID_INVALID)
+ (void) setegid(oldgid);
+
+ return r;
+}
+
+static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd, uid_t uid, gid_t gid) {
+ int fd, r;
assert(context);
assert(output < _EXEC_OUTPUT_MAX);
if (fd < 0)
return -errno;
- r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
- if (r < 0) {
- safe_close(fd);
- return -errno;
- }
+ r = connect_journal_socket(fd, uid, gid);
+ if (r < 0)
+ return r;
if (shutdown(fd, SHUT_RD) < 0) {
safe_close(fd);
}
}
-static int setup_output(const ExecContext *context, int fileno, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) {
+static 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) {
ExecOutput o;
ExecInput i;
int r;
case EXEC_OUTPUT_KMSG_AND_CONSOLE:
case EXEC_OUTPUT_JOURNAL:
case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
- r = connect_logger_as(context, o, ident, unit_id, fileno);
+ r = connect_logger_as(context, o, ident, unit_id, fileno, uid, gid);
if (r < 0) {
- log_unit_struct(LOG_CRIT, unit_id,
- "MESSAGE=Failed to connect std%s of %s to the journal socket: %s",
- fileno == STDOUT_FILENO ? "out" : "err",
- unit_id, strerror(-r),
- "ERRNO=%d", -r,
- NULL);
+ log_unit_struct(unit_id,
+ LOG_ERR,
+ LOG_MESSAGE("Failed to connect %s of %s to the journal socket: %s",
+ fileno == STDOUT_FILENO ? "stdout" : "stderr",
+ unit_id, strerror(-r)),
+ LOG_ERRNO(-r),
+ NULL);
r = open_null_as(O_WRONLY, fileno);
}
return r;
* daemon. We do things this way to ensure that the main PID
* of the daemon is the one we initially fork()ed. */
- if (log_get_max_level() < LOG_PRI(LOG_DEBUG))
+ if (log_get_max_level() < LOG_DEBUG)
flags |= PAM_SILENT;
pam_code = pam_start(name, user, &conv, &handle);
* If this fails, ignore the error - but expect sd-pam threads
* to fail to exit normally */
if (setresuid(uid, uid, uid) < 0)
- log_error("Error: Failed to setresuid() in sd-pam: %s", strerror(-r));
+ log_error_errno(r, "Error: Failed to setresuid() in sd-pam: %m");
/* Wait until our parent died. This will only work if
* the above setresuid() succeeds, otherwise the kernel
log_error("PAM failed: %s", pam_strerror(handle, pam_code));
err = -EPERM; /* PAM errors do not map to errno */
} else {
- log_error("PAM failed: %m");
+ log_error_errno(errno, "PAM failed: %m");
err = -errno;
}
int *error) {
_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_t) -1;
- gid_t gid = (gid_t) -1;
+ uid_t uid = UID_INVALID;
+ gid_t gid = GID_INVALID;
int i, err;
assert(command);
}
}
+ if (context->user) {
+ username = context->user;
+ err = get_user_creds(&username, &uid, &gid, &home, &shell);
+ if (err < 0) {
+ *error = EXIT_USER;
+ return err;
+ }
+ }
+
/* If a socket is connected to STDIN/STDOUT/STDERR, we
* must sure to drop O_NONBLOCK */
if (socket_fd >= 0)
return err;
}
- err = setup_output(context, STDOUT_FILENO, socket_fd, basename(command->path), params->unit_id, params->apply_tty_stdin);
+ err = setup_output(context, STDOUT_FILENO, socket_fd, basename(command->path), params->unit_id, params->apply_tty_stdin, uid, gid);
if (err < 0) {
*error = EXIT_STDOUT;
return err;
}
- err = setup_output(context, STDERR_FILENO, socket_fd, basename(command->path), params->unit_id, params->apply_tty_stdin);
+ err = setup_output(context, STDERR_FILENO, socket_fd, basename(command->path), params->unit_id, params->apply_tty_stdin, uid, gid);
if (err < 0) {
*error = EXIT_STDERR;
return err;
}
if (params->cgroup_path) {
- err = cg_attach_everywhere(params->cgroup_supported, params->cgroup_path, 0);
+ err = cg_attach_everywhere(params->cgroup_supported, params->cgroup_path, 0, NULL, NULL);
if (err < 0) {
*error = EXIT_CGROUP;
return err;
if (context->utmp_id)
utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path);
- if (context->user) {
- username = context->user;
- err = get_user_creds(&username, &uid, &gid, &home, &shell);
+ if (context->user && is_terminal_input(context->std_input)) {
+ err = chown_terminal(STDIN_FILENO, uid);
if (err < 0) {
- *error = EXIT_USER;
+ *error = EXIT_STDIN;
return err;
}
-
- if (is_terminal_input(context->std_input)) {
- err = chown_terminal(STDIN_FILENO, uid);
- if (err < 0) {
- *error = EXIT_STDIN;
- return err;
- }
- }
}
#ifdef ENABLE_KDBUS
if (params->bus_endpoint_fd >= 0 && context->bus_endpoint) {
- uid_t ep_uid = (uid == (uid_t) -1) ? 0 : uid;
+ uid_t ep_uid = (uid == UID_INVALID) ? 0 : uid;
err = bus_kernel_set_endpoint_policy(params->bus_endpoint_fd, ep_uid, context->bus_endpoint);
if (err < 0) {
context->mount_flags);
if (err == -EPERM)
- log_unit_warning(params->unit_id, "Failed to set up file system namespace due to lack of privileges. Execution sandbox will not be in effect: %s", strerror(-err));
+ log_unit_warning_errno(params->unit_id, err, "Failed to set up file system namespace due to lack of privileges. Execution sandbox will not be in effect: %m");
else if (err < 0) {
*error = EXIT_NAMESPACE;
return err;
}
}
+#ifdef HAVE_SELINUX
+ if (params->apply_permissions && mac_selinux_use() && params->selinux_context_net && socket_fd >= 0) {
+ err = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net);
+ if (err < 0) {
+ *error = EXIT_SELINUX_CONTEXT;
+ return err;
+ }
+ }
+#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
#ifdef HAVE_SELINUX
if (mac_selinux_use()) {
- if (context->selinux_context) {
- err = setexeccon(context->selinux_context);
- if (err < 0 && !context->selinux_context_ignore) {
- *error = EXIT_SELINUX_CONTEXT;
- return err;
- }
- }
-
- if (params->selinux_context_net && socket_fd >= 0) {
- _cleanup_free_ char *label = NULL;
-
- err = mac_selinux_get_child_mls_label(socket_fd, command->path, &label);
- if (err < 0) {
- *error = EXIT_SELINUX_CONTEXT;
- return err;
- }
+ char *exec_context = mac_selinux_context_net ?: context->selinux_context;
- err = setexeccon(label);
+ if (exec_context) {
+ err = setexeccon(exec_context);
if (err < 0) {
*error = EXIT_SELINUX_CONTEXT;
return err;
final_env = strv_env_clean(final_env);
- if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
+ 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(LOG_DEBUG,
- params->unit_id,
+ log_unit_struct(params->unit_id,
+ LOG_DEBUG,
"EXECUTABLE=%s", command->path,
- "MESSAGE=Executing: %s", line,
+ LOG_MESSAGE("Executing: %s", line),
NULL);
log_close();
}
err = exec_context_load_environment(context, params->unit_id, &files_env);
if (err < 0) {
- log_unit_struct(LOG_ERR,
- params->unit_id,
- "MESSAGE=Failed to load environment files: %s", strerror(-err),
- "ERRNO=%d", -err,
- NULL);
+ log_unit_struct(params->unit_id,
+ LOG_ERR,
+ LOG_MESSAGE("Failed to load environment files: %s", strerror(-err)),
+ LOG_ERRNO(-err),
+ NULL);
return err;
}
if (!line)
return log_oom();
- log_unit_struct(LOG_DEBUG,
- params->unit_id,
+ log_unit_struct(params->unit_id,
+ LOG_DEBUG,
"EXECUTABLE=%s", command->path,
- "MESSAGE=About to execute: %s", line,
+ LOG_MESSAGE("About to execute: %s", line),
NULL);
free(line);
&r);
if (r != 0) {
log_open();
- log_struct(LOG_ERR, MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
+ log_struct(LOG_ERR,
+ LOG_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,
+ LOG_MESSAGE("Failed at step %s spawning %s: %s",
+ exit_status_to_string(r, EXIT_STATUS_SYSTEMD),
+ command->path, strerror(-err)),
+ LOG_ERRNO(-err),
NULL);
log_close();
}
_exit(r);
}
- log_unit_struct(LOG_DEBUG,
- params->unit_id,
- "MESSAGE=Forked %s as "PID_FMT,
- command->path, pid,
+ log_unit_struct(params->unit_id,
+ LOG_DEBUG,
+ LOG_MESSAGE("Forked %s as "PID_FMT,
+ command->path, pid),
NULL);
/* We add the new process to the cgroup both in the child (so
/* We execute this synchronously, since we need to be
* sure this is gone when we start the service
* next. */
- rm_rf_dangerous(p, false, true, false);
+ rm_rf(p, false, true, false);
}
return 0;
exec_command_done(c+i);
}
-void exec_command_free_list(ExecCommand *c) {
+ExecCommand* exec_command_free_list(ExecCommand *c) {
ExecCommand *i;
while ((i = c)) {
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++) {
- exec_command_free_list(c[i]);
- c[i] = NULL;
- }
+ 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) {
return k;
}
/* Log invalid environment variables with filename */
- if (p)
- p = strv_env_clean_log(p, unit_id, pglob.gl_pathv[n]);
+ 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;
fprintf(f, "%sCapabilityBoundingSet:", prefix);
for (l = 0; l <= cap_last_cap(); l++)
- if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l))) {
- _cleanup_cap_free_charp_ char *t;
-
- t = cap_to_name(l);
- if (t)
- fprintf(f, " %s", t);
- }
+ if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l)))
+ fprintf(f, " %s", strna(capability_to_name(l)));
fputs("\n", f);
}
r = asynchronous_job(remove_tmpdir_thread, rt->tmp_dir);
if (r < 0) {
- log_warning("Failed to nuke %s: %s", rt->tmp_dir, strerror(-r));
+ log_warning_errno(r, "Failed to nuke %s: %m", rt->tmp_dir);
free(rt->tmp_dir);
}
r = asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir);
if (r < 0) {
- log_warning("Failed to nuke %s: %s", rt->var_tmp_dir, strerror(-r));
+ log_warning_errno(r, "Failed to nuke %s: %m", rt->var_tmp_dir);
free(rt->var_tmp_dir);
}