From 40cf6b0c80f89a3b71acae8f17900b15fe00215f Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 7 Nov 2016 11:19:05 -0700 Subject: [PATCH 1/4] Updating KEYS.txt with email address --- KEYS.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/KEYS.txt b/KEYS.txt index 421c2b3e0f0..a902b2e94a1 100644 --- a/KEYS.txt +++ b/KEYS.txt @@ -1,5 +1,6 @@ # GPG Release Key Fingerprints Jan Bartel AED5 EE6C 45D0 FE8D 5D1B 164F 27DE D4BF 6216 DB Simone Bordet 8B09 6546 B1A8 F026 56B1 5D3B 1677 D141 BCF3 58 -Joakim Erdfelt BFBB 21C2 46D7 7768 3628 7A48 A04E 0C74 ABB3 5F +Joakim Erdfelt BFBB 21C2 46D7 7768 3628 7A48 A04E 0C74 ABB3 5FEA +Joakim Erdfelt B59B 67FD 7904 9843 67F9 3180 0818 D9D6 8FB6 7BAC Jesse McConnell 2A68 4B57 436A 81FA 8706 B53C 61C3 351A 438A 3B7D From 6ae72e6f7d912d713cd5f3e4e24dc4c48ad00df3 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 7 Nov 2016 19:19:38 +0100 Subject: [PATCH 2/4] Code cleanups. --- .../authentication/DigestAuthenticator.java | 159 +++++++----------- 1 file changed, 65 insertions(+), 94 deletions(-) diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java index 9359da10a0a..088d2e5675f 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java @@ -50,8 +50,6 @@ import org.eclipse.jetty.util.security.Constraint; import org.eclipse.jetty.util.security.Credential; /** - * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ - * * The nonce max age in ms can be set with the {@link SecurityHandler#setInitParameter(String, String)} * using the name "maxNonceAge". The nonce max count can be set with {@link SecurityHandler#setInitParameter(String, String)} * using the name "maxNonceCount". When the age or count is exceeded, the nonce is considered stale. @@ -59,105 +57,58 @@ import org.eclipse.jetty.util.security.Credential; public class DigestAuthenticator extends LoginAuthenticator { private static final Logger LOG = Log.getLogger(DigestAuthenticator.class); - SecureRandom _random = new SecureRandom(); - private long _maxNonceAgeMs = 60*1000; - private int _maxNC=1024; - private ConcurrentMap _nonceMap = new ConcurrentHashMap(); - private Queue _nonceQueue = new ConcurrentLinkedQueue(); - private static class Nonce - { - final String _nonce; - final long _ts; - final BitSet _seen; - public Nonce(String nonce, long ts, int size) - { - _nonce=nonce; - _ts=ts; - _seen = new BitSet(size); - } + private final SecureRandom _random = new SecureRandom(); + private long _maxNonceAgeMs = 60 * 1000; + private int _maxNC = 1024; + private ConcurrentMap _nonceMap = new ConcurrentHashMap<>(); + private Queue _nonceQueue = new ConcurrentLinkedQueue<>(); - public boolean seen(int count) - { - synchronized (this) - { - if (count>=_seen.size()) - return true; - boolean s=_seen.get(count); - _seen.set(count); - return s; - } - } - } - - /* ------------------------------------------------------------ */ - public DigestAuthenticator() - { - super(); - } - - /* ------------------------------------------------------------ */ - /** - * @see org.eclipse.jetty.security.authentication.LoginAuthenticator#setConfiguration(org.eclipse.jetty.security.Authenticator.AuthConfiguration) - */ @Override public void setConfiguration(AuthConfiguration configuration) { super.setConfiguration(configuration); - String mna=configuration.getInitParameter("maxNonceAge"); - if (mna!=null) - { - _maxNonceAgeMs=Long.valueOf(mna); - } - String mnc=configuration.getInitParameter("maxNonceCount"); - if (mnc!=null) - { - _maxNC=Integer.valueOf(mnc); - } + String mna = configuration.getInitParameter("maxNonceAge"); + if (mna != null) + setMaxNonceAge(Long.valueOf(mna)); + String mnc = configuration.getInitParameter("maxNonceCount"); + if (mnc != null) + setMaxNonceCount(Integer.valueOf(mnc)); } - /* ------------------------------------------------------------ */ public int getMaxNonceCount() { return _maxNC; } - /* ------------------------------------------------------------ */ public void setMaxNonceCount(int maxNC) { _maxNC = maxNC; } - /* ------------------------------------------------------------ */ public long getMaxNonceAge() { return _maxNonceAgeMs; } - /* ------------------------------------------------------------ */ - public synchronized void setMaxNonceAge(long maxNonceAgeInMillis) + public void setMaxNonceAge(long maxNonceAgeInMillis) { _maxNonceAgeMs = maxNonceAgeInMillis; } - /* ------------------------------------------------------------ */ @Override public String getAuthMethod() { return Constraint.__DIGEST_AUTH; } - /* ------------------------------------------------------------ */ @Override public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, User validatedUser) throws ServerAuthException { return true; } - - - /* ------------------------------------------------------------ */ @Override public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException { @@ -217,20 +168,20 @@ public class DigestAuthenticator extends LoginAuthenticator digest.uri = tok; else if ("response".equalsIgnoreCase(name)) digest.response = tok; - name=null; + name = null; } } } - int n = checkNonce(digest,(Request)request); + int n = checkNonce(digest, (Request)request); if (n > 0) { //UserIdentity user = _loginService.login(digest.username,digest); UserIdentity user = login(digest.username, digest, req); - if (user!=null) + if (user != null) { - return new UserAuthentication(getAuthMethod(),user); + return new UserAuthentication(getAuthMethod(), user); } } else if (n == 0) @@ -261,10 +212,8 @@ public class DigestAuthenticator extends LoginAuthenticator { throw new ServerAuthException(e); } - } - /* ------------------------------------------------------------ */ public String newNonce(Request request) { Nonce nonce; @@ -274,43 +223,42 @@ public class DigestAuthenticator extends LoginAuthenticator byte[] nounce = new byte[24]; _random.nextBytes(nounce); - nonce = new Nonce(new String(B64Code.encode(nounce)),request.getTimeStamp(),_maxNC); + nonce = new Nonce(new String(B64Code.encode(nounce)), request.getTimeStamp(), getMaxNonceCount()); } - while (_nonceMap.putIfAbsent(nonce._nonce,nonce)!=null); + while (_nonceMap.putIfAbsent(nonce._nonce, nonce) != null); _nonceQueue.add(nonce); return nonce._nonce; } /** - * @param nstring nonce to check - * @param request + * @param digest the digest data to check + * @param request the request object * @return -1 for a bad nonce, 0 for a stale none, 1 for a good nonce */ - /* ------------------------------------------------------------ */ private int checkNonce(Digest digest, Request request) { // firstly let's expire old nonces - long expired = request.getTimeStamp()-_maxNonceAgeMs; - Nonce nonce=_nonceQueue.peek(); - while (nonce!=null && nonce._ts=_maxNC) + long count = Long.parseLong(digest.nc, 16); + if (count >= _maxNC) return 0; - + if (nonce.seen((int)count)) return -1; @@ -323,9 +271,32 @@ public class DigestAuthenticator extends LoginAuthenticator return -1; } - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ + private static class Nonce + { + final String _nonce; + final long _ts; + final BitSet _seen; + + public Nonce(String nonce, long ts, int size) + { + _nonce = nonce; + _ts = ts; + _seen = new BitSet(size); + } + + public boolean seen(int count) + { + synchronized (this) + { + if (count >= _seen.size()) + return true; + boolean s = _seen.get(count); + _seen.set(count); + return s; + } + } + } + private static class Digest extends Credential { private static final long serialVersionUID = -2484639019549527724L; @@ -350,8 +321,8 @@ public class DigestAuthenticator extends LoginAuthenticator public boolean check(Object credentials) { if (credentials instanceof char[]) - credentials=new String((char[])credentials); - String password = (credentials instanceof String) ? (String) credentials : credentials.toString(); + credentials = new String((char[])credentials); + String password = (credentials instanceof String) ? (String)credentials : credentials.toString(); try { @@ -362,22 +333,22 @@ public class DigestAuthenticator extends LoginAuthenticator // Credentials are already a MD5 digest - assume it's in // form user:realm:password (we have no way to know since // it's a digest, alright?) - ha1 = ((Credential.MD5) credentials).getDigest(); + ha1 = ((Credential.MD5)credentials).getDigest(); } else { // calc A1 digest md.update(username.getBytes(StandardCharsets.ISO_8859_1)); - md.update((byte) ':'); + md.update((byte)':'); md.update(realm.getBytes(StandardCharsets.ISO_8859_1)); - md.update((byte) ':'); + md.update((byte)':'); md.update(password.getBytes(StandardCharsets.ISO_8859_1)); ha1 = md.digest(); } // calc A2 digest md.reset(); md.update(method.getBytes(StandardCharsets.ISO_8859_1)); - md.update((byte) ':'); + md.update((byte)':'); md.update(uri.getBytes(StandardCharsets.ISO_8859_1)); byte[] ha2 = md.digest(); @@ -389,15 +360,15 @@ public class DigestAuthenticator extends LoginAuthenticator // ) > <"> md.update(TypeUtil.toString(ha1, 16).getBytes(StandardCharsets.ISO_8859_1)); - md.update((byte) ':'); + md.update((byte)':'); md.update(nonce.getBytes(StandardCharsets.ISO_8859_1)); - md.update((byte) ':'); + md.update((byte)':'); md.update(nc.getBytes(StandardCharsets.ISO_8859_1)); - md.update((byte) ':'); + md.update((byte)':'); md.update(cnonce.getBytes(StandardCharsets.ISO_8859_1)); - md.update((byte) ':'); + md.update((byte)':'); md.update(qop.getBytes(StandardCharsets.ISO_8859_1)); - md.update((byte) ':'); + md.update((byte)':'); md.update(TypeUtil.toString(ha2, 16).getBytes(StandardCharsets.ISO_8859_1)); byte[] digest = md.digest(); From 42e865227d2e3121c91ae77aa3a59047ea67dfd1 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 7 Nov 2016 19:21:06 +0100 Subject: [PATCH 3/4] Fixes #1081 - DigestAuthenticator does not check the realm sent by the client. --- .../security/authentication/DigestAuthenticator.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java index 088d2e5675f..869cb1772eb 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java @@ -23,6 +23,7 @@ import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.SecureRandom; import java.util.BitSet; +import java.util.Objects; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; @@ -214,6 +215,15 @@ public class DigestAuthenticator extends LoginAuthenticator } } + @Override + public UserIdentity login(String username, Object credentials, ServletRequest request) + { + Digest digest = (Digest)credentials; + if (!Objects.equals(digest.realm, _loginService.getName())) + return null; + return super.login(username, credentials, request); + } + public String newNonce(Request request) { Nonce nonce; From f82aa033002885d6b0d8a2d05e113ebd8b15bf4e Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 7 Nov 2016 19:24:38 +0100 Subject: [PATCH 4/4] Fixes #1078 - DigestAuthentication should use realm from server, even if unknown in advance. --- .../eclipse/jetty/client/util/DigestAuthentication.java | 5 ++++- .../jetty/client/HttpClientAuthenticationTest.java | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java index ab8916a4cc6..83e098f6327 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java @@ -99,7 +99,10 @@ public class DigestAuthentication extends AbstractAuthentication clientQOP = "auth-int"; } - return new DigestResult(headerInfo.getHeader(), response.getContent(), getRealm(), user, password, algorithm, nonce, clientQOP, opaque); + String realm = getRealm(); + if (ANY_REALM.equals(realm)) + realm = headerInfo.getRealm(); + return new DigestResult(headerInfo.getHeader(), response.getContent(), realm, user, password, algorithm, nonce, clientQOP, opaque); } private Map parseParameters(String wwwAuthenticate) diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java index a44a035f2bb..01820b32b04 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java @@ -135,6 +135,14 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest test_Authentication(new DigestAuthentication(uri, realm, "digest", "digest")); } + @Test + public void test_DigestAnyRealm() throws Exception + { + startDigest(new EmptyServerHandler()); + URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort()); + test_Authentication(new DigestAuthentication(uri, Authentication.ANY_REALM, "digest", "digest")); + } + private void test_Authentication(Authentication authentication) throws Exception { AuthenticationStore authenticationStore = client.getAuthenticationStore();