[PATCH #7] AIX password expiration

Darren Tucker dtucker at zip.com.au
Mon Oct 28 22:57:46 EST 2002


Ben Lindstrom wrote:
> What I'm doing is this.  I'm splitting the patch into v2 (against
> OpenBSD), v1 (against OpenBSD) and AIX only.  I'm done with the v2 part
> except to get bsd_auth to tell me expired vs invalid/locked account.
> <sigh>  Really need to find a tutorial on bsd_auth.

I haven't merged the changes mentioned yet, but here's something else:
this patch adds /etc/shadow-style password expiration without PAM.

Tested on Solaris 8 and Redhat 8. May work on other platforms using
shadow passwords.

		-Daz.

$ ssh -p 2022 localhost -l testuser
testuser at localhost's password: 
You must change your password now.
Enter testuser at localhost's old password: 
Enter testuser at localhost's new password: 
Retype testuser at localhost's new password: 
[snip]
$ uname -sr
SunOS 5.8

-- 
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 --------------
diff -ru openssh-3.5p1.orig/auth-pam.c openssh-3.5p1-passexpire/auth-pam.c
--- openssh-3.5p1.orig/auth-pam.c	Mon Jul 29 06:24:08 2002
+++ openssh-3.5p1-passexpire/auth-pam.c	Mon Oct 28 13:09:15 2002
@@ -60,7 +60,7 @@
 /* states for do_pam_conversation() */
 enum { INITIAL_LOGIN, OTHER } pamstate = INITIAL_LOGIN;
 /* remember whether pam_acct_mgmt() returned PAM_NEW_AUTHTOK_REQD */
-static int password_change_required = 0;
+extern int password_change_required;
 /* remember whether the last pam_authenticate() succeeded or not */
 static int was_authenticated = 0;
 
diff -ru openssh-3.5p1.orig/auth-passwd.c openssh-3.5p1-passexpire/auth-passwd.c
--- openssh-3.5p1.orig/auth-passwd.c	Thu Sep 26 09:14:16 2002
+++ openssh-3.5p1-passexpire/auth-passwd.c	Mon Oct 28 22:36:04 2002
@@ -42,6 +42,8 @@
 #include "log.h"
 #include "servconf.h"
 #include "auth.h"
+#include "misc.h"
+#include "xmalloc.h"
 
 #if !defined(USE_PAM) && !defined(HAVE_OSF_SIA)
 /* Don't need any of these headers for the PAM or SIA cases */
@@ -81,13 +83,15 @@
 #endif /* !USE_PAM && !HAVE_OSF_SIA */
 
 extern ServerOptions options;
+extern int password_change_required;
 #ifdef WITH_AIXAUTHENTICATE
 extern char *aixloginmsg;
 #endif
 
 /*
- * Tries to authenticate the user using password.  Returns true if
- * authentication succeeds.
+ * Tries to authenticate the user using password.  Returns true (1) if
+ * authentication succeeds, (2) if authentication succeeds but password
+ * change required.
  */
 int
 auth_password(Authctxt *authctxt, const char *password)
@@ -149,14 +153,25 @@
 #endif
 #ifdef WITH_AIXAUTHENTICATE
 	authsuccess = (authenticate(pw->pw_name,password,&reenter,&authmsg) == 0);
+	aix_remove_embedded_newlines(authmsg);
 
-	if (authsuccess)
+	if (authsuccess) {
+		debug("authenticate() succeeded for user %s: %.100s", pw->pw_name, authmsg);
 	        /* We don't have a pty yet, so just label the line as "ssh" */
 	        if (loginsuccess(authctxt->user,
 			get_canonical_hostname(options.verify_reverse_mapping),
 			"ssh", &aixloginmsg) < 0)
 				aixloginmsg = NULL;
+	} else {
+		debug("authenticate() failed for user %s: %.100s", pw->pw_name, authmsg);
+	}
+	if (authmsg)
+		xfree(authmsg);
 
+	debug("auth_password: authsuccess = %d", authsuccess);
+	if (authsuccess && password_change_required) {
+		return 2;
+	}
 	return(authsuccess);
 #endif
 #ifdef KRB4
@@ -230,6 +245,188 @@
 #endif /* HAVE_MD5_PASSWORDS */
 
 	/* Authentication is accepted if the encrypted passwords are identical. */
-	return (strcmp(encrypted_password, pw_password) == 0);
+	if (strcmp(encrypted_password, pw_password) == 0) {
+		if (password_change_required) {
+			debug("auth_password: password expired");
+			return 2;
+		} else {
+			debug("auth_password: not expired");
+			return 1;
+		}
+	}
+	return 0;
 #endif /* !USE_PAM && !HAVE_OSF_SIA */
 }
+
+/* password change for protocol 2 */
+int
+auth_change_password(Authctxt *authctxt, const char *oldpasswd, const char *newpasswd)
+{
+#ifdef WITH_AIXAUTHENTICATE
+        return aix_change_password(authctxt->pw, oldpasswd, newpasswd);
+#endif
+#if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW)
+	return shadow_change_password(authctxt->pw, oldpasswd, newpasswd);
+#endif
+	return 0;
+}
+
+
+/*
+ * generic password change routine. requires session established and tty alloced 
+ * Like do_pam_chauthtok(), it throws a fatal error if the password can't be changed.
+ */
+
+void
+do_tty_change_password(struct passwd *pw)
+{
+	pid_t pid;
+	int status;
+	mysig_t old_signal;
+
+	old_signal = mysignal(SIGCHLD, SIG_DFL);
+
+	if ((pid = fork()) == -1)
+		fatal("Couldn't fork: %s", strerror(errno));
+
+	if (pid == 0) {
+		setuid(pw->pw_uid);
+		execl("/usr/bin/passwd","passwd",pw->pw_name,
+			(char *)NULL);
+		/* execl shouldn't return */
+		fatal("Couldn't exec /usr/bin/passwd");
+		exit(1);
+	}
+
+	if (waitpid(pid, &status, 0) == -1)
+		fatal("Couldn't wait for child: %s", strerror(errno));
+
+	if (WEXITSTATUS(status))	/* Passwd exited abnormally */
+		fatal("Failed to change password for %s, passwd returned %d", pw->pw_name, status);
+
+	mysignal(SIGCHLD, old_signal);
+}
+
+#if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW)
+
+#define SHADOW_TEMPFILE		SHADOW ".tmp"
+#ifdef OSHADOW
+# define SHADOW_OLDPASSFILE	OSHADOW
+#else
+# define SHADOW_OLDPASSFILE	SHADOW "-"
+#endif
+
+/*
+ * shadow_change_password: change a password stored in a shadow password file.
+ * returns 1 if change successful, 0 otherwise.
+ * always attempts to back out its changes in case of failure.
+ */
+
+int
+shadow_change_password(struct passwd *pw, const char *oldpass, const char *newpass)
+{
+	int changed, replaced;
+	FILE *oldshadow, *newshadow;
+	struct spwd *spw;
+	struct stat statbuf;
+	mode_t saved_umask;
+
+	saved_umask = umask(0077);
+
+	if (lckpwdf() == -1) {
+		debug("%s: could not lock password file: %s", __func__,
+		    strerror(errno));
+		return 0;
+	}
+
+	if ((newshadow=fopen(SHADOW_TEMPFILE, "w")) == NULL) {
+		debug("%s: could not open shadow temp file %s: %s", __func__,
+		    SHADOW_TEMPFILE, strerror(errno));
+		goto shadow_cleanup;
+	}
+
+	if (chmod(SHADOW_TEMPFILE, 0400) == -1 ) {
+		debug("%s: could not change permissions on %s: %s", __func__,
+		    SHADOW_TEMPFILE, strerror(errno));
+		goto shadow_cleanup;
+	}
+
+	if ((oldshadow=fopen(SHADOW, "r")) == NULL) {
+		debug("%s: could not open shadow file %s: %s", __func__,
+		    SHADOW, strerror(errno));
+		goto shadow_cleanup;
+	}
+
+	while ((spw=fgetspent(oldshadow)) != NULL) {
+		if (strcmp(spw->sp_namp, pw->pw_name) == 0) {
+			if (strcmp(spw->sp_pwdp, crypt(oldpass, spw->sp_pwdp)) != 0)
+				fatal("%s: Old password doesn't match.", __func__);
+			spw->sp_pwdp = crypt(newpass, spw->sp_pwdp);
+			spw->sp_lstchg = time(0) / (24*60*60);
+			changed = 1;
+		}
+		if (putspent(spw, newshadow) < 0) {
+			debug("%s: error writing shadow file %s: %s", __func__,
+			    SHADOW_TEMPFILE, strerror(errno));
+			goto shadow_cleanup;
+		}
+	}
+
+	/* check that password was changed, otherwise skip file swapping */
+	if (!changed) {
+		debug("%s: user %s not found in shadow file", __func__, pw->pw_name);
+		goto shadow_cleanup;
+	}
+
+	/*
+	 * Swap in new file. Current shadow file becomes oshadow.
+	 */
+
+	if (unlink(SHADOW_OLDPASSFILE) == -1) {
+		debug("%s: error unlinking old shadow file %s: %s", __func__,
+		    SHADOW_OLDPASSFILE, strerror(errno));
+	}
+
+	if (link(SHADOW, SHADOW_OLDPASSFILE) == -1) {
+		debug("%s: error linking current shadow file %s to old %s: %s",
+		    __func__, SHADOW, SHADOW_OLDPASSFILE, strerror(errno));
+		goto shadow_cleanup;
+	}
+
+	if (rename(SHADOW_TEMPFILE, SHADOW) == -1) {
+		debug("%s: error renaming new shadow file %s to old %s: %s",
+		    __func__, SHADOW_TEMPFILE, SHADOW, strerror(errno));
+		goto shadow_cleanup;
+	} else {
+		replaced = 1;
+	}
+
+shadow_cleanup:
+	if (stat(SHADOW_TEMPFILE, &statbuf) == 0)
+		if (unlink(SHADOW_TEMPFILE) == -1) 
+			debug("%s: error unlinking shadow temp file %s: %s",
+			    __func__, SHADOW_TEMPFILE, strerror(errno));
+
+	if (stat(SHADOW, &statbuf) == -1)
+		if (link(SHADOW_OLDPASSFILE, SHADOW) == -1) 
+			debug("%s: restoring shadow password file %s",
+				__func__, SHADOW);
+
+	if (oldshadow != NULL && fclose(oldshadow) == EOF)
+		debug("%s: error closing original shadow file %s: %s",
+		    __func__, SHADOW_TEMPFILE, strerror(errno));
+
+	if (newshadow != NULL && fclose(newshadow) == EOF)
+		debug("%s: error closing shadow temp file %s: %s",
+		    __func__, SHADOW_TEMPFILE, strerror(errno));
+
+	if (ulckpwdf() == -1) {
+		debug("%s: could not unlock password file", __func__);
+	}
+
+	umask(saved_umask);
+
+	return (changed && replaced);
+}
+#endif /* defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) */
+

diff -ru openssh-3.5p1.orig/auth.c openssh-3.5p1-passexpire/auth.c
--- openssh-3.5p1.orig/auth.c	Sun Sep 22 01:26:53 2002
+++ openssh-3.5p1-passexpire/auth.c	Mon Oct 28 13:09:15 2002
@@ -59,6 +59,10 @@
 Buffer auth_debug;
 int auth_debug_init;
 
+/* Password change flag */
+int password_change_required = 0;
+char *password_expire_message = NULL;
+
 /*
  * Check if the user is allowed to log in via ssh. If user is listed
  * in DenyUsers or one of user's groups is listed in DenyGroups, false
@@ -75,9 +79,6 @@
 	const char *hostname = NULL, *ipaddr = NULL;
 	char *shell;
 	int i;
-#ifdef WITH_AIXAUTHENTICATE
-	char *loginmsg;
-#endif /* WITH_AIXAUTHENTICATE */
 #if !defined(USE_PAM) && defined(HAVE_SHADOW_H) && \
 	!defined(DISABLE_SHADOW) && defined(HAS_SHADOW_EXPIRE)
 	struct spwd *spw;
@@ -106,14 +107,18 @@
 		if (spw->sp_lstchg == 0) {
 			log("User %.100s password has expired (root forced)",
 			    pw->pw_name);
-			return 0;
+			password_change_required = 1;
+			password_expire_message =
+			    xstrdup("Your password has expired (root forced)");
 		}
 
 		if (spw->sp_max != -1 &&
 		    today > spw->sp_lstchg + spw->sp_max) {
 			log("User %.100s password has expired (password aged)",
 			    pw->pw_name);
-			return 0;
+			password_change_required = 1;
+			password_expire_message =
+			    xstrdup("Your password has expired");
 		}
 	}
 #else
@@ -202,19 +207,48 @@
 	}
 
 #ifdef WITH_AIXAUTHENTICATE
-	if (loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &loginmsg) != 0) {
-		if (loginmsg && *loginmsg) {
-			/* Remove embedded newlines (if any) */
-			char *p;
-			for (p = loginmsg; *p; p++) {
-				if (*p == '\n')
-					*p = ' ';
+	/*
+	 * Don't check loginrestrictions or expiry for root account (use
+	 * PermitRootLogin to control logins via ssh), or if running as
+	 * non-root user (since loginrestrictions will always fail).
+	 */
+	if ( (pw->pw_uid != 0) && (geteuid() == 0) ) {
+		char *restrictmsg, *expiremsg;
+		int passexpcode;
+
+		/* check for AIX account restrictions */
+		if (loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &restrictmsg) != 0) {
+			if (restrictmsg && *restrictmsg) {
+				aix_remove_embedded_newlines(restrictmsg);
+				log("Login restricted for %s: %.100s", pw->pw_name, restrictmsg);
+				xfree(restrictmsg);
 			}
-			/* Remove trailing newline */
-			*--p = '\0';
-			log("Login restricted for %s: %.100s", pw->pw_name, loginmsg);
+			return 0;
+		}
+
+		/* check for AIX expired account */
+		passexpcode = passwdexpired(pw->pw_name, &password_expire_message);
+		debug("passwdexpired() returned %d", passexpcode);
+
+		switch (passexpcode) {
+			case 0:		/* success, password not expired */
+				break;
+			case 1:		/* expired, password change required */
+				password_change_required = 1;
+				break;
+			default:	/* expired too long (2) or other error (-1) */
+				/* make local copy of message and remove newlines for logging */
+				if (password_expire_message && *password_expire_message) {
+					expiremsg = xstrdup(password_expire_message);
+					aix_remove_embedded_newlines(expiremsg);
+				}
+				debug("passwdexpired() returned %d", passexpcode);
+				log("Password expired too long or system failure for user %s: %.100s",
+				    pw->pw_name, expiremsg);
+				if (expiremsg)
+					xfree(expiremsg);
+				return 0;
 		}
-		return 0;
 	}
 #endif /* WITH_AIXAUTHENTICATE */
 
diff -ru openssh-3.5p1.orig/auth.h openssh-3.5p1-passexpire/auth.h
--- openssh-3.5p1.orig/auth.h	Fri Sep 27 13:26:01 2002
+++ openssh-3.5p1-passexpire/auth.h	Mon Oct 28 13:10:19 2002
@@ -101,6 +101,10 @@
 
 int	 auth_rhosts_rsa(struct passwd *, char *, Key *);
 int      auth_password(Authctxt *, const char *);
+int	 auth_change_password(Authctxt *, const char *, const char *);
+#if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW)
+int	 shadow_change_password(struct passwd *, const char *, const char *);
+#endif
 int      auth_rsa(struct passwd *, BIGNUM *);
 int      auth_rsa_challenge_dialog(Key *);
 BIGNUM	*auth_rsa_generate_challenge(Key *);

diff -ru openssh-3.5p1.orig/auth2-passwd.c openssh-3.5p1-passexpire/auth2-passwd.c
--- openssh-3.5p1.orig/auth2-passwd.c	Fri Jun  7 06:27:56 2002
+++ openssh-3.5p1-passexpire/auth2-passwd.c	Mon Oct 28 13:09:15 2002
@@ -31,28 +31,48 @@
 #include "auth.h"
 #include "monitor_wrap.h"
 #include "servconf.h"
+#include "ssh2.h"
 
 /* import */
 extern ServerOptions options;
+extern int password_change_required;
 
 static int
 userauth_passwd(Authctxt *authctxt)
 {
-	char *password;
-	int authenticated = 0;
-	int change;
-	u_int len;
-	change = packet_get_char();
-	if (change)
-		log("password change not supported");
+	char *password, *npassword;
+	int authenticated = 0, change_requested;
+	u_int len, nlen;
+
+	change_requested = packet_get_char();
 	password = packet_get_string(&len);
+	if (change_requested) {
+		debug("userauth_passwd: password change requested by client");
+		npassword = packet_get_string(&nlen);
+	}
 	packet_check_eom();
+
 	if (authctxt->valid &&
 #ifdef HAVE_CYGWIN
 	    check_nt_auth(1, authctxt->pw) &&
 #endif
-	    PRIVSEP(auth_password(authctxt, password)) == 1)
-		authenticated = 1;
+	    (authenticated = (PRIVSEP(auth_password(authctxt, password))))) {
+		debug("auth_password returned %d, pid=%d ppid=%d",
+		    authenticated, getpid(), getppid());
+
+		/* now that the password has been checked, change password
+		 * if requested by client and revalidate new password */
+		if (change_requested) {
+			if (PRIVSEP(auth_change_password(authctxt, password, npassword))) {
+				debug("userauth_passwd: password changed successfully");
+				authenticated = 1;
+			} else {
+				debug("userauth_passwd: password change failed");
+			}
+			memset(npassword, 0, nlen);
+			xfree(npassword);
+		}
+	}
 	memset(password, 0, len);
 	xfree(password);
 	return authenticated;

diff -ru openssh-3.5p1.orig/auth2.c openssh-3.5p1-passexpire/auth2.c
--- openssh-3.5p1.orig/auth2.c	Thu Sep 26 10:38:49 2002
+++ openssh-3.5p1-passexpire/auth2.c	Mon Oct 28 13:09:15 2002
@@ -40,6 +40,7 @@
 extern ServerOptions options;
 extern u_char *session_id2;
 extern int session_id2_len;
+extern char *password_expire_message;
 
 Authctxt *x_authctxt = NULL;
 
@@ -199,6 +200,7 @@
 userauth_finish(Authctxt *authctxt, int authenticated, char *method)
 {
 	char *methods;
+	static const char default_prompt[] = "You must change your password now.";
 
 	if (!authctxt->valid && authenticated)
 		fatal("INTERNAL ERROR: authenticated invalid user %s",
@@ -238,6 +240,15 @@
 		packet_write_wait();
 		/* now we can break out */
 		authctxt->success = 1;
+	} else if (authenticated == 2 ) { /* password change required */
+		if (password_expire_message == NULL)
+			password_expire_message = (char *)default_prompt;
+		debug("sending SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ");
+		packet_start(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ);
+		packet_put_cstring(password_expire_message);
+		packet_put_cstring("");                 /* language */
+		packet_send();
+		packet_write_wait();
 	} else {
 		if (authctxt->failures++ > AUTH_FAIL_MAX) {
 			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);

diff -ru openssh-3.5p1.orig/monitor.c openssh-3.5p1-passexpire/monitor.c
--- openssh-3.5p1.orig/monitor.c	Fri Sep 27 13:26:02 2002
+++ openssh-3.5p1-passexpire/monitor.c	Sun Oct 27 21:49:43 2002
@@ -101,6 +101,7 @@
 int mm_answer_auth2_read_banner(int, Buffer *);
 int mm_answer_authserv(int, Buffer *);
 int mm_answer_authpassword(int, Buffer *);
+int mm_answer_auth_change_password(int, Buffer *);
 int mm_answer_bsdauthquery(int, Buffer *);
 int mm_answer_bsdauthrespond(int, Buffer *);
 int mm_answer_skeyquery(int, Buffer *);
@@ -161,6 +162,7 @@
     {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv},
     {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner},
     {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword},
+    {MONITOR_REQ_CHPASS, MON_AUTH, mm_answer_auth_change_password}, 
 #ifdef USE_PAM
     {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start},
 #endif
@@ -267,6 +269,7 @@
 		/* Permit requests for moduli and signatures */
 		monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
 		monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
+		monitor_permit(mon_dispatch, MONITOR_REQ_CHPASS, 1);
 	} else {
 		mon_dispatch = mon_dispatch_proto15;
 
@@ -275,8 +278,11 @@
 
 	authctxt = authctxt_new();
 
-	/* The first few requests do not require asynchronous access */
-	while (!authenticated) {
+	/* The first few requests do not require asynchronous access
+	 * exit loop if authenticated and password change no required (proto 2)
+	 * or if password correct (proto 1)
+	 */
+	while ((compat20 && authenticated != 1) || (!compat20 && !authenticated)) {
 		authenticated = monitor_read(pmonitor, mon_dispatch, &ent);
 		if (authenticated) {
 			if (!(ent->flags & MON_AUTHDECIDE))
@@ -600,13 +606,14 @@
 {
 	static int call_count;
 	char *passwd;
-	int authenticated;
+	int authenticated = 0;
 	u_int plen;
 
 	passwd = buffer_get_string(m, &plen);
 	/* Only authenticate if the context is valid */
-	authenticated = options.password_authentication &&
-	    authctxt->valid && auth_password(authctxt, passwd);
+	if ( options.password_authentication && authctxt->valid ) 
+		authenticated = auth_password(authctxt, passwd);
+	
 	memset(passwd, 0, strlen(passwd));
 	xfree(passwd);
 
@@ -624,6 +631,32 @@
 
 	/* Causes monitor loop to terminate if authenticated */
 	return (authenticated);
+}
+
+int
+mm_answer_auth_change_password(int socket, Buffer *m)
+{
+	char *oldpass, *newpass;
+	int changed;
+
+	oldpass = buffer_get_string(m, NULL);
+	newpass = buffer_get_string(m, NULL);
+
+	/* Only attempt if the context is valid */
+	if ( options.password_authentication && authctxt->valid ) 
+		changed = auth_change_password(authctxt, oldpass, newpass);
+
+	buffer_clear(m);
+	buffer_put_int(m, changed);
+
+	mm_request_send(socket, MONITOR_ANS_CHPASS, m);
+
+	memset(oldpass, 0, strlen(oldpass));
+	xfree(oldpass);
+	memset(newpass, 0, strlen(newpass));
+	xfree(newpass);
+
+	return changed;
 }
 
 #ifdef BSD_AUTH

diff -ru openssh-3.5p1.orig/monitor.h openssh-3.5p1-passexpire/monitor.h
--- openssh-3.5p1.orig/monitor.h	Fri Sep 27 13:26:02 2002
+++ openssh-3.5p1-passexpire/monitor.h	Sun Oct 27 15:36:25 2002
@@ -35,6 +35,7 @@
 	MONITOR_REQ_PWNAM, MONITOR_ANS_PWNAM,
 	MONITOR_REQ_AUTH2_READ_BANNER, MONITOR_ANS_AUTH2_READ_BANNER,
 	MONITOR_REQ_AUTHPASSWORD, MONITOR_ANS_AUTHPASSWORD,
+	MONITOR_REQ_CHPASS, MONITOR_ANS_CHPASS,
 	MONITOR_REQ_BSDAUTHQUERY, MONITOR_ANS_BSDAUTHQUERY,
 	MONITOR_REQ_BSDAUTHRESPOND, MONITOR_ANS_BSDAUTHRESPOND,
 	MONITOR_REQ_SKEYQUERY, MONITOR_ANS_SKEYQUERY,

diff -ru openssh-3.5p1.orig/monitor_wrap.c openssh-3.5p1-passexpire/monitor_wrap.c
--- openssh-3.5p1.orig/monitor_wrap.c	Fri Sep 27 13:26:03 2002
+++ openssh-3.5p1-passexpire/monitor_wrap.c	Sun Oct 27 17:05:03 2002
@@ -256,7 +256,7 @@
 	buffer_put_cstring(&m, password);
 	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHPASSWORD, &m);
 
-	debug3("%s: waiting for MONITOR_ANS_AUTHPASSWORD", __func__);
+	debug3("%s: waiting for MONITOR_ANS_AUTHPASSWORD (type %d)", __func__, MONITOR_ANS_AUTHPASSWORD);
 	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUTHPASSWORD, &m);
 
 	authenticated = buffer_get_int(&m);
@@ -266,6 +266,32 @@
 	debug3("%s: user %sauthenticated",
 	    __func__, authenticated ? "" : "not ");
 	return (authenticated);
+}
+
+int
+mm_auth_change_password(Authctxt *authctxt, char *oldpass, char *newpass)
+{
+	Buffer m;
+	int changed = 0;
+
+	debug3("%s entering", __func__);
+
+	buffer_init(&m);
+	buffer_put_cstring(&m, oldpass);
+	buffer_put_cstring(&m, newpass);
+
+	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_CHPASS, &m);
+
+	debug3("%s: waiting for MONITOR_ANS_CHPASS (type %d)", __func__, MONITOR_ANS_CHPASS);
+	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_CHPASS, &m);
+
+	changed = buffer_get_int(&m);
+
+	debug3("%s: password %schanged",
+	    __func__, changed ? "" : "not ");
+
+	buffer_free(&m);
+	return changed;
 }
 
 int

diff -ru openssh-3.5p1.orig/monitor_wrap.h openssh-3.5p1-passexpire/monitor_wrap.h
--- openssh-3.5p1.orig/monitor_wrap.h	Fri Sep 27 13:26:04 2002
+++ openssh-3.5p1-passexpire/monitor_wrap.h	Sun Oct 27 16:07:18 2002
@@ -46,6 +46,7 @@
 struct passwd *mm_getpwnamallow(const char *);
 char *mm_auth2_read_banner(void);
 int mm_auth_password(struct Authctxt *, char *);
+int mm_auth_change_password(struct Authctxt *, char *, char *);
 int mm_key_allowed(enum mm_keytype, char *, char *, Key *);
 int mm_user_key_allowed(struct passwd *, Key *);
 int mm_hostbased_key_allowed(struct passwd *, char *, char *, Key *);

diff -ru openssh-3.5p1.orig/openbsd-compat/port-aix.c openssh-3.5p1-passexpire/openbsd-compat/port-aix.c
--- openssh-3.5p1.orig/openbsd-compat/port-aix.c	Sun Jul  7 12:17:36 2002
+++ openssh-3.5p1-passexpire/openbsd-compat/port-aix.c	Sun Oct 27 19:20:33 2002
@@ -24,11 +24,15 @@
  *
  */
 #include "includes.h"
+#include "misc.h"
+#include "log.h"
 
 #ifdef _AIX
 
 #include <uinfo.h>
 #include <../xmalloc.h>
+#include <userpw.h>
+#include <usersec.h>
 
 /*
  * AIX has a "usrinfo" area where logname and other stuff is stored - 
@@ -52,5 +56,74 @@
 	xfree(cp);
 }
 
-#endif /* _AIX */
+#ifdef WITH_AIXAUTHENTICATE
+
+/*
+ * Remove embedded newlines in string (if any).
+ * Used before logging messages returned by AIX authentication functions
+ * so the message is logged on one line.
+ */
+void
+aix_remove_embedded_newlines(char *p)
+{
+	if (p == NULL)
+		return;
+
+	for (; *p; p++) {
+		if (*p == '\n')
+			*p = ' ';
+	}
+	/* Remove trailing newline */
+	*--p = '\0';
+}
+
+/*
+ * aix_change_password: AIX password change routine
+ */
+int
+aix_change_password(struct passwd *pw, const char *oldpassword, const char *newpassword)
+{
+	struct userpw *upw;
+
+	debug("userauth_change_password: changing password for %s", pw->pw_name);
+
+	if (setpwdb(S_READ|S_WRITE) == -1) {
+		debug("Couldn't open authentication database: %s", strerror(errno));
+		return 0;
+	}
+
+	if ((upw = getuserpw(pw->pw_name)) == NULL) {
+		debug("Couldn't get user details for %s: %s",
+		    pw->pw_name, strerror(errno));
+		enduserdb();
+		return 0;
+	}
+
+	/*
+	 * Validate current password. Because we should never be called before the user
+	 * has been successfully authenticated, failure here is fatal
+	 */
+	if (strcmp(upw->upw_passwd, crypt(oldpassword, upw->upw_passwd)) != 0)
+		fatal("aix_change_password: old password does not match database");
+
+	upw->upw_passwd = crypt(newpassword, upw->upw_passwd);
+	pw->pw_passwd = upw->upw_passwd;
+	upw->upw_flags &= ~PW_ADMCHG;	/* clear password change flag */
+	if (putuserpw(upw) == -1) {
+		debug("Couldn't update user details for %s: %s",
+		    pw->pw_name, strerror(errno));
+		enduserdb();
+		return 0;
+	}
+	if(enduserdb() == -1) {
+		debug("Error closing authentication database: %s",
+		    strerror(errno));
+		return 0;
+	}
 
+	return 1;
+}
+
+#endif /* WITH_AIXAUTHENTICATE */
+
+#endif /* _AIX */

diff -ru openssh-3.5p1.orig/openbsd-compat/port-aix.h openssh-3.5p1-passexpire/openbsd-compat/port-aix.h
--- openssh-3.5p1.orig/openbsd-compat/port-aix.h	Sun Jul  7 12:17:36 2002
+++ openssh-3.5p1-passexpire/openbsd-compat/port-aix.h	Sun Oct 27 18:06:14 2002
@@ -25,5 +25,12 @@
  */
 
 #ifdef _AIX
+
 void aix_usrinfo(struct passwd *pw);
+
+#ifdef WITH_AIXAUTHENTICATE
+void aix_remove_embedded_newlines(char *);
+int aix_change_password(struct passwd *, const char *, const char *);
+#endif
+
 #endif /* _AIX */

diff -ru openssh-3.5p1.orig/session.c openssh-3.5p1-passexpire/session.c
--- openssh-3.5p1.orig/session.c	Thu Sep 26 10:38:50 2002
+++ openssh-3.5p1-passexpire/session.c	Sun Oct 27 21:31:31 2002
@@ -103,8 +103,12 @@
 #define MAX_SESSIONS 10
 Session	sessions[MAX_SESSIONS];
 
+void do_tty_change_password(struct passwd *);
+extern int password_change_required;
+extern char *password_expire_message;
+
 #ifdef WITH_AIXAUTHENTICATE
-char *aixloginmsg;
+char *aixloginmsg;	/* message returned by loginsuccess() */
 #endif /* WITH_AIXAUTHENTICATE */
 
 #ifdef HAVE_LOGIN_CAP
@@ -461,6 +465,12 @@
 		    "TTY available");
 #endif /* USE_PAM */
 
+#ifdef WITH_AIXAUTHENTICATE
+	if (!compat20 && password_change_required)
+		packet_disconnect("Password change required but no "
+		    "TTY available");
+#endif /* WITH_AIXAUTHENTICATE */
+
 	/* Fork the child. */
 	if ((pid = fork()) == 0) {
 		fatal_remove_all_cleanups();
@@ -757,6 +767,11 @@
 	}
 #endif
 
+	if (!compat20 && password_change_required) {
+		printf("%s\n", password_expire_message);
+		do_tty_change_password(pw);
+	}
+
 	if (check_quietlogin(s, command))
 		return;
 
@@ -764,9 +779,18 @@
 	if (!is_pam_password_change_required())
 		print_pam_messages();
 #endif /* USE_PAM */
+
+	if (password_expire_message && *password_expire_message) {
+		if (!password_change_required)
+			printf("%s\n", password_expire_message);
+		xfree(password_expire_message);
+	}
+
 #ifdef WITH_AIXAUTHENTICATE
-	if (aixloginmsg && *aixloginmsg)
+	if (aixloginmsg && *aixloginmsg) {
 		printf("%s\n", aixloginmsg);
+		xfree(aixloginmsg);
+	}
 #endif /* WITH_AIXAUTHENTICATE */
 
 #ifndef NO_SSH_LASTLOG


More information about the openssh-unix-dev mailing list