103928aee4356845252ac6b662d5c72c29903813eJake Slack// 203928aee4356845252ac6b662d5c72c29903813eJake Slack// ======================================================================== 303928aee4356845252ac6b662d5c72c29903813eJake Slack// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. 403928aee4356845252ac6b662d5c72c29903813eJake Slack// ------------------------------------------------------------------------ 503928aee4356845252ac6b662d5c72c29903813eJake Slack// All rights reserved. This program and the accompanying materials 603928aee4356845252ac6b662d5c72c29903813eJake Slack// are made available under the terms of the Eclipse Public License v1.0 703928aee4356845252ac6b662d5c72c29903813eJake Slack// and Apache License v2.0 which accompanies this distribution. 803928aee4356845252ac6b662d5c72c29903813eJake Slack// 903928aee4356845252ac6b662d5c72c29903813eJake Slack// The Eclipse Public License is available at 1003928aee4356845252ac6b662d5c72c29903813eJake Slack// http://www.eclipse.org/legal/epl-v10.html 1103928aee4356845252ac6b662d5c72c29903813eJake Slack// 1203928aee4356845252ac6b662d5c72c29903813eJake Slack// The Apache License v2.0 is available at 1303928aee4356845252ac6b662d5c72c29903813eJake Slack// http://www.opensource.org/licenses/apache2.0.php 1403928aee4356845252ac6b662d5c72c29903813eJake Slack// 1503928aee4356845252ac6b662d5c72c29903813eJake Slack// You may elect to redistribute this code under either of these licenses. 1603928aee4356845252ac6b662d5c72c29903813eJake Slack// ======================================================================== 1703928aee4356845252ac6b662d5c72c29903813eJake Slack// 1803928aee4356845252ac6b662d5c72c29903813eJake Slack 1903928aee4356845252ac6b662d5c72c29903813eJake Slackpackage org.eclipse.jetty.security.authentication; 2003928aee4356845252ac6b662d5c72c29903813eJake Slack 2103928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.IOException; 2203928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.security.MessageDigest; 2303928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.security.SecureRandom; 2403928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.BitSet; 2503928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Queue; 2603928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.concurrent.ConcurrentHashMap; 2703928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.concurrent.ConcurrentLinkedQueue; 2803928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.concurrent.ConcurrentMap; 2903928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.concurrent.atomic.AtomicInteger; 3003928aee4356845252ac6b662d5c72c29903813eJake Slack 3103928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.servlet.ServletRequest; 3203928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.servlet.ServletResponse; 3303928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.servlet.http.HttpServletRequest; 3403928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.servlet.http.HttpServletResponse; 3503928aee4356845252ac6b662d5c72c29903813eJake Slack 3603928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.http.HttpHeaders; 3703928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.security.SecurityHandler; 3803928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.security.ServerAuthException; 3903928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.security.UserAuthentication; 4003928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.server.Authentication; 4103928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.server.Authentication.User; 4203928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.server.Request; 4303928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.server.UserIdentity; 4403928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.B64Code; 4503928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.QuotedStringTokenizer; 4603928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.StringUtil; 4703928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.TypeUtil; 4803928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Log; 4903928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Logger; 5003928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.security.Constraint; 5103928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.security.Credential; 5203928aee4356845252ac6b662d5c72c29903813eJake Slack 5303928aee4356845252ac6b662d5c72c29903813eJake Slack/** 5403928aee4356845252ac6b662d5c72c29903813eJake Slack * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ 5503928aee4356845252ac6b662d5c72c29903813eJake Slack * 5603928aee4356845252ac6b662d5c72c29903813eJake Slack * The nonce max age in ms can be set with the {@link SecurityHandler#setInitParameter(String, String)} 5703928aee4356845252ac6b662d5c72c29903813eJake Slack * using the name "maxNonceAge" 5803928aee4356845252ac6b662d5c72c29903813eJake Slack */ 5903928aee4356845252ac6b662d5c72c29903813eJake Slackpublic class DigestAuthenticator extends LoginAuthenticator 6003928aee4356845252ac6b662d5c72c29903813eJake Slack{ 6103928aee4356845252ac6b662d5c72c29903813eJake Slack private static final Logger LOG = Log.getLogger(DigestAuthenticator.class); 6203928aee4356845252ac6b662d5c72c29903813eJake Slack SecureRandom _random = new SecureRandom(); 6303928aee4356845252ac6b662d5c72c29903813eJake Slack private long _maxNonceAgeMs = 60*1000; 6403928aee4356845252ac6b662d5c72c29903813eJake Slack private int _maxNC=1024; 6503928aee4356845252ac6b662d5c72c29903813eJake Slack private ConcurrentMap<String, Nonce> _nonceMap = new ConcurrentHashMap<String, Nonce>(); 6603928aee4356845252ac6b662d5c72c29903813eJake Slack private Queue<Nonce> _nonceQueue = new ConcurrentLinkedQueue<Nonce>(); 6703928aee4356845252ac6b662d5c72c29903813eJake Slack private static class Nonce 6803928aee4356845252ac6b662d5c72c29903813eJake Slack { 6903928aee4356845252ac6b662d5c72c29903813eJake Slack final String _nonce; 7003928aee4356845252ac6b662d5c72c29903813eJake Slack final long _ts; 7103928aee4356845252ac6b662d5c72c29903813eJake Slack final BitSet _seen; 7203928aee4356845252ac6b662d5c72c29903813eJake Slack 7303928aee4356845252ac6b662d5c72c29903813eJake Slack public Nonce(String nonce, long ts, int size) 7403928aee4356845252ac6b662d5c72c29903813eJake Slack { 7503928aee4356845252ac6b662d5c72c29903813eJake Slack _nonce=nonce; 7603928aee4356845252ac6b662d5c72c29903813eJake Slack _ts=ts; 7703928aee4356845252ac6b662d5c72c29903813eJake Slack _seen = new BitSet(size); 7803928aee4356845252ac6b662d5c72c29903813eJake Slack } 7903928aee4356845252ac6b662d5c72c29903813eJake Slack 8003928aee4356845252ac6b662d5c72c29903813eJake Slack public boolean seen(int count) 8103928aee4356845252ac6b662d5c72c29903813eJake Slack { 8203928aee4356845252ac6b662d5c72c29903813eJake Slack synchronized (this) 8303928aee4356845252ac6b662d5c72c29903813eJake Slack { 8403928aee4356845252ac6b662d5c72c29903813eJake Slack if (count>=_seen.size()) 8503928aee4356845252ac6b662d5c72c29903813eJake Slack return true; 8603928aee4356845252ac6b662d5c72c29903813eJake Slack boolean s=_seen.get(count); 8703928aee4356845252ac6b662d5c72c29903813eJake Slack _seen.set(count); 8803928aee4356845252ac6b662d5c72c29903813eJake Slack return s; 8903928aee4356845252ac6b662d5c72c29903813eJake Slack } 9003928aee4356845252ac6b662d5c72c29903813eJake Slack } 9103928aee4356845252ac6b662d5c72c29903813eJake Slack } 9203928aee4356845252ac6b662d5c72c29903813eJake Slack 9303928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 9403928aee4356845252ac6b662d5c72c29903813eJake Slack public DigestAuthenticator() 9503928aee4356845252ac6b662d5c72c29903813eJake Slack { 9603928aee4356845252ac6b662d5c72c29903813eJake Slack super(); 9703928aee4356845252ac6b662d5c72c29903813eJake Slack } 9803928aee4356845252ac6b662d5c72c29903813eJake Slack 9903928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 10003928aee4356845252ac6b662d5c72c29903813eJake Slack /** 10103928aee4356845252ac6b662d5c72c29903813eJake Slack * @see org.eclipse.jetty.security.authentication.LoginAuthenticator#setConfiguration(org.eclipse.jetty.security.Authenticator.AuthConfiguration) 10203928aee4356845252ac6b662d5c72c29903813eJake Slack */ 10303928aee4356845252ac6b662d5c72c29903813eJake Slack @Override 10403928aee4356845252ac6b662d5c72c29903813eJake Slack public void setConfiguration(AuthConfiguration configuration) 10503928aee4356845252ac6b662d5c72c29903813eJake Slack { 10603928aee4356845252ac6b662d5c72c29903813eJake Slack super.setConfiguration(configuration); 10703928aee4356845252ac6b662d5c72c29903813eJake Slack 10803928aee4356845252ac6b662d5c72c29903813eJake Slack String mna=configuration.getInitParameter("maxNonceAge"); 10903928aee4356845252ac6b662d5c72c29903813eJake Slack if (mna!=null) 11003928aee4356845252ac6b662d5c72c29903813eJake Slack { 11103928aee4356845252ac6b662d5c72c29903813eJake Slack _maxNonceAgeMs=Long.valueOf(mna); 11203928aee4356845252ac6b662d5c72c29903813eJake Slack } 11303928aee4356845252ac6b662d5c72c29903813eJake Slack } 11403928aee4356845252ac6b662d5c72c29903813eJake Slack 11503928aee4356845252ac6b662d5c72c29903813eJake Slack 11603928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 11703928aee4356845252ac6b662d5c72c29903813eJake Slack public int getMaxNonceCount() 11803928aee4356845252ac6b662d5c72c29903813eJake Slack { 11903928aee4356845252ac6b662d5c72c29903813eJake Slack return _maxNC; 12003928aee4356845252ac6b662d5c72c29903813eJake Slack } 12103928aee4356845252ac6b662d5c72c29903813eJake Slack 12203928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 12303928aee4356845252ac6b662d5c72c29903813eJake Slack public void setMaxNonceCount(int maxNC) 12403928aee4356845252ac6b662d5c72c29903813eJake Slack { 12503928aee4356845252ac6b662d5c72c29903813eJake Slack _maxNC = maxNC; 12603928aee4356845252ac6b662d5c72c29903813eJake Slack } 12703928aee4356845252ac6b662d5c72c29903813eJake Slack 12803928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 12903928aee4356845252ac6b662d5c72c29903813eJake Slack public void setMaxNonceAge(long maxNonceAgeInMillis) 13003928aee4356845252ac6b662d5c72c29903813eJake Slack { 13103928aee4356845252ac6b662d5c72c29903813eJake Slack _maxNonceAgeMs = maxNonceAgeInMillis; 13203928aee4356845252ac6b662d5c72c29903813eJake Slack } 13303928aee4356845252ac6b662d5c72c29903813eJake Slack 13403928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 13503928aee4356845252ac6b662d5c72c29903813eJake Slack public long getMaxNonceAge() 13603928aee4356845252ac6b662d5c72c29903813eJake Slack { 13703928aee4356845252ac6b662d5c72c29903813eJake Slack return _maxNonceAgeMs; 13803928aee4356845252ac6b662d5c72c29903813eJake Slack } 13903928aee4356845252ac6b662d5c72c29903813eJake Slack 14003928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 14103928aee4356845252ac6b662d5c72c29903813eJake Slack public String getAuthMethod() 14203928aee4356845252ac6b662d5c72c29903813eJake Slack { 14303928aee4356845252ac6b662d5c72c29903813eJake Slack return Constraint.__DIGEST_AUTH; 14403928aee4356845252ac6b662d5c72c29903813eJake Slack } 14503928aee4356845252ac6b662d5c72c29903813eJake Slack 14603928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 14703928aee4356845252ac6b662d5c72c29903813eJake Slack public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, User validatedUser) throws ServerAuthException 14803928aee4356845252ac6b662d5c72c29903813eJake Slack { 14903928aee4356845252ac6b662d5c72c29903813eJake Slack return true; 15003928aee4356845252ac6b662d5c72c29903813eJake Slack } 15103928aee4356845252ac6b662d5c72c29903813eJake Slack 15203928aee4356845252ac6b662d5c72c29903813eJake Slack 15303928aee4356845252ac6b662d5c72c29903813eJake Slack 15403928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 15503928aee4356845252ac6b662d5c72c29903813eJake Slack public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException 15603928aee4356845252ac6b662d5c72c29903813eJake Slack { 15703928aee4356845252ac6b662d5c72c29903813eJake Slack if (!mandatory) 15803928aee4356845252ac6b662d5c72c29903813eJake Slack return new DeferredAuthentication(this); 15903928aee4356845252ac6b662d5c72c29903813eJake Slack 16003928aee4356845252ac6b662d5c72c29903813eJake Slack HttpServletRequest request = (HttpServletRequest)req; 16103928aee4356845252ac6b662d5c72c29903813eJake Slack HttpServletResponse response = (HttpServletResponse)res; 16203928aee4356845252ac6b662d5c72c29903813eJake Slack String credentials = request.getHeader(HttpHeaders.AUTHORIZATION); 16303928aee4356845252ac6b662d5c72c29903813eJake Slack 16403928aee4356845252ac6b662d5c72c29903813eJake Slack try 16503928aee4356845252ac6b662d5c72c29903813eJake Slack { 16603928aee4356845252ac6b662d5c72c29903813eJake Slack boolean stale = false; 16703928aee4356845252ac6b662d5c72c29903813eJake Slack if (credentials != null) 16803928aee4356845252ac6b662d5c72c29903813eJake Slack { 16903928aee4356845252ac6b662d5c72c29903813eJake Slack if (LOG.isDebugEnabled()) 17003928aee4356845252ac6b662d5c72c29903813eJake Slack LOG.debug("Credentials: " + credentials); 17103928aee4356845252ac6b662d5c72c29903813eJake Slack QuotedStringTokenizer tokenizer = new QuotedStringTokenizer(credentials, "=, ", true, false); 17203928aee4356845252ac6b662d5c72c29903813eJake Slack final Digest digest = new Digest(request.getMethod()); 17303928aee4356845252ac6b662d5c72c29903813eJake Slack String last = null; 17403928aee4356845252ac6b662d5c72c29903813eJake Slack String name = null; 17503928aee4356845252ac6b662d5c72c29903813eJake Slack 17603928aee4356845252ac6b662d5c72c29903813eJake Slack while (tokenizer.hasMoreTokens()) 17703928aee4356845252ac6b662d5c72c29903813eJake Slack { 17803928aee4356845252ac6b662d5c72c29903813eJake Slack String tok = tokenizer.nextToken(); 17903928aee4356845252ac6b662d5c72c29903813eJake Slack char c = (tok.length() == 1) ? tok.charAt(0) : '\0'; 18003928aee4356845252ac6b662d5c72c29903813eJake Slack 18103928aee4356845252ac6b662d5c72c29903813eJake Slack switch (c) 18203928aee4356845252ac6b662d5c72c29903813eJake Slack { 18303928aee4356845252ac6b662d5c72c29903813eJake Slack case '=': 18403928aee4356845252ac6b662d5c72c29903813eJake Slack name = last; 18503928aee4356845252ac6b662d5c72c29903813eJake Slack last = tok; 18603928aee4356845252ac6b662d5c72c29903813eJake Slack break; 18703928aee4356845252ac6b662d5c72c29903813eJake Slack case ',': 18803928aee4356845252ac6b662d5c72c29903813eJake Slack name = null; 18903928aee4356845252ac6b662d5c72c29903813eJake Slack break; 19003928aee4356845252ac6b662d5c72c29903813eJake Slack case ' ': 19103928aee4356845252ac6b662d5c72c29903813eJake Slack break; 19203928aee4356845252ac6b662d5c72c29903813eJake Slack 19303928aee4356845252ac6b662d5c72c29903813eJake Slack default: 19403928aee4356845252ac6b662d5c72c29903813eJake Slack last = tok; 19503928aee4356845252ac6b662d5c72c29903813eJake Slack if (name != null) 19603928aee4356845252ac6b662d5c72c29903813eJake Slack { 19703928aee4356845252ac6b662d5c72c29903813eJake Slack if ("username".equalsIgnoreCase(name)) 19803928aee4356845252ac6b662d5c72c29903813eJake Slack digest.username = tok; 19903928aee4356845252ac6b662d5c72c29903813eJake Slack else if ("realm".equalsIgnoreCase(name)) 20003928aee4356845252ac6b662d5c72c29903813eJake Slack digest.realm = tok; 20103928aee4356845252ac6b662d5c72c29903813eJake Slack else if ("nonce".equalsIgnoreCase(name)) 20203928aee4356845252ac6b662d5c72c29903813eJake Slack digest.nonce = tok; 20303928aee4356845252ac6b662d5c72c29903813eJake Slack else if ("nc".equalsIgnoreCase(name)) 20403928aee4356845252ac6b662d5c72c29903813eJake Slack digest.nc = tok; 20503928aee4356845252ac6b662d5c72c29903813eJake Slack else if ("cnonce".equalsIgnoreCase(name)) 20603928aee4356845252ac6b662d5c72c29903813eJake Slack digest.cnonce = tok; 20703928aee4356845252ac6b662d5c72c29903813eJake Slack else if ("qop".equalsIgnoreCase(name)) 20803928aee4356845252ac6b662d5c72c29903813eJake Slack digest.qop = tok; 20903928aee4356845252ac6b662d5c72c29903813eJake Slack else if ("uri".equalsIgnoreCase(name)) 21003928aee4356845252ac6b662d5c72c29903813eJake Slack digest.uri = tok; 21103928aee4356845252ac6b662d5c72c29903813eJake Slack else if ("response".equalsIgnoreCase(name)) 21203928aee4356845252ac6b662d5c72c29903813eJake Slack digest.response = tok; 21303928aee4356845252ac6b662d5c72c29903813eJake Slack name=null; 21403928aee4356845252ac6b662d5c72c29903813eJake Slack } 21503928aee4356845252ac6b662d5c72c29903813eJake Slack } 21603928aee4356845252ac6b662d5c72c29903813eJake Slack } 21703928aee4356845252ac6b662d5c72c29903813eJake Slack 21803928aee4356845252ac6b662d5c72c29903813eJake Slack int n = checkNonce(digest,(Request)request); 21903928aee4356845252ac6b662d5c72c29903813eJake Slack 22003928aee4356845252ac6b662d5c72c29903813eJake Slack if (n > 0) 22103928aee4356845252ac6b662d5c72c29903813eJake Slack { 22203928aee4356845252ac6b662d5c72c29903813eJake Slack //UserIdentity user = _loginService.login(digest.username,digest); 22303928aee4356845252ac6b662d5c72c29903813eJake Slack UserIdentity user = login(digest.username, digest, req); 22403928aee4356845252ac6b662d5c72c29903813eJake Slack if (user!=null) 22503928aee4356845252ac6b662d5c72c29903813eJake Slack { 22603928aee4356845252ac6b662d5c72c29903813eJake Slack return new UserAuthentication(getAuthMethod(),user); 22703928aee4356845252ac6b662d5c72c29903813eJake Slack } 22803928aee4356845252ac6b662d5c72c29903813eJake Slack } 22903928aee4356845252ac6b662d5c72c29903813eJake Slack else if (n == 0) 23003928aee4356845252ac6b662d5c72c29903813eJake Slack stale = true; 23103928aee4356845252ac6b662d5c72c29903813eJake Slack 23203928aee4356845252ac6b662d5c72c29903813eJake Slack } 23303928aee4356845252ac6b662d5c72c29903813eJake Slack 23403928aee4356845252ac6b662d5c72c29903813eJake Slack if (!DeferredAuthentication.isDeferred(response)) 23503928aee4356845252ac6b662d5c72c29903813eJake Slack { 23603928aee4356845252ac6b662d5c72c29903813eJake Slack String domain = request.getContextPath(); 23703928aee4356845252ac6b662d5c72c29903813eJake Slack if (domain == null) 23803928aee4356845252ac6b662d5c72c29903813eJake Slack domain = "/"; 23903928aee4356845252ac6b662d5c72c29903813eJake Slack response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Digest realm=\"" + _loginService.getName() 24003928aee4356845252ac6b662d5c72c29903813eJake Slack + "\", domain=\"" 24103928aee4356845252ac6b662d5c72c29903813eJake Slack + domain 24203928aee4356845252ac6b662d5c72c29903813eJake Slack + "\", nonce=\"" 24303928aee4356845252ac6b662d5c72c29903813eJake Slack + newNonce((Request)request) 24403928aee4356845252ac6b662d5c72c29903813eJake Slack + "\", algorithm=MD5, qop=\"auth\"," 24503928aee4356845252ac6b662d5c72c29903813eJake Slack + " stale=" + stale); 24603928aee4356845252ac6b662d5c72c29903813eJake Slack response.sendError(HttpServletResponse.SC_UNAUTHORIZED); 24703928aee4356845252ac6b662d5c72c29903813eJake Slack 24803928aee4356845252ac6b662d5c72c29903813eJake Slack return Authentication.SEND_CONTINUE; 24903928aee4356845252ac6b662d5c72c29903813eJake Slack } 25003928aee4356845252ac6b662d5c72c29903813eJake Slack 25103928aee4356845252ac6b662d5c72c29903813eJake Slack return Authentication.UNAUTHENTICATED; 25203928aee4356845252ac6b662d5c72c29903813eJake Slack } 25303928aee4356845252ac6b662d5c72c29903813eJake Slack catch (IOException e) 25403928aee4356845252ac6b662d5c72c29903813eJake Slack { 25503928aee4356845252ac6b662d5c72c29903813eJake Slack throw new ServerAuthException(e); 25603928aee4356845252ac6b662d5c72c29903813eJake Slack } 25703928aee4356845252ac6b662d5c72c29903813eJake Slack 25803928aee4356845252ac6b662d5c72c29903813eJake Slack } 25903928aee4356845252ac6b662d5c72c29903813eJake Slack 26003928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 26103928aee4356845252ac6b662d5c72c29903813eJake Slack public String newNonce(Request request) 26203928aee4356845252ac6b662d5c72c29903813eJake Slack { 26303928aee4356845252ac6b662d5c72c29903813eJake Slack Nonce nonce; 26403928aee4356845252ac6b662d5c72c29903813eJake Slack 26503928aee4356845252ac6b662d5c72c29903813eJake Slack do 26603928aee4356845252ac6b662d5c72c29903813eJake Slack { 26703928aee4356845252ac6b662d5c72c29903813eJake Slack byte[] nounce = new byte[24]; 26803928aee4356845252ac6b662d5c72c29903813eJake Slack _random.nextBytes(nounce); 26903928aee4356845252ac6b662d5c72c29903813eJake Slack 27003928aee4356845252ac6b662d5c72c29903813eJake Slack nonce = new Nonce(new String(B64Code.encode(nounce)),request.getTimeStamp(),_maxNC); 27103928aee4356845252ac6b662d5c72c29903813eJake Slack } 27203928aee4356845252ac6b662d5c72c29903813eJake Slack while (_nonceMap.putIfAbsent(nonce._nonce,nonce)!=null); 27303928aee4356845252ac6b662d5c72c29903813eJake Slack _nonceQueue.add(nonce); 27403928aee4356845252ac6b662d5c72c29903813eJake Slack 27503928aee4356845252ac6b662d5c72c29903813eJake Slack return nonce._nonce; 27603928aee4356845252ac6b662d5c72c29903813eJake Slack } 27703928aee4356845252ac6b662d5c72c29903813eJake Slack 27803928aee4356845252ac6b662d5c72c29903813eJake Slack /** 27903928aee4356845252ac6b662d5c72c29903813eJake Slack * @param nstring nonce to check 28003928aee4356845252ac6b662d5c72c29903813eJake Slack * @param request 28103928aee4356845252ac6b662d5c72c29903813eJake Slack * @return -1 for a bad nonce, 0 for a stale none, 1 for a good nonce 28203928aee4356845252ac6b662d5c72c29903813eJake Slack */ 28303928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 28403928aee4356845252ac6b662d5c72c29903813eJake Slack private int checkNonce(Digest digest, Request request) 28503928aee4356845252ac6b662d5c72c29903813eJake Slack { 28603928aee4356845252ac6b662d5c72c29903813eJake Slack // firstly let's expire old nonces 28703928aee4356845252ac6b662d5c72c29903813eJake Slack long expired = request.getTimeStamp()-_maxNonceAgeMs; 28803928aee4356845252ac6b662d5c72c29903813eJake Slack Nonce nonce=_nonceQueue.peek(); 28903928aee4356845252ac6b662d5c72c29903813eJake Slack while (nonce!=null && nonce._ts<expired) 29003928aee4356845252ac6b662d5c72c29903813eJake Slack { 29103928aee4356845252ac6b662d5c72c29903813eJake Slack _nonceQueue.remove(nonce); 29203928aee4356845252ac6b662d5c72c29903813eJake Slack _nonceMap.remove(nonce._nonce); 29303928aee4356845252ac6b662d5c72c29903813eJake Slack nonce=_nonceQueue.peek(); 29403928aee4356845252ac6b662d5c72c29903813eJake Slack } 29503928aee4356845252ac6b662d5c72c29903813eJake Slack 29603928aee4356845252ac6b662d5c72c29903813eJake Slack // Now check the requested nonce 29703928aee4356845252ac6b662d5c72c29903813eJake Slack try 29803928aee4356845252ac6b662d5c72c29903813eJake Slack { 29903928aee4356845252ac6b662d5c72c29903813eJake Slack nonce = _nonceMap.get(digest.nonce); 30003928aee4356845252ac6b662d5c72c29903813eJake Slack if (nonce==null) 30103928aee4356845252ac6b662d5c72c29903813eJake Slack return 0; 30203928aee4356845252ac6b662d5c72c29903813eJake Slack 30303928aee4356845252ac6b662d5c72c29903813eJake Slack long count = Long.parseLong(digest.nc,16); 30403928aee4356845252ac6b662d5c72c29903813eJake Slack if (count>=_maxNC) 30503928aee4356845252ac6b662d5c72c29903813eJake Slack return 0; 30603928aee4356845252ac6b662d5c72c29903813eJake Slack if (nonce.seen((int)count)) 30703928aee4356845252ac6b662d5c72c29903813eJake Slack return -1; 30803928aee4356845252ac6b662d5c72c29903813eJake Slack return 1; 30903928aee4356845252ac6b662d5c72c29903813eJake Slack } 31003928aee4356845252ac6b662d5c72c29903813eJake Slack catch (Exception e) 31103928aee4356845252ac6b662d5c72c29903813eJake Slack { 31203928aee4356845252ac6b662d5c72c29903813eJake Slack LOG.ignore(e); 31303928aee4356845252ac6b662d5c72c29903813eJake Slack } 31403928aee4356845252ac6b662d5c72c29903813eJake Slack return -1; 31503928aee4356845252ac6b662d5c72c29903813eJake Slack } 31603928aee4356845252ac6b662d5c72c29903813eJake Slack 31703928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 31803928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 31903928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 32003928aee4356845252ac6b662d5c72c29903813eJake Slack private static class Digest extends Credential 32103928aee4356845252ac6b662d5c72c29903813eJake Slack { 32203928aee4356845252ac6b662d5c72c29903813eJake Slack private static final long serialVersionUID = -2484639019549527724L; 32303928aee4356845252ac6b662d5c72c29903813eJake Slack final String method; 32403928aee4356845252ac6b662d5c72c29903813eJake Slack String username = ""; 32503928aee4356845252ac6b662d5c72c29903813eJake Slack String realm = ""; 32603928aee4356845252ac6b662d5c72c29903813eJake Slack String nonce = ""; 32703928aee4356845252ac6b662d5c72c29903813eJake Slack String nc = ""; 32803928aee4356845252ac6b662d5c72c29903813eJake Slack String cnonce = ""; 32903928aee4356845252ac6b662d5c72c29903813eJake Slack String qop = ""; 33003928aee4356845252ac6b662d5c72c29903813eJake Slack String uri = ""; 33103928aee4356845252ac6b662d5c72c29903813eJake Slack String response = ""; 33203928aee4356845252ac6b662d5c72c29903813eJake Slack 33303928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 33403928aee4356845252ac6b662d5c72c29903813eJake Slack Digest(String m) 33503928aee4356845252ac6b662d5c72c29903813eJake Slack { 33603928aee4356845252ac6b662d5c72c29903813eJake Slack method = m; 33703928aee4356845252ac6b662d5c72c29903813eJake Slack } 33803928aee4356845252ac6b662d5c72c29903813eJake Slack 33903928aee4356845252ac6b662d5c72c29903813eJake Slack /* ------------------------------------------------------------ */ 34003928aee4356845252ac6b662d5c72c29903813eJake Slack @Override 34103928aee4356845252ac6b662d5c72c29903813eJake Slack public boolean check(Object credentials) 34203928aee4356845252ac6b662d5c72c29903813eJake Slack { 34303928aee4356845252ac6b662d5c72c29903813eJake Slack if (credentials instanceof char[]) 34403928aee4356845252ac6b662d5c72c29903813eJake Slack credentials=new String((char[])credentials); 34503928aee4356845252ac6b662d5c72c29903813eJake Slack String password = (credentials instanceof String) ? (String) credentials : credentials.toString(); 34603928aee4356845252ac6b662d5c72c29903813eJake Slack 34703928aee4356845252ac6b662d5c72c29903813eJake Slack try 34803928aee4356845252ac6b662d5c72c29903813eJake Slack { 34903928aee4356845252ac6b662d5c72c29903813eJake Slack MessageDigest md = MessageDigest.getInstance("MD5"); 35003928aee4356845252ac6b662d5c72c29903813eJake Slack byte[] ha1; 35103928aee4356845252ac6b662d5c72c29903813eJake Slack if (credentials instanceof Credential.MD5) 35203928aee4356845252ac6b662d5c72c29903813eJake Slack { 35303928aee4356845252ac6b662d5c72c29903813eJake Slack // Credentials are already a MD5 digest - assume it's in 35403928aee4356845252ac6b662d5c72c29903813eJake Slack // form user:realm:password (we have no way to know since 35503928aee4356845252ac6b662d5c72c29903813eJake Slack // it's a digest, alright?) 35603928aee4356845252ac6b662d5c72c29903813eJake Slack ha1 = ((Credential.MD5) credentials).getDigest(); 35703928aee4356845252ac6b662d5c72c29903813eJake Slack } 35803928aee4356845252ac6b662d5c72c29903813eJake Slack else 35903928aee4356845252ac6b662d5c72c29903813eJake Slack { 36003928aee4356845252ac6b662d5c72c29903813eJake Slack // calc A1 digest 36103928aee4356845252ac6b662d5c72c29903813eJake Slack md.update(username.getBytes(StringUtil.__ISO_8859_1)); 36203928aee4356845252ac6b662d5c72c29903813eJake Slack md.update((byte) ':'); 36303928aee4356845252ac6b662d5c72c29903813eJake Slack md.update(realm.getBytes(StringUtil.__ISO_8859_1)); 36403928aee4356845252ac6b662d5c72c29903813eJake Slack md.update((byte) ':'); 36503928aee4356845252ac6b662d5c72c29903813eJake Slack md.update(password.getBytes(StringUtil.__ISO_8859_1)); 36603928aee4356845252ac6b662d5c72c29903813eJake Slack ha1 = md.digest(); 36703928aee4356845252ac6b662d5c72c29903813eJake Slack } 36803928aee4356845252ac6b662d5c72c29903813eJake Slack // calc A2 digest 36903928aee4356845252ac6b662d5c72c29903813eJake Slack md.reset(); 37003928aee4356845252ac6b662d5c72c29903813eJake Slack md.update(method.getBytes(StringUtil.__ISO_8859_1)); 37103928aee4356845252ac6b662d5c72c29903813eJake Slack md.update((byte) ':'); 37203928aee4356845252ac6b662d5c72c29903813eJake Slack md.update(uri.getBytes(StringUtil.__ISO_8859_1)); 37303928aee4356845252ac6b662d5c72c29903813eJake Slack byte[] ha2 = md.digest(); 37403928aee4356845252ac6b662d5c72c29903813eJake Slack 37503928aee4356845252ac6b662d5c72c29903813eJake Slack // calc digest 37603928aee4356845252ac6b662d5c72c29903813eJake Slack // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" 37703928aee4356845252ac6b662d5c72c29903813eJake Slack // nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":" H(A2) ) 37803928aee4356845252ac6b662d5c72c29903813eJake Slack // <"> 37903928aee4356845252ac6b662d5c72c29903813eJake Slack // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) 38003928aee4356845252ac6b662d5c72c29903813eJake Slack // ) > <"> 38103928aee4356845252ac6b662d5c72c29903813eJake Slack 38203928aee4356845252ac6b662d5c72c29903813eJake Slack md.update(TypeUtil.toString(ha1, 16).getBytes(StringUtil.__ISO_8859_1)); 38303928aee4356845252ac6b662d5c72c29903813eJake Slack md.update((byte) ':'); 38403928aee4356845252ac6b662d5c72c29903813eJake Slack md.update(nonce.getBytes(StringUtil.__ISO_8859_1)); 38503928aee4356845252ac6b662d5c72c29903813eJake Slack md.update((byte) ':'); 38603928aee4356845252ac6b662d5c72c29903813eJake Slack md.update(nc.getBytes(StringUtil.__ISO_8859_1)); 38703928aee4356845252ac6b662d5c72c29903813eJake Slack md.update((byte) ':'); 38803928aee4356845252ac6b662d5c72c29903813eJake Slack md.update(cnonce.getBytes(StringUtil.__ISO_8859_1)); 38903928aee4356845252ac6b662d5c72c29903813eJake Slack md.update((byte) ':'); 39003928aee4356845252ac6b662d5c72c29903813eJake Slack md.update(qop.getBytes(StringUtil.__ISO_8859_1)); 39103928aee4356845252ac6b662d5c72c29903813eJake Slack md.update((byte) ':'); 39203928aee4356845252ac6b662d5c72c29903813eJake Slack md.update(TypeUtil.toString(ha2, 16).getBytes(StringUtil.__ISO_8859_1)); 39303928aee4356845252ac6b662d5c72c29903813eJake Slack byte[] digest = md.digest(); 39403928aee4356845252ac6b662d5c72c29903813eJake Slack 39503928aee4356845252ac6b662d5c72c29903813eJake Slack // check digest 39603928aee4356845252ac6b662d5c72c29903813eJake Slack return (TypeUtil.toString(digest, 16).equalsIgnoreCase(response)); 39703928aee4356845252ac6b662d5c72c29903813eJake Slack } 39803928aee4356845252ac6b662d5c72c29903813eJake Slack catch (Exception e) 39903928aee4356845252ac6b662d5c72c29903813eJake Slack { 40003928aee4356845252ac6b662d5c72c29903813eJake Slack LOG.warn(e); 40103928aee4356845252ac6b662d5c72c29903813eJake Slack } 40203928aee4356845252ac6b662d5c72c29903813eJake Slack 40303928aee4356845252ac6b662d5c72c29903813eJake Slack return false; 40403928aee4356845252ac6b662d5c72c29903813eJake Slack } 40503928aee4356845252ac6b662d5c72c29903813eJake Slack 40603928aee4356845252ac6b662d5c72c29903813eJake Slack @Override 40703928aee4356845252ac6b662d5c72c29903813eJake Slack public String toString() 40803928aee4356845252ac6b662d5c72c29903813eJake Slack { 40903928aee4356845252ac6b662d5c72c29903813eJake Slack return username + "," + response; 41003928aee4356845252ac6b662d5c72c29903813eJake Slack } 41103928aee4356845252ac6b662d5c72c29903813eJake Slack } 41203928aee4356845252ac6b662d5c72c29903813eJake Slack} 413