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 8425af1ea..abd6a5e1b 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
@@ -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 */
817static char *
818fake_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 */
814static int 835static int
815sshpam_respond(void *ctx, u_int num, char **resp) 836sshpam_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);