summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarren Tucker <dtucker@zip.com.au>2016-07-15 13:49:44 +1000
committerColin Watson <cjwatson@debian.org>2016-07-22 13:59:49 +0100
commitdde63f7f998ac3812a26bbb2c1b2947f24fcd060 (patch)
treebe4b41e362d31150cc84039aa6150ccb637d8107
parente5ef9d3942cebda819a6fd81647b51c8d87d23df (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.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);