From: Markus Koschany <apo@debian.org>
Date: Sun, 9 Jul 2017 22:43:52 +0200
Subject: CVE-2017-9735

Possible Timing Attack.

Bug-Upstream: https://github.com/eclipse/jetty.project/issues/1556
Bug-Debian: https://bugs.debian.org/864898
Origin: https://github.com/eclipse/jetty.project/commit/042f325f1cd6e7891d72c7e668f5947b5457dc02
Origin: https://github.com/eclipse/jetty.project/commit/f3751d70787fd8ab93932a51c60514c2eb37cb58
Origin: https://github.com/eclipse/jetty.project/commit/2baa1abe4b1c380a30deacca1ed367466a1a62ea
---
 .../org/mortbay/jetty/security/Credential.java     | 67 +++++++++++++++++-----
 .../jetty/security/DigestAuthenticator.java        |  2 +-
 .../java/org/mortbay/jetty/security/Password.java  |  9 +--
 3 files changed, 57 insertions(+), 21 deletions(-)

diff --git a/modules/jetty/src/main/java/org/mortbay/jetty/security/Credential.java b/modules/jetty/src/main/java/org/mortbay/jetty/security/Credential.java
index eb13883..1455cdb 100644
--- a/modules/jetty/src/main/java/org/mortbay/jetty/security/Credential.java
+++ b/modules/jetty/src/main/java/org/mortbay/jetty/security/Credential.java
@@ -64,6 +64,53 @@ public abstract class Credential
         
         return new Password(credential);
     }
+    /**
+     * <p>Utility method that replaces String.equals() to avoid timing attacks.</p>
+     *
+     * @param s1 the first string to compare
+     * @param s2 the second string to compare
+     * @return whether the two strings are equal
+     */
+    protected static boolean stringEquals(String s1, String s2)
+    {
+        if (s1 == s2)
+            return true;
+        if (s1 == null || s2 == null)
+            return false;
+        boolean result = true;
+        int l1 = s1.length();
+        int l2 = s2.length();
+        if (l1 != l2)
+            result = false;
+        int l = Math.min(l1, l2);
+        for (int i = 0; i < l; ++i)
+            result &= s1.charAt(i) == s2.charAt(i);
+        return result;
+    }
+
+    /**
+     * <p>Utility method that replaces Arrays.equals() to avoid timing attacks.</p>
+     *
+     * @param b1 the first byte array to compare
+     * @param b2 the second byte array to compare
+     * @return whether the two byte arrays are equal
+     */
+    protected static boolean byteEquals(byte[] b1, byte[] b2)
+    {
+        if (b1 == b2)
+            return true;
+        if (b1 == null || b2 == null)
+            return false;
+        boolean result = true;
+        int l1 = b1.length;
+        int l2 = b2.length;
+        if (l1 != l2)
+            result = false;
+        int l = Math.min(l1, l2);
+        for (int i = 0; i < l; ++i)
+            result &= b1[i] == b2[i];
+        return result;
+    }
 
 
     /* ------------------------------------------------------------ */
@@ -87,8 +134,8 @@ public abstract class Credential
                 !(credentials instanceof Password))
                 Log.warn("Can't check "+credentials.getClass()+" against CRYPT");
             
-            String passwd = credentials.toString();
-            return _cooked.equals(UnixCrypt.crypt(passwd,_cooked));
+
+            return stringEquals(_cooked, UnixCrypt.crypt(credentials.toString(), _cooked));
         }
 
         public static String crypt(String user,String pw)
@@ -141,22 +188,14 @@ public abstract class Credential
                         __md.update(credentials.toString().getBytes(StringUtil.__ISO_8859_1));
                         digest=__md.digest();
                     }
-                    if (digest==null || digest.length!=_digest.length)
-                        return false;
-                    for (int i=0;i<digest.length;i++)
-                        if (digest[i]!=_digest[i])
-                            return false;
-                    return true;
+
+                    return byteEquals(_digest, digest);
                 }
                 else if (credentials instanceof MD5)
                 {
+
                     MD5 md5 = (MD5)credentials;
-                    if (_digest.length!=md5._digest.length)
-                        return false;
-                    for (int i=0;i<_digest.length;i++)
-                        if (_digest[i]!=md5._digest[i])
-                            return false;
-                    return true;
+                    return byteEquals(_digest, md5._digest);
                 }
                 else if(credentials instanceof Credential)
                 {
diff --git a/modules/jetty/src/main/java/org/mortbay/jetty/security/DigestAuthenticator.java b/modules/jetty/src/main/java/org/mortbay/jetty/security/DigestAuthenticator.java
index 4edd147..7515af2 100644
--- a/modules/jetty/src/main/java/org/mortbay/jetty/security/DigestAuthenticator.java
+++ b/modules/jetty/src/main/java/org/mortbay/jetty/security/DigestAuthenticator.java
@@ -332,7 +332,7 @@ public class DigestAuthenticator implements Authenticator
                 byte[] digest=md.digest();
                 
                 // check digest
-                return (TypeUtil.toString(digest,16).equalsIgnoreCase(response));
+                return stringEquals(TypeUtil.toString(digest, 16).toLowerCase(), response == null ? null : response.toLowerCase());
             }
             catch (Exception e)
             {Log.warn(e);}
diff --git a/modules/jetty/src/main/java/org/mortbay/jetty/security/Password.java b/modules/jetty/src/main/java/org/mortbay/jetty/security/Password.java
index 755e4c7..4b1f13c 100644
--- a/modules/jetty/src/main/java/org/mortbay/jetty/security/Password.java
+++ b/modules/jetty/src/main/java/org/mortbay/jetty/security/Password.java
@@ -85,7 +85,7 @@ public class Password extends Credential
             return credentials.equals(_pw);
         
         if (credentials instanceof String)
-            return credentials.equals(_pw);
+            return stringEquals(_pw, (String)credentials);
         
         if (credentials instanceof Credential)
             return ((Credential)credentials).check(_pw);
@@ -103,13 +103,10 @@ public class Password extends Credential
             return false;
 
         if (o instanceof Password)
-        {
-            Password p=(Password)o;
-            return p._pw == _pw || (null != _pw && _pw.equals(p._pw));
-        }
+            return stringEquals(_pw, ((Password)o)._pw);
         
         if (o instanceof String)
-            return o.equals(_pw);
+            return stringEquals(_pw, (String)o);
             
         return false;
     }
