[PATCH] Password expiry merge (AIX parts)

Darren Tucker dtucker at zip.com.au
Wed Jul 30 14:58:30 EST 2003


Hi All.
	Attached is a patch introduces password expiry handling for AIX (other
platforms to follow).  It is more or less the same as the previous patch
but has been updated to reflect recent changes to auth-passwd.c

	I'm wondering if the AIX parts of auth.c should be moved to port-aix.c
and if the generic password change functions (currently at the end of
auth-passwd.c) belong in a separate file (eg pwchange.c).

	Comments?  Objections?  Any volunteers to look this one over?

-- 
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.159
diff -u -r1.159 acconfig.h
--- acconfig.h	14 Jul 2003 06:21:44 -0000	1.159
+++ acconfig.h	16 Jul 2003 04:42:22 -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.57
diff -u -r1.57 auth-passwd.c
--- auth-passwd.c	24 Jul 2003 06:52:13 -0000	1.57
+++ auth-passwd.c	26 Jul 2003 14:53:20 -0000
@@ -42,14 +42,18 @@
 #include "log.h"
 #include "servconf.h"
 #include "auth.h"
+#include "auth-options.h"
+#include "misc.h"
+#include "buffer.h"
 #include "openbsd-compat/xcrypt.h"
 #ifdef WITH_AIXAUTHENTICATE
 # include "buffer.h"
 # include "canohost.h"
-extern Buffer loginmsg;
 #endif
 
 extern ServerOptions options;
+extern Buffer loginmsg;
+int password_change_required = 0;
 
 /*
  * Tries to authenticate the user using password.  Returns true if
@@ -168,4 +172,81 @@
 	}
 # endif
 #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.137
diff -u -r1.137 configure.ac
--- configure.ac	23 Jul 2003 04:33:10 -0000	1.137
+++ configure.ac	26 Jul 2003 14:36:55 -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.255
diff -u -r1.255 sshd.c
--- sshd.c	19 Jul 2003 09:54:31 -0000	1.255
+++ sshd.c	26 Jul 2003 14:36:56 -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);
@@ -1495,6 +1496,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