summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);