6882594: Remove static dependancy on NTLM authentication

Reviewed-by: alanb, weijun
This commit is contained in:
Chris Hegarty 2009-09-18 22:18:19 +01:00
parent be7dddc8a3
commit fc6bda88ab
12 changed files with 418 additions and 161 deletions

View File

@ -86,9 +86,11 @@ FILES_java = \
sun/net/www/protocol/http/AuthCache.java \ sun/net/www/protocol/http/AuthCache.java \
sun/net/www/protocol/http/AuthCacheImpl.java \ sun/net/www/protocol/http/AuthCacheImpl.java \
sun/net/www/protocol/http/AuthCacheValue.java \ sun/net/www/protocol/http/AuthCacheValue.java \
sun/net/www/protocol/http/AuthScheme.java \
sun/net/www/protocol/http/BasicAuthentication.java \ sun/net/www/protocol/http/BasicAuthentication.java \
sun/net/www/protocol/http/DigestAuthentication.java \ sun/net/www/protocol/http/DigestAuthentication.java \
sun/net/www/protocol/http/NTLMAuthentication.java \ sun/net/www/protocol/http/NTLMAuthentication.java \
sun/net/www/protocol/http/NTLMAuthenticationProxy.java \
sun/net/www/protocol/http/NegotiateAuthentication.java \ sun/net/www/protocol/http/NegotiateAuthentication.java \
sun/net/www/protocol/http/NegotiatorImpl.java \ sun/net/www/protocol/http/NegotiatorImpl.java \
sun/net/www/protocol/http/NegotiateCallbackHandler.java \ sun/net/www/protocol/http/NegotiateCallbackHandler.java \

View File

@ -25,14 +25,6 @@
package sun.net.www.protocol.http; package sun.net.www.protocol.http;
import java.io.IOException;
import java.net.URL;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Enumeration;
import java.util.HashMap;
/** /**
* @author Michael McMahon * @author Michael McMahon
* *
@ -49,7 +41,7 @@ public interface AuthCache {
* A:[B:]C:D:E[:F] Between 4 and 6 fields separated by ":" * A:[B:]C:D:E[:F] Between 4 and 6 fields separated by ":"
* where the fields have the following meaning: * where the fields have the following meaning:
* A is "s" or "p" for server or proxy authentication respectively * A is "s" or "p" for server or proxy authentication respectively
* B is optional and is "D", "B", or "N" for digest, basic or ntlm auth. * B is optional and is the {@link AuthScheme}, e.g. BASIC, DIGEST, NTLM, etc
* C is either "http" or "https" * C is either "http" or "https"
* D is the hostname * D is the hostname
* E is the port number * E is the port number

View File

@ -25,15 +25,8 @@
package sun.net.www.protocol.http; package sun.net.www.protocol.http;
import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.net.*; import java.net.PasswordAuthentication;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Enumeration;
import java.util.HashMap;
/** /**
* AuthCacheValue: interface to minimise exposure to authentication cache * AuthCacheValue: interface to minimise exposure to authentication cache
@ -62,8 +55,16 @@ public abstract class AuthCacheValue implements Serializable {
AuthCacheValue() {} AuthCacheValue() {}
/**
* Proxy or Server
*/
abstract Type getAuthType (); abstract Type getAuthType ();
/**
* Authentication scheme
*/
abstract AuthScheme getAuthScheme();
/** /**
* name of server/proxy * name of server/proxy
*/ */

View File

@ -0,0 +1,38 @@
/*
* Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.net.www.protocol.http;
/* Authentication schemes supported by the http implementation. New schemes, if
* supported, should be defined here.
*/
public enum AuthScheme {
BASIC,
DIGEST,
NTLM,
NEGOTIATE,
KERBEROS,
UNKNOWN;
}

View File

@ -85,6 +85,11 @@ abstract class AuthenticationInfo extends AuthCacheValue implements Cloneable {
AuthCacheValue.Type.Server: AuthCacheValue.Type.Server:
AuthCacheValue.Type.Proxy; AuthCacheValue.Type.Proxy;
} }
AuthScheme getAuthScheme() {
return authScheme;
}
public String getHost() { public String getHost() {
return host; return host;
} }
@ -151,7 +156,7 @@ abstract class AuthenticationInfo extends AuthCacheValue implements Cloneable {
} }
//public String toString () { //public String toString () {
//return ("{"+type+":"+authType+":"+protocol+":"+host+":"+port+":"+realm+":"+path+"}"); //return ("{"+type+":"+authScheme+":"+protocol+":"+host+":"+port+":"+realm+":"+path+"}");
//} //}
// REMIND: This cache just grows forever. We should put in a bounded // REMIND: This cache just grows forever. We should put in a bounded
@ -160,8 +165,8 @@ abstract class AuthenticationInfo extends AuthCacheValue implements Cloneable {
/** The type (server/proxy) of authentication this is. Used for key lookup */ /** The type (server/proxy) of authentication this is. Used for key lookup */
char type; char type;
/** The authentication type (basic/digest). Also used for key lookup */ /** The authentication scheme (basic/digest). Also used for key lookup */
char authType; AuthScheme authScheme;
/** The protocol/scheme (i.e. http or https ). Need to keep the caches /** The protocol/scheme (i.e. http or https ). Need to keep the caches
* logically separate for the two protocols. This field is only used * logically separate for the two protocols. This field is only used
@ -183,9 +188,9 @@ abstract class AuthenticationInfo extends AuthCacheValue implements Cloneable {
String path; String path;
/** Use this constructor only for proxy entries */ /** Use this constructor only for proxy entries */
AuthenticationInfo(char type, char authType, String host, int port, String realm) { AuthenticationInfo(char type, AuthScheme authScheme, String host, int port, String realm) {
this.type = type; this.type = type;
this.authType = authType; this.authScheme = authScheme;
this.protocol = ""; this.protocol = "";
this.host = host.toLowerCase(); this.host = host.toLowerCase();
this.port = port; this.port = port;
@ -206,9 +211,9 @@ abstract class AuthenticationInfo extends AuthCacheValue implements Cloneable {
* Constructor used to limit the authorization to the path within * Constructor used to limit the authorization to the path within
* the URL. Use this constructor for origin server entries. * the URL. Use this constructor for origin server entries.
*/ */
AuthenticationInfo(char type, char authType, URL url, String realm) { AuthenticationInfo(char type, AuthScheme authScheme, URL url, String realm) {
this.type = type; this.type = type;
this.authType = authType; this.authScheme = authScheme;
this.protocol = url.getProtocol().toLowerCase(); this.protocol = url.getProtocol().toLowerCase();
this.host = url.getHost().toLowerCase(); this.host = url.getHost().toLowerCase();
this.port = url.getPort(); this.port = url.getPort();
@ -264,12 +269,12 @@ abstract class AuthenticationInfo extends AuthCacheValue implements Cloneable {
* In this case we do not use the path because the protection space * In this case we do not use the path because the protection space
* is identified by the host:port:realm only * is identified by the host:port:realm only
*/ */
static AuthenticationInfo getServerAuth(URL url, String realm, char atype) { static AuthenticationInfo getServerAuth(URL url, String realm, AuthScheme scheme) {
int port = url.getPort(); int port = url.getPort();
if (port == -1) { if (port == -1) {
port = url.getDefaultPort(); port = url.getDefaultPort();
} }
String key = SERVER_AUTHENTICATION + ":" + atype + ":" + url.getProtocol().toLowerCase() String key = SERVER_AUTHENTICATION + ":" + scheme + ":" + url.getProtocol().toLowerCase()
+ ":" + url.getHost().toLowerCase() + ":" + port + ":" + realm; + ":" + url.getHost().toLowerCase() + ":" + port + ":" + realm;
AuthenticationInfo cached = getAuth(key, null); AuthenticationInfo cached = getAuth(key, null);
if ((cached == null) && requestIsInProgress (key)) { if ((cached == null) && requestIsInProgress (key)) {
@ -308,8 +313,8 @@ abstract class AuthenticationInfo extends AuthCacheValue implements Cloneable {
* Used in response to a challenge. Note, the protocol field is always * Used in response to a challenge. Note, the protocol field is always
* blank for proxies. * blank for proxies.
*/ */
static AuthenticationInfo getProxyAuth(String host, int port, String realm, char atype) { static AuthenticationInfo getProxyAuth(String host, int port, String realm, AuthScheme scheme) {
String key = PROXY_AUTHENTICATION + ":" + atype + "::" + host.toLowerCase() String key = PROXY_AUTHENTICATION + ":" + scheme + "::" + host.toLowerCase()
+ ":" + port + ":" + realm; + ":" + port + ":" + realm;
AuthenticationInfo cached = (AuthenticationInfo) cache.get(key, null); AuthenticationInfo cached = (AuthenticationInfo) cache.get(key, null);
if ((cached == null) && requestIsInProgress (key)) { if ((cached == null) && requestIsInProgress (key)) {
@ -409,7 +414,7 @@ abstract class AuthenticationInfo extends AuthCacheValue implements Cloneable {
// This must be kept in sync with the getXXXAuth() methods in this // This must be kept in sync with the getXXXAuth() methods in this
// class. // class.
if (includeRealm) { if (includeRealm) {
return type + ":" + authType + ":" + protocol + ":" return type + ":" + authScheme + ":" + protocol + ":"
+ host + ":" + port + ":" + realm; + host + ":" + port + ":" + realm;
} else { } else {
return type + ":" + protocol + ":" + host + ":" + port; return type + ":" + protocol + ":" + host + ":" + port;

View File

@ -44,8 +44,6 @@ class BasicAuthentication extends AuthenticationInfo {
private static final long serialVersionUID = 100L; private static final long serialVersionUID = 100L;
static final char BASIC_AUTH = 'B';
/** The authentication string for this host, port, and realm. This is /** The authentication string for this host, port, and realm. This is
a simple BASE64 encoding of "login:password". */ a simple BASE64 encoding of "login:password". */
String auth; String auth;
@ -56,7 +54,7 @@ class BasicAuthentication extends AuthenticationInfo {
public BasicAuthentication(boolean isProxy, String host, int port, public BasicAuthentication(boolean isProxy, String host, int port,
String realm, PasswordAuthentication pw) { String realm, PasswordAuthentication pw) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
BASIC_AUTH, host, port, realm); AuthScheme.BASIC, host, port, realm);
String plain = pw.getUserName() + ":"; String plain = pw.getUserName() + ":";
byte[] nameBytes = null; byte[] nameBytes = null;
try { try {
@ -86,7 +84,7 @@ class BasicAuthentication extends AuthenticationInfo {
public BasicAuthentication(boolean isProxy, String host, int port, public BasicAuthentication(boolean isProxy, String host, int port,
String realm, String auth) { String realm, String auth) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
BASIC_AUTH, host, port, realm); AuthScheme.BASIC, host, port, realm);
this.auth = "Basic " + auth; this.auth = "Basic " + auth;
} }
@ -96,7 +94,7 @@ class BasicAuthentication extends AuthenticationInfo {
public BasicAuthentication(boolean isProxy, URL url, String realm, public BasicAuthentication(boolean isProxy, URL url, String realm,
PasswordAuthentication pw) { PasswordAuthentication pw) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
BASIC_AUTH, url, realm); AuthScheme.BASIC, url, realm);
String plain = pw.getUserName() + ":"; String plain = pw.getUserName() + ":";
byte[] nameBytes = null; byte[] nameBytes = null;
try { try {
@ -126,7 +124,7 @@ class BasicAuthentication extends AuthenticationInfo {
public BasicAuthentication(boolean isProxy, URL url, String realm, public BasicAuthentication(boolean isProxy, URL url, String realm,
String auth) { String auth) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
BASIC_AUTH, url, realm); AuthScheme.BASIC, url, realm);
this.auth = "Basic " + auth; this.auth = "Basic " + auth;
} }

View File

@ -38,7 +38,6 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import static sun.net.www.protocol.http.HttpURLConnection.HTTP_CONNECT; import static sun.net.www.protocol.http.HttpURLConnection.HTTP_CONNECT;
/** /**
* DigestAuthentication: Encapsulate an http server authentication using * DigestAuthentication: Encapsulate an http server authentication using
* the "Digest" scheme, as described in RFC2069 and updated in RFC2617 * the "Digest" scheme, as described in RFC2069 and updated in RFC2617
@ -50,8 +49,6 @@ class DigestAuthentication extends AuthenticationInfo {
private static final long serialVersionUID = 100L; private static final long serialVersionUID = 100L;
static final char DIGEST_AUTH = 'D';
private String authMethod; private String authMethod;
// Authentication parameters defined in RFC2617. // Authentication parameters defined in RFC2617.
@ -178,7 +175,10 @@ class DigestAuthentication extends AuthenticationInfo {
public DigestAuthentication(boolean isProxy, URL url, String realm, public DigestAuthentication(boolean isProxy, URL url, String realm,
String authMethod, PasswordAuthentication pw, String authMethod, PasswordAuthentication pw,
Parameters params) { Parameters params) {
super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION, DIGEST_AUTH,url, realm); super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.DIGEST,
url,
realm);
this.authMethod = authMethod; this.authMethod = authMethod;
this.pw = pw; this.pw = pw;
this.params = params; this.params = params;
@ -187,7 +187,11 @@ class DigestAuthentication extends AuthenticationInfo {
public DigestAuthentication(boolean isProxy, String host, int port, String realm, public DigestAuthentication(boolean isProxy, String host, int port, String realm,
String authMethod, PasswordAuthentication pw, String authMethod, PasswordAuthentication pw,
Parameters params) { Parameters params) {
super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION, DIGEST_AUTH,host, port, realm); super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.DIGEST,
host,
port,
realm);
this.authMethod = authMethod; this.authMethod = authMethod;
this.pw = pw; this.pw = pw;
this.params = params; this.params = params;

View File

@ -62,6 +62,12 @@ import java.text.SimpleDateFormat;
import java.util.TimeZone; import java.util.TimeZone;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import static sun.net.www.protocol.http.AuthScheme.BASIC;
import static sun.net.www.protocol.http.AuthScheme.DIGEST;
import static sun.net.www.protocol.http.AuthScheme.NTLM;
import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE;
import static sun.net.www.protocol.http.AuthScheme.KERBEROS;
import static sun.net.www.protocol.http.AuthScheme.UNKNOWN;
/** /**
* A class to represent an HTTP connection to a remote object. * A class to represent an HTTP connection to a remote object.
@ -231,9 +237,11 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
boolean needToCheck = true; boolean needToCheck = true;
private boolean doingNTLM2ndStage = false; /* doing the 2nd stage of an NTLM server authentication */ private boolean doingNTLM2ndStage = false; /* doing the 2nd stage of an NTLM server authentication */
private boolean doingNTLMp2ndStage = false; /* doing the 2nd stage of an NTLM proxy authentication */ private boolean doingNTLMp2ndStage = false; /* doing the 2nd stage of an NTLM proxy authentication */
/* try auth without calling Authenticator */
private boolean tryTransparentNTLMServer = NTLMAuthentication.supportsTransparentAuth(); /* try auth without calling Authenticator. Used for transparent NTLM authentication */
private boolean tryTransparentNTLMProxy = NTLMAuthentication.supportsTransparentAuth(); private boolean tryTransparentNTLMServer = true;
private boolean tryTransparentNTLMProxy = true;
/* Used by Windows specific code */ /* Used by Windows specific code */
Object authObj; Object authObj;
@ -1270,7 +1278,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
String raw = srvHdr.raw(); String raw = srvHdr.raw();
if (!doingNTLM2ndStage) { if (!doingNTLM2ndStage) {
if ((serverAuthentication != null)&& if ((serverAuthentication != null)&&
!(serverAuthentication instanceof NTLMAuthentication)) { serverAuthentication.getAuthScheme() != NTLM) {
if (serverAuthentication.isAuthorizationStale (raw)) { if (serverAuthentication.isAuthorizationStale (raw)) {
/* we can retry with the current credentials */ /* we can retry with the current credentials */
disconnectInternal(); disconnectInternal();
@ -1523,8 +1531,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
*/ */
private AuthenticationInfo private AuthenticationInfo
resetProxyAuthentication(AuthenticationInfo proxyAuthentication, AuthenticationHeader auth) { resetProxyAuthentication(AuthenticationInfo proxyAuthentication, AuthenticationHeader auth) {
if ((proxyAuthentication != null )&& ! (proxyAuthentication instanceof if ((proxyAuthentication != null )&&
NTLMAuthentication)) { proxyAuthentication.getAuthScheme() != NTLM) {
String raw = auth.raw(); String raw = auth.raw();
if (proxyAuthentication.isAuthorizationStale (raw)) { if (proxyAuthentication.isAuthorizationStale (raw)) {
/* we can retry with the current credentials */ /* we can retry with the current credentials */
@ -1776,28 +1784,31 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
HeaderParser p = authhdr.headerParser(); HeaderParser p = authhdr.headerParser();
String realm = p.findValue("realm"); String realm = p.findValue("realm");
String scheme = authhdr.scheme(); String scheme = authhdr.scheme();
char schemeID; AuthScheme authScheme = UNKNOWN;
if ("basic".equalsIgnoreCase(scheme)) { if ("basic".equalsIgnoreCase(scheme)) {
schemeID = BasicAuthentication.BASIC_AUTH; authScheme = BASIC;
} else if ("digest".equalsIgnoreCase(scheme)) { } else if ("digest".equalsIgnoreCase(scheme)) {
schemeID = DigestAuthentication.DIGEST_AUTH; authScheme = DIGEST;
} else if ("ntlm".equalsIgnoreCase(scheme)) { } else if ("ntlm".equalsIgnoreCase(scheme)) {
schemeID = NTLMAuthentication.NTLM_AUTH; authScheme = NTLM;
doingNTLMp2ndStage = true; doingNTLMp2ndStage = true;
} else if ("Kerberos".equalsIgnoreCase(scheme)) { } else if ("Kerberos".equalsIgnoreCase(scheme)) {
schemeID = NegotiateAuthentication.KERBEROS_AUTH; authScheme = KERBEROS;
doingNTLMp2ndStage = true; doingNTLMp2ndStage = true;
} else if ("Negotiate".equalsIgnoreCase(scheme)) { } else if ("Negotiate".equalsIgnoreCase(scheme)) {
schemeID = NegotiateAuthentication.NEGOTIATE_AUTH; authScheme = NEGOTIATE;
doingNTLMp2ndStage = true; doingNTLMp2ndStage = true;
} else {
schemeID = 0;
} }
if (realm == null) if (realm == null)
realm = ""; realm = "";
ret = AuthenticationInfo.getProxyAuth(host, port, realm, schemeID); ret = AuthenticationInfo.getProxyAuth(host,
port,
realm,
authScheme);
if (ret == null) { if (ret == null) {
if (schemeID == BasicAuthentication.BASIC_AUTH) { switch (authScheme) {
case BASIC:
InetAddress addr = null; InetAddress addr = null;
try { try {
final String finalHost = host; final String finalHost = host;
@ -1818,9 +1829,9 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
if (a != null) { if (a != null) {
ret = new BasicAuthentication(true, host, port, realm, a); ret = new BasicAuthentication(true, host, port, realm, a);
} }
} else if (schemeID == DigestAuthentication.DIGEST_AUTH) { break;
PasswordAuthentication a = case DIGEST:
privilegedRequestPasswordAuthentication( a = privilegedRequestPasswordAuthentication(
host, null, port, url.getProtocol(), host, null, port, url.getProtocol(),
realm, scheme, url, RequestorType.PROXY); realm, scheme, url, RequestorType.PROXY);
if (a != null) { if (a != null) {
@ -1829,29 +1840,49 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
ret = new DigestAuthentication(true, host, port, realm, ret = new DigestAuthentication(true, host, port, realm,
scheme, a, params); scheme, a, params);
} }
} else if (schemeID == NTLMAuthentication.NTLM_AUTH) { break;
PasswordAuthentication a = null; case NTLM:
if (!tryTransparentNTLMProxy) { if (NTLMAuthenticationProxy.proxy.supported) {
a = privilegedRequestPasswordAuthentication( /* tryTransparentNTLMProxy will always be true the first
host, null, port, url.getProtocol(), * time around, but verify that the platform supports it
"", scheme, url, RequestorType.PROXY); * otherwise don't try. */
} if (tryTransparentNTLMProxy) {
/* If we are not trying transparent authentication then tryTransparentNTLMProxy =
* we need to have a PasswordAuthentication instance. For NTLMAuthenticationProxy.proxy.supportsTransparentAuth;
* transparent authentication (Windows only) the username }
* and password will be picked up from the current logged a = null;
* on users credentials. if (tryTransparentNTLMProxy) {
*/ HttpCapture.finest("Trying Transparent NTLM authentication");
if (tryTransparentNTLMProxy || } else {
(!tryTransparentNTLMProxy && a != null)) { a = privilegedRequestPasswordAuthentication(
ret = new NTLMAuthentication(true, host, port, a); host, null, port, url.getProtocol(),
} "", scheme, url, RequestorType.PROXY);
}
/* If we are not trying transparent authentication then
* we need to have a PasswordAuthentication instance. For
* transparent authentication (Windows only) the username
* and password will be picked up from the current logged
* on users credentials.
*/
if (tryTransparentNTLMProxy ||
(!tryTransparentNTLMProxy && a != null)) {
ret = NTLMAuthenticationProxy.proxy.create(true, host, port, a);
}
tryTransparentNTLMProxy = false; /* set to false so that we do not try again */
} else if (schemeID == NegotiateAuthentication.NEGOTIATE_AUTH) { tryTransparentNTLMProxy = false;
}
break;
case NEGOTIATE:
ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate")); ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate"));
} else if (schemeID == NegotiateAuthentication.KERBEROS_AUTH) { break;
case KERBEROS:
ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos")); ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos"));
break;
case UNKNOWN:
HttpCapture.finest("Unknown/Unsupported authentication scheme: " + scheme);
default:
throw new AssertionError("should not reach here");
} }
} }
// For backwards compatibility, we also try defaultAuth // For backwards compatibility, we also try defaultAuth
@ -1896,27 +1927,26 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
HeaderParser p = authhdr.headerParser(); HeaderParser p = authhdr.headerParser();
String realm = p.findValue("realm"); String realm = p.findValue("realm");
String scheme = authhdr.scheme(); String scheme = authhdr.scheme();
char schemeID; AuthScheme authScheme = UNKNOWN;
if ("basic".equalsIgnoreCase(scheme)) { if ("basic".equalsIgnoreCase(scheme)) {
schemeID = BasicAuthentication.BASIC_AUTH; authScheme = BASIC;
} else if ("digest".equalsIgnoreCase(scheme)) { } else if ("digest".equalsIgnoreCase(scheme)) {
schemeID = DigestAuthentication.DIGEST_AUTH; authScheme = DIGEST;
} else if ("ntlm".equalsIgnoreCase(scheme)) { } else if ("ntlm".equalsIgnoreCase(scheme)) {
schemeID = NTLMAuthentication.NTLM_AUTH; authScheme = NTLM;
doingNTLM2ndStage = true; doingNTLM2ndStage = true;
} else if ("Kerberos".equalsIgnoreCase(scheme)) { } else if ("Kerberos".equalsIgnoreCase(scheme)) {
schemeID = NegotiateAuthentication.KERBEROS_AUTH; authScheme = KERBEROS;
doingNTLM2ndStage = true; doingNTLM2ndStage = true;
} else if ("Negotiate".equalsIgnoreCase(scheme)) { } else if ("Negotiate".equalsIgnoreCase(scheme)) {
schemeID = NegotiateAuthentication.NEGOTIATE_AUTH; authScheme = NEGOTIATE;
doingNTLM2ndStage = true; doingNTLM2ndStage = true;
} else {
schemeID = 0;
} }
domain = p.findValue ("domain"); domain = p.findValue ("domain");
if (realm == null) if (realm == null)
realm = ""; realm = "";
ret = AuthenticationInfo.getServerAuth(url, realm, schemeID); ret = AuthenticationInfo.getServerAuth(url, realm, authScheme);
InetAddress addr = null; InetAddress addr = null;
if (ret == null) { if (ret == null) {
try { try {
@ -1931,13 +1961,14 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
port = url.getDefaultPort(); port = url.getDefaultPort();
} }
if (ret == null) { if (ret == null) {
if (schemeID == NegotiateAuthentication.KERBEROS_AUTH) { switch(authScheme) {
case KERBEROS:
ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos")); ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos"));
} break;
if (schemeID == NegotiateAuthentication.NEGOTIATE_AUTH) { case NEGOTIATE:
ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate")); ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate"));
} break;
if (schemeID == BasicAuthentication.BASIC_AUTH) { case BASIC:
PasswordAuthentication a = PasswordAuthentication a =
privilegedRequestPasswordAuthentication( privilegedRequestPasswordAuthentication(
url.getHost(), addr, port, url.getProtocol(), url.getHost(), addr, port, url.getProtocol(),
@ -1945,45 +1976,60 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
if (a != null) { if (a != null) {
ret = new BasicAuthentication(false, url, realm, a); ret = new BasicAuthentication(false, url, realm, a);
} }
} break;
case DIGEST:
if (schemeID == DigestAuthentication.DIGEST_AUTH) { a = privilegedRequestPasswordAuthentication(
PasswordAuthentication a =
privilegedRequestPasswordAuthentication(
url.getHost(), addr, port, url.getProtocol(), url.getHost(), addr, port, url.getProtocol(),
realm, scheme, url, RequestorType.SERVER); realm, scheme, url, RequestorType.SERVER);
if (a != null) { if (a != null) {
digestparams = new DigestAuthentication.Parameters(); digestparams = new DigestAuthentication.Parameters();
ret = new DigestAuthentication(false, url, realm, scheme, a, digestparams); ret = new DigestAuthentication(false, url, realm, scheme, a, digestparams);
} }
} break;
case NTLM:
if (NTLMAuthenticationProxy.proxy.supported) {
URL url1;
try {
url1 = new URL (url, "/"); /* truncate the path */
} catch (Exception e) {
url1 = url;
}
if (schemeID == NTLMAuthentication.NTLM_AUTH) { /* tryTransparentNTLMServer will always be true the first
URL url1; * time around, but verify that the platform supports it
try { * otherwise don't try. */
url1 = new URL (url, "/"); /* truncate the path */ if (tryTransparentNTLMServer) {
} catch (Exception e) { tryTransparentNTLMServer =
url1 = url; NTLMAuthenticationProxy.proxy.supportsTransparentAuth;
} }
PasswordAuthentication a = null; a = null;
if (!tryTransparentNTLMServer) { if (tryTransparentNTLMServer) {
a = privilegedRequestPasswordAuthentication( HttpCapture.finest("Trying Transparent NTLM authentication");
url.getHost(), addr, port, url.getProtocol(), } else {
"", scheme, url, RequestorType.SERVER); a = privilegedRequestPasswordAuthentication(
} url.getHost(), addr, port, url.getProtocol(),
"", scheme, url, RequestorType.SERVER);
}
/* If we are not trying transparent authentication then /* If we are not trying transparent authentication then
* we need to have a PasswordAuthentication instance. For * we need to have a PasswordAuthentication instance. For
* transparent authentication (Windows only) the username * transparent authentication (Windows only) the username
* and password will be picked up from the current logged * and password will be picked up from the current logged
* on users credentials. * on users credentials.
*/ */
if (tryTransparentNTLMServer || if (tryTransparentNTLMServer ||
(!tryTransparentNTLMServer && a != null)) { (!tryTransparentNTLMServer && a != null)) {
ret = new NTLMAuthentication(false, url1, a); ret = NTLMAuthenticationProxy.proxy.create(false, url1, a);
} }
tryTransparentNTLMServer = false; /* set to false so that we do not try again */
tryTransparentNTLMServer = false;
}
break;
case UNKNOWN:
HttpCapture.finest("Unknown/Unsupported authentication scheme: " + scheme);
default:
throw new AssertionError("should not reach here");
} }
} }

View File

@ -0,0 +1,132 @@
/*
* Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.net.www.protocol.http;
import java.net.URL;
import java.net.PasswordAuthentication;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import sun.net.www.http.HttpCapture;
/**
* Proxy class for loading NTLMAuthentication, so as to remove static
* dependancy.
*/
class NTLMAuthenticationProxy {
private static Method supportsTA;
private static final String clazzStr = "sun.net.www.protocol.http.NTLMAuthentication";
private static final String supportsTAStr = "supportsTransparentAuth";
static final NTLMAuthenticationProxy proxy = tryLoadNTLMAuthentication();
static final boolean supported = proxy != null ? true : false;
static final boolean supportsTransparentAuth = supported ? supportsTransparentAuth(supportsTA) : false;
private final Constructor<? extends AuthenticationInfo> threeArgCtr;
private final Constructor<? extends AuthenticationInfo> fiveArgCtr;
private NTLMAuthenticationProxy(Constructor<? extends AuthenticationInfo> threeArgCtr,
Constructor<? extends AuthenticationInfo> fiveArgCtr) {
this.threeArgCtr = threeArgCtr;
this.fiveArgCtr = fiveArgCtr;
}
AuthenticationInfo create(boolean isProxy,
URL url,
PasswordAuthentication pw) {
try {
return threeArgCtr.newInstance(isProxy, url, pw);
} catch (ReflectiveOperationException roe) {
log(roe);
}
return null;
}
AuthenticationInfo create(boolean isProxy,
String host,
int port,
PasswordAuthentication pw) {
try {
return fiveArgCtr.newInstance(isProxy, host, port, pw);
} catch (ReflectiveOperationException roe) {
log(roe);
}
return null;
}
/* Returns true if the NTLM implementation supports transparent
* authentication (try with the current users credentials before
* prompting for username and password, etc).
*/
private static boolean supportsTransparentAuth(Method method) {
try {
return (Boolean)method.invoke(null);
} catch (ReflectiveOperationException roe) {
log(roe);
}
return false;
}
/**
* Loads the NTLM authentiation implementation through reflection. If
* the class is present, then it must have the required constructors and
* method. Otherwise, it is considered an error.
*/
@SuppressWarnings("unchecked")
private static NTLMAuthenticationProxy tryLoadNTLMAuthentication() {
Class<? extends AuthenticationInfo> cl;
Constructor<? extends AuthenticationInfo> threeArg, fiveArg;
try {
cl = (Class<? extends AuthenticationInfo>)Class.forName(clazzStr, true, null);
if (cl != null) {
threeArg = cl.getConstructor(boolean.class,
URL.class,
PasswordAuthentication.class);
fiveArg = cl.getConstructor(boolean.class,
String.class,
int.class,
PasswordAuthentication.class);
supportsTA = cl.getDeclaredMethod(supportsTAStr);
return new NTLMAuthenticationProxy(threeArg,
fiveArg);
}
} catch (ClassNotFoundException cnfe) {
log(cnfe);
} catch (ReflectiveOperationException roe) {
throw new AssertionError(roe);
}
return null;
}
static void log(Exception e) {
if (HttpCapture.isLoggable("FINEST")) {
HttpCapture.finest("NTLMAuthenticationProxy: " + e);
}
}
}

View File

@ -34,7 +34,10 @@ import sun.misc.BASE64Encoder;
import java.net.URL; import java.net.URL;
import java.io.IOException; import java.io.IOException;
import java.net.Authenticator.RequestorType; import java.net.Authenticator.RequestorType;
import java.lang.reflect.Constructor;
import sun.net.www.http.HttpCapture;
import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE;
import static sun.net.www.protocol.http.AuthScheme.KERBEROS;
/** /**
* NegotiateAuthentication: * NegotiateAuthentication:
@ -49,9 +52,6 @@ class NegotiateAuthentication extends AuthenticationInfo {
final private HttpCallerInfo hci; final private HttpCallerInfo hci;
static final char NEGOTIATE_AUTH = 'S';
static final char KERBEROS_AUTH = 'K';
// These maps are used to manage the GSS availability for diffrent // These maps are used to manage the GSS availability for diffrent
// hosts. The key for both maps is the host name. // hosts. The key for both maps is the host name.
// <code>supported</code> is set when isSupported is checked, // <code>supported</code> is set when isSupported is checked,
@ -68,11 +68,10 @@ class NegotiateAuthentication extends AuthenticationInfo {
* @param hci a schemed object. * @param hci a schemed object.
*/ */
public NegotiateAuthentication(HttpCallerInfo hci) { public NegotiateAuthentication(HttpCallerInfo hci) {
super(RequestorType.PROXY==hci.authType? super(RequestorType.PROXY==hci.authType ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
PROXY_AUTHENTICATION:SERVER_AUTHENTICATION, hci.scheme.equalsIgnoreCase("Negotiate") ? NEGOTIATE : KERBEROS,
hci.scheme.equalsIgnoreCase("Negotiate")? hci.url,
NEGOTIATE_AUTH:KERBEROS_AUTH, "");
hci.url, "");
this.hci = hci; this.hci = hci;
} }
@ -249,13 +248,42 @@ abstract class Negotiator {
// The current implementation will make sure NegotiatorImpl is not // The current implementation will make sure NegotiatorImpl is not
// directly referenced when compiling, thus smooth the way of building // directly referenced when compiling, thus smooth the way of building
// the J2SE platform where HttpURLConnection is a bootstrap class. // the J2SE platform where HttpURLConnection is a bootstrap class.
//
// Makes NegotiatorImpl, and the security classes it references, a
// runtime dependency rather than a static one.
Class clazz = Class.forName("sun.net.www.protocol.http.NegotiatorImpl"); Class clazz;
java.lang.reflect.Constructor c = clazz.getConstructor(HttpCallerInfo.class); Constructor c;
return (Negotiator) (c.newInstance(hci)); try {
clazz = Class.forName("sun.net.www.protocol.http.NegotiatorImpl", true, null);
c = clazz.getConstructor(HttpCallerInfo.class);
} catch (ClassNotFoundException cnfe) {
log(cnfe);
throw cnfe;
} catch (ReflectiveOperationException roe) {
// if the class is there then something seriously wrong if
// the constructor is not.
throw new AssertionError(roe);
}
try {
return (Negotiator) (c.newInstance(hci));
} catch (ReflectiveOperationException roe) {
log(roe);
Throwable t = roe.getCause();
if (t != null && t instanceof Exception)
log((Exception)t);
throw roe;
}
} }
abstract byte[] firstToken() throws IOException; abstract byte[] firstToken() throws IOException;
abstract byte[] nextToken(byte[] in) throws IOException; abstract byte[] nextToken(byte[] in) throws IOException;
static void log(Exception e) {
if (HttpCapture.isLoggable("FINEST")) {
HttpCapture.finest("NegotiateAuthentication: " + e);
}
}
} }

View File

@ -25,18 +25,23 @@
package sun.net.www.protocol.http; package sun.net.www.protocol.http;
import java.util.Arrays; import java.io.IOException;
import java.util.StringTokenizer; import java.io.UnsupportedEncodingException;
import java.util.Random; import java.net.InetAddress;
import java.net.PasswordAuthentication;
import java.net.UnknownHostException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import sun.net.www.HeaderParser; import sun.net.www.HeaderParser;
import java.io.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.net.*;
/** /**
* NTLMAuthentication: * NTLMAuthentication:
* *
@ -66,8 +71,6 @@ import java.net.*;
class NTLMAuthentication extends AuthenticationInfo { class NTLMAuthentication extends AuthenticationInfo {
private static final long serialVersionUID = -2403849171106437142L; private static final long serialVersionUID = -2403849171106437142L;
static char NTLM_AUTH = 'N';
private byte[] type1; private byte[] type1;
private byte[] type3; private byte[] type3;
@ -142,7 +145,10 @@ class NTLMAuthentication extends AuthenticationInfo {
* from a system property: "http.auth.ntlm.domain". * from a system property: "http.auth.ntlm.domain".
*/ */
public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw) { public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw) {
super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION, NTLM_AUTH, url, ""); super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.NTLM,
url,
"");
init (pw); init (pw);
} }
@ -166,7 +172,11 @@ class NTLMAuthentication extends AuthenticationInfo {
*/ */
public NTLMAuthentication(boolean isProxy, String host, int port, public NTLMAuthentication(boolean isProxy, String host, int port,
PasswordAuthentication pw) { PasswordAuthentication pw) {
super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION, NTLM_AUTH,host, port, ""); super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.NTLM,
host,
port,
"");
init (pw); init (pw);
} }

View File

@ -25,18 +25,13 @@
package sun.net.www.protocol.http; package sun.net.www.protocol.http;
import java.util.Arrays; import java.io.IOException;
import java.util.StringTokenizer; import java.net.InetAddress;
import java.util.Random; import java.net.PasswordAuthentication;
import java.net.UnknownHostException;
import java.net.URL;
import sun.net.www.HeaderParser; import sun.net.www.HeaderParser;
import java.io.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.net.*;
/** /**
* NTLMAuthentication: * NTLMAuthentication:
* *
@ -47,7 +42,6 @@ class NTLMAuthentication extends AuthenticationInfo {
private static final long serialVersionUID = 100L; private static final long serialVersionUID = 100L;
static final char NTLM_AUTH = 'N';
private String hostname; private String hostname;
private static String defaultDomain; /* Domain to use if not specified by user */ private static String defaultDomain; /* Domain to use if not specified by user */
@ -88,7 +82,10 @@ class NTLMAuthentication extends AuthenticationInfo {
* from a system property: "http.auth.ntlm.domain". * from a system property: "http.auth.ntlm.domain".
*/ */
public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw) { public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw) {
super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION, NTLM_AUTH, url, ""); super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.NTLM,
url,
"");
init (pw); init (pw);
} }
@ -119,7 +116,11 @@ class NTLMAuthentication extends AuthenticationInfo {
*/ */
public NTLMAuthentication(boolean isProxy, String host, int port, public NTLMAuthentication(boolean isProxy, String host, int port,
PasswordAuthentication pw) { PasswordAuthentication pw) {
super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION, NTLM_AUTH,host, port, ""); super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION,
AuthScheme.NTLM,
host,
port,
"");
init (pw); init (pw);
} }