+#ifdef HAVE_PAM
+
+static int null_conv(
+ int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr) {
+
+ /* We don't support conversations */
+
+ return PAM_CONV_ERR;
+}
+
+static int setup_pam(
+ const char *name,
+ const char *user,
+ const char *tty,
+ char ***pam_env,
+ int fds[], unsigned n_fds) {
+
+ static const struct pam_conv conv = {
+ .conv = null_conv,
+ .appdata_ptr = NULL
+ };
+
+ pam_handle_t *handle = NULL;
+ sigset_t ss, old_ss;
+ int pam_code = PAM_SUCCESS;
+ int err;
+ char **e = NULL;
+ bool close_session = false;
+ pid_t pam_pid = 0, parent_pid;
+
+ assert(name);
+ assert(user);
+ assert(pam_env);
+
+ /* We set up PAM in the parent process, then fork. The child
+ * will then stay around until killed via PR_GET_PDEATHSIG or
+ * systemd via the cgroup logic. It will then remove the PAM
+ * session again. The parent process will exec() the actual
+ * daemon. We do things this way to ensure that the main PID
+ * of the daemon is the one we initially fork()ed. */
+
+ if ((pam_code = pam_start(name, user, &conv, &handle)) != PAM_SUCCESS) {
+ handle = NULL;
+ goto fail;
+ }
+
+ if (tty)
+ if ((pam_code = pam_set_item(handle, PAM_TTY, tty)) != PAM_SUCCESS)
+ goto fail;
+
+ if ((pam_code = pam_acct_mgmt(handle, PAM_SILENT)) != PAM_SUCCESS)
+ goto fail;
+
+ if ((pam_code = pam_open_session(handle, PAM_SILENT)) != PAM_SUCCESS)
+ goto fail;
+
+ close_session = true;
+
+ if ((!(e = pam_getenvlist(handle)))) {
+ pam_code = PAM_BUF_ERR;
+ goto fail;
+ }
+
+ /* Block SIGTERM, so that we know that it won't get lost in
+ * the child */
+ if (sigemptyset(&ss) < 0 ||
+ sigaddset(&ss, SIGTERM) < 0 ||
+ sigprocmask(SIG_BLOCK, &ss, &old_ss) < 0)
+ goto fail;
+
+ parent_pid = getpid();
+
+ if ((pam_pid = fork()) < 0)
+ goto fail;
+
+ if (pam_pid == 0) {
+ int sig;
+ int r = EXIT_PAM;
+
+ /* The child's job is to reset the PAM session on
+ * termination */
+
+ /* This string must fit in 10 chars (i.e. the length
+ * of "/sbin/init") */
+ rename_process("sd(PAM)");
+
+ /* Make sure we don't keep open the passed fds in this
+ child. We assume that otherwise only those fds are
+ open here that have been opened by PAM. */
+ close_many(fds, n_fds);
+
+ /* Wait until our parent died. This will most likely
+ * not work since the kernel does not allow
+ * unprivileged parents kill their privileged children
+ * this way. We rely on the control groups kill logic
+ * to do the rest for us. */
+ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
+ goto child_finish;
+
+ /* Check if our parent process might already have
+ * died? */
+ if (getppid() == parent_pid) {
+ for (;;) {
+ if (sigwait(&ss, &sig) < 0) {
+ if (errno == EINTR)
+ continue;
+
+ goto child_finish;
+ }
+
+ assert(sig == SIGTERM);
+ break;
+ }
+ }
+
+ /* If our parent died we'll end the session */
+ if (getppid() != parent_pid)
+ if ((pam_code = pam_close_session(handle, PAM_DATA_SILENT)) != PAM_SUCCESS)
+ goto child_finish;
+
+ r = 0;
+
+ child_finish:
+ pam_end(handle, pam_code | PAM_DATA_SILENT);
+ _exit(r);
+ }
+
+ /* If the child was forked off successfully it will do all the
+ * cleanups, so forget about the handle here. */
+ handle = NULL;
+
+ /* Unblock SIGTERM again in the parent */
+ if (sigprocmask(SIG_SETMASK, &old_ss, NULL) < 0)
+ goto fail;
+
+ /* We close the log explicitly here, since the PAM modules
+ * might have opened it, but we don't want this fd around. */
+ closelog();
+
+ *pam_env = e;
+ e = NULL;
+
+ return 0;
+
+fail:
+ if (pam_code != PAM_SUCCESS)
+ err = -EPERM; /* PAM errors do not map to errno */
+ else
+ err = -errno;
+
+ if (handle) {
+ if (close_session)
+ pam_code = pam_close_session(handle, PAM_DATA_SILENT);
+
+ pam_end(handle, pam_code | PAM_DATA_SILENT);
+ }
+
+ strv_free(e);
+
+ closelog();
+
+ if (pam_pid > 1) {
+ kill(pam_pid, SIGTERM);
+ kill(pam_pid, SIGCONT);
+ }
+
+ return err;
+}
+#endif
+
+static int do_capability_bounding_set_drop(uint64_t drop) {
+ unsigned long i;
+ cap_t old_cap = NULL, new_cap = NULL;
+ cap_flag_value_t fv;
+ int r;
+
+ /* If we are run as PID 1 we will lack CAP_SETPCAP by default
+ * in the effective set (yes, the kernel drops that when
+ * executing init!), so get it back temporarily so that we can
+ * call PR_CAPBSET_DROP. */
+
+ old_cap = cap_get_proc();
+ if (!old_cap)
+ return -errno;
+
+ if (cap_get_flag(old_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (fv != CAP_SET) {
+ static const cap_value_t v = CAP_SETPCAP;
+
+ new_cap = cap_dup(old_cap);
+ if (!new_cap) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (cap_set_flag(new_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (cap_set_proc(new_cap) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ }
+
+ for (i = 0; i <= cap_last_cap(); i++)
+ if (drop & ((uint64_t) 1ULL << (uint64_t) i)) {
+ if (prctl(PR_CAPBSET_DROP, i) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ }
+
+ r = 0;
+
+finish:
+ if (new_cap)
+ cap_free(new_cap);
+
+ if (old_cap) {
+ cap_set_proc(old_cap);
+ cap_free(old_cap);
+ }
+
+ return r;
+}
+