[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