diff options
-rw-r--r-- | auth-pam.c | 35 |
1 files changed, 31 insertions, 4 deletions
diff --git a/auth-pam.c b/auth-pam.c index 8425af1ea..abd6a5e1b 100644 --- a/auth-pam.c +++ b/auth-pam.c | |||
@@ -232,7 +232,6 @@ static int sshpam_account_status = -1; | |||
232 | static char **sshpam_env = NULL; | 232 | static char **sshpam_env = NULL; |
233 | static Authctxt *sshpam_authctxt = NULL; | 233 | static Authctxt *sshpam_authctxt = NULL; |
234 | static const char *sshpam_password = NULL; | 234 | static const char *sshpam_password = NULL; |
235 | static 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 |
@@ -810,12 +809,35 @@ sshpam_query(void *ctx, char **name, char **info, | |||
810 | return (-1); | 809 | return (-1); |
811 | } | 810 | } |
812 | 811 | ||
812 | /* | ||
813 | * Returns a junk password of identical length to that the user supplied. | ||
814 | * Used to mitigate timing attacks against crypt(3)/PAM stacks that | ||
815 | * vary processing time in proportion to password length. | ||
816 | */ | ||
817 | static char * | ||
818 | fake_password(const char *wire_password) | ||
819 | { | ||
820 | const char junk[] = "\b\n\r\177INCORRECT"; | ||
821 | char *ret = NULL; | ||
822 | size_t i, l = wire_password != NULL ? strlen(wire_password) : 0; | ||
823 | |||
824 | if (l >= INT_MAX) | ||
825 | fatal("%s: password length too long: %zu", __func__, l); | ||
826 | |||
827 | ret = malloc(l + 1); | ||
828 | for (i = 0; i < l; i++) | ||
829 | ret[i] = junk[i % (sizeof(junk) - 1)]; | ||
830 | ret[i] = '\0'; | ||
831 | return ret; | ||
832 | } | ||
833 | |||
813 | /* XXX - see also comment in auth-chall.c:verify_response */ | 834 | /* XXX - see also comment in auth-chall.c:verify_response */ |
814 | static int | 835 | static int |
815 | sshpam_respond(void *ctx, u_int num, char **resp) | 836 | sshpam_respond(void *ctx, u_int num, char **resp) |
816 | { | 837 | { |
817 | Buffer buffer; | 838 | Buffer buffer; |
818 | struct pam_ctxt *ctxt = ctx; | 839 | struct pam_ctxt *ctxt = ctx; |
840 | char *fake; | ||
819 | 841 | ||
820 | debug2("PAM: %s entering, %u responses", __func__, num); | 842 | debug2("PAM: %s entering, %u responses", __func__, num); |
821 | switch (ctxt->pam_done) { | 843 | switch (ctxt->pam_done) { |
@@ -836,8 +858,11 @@ sshpam_respond(void *ctx, u_int num, char **resp) | |||
836 | (sshpam_authctxt->pw->pw_uid != 0 || | 858 | (sshpam_authctxt->pw->pw_uid != 0 || |
837 | options.permit_root_login == PERMIT_YES)) | 859 | options.permit_root_login == PERMIT_YES)) |
838 | buffer_put_cstring(&buffer, *resp); | 860 | buffer_put_cstring(&buffer, *resp); |
839 | else | 861 | else { |
840 | buffer_put_cstring(&buffer, badpw); | 862 | fake = fake_password(*resp); |
863 | buffer_put_cstring(&buffer, fake); | ||
864 | free(fake); | ||
865 | } | ||
841 | if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) { | 866 | if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) { |
842 | buffer_free(&buffer); | 867 | buffer_free(&buffer); |
843 | return (-1); | 868 | return (-1); |
@@ -1181,6 +1206,7 @@ sshpam_auth_passwd(Authctxt *authctxt, const char *password) | |||
1181 | { | 1206 | { |
1182 | int flags = (options.permit_empty_passwd == 0 ? | 1207 | int flags = (options.permit_empty_passwd == 0 ? |
1183 | PAM_DISALLOW_NULL_AUTHTOK : 0); | 1208 | PAM_DISALLOW_NULL_AUTHTOK : 0); |
1209 | char *fake = NULL; | ||
1184 | 1210 | ||
1185 | if (!options.use_pam || sshpam_handle == NULL) | 1211 | if (!options.use_pam || sshpam_handle == NULL) |
1186 | fatal("PAM: %s called when PAM disabled or failed to " | 1212 | fatal("PAM: %s called when PAM disabled or failed to " |
@@ -1196,7 +1222,7 @@ sshpam_auth_passwd(Authctxt *authctxt, const char *password) | |||
1196 | */ | 1222 | */ |
1197 | if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && | 1223 | if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && |
1198 | options.permit_root_login != PERMIT_YES)) | 1224 | options.permit_root_login != PERMIT_YES)) |
1199 | sshpam_password = badpw; | 1225 | sshpam_password = fake = fake_password(password); |
1200 | 1226 | ||
1201 | sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, | 1227 | sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, |
1202 | (const void *)&passwd_conv); | 1228 | (const void *)&passwd_conv); |
@@ -1206,6 +1232,7 @@ sshpam_auth_passwd(Authctxt *authctxt, const char *password) | |||
1206 | 1232 | ||
1207 | sshpam_err = pam_authenticate(sshpam_handle, flags); | 1233 | sshpam_err = pam_authenticate(sshpam_handle, flags); |
1208 | sshpam_password = NULL; | 1234 | sshpam_password = NULL; |
1235 | free(fake); | ||
1209 | if (sshpam_err == PAM_SUCCESS && authctxt->valid) { | 1236 | if (sshpam_err == PAM_SUCCESS && authctxt->valid) { |
1210 | debug("PAM: password authentication accepted for %.100s", | 1237 | debug("PAM: password authentication accepted for %.100s", |
1211 | authctxt->user); | 1238 | authctxt->user); |