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 451de78cd..465b5a702 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 |
@@ -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 | */ | ||
802 | static char * | ||
803 | fake_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 */ |
799 | static int | 820 | static int |
800 | sshpam_respond(void *ctx, u_int num, char **resp) | 821 | sshpam_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); |