[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