[PATCH] Add expired password handling for AIX.

Darren Tucker dtucker at zip.com.au
Wed Jul 9 13:24:10 EST 2003


Hi All.
	Attached is a patch which adds AIX native password expiry support to
sshd.  It will only apply to -current and is a subset of the patch I have
been working on in the last few months (see bug #14 [1]).   It contains
code by Pablo Sor, Mark Pitt and Zdenek Tlusty and fixes for bugs reported
by many others (see [2] for a full list).

	It adds a do_tty_change_password function that execs /bin/passwd and the
logic to detect when an AIX password is expired.

	Unlike the previous patches, it does not contain:
* /etc/shadow expiry support.  This is next.
* HP-UX native expiry support.  This can be probably be added if there is
sufficient interest.
* PAM support.  I have not investigated expiry in the new PAM code,
consequently this patch tries hard not to touch the PAM code paths.
* Calling loginsuccess() and printing "Last login at..." messages for
non-password authentications.  This is waiting on some kind of
"get_login_messages" monitor functionality (see bug #463 [3]).

	Please review.  I am looking for any comments on style or substance.

		-Daz.

[1] http://bugzilla.mindrot.org/show_bug.cgi?id=14
[2] http://www.zip.com.au/~dtucker/openssh/
[3] http://bugzilla.mindrot.org/show_bug.cgi?id=463

-- 
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: acconfig.h
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/acconfig.h,v
retrieving revision 1.158
diff -u -r1.158 acconfig.h
--- acconfig.h	8 Jul 2003 10:52:13 -0000	1.158
+++ acconfig.h	8 Jul 2003 13:19:33 -0000
@@ -53,6 +53,9 @@
 /* from environment and PATH */
 #undef LOGIN_PROGRAM_FALLBACK
 
+/* Path to passwd program */
+#undef PASSWD_PROGRAM_PATH
+
 /* Define if your password has a pw_class field */
 #undef HAVE_PW_CLASS_IN_PASSWD
 
Index: auth-passwd.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/auth-passwd.c,v
retrieving revision 1.56
diff -u -r1.56 auth-passwd.c
--- auth-passwd.c	8 Jul 2003 12:59:59 -0000	1.56
+++ auth-passwd.c	9 Jul 2003 02:14:18 -0000
@@ -45,6 +45,8 @@
 #include "buffer.h"
 #include "xmalloc.h"
 #include "canohost.h"
+#include "misc.h"
+#include "auth-options.h"
 
 #if !defined(HAVE_OSF_SIA)
 /* Don't need any of these headers for the SIA cases */
@@ -82,6 +84,7 @@
 
 extern ServerOptions options;
 extern Buffer loginmsg;
+int password_change_required = 0;
 
 /*
  * Tries to authenticate the user using password.  Returns true if
@@ -248,4 +251,81 @@
 	/* Authentication is accepted if the encrypted passwords are identical. */
 	return (strcmp(encrypted_password, pw_password) == 0);
 #endif /* !HAVE_OSF_SIA */
+}
+
+/*
+ * Perform generic password change via tty.  Like do_pam_chauthtok(),
+ * it throws a fatal error if the password can't be changed.
+ */
+int
+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) {
+		permanently_set_uid(pw);
+		if (geteuid() == 0) 
+			execl(PASSWD_PROGRAM_PATH, PASSWD_PROGRAM_PATH,
+			    pw->pw_name, (char *)NULL);
+		else
+			execl(PASSWD_PROGRAM_PATH, PASSWD_PROGRAM_PATH,
+			    (char *)NULL);
+
+		/* NOTREACHED: execl shouldn't return */
+		fatal("Couldn't exec %s", PASSWD_PROGRAM_PATH);
+		exit(1);
+	}
+
+	if (waitpid(pid, &status, 0) == -1)
+		fatal("Couldn't wait for child: %s", strerror(errno));
+	mysignal(SIGCHLD, old_signal);
+
+	if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+		debug("%s password changed sucessfully", __func__);
+		flag_password_change_successful();
+		return 1;
+	} else {
+		fatal("Failed to change password for %s, passwd returned %d",
+		    pw->pw_name, status);
+		return 0;	/* NOTREACHED */
+	}
+}
+
+/*
+ * flag that password change is necessary and disable all forwarding
+ */
+void
+flag_password_change_required(void)
+{
+	debug3("disabling forwarding");
+	password_change_required = 1;
+
+	/* disallow other functionality for now */
+	no_port_forwarding_flag |= 2;
+	no_agent_forwarding_flag |= 2;
+	no_x11_forwarding_flag |= 2;
+}
+
+/*
+ * Flags that password change was successful.
+ * XXX: the password change is performed in the process that becomes the
+ * shell, but the flags must be reset in its parent and currently there is no
+ * way to notify the parent that the change was successful.
+ */
+void
+flag_password_change_successful(void)
+{
+	debug3("reenabling forwarding");
+
+	password_change_required = 0;
+	no_port_forwarding_flag &= ~2;
+	no_agent_forwarding_flag &= ~2;
+	no_x11_forwarding_flag &= ~2;
 }
Index: auth.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/auth.c,v
retrieving revision 1.74
diff -u -r1.74 auth.c
--- auth.c	8 Jul 2003 12:59:59 -0000	1.74
+++ auth.c	9 Jul 2003 02:07:01 -0000
@@ -55,6 +55,7 @@
 /* import */
 extern ServerOptions options;
 extern Buffer loginmsg;
+extern Buffer expiremsg;
 
 /* Debugging messages */
 Buffer auth_debug;
@@ -86,9 +87,10 @@
 	if (!pw || !pw->pw_name)
 		return 0;
 
+#define	DAY		(24L * 60 * 60) /* 1 day in seconds */
+#define	WEEK		(DAY * 7)	/* 1 week in seconds */
 #if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) && \
     defined(HAS_SHADOW_EXPIRE)
-#define	DAY		(24L * 60 * 60) /* 1 day in seconds */
 	if (!options.use_pam && (spw = getspnam(pw->pw_name)) != NULL) {
 		today = time(NULL) / DAY;
 		debug3("allowed_user: today %d sp_expire %d sp_lstchg %d"
@@ -221,6 +223,65 @@
 			    stat(_PATH_NOLOGIN, &st) == 0))
 				return 0;
 		}
+	}
+
+	/*
+	 * Check AIX password expiry.  Only check when running as root.
+	 * Unpriv'ed users can't access /etc/security/passwd or
+	 * /etc/security/user so passwdexpired will always fail.
+	 */
+	if (geteuid() == 0) {
+		char *msg, *user = pw->pw_name;
+		int result, maxage, result2, maxexpired;
+		struct userpw *upw;
+
+		/*
+		 * Check if password has been expired too long.  In this case,
+		 * passwdexpired still returns 1 but /bin/passwd will fail
+		 * while still returning a successiful status, allowing the
+		 * login.  So, we deny these login attempts here.
+		 */
+		upw = getuserpw(user);
+		result = getuserattr(user, S_MAXEXPIRED, &maxexpired, SEC_INT);
+		result2 = getuserattr(user, S_MAXAGE, &maxage, SEC_INT);
+		if (upw != NULL && result == 0 && result2 == 0) {
+			time_t now, lastup = upw->upw_lastupdate;
+
+			now = time(NULL);
+			debug3("%s lastupdate %lu maxage %d wks maxexpired %d"
+			    "wks time now %d", __func__, lastup, maxage,
+			    maxexpired, now);
+
+			if (maxexpired != -1 && maxage != 0 &&
+			    lastup + ((maxage + maxexpired) * WEEK) <= now ){
+				logit("User %.100s password expired too long",
+				    user);
+				return 0;
+			}
+		}
+
+		result = passwdexpired(user, &msg);
+		if (msg && *msg) {
+			buffer_append(&expiremsg, msg, strlen(msg));
+			aix_remove_embedded_newlines(msg);
+		}
+		debug3("AIX/passwdexpired returned %d msg %.100s", result, msg);
+
+		switch (result) {
+			case 0: /* success, password not expired */
+				break;
+			case 1: /* expired, password change required */
+				flag_password_change_required();
+				break;
+			default: /* user can't change(2) or other error (-1) */
+				logit("Password can't be changed for user %s: "
+				    "%.100s", user, msg);
+				if (msg)
+					xfree(msg);
+				return 0;
+		}
+		if (msg)
+			xfree(msg);
 	}
 #endif /* WITH_AIXAUTHENTICATE */
 
Index: configure.ac
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/configure.ac,v
retrieving revision 1.132
diff -u -r1.132 configure.ac
--- configure.ac	8 Jul 2003 10:52:13 -0000	1.132
+++ configure.ac	8 Jul 2003 13:32:36 -0000
@@ -41,6 +41,13 @@
 	fi
 fi
 
+AC_PATH_PROG(PASSWD_PROGRAM_PATH, passwd)
+if test ! -z "$PASSWD_PROGRAM_PATH" ; then
+	AC_DEFINE_UNQUOTED(PASSWD_PROGRAM_PATH, "$PASSWD_PROGRAM_PATH")
+else
+	AC_MSG_ERROR([*** passwd command not found - check config.log ***])
+fi
+
 if test -z "$LD" ; then
 	LD=$CC
 fi
Index: session.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/session.c,v
retrieving revision 1.241
diff -u -r1.241 session.c
--- session.c	8 Jul 2003 12:59:59 -0000	1.241
+++ session.c	9 Jul 2003 02:02:53 -0000
@@ -95,7 +95,9 @@
 extern u_int utmp_len;
 extern int startup_pipe;
 extern void destroy_sensitive_data(void);
+extern int password_change_required;
 extern Buffer loginmsg;
+extern Buffer expiremsg;
 
 /* original command from peer. */
 const char *original_command = NULL;
@@ -461,6 +463,9 @@
 			    "TTY available");
 	}
 #endif /* USE_PAM */
+	if (password_change_required)
+		packet_disconnect("Password change required but no "
+		    "TTY available");
 
 	/* Fork the child. */
 	if ((pid = fork()) == 0) {
@@ -726,6 +731,7 @@
 	socklen_t fromlen;
 	struct sockaddr_storage from;
 	struct passwd * pw = s->pw;
+	int password_changed = 0;
 	pid_t pid = getpid();
 
 	/*
@@ -758,6 +764,13 @@
 		print_pam_messages();
 		do_pam_chauthtok();
 	}
+#else
+	buffer_append(&expiremsg, "\0", 1);
+	if (password_change_required) {
+		printf("%s\n", (char *)buffer_ptr(&expiremsg));
+		fflush(stdout);
+		password_changed = do_tty_change_password(pw);
+	}
 #endif
 
 	if (check_quietlogin(s, command))
@@ -766,6 +779,9 @@
 #ifdef USE_PAM
 	if (options.use_pam && !is_pam_password_change_required())
 		print_pam_messages();
+#else
+	if (!password_changed)
+		printf("%s\n", (char *)buffer_ptr(&expiremsg));
 #endif /* USE_PAM */
 
 	/* display post-login message */
Index: sshd.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/sshd.c,v
retrieving revision 1.253
diff -u -r1.253 sshd.c
--- sshd.c	8 Jul 2003 12:59:59 -0000	1.253
+++ sshd.c	8 Jul 2003 13:44:46 -0000
@@ -203,6 +203,7 @@
 
 /* message to be displayed after login */
 Buffer loginmsg;
+Buffer expiremsg;
 
 /* Prototypes for various functions defined later in this file. */
 void destroy_sensitive_data(void);
@@ -1506,6 +1507,7 @@
 
         /* prepare buffers to collect authentication messages */
 	buffer_init(&loginmsg);
+	buffer_init(&expiremsg);
 
 	if (use_privsep)
 		if ((authctxt = privsep_preauth()) != NULL)


More information about the openssh-unix-dev mailing list