Merge pull request #17143 from keszybz/late-exec-resolution-alt
Late exec resolution (subset)
This commit is contained in:
+79
-66
@@ -2881,12 +2881,11 @@ static int setup_credentials(
|
||||
#if ENABLE_SMACK
|
||||
static int setup_smack(
|
||||
const ExecContext *context,
|
||||
const ExecCommand *command) {
|
||||
|
||||
const char *executable) {
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
assert(command);
|
||||
assert(executable);
|
||||
|
||||
if (context->smack_process_label) {
|
||||
r = mac_smack_apply_pid(0, context->smack_process_label);
|
||||
@@ -2897,7 +2896,7 @@ static int setup_smack(
|
||||
else {
|
||||
_cleanup_free_ char *exec_label = NULL;
|
||||
|
||||
r = mac_smack_read(command->path, SMACK_ATTR_EXEC, &exec_label);
|
||||
r = mac_smack_read(executable, SMACK_ATTR_EXEC, &exec_label);
|
||||
if (r < 0 && !IN_SET(r, -ENODATA, -EOPNOTSUPP))
|
||||
return r;
|
||||
|
||||
@@ -3092,7 +3091,7 @@ static bool insist_on_sandboxing(
|
||||
|
||||
static int apply_mount_namespace(
|
||||
const Unit *u,
|
||||
const ExecCommand *command,
|
||||
ExecCommandFlags command_flags,
|
||||
const ExecContext *context,
|
||||
const ExecParameters *params,
|
||||
const ExecRuntime *runtime,
|
||||
@@ -3121,7 +3120,7 @@ static int apply_mount_namespace(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
needs_sandboxing = (params->flags & EXEC_APPLY_SANDBOXING) && !(command->flags & EXEC_COMMAND_FULLY_PRIVILEGED);
|
||||
needs_sandboxing = (params->flags & EXEC_APPLY_SANDBOXING) && !(command_flags & EXEC_COMMAND_FULLY_PRIVILEGED);
|
||||
if (needs_sandboxing) {
|
||||
/* The runtime struct only contains the parent of the private /tmp,
|
||||
* which is non-accessible to world users. Inside of it there's a /tmp
|
||||
@@ -3700,11 +3699,11 @@ static int exec_child(
|
||||
return log_unit_error_errno(unit, r, "Failed to close unwanted file descriptors: %m");
|
||||
}
|
||||
|
||||
if (!context->same_pgrp)
|
||||
if (setsid() < 0) {
|
||||
*exit_status = EXIT_SETSID;
|
||||
return log_unit_error_errno(unit, errno, "Failed to create new process session: %m");
|
||||
}
|
||||
if (!context->same_pgrp &&
|
||||
setsid() < 0) {
|
||||
*exit_status = EXIT_SETSID;
|
||||
return log_unit_error_errno(unit, errno, "Failed to create new process session: %m");
|
||||
}
|
||||
|
||||
exec_context_tty_reset(context, params);
|
||||
|
||||
@@ -3725,8 +3724,8 @@ static int exec_child(
|
||||
return 0;
|
||||
}
|
||||
*exit_status = EXIT_CONFIRM;
|
||||
log_unit_error(unit, "Execution cancelled by the user");
|
||||
return -ECANCELED;
|
||||
return log_unit_error_errno(unit, SYNTHETIC_ERRNO(ECANCELED),
|
||||
"Execution cancelled by the user");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4085,47 +4084,33 @@ static int exec_child(
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_setuid) {
|
||||
|
||||
if (needs_setuid && context->pam_name && username) {
|
||||
/* Let's call into PAM after we set up our own idea of resource limits to that pam_limits
|
||||
* wins here. (See above.) */
|
||||
|
||||
if (context->pam_name && username) {
|
||||
r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, fds, n_fds);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_PAM;
|
||||
return log_unit_error_errno(unit, r, "Failed to set up PAM session: %m");
|
||||
}
|
||||
r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, fds, n_fds);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_PAM;
|
||||
return log_unit_error_errno(unit, r, "Failed to set up PAM session: %m");
|
||||
}
|
||||
|
||||
ngids_after_pam = getgroups_alloc(&gids_after_pam);
|
||||
if (ngids_after_pam < 0) {
|
||||
*exit_status = EXIT_MEMORY;
|
||||
return log_unit_error_errno(unit, ngids_after_pam, "Failed to obtain groups after setting up PAM: %m");
|
||||
}
|
||||
ngids_after_pam = getgroups_alloc(&gids_after_pam);
|
||||
if (ngids_after_pam < 0) {
|
||||
*exit_status = EXIT_MEMORY;
|
||||
return log_unit_error_errno(unit, ngids_after_pam, "Failed to obtain groups after setting up PAM: %m");
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_sandboxing) {
|
||||
#if HAVE_SELINUX
|
||||
if (use_selinux && params->selinux_context_net && socket_fd >= 0) {
|
||||
r = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_SELINUX_CONTEXT;
|
||||
return log_unit_error_errno(unit, r, "Failed to determine SELinux context: %m");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (needs_sandboxing && context->private_users && !have_effective_cap(CAP_SYS_ADMIN)) {
|
||||
/* If we're unprivileged, set up the user namespace first to enable use of the other namespaces.
|
||||
* Users with CAP_SYS_ADMIN can set up user namespaces last because they will be able to
|
||||
* set up the all of the other namespaces (i.e. network, mount, UTS) without a user namespace. */
|
||||
if (context->private_users && !have_effective_cap(CAP_SYS_ADMIN)) {
|
||||
userns_set_up = true;
|
||||
r = setup_private_users(saved_uid, saved_gid, uid, gid);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_USER;
|
||||
return log_unit_error_errno(unit, r, "Failed to set up user namespacing for unprivileged user: %m");
|
||||
}
|
||||
|
||||
userns_set_up = true;
|
||||
r = setup_private_users(saved_uid, saved_gid, uid, gid);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_USER;
|
||||
return log_unit_error_errno(unit, r, "Failed to set up user namespacing for unprivileged user: %m");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4152,7 +4137,7 @@ static int exec_child(
|
||||
if (needs_mount_namespace) {
|
||||
_cleanup_free_ char *error_path = NULL;
|
||||
|
||||
r = apply_mount_namespace(unit, command, context, params, runtime, &error_path);
|
||||
r = apply_mount_namespace(unit, command->flags, context, params, runtime, &error_path);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_NAMESPACE;
|
||||
return log_unit_error_errno(unit, r, "Failed to set up mount namespacing%s%s: %m",
|
||||
@@ -4206,6 +4191,43 @@ static int exec_child(
|
||||
}
|
||||
}
|
||||
|
||||
/* Now that the mount namespace has been set up and privileges adjusted, let's look for the thing we
|
||||
* shall execute. */
|
||||
|
||||
_cleanup_free_ char *executable = NULL;
|
||||
r = find_executable_full(command->path, false, &executable);
|
||||
if (r < 0) {
|
||||
if (r != -ENOMEM && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) {
|
||||
log_struct_errno(LOG_INFO, r,
|
||||
"MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
|
||||
LOG_UNIT_ID(unit),
|
||||
LOG_UNIT_INVOCATION_ID(unit),
|
||||
LOG_UNIT_MESSAGE(unit, "Executable %s missing, skipping: %m",
|
||||
command->path),
|
||||
"EXECUTABLE=%s", command->path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*exit_status = EXIT_EXEC;
|
||||
return log_struct_errno(LOG_INFO, r,
|
||||
"MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
|
||||
LOG_UNIT_ID(unit),
|
||||
LOG_UNIT_INVOCATION_ID(unit),
|
||||
LOG_UNIT_MESSAGE(unit, "Failed to locate executable %s: %m",
|
||||
command->path),
|
||||
"EXECUTABLE=%s", command->path);
|
||||
}
|
||||
|
||||
#if HAVE_SELINUX
|
||||
if (needs_sandboxing && use_selinux && params->selinux_context_net && socket_fd >= 0) {
|
||||
r = mac_selinux_get_child_mls_label(socket_fd, executable, context->selinux_context, &mac_selinux_context_net);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_SELINUX_CONTEXT;
|
||||
return log_unit_error_errno(unit, r, "Failed to determine SELinux context: %m");
|
||||
}
|
||||
}
|
||||
#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 and the netns fds we don't need anymore. We do keep the exec_fd
|
||||
* however if we have it as we want to keep it open until the final execve(). */
|
||||
@@ -4225,8 +4247,7 @@ static int exec_child(
|
||||
return log_unit_error_errno(unit, errno, "Couldn't move exec fd up: %m");
|
||||
}
|
||||
|
||||
safe_close(exec_fd);
|
||||
exec_fd = moved_fd;
|
||||
CLOSE_AND_REPLACE(exec_fd, moved_fd);
|
||||
} else {
|
||||
/* This fd should be FD_CLOEXEC already, but let's make sure. */
|
||||
r = fd_cloexec(exec_fd, true);
|
||||
@@ -4279,7 +4300,7 @@ static int exec_child(
|
||||
/* LSM Smack needs the capability CAP_MAC_ADMIN to change the current execution security context of the
|
||||
* process. This is the latest place before dropping capabilities. Other MAC context are set later. */
|
||||
if (use_smack) {
|
||||
r = setup_smack(context, command);
|
||||
r = setup_smack(context, executable);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_SMACK_PROCESS_LABEL;
|
||||
return log_unit_error_errno(unit, r, "Failed to set SMACK process label: %m");
|
||||
@@ -4531,7 +4552,7 @@ static int exec_child(
|
||||
line = exec_command_line(final_argv);
|
||||
if (line)
|
||||
log_struct(LOG_DEBUG,
|
||||
"EXECUTABLE=%s", command->path,
|
||||
"EXECUTABLE=%s", executable,
|
||||
LOG_UNIT_MESSAGE(unit, "Executing: %s", line),
|
||||
LOG_UNIT_ID(unit),
|
||||
LOG_UNIT_INVOCATION_ID(unit));
|
||||
@@ -4549,7 +4570,7 @@ static int exec_child(
|
||||
}
|
||||
}
|
||||
|
||||
execve(command->path, final_argv, accum_env);
|
||||
execve(executable, final_argv, accum_env);
|
||||
r = -errno;
|
||||
|
||||
if (exec_fd >= 0) {
|
||||
@@ -4564,19 +4585,8 @@ static int exec_child(
|
||||
}
|
||||
}
|
||||
|
||||
if (r == -ENOENT && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) {
|
||||
log_struct_errno(LOG_INFO, r,
|
||||
"MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
|
||||
LOG_UNIT_ID(unit),
|
||||
LOG_UNIT_INVOCATION_ID(unit),
|
||||
LOG_UNIT_MESSAGE(unit, "Executable %s missing, skipping: %m",
|
||||
command->path),
|
||||
"EXECUTABLE=%s", command->path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*exit_status = EXIT_EXEC;
|
||||
return log_unit_error_errno(unit, r, "Failed to execute command: %m");
|
||||
return log_unit_error_errno(unit, r, "Failed to execute %s: %m", executable);
|
||||
}
|
||||
|
||||
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l);
|
||||
@@ -4638,13 +4648,16 @@ int exec_spawn(Unit *unit,
|
||||
if (!line)
|
||||
return log_oom();
|
||||
|
||||
/* fork with up-to-date SELinux label database, so the child inherits the up-to-date db
|
||||
and, until the next SELinux policy changes, we safe further reloads in future children */
|
||||
/* Fork with up-to-date SELinux label database, so the child inherits the up-to-date db
|
||||
and, until the next SELinux policy changes, we save further reloads in future children. */
|
||||
mac_selinux_maybe_reload();
|
||||
|
||||
log_struct(LOG_DEBUG,
|
||||
LOG_UNIT_MESSAGE(unit, "About to execute: %s", line),
|
||||
"EXECUTABLE=%s", command->path,
|
||||
LOG_UNIT_MESSAGE(unit, "About to execute %s", line),
|
||||
"EXECUTABLE=%s", command->path, /* We won't know the real executable path until we create
|
||||
the mount namespace in the child, but we want to log
|
||||
from the parent, so we need to use the (possibly
|
||||
inaccurate) path here. */
|
||||
LOG_UNIT_ID(unit),
|
||||
LOG_UNIT_INVOCATION_ID(unit));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user