[PATCH] implement server PAM expired password dialog
From: Joseph Reynolds This implements the SSH server Linux PAM expired password dialog. That is, when Linux PAM is used for authentication, and a RFC 4252 SSH_MSG_USERAUTH_REQUEST comes in with a request to change the password, this code will call pam_chauthtok to perform the password change request. This is a re-work of a patch to support handling/changing expired password, found in the dropbear email list: https://lists.ucc.gu.uwa.edu.au/pipermail/dropbear/2016q2/001895.html The patch was modified to work with DROPBEAR_2019.78. This may require PAM config file changes. The dropbear SSH server identifies itself to PAM (via pam_start) as "sshd". So PAM config files must have an entry for the "sshd" service "password" type, for example: sshd password include common-password tested: yes Via the OpenSSH client (ssh -v: OpenSSH_7.8p1, OpenSSL 1.0.1u 22 Sep 2016) Test scenario: As the root user on the device which implements the dropbear SSH server: useradd testuser passwd testuser TESTPASSWORD1=TestP4ssw0rd TESTPASSWORD2=N3wTestPswd TESTPASSWORD3=Th1rdB4dPswd passwd --expire testuser From a test host (various ssh clients): ssh testuser@$ip "echo GOT HERE" Use various combinations of good and bad passwords from the initial prompt, from the password change prompt (for old and new). The ssh prompts and responses were all reasonable, and when I successfully changed the password, was able to access the device's shell. Signed-off-by: Joseph Reynolds --- auth.h| 1 + svr-auth.c| 22 ++ svr-authpam.c | 57 +++-- 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/auth.h b/auth.h index 4bdb359..b17d26b 100644 --- a/auth.h +++ b/auth.h @@ -36,6 +36,7 @@ void cli_authinitialise(void); void recv_msg_userauth_request(void); void send_msg_userauth_failure(int partial, int incrfail); void send_msg_userauth_success(void); +void send_msg_userauth_chauthtok(void); void send_msg_userauth_banner(const buffer *msg); void svr_auth_password(int valid_user); void svr_auth_pubkey(int valid_user); diff --git a/svr-auth.c b/svr-auth.c index 7575f90..7fd641e 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -475,3 +475,25 @@ 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 = "The password has expired. Please change it 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 e236db4..6361f3c 100644 --- a/svr-authpam.c +++ b/svr-authpam.c @@ -42,6 +42,7 @@ struct UserDataS { 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 @@ -107,8 +108,10 @@ pamConvFunc(int num_msg, 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; @@ -138,7 +141,6 @@ pamConvFunc(int num_msg, memset(resp, 0, sizeof(struct pam_response)); resp->resp = m_strdup(userDatap->user); - TRACE(("userDatap->user='%s'", userDatap->user)) (*respp) = resp; break; @@ -180,7 +182,7 @@ pamConvFunc(int
Dropbear server patch for PAM expired password dialog
Dropbears, I submitted a patch to enhance the dropbear SSH server to handle the Linux-PAM "expired password dialog". That is, if you ssh into a system running the dropbear SSH server and configured to use Linux PAM and your password is expired, this patch enhances the SSH server so you can change your password while logging in. I believe this feature will become more important to small devices because of increased security concerns. Specifically, if a device is created with a default ssh password, it is too easy for an attacker to learn that password and gain access to the device. However, if that password is pre-expired (such as with the `passwd --expire USERNAME` command), the password must be changed before the device can be accessed; that encourages folks to change the password away from the default, so the attackers won't be able to access the device. For example, California Law SB-327 (https://leginfo.legislature.ca.gov/faces/billTextClient.xhtml?bill_id=201720180SB327) title 1.81.26 ("Security of Connected Devices") paragraph 1798.91.04. (b) (2) states (excerpted): it shall be deemed a reasonable security feature [if] ... the device contains a security feature that requires a user to generate a new means of authentication before access is granted to the device for the first time. That means you need to set or change the password before you can use the device. A use case is: 1. Someone creates a device which uses the dropbear SSH server, and provisions the device with a username and default password, and administratively expires the password. 2. A user installs the device and tries to use it. They are required to change its password the first time they attempt to access it. 3. After this point, the attacker cannot access the device with its default password. Anyway, that's my motivation for making this patch. This is a refresh of a previous patch discussed on the dropbear mailing list: https://lists.ucc.gu.uwa.edu.au/pipermail/dropbear/2016q2/thread.html in threads titled "dropbear and PAM_NEW_AUTHTOK_REQ" and "[PATCH 1/2] Support changing expired passwords". This patch is substantially the same as the previous patch. I did not make the corresponding change to the SSH client because I am not using it. Here is a code walkthrough to help explain the changes. The scenario involves: - A user who invokes the "ssh" command. - The client "ssh" program. - The dropbear "ssh" server. - The Linux PAM implementation. The sequence is: 1. A user directs their SSH client to login to the dropbear SSH server. 2. The ssh client passes the username and password to the dropbear ssh server in a SSH_MSG_USERAUTH_REQUEST per RFC 4252. 3. The dropbear ssh server response to the client indicates the password is expired. ==> dropbear is enhanced with a new message for this. Specifically, return code PAM_NEW_AUTHTOK_REQD means that everything is okay, except the password is expired and must be changed. Changed code in svr-authpam.c function svr_auth_pam() handles this condition by calling the new send_msg_userauth_chauthtok() function. 4. The SSH client prompts the user for a new password, and sends another SSH_MSG_USERAUTH_REQUEST to the SSH server, this time with the fifth element TRUE to indicate a password change request. 5. The dropbear ssh server changes the user's password, and the ssh connection continues. ==> dropbear is enhanced to invoke pam_chauthtok ("change password") to handle this request. This detailed code walkthrough begins at the last step. Referring to the patched code: - The SSH_MSG_USERAUTH_REQUEST flows into the dropbear SSH server, is decoded partially, and control is passed into svr-authpam.c function svr_auth_pam(). - The client_request_changepw boolean and existing password are read. - If client_request_changepw==true, the new password is also read. - Both passwords are stored in the UserData. (This is eventually passed into pamConvFunc() as the appdata_ptr parameter via the call to pam_start(..., pamConv, ...)). - The pam_start and pam_authenticate functions are called as usual. - If client_request_changepw==true, we invoke PAM pam_chauthtok() to change the password. The call to pam_chauthtok() does not use the PAM_CHANGE_EXPIRED_AUTHTOK flag, making this an unconditional request. That is, if the SSH client requests a password change, we will definitely call PAM to change the password. - As part of its processing, the pam_chauthtok() function calls back into the srv-authpam.c pamConvFunc() to obtain the new password. It passes in a PAM_PROMPT_ECHO_OFF message like "Enter new password:". The patch enhances this function to return the new password which it gets from the userData set up by svr_auth_pam(). - If everything goes well, pam_chauthtok takes care of writing that password into the /etc/passwd file. - If not, PAM error codes are handled correctly. Notes: - The unnecessary m_burn(userDatap->passwd, ...) was remove