+ } else {
+ _cleanup_free_ char *d = NULL;
+
+ if (asprintf(&d, "%s/%s",
+ context->root_directory ? context->root_directory : "",
+ context->working_directory ? context->working_directory : "") < 0) {
+ *error = EXIT_MEMORY;
+ return -ENOMEM;
+ }
+
+ if (chdir(d) < 0) {
+ *error = EXIT_CHDIR;
+ return -errno;
+ }
+ }
+
+#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
+ * and the netns fds we don't need anymore. The custom
+ * endpoint fd was needed to upload the policy and can
+ * now be closed as well. */
+ err = close_all_fds(fds, n_fds);
+ if (err >= 0)
+ err = shift_fds(fds, n_fds);
+ if (err >= 0)
+ err = flags_fds(fds, n_fds, context->non_blocking);
+ if (err < 0) {
+ *error = EXIT_FDS;
+ return err;
+ }
+
+ if (params->apply_permissions) {
+
+ for (i = 0; i < _RLIMIT_MAX; i++) {
+ if (!context->rlimit[i])
+ continue;
+
+ if (setrlimit_closest(i, context->rlimit[i]) < 0) {
+ *error = EXIT_LIMITS;
+ return -errno;
+ }
+ }
+
+ if (context->capability_bounding_set_drop) {
+ err = capability_bounding_set_drop(context->capability_bounding_set_drop, false);
+ if (err < 0) {
+ *error = EXIT_CAPABILITIES;
+ return err;
+ }
+ }
+
+#ifdef HAVE_SMACK
+ if (context->smack_process_label) {
+ err = mac_smack_apply_pid(0, context->smack_process_label);
+ if (err < 0) {
+ *error = EXIT_SMACK_PROCESS_LABEL;
+ return err;
+ }
+ }
+#endif
+
+ if (context->user) {
+ err = enforce_user(context, uid);
+ if (err < 0) {
+ *error = EXIT_USER;
+ return err;
+ }
+ }
+
+ /* PR_GET_SECUREBITS is not privileged, while
+ * PR_SET_SECUREBITS is. So to suppress
+ * potential EPERMs we'll try not to call
+ * PR_SET_SECUREBITS unless necessary. */
+ if (prctl(PR_GET_SECUREBITS) != context->secure_bits)
+ if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) {
+ *error = EXIT_SECUREBITS;
+ return -errno;
+ }
+
+ if (context->capabilities)
+ if (cap_set_proc(context->capabilities) < 0) {
+ *error = EXIT_CAPABILITIES;
+ return -errno;
+ }
+
+ if (context->no_new_privileges)
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
+ *error = EXIT_NO_NEW_PRIVILEGES;
+ return -errno;
+ }
+
+#ifdef HAVE_SECCOMP
+ if (context->address_families_whitelist ||
+ !set_isempty(context->address_families)) {
+ err = apply_address_families(context);
+ if (err < 0) {
+ *error = EXIT_ADDRESS_FAMILIES;
+ return err;
+ }
+ }
+
+ if (context->syscall_whitelist ||
+ !set_isempty(context->syscall_filter) ||
+ !set_isempty(context->syscall_archs)) {
+ err = apply_seccomp(context);
+ if (err < 0) {
+ *error = EXIT_SECCOMP;
+ return err;
+ }
+ }
+#endif
+
+#ifdef HAVE_SELINUX
+ if (mac_selinux_use()) {
+ char *exec_context = mac_selinux_context_net ?: context->selinux_context;
+
+ if (exec_context) {
+ err = setexeccon(exec_context);
+ if (err < 0) {
+ *error = EXIT_SELINUX_CONTEXT;
+ return err;
+ }
+ }
+ }
+#endif
+
+#ifdef HAVE_APPARMOR
+ if (context->apparmor_profile && mac_apparmor_use()) {
+ err = aa_change_onexec(context->apparmor_profile);
+ if (err < 0 && !context->apparmor_profile_ignore) {
+ *error = EXIT_APPARMOR_PROFILE;
+ return -errno;
+ }
+ }
+#endif
+ }
+
+ err = build_environment(context, n_fds, params->watchdog_usec, home, username, shell, &our_env);
+ if (err < 0) {
+ *error = EXIT_MEMORY;
+ return err;
+ }
+
+ final_env = strv_env_merge(5,
+ params->environment,
+ our_env,
+ context->environment,
+ files_env,
+ pam_env,
+ NULL);
+ if (!final_env) {
+ *error = EXIT_MEMORY;
+ return -ENOMEM;
+ }
+
+ final_argv = replace_env_argv(argv, final_env);
+ if (!final_argv) {
+ *error = EXIT_MEMORY;
+ return -ENOMEM;
+ }
+
+ final_env = strv_env_clean(final_env);
+
+ 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(params->unit_id,
+ LOG_DEBUG,
+ "EXECUTABLE=%s", command->path,
+ LOG_MESSAGE("Executing: %s", line),
+ NULL);
+ log_close();
+ }
+ }
+ execve(command->path, final_argv, final_env);
+ *error = EXIT_EXEC;
+ return -errno;
+}
+
+int exec_spawn(ExecCommand *command,
+ const ExecContext *context,
+ const ExecParameters *params,
+ ExecRuntime *runtime,
+ pid_t *ret) {
+
+ _cleanup_strv_free_ char **files_env = NULL;
+ int *fds = NULL; unsigned n_fds = 0;
+ char *line, **argv;
+ int socket_fd;
+ pid_t pid;
+ int err;
+
+ assert(command);
+ assert(context);
+ assert(ret);
+ assert(params);
+ assert(params->fds || params->n_fds <= 0);
+
+ if (context->std_input == EXEC_INPUT_SOCKET ||
+ context->std_output == EXEC_OUTPUT_SOCKET ||
+ context->std_error == EXEC_OUTPUT_SOCKET) {
+
+ if (params->n_fds != 1)
+ return -EINVAL;
+
+ socket_fd = params->fds[0];
+ } else {
+ socket_fd = -1;
+ fds = params->fds;
+ n_fds = params->n_fds;
+ }
+
+ err = exec_context_load_environment(context, params->unit_id, &files_env);
+ if (err < 0) {
+ log_unit_struct(params->unit_id,
+ LOG_ERR,
+ LOG_MESSAGE("Failed to load environment files: %s", strerror(-err)),
+ LOG_ERRNO(-err),
+ NULL);
+ return err;
+ }