summaryrefslogtreecommitdiff
path: root/auth-pam.c
diff options
context:
space:
mode:
authorDarren Tucker <dtucker@zip.com.au>2016-07-15 13:49:44 +1000
committerDarren Tucker <dtucker@zip.com.au>2016-07-15 13:49:44 +1000
commit283b97ff33ea2c641161950849931bd578de6946 (patch)
tree2f8127f4ca0bd727252c860e105ad1140ce54b6b /auth-pam.c
parent9286875a73b2de7736b5e50692739d314cd8d9dc (diff)
Mitigate timing of disallowed users PAM logins.
When sshd decides to not allow a login (eg PermitRootLogin=no) and it's using PAM, it sends a fake password to PAM so that the timing for the failure is not noticeably different whether or not the password is correct. This behaviour can be detected by sending a very long password string which is slower to hash than the fake password. Mitigate by constructing an invalid password that is the same length as the one from the client and thus takes the same time to hash. Diff from djm@
Diffstat (limited to 'auth-pam.c')
-rw-r--r--auth-pam.c35
1 files changed, 31 insertions, 4 deletions
diff --git a/auth-pam.c b/auth-pam.c
index 451de78cd..465b5a702 100644
--- a/auth-pam.c
+++ b/auth-pam.c
@@ -232,7 +232,6 @@ static int sshpam_account_status = -1;
232static char **sshpam_env = NULL; 232static char **sshpam_env = NULL;
233static Authctxt *sshpam_authctxt = NULL; 233static Authctxt *sshpam_authctxt = NULL;
234static const char *sshpam_password = NULL; 234static const char *sshpam_password = NULL;
235static char badpw[] = "\b\n\r\177INCORRECT";
236 235
237/* Some PAM implementations don't implement this */ 236/* Some PAM implementations don't implement this */
238#ifndef HAVE_PAM_GETENVLIST 237#ifndef HAVE_PAM_GETENVLIST
@@ -795,12 +794,35 @@ sshpam_query(void *ctx, char **name, char **info,
795 return (-1); 794 return (-1);
796} 795}
797 796
797/*
798 * Returns a junk password of identical length to that the user supplied.
799 * Used to mitigate timing attacks against crypt(3)/PAM stacks that
800 * vary processing time in proportion to password length.
801 */
802static char *
803fake_password(const char *wire_password)
804{
805 const char junk[] = "\b\n\r\177INCORRECT";
806 char *ret = NULL;
807 size_t i, l = wire_password != NULL ? strlen(wire_password) : 0;
808
809 if (l >= INT_MAX)
810 fatal("%s: password length too long: %zu", __func__, l);
811
812 ret = malloc(l + 1);
813 for (i = 0; i < l; i++)
814 ret[i] = junk[i % (sizeof(junk) - 1)];
815 ret[i] = '\0';
816 return ret;
817}
818
798/* XXX - see also comment in auth-chall.c:verify_response */ 819/* XXX - see also comment in auth-chall.c:verify_response */
799static int 820static int
800sshpam_respond(void *ctx, u_int num, char **resp) 821sshpam_respond(void *ctx, u_int num, char **resp)
801{ 822{
802 Buffer buffer; 823 Buffer buffer;
803 struct pam_ctxt *ctxt = ctx; 824 struct pam_ctxt *ctxt = ctx;
825 char *fake;
804 826
805 debug2("PAM: %s entering, %u responses", __func__, num); 827 debug2("PAM: %s entering, %u responses", __func__, num);
806 switch (ctxt->pam_done) { 828 switch (ctxt->pam_done) {
@@ -821,8 +843,11 @@ sshpam_respond(void *ctx, u_int num, char **resp)
821 (sshpam_authctxt->pw->pw_uid != 0 || 843 (sshpam_authctxt->pw->pw_uid != 0 ||
822 options.permit_root_login == PERMIT_YES)) 844 options.permit_root_login == PERMIT_YES))
823 buffer_put_cstring(&buffer, *resp); 845 buffer_put_cstring(&buffer, *resp);
824 else 846 else {
825 buffer_put_cstring(&buffer, badpw); 847 fake = fake_password(*resp);
848 buffer_put_cstring(&buffer, fake);
849 free(fake);
850 }
826 if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) { 851 if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) {
827 buffer_free(&buffer); 852 buffer_free(&buffer);
828 return (-1); 853 return (-1);
@@ -1166,6 +1191,7 @@ sshpam_auth_passwd(Authctxt *authctxt, const char *password)
1166{ 1191{
1167 int flags = (options.permit_empty_passwd == 0 ? 1192 int flags = (options.permit_empty_passwd == 0 ?
1168 PAM_DISALLOW_NULL_AUTHTOK : 0); 1193 PAM_DISALLOW_NULL_AUTHTOK : 0);
1194 char *fake = NULL;
1169 1195
1170 if (!options.use_pam || sshpam_handle == NULL) 1196 if (!options.use_pam || sshpam_handle == NULL)
1171 fatal("PAM: %s called when PAM disabled or failed to " 1197 fatal("PAM: %s called when PAM disabled or failed to "
@@ -1181,7 +1207,7 @@ sshpam_auth_passwd(Authctxt *authctxt, const char *password)
1181 */ 1207 */
1182 if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && 1208 if (!authctxt->valid || (authctxt->pw->pw_uid == 0 &&
1183 options.permit_root_login != PERMIT_YES)) 1209 options.permit_root_login != PERMIT_YES))
1184 sshpam_password = badpw; 1210 sshpam_password = fake = fake_password(password);
1185 1211
1186 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 1212 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
1187 (const void *)&passwd_conv); 1213 (const void *)&passwd_conv);
@@ -1191,6 +1217,7 @@ sshpam_auth_passwd(Authctxt *authctxt, const char *password)
1191 1217
1192 sshpam_err = pam_authenticate(sshpam_handle, flags); 1218 sshpam_err = pam_authenticate(sshpam_handle, flags);
1193 sshpam_password = NULL; 1219 sshpam_password = NULL;
1220 free(fake);
1194 if (sshpam_err == PAM_SUCCESS && authctxt->valid) { 1221 if (sshpam_err == PAM_SUCCESS && authctxt->valid) {
1195 debug("PAM: password authentication accepted for %.100s", 1222 debug("PAM: password authentication accepted for %.100s",
1196 authctxt->user); 1223 authctxt->user);