258 lines · 9.4 KB · Java
RawDownload
/* * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.net.www.protocol.http; import sun.net.www.*;import java.util.Iterator;import java.util.HashMap; /** * This class is used to parse the information in WWW-Authenticate: and Proxy-Authenticate: * headers. It searches among multiple header lines and within each header line * for the best currently supported scheme. It can also return a HeaderParser * containing the challenge data for that particular scheme. * * Some examples: * * WWW-Authenticate: Basic realm="foo" Digest realm="bar" NTLM *  Note the realm parameter must be associated with the particular scheme. * * or * * WWW-Authenticate: Basic realm="foo" * WWW-Authenticate: Digest realm="foo",qop="auth",nonce="thisisanunlikelynonce" * WWW-Authenticate: NTLM * * or * * WWW-Authenticate: Basic realm="foo" * WWW-Authenticate: NTLM ASKAJK9893289889QWQIOIONMNMN * * The last example shows how NTLM breaks the rules of rfc2617 for the structure of * the authentication header. This is the reason why the raw header field is used for ntlm. * * At present, the class chooses schemes in following order : *      1. Negotiate (if supported) *      2. Kerberos (if supported) *      3. Digest *      4. NTLM (if supported) *      5. Basic * * This choice can be modified by setting a system property: * *      -Dhttp.auth.preference="scheme" * * which in this case, specifies that "scheme" should be used as the auth scheme when offered * disregarding the default prioritisation. If scheme is not offered then the default priority * is used. * * Attention: when http.auth.preference is set as SPNEGO or Kerberos, it's actually "Negotiate * with SPNEGO" or "Negotiate with Kerberos", which means the user will prefer the Negotiate * scheme with GSS/SPNEGO or GSS/Kerberos mechanism. * * This also means that the real "Kerberos" scheme can never be set as a preference. */ public class AuthenticationHeader {     MessageHeader rsp; // the response to be parsed    HeaderParser preferred;    String preferred_r; // raw Strings    private final HttpCallerInfo hci;   // un-schemed, need check     // When set true, do not use Negotiate even if the response    // headers suggest so.    boolean dontUseNegotiate = false;    static String authPref=null;     public String toString() {        return "AuthenticationHeader: prefer " + preferred_r;    }     static {        authPref = java.security.AccessController.doPrivileged(            new sun.security.action.GetPropertyAction("http.auth.preference"));         // http.auth.preference can be set to SPNEGO or Kerberos.        // In fact they means "Negotiate with SPNEGO" and "Negotiate with        // Kerberos" separately, so here they are all translated into        // Negotiate. Read NegotiateAuthentication.java to see how they        // were used later.         if (authPref != null) {            authPref = authPref.toLowerCase();            if(authPref.equals("spnego") || authPref.equals("kerberos")) {                authPref = "negotiate";            }        }    }     String hdrname; // Name of the header to look for     /**     * parse a set of authentication headers and choose the preferred scheme     * that we support for a given host     */    public AuthenticationHeader (String hdrname, MessageHeader response,            HttpCallerInfo hci, boolean dontUseNegotiate) {        this.hci = hci;        this.dontUseNegotiate = dontUseNegotiate;        rsp = response;        this.hdrname = hdrname;        schemes = new HashMap();        parse();    }     public HttpCallerInfo getHttpCallerInfo() {        return hci;    }    /* we build up a map of scheme names mapped to SchemeMapValue objects */    static class SchemeMapValue {        SchemeMapValue (HeaderParser h, String r) {raw=r; parser=h;}        String raw;        HeaderParser parser;    }     HashMap schemes;     /* Iterate through each header line, and then within each line.     * If multiple entries exist for a particular scheme (unlikely)     * then the last one will be used. The     * preferred scheme that we support will be used.     */    private void parse () {        Iterator iter = rsp.multiValueIterator (hdrname);        while (iter.hasNext()) {            String raw = (String)iter.next();            HeaderParser hp = new HeaderParser (raw);            Iterator keys = hp.keys();            int i, lastSchemeIndex;            for (i=0, lastSchemeIndex = -1; keys.hasNext(); i++) {                keys.next();                if (hp.findValue(i) == null) { /* found a scheme name */                    if (lastSchemeIndex != -1) {                        HeaderParser hpn = hp.subsequence (lastSchemeIndex, i);                        String scheme = hpn.findKey(0);                        schemes.put (scheme, new SchemeMapValue (hpn, raw));                    }                    lastSchemeIndex = i;                }            }            if (i > lastSchemeIndex) {                HeaderParser hpn = hp.subsequence (lastSchemeIndex, i);                String scheme = hpn.findKey(0);                schemes.put (scheme, new SchemeMapValue (hpn, raw));            }        }         /* choose the best of them, the order is         * negotiate -> kerberos -> digest -> ntlm -> basic         */        SchemeMapValue v = null;        if (authPref == null || (v=(SchemeMapValue)schemes.get (authPref)) == null) {             if(v == null && !dontUseNegotiate) {                SchemeMapValue tmp = (SchemeMapValue)schemes.get("negotiate");                if(tmp != null) {                    if(hci == null || !NegotiateAuthentication.isSupported(new HttpCallerInfo(hci, "Negotiate"))) {                        tmp = null;                    }                    v = tmp;                }            }             if(v == null && !dontUseNegotiate) {                SchemeMapValue tmp = (SchemeMapValue)schemes.get("kerberos");                if(tmp != null) {                    // the Kerberos scheme is only observed in MS ISA Server. In                    // fact i think it's a Kerberos-mechnism-only Negotiate.                    // Since the Kerberos scheme is always accompanied with the                    // Negotiate scheme, so it seems impossible to reach this                    // line. Even if the user explicitly set http.auth.preference                    // as Kerberos, it means Negotiate with Kerberos, and the code                    // will still tried to use Negotiate at first.                    //                    // The only chance this line get executed is that the server                    // only suggest the Kerberos scheme.                    if(hci == null || !NegotiateAuthentication.isSupported(new HttpCallerInfo(hci, "Kerberos"))) {                        tmp = null;                    }                    v = tmp;                }            }             if(v == null) {                if ((v=(SchemeMapValue)schemes.get ("digest")) == null) {                    if (((v=(SchemeMapValue)schemes.get("ntlm"))==null)) {                        v = (SchemeMapValue)schemes.get ("basic");                    }                }            }        } else {    // authPref != null && it's found in reponses'            if (dontUseNegotiate && authPref.equals("negotiate")) {                v = null;            }        }         if (v != null) {            preferred = v.parser;            preferred_r = v.raw;        }    }     /**     * return a header parser containing the preferred authentication scheme (only).     * The preferred scheme is the strongest of the schemes proposed by the server.     * The returned HeaderParser will contain the relevant parameters for that scheme     */    public HeaderParser headerParser() {        return preferred;    }     /**     * return the name of the preferred scheme     */    public String scheme() {        if (preferred != null) {            return preferred.findKey(0);        } else {            return null;        }    }     /* return the raw header field for the preferred/chosen scheme */     public String raw () {        return preferred_r;    }     /**     * returns true is the header exists and contains a recognised scheme     */    public boolean isPresent () {        return preferred != null;    }}