From 57bd9f3bced17a05b837d1285dcef20eb671ee94 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Tue, 20 Sep 2011 17:04:01 +1000 Subject: [PATCH] 336443 Check nonce count is increasing --- .../jetty/embedded/SecuredHelloHandler.java | 5 +- .../authentication/DigestAuthenticator.java | 164 +++++++++--------- 2 files changed, 83 insertions(+), 86 deletions(-) diff --git a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java index 7224824cb3e..5d2547e3f1b 100644 --- a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java +++ b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java @@ -23,13 +23,14 @@ import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.security.LoginService; import org.eclipse.jetty.security.authentication.BasicAuthenticator; +import org.eclipse.jetty.security.authentication.DigestAuthenticator; import org.eclipse.jetty.server.Server; public class SecuredHelloHandler { public static void main(String[] args) throws Exception { - Server server = new Server(0); + Server server = new Server(8080); LoginService loginService = new HashLoginService("MyRealm","src/test/resources/realm.properties"); server.addBean(loginService); @@ -51,7 +52,7 @@ public class SecuredHelloHandler knownRoles.add("admin"); security.setConstraintMappings(Collections.singletonList(mapping), knownRoles); - security.setAuthenticator(new BasicAuthenticator()); + security.setAuthenticator(new DigestAuthenticator()); security.setLoginService(loginService); security.setStrict(false); 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 7fa563a8502..ee9f5625aeb 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 @@ -15,6 +15,12 @@ package org.eclipse.jetty.security.authentication; import java.io.IOException; import java.security.MessageDigest; +import java.security.SecureRandom; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; @@ -42,17 +48,29 @@ import org.eclipse.jetty.util.log.Logger; /** * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ * - * The nonce max age can be set with the {@link SecurityHandler#setInitParameter(String, String)} + * The nonce max age in ms can be set with the {@link SecurityHandler#setInitParameter(String, String)} * using the name "maxNonceAge" */ public class DigestAuthenticator extends LoginAuthenticator { private static final Logger LOG = Log.getLogger(DigestAuthenticator.class); + SecureRandom _random = new SecureRandom(); + private long _maxNonceAgeMs = 60*1000; + private ConcurrentMap _nonceCount = new ConcurrentHashMap(); + private Queue _nonceQueue = new ConcurrentLinkedQueue(); + private static class Nonce + { + final String _nonce; + final long _ts; + AtomicInteger _nc=new AtomicInteger(); + public Nonce(String nonce, long ts) + { + _nonce=nonce; + _ts=ts; + } + } - protected long _maxNonceAge = 0; - protected long _nonceSecret = this.hashCode() ^ System.currentTimeMillis(); - protected boolean _useStale = false; - + /* ------------------------------------------------------------ */ public DigestAuthenticator() { super(); @@ -69,19 +87,22 @@ public class DigestAuthenticator extends LoginAuthenticator String mna=configuration.getInitParameter("maxNonceAge"); if (mna!=null) - _maxNonceAge=Long.valueOf(mna); + _maxNonceAgeMs=Long.valueOf(mna); } + /* ------------------------------------------------------------ */ public String getAuthMethod() { return Constraint.__DIGEST_AUTH; } - + + /* ------------------------------------------------------------ */ public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, User validatedUser) throws ServerAuthException { return true; } + /* ------------------------------------------------------------ */ public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException { if (!mandatory) @@ -144,7 +165,7 @@ public class DigestAuthenticator extends LoginAuthenticator } } - int n = checkNonce(digest.nonce, (Request)request); + int n = checkNonce(digest,(Request)request); if (n > 0) { @@ -171,7 +192,7 @@ public class DigestAuthenticator extends LoginAuthenticator + "\", nonce=\"" + newNonce((Request)request) + "\", algorithm=MD5, qop=\"auth\"" - + (_useStale ? (" stale=" + stale) : "")); + + " stale=" + stale); response.sendError(HttpServletResponse.SC_UNAUTHORIZED); return Authentication.SEND_CONTINUE; @@ -186,87 +207,59 @@ public class DigestAuthenticator extends LoginAuthenticator } + /* ------------------------------------------------------------ */ public String newNonce(Request request) { - long ts=request.getTimeStamp(); - long sk = _nonceSecret; - - byte[] nounce = new byte[24]; - for (int i = 0; i < 8; i++) + Nonce nonce; + + do { - nounce[i] = (byte) (ts & 0xff); - ts = ts >> 8; - nounce[8 + i] = (byte) (sk & 0xff); - sk = sk >> 8; - } + byte[] nounce = new byte[24]; + _random.nextBytes(nounce); - byte[] hash = null; - try - { - MessageDigest md = MessageDigest.getInstance("MD5"); - md.reset(); - md.update(nounce, 0, 16); - hash = md.digest(); + nonce = new Nonce(new String(B64Code.encode(nounce)),request.getTimeStamp()); } - catch (Exception e) - { - LOG.warn(e); - } - - for (int i = 0; i < hash.length; i++) - { - nounce[8 + i] = hash[i]; - if (i == 23) break; - } - - return new String(B64Code.encode(nounce)); + while (_nonceCount.putIfAbsent(nonce._nonce,nonce)!=null); + _nonceQueue.add(nonce); + + return nonce._nonce; } /** - * @param nonce nonce to check + * @param nstring nonce to check * @param request * @return -1 for a bad nonce, 0 for a stale none, 1 for a good nonce */ /* ------------------------------------------------------------ */ - private int checkNonce(String nonce, Request request) + 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> 8; - ts = (ts << 8) + (0xff & (long) n[7 - i]); - } - - long age = request.getTimeStamp() - ts; - if (LOG.isDebugEnabled()) LOG.debug("age=" + age); - - byte[] hash = null; - try - { - MessageDigest md = MessageDigest.getInstance("MD5"); - md.reset(); - md.update(n2, 0, 16); - hash = md.digest(); - } - catch (Exception e) - { - LOG.warn(e); - } - - for (int i = 0; i < 16; i++) - if (n[i + 8] != hash[i]) return -1; - - if (_maxNonceAge > 0 && (age < 0 || age > _maxNonceAge)) return 0; // stale - + nonce = _nonceCount.get(digest.nonce); + if (nonce==null) + return 0; + + long count = Long.parseLong(digest.nc,16); + if (count>Integer.MAX_VALUE) + return 0; + int old=nonce._nc.get(); + while (!nonce._nc.compareAndSet(old,(int)count)) + old=nonce._nc.get(); + if (count<=old) + return -1; + return 1; } catch (Exception e) @@ -276,18 +269,21 @@ public class DigestAuthenticator extends LoginAuthenticator return -1; } + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ private static class Digest extends Credential { private static final long serialVersionUID = -2484639019549527724L; - String method = null; - String username = null; - String realm = null; - String nonce = null; - String nc = null; - String cnonce = null; - String qop = null; - String uri = null; - String response = null; + String method = ""; + String username = ""; + String realm = ""; + String nonce = ""; + String nc = ""; + String cnonce = ""; + String qop = ""; + String uri = ""; + String response = ""; /* ------------------------------------------------------------ */ Digest(String m)