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)