int *fds, unsigned n_fds,
char **files_env,
int user_lookup_fd,
- int *exit_status) {
+ int *exit_status,
+ char **error_message) {
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL;
_cleanup_free_ char *mac_selinux_context_net = NULL;
assert(context);
assert(params);
assert(exit_status);
+ assert(error_message);
+ /* We don't always set error_message, hence it must be initialized */
+ assert(*error_message == NULL);
rename_process_from_path(command->path);
r = reset_signal_mask();
if (r < 0) {
*exit_status = EXIT_SIGNAL_MASK;
+ *error_message = strdup("Failed to reset signal mask");
+ /* If strdup fails, here and below, we will just print the generic error message. */
return r;
}
r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, fds, n_fds);
if (r < 0) {
*exit_status = EXIT_FDS;
+ *error_message = strdup("Failed to close remaining fds");
return r;
}
return 0;
}
*exit_status = EXIT_CONFIRM;
+ *error_message = strdup("Execution cancelled");
return -ECANCELED;
}
}
/* Make sure we bypass our own NSS module for any NSS checks */
if (putenv((char*) "SYSTEMD_NSS_DYNAMIC_BYPASS=1") != 0) {
*exit_status = EXIT_USER;
+ *error_message = strdup("Failed to update environment");
return -errno;
}
r = dynamic_creds_realize(dcreds, &uid, &gid);
if (r < 0) {
*exit_status = EXIT_USER;
+ *error_message = strdup("Failed to update dynamic user credentials");
return r;
}
- if (!uid_is_valid(uid) || !gid_is_valid(gid)) {
+ if (!uid_is_valid(uid)) {
*exit_status = EXIT_USER;
+ (void) asprintf(error_message, "UID validation failed for \""UID_FMT"\"", uid);
+ /* If asprintf fails, here and below, we will just print the generic error message. */
+ return -ESRCH;
+ }
+
+ if (!gid_is_valid(gid)) {
+ *exit_status = EXIT_USER;
+ (void) asprintf(error_message, "GID validation failed for \""GID_FMT"\"", gid);
return -ESRCH;
}
r = get_fixed_user(context, &username, &uid, &gid, &home, &shell);
if (r < 0) {
*exit_status = EXIT_USER;
+ *error_message = strdup("Failed to determine user credentials");
return r;
}
r = get_fixed_group(context, &groupname, &gid);
if (r < 0) {
*exit_status = EXIT_GROUP;
+ *error_message = strdup("Failed to determine group credentials");
return r;
}
}
&supplementary_gids, &ngids);
if (r < 0) {
*exit_status = EXIT_GROUP;
+ *error_message = strdup("Failed to determine supplementary groups");
return r;
}
r = send_user_lookup(unit, user_lookup_fd, uid, gid);
if (r < 0) {
*exit_status = EXIT_USER;
+ *error_message = strdup("Failed to send user credentials to PID1");
return r;
}
r = setup_input(context, params, socket_fd, named_iofds);
if (r < 0) {
*exit_status = EXIT_STDIN;
+ *error_message = strdup("Failed to set up stdin");
return r;
}
r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, named_iofds, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDOUT;
+ *error_message = strdup("Failed to set up stdout");
return r;
}
r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, named_iofds, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDERR;
+ *error_message = strdup("Failed to set up stderr");
return r;
}
r = cg_attach_everywhere(params->cgroup_supported, params->cgroup_path, 0, NULL, NULL);
if (r < 0) {
*exit_status = EXIT_CGROUP;
+ (void) asprintf(error_message, "Failed to attach to cgroup %s", params->cgroup_path);
return r;
}
}
log_close();
} else if (r < 0) {
*exit_status = EXIT_OOM_ADJUST;
+ *error_message = strdup("Failed to write /proc/self/oom_score_adj");
return -errno;
}
}
r = capability_bounding_set_drop(context->capability_bounding_set, false);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
+ *error_message = strdup("Failed to drop capabilities");
return r;
}
}
r = capability_ambient_set_apply(context->capability_ambient_set, true);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
+ *error_message = strdup("Failed to apply ambient capabilities (before UID change)");
return r;
}
}
r = enforce_user(context, uid);
if (r < 0) {
*exit_status = EXIT_USER;
+ (void) asprintf(error_message, "Failed to change UID to "UID_FMT, uid);
return r;
}
if (context->capability_ambient_set != 0) {
r = capability_ambient_set_apply(context->capability_ambient_set, false);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
+ *error_message = strdup("Failed to apply ambient capabilities (after UID change)");
return r;
}
r = setexeccon(exec_context);
if (r < 0) {
*exit_status = EXIT_SELINUX_CONTEXT;
+ (void) asprintf(error_message, "Failed to set SELinux context to %s", exec_context);
return r;
}
}
r = setup_smack(context, command);
if (r < 0) {
*exit_status = EXIT_SMACK_PROCESS_LABEL;
+ *error_message = strdup("Failed to set SMACK process label");
return r;
}
r = aa_change_onexec(context->apparmor_profile);
if (r < 0 && !context->apparmor_profile_ignore) {
*exit_status = EXIT_APPARMOR_PROFILE;
+ (void) asprintf(error_message,
+ "Failed to prepare AppArmor profile change to %s",
+ context->apparmor_profile);
return -errno;
}
}
if (prctl(PR_GET_SECUREBITS) != secure_bits)
if (prctl(PR_SET_SECUREBITS, secure_bits) < 0) {
*exit_status = EXIT_SECUREBITS;
+ *error_message = strdup("Failed to set secure bits");
return -errno;
}
if (context_has_no_new_privileges(context))
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
*exit_status = EXIT_NO_NEW_PRIVILEGES;
+ *error_message = strdup("Failed to disable new privileges");
return -errno;
}
r = apply_address_families(unit, context);
if (r < 0) {
*exit_status = EXIT_ADDRESS_FAMILIES;
+ *error_message = strdup("Failed to restrict address families");
return r;
}
}
r = apply_memory_deny_write_execute(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to disable writing to executable memory");
return r;
}
}
r = apply_restrict_realtime(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to apply realtime restrictions");
return r;
}
}
r = apply_restrict_namespaces(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to apply namespace restrictions");
return r;
}
r = apply_protect_sysctl(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to apply sysctl restrictions");
return r;
}
}
r = apply_protect_kernel_modules(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to apply module loading restrictions");
return r;
}
}
r = apply_private_devices(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to set up private devices");
return r;
}
}
r = apply_seccomp(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to apply syscall filters");
return r;
}
}
final_argv = replace_env_argv(argv, accum_env);
if (!final_argv) {
*exit_status = EXIT_MEMORY;
+ *error_message = strdup("Failed to prepare process arguments");
return -ENOMEM;
}
if (pid == 0) {
int exit_status;
+ _cleanup_free_ char *error_message = NULL;
r = exec_child(unit,
command,
fds, n_fds,
files_env,
unit->manager->user_lookup_fds[1],
- &exit_status);
+ &exit_status,
+ &error_message);
if (r < 0) {
log_open();
- log_struct_errno(LOG_ERR, r,
- LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
- LOG_UNIT_ID(unit),
- LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m",
- exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD),
- command->path),
- "EXECUTABLE=%s", command->path,
- NULL);
+ if (error_message)
+ log_struct_errno(LOG_ERR, r,
+ LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
+ LOG_UNIT_ID(unit),
+ LOG_UNIT_MESSAGE(unit, "%s: %m",
+ error_message),
+ "EXECUTABLE=%s", command->path,
+ NULL);
+ else
+ log_struct_errno(LOG_ERR, r,
+ LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
+ LOG_UNIT_ID(unit),
+ LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m",
+ exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD),
+ command->path),
+ "EXECUTABLE=%s", command->path,
+ NULL);
}
_exit(exit_status);