diff options
author | Darren Tucker <dtucker@zip.com.au> | 2016-07-15 13:49:44 +1000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2016-07-22 13:59:49 +0100 |
commit | dde63f7f998ac3812a26bbb2c1b2947f24fcd060 (patch) | |
tree | be4b41e362d31150cc84039aa6150ccb637d8107 | |
parent | e5ef9d3942cebda819a6fd81647b51c8d87d23df (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@
Origin: upstream, https://anongit.mindrot.org/openssh.git/commit/?id=283b97ff33ea2c641161950849931bd578de6946
Bug-Debian: https://bugs.debian.org/831902
Last-Update: 2016-07-22
Patch-Name: CVE-2016-6210-2.patch
-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); |