[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