[PATCH] PAM chauthtok + Privsep

Darren Tucker dtucker at zip.com.au
Sat Dec 21 23:11:04 EST 2002


Hello All.
	Attached is an update to my previous patch to make do_pam_chauthtok and
privsep play nicely together.

	First, a question: does anybody care about these or the password
expiration patches?

	Anyway, the "PRIVSEP(do_pam_hauthtok())" has been moved to just after
the pty has been allocated but before it's made the controlling tty. 
This allows the child running chauthtok to acquire a controlling tty so
the PAM conversation function works without modification.  The child
then runs to completion so the slave can acquire its controlling tty and
continue as normal.

Description from previous patch:
      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.

-- 
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 -r1.54 auth-pam.c
--- auth-pam.c	28 Jul 2002 20:24:08 -0000	1.54
+++ auth-pam.c	21 Dec 2002 11:39:05 -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 -r1.16 auth-pam.h
--- auth-pam.h	23 Jul 2002 00:44:07 -0000	1.16
+++ auth-pam.h	21 Dec 2002 11:39:05 -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 -r1.33 monitor.c
--- monitor.c	9 Nov 2002 15:47:49 -0000	1.33
+++ monitor.c	21 Dec 2002 11:39:06 -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,56 @@
 	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);
+	debug3("%s: ttyfd=%d, ttyname=%s", __func__, ttyfd, ttyname(ttyfd));
+
+	if ((pid = fork()) == 0) {
+		/* acquire controlling tty */
+		pty_make_controlling_tty(ttyfd, ttyname(ttyfd));
+
+		/* set up stdin, stdout and stderr */
+		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 extra descriptors */
+		close(socket);
+		close(ttyfd);
+
+		/* call PAM chauthtok and return status to parent */
+		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 -r1.10 monitor.h
--- monitor.h	27 Sep 2002 03:26:02 -0000	1.10
+++ monitor.h	21 Dec 2002 11:39:06 -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 -r1.20 monitor_wrap.c
--- monitor_wrap.c	27 Sep 2002 03:26:03 -0000	1.20
+++ monitor_wrap.c	21 Dec 2002 11:39:06 -0000
@@ -663,6 +663,31 @@
 
 	buffer_free(&m);
 }
+
+/*
+ * Privsep chauthtok works by passing a descriptor to the session's
+ * stdin/stdout to the monitor, which then sets up a child with this
+ * descriptor as stdin, stdout and controlling terminal, then calls
+ * chauthtok()
+ *
+ * This MUST be called before the session has acquired its controlling
+ * tty or the chauthtok child will not be able to acquire it and
+ * will fail.
+ */
+
+void
+mm_do_pam_chauthtok(void)
+{
+	int result;
+	Buffer m;
+
+	buffer_init(&m);
+	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_CHAUTHTOK, &m);
+	mm_send_fd(pmonitor->m_recvfd, STDIN_FILENO);
+	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_CHAUTHTOK, &m);
+
+	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 -r1.9 monitor_wrap.h
--- monitor_wrap.h	27 Sep 2002 03:26:04 -0000	1.9
+++ monitor_wrap.h	21 Dec 2002 11:39:06 -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 -r1.222 session.c
--- session.c	26 Sep 2002 00:38:50 -0000	1.222
+++ session.c	21 Dec 2002 11:39:07 -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
 
@@ -594,9 +593,6 @@
 		/* Close the master side of the pseudo tty. */
 		close(ptyfd);
 
-		/* Make the pseudo tty our controlling tty. */
-		pty_make_controlling_tty(&ttyfd, s->tty);
-
 		/* Redirect stdin/stdout/stderr from the pseudo tty. */
 		if (dup2(ttyfd, 0) < 0)
 			error("dup2 stdin: %s", strerror(errno));
@@ -608,6 +604,24 @@
 		/* Close the extra descriptor for the pseudo tty. */
 		close(ttyfd);
 
+#ifdef USE_PAM
+		/*
+		 * If password change is needed, do it now.
+		 * For privsep, this needs to occur before we acquire a
+		 * controlling tty.
+		 */
+		print_pam_messages();
+		if (use_privsep  && is_pam_password_change_required())
+			PRIVSEP(do_pam_chauthtok());
+#endif
+		/* Make the pseudo tty our controlling tty. */
+		pty_make_controlling_tty(&ttyfd, s->tty);
+
+		/* without privsep, chauthtok requires a controlling tty */
+		if (!use_privsep)
+			do_pam_chauthtok();
+
+
 		/* record login, etc. similar to login(1) */
 #ifndef HAVE_OSF_SIA
 		if (!(options.use_login && command == NULL)) {
@@ -746,16 +760,6 @@
 		    options.verify_reverse_mapping),
 		    (struct sockaddr *)&from, fromlen);
 
-#ifdef USE_PAM
-	/*
-	 * If password change is needed, do it now.
-	 * This needs to occur before the ~/.hushlogin check.
-	 */
-	if (is_pam_password_change_required()) {
-		print_pam_messages();
-		do_pam_chauthtok();
-	}
-#endif
 
 	if (check_quietlogin(s, command))
 		return;
@@ -1238,6 +1242,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);


More information about the openssh-unix-dev mailing list