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