[PATCH 1/2] Support changing expired passwords

Joakim Tjernlund joakim.tjernlund at infinera.com
Mon Apr 25 16:26:58 AWST 2016


This adds support for handling expired passwords in the server.
Client remains to do.

Signed-off-by: Joakim Tjernlund <joakim.tjernlund at 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



More information about the Dropbear mailing list