[PATCH] Password expiry with Privsep and PAM

Darren Tucker dtucker at zip.com.au
Tue Dec 10 23:51:16 EST 2002


Hi All.
	Attached is a patch that implements password expiry with PAM and
privsep.  It works by passing a descriptor to the tty to the monitor,
which sets up a child with that tty as stdin/stdout/stderr, then runs
chauthtok().  No setuid helpers.

	I used some parts of Michael Steffens' patch (bugid #423) to make it
work on HP-UX.

	It's still rough but it works. Tested on Solaris 8 and HPUX 11 (trusted
configuration).

	Comments?
 
-- 
Darren Tucker (dtucker at zip.com.au)
GPG key 8FF4FA69 / D9A3 86E9 7EEE AF4B B2D4  37C9 C982 80C7 8FF4 FA69
    Good judgement comes with experience. Unfortunately, the experience
usually comes from bad judgement.
-------------- next part --------------
Index: auth-pam.c
===================================================================
RCS file: /cvs/openssh/auth-pam.c,v
retrieving revision 1.54
diff -u -u -r1.54 auth-pam.c
--- auth-pam.c	28 Jul 2002 20:24:08 -0000	1.54
+++ auth-pam.c	10 Dec 2002 12:34:10 -0000
@@ -42,8 +42,6 @@
 
 #define NEW_AUTHTOK_MSG \
 	"Warning: Your password has expired, please change it now."
-#define NEW_AUTHTOK_MSG_PRIVSEP \
-	"Your password has expired, the session cannot proceed."
 
 static int do_pam_conversation(int num_msg, const struct pam_message **msg,
 	struct pam_response **resp, void *appdata_ptr);
@@ -186,12 +184,15 @@
 			    pam_retval, PAM_STRERROR(__pamh, pam_retval));
 	}
 
+/* HP-UX doesn't like credentials to be deleted. Skip and rely on pam_end() */
+#ifndef __hpux
 	if (__pamh && creds_set) {
 		pam_retval = pam_setcred(__pamh, PAM_DELETE_CRED);
 		if (pam_retval != PAM_SUCCESS)
 			debug("Cannot delete credentials[%d]: %.200s", 
 			    pam_retval, PAM_STRERROR(__pamh, pam_retval));
 	}
+#endif
 
 	if (__pamh) {
 		pam_retval = pam_end(__pamh, pam_retval);
@@ -256,10 +257,8 @@
 		case PAM_SUCCESS:
 			/* This is what we want */
 			break;
-#if 0
 		case PAM_NEW_AUTHTOK_REQD:
-			message_cat(&__pam_msg, use_privsep ?
-			    NEW_AUTHTOK_MSG_PRIVSEP : NEW_AUTHTOK_MSG);
+			message_cat(&__pam_msg, NEW_AUTHTOK_MSG);
 			/* flag that password change is necessary */
 			password_change_required = 1;
 			/* disallow other functionality for now */
@@ -267,7 +266,6 @@
 			no_agent_forwarding_flag |= 2;
 			no_x11_forwarding_flag |= 2;
 			break;
-#endif
 		default:
 			log("PAM rejected by account configuration[%d]: "
 			    "%.200s", pam_retval, PAM_STRERROR(__pamh, 
@@ -301,6 +299,18 @@
 	session_opened = 1;
 }
 
+/* Set the TTY after session is open */
+void do_pam_set_tty(const char *ttyname) {
+	int pam_retval;
+	if (ttyname != NULL) {
+		debug("PAM setting tty to \"%.200s\"", ttyname);
+		pam_retval = pam_set_item(__pamh, PAM_TTY, ttyname);
+		if (pam_retval != PAM_SUCCESS)
+			fatal("PAM set tty failed[%d]: %.200s",
+			    pam_retval, PAM_STRERROR(__pamh, pam_retval));
+	}
+}
+
 /* Set PAM credentials */
 void do_pam_setcred(int init)
 {
@@ -344,17 +354,15 @@
 	do_pam_set_conv(&conv);
 
 	if (password_change_required) {
-		if (use_privsep)
-			fatal("Password changing is currently unsupported"
-			    " with privilege separation");
 		pamstate = OTHER;
 		pam_retval = pam_chauthtok(__pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
 		if (pam_retval != PAM_SUCCESS)
 			fatal("PAM pam_chauthtok failed[%d]: %.200s",
 			    pam_retval, PAM_STRERROR(__pamh, pam_retval));
-#if 0
 		/* XXX: This would need to be done in the parent process,
 		 * but there's currently no way to pass such request. */
+		password_change_required = 0;
+#if 0
 		no_port_forwarding_flag &= ~2;
 		no_agent_forwarding_flag &= ~2;
 		no_x11_forwarding_flag &= ~2;
Index: auth-pam.h
===================================================================
RCS file: /cvs/openssh/auth-pam.h,v
retrieving revision 1.16
diff -u -u -r1.16 auth-pam.h
--- auth-pam.h	23 Jul 2002 00:44:07 -0000	1.16
+++ auth-pam.h	10 Dec 2002 12:34:10 -0000
@@ -25,6 +25,8 @@
  */
 
 #include "includes.h"
+#include "channels.h"
+#include "session.h"
 #ifdef USE_PAM
 
 #if !defined(SSHD_PAM_SERVICE)
Index: monitor.c
===================================================================
RCS file: /cvs/openssh/monitor.c,v
retrieving revision 1.33
diff -u -u -r1.33 monitor.c
--- monitor.c	9 Nov 2002 15:47:49 -0000	1.33
+++ monitor.c	10 Dec 2002 12:34:11 -0000
@@ -118,6 +118,7 @@
 
 #ifdef USE_PAM
 int mm_answer_pam_start(int, Buffer *);
+int mm_answer_pam_chauthtok(int, Buffer *);
 #endif
 
 #ifdef KRB4
@@ -183,6 +184,9 @@
     {MONITOR_REQ_PTY, 0, mm_answer_pty},
     {MONITOR_REQ_PTYCLEANUP, 0, mm_answer_pty_cleanup},
     {MONITOR_REQ_TERM, 0, mm_answer_term},
+#ifdef USE_PAM
+    {MONITOR_REQ_PAM_CHAUTHTOK, 0, mm_answer_pam_chauthtok},
+#endif
     {0, 0, NULL}
 };
 
@@ -219,6 +223,9 @@
     {MONITOR_REQ_PTY, MON_ONCE, mm_answer_pty},
     {MONITOR_REQ_PTYCLEANUP, MON_ONCE, mm_answer_pty_cleanup},
     {MONITOR_REQ_TERM, 0, mm_answer_term},
+#ifdef USE_PAM
+    {MONITOR_REQ_PAM_CHAUTHTOK, 0, mm_answer_pam_chauthtok},
+#endif
     {0, 0, NULL}
 };
 
@@ -328,6 +335,7 @@
 	if (!no_pty_flag) {
 		monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
 		monitor_permit(mon_dispatch, MONITOR_REQ_PTYCLEANUP, 1);
+		monitor_permit(mon_dispatch, MONITOR_REQ_PAM_CHAUTHTOK, 1);
 	}
 
 	for (;;)
@@ -746,6 +754,49 @@
 	xfree(user);
 
 	return (0);
+}
+
+int
+mm_answer_pam_chauthtok(int socket, Buffer *m)
+{
+	pid_t pid;
+	int ttyfd, status;
+	mysig_t old_signal;
+
+	old_signal = mysignal(SIGCHLD, SIG_DFL);
+
+	ttyfd = mm_receive_fd(socket);
+	debug("%s: ttyfd=%d, ttyname=%s", __func__, ttyfd, ttyname(ttyfd));
+
+	if ((pid = fork()) == 0) {
+		close(socket);
+		if (dup2(ttyfd, 0) < 0)
+			error("dup2 stdin: %s", strerror(errno));
+		if (dup2(ttyfd, 1) < 0)
+			error("dup2 stdout: %s", strerror(errno));
+		if (dup2(ttyfd, 2) < 0)
+			error("dup2 stderr: %s", strerror(errno));
+		close(ttyfd);
+		/* execl("/bin/sh", "sh", NULL); */
+		do_pam_chauthtok();
+		if(is_pam_password_change_required())
+			exit(1); 	/* failed */
+		else
+			exit(0);	/* success */
+	}
+	close(ttyfd);
+
+	if (waitpid(pid, &status, 0) == -1)
+		fatal("Couldn't wait for child: %s", strerror(errno));
+	 
+	if (WEXITSTATUS(status))
+		fatal("do_pam_chauthtok() failed, child returned %d", status);
+
+	mysignal(SIGCHLD, old_signal);
+
+	mm_request_send(socket, MONITOR_ANS_PAM_CHAUTHTOK, m);
+
+	return 1;
 }
 #endif
 
Index: monitor.h
===================================================================
RCS file: /cvs/openssh/monitor.h,v
retrieving revision 1.10
diff -u -u -r1.10 monitor.h
--- monitor.h	27 Sep 2002 03:26:02 -0000	1.10
+++ monitor.h	10 Dec 2002 12:34:11 -0000
@@ -52,6 +52,7 @@
 	MONITOR_REQ_KRB4, MONITOR_ANS_KRB4,
  	MONITOR_REQ_KRB5, MONITOR_ANS_KRB5,
 	MONITOR_REQ_PAM_START,
+	MONITOR_REQ_PAM_CHAUTHTOK, MONITOR_ANS_PAM_CHAUTHTOK,
 	MONITOR_REQ_TERM
 };
 
Index: monitor_wrap.c
===================================================================
RCS file: /cvs/openssh/monitor_wrap.c,v
retrieving revision 1.20
diff -u -u -r1.20 monitor_wrap.c
--- monitor_wrap.c	27 Sep 2002 03:26:03 -0000	1.20
+++ monitor_wrap.c	10 Dec 2002 12:34:11 -0000
@@ -663,6 +663,25 @@
 
 	buffer_free(&m);
 }
+
+void
+mm_do_pam_chauthtok(void)
+{
+	Buffer m;
+	int ttyfd;
+
+	buffer_init(&m);
+
+	if ((ttyfd = open(_PATH_TTY, O_RDWR)) < 0)
+		fatal("%s: can't open %s", __func__, _PATH_TTY);
+
+	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_CHAUTHTOK, &m);
+	mm_send_fd(pmonitor->m_recvfd, 0);
+	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_CHAUTHTOK, &m);
+	close(ttyfd);
+
+	buffer_free(&m);
+}
 #endif /* USE_PAM */
 
 /* Request process termination */
Index: monitor_wrap.h
===================================================================
RCS file: /cvs/openssh/monitor_wrap.h,v
retrieving revision 1.9
diff -u -u -r1.9 monitor_wrap.h
--- monitor_wrap.h	27 Sep 2002 03:26:04 -0000	1.9
+++ monitor_wrap.h	10 Dec 2002 12:34:11 -0000
@@ -57,6 +57,7 @@
 
 #ifdef USE_PAM
 void mm_start_pam(char *);
+void mm_pam_chauthtok(void);
 #endif
 
 void mm_terminate(void);
Index: session.c
===================================================================
RCS file: /cvs/openssh/session.c,v
retrieving revision 1.222
diff -u -u -r1.222 session.c
--- session.c	26 Sep 2002 00:38:50 -0000	1.222
+++ session.c	10 Dec 2002 12:34:13 -0000
@@ -454,7 +454,6 @@
 	session_proctitle(s);
 
 #if defined(USE_PAM)
-	do_pam_session(s->pw->pw_name, NULL);
 	do_pam_setcred(1);
 	if (is_pam_password_change_required())
 		packet_disconnect("Password change required but no "
@@ -581,7 +580,7 @@
 	ttyfd = s->ttyfd;
 
 #if defined(USE_PAM)
-	do_pam_session(s->pw->pw_name, s->tty);
+	do_pam_set_tty(s->tty);
 	do_pam_setcred(1);
 #endif
 
@@ -753,7 +752,7 @@
 	 */
 	if (is_pam_password_change_required()) {
 		print_pam_messages();
-		do_pam_chauthtok();
+		PRIVSEP(do_pam_chauthtok());
 	}
 #endif
 
@@ -1238,6 +1237,12 @@
 		 * Reestablish them here.
 		 */
 		do_pam_setcred(0);
+
+		/*
+		 * We need to open the session here because PAM on HP-UX does not
+		 * work after the call to permanently_set_uid.
+		 */
+		do_pam_session(pw->pw_name,NULL);
 # endif /* USE_PAM */
 # if defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY)
 		irix_setusercontext(pw);
Index: openbsd-compat/readpassphrase.c
===================================================================
RCS file: /cvs/openssh/openbsd-compat/readpassphrase.c,v
retrieving revision 1.9
diff -u -u -r1.9 readpassphrase.c
--- openbsd-compat/readpassphrase.c	11 Sep 2002 00:29:13 -0000	1.9
+++ openbsd-compat/readpassphrase.c	10 Dec 2002 12:34:13 -0000
@@ -86,6 +86,15 @@
 	}
 
 	/*
+	 * Odd case where stdin is a tty but /dev/tty is not
+	 * available. Used for passed file descriptor during privsep.
+	 */
+	if (isatty(STDIN_FILENO)) {
+		input = dup(STDIN_FILENO);
+		output = dup(STDERR_FILENO);
+	}
+
+	/*
 	 * Catch signals that would otherwise cause the user to end
 	 * up with echo turned off in the shell.  Don't worry about
 	 * things like SIGXCPU and SIGVTALRM for now.


More information about the openssh-unix-dev mailing list