|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.net.www.protocol.http; |
|
|
|
import java.util.Collections; |
|
import java.util.Iterator; |
|
import java.util.HashMap; |
|
import java.util.Set; |
|
|
|
import sun.net.www.*; |
|
import sun.security.action.GetPropertyAction; |
|
|
|
/** |
|
* 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, or explicitly |
|
* disabled, by {@code disabledSchemes}, 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; |
|
HeaderParser preferred; |
|
String preferred_r; |
|
private final HttpCallerInfo hci; |
|
|
|
// When set true, do not use Negotiate even if the response |
|
|
|
boolean dontUseNegotiate = false; |
|
static String authPref=null; |
|
|
|
public String toString() { |
|
return "AuthenticationHeader: prefer " + preferred_r; |
|
} |
|
|
|
static { |
|
authPref = GetPropertyAction.privilegedGetProperty("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; |
|
|
|
|
|
|
|
|
|
*/ |
|
public AuthenticationHeader (String hdrname, MessageHeader response, |
|
HttpCallerInfo hci, boolean dontUseNegotiate) { |
|
this(hdrname, response, hci, dontUseNegotiate, Collections.emptySet()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public AuthenticationHeader(String hdrname, |
|
MessageHeader response, |
|
HttpCallerInfo hci, |
|
boolean dontUseNegotiate, |
|
Set<String> disabledSchemes) { |
|
this.hci = hci; |
|
this.dontUseNegotiate = dontUseNegotiate; |
|
this.rsp = response; |
|
this.hdrname = hdrname; |
|
this.schemes = new HashMap<>(); |
|
parse(disabledSchemes); |
|
} |
|
|
|
public HttpCallerInfo getHttpCallerInfo() { |
|
return hci; |
|
} |
|
|
|
static class SchemeMapValue { |
|
SchemeMapValue (HeaderParser h, String r) {raw=r; parser=h;} |
|
String raw; |
|
HeaderParser parser; |
|
} |
|
|
|
HashMap<String, SchemeMapValue> schemes; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void parse(Set<String> disabledSchemes) { |
|
Iterator<String> iter = rsp.multiValueIterator(hdrname); |
|
while (iter.hasNext()) { |
|
String raw = iter.next(); |
|
|
|
HeaderParser hp = new HeaderParser(raw); |
|
Iterator<String> keys = hp.keys(); |
|
int i, lastSchemeIndex; |
|
for (i=0, lastSchemeIndex = -1; keys.hasNext(); i++) { |
|
keys.next(); |
|
if (hp.findValue(i) == null) { |
|
if (lastSchemeIndex != -1) { |
|
HeaderParser hpn = hp.subsequence (lastSchemeIndex, i); |
|
String scheme = hpn.findKey(0); |
|
if (!disabledSchemes.contains(scheme)) |
|
schemes.put(scheme, new SchemeMapValue (hpn, raw)); |
|
} |
|
lastSchemeIndex = i; |
|
} |
|
} |
|
if (i > lastSchemeIndex) { |
|
HeaderParser hpn = hp.subsequence (lastSchemeIndex, i); |
|
String scheme = hpn.findKey(0); |
|
if (!disabledSchemes.contains(scheme)) |
|
schemes.put(scheme, new SchemeMapValue (hpn, raw)); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
SchemeMapValue v = null; |
|
if (authPref == null || (v=schemes.get (authPref)) == null) { |
|
|
|
if(v == null && !dontUseNegotiate) { |
|
SchemeMapValue tmp = 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 = 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 |
|
|
|
if(hci == null || !NegotiateAuthentication.isSupported(new HttpCallerInfo(hci, "Kerberos"))) { |
|
tmp = null; |
|
} |
|
v = tmp; |
|
} |
|
} |
|
|
|
if(v == null) { |
|
if ((v=schemes.get ("digest")) == null) { |
|
if (!NTLMAuthenticationProxy.supported |
|
|| ((v=schemes.get("ntlm"))==null)) { |
|
v = schemes.get ("basic"); |
|
} |
|
} |
|
} |
|
} else { |
|
if (dontUseNegotiate && authPref.equals("negotiate")) { |
|
v = null; |
|
} |
|
} |
|
|
|
if (v != null) { |
|
preferred = v.parser; |
|
preferred_r = v.raw; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public HeaderParser headerParser() { |
|
return preferred; |
|
} |
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean isPresent () { |
|
return preferred != null; |
|
} |
|
} |