[PATCH] AIX password expiration

Darren Tucker dtucker at zip.com.au
Sat Oct 19 17:10:44 EST 2002


Darren Tucker wrote:
>         The patch extends the loginrestrictions test to include expired
> accounts and adds PAM-like password expiry and forced change.

I've updated the patch. The diff is against 3.5p1. There should be no
functional differences between the original and this patch.

I'm still interested in feedback from anyone who tried either or has
comments on the patch itself.

The changes relative to the previous patch are:

* cleaned up somewhat.
* added some debugs
* now frees memory allocated by library functions
* added some 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 --------------
diff -ru openssh-3.5p1.orig/auth-passwd.c openssh-3.5p1/auth-passwd.c
--- openssh-3.5p1.orig/auth-passwd.c	Thu Sep 26 09:14:16 2002
+++ openssh-3.5p1/auth-passwd.c	Sat Oct 19 14:59:26 2002
@@ -82,6 +82,7 @@
 
 extern ServerOptions options;
 #ifdef WITH_AIXAUTHENTICATE
+void aix_remove_embedded_newlines(char *);
 extern char *aixloginmsg;
 #endif
 
@@ -149,13 +150,20 @@
 #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);
 
 	return(authsuccess);
 #endif
diff -ru openssh-3.5p1.orig/auth.c openssh-3.5p1/auth.c
--- openssh-3.5p1.orig/auth.c	Sun Sep 22 01:26:53 2002
+++ openssh-3.5p1/auth.c	Sat Oct 19 15:49:22 2002
@@ -59,6 +59,12 @@
 Buffer auth_debug;
 int auth_debug_init;
 
+#ifdef WITH_AIXAUTHENTICATE
+void aix_remove_embedded_newlines(char *);
+extern char *aixexpiremsg;
+extern int aix_password_change_required;
+#endif
+
 /*
  * 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 +81,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;
@@ -202,19 +205,47 @@
 	}
 
 #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, &aixexpiremsg);
+
+		switch (passexpcode) {
+			case 0:		/* success, password not expired */
+				break;
+			case 1:		/* expired, password change required */
+				aix_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 (aixexpiremsg && *aixexpiremsg) {
+					expiremsg = xstrdup(aixexpiremsg);
+					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/openbsd-compat/port-aix.c openssh-3.5p1/openbsd-compat/port-aix.c
--- openssh-3.5p1.orig/openbsd-compat/port-aix.c	Sun Jul  7 12:17:36 2002
+++ openssh-3.5p1/openbsd-compat/port-aix.c	Sat Oct 19 15:02:26 2002
@@ -24,6 +24,7 @@
  *
  */
 #include "includes.h"
+#include "misc.h"
 
 #ifdef _AIX
 
@@ -52,5 +53,60 @@
 	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';
+}
+
+/*
+ * Perform password change on AIX
+ * Like do_pam_chauthtok(), it throws a fatal error if the password can't be changed.
+ */
+void
+do_aix_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);
+}
+#endif /* WITH_AIXAUTHENTICATE */
+
+#endif /* _AIX */
diff -ru openssh-3.5p1.orig/session.c openssh-3.5p1/session.c
--- openssh-3.5p1.orig/session.c	Thu Sep 26 10:38:50 2002
+++ openssh-3.5p1/session.c	Sat Oct 19 15:11:06 2002
@@ -104,7 +104,10 @@
 Session	sessions[MAX_SESSIONS];
 
 #ifdef WITH_AIXAUTHENTICATE
+void do_aix_change_password(struct passwd *);
 char *aixloginmsg;
+char *aixexpiremsg;
+int aix_password_change_required = 0;
 #endif /* WITH_AIXAUTHENTICATE */
 
 #ifdef HAVE_LOGIN_CAP
@@ -461,6 +464,12 @@
 		    "TTY available");
 #endif /* USE_PAM */
 
+#ifdef WITH_AIXAUTHENTICATE
+	if (aix_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 +766,13 @@
 	}
 #endif
 
+#ifdef WITH_AIXAUTHENTICATE
+	if (aix_password_change_required) {
+		printf("%s\n", aixexpiremsg);
+		do_aix_change_password(pw);
+	}
+#endif
+
 	if (check_quietlogin(s, command))
 		return;
 
@@ -764,9 +780,17 @@
 	if (!is_pam_password_change_required())
 		print_pam_messages();
 #endif /* USE_PAM */
+
 #ifdef WITH_AIXAUTHENTICATE
-	if (aixloginmsg && *aixloginmsg)
+	if (aixexpiremsg && *aixexpiremsg) {
+		if (!aix_password_change_required)
+			printf("%s\n", aixexpiremsg);
+		xfree(aixexpiremsg);
+	}
+	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