#include "hexdecoct.h"
#include "io-util.h"
#include "iovec-util.h"
+#include "journal-send.h"
#include "missing_ioprio.h"
#include "missing_prctl.h"
#include "missing_securebits.h"
#define SNDBUF_SIZE (8*1024*1024)
-static int shift_fds(int fds[], size_t n_fds) {
- if (n_fds <= 0)
- return 0;
-
- /* Modifies the fds array! (sorts it) */
-
- assert(fds);
-
- for (int start = 0;;) {
- int restart_from = -1;
-
- for (int i = start; i < (int) n_fds; i++) {
- int nfd;
-
- /* Already at right index? */
- if (fds[i] == i+3)
- continue;
-
- nfd = fcntl(fds[i], F_DUPFD, i + 3);
- if (nfd < 0)
- return -errno;
-
- safe_close(fds[i]);
- fds[i] = nfd;
-
- /* Hmm, the fd we wanted isn't free? Then
- * let's remember that and try again from here */
- if (nfd != i+3 && restart_from < 0)
- restart_from = i;
- }
-
- if (restart_from < 0)
- break;
-
- start = restart_from;
- }
-
- return 0;
-}
-
static int flag_fds(
const int fds[],
size_t n_socket_fds,
const char *j;
int r;
- j = log_namespace ?
- strjoina("/run/systemd/journal.", log_namespace, "/stdout") :
- "/run/systemd/journal/stdout";
+ assert(fd >= 0);
+
+ j = journal_stream_path(log_namespace);
+ if (!j)
+ return -EINVAL;
if (gid_is_valid(gid)) {
oldgid = getgid();
case EXEC_INPUT_DATA: {
int fd;
- fd = acquire_data_fd(context->stdin_data, context->stdin_data_size, 0);
+ fd = acquire_data_fd_full(context->stdin_data, context->stdin_data_size, /* flags = */ 0);
if (fd < 0)
return fd;
return PAM_CONV_ERR;
}
+static int pam_close_session_and_delete_credentials(pam_handle_t *handle, int flags) {
+ int r, s;
+
+ assert(handle);
+
+ r = pam_close_session(handle, flags);
+ if (r != PAM_SUCCESS)
+ log_debug("pam_close_session() failed: %s", pam_strerror(handle, r));
+
+ s = pam_setcred(handle, PAM_DELETE_CRED | flags);
+ if (s != PAM_SUCCESS)
+ log_debug("pam_setcred(PAM_DELETE_CRED) failed: %s", pam_strerror(handle, s));
+
+ return r != PAM_SUCCESS ? r : s;
+}
+
#endif
static int setup_pam(
pam_code = pam_setcred(handle, PAM_ESTABLISH_CRED | flags);
if (pam_code != PAM_SUCCESS)
- log_debug("pam_setcred() failed, ignoring: %s", pam_strerror(handle, pam_code));
+ log_debug("pam_setcred(PAM_ESTABLISH_CRED) failed, ignoring: %s", pam_strerror(handle, pam_code));
pam_code = pam_open_session(handle, flags);
if (pam_code != PAM_SUCCESS)
/* Block SIGTERM, so that we know that it won't get lost in the child */
- assert_se(sigprocmask_many(SIG_BLOCK, &old_ss, SIGTERM, -1) >= 0);
+ assert_se(sigprocmask_many(SIG_BLOCK, &old_ss, SIGTERM) >= 0);
parent_pid = getpid_cached();
assert(sig == SIGTERM);
}
- pam_code = pam_setcred(handle, PAM_DELETE_CRED | flags);
- if (pam_code != PAM_SUCCESS)
- goto child_finish;
-
/* If our parent died we'll end the session */
if (getppid() != parent_pid) {
- pam_code = pam_close_session(handle, flags);
+ pam_code = pam_close_session_and_delete_credentials(handle, flags);
if (pam_code != PAM_SUCCESS)
goto child_finish;
}
if (handle) {
if (close_session)
- pam_code = pam_close_session(handle, flags);
+ pam_code = pam_close_session_and_delete_credentials(handle, flags);
(void) pam_end(handle, pam_code | flags);
}
* to inherit the $TERM set for PID 1. This is useful for containers so that the $TERM the
* container manager passes to PID 1 ends up all the way in the console login shown. */
- if (path_equal_ptr(tty_path, "/dev/console") && getppid() == 1)
+ if (path_equal(tty_path, "/dev/console") && getppid() == 1)
term = getenv("TERM");
else if (tty_path && in_charset(skip_dev_prefix(tty_path), ALPHANUMERICAL)) {
_cleanup_free_ char *key = NULL;
const ExecParameters *params,
ExecRuntime *runtime,
const char *memory_pressure_path,
+ bool needs_sandboxing,
char **error_path) {
_cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
*extension_dir = NULL, *host_os_release_stage = NULL, *root_image = NULL, *root_dir = NULL;
const char *tmp_dir = NULL, *var_tmp_dir = NULL;
char **read_write_paths;
- bool needs_sandboxing, setup_os_release_symlink;
+ bool setup_os_release_symlink;
BindMount *bind_mounts = NULL;
size_t n_bind_mounts = 0;
int r;
} else
read_write_paths = context->read_write_paths;
- 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 that is sticky, and that's the one we want to
params,
"shared mount propagation hidden by other fs namespacing unit settings: ignoring");
- if (FLAGS_SET(params->flags, EXEC_WRITE_CREDENTIALS)) {
- r = exec_context_get_credential_directory(context, params, params->unit_id, &creds_path);
- if (r < 0)
- return r;
- }
+ r = exec_context_get_credential_directory(context, params, params->unit_id, &creds_path);
+ if (r < 0)
+ return r;
if (params->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
propagate_dir = path_join("/run/systemd/propagate/", params->unit_id);
const char *home,
int *exit_status) {
- const char *d, *wd;
+ const char *wd;
+ int r;
assert(context);
assert(exit_status);
if (context->working_directory_home) {
-
if (!home) {
*exit_status = EXIT_CHDIR;
return -ENXIO;
}
wd = home;
-
} else
wd = empty_to_root(context->working_directory);
if (params->flags & EXEC_APPLY_CHROOT)
- d = wd;
- else
- d = prefix_roota((runtime ? runtime->ephemeral_copy : NULL) ?: context->root_directory, wd);
+ r = RET_NERRNO(chdir(wd));
+ else {
+ _cleanup_close_ int dfd = -EBADF;
- if (chdir(d) < 0 && !context->working_directory_missing_ok) {
+ r = chase(wd,
+ (runtime ? runtime->ephemeral_copy : NULL) ?: context->root_directory,
+ CHASE_PREFIX_ROOT|CHASE_AT_RESOLVE_IN_ROOT,
+ /* ret_path= */ NULL,
+ &dfd);
+ if (r >= 0)
+ r = RET_NERRNO(fchdir(dfd));
+ }
+
+ if (r < 0 && !context->working_directory_missing_ok) {
*exit_status = EXIT_CHDIR;
- return -errno;
+ return r;
}
return 0;
#if ENABLE_UTMP
if (context->utmp_id) {
+ _cleanup_free_ char *username_alloc = NULL;
+
+ if (!username && context->utmp_mode == EXEC_UTMP_USER) {
+ username_alloc = uid_to_name(uid_is_valid(uid) ? uid : saved_uid);
+ if (!username_alloc) {
+ *exit_status = EXIT_USER;
+ return log_oom();
+ }
+ }
+
const char *line = context->tty_path ?
(path_startswith(context->tty_path, "/dev/") ?: context->tty_path) :
NULL;
context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS :
context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS :
USER_PROCESS,
- username);
+ username ?: username_alloc);
}
#endif
return log_exec_error_errno(context, params, r, "Failed to set up special execution directory in %s: %m", params->prefix[dt]);
}
- if (FLAGS_SET(params->flags, EXEC_WRITE_CREDENTIALS)) {
- r = exec_setup_credentials(context, params, params->unit_id, uid, gid);
- if (r < 0) {
- *exit_status = EXIT_CREDENTIALS;
- return log_exec_error_errno(context, params, r, "Failed to set up credentials: %m");
- }
+ r = exec_setup_credentials(context, params, params->unit_id, uid, gid);
+ if (r < 0) {
+ *exit_status = EXIT_CREDENTIALS;
+ return log_exec_error_errno(context, params, r, "Failed to set up credentials: %m");
}
r = build_environment(
if (needs_mount_namespace) {
_cleanup_free_ char *error_path = NULL;
- r = apply_mount_namespace(command->flags, context, params, runtime, memory_pressure_path, &error_path);
+ r = apply_mount_namespace(command->flags,
+ context,
+ params,
+ runtime,
+ memory_pressure_path,
+ needs_sandboxing,
+ &error_path);
if (r < 0) {
*exit_status = EXIT_NAMESPACE;
return log_exec_error_errno(context, params, r, "Failed to set up mount namespacing%s%s: %m",
}
if (context->memory_ksm >= 0)
- if (prctl(PR_SET_MEMORY_MERGE, context->memory_ksm) < 0) {
+ if (prctl(PR_SET_MEMORY_MERGE, context->memory_ksm, 0, 0, 0) < 0) {
if (ERRNO_IS_NOT_SUPPORTED(errno))
log_exec_debug_errno(context,
params,
_cleanup_close_ int executable_fd = -EBADF;
r = find_executable_full(command->path, /* root= */ NULL, context->exec_search_path, false, &executable, &executable_fd);
if (r < 0) {
- if (r != -ENOMEM && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) {
- log_exec_struct_errno(context, params, LOG_INFO, r,
- "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
- LOG_EXEC_INVOCATION_ID(params),
- LOG_EXEC_MESSAGE(params,
- "Executable %s missing, skipping: %m",
- command->path),
- "EXECUTABLE=%s", command->path);
- *exit_status = EXIT_SUCCESS;
- return 0;
- }
-
*exit_status = EXIT_EXEC;
- return log_exec_struct_errno(context, params, LOG_INFO, r,
- "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
- LOG_EXEC_INVOCATION_ID(params),
- LOG_EXEC_MESSAGE(params,
- "Failed to locate executable %s: %m",
- command->path),
- "EXECUTABLE=%s", command->path);
+ log_exec_struct_errno(context, params, LOG_NOTICE, r,
+ "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
+ LOG_EXEC_MESSAGE(params,
+ "Unable to locate executable '%s': %m",
+ command->path),
+ "EXECUTABLE=%s", command->path);
+ /* If the error will be ignored by manager, tune down the log level here. Missing executable
+ * is very much expected in this case. */
+ return r != -ENOMEM && FLAGS_SET(command->flags, EXEC_COMMAND_IGNORE_FAILURE) ? 1 : r;
}
r = add_shifted_fd(keep_fds, ELEMENTSOF(keep_fds), &n_keep_fds, &executable_fd);
r = close_all_fds(keep_fds, n_keep_fds);
if (r >= 0)
- r = shift_fds(params->fds, n_fds);
+ r = pack_fds(params->fds, n_fds);
if (r >= 0)
r = flag_fds(params->fds, n_socket_fds, n_fds, context->non_blocking);
if (r < 0) {
}
}
- /* Apply working directory here, because the working directory might be on NFS and only the user running
- * this service might have the correct privilege to change to the working directory */
+ /* Apply working directory here, because the working directory might be on NFS and only the user
+ * running this service might have the correct privilege to change to the working directory. Also, it
+ * is absolutely 💣 crucial 💣 we applied all mount namespacing rearrangements before this, so that
+ * the cwd cannot be used to pin directories outside of the sandbox. */
r = apply_working_directory(context, params, runtime, home, exit_status);
if (r < 0)
return log_exec_error_errno(context, params, r, "Changing to the requested working directory failed: %m");