This adds support for handling expired passwords in the server. Client remains to do.
Signed-off-by: Joakim Tjernlund <joakim.tjernl...@infinera.com> --- auth.h | 1 + svr-auth.c | 21 +++++++++++++++++++++ svr-authpam.c | 52 +++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 59 insertions(+), 15 deletions(-) diff --git a/auth.h b/auth.h index 3a47401..a6bb3f1 100644 --- a/auth.h +++ b/auth.h @@ -36,6 +36,7 @@ void cli_authinitialise(); void recv_msg_userauth_request(); void send_msg_userauth_failure(int partial, int incrfail); void send_msg_userauth_success(); +void send_msg_userauth_chauthtok(); void send_msg_userauth_banner(buffer *msg); void svr_auth_password(); void svr_auth_pubkey(); diff --git a/svr-auth.c b/svr-auth.c index 2febe8d..35c4758 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -419,3 +419,24 @@ void send_msg_userauth_success() { TRACE(("leave send_msg_userauth_success")) } + +/* Send change password */ +void send_msg_userauth_chauthtok() { +#ifdef ENABLE_SVR_PAM_AUTH + const char * msg = ""; +#else + const char * msg = "Password has expired, please change now"; +#endif + + TRACE(("enter send_msg_userauth_chauthtok")) + + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_PASSWD_CHANGEREQ); + buf_putstring(ses.writepayload, msg, strlen(msg)); + buf_putstring(ses.writepayload, "en", 2); + + encrypt_packet(); + + TRACE(("leave send_msg_userauth_chauthtok")) +} diff --git a/svr-authpam.c b/svr-authpam.c index 30de5b3..033dfae 100644 --- a/svr-authpam.c +++ b/svr-authpam.c @@ -40,8 +40,9 @@ #endif struct UserDataS { - char* user; + const char* user; char* passwd; + char* new_passwd; }; /* PAM conversation function - for now we only handle one message */ @@ -89,7 +90,7 @@ pamConvFunc(int num_msg, case PAM_PROMPT_ECHO_OFF: - if (!(strcmp(compare_message, "password:") == 0)) { + if (strstr(compare_message, "password:") == NULL) { /* We don't recognise the prompt as asking for a password, so can't handle it. Add more above as required for different pam modules/implementations. If you need @@ -106,9 +107,10 @@ pamConvFunc(int num_msg, * it here */ resp = (struct pam_response*) m_malloc(sizeof(struct pam_response)); memset(resp, 0, sizeof(struct pam_response)); - - resp->resp = m_strdup(userDatap->passwd); - m_burn(userDatap->passwd, strlen(userDatap->passwd)); + if (strstr(compare_message, "new")) + resp->resp = m_strdup(userDatap->new_passwd); + else + resp->resp = m_strdup(userDatap->passwd); (*respp) = resp; break; @@ -180,7 +182,7 @@ pamConvFunc(int num_msg, * interactive responses, over the network. */ void svr_auth_pam(const char * username, int localUserMissing) { - struct UserDataS userData = {NULL, NULL}; + struct UserDataS userData = {NULL, NULL, NULL}; struct pam_conv pamConv = { pamConvFunc, &userData /* submitted to pamvConvFunc as appdata_ptr */ @@ -189,6 +191,7 @@ void svr_auth_pam(const char * username, int localUserMissing) { pam_handle_t* pamHandlep = NULL; char * password = NULL; + char * new_password = NULL; unsigned int passwordlen; int rc = PAM_SUCCESS; @@ -196,19 +199,16 @@ void svr_auth_pam(const char * username, int localUserMissing) { /* check if client wants to change password */ changepw = buf_getbool(ses.payload); - if (changepw) { - /* not implemented by this server */ - send_msg_userauth_failure(0, 1); - goto cleanup; - } password = buf_getstring(ses.payload, &passwordlen); - + if (changepw) + new_password = buf_getstring(ses.payload, &passwordlen); /* used to pass data to the PAM conversation function - don't bother with * strdup() etc since these are touched only by our own conversation * function (above) which takes care of it */ userData.user = username; userData.passwd = password; + userData.new_passwd = new_password; /* Init pam */ if ((rc = pam_start("sshd", NULL, &pamConv, &pamHandlep)) != PAM_SUCCESS) { @@ -242,7 +242,22 @@ void svr_auth_pam(const char * username, int localUserMissing) { goto cleanup; } - if ((rc = pam_acct_mgmt(pamHandlep, 0)) != PAM_SUCCESS) { + if (changepw) { + rc = pam_chauthtok(pamHandlep, PAM_CHANGE_EXPIRED_AUTHTOK); + if (rc != PAM_SUCCESS) { + dropbear_log(LOG_WARNING, "pam_chauthtok() failed, rc=%d, %s", + rc, pam_strerror(pamHandlep, rc)); + dropbear_log(LOG_WARNING, + "Bad PAM password changing attempt for '%s' from %s", + ses.authstate.pw_name, + svr_ses.addrstring); + send_msg_userauth_failure(0, 1); + goto cleanup; + } + } + rc = pam_acct_mgmt(pamHandlep, 0); + + if (!(rc == PAM_SUCCESS || rc == PAM_NEW_AUTHTOK_REQD)) { dropbear_log(LOG_WARNING, "pam_acct_mgmt() failed, rc=%d, %s", rc, pam_strerror(pamHandlep, rc)); dropbear_log(LOG_WARNING, @@ -275,12 +290,19 @@ void svr_auth_pam(const char * username, int localUserMissing) { dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s", ses.authstate.pw_name, svr_ses.addrstring); - send_msg_userauth_success(); + if (rc == PAM_NEW_AUTHTOK_REQD) + send_msg_userauth_chauthtok(); + else + send_msg_userauth_success(); cleanup: if (password != NULL) { - m_burn(password, passwordlen); + m_burn(password, strlen(password)); m_free(password); + if (new_password) { + m_burn(new_password, strlen(new_password)); + m_free(new_password); + } } if (pamHandlep != NULL) { TRACE(("pam_end")) -- 2.7.3