+void
+pamcleanup(int ret, int sess, int cred)
+{
+ if (sess) {
+ ret = pam_close_session(pamh, 0);
+ if (ret != PAM_SUCCESS)
+ errx(1, "pam_close_session: %s", pam_strerror(pamh, ret));
+ }
+ if (cred) {
+ ret = pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT);
+ if (ret != PAM_SUCCESS)
+ warn("pam_setcred(?, PAM_DELETE_CRED | PAM_SILENT): %s",
+ pam_strerror(pamh, ret));
+ }
+ pam_end(pamh, ret);
+}
+
+void
+watchsession(pid_t child, int sess, int cred)
+{
+ sigset_t sigs;
+ struct sigaction act, oldact;
+ int status = 1;
+
+ /* block signals */
+ sigfillset(&sigs);
+ if (sigprocmask(SIG_BLOCK, &sigs, NULL)) {
+ warn("failed to block signals");
+ caught_signal = 1;
+ goto close;
+ }
+
+ /* setup signal handler */
+ act.sa_handler = catchsig;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ /* unblock SIGTERM and SIGALRM to catch them */
+ sigemptyset(&sigs);
+ if (sigaddset(&sigs, SIGTERM) ||
+ sigaddset(&sigs, SIGALRM) ||
+ sigaddset(&sigs, SIGTSTP) ||
+ sigaction(SIGTERM, &act, &oldact) ||
+ sigprocmask(SIG_UNBLOCK, &sigs, NULL)) {
+ warn("failed to set signal handler");
+ caught_signal = 1;
+ goto close;
+ }
+
+ /* wait for child to be terminated */
+ if (waitpid(child, &status, 0) != -1) {
+ if (WIFSIGNALED(status)) {
+ fprintf(stderr, "%s%s\n", strsignal(WTERMSIG(status)),
+ WCOREDUMP(status) ? " (core dumped)" : "");
+ status = WTERMSIG(status) + 128;
+ } else
+ status = WEXITSTATUS(status);
+ }
+ else if (caught_signal)
+ status = caught_signal + 128;
+ else
+ status = 1;
+
+close:
+ if (caught_signal && child != (pid_t)-1) {
+ fprintf(stderr, "\nSession terminated, killing shell\n");
+ kill(child, SIGTERM);
+ }
+
+ pamcleanup(PAM_SUCCESS, sess, cred);
+
+ if (caught_signal) {
+ if (child != (pid_t)-1) {
+ /* kill child */
+ sleep(2);
+ kill(child, SIGKILL);
+ fprintf(stderr, " ...killed.\n");
+ }
+
+ /* unblock cached signal and resend */
+ sigaction(SIGTERM, &oldact, NULL);
+ if (caught_signal != SIGTERM)
+ caught_signal = SIGKILL;
+ kill(getpid(), caught_signal);
+ }
+
+ exit(status);
+}
+
+void
+pamauth(const char *user, const char *myname, int interactive, int nopass, int persist)