diff -ru openssh-3.5p1.orig/TODO openssh-3.5p1/TODO --- openssh-3.5p1.orig/TODO 2002-09-05 16:32:03.000000000 +1000 +++ openssh-3.5p1/TODO 2003-01-11 01:59:09.000000000 +1100 @@ -15,8 +15,6 @@ - Replacement for setproctitle() - HP-UX support only currently -- Handle changing passwords for the non-PAM expired password case - - Improve PAM support (a pam_lastlog module will cause sshd to exit) and maybe support alternate forms of authentications like OPIE via pam? diff -ru openssh-3.5p1.orig/acconfig.h openssh-3.5p1/acconfig.h --- openssh-3.5p1.orig/acconfig.h 2002-09-26 10:38:48.000000000 +1000 +++ openssh-3.5p1/acconfig.h 2003-01-11 01:59:09.000000000 +1100 @@ -25,6 +25,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 diff -ru openssh-3.5p1.orig/auth-pam.c openssh-3.5p1/auth-pam.c --- openssh-3.5p1.orig/auth-pam.c 2002-07-29 06:24:08.000000000 +1000 +++ openssh-3.5p1/auth-pam.c 2003-01-11 01:59:09.000000000 +1100 @@ -60,7 +60,7 @@ /* states for do_pam_conversation() */ enum { INITIAL_LOGIN, OTHER } pamstate = INITIAL_LOGIN; /* remember whether pam_acct_mgmt() returned PAM_NEW_AUTHTOK_REQD */ -static int password_change_required = 0; +extern int password_change_required; /* remember whether the last pam_authenticate() succeeded or not */ static int was_authenticated = 0; @@ -260,12 +260,7 @@ case PAM_NEW_AUTHTOK_REQD: message_cat(&__pam_msg, use_privsep ? NEW_AUTHTOK_MSG_PRIVSEP : NEW_AUTHTOK_MSG); - /* flag that password change is necessary */ - password_change_required = 1; - /* disallow other functionality for now */ - no_port_forwarding_flag |= 2; - no_agent_forwarding_flag |= 2; - no_x11_forwarding_flag |= 2; + flag_password_change_required(); break; #endif default: diff -ru openssh-3.5p1.orig/auth-passwd.c openssh-3.5p1/auth-passwd.c --- openssh-3.5p1.orig/auth-passwd.c 2002-09-26 09:14:16.000000000 +1000 +++ openssh-3.5p1/auth-passwd.c 2003-01-11 01:59:09.000000000 +1100 @@ -42,6 +42,11 @@ #include "log.h" #include "servconf.h" #include "auth.h" +#include "buffer.h" +#include "misc.h" +#include "channels.h" +#include "monitor_wrap.h" +#include "auth-options.h" #if !defined(USE_PAM) && !defined(HAVE_OSF_SIA) /* Don't need any of these headers for the PAM or SIA cases */ @@ -81,9 +86,9 @@ #endif /* !USE_PAM && !HAVE_OSF_SIA */ extern ServerOptions options; -#ifdef WITH_AIXAUTHENTICATE -extern char *aixloginmsg; -#endif +extern Buffer login_message; +extern int password_change_required; +pid_t password_change_pid; /* pid used to reset forwarding flags */ /* * Tries to authenticate the user using password. Returns true if @@ -123,6 +128,7 @@ /* deny if no user. */ if (pw == NULL) return 0; + buffer_init(&login_message); #ifndef HAVE_CYGWIN if (pw->pw_uid == 0 && options.permit_root_login != PERMIT_YES) return 0; @@ -149,13 +155,25 @@ #endif #ifdef WITH_AIXAUTHENTICATE authsuccess = (authenticate(pw->pw_name,password,&reenter,&authmsg) == 0); + aix_remove_embedded_newlines(authmsg); + + if (authsuccess) { + char *msg; - 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; + get_canonical_hostname(options.verify_reverse_mapping), + "ssh", &msg) < 0) + msg = NULL; + buffer_append(&login_message, msg, strlen(msg)); + } else { + debug("AIX authenticate() failed for user %s: %.100s", + pw->pw_name, authmsg); + } + if (authmsg) + xfree(authmsg); return(authsuccess); #endif @@ -233,3 +251,123 @@ return (strcmp(encrypted_password, pw_password) == 0); #endif /* !USE_PAM && !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) { + setuid(pw->pw_uid); + if (geteuid() == 0) + execl(PASSWD_PROGRAM_PATH, "passwd", pw->pw_name, + (char *)NULL); + else + execl(PASSWD_PROGRAM_PATH, "passwd", (char *)NULL); + + /* 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); + + /* + * passwd sometimes returns 0 when the password has not been changed + * so we must re-test if change is still required. + */ + if (WIFEXITED(status) && WEXITSTATUS(status) == 0 && + PRIVSEP(is_password_change_required(pw)) == 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; + } +} + +/* + * Checks account requires password to be changed. + * 0 = no change, 1 = change reqd, 2 = can't change + */ +int +is_password_change_required(struct passwd *pw) +{ + password_change_required = 0; + /* re-test, sets password_change_required */ + if (getpwnamallow(pw->pw_name) == NULL) + password_change_required = 2; + debug3("%s change required %d", __func__, password_change_required); + return password_change_required; +} + +/* + * Because an expired password is changed after forking to exec the user's + * shell, restoring the port forwarding flags is done by sending a + * USR1 signal to the parent after the password is changed successfully. + */ +void +flag_password_change_required(void) +{ + debug("%s disabling forwarding flags", __func__); + /* flag that password change is necessary */ + password_change_required = 1; + + /* disallow other functionality for now */ + no_port_forwarding_flag |= 2; + no_agent_forwarding_flag |= 2; + no_x11_forwarding_flag |= 2; + + /* set handler to reset flags */ + password_change_pid = getpid(); + mysignal(SIGUSR1, password_change_successful_handler); +} + +/* + * password change successful, tell parent to restore port + * forwarding flags + */ +void +flag_password_change_successful(void) +{ + debug("%s signalling parent to reset forwarding flags", __func__); + kill(password_change_pid, SIGUSR1); + + /* reset flags in local process too */ + password_change_required = 0; + no_port_forwarding_flag &= ~2; + no_agent_forwarding_flag &= ~2; + no_x11_forwarding_flag &= ~2; +} + +/* + * signal handler to reset change flags + */ +void +password_change_successful_handler(int sig) +{ + debug("%s restoring port forwarding flags", __func__); + mysignal(SIGUSR1, SIG_DFL); /* unset handler */ + + password_change_required = 0; + no_port_forwarding_flag &= ~2; + no_agent_forwarding_flag &= ~2; + no_x11_forwarding_flag &= ~2; + if (!no_port_forwarding_flag && options.allow_tcp_forwarding) + channel_permit_all_opens(); +} diff -ru openssh-3.5p1.orig/auth.c openssh-3.5p1/auth.c --- openssh-3.5p1.orig/auth.c 2002-09-22 01:26:53.000000000 +1000 +++ openssh-3.5p1/auth.c 2003-01-11 01:59:09.000000000 +1100 @@ -23,7 +23,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: auth.c,v 1.45 2002/09/20 18:41:29 stevesk Exp $"); +RCSID("$OpenBSD: auth.c,v 1.46 2002/11/04 10:07:53 markus Exp $"); #ifdef HAVE_LOGIN_H #include @@ -52,12 +52,17 @@ #include "bufaux.h" #include "packet.h" +#define PWCHG_FORCED "You must change your password now.\n" +#define PWCHG_EXPIRED "Your password has expired, you must change it now.\n" + /* import */ extern ServerOptions options; /* Debugging messages */ Buffer auth_debug; int auth_debug_init; +extern int password_change_required; +extern Buffer expire_message; /* * Check if the user is allowed to log in via ssh. If user is listed @@ -72,54 +77,85 @@ allowed_user(struct passwd * pw) { struct stat st; - const char *hostname = NULL, *ipaddr = NULL; + const char *hostname = NULL, *ipaddr = NULL, *passwd; 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) +#if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) struct spwd *spw; +#if !defined(USE_PAM) && defined(HAS_SHADOW_EXPIRE) + time_t today, expiredate; +#endif +#endif /* Shouldn't be called if pw is NULL, but better safe than sorry... */ if (!pw || !pw->pw_name) return 0; -#define DAY (24L * 60 * 60) /* 1 day in seconds */ + /* Grab the password for locked account checking */ +#if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) spw = getspnam(pw->pw_name); - if (spw != NULL) { - time_t today = time(NULL) / DAY; - debug3("allowed_user: today %d sp_expire %d sp_lstchg %d" - " sp_max %d", (int)today, (int)spw->sp_expire, - (int)spw->sp_lstchg, (int)spw->sp_max); + if (!spw) + return 0; + passwd = spw->sp_pwdp; +#else + passwd = pw->pw_passwd; +#endif - /* - * We assume account and password expiration occurs the - * day after the day specified. - */ - if (spw->sp_expire != -1 && today > spw->sp_expire) { - log("Account %.100s has expired", pw->pw_name); - return 0; - } + /* check for locked account */ + if (strcmp(passwd, "*LK*") == 0 || passwd[0] == '!') { + log("User %.100s not allowed because account is locked", + pw->pw_name); + return 0; + } - if (spw->sp_lstchg == 0) { - log("User %.100s password has expired (root forced)", - pw->pw_name); - return 0; - } +#if !defined(USE_PAM) && defined(HAVE_SHADOW_H) && \ + !defined(DISABLE_SHADOW) && defined(HAS_SHADOW_EXPIRE) +#define DAY (24L * 60 * 60) /* 1 day in seconds */ + today = time(NULL) / DAY; + debug3("allowed_user: today %d sp_expire %d sp_lstchg %d" + " sp_max %d sp_warn %d", (int)today, (int)spw->sp_expire, + (int)spw->sp_lstchg, (int)spw->sp_max, (int)spw->sp_warn); - if (spw->sp_max != -1 && - today > spw->sp_lstchg + spw->sp_max) { - log("User %.100s password has expired (password aged)", - pw->pw_name); - return 0; - } - } -#else - /* Shouldn't be called if pw is NULL, but better safe than sorry... */ - if (!pw || !pw->pw_name) + /* + * We assume account and password expiration occurs the + * day after the day specified. + */ + if (spw->sp_expire != -1 && today > spw->sp_expire) { + log("Account %.100s has expired", pw->pw_name); return 0; + } else if (spw->sp_expire != -1 && + spw->sp_expire - today < spw->sp_warn) { + char msg[100]; + + snprintf(msg, 100, + "Your account will expire in %d days.\n", + (int)(spw->sp_expire - today)); + buffer_append(&expire_message, msg, strlen(msg)); + } + + expiredate = spw->sp_lstchg + spw->sp_max; + if (spw->sp_lstchg == 0) { + log("User %.100s password has expired (root forced)", + pw->pw_name); + flag_password_change_required(); + buffer_append(&expire_message, PWCHG_FORCED, + strlen(PWCHG_FORCED)); + } else if (spw->sp_max == -1) { + debug3("%s password aging disabled", __func__); + } else if (today > expiredate) { + log("User %.100s password has expired (password aged)", + pw->pw_name); + flag_password_change_required(); + buffer_append(&expire_message, PWCHG_EXPIRED, + strlen(PWCHG_EXPIRED)); + } else if (expiredate - today < spw->sp_warn) { + char msg[100]; + + snprintf(msg, 100, + "Your password will expire in %d days.\n", + (int)(expiredate - today)); + buffer_append(&expire_message, msg, strlen(msg)); + } #endif /* @@ -202,20 +238,65 @@ } #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() 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) ) { + int loginrestrict_errno = errno; + char *msg; + + /* check for AIX account restrictions */ + if (loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &msg) != 0) { + if (msg && *msg) { + aix_remove_embedded_newlines(msg); + log("Login restricted for %s: %.100s", + pw->pw_name, msg); + xfree(msg); } - /* Remove trailing newline */ - *--p = '\0'; - log("Login restricted for %s: %.100s", pw->pw_name, loginmsg); + + /* Don't fail if /etc/nologin set */ + if (!(loginrestrict_errno == EPERM && + stat(_PATH_NOLOGIN, &st) == 0)) + return 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; + int passexpcode; + + enduserdb(); /* flush cached results for passwdexpired */ + passexpcode = passwdexpired(pw->pw_name, &msg); + buffer_append(&expire_message, msg, strlen(msg)); + if (msg && *msg) + aix_remove_embedded_newlines(msg); + debug("AIX passwdexpired returned %d errno %d msg %.100s", + errno, passexpcode, msg); + + switch (passexpcode) { + case 0: /* success, password not expired */ + break; + case 1: /* expired, password change required */ + flag_password_change_required(); + break; + default: /* only admin can change (2) or other error (-1) */ + log("Password can't be changed for user %s: %.100s", + pw->pw_name, msg); + if (msg) + xfree(msg); + return 0; + } + if (msg) + xfree(msg); + + } #endif /* WITH_AIXAUTHENTICATE */ /* We found no reason not to let this user try to log on... */ @@ -417,6 +498,7 @@ uid_t uid = pw->pw_uid; char buf[MAXPATHLEN], homedir[MAXPATHLEN]; char *cp; + int comparehome = 0; struct stat st; if (realpath(file, buf) == NULL) { @@ -424,11 +506,8 @@ strerror(errno)); return -1; } - if (realpath(pw->pw_dir, homedir) == NULL) { - snprintf(err, errlen, "realpath %s failed: %s", pw->pw_dir, - strerror(errno)); - return -1; - } + if (realpath(pw->pw_dir, homedir) != NULL) + comparehome = 1; /* check the open file to avoid races */ if (fstat(fileno(f), &st) < 0 || @@ -457,7 +536,7 @@ } /* If are passed the homedir then we can stop */ - if (strcmp(homedir, buf) == 0) { + if (comparehome && strcmp(homedir, buf) == 0) { debug3("secure_filename: terminating check at '%s'", buf); break; @@ -483,10 +562,16 @@ #endif struct passwd *pw; + buffer_init(&expire_message); pw = getpwnam(user); if (pw == NULL) { log("Illegal user %.100s from %.100s", user, get_remote_ipaddr()); +#ifdef WITH_AIXAUTHENTICATE + loginfailed(user, + get_canonical_hostname(options.verify_reverse_mapping), + "ssh"); +#endif return (NULL); } if (!allowed_user(pw)) diff -ru openssh-3.5p1.orig/auth.h openssh-3.5p1/auth.h --- openssh-3.5p1.orig/auth.h 2002-09-27 13:26:01.000000000 +1000 +++ openssh-3.5p1/auth.h 2003-01-11 01:59:09.000000000 +1100 @@ -101,6 +101,8 @@ int auth_rhosts_rsa(struct passwd *, char *, Key *); int auth_password(Authctxt *, const char *); +int do_tty_change_password(struct passwd *pw); +void flag_password_change_required(void); int auth_rsa(struct passwd *, BIGNUM *); int auth_rsa_challenge_dialog(Key *); BIGNUM *auth_rsa_generate_challenge(Key *); @@ -156,6 +158,8 @@ int allowed_user(struct passwd *); struct passwd * getpwnamallow(const char *user); +int is_password_change_required(struct passwd *); +void flag_password_change_successful(void); char *get_challenge(Authctxt *); int verify_response(Authctxt *, const char *); @@ -184,6 +188,8 @@ void auth_debug_send(void); void auth_debug_reset(void); +void password_change_successful_handler(int); + #define AUTH_FAIL_MAX 6 #define AUTH_FAIL_LOG (AUTH_FAIL_MAX/2) #define AUTH_FAIL_MSG "Too many authentication failures for %.100s" diff -ru openssh-3.5p1.orig/config.h.in openssh-3.5p1/config.h.in --- openssh-3.5p1.orig/config.h.in 2002-10-04 11:31:57.000000000 +1000 +++ openssh-3.5p1/config.h.in 2003-01-11 01:59:09.000000000 +1100 @@ -25,6 +25,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 diff -ru openssh-3.5p1.orig/configure openssh-3.5p1/configure --- openssh-3.5p1.orig/configure 2002-10-04 11:31:56.000000000 +1000 +++ openssh-3.5p1/configure 2003-01-11 01:59:09.000000000 +1100 @@ -3293,6 +3293,56 @@ fi fi +# Extract the first word of "passwd", so it can be a program name with args. +set dummy passwd; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_PASSWD_PROGRAM_PATH+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $PASSWD_PROGRAM_PATH in + [\\/]* | ?:[\\/]*) + ac_cv_path_PASSWD_PROGRAM_PATH="$PASSWD_PROGRAM_PATH" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PASSWD_PROGRAM_PATH="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + ;; +esac +fi +PASSWD_PROGRAM_PATH=$ac_cv_path_PASSWD_PROGRAM_PATH + +if test -n "$PASSWD_PROGRAM_PATH"; then + echo "$as_me:$LINENO: result: $PASSWD_PROGRAM_PATH" >&5 +echo "${ECHO_T}$PASSWD_PROGRAM_PATH" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +if test ! -z "$PASSWD_PROGRAM_PATH" ; then + cat >>confdefs.h <<_ACEOF +#define PASSWD_PROGRAM_PATH "$PASSWD_PROGRAM_PATH" +_ACEOF + +else + { { echo "$as_me:$LINENO: error: *** passwd command not found - check config.log ***" >&5 +echo "$as_me: error: *** passwd command not found - check config.log ***" >&2;} + { (exit 1); exit 1; }; } +fi + if test -z "$LD" ; then LD=$CC fi @@ -17350,6 +17400,7 @@ s,@TEST_MINUS_S_SH@,$TEST_MINUS_S_SH,;t t s,@SH@,$SH,;t t s,@LOGIN_PROGRAM_FALLBACK@,$LOGIN_PROGRAM_FALLBACK,;t t +s,@PASSWD_PROGRAM_PATH@,$PASSWD_PROGRAM_PATH,;t t s,@LD@,$LD,;t t s,@LIBWRAP@,$LIBWRAP,;t t s,@LIBPAM@,$LIBPAM,;t t diff -ru openssh-3.5p1.orig/configure.ac openssh-3.5p1/configure.ac --- openssh-3.5p1.orig/configure.ac 2002-09-26 10:38:47.000000000 +1000 +++ openssh-3.5p1/configure.ac 2003-01-11 01:59:09.000000000 +1100 @@ -40,6 +40,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 diff -ru openssh-3.5p1.orig/log.c openssh-3.5p1/log.c --- openssh-3.5p1.orig/log.c 2002-07-24 07:01:57.000000000 +1000 +++ openssh-3.5p1/log.c 2003-01-11 01:59:09.000000000 +1100 @@ -233,6 +233,7 @@ next_cu = cu->next; xfree(cu); } + fatal_cleanups = NULL; } /* Cleanup and exit */ diff -ru openssh-3.5p1.orig/monitor.c openssh-3.5p1/monitor.c --- openssh-3.5p1.orig/monitor.c 2002-09-27 13:26:02.000000000 +1000 +++ openssh-3.5p1/monitor.c 2003-01-11 01:59:09.000000000 +1100 @@ -98,6 +98,7 @@ int mm_answer_moduli(int, Buffer *); int mm_answer_sign(int, Buffer *); int mm_answer_pwnamallow(int, Buffer *); +int mm_answer_is_pwchange_reqd(int, Buffer *); int mm_answer_auth2_read_banner(int, Buffer *); int mm_answer_authserv(int, Buffer *); int mm_answer_authpassword(int, Buffer *); @@ -183,6 +184,7 @@ {MONITOR_REQ_PTY, 0, mm_answer_pty}, {MONITOR_REQ_PTYCLEANUP, 0, mm_answer_pty_cleanup}, {MONITOR_REQ_TERM, 0, mm_answer_term}, + {MONITOR_REQ_PWCHANGE_REQD, 0, mm_answer_is_pwchange_reqd}, {0, 0, NULL} }; @@ -219,6 +221,7 @@ {MONITOR_REQ_PTY, MON_ONCE, mm_answer_pty}, {MONITOR_REQ_PTYCLEANUP, MON_ONCE, mm_answer_pty_cleanup}, {MONITOR_REQ_TERM, 0, mm_answer_term}, + {MONITOR_REQ_PWCHANGE_REQD, 0, mm_answer_is_pwchange_reqd}, {0, 0, NULL} }; @@ -320,10 +323,12 @@ monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_PWCHANGE_REQD, 1); } else { mon_dispatch = mon_dispatch_postauth15; monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_PWCHANGE_REQD, 1); } if (!no_pty_flag) { monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); @@ -562,6 +567,21 @@ return (0); } +int +mm_answer_is_pwchange_reqd(int socket, Buffer *m) +{ + int required; + + buffer_clear(m); + required = is_password_change_required(authctxt->pw); + buffer_put_int(m, required); + + debug3("%s: sending MONITOR_ANS_PWCHANGE_REQD: %d", __func__, required); + mm_request_send(socket, MONITOR_ANS_PWCHANGE_REQD, m); + + return (0); +} + int mm_answer_auth2_read_banner(int socket, Buffer *m) { char *banner; diff -ru openssh-3.5p1.orig/monitor.h openssh-3.5p1/monitor.h --- openssh-3.5p1.orig/monitor.h 2002-09-27 13:26:02.000000000 +1000 +++ openssh-3.5p1/monitor.h 2003-01-11 01:59:09.000000000 +1100 @@ -33,6 +33,7 @@ MONITOR_REQ_FREE, MONITOR_REQ_AUTHSERV, MONITOR_REQ_SIGN, MONITOR_ANS_SIGN, MONITOR_REQ_PWNAM, MONITOR_ANS_PWNAM, + MONITOR_REQ_PWCHANGE_REQD, MONITOR_ANS_PWCHANGE_REQD, MONITOR_REQ_AUTH2_READ_BANNER, MONITOR_ANS_AUTH2_READ_BANNER, MONITOR_REQ_AUTHPASSWORD, MONITOR_ANS_AUTHPASSWORD, MONITOR_REQ_BSDAUTHQUERY, MONITOR_ANS_BSDAUTHQUERY, diff -ru openssh-3.5p1.orig/monitor_wrap.c openssh-3.5p1/monitor_wrap.c --- openssh-3.5p1.orig/monitor_wrap.c 2002-09-27 13:26:03.000000000 +1000 +++ openssh-3.5p1/monitor_wrap.c 2003-01-11 01:59:09.000000000 +1100 @@ -207,6 +207,24 @@ return (pw); } +int +mm_is_password_change_required(struct passwd *pw) +{ + Buffer m; + int result; + + debug3("%s entering", __func__); + buffer_init(&m); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PWCHANGE_REQD, &m); + buffer_clear(&m); + mm_request_receive_expect(pmonitor->m_recvfd, + MONITOR_ANS_PWCHANGE_REQD, &m); + result = buffer_get_int(&m); + buffer_free(&m); + + return (result); +} + char *mm_auth2_read_banner(void) { Buffer m; diff -ru openssh-3.5p1.orig/monitor_wrap.h openssh-3.5p1/monitor_wrap.h --- openssh-3.5p1.orig/monitor_wrap.h 2002-09-27 13:26:04.000000000 +1000 +++ openssh-3.5p1/monitor_wrap.h 2003-01-11 01:59:09.000000000 +1100 @@ -44,6 +44,7 @@ int mm_key_sign(Key *, u_char **, u_int *, u_char *, u_int); void mm_inform_authserv(char *, char *); struct passwd *mm_getpwnamallow(const char *); +int mm_is_password_change_required(struct passwd *); char *mm_auth2_read_banner(void); int mm_auth_password(struct Authctxt *, char *); int mm_key_allowed(enum mm_keytype, char *, char *, Key *); 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 2002-07-07 12:17:36.000000000 +1000 +++ openssh-3.5p1/openbsd-compat/port-aix.c 2003-01-11 01:59:09.000000000 +1100 @@ -52,5 +52,25 @@ 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'; +} +#endif /* WITH_AIXAUTHENTICATE */ +#endif /* _AIX */ diff -ru openssh-3.5p1.orig/openbsd-compat/port-aix.h openssh-3.5p1/openbsd-compat/port-aix.h --- openssh-3.5p1.orig/openbsd-compat/port-aix.h 2002-07-07 12:17:36.000000000 +1000 +++ openssh-3.5p1/openbsd-compat/port-aix.h 2003-01-11 01:59:09.000000000 +1100 @@ -26,4 +26,5 @@ #ifdef _AIX void aix_usrinfo(struct passwd *pw); +void aix_remove_embedded_newlines(char *); #endif /* _AIX */ diff -ru openssh-3.5p1.orig/session.c openssh-3.5p1/session.c --- openssh-3.5p1.orig/session.c 2002-09-26 10:38:50.000000000 +1000 +++ openssh-3.5p1/session.c 2003-01-11 01:59:09.000000000 +1100 @@ -102,10 +102,9 @@ /* data */ #define MAX_SESSIONS 10 Session sessions[MAX_SESSIONS]; - -#ifdef WITH_AIXAUTHENTICATE -char *aixloginmsg; -#endif /* WITH_AIXAUTHENTICATE */ +Buffer expire_message; /* "password will expire/has expired" messages */ +Buffer login_message; /* message to be displayed after login */ +int password_change_required = 0; #ifdef HAVE_LOGIN_CAP login_cap_t *lc; @@ -456,10 +455,11 @@ #if defined(USE_PAM) do_pam_session(s->pw->pw_name, NULL); do_pam_setcred(1); - if (is_pam_password_change_required()) +#endif /* USE_PAM */ + + if (password_change_required) packet_disconnect("Password change required but no " "TTY available"); -#endif /* USE_PAM */ /* Fork the child. */ if ((pid = fork()) == 0) { @@ -723,6 +723,7 @@ socklen_t fromlen; struct sockaddr_storage from; struct passwd * pw = s->pw; + int password_changed = 0; pid_t pid = getpid(); /* @@ -746,16 +747,22 @@ options.verify_reverse_mapping), (struct sockaddr *)&from, fromlen); -#ifdef USE_PAM /* * If password change is needed, do it now. * This needs to occur before the ~/.hushlogin check. */ +#ifdef USE_PAM if (is_pam_password_change_required()) { print_pam_messages(); do_pam_chauthtok(); } #endif + buffer_append(&expire_message, "\0", 1); + if (password_change_required) { + puts((char *)buffer_ptr(&expire_message)); + do_tty_change_password(pw); + password_changed = 1; + } if (check_quietlogin(s, command)) return; @@ -764,10 +771,12 @@ if (!is_pam_password_change_required()) print_pam_messages(); #endif /* USE_PAM */ -#ifdef WITH_AIXAUTHENTICATE - if (aixloginmsg && *aixloginmsg) - printf("%s\n", aixloginmsg); -#endif /* WITH_AIXAUTHENTICATE */ + if (!password_changed) + printf("%s", (char *)buffer_ptr(&expire_message)); + + /* display post-login message */ + buffer_append(&login_message, "\0", 1); + puts((char *)buffer_ptr(&login_message)); #ifndef NO_SSH_LASTLOG if (options.print_lastlog && s->last_login_time != 0) {