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

Reply via email to