[PATCH] implement server PAM expired password dialog

2019-11-20 Thread Joseph Reynolds
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 num_msg,
  * interactive

Dropbear server patch for PAM expired password dialog

2019-11-20 Thread Joseph Reynolds

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