--- old/make/java/net/Makefile Wed Oct 21 15:28:41 2009 +++ new/make/java/net/Makefile Wed Oct 21 15:28:40 2009 @@ -83,7 +83,7 @@ # # Find platform specific native code # -vpath %.c $(PLATFORM_SRC)/native/sun/net/dns $(PLATFORM_SRC)/native/sun/net/www/protocol/http $(PLATFORM_SRC)/native/sun/net/spi +vpath %.c $(PLATFORM_SRC)/native/sun/net/dns $(PLATFORM_SRC)/native/sun/net/www/protocol/http/ntlm $(PLATFORM_SRC)/native/sun/net/spi # # Include rules --- old/make/sun/net/FILES_java.gmk Wed Oct 21 15:28:43 2009 +++ new/make/sun/net/FILES_java.gmk Wed Oct 21 15:28:42 2009 @@ -89,11 +89,12 @@ sun/net/www/protocol/http/AuthScheme.java \ sun/net/www/protocol/http/BasicAuthentication.java \ sun/net/www/protocol/http/DigestAuthentication.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/NegotiatorImpl.java \ - sun/net/www/protocol/http/NegotiateCallbackHandler.java \ + sun/net/www/protocol/http/Negotiator.java \ + sun/net/www/protocol/http/ntlm/NTLMAuthentication.java \ + sun/net/www/protocol/http/spnego/NegotiatorImpl.java \ + sun/net/www/protocol/http/spnego/NegotiateCallbackHandler.java \ sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java \ sun/net/www/protocol/https/HttpsClient.java \ sun/net/www/protocol/https/DefaultHostnameVerifier.java \ @@ -128,7 +129,7 @@ sun/net/idn/StringPrep.java ifeq ($(PLATFORM), windows) - FILES_java += sun/net/www/protocol/http/NTLMAuthSequence.java + FILES_java += sun/net/www/protocol/http/ntlm/NTLMAuthSequence.java endif ifeq ($(PLATFORM), solaris) --- old/src/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java Wed Oct 21 15:28:45 2009 +++ new/src/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java Wed Oct 21 15:28:44 2009 @@ -25,8 +25,10 @@ package sun.net.www.protocol.http; -import java.io.*; -import java.net.*; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.net.PasswordAuthentication; +import java.net.URL; import java.util.Hashtable; import java.util.LinkedList; import java.util.ListIterator; @@ -51,12 +53,12 @@ // policy in HttpURLConnection. A failure on baz.foo.com shouldn't // uncache foo.com! -abstract class AuthenticationInfo extends AuthCacheValue implements Cloneable { +public abstract class AuthenticationInfo extends AuthCacheValue implements Cloneable { // Constants saying what kind of authroization this is. This determines // the namespace in the hash table lookup. - static final char SERVER_AUTHENTICATION = 's'; - static final char PROXY_AUTHENTICATION = 'p'; + public static final char SERVER_AUTHENTICATION = 's'; + public static final char PROXY_AUTHENTICATION = 'p'; /** * If true, then simultaneous authentication requests to the same realm/proxy @@ -188,7 +190,7 @@ String path; /** Use this constructor only for proxy entries */ - AuthenticationInfo(char type, AuthScheme authScheme, String host, int port, String realm) { + public AuthenticationInfo(char type, AuthScheme authScheme, String host, int port, String realm) { this.type = type; this.authScheme = authScheme; this.protocol = ""; @@ -211,7 +213,7 @@ * Constructor used to limit the authorization to the path within * the URL. Use this constructor for origin server entries. */ - AuthenticationInfo(char type, AuthScheme authScheme, URL url, String realm) { + public AuthenticationInfo(char type, AuthScheme authScheme, URL url, String realm) { this.type = type; this.authScheme = authScheme; this.protocol = url.getProtocol().toLowerCase(); @@ -358,13 +360,19 @@ /** * @return true if this authentication supports preemptive authorization */ - abstract boolean supportsPreemptiveAuthorization(); + public abstract boolean supportsPreemptiveAuthorization(); /** * @return the name of the HTTP header this authentication wants set. * This is used for preemptive authorization. */ - abstract String getHeaderName(); + public String getHeaderName() { + if (type == SERVER_AUTHENTICATION) { + return "Authorization"; + } else { + return "Proxy-authorization"; + } + } /** * Calculates and returns the authentication header value based @@ -375,7 +383,7 @@ * @return the value of the HTTP header this authentication wants set. * Used for preemptive authorization. */ - abstract String getHeaderValue(URL url, String method); + public abstract String getHeaderValue(URL url, String method); /** * Set header(s) on the given connection. Subclasses must override @@ -386,7 +394,7 @@ * @param raw The raw header field (if needed) * @return true if all goes well, false if no headers were set. */ - abstract boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw); + public abstract boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw); /** * Check if the header indicates that the current auth. parameters are stale. @@ -396,7 +404,7 @@ * returning false means we have to go back to the user to ask for a new * username password. */ - abstract boolean isAuthorizationStale (String header); + public abstract boolean isAuthorizationStale (String header); /** * Give a key for hash table lookups. --- old/src/share/classes/sun/net/www/protocol/http/BasicAuthentication.java Wed Oct 21 15:28:47 2009 +++ new/src/share/classes/sun/net/www/protocol/http/BasicAuthentication.java Wed Oct 21 15:28:46 2009 @@ -131,22 +131,12 @@ /** * @return true if this authentication supports preemptive authorization */ - boolean supportsPreemptiveAuthorization() { + @Override + public boolean supportsPreemptiveAuthorization() { return true; } /** - * @return the name of the HTTP header this authentication wants set - */ - String getHeaderName() { - if (type == SERVER_AUTHENTICATION) { - return "Authorization"; - } else { - return "Proxy-authorization"; - } - } - - /** * Set header(s) on the given connection. This will only be called for * definitive (i.e. non-preemptive) authorization. * @param conn The connection to apply the header(s) to @@ -154,7 +144,8 @@ * @param raw The raw header values for this connection, if needed. * @return true if all goes well, false if no headers were set. */ - boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { + @Override + public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { conn.setAuthenticationProperty(getHeaderName(), getHeaderValue(null,null)); return true; } @@ -162,7 +153,8 @@ /** * @return the value of the HTTP header this authentication wants set */ - String getHeaderValue(URL url, String method) { + @Override + public String getHeaderValue(URL url, String method) { /* For Basic the authorization string does not depend on the request URL * or the request method */ @@ -174,7 +166,8 @@ * In other words there is no possibility to reuse the credentials. * They are always either valid or invalid. */ - boolean isAuthorizationStale (String header) { + @Override + public boolean isAuthorizationStale (String header) { return false; } --- old/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java Wed Oct 21 15:28:49 2009 +++ new/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java Wed Oct 21 15:28:48 2009 @@ -200,22 +200,12 @@ /** * @return true if this authentication supports preemptive authorization */ - boolean supportsPreemptiveAuthorization() { + @Override + public boolean supportsPreemptiveAuthorization() { return true; } /** - * @return the name of the HTTP header this authentication wants set - */ - String getHeaderName() { - if (type == SERVER_AUTHENTICATION) { - return "Authorization"; - } else { - return "Proxy-Authorization"; - } - } - - /** * Reclaculates the request-digest and returns it. * *

Used in the common case where the requestURI is simply the @@ -229,7 +219,8 @@ * * @return the value of the HTTP header this authentication wants set */ - String getHeaderValue(URL url, String method) { + @Override + public String getHeaderValue(URL url, String method) { return getHeaderValueImpl(url.getFile(), method); } @@ -259,7 +250,8 @@ * returning false means we have to go back to the user to ask for a new * username password. */ - boolean isAuthorizationStale (String header) { + @Override + public boolean isAuthorizationStale (String header) { HeaderParser p = new HeaderParser (header); String s = p.findValue ("stale"); if (s == null || !s.equals("true")) @@ -279,7 +271,8 @@ * @param raw Raw header values for this connection, if needed. * @return true if all goes well, false if no headers were set. */ - boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { + @Override + public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { params.setNonce (p.findValue("nonce")); params.setOpaque (p.findValue("opaque")); params.setQop (p.findValue("qop")); --- old/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Wed Oct 21 15:28:51 2009 +++ new/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Wed Oct 21 15:28:50 2009 @@ -243,7 +243,7 @@ private boolean tryTransparentNTLMProxy = true; /* Used by Windows specific code */ - Object authObj; + private Object authObj; /* Set if the user is manually setting the Authorization or Proxy-Authorization headers */ boolean isUserServerAuth; @@ -332,6 +332,15 @@ return logger; } + /* Used for Windows NTLM implementation */ + public Object authObj() { + return authObj; + } + + public void authObj(Object authObj) { + this.authObj = authObj; + } + /* * checks the validity of http message header and throws * IllegalArgumentException if invalid. @@ -2529,7 +2538,7 @@ // Set a property for authentication. This can safely disregard // the connected test. // - void setAuthenticationProperty(String key, String value) { + public void setAuthenticationProperty(String key, String value) { checkMessageHeader(key, value); requests.set(key, value); } --- old/src/share/classes/sun/net/www/protocol/http/NTLMAuthenticationProxy.java Wed Oct 21 15:28:53 2009 +++ new/src/share/classes/sun/net/www/protocol/http/NTLMAuthenticationProxy.java Wed Oct 21 15:28:52 2009 @@ -36,7 +36,7 @@ */ class NTLMAuthenticationProxy { private static Method supportsTA; - private static final String clazzStr = "sun.net.www.protocol.http.NTLMAuthentication"; + private static final String clazzStr = "sun.net.www.protocol.http.ntlm.NTLMAuthentication"; private static final String supportsTAStr = "supportsTransparentAuth"; static final NTLMAuthenticationProxy proxy = tryLoadNTLMAuthentication(); --- old/src/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java Wed Oct 21 15:28:55 2009 +++ new/src/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java Wed Oct 21 15:28:54 2009 @@ -25,17 +25,13 @@ package sun.net.www.protocol.http; +import java.net.URL; +import java.io.IOException; +import java.net.Authenticator.RequestorType; import java.util.HashMap; - import sun.net.www.HeaderParser; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; -import sun.util.logging.PlatformLogger; - -import java.net.URL; -import java.io.IOException; -import java.net.Authenticator.RequestorType; -import java.lang.reflect.Constructor; import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE; import static sun.net.www.protocol.http.AuthScheme.KERBEROS; @@ -78,7 +74,8 @@ /** * @return true if this authentication supports preemptive authorization */ - boolean supportsPreemptiveAuthorization() { + @Override + public boolean supportsPreemptiveAuthorization() { return false; } @@ -104,14 +101,14 @@ return supported.get(hostname); } - try { - Negotiator neg = Negotiator.getSupported(hci); + Negotiator neg = Negotiator.getNegotiator(hci); + if (neg != null) { supported.put(hostname, true); // the only place cache.put is called. here we can make sure // the object is valid and the oneToken inside is not null cache.put(hostname, neg); return true; - } catch(Exception e) { + } else { supported.put(hostname, false); return false; } @@ -118,20 +115,10 @@ } /** - * @return the name of the HTTP header this authentication wants to set - */ - String getHeaderName() { - if (type == SERVER_AUTHENTICATION) { - return "Authorization"; - } else { - return "Proxy-Authorization"; - } - } - - /** * Not supported. Must use the setHeaders() method */ - String getHeaderValue(URL url, String method) { + @Override + public String getHeaderValue(URL url, String method) { throw new RuntimeException ("getHeaderValue not supported"); } @@ -143,7 +130,8 @@ * returning false means we have to go back to the user to ask for a new * username password. */ - boolean isAuthorizationStale (String header) { + @Override + public boolean isAuthorizationStale (String header) { return false; /* should not be called for Negotiate */ } @@ -155,7 +143,8 @@ * @param raw The raw header field. * @return true if all goes well, false if no headers were set. */ - synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { + @Override + public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { try { String response; @@ -177,7 +166,7 @@ /** * return the first token. * @returns the token - * @throws IOException if Negotiator.getSupported() or + * @throws IOException if Negotiator.getNegotiator() or * Negotiator.firstToken() failed. */ private byte[] firstToken() throws IOException { @@ -191,11 +180,9 @@ } } if (negotiator == null) { - try { - negotiator = Negotiator.getSupported(hci); - } catch(Exception e) { + negotiator = Negotiator.getNegotiator(hci); + if (negotiator == null) { IOException ioe = new IOException("Cannot initialize Negotiator"); - ioe.initCause(e); throw ioe; } } @@ -227,56 +214,4 @@ // // Currently we ignore this header. -} - -/** - * This abstract class is a bridge to connect NegotiteAuthentication and - * NegotiatorImpl, so that JAAS and JGSS calls can be made - */ -abstract class Negotiator { - static Negotiator getSupported(HttpCallerInfo hci) - throws Exception { - - // These lines are equivalent to - // return new NegotiatorImpl(hci); - // The current implementation will make sure NegotiatorImpl is not - // directly referenced when compiling, thus smooth the way of building - // 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; - Constructor c; - try { - clazz = Class.forName("sun.net.www.protocol.http.NegotiatorImpl", true, null); - c = clazz.getConstructor(HttpCallerInfo.class); - } catch (ClassNotFoundException cnfe) { - finest(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) { - finest(roe); - Throwable t = roe.getCause(); - if (t != null && t instanceof Exception) - finest((Exception)t); - throw roe; - } - } - - abstract byte[] firstToken() throws IOException; - - abstract byte[] nextToken(byte[] in) throws IOException; - - static void finest(Exception e) { - PlatformLogger logger = HttpURLConnection.getHttpLogger(); - logger.finest("NegotiateAuthentication: " + e); - } } --- old/src/share/classes/sun/security/jgss/GSSUtil.java Wed Oct 21 15:28:57 2009 +++ new/src/share/classes/sun/security/jgss/GSSUtil.java Wed Oct 21 15:28:56 2009 @@ -237,7 +237,7 @@ CallbackHandler cb = null; if (caller instanceof HttpCaller) { - cb = new sun.net.www.protocol.http.NegotiateCallbackHandler( + cb = new sun.net.www.protocol.http.spnego.NegotiateCallbackHandler( ((HttpCaller)caller).info()); } else { String defaultHandler = --- /dev/null Wed Oct 21 15:28:59 2009 +++ new/src/share/classes/sun/net/www/protocol/http/Negotiator.java Wed Oct 21 15:28:58 2009 @@ -0,0 +1,82 @@ +/* + * 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.io.IOException; +import java.lang.reflect.Constructor; +import sun.util.logging.PlatformLogger; + +/** + * This abstract class is a bridge to connect NegotiteAuthentication and + * NegotiatorImpl, so that JAAS and JGSS calls can be made + */ +public abstract class Negotiator { + static Negotiator getNegotiator(HttpCallerInfo hci) { + + // These lines are equivalent to + // return new NegotiatorImpl(hci); + // The current implementation will make sure NegotiatorImpl is not + // directly referenced when compiling, thus smooth the way of building + // 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; + Constructor c; + try { + clazz = Class.forName("sun.net.www.protocol.http.spnego.NegotiatorImpl", true, null); + c = clazz.getConstructor(HttpCallerInfo.class); + } catch (ClassNotFoundException cnfe) { + finest(cnfe); + return null; + } 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) { + finest(roe); + Throwable t = roe.getCause(); + if (t != null && t instanceof Exception) + finest((Exception)t); + return null; + } + } + + public abstract byte[] firstToken() throws IOException; + + public abstract byte[] nextToken(byte[] in) throws IOException; + + private static void finest(Exception e) { + PlatformLogger logger = HttpURLConnection.getHttpLogger(); + logger.finest("NegotiateAuthentication: " + e); + } +} + --- old/src/share/classes/sun/net/www/protocol/http/NegotiateCallbackHandler.java Wed Oct 21 15:29:01 2009 +++ /dev/null Wed Oct 21 15:29:01 2009 @@ -1,89 +0,0 @@ -/* - * Copyright 2005-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.io.IOException; -import java.net.Authenticator; -import java.net.PasswordAuthentication; -import java.util.Arrays; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; - -/** - * @since 1.6 - * Special callback handler used in JGSS for the HttpCaller. - */ -public class NegotiateCallbackHandler implements CallbackHandler { - - private String username; - private char[] password; - - private final HttpCallerInfo hci; - - public NegotiateCallbackHandler(HttpCallerInfo hci) { - this.hci = hci; - } - - public void handle(Callback[] callbacks) throws - UnsupportedCallbackException, IOException { - for (int i=0; i - *

  • Find out what GSS mechanism to use from the system property - * http.negotiate.mechanism.oid, defaults SPNEGO - *
  • Creating the GSSName for the target host, "HTTP/"+hostname - *
  • Creating GSSContext - *
  • A first call to initSecContext - */ - private void init(HttpCallerInfo hci) throws GSSException { - final Oid oid; - - if (hci.scheme.equalsIgnoreCase("Kerberos")) { - // we can only use Kerberos mech when the scheme is kerberos - oid = GSSUtil.GSS_KRB5_MECH_OID; - } else { - String pref = java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public String run() { - return System.getProperty( - "http.auth.preference", - "spnego"); - } - }); - if (pref.equalsIgnoreCase("kerberos")) { - oid = GSSUtil.GSS_KRB5_MECH_OID; - } else { - // currently there is no 3rd mech we can use - oid = GSSUtil.GSS_SPNEGO_MECH_OID; - } - } - - GSSManagerImpl manager = new GSSManagerImpl( - new HttpCaller(hci)); - - // RFC 4559 4.1 uses uppercase service name "HTTP". - // RFC 4120 6.2.1 demands the host be lowercase - String peerName = "HTTP@" + hci.host.toLowerCase(); - - GSSName serverName = manager.createName(peerName, - GSSName.NT_HOSTBASED_SERVICE); - context = manager.createContext(serverName, - oid, - null, - GSSContext.DEFAULT_LIFETIME); - - // In order to support credential delegation in HTTP/SPNEGO, - // we always request it before initSecContext. The current - // implementation will check the OK-AS-DELEGATE flag inside - // the service ticket of the web server, and only enable - // delegation when this flag is set. This check is only - // performed when the GSS caller is CALLER_HTTP_NEGOTIATE, - // so all other normal GSS-API calls are not affected. - - context.requestCredDeleg(true); - oneToken = context.initSecContext(new byte[0], 0, 0); - } - - /** - * Constructor - * @throws java.io.IOException If negotiator cannot be constructed - */ - public NegotiatorImpl(HttpCallerInfo hci) throws IOException { - try { - init(hci); - } catch (GSSException e) { - if (DEBUG) { - System.out.println("Negotiate support not initiated, will " + - "fallback to other scheme if allowed. Reason:"); - e.printStackTrace(); - } - IOException ioe = new IOException("Negotiate support not initiated"); - ioe.initCause(e); - throw ioe; - } - } - - /** - * Return the first token of GSS, in SPNEGO, it's called NegTokenInit - * @return the first token - */ - public byte[] firstToken() { - return oneToken; - } - - /** - * Return the rest tokens of GSS, in SPNEGO, it's called NegTokenTarg - * @param token the token received from server - * @return the next token - * @throws java.io.IOException if the token cannot be created successfully - */ - public byte[] nextToken(byte[] token) throws IOException { - try { - return context.initSecContext(token, 0, token.length); - } catch (GSSException e) { - if (DEBUG) { - System.out.println("Negotiate support cannot continue. Reason:"); - e.printStackTrace(); - } - IOException ioe = new IOException("Negotiate support cannot continue"); - ioe.initCause(e); - throw ioe; - } - } -} --- /dev/null Wed Oct 21 15:29:03 2009 +++ new/src/share/classes/sun/net/www/protocol/http/spnego/NegotiatorImpl.java Wed Oct 21 15:29:02 2009 @@ -0,0 +1,163 @@ +/* + * Copyright 2005-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.spnego; + +import java.io.IOException; + +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; + +import sun.net.www.protocol.http.HttpCallerInfo; +import sun.net.www.protocol.http.Negotiator; +import sun.security.jgss.GSSManagerImpl; +import sun.security.jgss.GSSUtil; +import sun.security.jgss.HttpCaller; + +/** + * This class encapsulates all JAAS and JGSS API calls in a separate class + * outside NegotiateAuthentication.java so that J2SE build can go smoothly + * without the presence of it. + * + * @author weijun.wang@sun.com + * @since 1.6 + */ +public class NegotiatorImpl extends Negotiator { + + private static final boolean DEBUG = + java.security.AccessController.doPrivileged( + new sun.security.action.GetBooleanAction("sun.security.krb5.debug")); + + private GSSContext context; + private byte[] oneToken; + + /** + * Initialize the object, which includes: + */ + private void init(HttpCallerInfo hci) throws GSSException { + final Oid oid; + + if (hci.scheme.equalsIgnoreCase("Kerberos")) { + // we can only use Kerberos mech when the scheme is kerberos + oid = GSSUtil.GSS_KRB5_MECH_OID; + } else { + String pref = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public String run() { + return System.getProperty( + "http.auth.preference", + "spnego"); + } + }); + if (pref.equalsIgnoreCase("kerberos")) { + oid = GSSUtil.GSS_KRB5_MECH_OID; + } else { + // currently there is no 3rd mech we can use + oid = GSSUtil.GSS_SPNEGO_MECH_OID; + } + } + + GSSManagerImpl manager = new GSSManagerImpl( + new HttpCaller(hci)); + + // RFC 4559 4.1 uses uppercase service name "HTTP". + // RFC 4120 6.2.1 demands the host be lowercase + String peerName = "HTTP@" + hci.host.toLowerCase(); + + GSSName serverName = manager.createName(peerName, + GSSName.NT_HOSTBASED_SERVICE); + context = manager.createContext(serverName, + oid, + null, + GSSContext.DEFAULT_LIFETIME); + + // In order to support credential delegation in HTTP/SPNEGO, + // we always request it before initSecContext. The current + // implementation will check the OK-AS-DELEGATE flag inside + // the service ticket of the web server, and only enable + // delegation when this flag is set. This check is only + // performed when the GSS caller is CALLER_HTTP_NEGOTIATE, + // so all other normal GSS-API calls are not affected. + + context.requestCredDeleg(true); + oneToken = context.initSecContext(new byte[0], 0, 0); + } + + /** + * Constructor + * @throws java.io.IOException If negotiator cannot be constructed + */ + public NegotiatorImpl(HttpCallerInfo hci) throws IOException { + try { + init(hci); + } catch (GSSException e) { + if (DEBUG) { + System.out.println("Negotiate support not initiated, will " + + "fallback to other scheme if allowed. Reason:"); + e.printStackTrace(); + } + IOException ioe = new IOException("Negotiate support not initiated"); + ioe.initCause(e); + throw ioe; + } + } + + /** + * Return the first token of GSS, in SPNEGO, it's called NegTokenInit + * @return the first token + */ + @Override + public byte[] firstToken() { + return oneToken; + } + + /** + * Return the rest tokens of GSS, in SPNEGO, it's called NegTokenTarg + * @param token the token received from server + * @return the next token + * @throws java.io.IOException if the token cannot be created successfully + */ + @Override + public byte[] nextToken(byte[] token) throws IOException { + try { + return context.initSecContext(token, 0, token.length); + } catch (GSSException e) { + if (DEBUG) { + System.out.println("Negotiate support cannot continue. Reason:"); + e.printStackTrace(); + } + IOException ioe = new IOException("Negotiate support cannot continue"); + ioe.initCause(e); + throw ioe; + } + } +} --- old/src/solaris/classes/sun/net/www/protocol/http/NTLMAuthentication.java Wed Oct 21 15:29:05 2009 +++ /dev/null Wed Oct 21 15:29:05 2009 @@ -1,427 +0,0 @@ -/* - * Copyright 2005-2008 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.io.IOException; -import java.io.UnsupportedEncodingException; -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; - -/** - * NTLMAuthentication: - * - * @author Michael McMahon - */ - -/* - * NTLM authentication is nominally based on the framework defined in RFC2617, - * but differs from the standard (Basic & Digest) schemes as follows: - * - * 1. A complete authentication requires three request/response transactions - * as shown below: - * REQ -------------------------------> - * <---- 401 (signalling NTLM) -------- - * - * REQ (with type1 NTLM msg) ---------> - * <---- 401 (with type 2 NTLM msg) --- - * - * REQ (with type3 NTLM msg) ---------> - * <---- OK --------------------------- - * - * 2. The scope of the authentication is the TCP connection (which must be kept-alive) - * after the type2 response is received. This means that NTLM does not work end-to-end - * through a proxy, rather between client and proxy, or between client and server (with no proxy) - */ - -class NTLMAuthentication extends AuthenticationInfo { - private static final long serialVersionUID = -2403849171106437142L; - - private byte[] type1; - private byte[] type3; - - private SecretKeyFactory fac; - private Cipher cipher; - private MessageDigest md4; - private String hostname; - private static String defaultDomain; /* Domain to use if not specified by user */ - - static { - defaultDomain = java.security.AccessController.doPrivileged( - new sun.security.action.GetPropertyAction("http.auth.ntlm.domain", - "domain")); - }; - - static boolean supportsTransparentAuth () { - return false; - } - - private void init0() { - type1 = new byte[256]; - type3 = new byte[256]; - System.arraycopy (new byte[] {'N','T','L','M','S','S','P',0,1}, 0, type1, 0, 9); - type1[12] = (byte) 3; - type1[13] = (byte) 0xb2; - type1[28] = (byte) 0x20; - System.arraycopy (new byte[] {'N','T','L','M','S','S','P',0,3}, 0, type3, 0, 9); - type3[12] = (byte) 0x18; - type3[14] = (byte) 0x18; - type3[20] = (byte) 0x18; - type3[22] = (byte) 0x18; - type3[32] = (byte) 0x40; - type3[60] = (byte) 1; - type3[61] = (byte) 0x82; - - try { - hostname = java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public String run() { - String localhost; - try { - localhost = InetAddress.getLocalHost().getHostName().toUpperCase(); - } catch (UnknownHostException e) { - localhost = "localhost"; - } - return localhost; - } - }); - int x = hostname.indexOf ('.'); - if (x != -1) { - hostname = hostname.substring (0, x); - } - fac = SecretKeyFactory.getInstance ("DES"); - cipher = Cipher.getInstance ("DES/ECB/NoPadding"); - md4 = sun.security.provider.MD4.getInstance(); - } catch (NoSuchPaddingException e) { - assert false; - } catch (NoSuchAlgorithmException e) { - assert false; - } - }; - - PasswordAuthentication pw; - String username; - String ntdomain; - String password; - - /** - * Create a NTLMAuthentication: - * Username may be specified as domainusername in the application Authenticator. - * If this notation is not used, then the domain will be taken - * from a system property: "http.auth.ntlm.domain". - */ - public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw) { - super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, - AuthScheme.NTLM, - url, - ""); - init (pw); - } - - private void init (PasswordAuthentication pw) { - this.pw = pw; - String s = pw.getUserName(); - int i = s.indexOf ('\\'); - if (i == -1) { - username = s; - ntdomain = defaultDomain; - } else { - ntdomain = s.substring (0, i).toUpperCase(); - username = s.substring (i+1); - } - password = new String (pw.getPassword()); - init0(); - } - - /** - * Constructor used for proxy entries - */ - public NTLMAuthentication(boolean isProxy, String host, int port, - PasswordAuthentication pw) { - super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, - AuthScheme.NTLM, - host, - port, - ""); - init (pw); - } - - /** - * @return true if this authentication supports preemptive authorization - */ - boolean supportsPreemptiveAuthorization() { - return false; - } - - /** - * @return the name of the HTTP header this authentication wants set - */ - String getHeaderName() { - if (type == SERVER_AUTHENTICATION) { - return "Authorization"; - } else { - return "Proxy-authorization"; - } - } - - /** - * Not supported. Must use the setHeaders() method - */ - String getHeaderValue(URL url, String method) { - throw new RuntimeException ("getHeaderValue not supported"); - } - - /** - * Check if the header indicates that the current auth. parameters are stale. - * If so, then replace the relevant field with the new value - * and return true. Otherwise return false. - * returning true means the request can be retried with the same userid/password - * returning false means we have to go back to the user to ask for a new - * username password. - */ - boolean isAuthorizationStale (String header) { - return false; /* should not be called for ntlm */ - } - - /** - * Set header(s) on the given connection. - * @param conn The connection to apply the header(s) to - * @param p A source of header values for this connection, not used because - * HeaderParser converts the fields to lower case, use raw instead - * @param raw The raw header field. - * @return true if all goes well, false if no headers were set. - */ - synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { - - try { - String response; - if (raw.length() < 6) { /* NTLM */ - response = buildType1Msg (); - } else { - String msg = raw.substring (5); /* skip NTLM */ - response = buildType3Msg (msg); - } - conn.setAuthenticationProperty(getHeaderName(), response); - return true; - } catch (IOException e) { - return false; - } catch (GeneralSecurityException e) { - return false; - } - } - - private void copybytes (byte[] dest, int destpos, String src, String enc) { - try { - byte[] x = src.getBytes(enc); - System.arraycopy (x, 0, dest, destpos, x.length); - } catch (UnsupportedEncodingException e) { - assert false; - } - } - - private String buildType1Msg () { - int dlen = ntdomain.length(); - type1[16]= (byte) (dlen % 256); - type1[17]= (byte) (dlen / 256); - type1[18] = type1[16]; - type1[19] = type1[17]; - - int hlen = hostname.length(); - type1[24]= (byte) (hlen % 256); - type1[25]= (byte) (hlen / 256); - type1[26] = type1[24]; - type1[27] = type1[25]; - - copybytes (type1, 32, hostname, "ISO8859_1"); - copybytes (type1, hlen+32, ntdomain, "ISO8859_1"); - type1[20] = (byte) ((hlen+32) % 256); - type1[21] = (byte) ((hlen+32) / 256); - - byte[] msg = new byte [32 + hlen + dlen]; - System.arraycopy (type1, 0, msg, 0, 32 + hlen + dlen); - String result = "NTLM " + (new B64Encoder()).encode (msg); - return result; - } - - - /* Convert a 7 byte array to an 8 byte array (for a des key with parity) - * input starts at offset off - */ - private byte[] makeDesKey (byte[] input, int off) { - int[] in = new int [input.length]; - for (int i=0; i> 1)); - out[2] = (byte)(((in[off+1] << 6) & 0xFF) | (in[off+2] >> 2)); - out[3] = (byte)(((in[off+2] << 5) & 0xFF) | (in[off+3] >> 3)); - out[4] = (byte)(((in[off+3] << 4) & 0xFF) | (in[off+4] >> 4)); - out[5] = (byte)(((in[off+4] << 3) & 0xFF) | (in[off+5] >> 5)); - out[6] = (byte)(((in[off+5] << 2) & 0xFF) | (in[off+6] >> 6)); - out[7] = (byte)((in[off+6] << 1) & 0xFF); - return out; - } - - private byte[] calcLMHash () throws GeneralSecurityException { - byte[] magic = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; - byte[] pwb = password.toUpperCase ().getBytes(); - byte[] pwb1 = new byte [14]; - int len = password.length(); - if (len > 14) - len = 14; - System.arraycopy (pwb, 0, pwb1, 0, len); /* Zero padded */ - - DESKeySpec dks1 = new DESKeySpec (makeDesKey (pwb1, 0)); - DESKeySpec dks2 = new DESKeySpec (makeDesKey (pwb1, 7)); - - SecretKey key1 = fac.generateSecret (dks1); - SecretKey key2 = fac.generateSecret (dks2); - cipher.init (Cipher.ENCRYPT_MODE, key1); - byte[] out1 = cipher.doFinal (magic, 0, 8); - cipher.init (Cipher.ENCRYPT_MODE, key2); - byte[] out2 = cipher.doFinal (magic, 0, 8); - - byte[] result = new byte [21]; - System.arraycopy (out1, 0, result, 0, 8); - System.arraycopy (out2, 0, result, 8, 8); - return result; - } - - private byte[] calcNTHash () throws GeneralSecurityException { - byte[] pw = null; - try { - pw = password.getBytes ("UnicodeLittleUnmarked"); - } catch (UnsupportedEncodingException e) { - assert false; - } - byte[] out = md4.digest (pw); - byte[] result = new byte [21]; - System.arraycopy (out, 0, result, 0, 16); - return result; - } - - /* key is a 21 byte array. Split it into 3 7 byte chunks, - * Convert each to 8 byte DES keys, encrypt the text arg with - * each key and return the three results in a sequential [] - */ - private byte[] calcResponse (byte[] key, byte[] text) - throws GeneralSecurityException { - assert key.length == 21; - DESKeySpec dks1 = new DESKeySpec (makeDesKey (key, 0)); - DESKeySpec dks2 = new DESKeySpec (makeDesKey (key, 7)); - DESKeySpec dks3 = new DESKeySpec (makeDesKey (key, 14)); - SecretKey key1 = fac.generateSecret (dks1); - SecretKey key2 = fac.generateSecret (dks2); - SecretKey key3 = fac.generateSecret (dks3); - cipher.init (Cipher.ENCRYPT_MODE, key1); - byte[] out1 = cipher.doFinal (text, 0, 8); - cipher.init (Cipher.ENCRYPT_MODE, key2); - byte[] out2 = cipher.doFinal (text, 0, 8); - cipher.init (Cipher.ENCRYPT_MODE, key3); - byte[] out3 = cipher.doFinal (text, 0, 8); - byte[] result = new byte [24]; - System.arraycopy (out1, 0, result, 0, 8); - System.arraycopy (out2, 0, result, 8, 8); - System.arraycopy (out3, 0, result, 16, 8); - return result; - } - - private String buildType3Msg (String challenge) throws GeneralSecurityException, - IOException { - /* First decode the type2 message to get the server nonce */ - /* nonce is located at type2[24] for 8 bytes */ - - byte[] type2 = (new sun.misc.BASE64Decoder()).decodeBuffer (challenge); - byte[] nonce = new byte [8]; - System.arraycopy (type2, 24, nonce, 0, 8); - - int ulen = username.length()*2; - type3[36] = type3[38] = (byte) (ulen % 256); - type3[37] = type3[39] = (byte) (ulen / 256); - int dlen = ntdomain.length()*2; - type3[28] = type3[30] = (byte) (dlen % 256); - type3[29] = type3[31] = (byte) (dlen / 256); - int hlen = hostname.length()*2; - type3[44] = type3[46] = (byte) (hlen % 256); - type3[45] = type3[47] = (byte) (hlen / 256); - - int l = 64; - copybytes (type3, l, ntdomain, "UnicodeLittleUnmarked"); - type3[32] = (byte) (l % 256); - type3[33] = (byte) (l / 256); - l += dlen; - copybytes (type3, l, username, "UnicodeLittleUnmarked"); - type3[40] = (byte) (l % 256); - type3[41] = (byte) (l / 256); - l += ulen; - copybytes (type3, l, hostname, "UnicodeLittleUnmarked"); - type3[48] = (byte) (l % 256); - type3[49] = (byte) (l / 256); - l += hlen; - - byte[] lmhash = calcLMHash(); - byte[] lmresponse = calcResponse (lmhash, nonce); - byte[] nthash = calcNTHash(); - byte[] ntresponse = calcResponse (nthash, nonce); - System.arraycopy (lmresponse, 0, type3, l, 24); - type3[16] = (byte) (l % 256); - type3[17] = (byte) (l / 256); - l += 24; - System.arraycopy (ntresponse, 0, type3, l, 24); - type3[24] = (byte) (l % 256); - type3[25] = (byte) (l / 256); - l += 24; - type3[56] = (byte) (l % 256); - type3[57] = (byte) (l / 256); - - byte[] msg = new byte [l]; - System.arraycopy (type3, 0, msg, 0, l); - String result = "NTLM " + (new B64Encoder()).encode (msg); - return result; - } - -} - - -class B64Encoder extends sun.misc.BASE64Encoder { - /* to force it to to the entire encoding in one line */ - protected int bytesPerLine () { - return 1024; - } -} --- /dev/null Wed Oct 21 15:29:05 2009 +++ new/src/solaris/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java Wed Oct 21 15:29:04 2009 @@ -0,0 +1,423 @@ +/* + * Copyright 2005-2008 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.ntlm; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +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.protocol.http.AuthenticationInfo; +import sun.net.www.protocol.http.AuthScheme; +import sun.net.www.protocol.http.HttpURLConnection; + +/** + * NTLMAuthentication: + * + * @author Michael McMahon + */ + +/* + * NTLM authentication is nominally based on the framework defined in RFC2617, + * but differs from the standard (Basic & Digest) schemes as follows: + * + * 1. A complete authentication requires three request/response transactions + * as shown below: + * REQ -------------------------------> + * <---- 401 (signalling NTLM) -------- + * + * REQ (with type1 NTLM msg) ---------> + * <---- 401 (with type 2 NTLM msg) --- + * + * REQ (with type3 NTLM msg) ---------> + * <---- OK --------------------------- + * + * 2. The scope of the authentication is the TCP connection (which must be kept-alive) + * after the type2 response is received. This means that NTLM does not work end-to-end + * through a proxy, rather between client and proxy, or between client and server (with no proxy) + */ + +public class NTLMAuthentication extends AuthenticationInfo { + private static final long serialVersionUID = -2403849171106437142L; + + private byte[] type1; + private byte[] type3; + + private SecretKeyFactory fac; + private Cipher cipher; + private MessageDigest md4; + private String hostname; + private static String defaultDomain; /* Domain to use if not specified by user */ + + static { + defaultDomain = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("http.auth.ntlm.domain", + "domain")); + }; + + public static boolean supportsTransparentAuth () { + return false; + } + + private void init0() { + type1 = new byte[256]; + type3 = new byte[256]; + System.arraycopy (new byte[] {'N','T','L','M','S','S','P',0,1}, 0, type1, 0, 9); + type1[12] = (byte) 3; + type1[13] = (byte) 0xb2; + type1[28] = (byte) 0x20; + System.arraycopy (new byte[] {'N','T','L','M','S','S','P',0,3}, 0, type3, 0, 9); + type3[12] = (byte) 0x18; + type3[14] = (byte) 0x18; + type3[20] = (byte) 0x18; + type3[22] = (byte) 0x18; + type3[32] = (byte) 0x40; + type3[60] = (byte) 1; + type3[61] = (byte) 0x82; + + try { + hostname = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public String run() { + String localhost; + try { + localhost = InetAddress.getLocalHost().getHostName().toUpperCase(); + } catch (UnknownHostException e) { + localhost = "localhost"; + } + return localhost; + } + }); + int x = hostname.indexOf ('.'); + if (x != -1) { + hostname = hostname.substring (0, x); + } + fac = SecretKeyFactory.getInstance ("DES"); + cipher = Cipher.getInstance ("DES/ECB/NoPadding"); + md4 = sun.security.provider.MD4.getInstance(); + } catch (NoSuchPaddingException e) { + assert false; + } catch (NoSuchAlgorithmException e) { + assert false; + } + }; + + PasswordAuthentication pw; + String username; + String ntdomain; + String password; + + /** + * Create a NTLMAuthentication: + * Username may be specified as domainusername in the application Authenticator. + * If this notation is not used, then the domain will be taken + * from a system property: "http.auth.ntlm.domain". + */ + public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw) { + super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, + AuthScheme.NTLM, + url, + ""); + init (pw); + } + + private void init (PasswordAuthentication pw) { + this.pw = pw; + String s = pw.getUserName(); + int i = s.indexOf ('\\'); + if (i == -1) { + username = s; + ntdomain = defaultDomain; + } else { + ntdomain = s.substring (0, i).toUpperCase(); + username = s.substring (i+1); + } + password = new String (pw.getPassword()); + init0(); + } + + /** + * Constructor used for proxy entries + */ + public NTLMAuthentication(boolean isProxy, String host, int port, + PasswordAuthentication pw) { + super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, + AuthScheme.NTLM, + host, + port, + ""); + init (pw); + } + + /** + * @return true if this authentication supports preemptive authorization + */ + @Override + public boolean supportsPreemptiveAuthorization() { + return false; + } + + /** + * Not supported. Must use the setHeaders() method + */ + @Override + public String getHeaderValue(URL url, String method) { + throw new RuntimeException ("getHeaderValue not supported"); + } + + /** + * Check if the header indicates that the current auth. parameters are stale. + * If so, then replace the relevant field with the new value + * and return true. Otherwise return false. + * returning true means the request can be retried with the same userid/password + * returning false means we have to go back to the user to ask for a new + * username password. + */ + @Override + public boolean isAuthorizationStale (String header) { + return false; /* should not be called for ntlm */ + } + + /** + * Set header(s) on the given connection. + * @param conn The connection to apply the header(s) to + * @param p A source of header values for this connection, not used because + * HeaderParser converts the fields to lower case, use raw instead + * @param raw The raw header field. + * @return true if all goes well, false if no headers were set. + */ + @Override + public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { + + try { + String response; + if (raw.length() < 6) { /* NTLM */ + response = buildType1Msg (); + } else { + String msg = raw.substring (5); /* skip NTLM */ + response = buildType3Msg (msg); + } + conn.setAuthenticationProperty(getHeaderName(), response); + return true; + } catch (IOException e) { + return false; + } catch (GeneralSecurityException e) { + return false; + } + } + + private void copybytes (byte[] dest, int destpos, String src, String enc) { + try { + byte[] x = src.getBytes(enc); + System.arraycopy (x, 0, dest, destpos, x.length); + } catch (UnsupportedEncodingException e) { + assert false; + } + } + + private String buildType1Msg () { + int dlen = ntdomain.length(); + type1[16]= (byte) (dlen % 256); + type1[17]= (byte) (dlen / 256); + type1[18] = type1[16]; + type1[19] = type1[17]; + + int hlen = hostname.length(); + type1[24]= (byte) (hlen % 256); + type1[25]= (byte) (hlen / 256); + type1[26] = type1[24]; + type1[27] = type1[25]; + + copybytes (type1, 32, hostname, "ISO8859_1"); + copybytes (type1, hlen+32, ntdomain, "ISO8859_1"); + type1[20] = (byte) ((hlen+32) % 256); + type1[21] = (byte) ((hlen+32) / 256); + + byte[] msg = new byte [32 + hlen + dlen]; + System.arraycopy (type1, 0, msg, 0, 32 + hlen + dlen); + String result = "NTLM " + (new B64Encoder()).encode (msg); + return result; + } + + + /* Convert a 7 byte array to an 8 byte array (for a des key with parity) + * input starts at offset off + */ + private byte[] makeDesKey (byte[] input, int off) { + int[] in = new int [input.length]; + for (int i=0; i> 1)); + out[2] = (byte)(((in[off+1] << 6) & 0xFF) | (in[off+2] >> 2)); + out[3] = (byte)(((in[off+2] << 5) & 0xFF) | (in[off+3] >> 3)); + out[4] = (byte)(((in[off+3] << 4) & 0xFF) | (in[off+4] >> 4)); + out[5] = (byte)(((in[off+4] << 3) & 0xFF) | (in[off+5] >> 5)); + out[6] = (byte)(((in[off+5] << 2) & 0xFF) | (in[off+6] >> 6)); + out[7] = (byte)((in[off+6] << 1) & 0xFF); + return out; + } + + private byte[] calcLMHash () throws GeneralSecurityException { + byte[] magic = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; + byte[] pwb = password.toUpperCase ().getBytes(); + byte[] pwb1 = new byte [14]; + int len = password.length(); + if (len > 14) + len = 14; + System.arraycopy (pwb, 0, pwb1, 0, len); /* Zero padded */ + + DESKeySpec dks1 = new DESKeySpec (makeDesKey (pwb1, 0)); + DESKeySpec dks2 = new DESKeySpec (makeDesKey (pwb1, 7)); + + SecretKey key1 = fac.generateSecret (dks1); + SecretKey key2 = fac.generateSecret (dks2); + cipher.init (Cipher.ENCRYPT_MODE, key1); + byte[] out1 = cipher.doFinal (magic, 0, 8); + cipher.init (Cipher.ENCRYPT_MODE, key2); + byte[] out2 = cipher.doFinal (magic, 0, 8); + + byte[] result = new byte [21]; + System.arraycopy (out1, 0, result, 0, 8); + System.arraycopy (out2, 0, result, 8, 8); + return result; + } + + private byte[] calcNTHash () throws GeneralSecurityException { + byte[] pw = null; + try { + pw = password.getBytes ("UnicodeLittleUnmarked"); + } catch (UnsupportedEncodingException e) { + assert false; + } + byte[] out = md4.digest (pw); + byte[] result = new byte [21]; + System.arraycopy (out, 0, result, 0, 16); + return result; + } + + /* key is a 21 byte array. Split it into 3 7 byte chunks, + * Convert each to 8 byte DES keys, encrypt the text arg with + * each key and return the three results in a sequential [] + */ + private byte[] calcResponse (byte[] key, byte[] text) + throws GeneralSecurityException { + assert key.length == 21; + DESKeySpec dks1 = new DESKeySpec (makeDesKey (key, 0)); + DESKeySpec dks2 = new DESKeySpec (makeDesKey (key, 7)); + DESKeySpec dks3 = new DESKeySpec (makeDesKey (key, 14)); + SecretKey key1 = fac.generateSecret (dks1); + SecretKey key2 = fac.generateSecret (dks2); + SecretKey key3 = fac.generateSecret (dks3); + cipher.init (Cipher.ENCRYPT_MODE, key1); + byte[] out1 = cipher.doFinal (text, 0, 8); + cipher.init (Cipher.ENCRYPT_MODE, key2); + byte[] out2 = cipher.doFinal (text, 0, 8); + cipher.init (Cipher.ENCRYPT_MODE, key3); + byte[] out3 = cipher.doFinal (text, 0, 8); + byte[] result = new byte [24]; + System.arraycopy (out1, 0, result, 0, 8); + System.arraycopy (out2, 0, result, 8, 8); + System.arraycopy (out3, 0, result, 16, 8); + return result; + } + + private String buildType3Msg (String challenge) throws GeneralSecurityException, + IOException { + /* First decode the type2 message to get the server nonce */ + /* nonce is located at type2[24] for 8 bytes */ + + byte[] type2 = (new sun.misc.BASE64Decoder()).decodeBuffer (challenge); + byte[] nonce = new byte [8]; + System.arraycopy (type2, 24, nonce, 0, 8); + + int ulen = username.length()*2; + type3[36] = type3[38] = (byte) (ulen % 256); + type3[37] = type3[39] = (byte) (ulen / 256); + int dlen = ntdomain.length()*2; + type3[28] = type3[30] = (byte) (dlen % 256); + type3[29] = type3[31] = (byte) (dlen / 256); + int hlen = hostname.length()*2; + type3[44] = type3[46] = (byte) (hlen % 256); + type3[45] = type3[47] = (byte) (hlen / 256); + + int l = 64; + copybytes (type3, l, ntdomain, "UnicodeLittleUnmarked"); + type3[32] = (byte) (l % 256); + type3[33] = (byte) (l / 256); + l += dlen; + copybytes (type3, l, username, "UnicodeLittleUnmarked"); + type3[40] = (byte) (l % 256); + type3[41] = (byte) (l / 256); + l += ulen; + copybytes (type3, l, hostname, "UnicodeLittleUnmarked"); + type3[48] = (byte) (l % 256); + type3[49] = (byte) (l / 256); + l += hlen; + + byte[] lmhash = calcLMHash(); + byte[] lmresponse = calcResponse (lmhash, nonce); + byte[] nthash = calcNTHash(); + byte[] ntresponse = calcResponse (nthash, nonce); + System.arraycopy (lmresponse, 0, type3, l, 24); + type3[16] = (byte) (l % 256); + type3[17] = (byte) (l / 256); + l += 24; + System.arraycopy (ntresponse, 0, type3, l, 24); + type3[24] = (byte) (l % 256); + type3[25] = (byte) (l / 256); + l += 24; + type3[56] = (byte) (l % 256); + type3[57] = (byte) (l / 256); + + byte[] msg = new byte [l]; + System.arraycopy (type3, 0, msg, 0, l); + String result = "NTLM " + (new B64Encoder()).encode (msg); + return result; + } + +} + + +class B64Encoder extends sun.misc.BASE64Encoder { + /* to force it to to the entire encoding in one line */ + protected int bytesPerLine () { + return 1024; + } +} --- old/src/windows/classes/sun/net/www/protocol/http/NTLMAuthSequence.java Wed Oct 21 15:29:07 2009 +++ /dev/null Wed Oct 21 15:29:07 2009 @@ -1,85 +0,0 @@ -/* - * Copyright 2002 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.io.IOException; -import sun.misc.BASE64Encoder; -import sun.misc.BASE64Decoder; - -/* - * Hooks into Windows implementation of NTLM. - * This class will be replaced if a cross-platform version of NTLM - * is implemented in the future. - */ - -public class NTLMAuthSequence { - - private String username; - private String password; - private String ntdomain; - private int state; - private long crdHandle; - private long ctxHandle; - - static { - initFirst(); - } - - NTLMAuthSequence (String username, String password, String ntdomain) - throws IOException - { - this.username = username; - this.password = password; - this.ntdomain = ntdomain; - state = 0; - crdHandle = getCredentialsHandle (username, ntdomain, password); - if (crdHandle == 0) { - throw new IOException ("could not get credentials handle"); - } - } - - public String getAuthHeader (String token) throws IOException { - byte[] input = null; - if (token != null) - input = (new BASE64Decoder()).decodeBuffer(token); - byte[] b = getNextToken (crdHandle, input); - if (b == null) - throw new IOException ("Internal authentication error"); - return (new B64Encoder()).encode (b); - } - - private native static void initFirst (); - - private native long getCredentialsHandle (String user, String domain, String password); - - private native byte[] getNextToken (long crdHandle, byte[] lastToken); -} - -class B64Encoder extends BASE64Encoder { - protected int bytesPerLine () { - return 1024; - } -} --- /dev/null Wed Oct 21 15:29:07 2009 +++ new/src/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthSequence.java Wed Oct 21 15:29:06 2009 @@ -0,0 +1,85 @@ +/* + * Copyright 2002 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.ntlm; + +import java.io.IOException; +import sun.misc.BASE64Encoder; +import sun.misc.BASE64Decoder; + +/* + * Hooks into Windows implementation of NTLM. + * This class will be replaced if a cross-platform version of NTLM + * is implemented in the future. + */ + +public class NTLMAuthSequence { + + private String username; + private String password; + private String ntdomain; + private int state; + private long crdHandle; + private long ctxHandle; + + static { + initFirst(); + } + + NTLMAuthSequence (String username, String password, String ntdomain) + throws IOException + { + this.username = username; + this.password = password; + this.ntdomain = ntdomain; + state = 0; + crdHandle = getCredentialsHandle (username, ntdomain, password); + if (crdHandle == 0) { + throw new IOException ("could not get credentials handle"); + } + } + + public String getAuthHeader (String token) throws IOException { + byte[] input = null; + if (token != null) + input = (new BASE64Decoder()).decodeBuffer(token); + byte[] b = getNextToken (crdHandle, input); + if (b == null) + throw new IOException ("Internal authentication error"); + return (new B64Encoder()).encode (b); + } + + private native static void initFirst (); + + private native long getCredentialsHandle (String user, String domain, String password); + + private native byte[] getNextToken (long crdHandle, byte[] lastToken); +} + +class B64Encoder extends BASE64Encoder { + protected int bytesPerLine () { + return 1024; + } +} --- old/src/windows/classes/sun/net/www/protocol/http/NTLMAuthentication.java Wed Oct 21 15:29:09 2009 +++ /dev/null Wed Oct 21 15:29:09 2009 @@ -1,195 +0,0 @@ -/* - * Copyright 2002-2005 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.io.IOException; -import java.net.InetAddress; -import java.net.PasswordAuthentication; -import java.net.UnknownHostException; -import java.net.URL; -import sun.net.www.HeaderParser; - -/** - * NTLMAuthentication: - * - * @author Michael McMahon - */ - -class NTLMAuthentication extends AuthenticationInfo { - - private static final long serialVersionUID = 100L; - - private String hostname; - private static String defaultDomain; /* Domain to use if not specified by user */ - - static { - defaultDomain = java.security.AccessController.doPrivileged( - new sun.security.action.GetPropertyAction("http.auth.ntlm.domain", - "domain")); - }; - - private void init0() { - - hostname = java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public String run() { - String localhost; - try { - localhost = InetAddress.getLocalHost().getHostName().toUpperCase(); - } catch (UnknownHostException e) { - localhost = "localhost"; - } - return localhost; - } - }); - int x = hostname.indexOf ('.'); - if (x != -1) { - hostname = hostname.substring (0, x); - } - } - - String username; - String ntdomain; - String password; - - /** - * Create a NTLMAuthentication: - * Username may be specified as domainusername in the application Authenticator. - * If this notation is not used, then the domain will be taken - * from a system property: "http.auth.ntlm.domain". - */ - public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw) { - super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, - AuthScheme.NTLM, - url, - ""); - init (pw); - } - - private void init (PasswordAuthentication pw) { - this.pw = pw; - if (pw != null) { - String s = pw.getUserName(); - int i = s.indexOf ('\\'); - if (i == -1) { - username = s; - ntdomain = defaultDomain; - } else { - ntdomain = s.substring (0, i).toUpperCase(); - username = s.substring (i+1); - } - password = new String (pw.getPassword()); - } else { - /* credentials will be acquired from OS */ - username = null; - ntdomain = null; - password = null; - } - init0(); - } - - /** - * Constructor used for proxy entries - */ - public NTLMAuthentication(boolean isProxy, String host, int port, - PasswordAuthentication pw) { - super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION, - AuthScheme.NTLM, - host, - port, - ""); - init (pw); - } - - /** - * @return true if this authentication supports preemptive authorization - */ - boolean supportsPreemptiveAuthorization() { - return false; - } - - /** - * @return true if NTLM supported transparently (no password needed, SSO) - */ - static boolean supportsTransparentAuth() { - return true; - } - - /** - * @return the name of the HTTP header this authentication wants set - */ - String getHeaderName() { - if (type == SERVER_AUTHENTICATION) { - return "Authorization"; - } else { - return "Proxy-authorization"; - } - } - - /** - * Not supported. Must use the setHeaders() method - */ - String getHeaderValue(URL url, String method) { - throw new RuntimeException ("getHeaderValue not supported"); - } - - /** - * Check if the header indicates that the current auth. parameters are stale. - * If so, then replace the relevant field with the new value - * and return true. Otherwise return false. - * returning true means the request can be retried with the same userid/password - * returning false means we have to go back to the user to ask for a new - * username password. - */ - boolean isAuthorizationStale (String header) { - return false; /* should not be called for ntlm */ - } - - /** - * Set header(s) on the given connection. - * @param conn The connection to apply the header(s) to - * @param p A source of header values for this connection, not used because - * HeaderParser converts the fields to lower case, use raw instead - * @param raw The raw header field. - * @return true if all goes well, false if no headers were set. - */ - synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { - - try { - NTLMAuthSequence seq = (NTLMAuthSequence)conn.authObj; - if (seq == null) { - seq = new NTLMAuthSequence (username, password, ntdomain); - conn.authObj = seq; - } - String response = "NTLM " + seq.getAuthHeader (raw.length()>6?raw.substring(5):null); - conn.setAuthenticationProperty(getHeaderName(), response); - return true; - } catch (IOException e) { - return false; - } - } - -} --- /dev/null Wed Oct 21 15:29:09 2009 +++ new/src/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java Wed Oct 21 15:29:08 2009 @@ -0,0 +1,191 @@ +/* + * Copyright 2002-2005 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.ntlm; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.PasswordAuthentication; +import java.net.UnknownHostException; +import java.net.URL; +import sun.net.www.HeaderParser; +import sun.net.www.protocol.http.AuthenticationInfo; +import sun.net.www.protocol.http.AuthScheme; +import sun.net.www.protocol.http.HttpURLConnection; + +/** + * NTLMAuthentication: + * + * @author Michael McMahon + */ + +public class NTLMAuthentication extends AuthenticationInfo { + + private static final long serialVersionUID = 100L; + + private String hostname; + private static String defaultDomain; /* Domain to use if not specified by user */ + + static { + defaultDomain = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("http.auth.ntlm.domain", + "domain")); + }; + + private void init0() { + + hostname = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public String run() { + String localhost; + try { + localhost = InetAddress.getLocalHost().getHostName().toUpperCase(); + } catch (UnknownHostException e) { + localhost = "localhost"; + } + return localhost; + } + }); + int x = hostname.indexOf ('.'); + if (x != -1) { + hostname = hostname.substring (0, x); + } + } + + String username; + String ntdomain; + String password; + + /** + * Create a NTLMAuthentication: + * Username may be specified as domainusername in the application Authenticator. + * If this notation is not used, then the domain will be taken + * from a system property: "http.auth.ntlm.domain". + */ + public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw) { + super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, + AuthScheme.NTLM, + url, + ""); + init (pw); + } + + private void init (PasswordAuthentication pw) { + this.pw = pw; + if (pw != null) { + String s = pw.getUserName(); + int i = s.indexOf ('\\'); + if (i == -1) { + username = s; + ntdomain = defaultDomain; + } else { + ntdomain = s.substring (0, i).toUpperCase(); + username = s.substring (i+1); + } + password = new String (pw.getPassword()); + } else { + /* credentials will be acquired from OS */ + username = null; + ntdomain = null; + password = null; + } + init0(); + } + + /** + * Constructor used for proxy entries + */ + public NTLMAuthentication(boolean isProxy, String host, int port, + PasswordAuthentication pw) { + super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION, + AuthScheme.NTLM, + host, + port, + ""); + init (pw); + } + + /** + * @return true if this authentication supports preemptive authorization + */ + @Override + public boolean supportsPreemptiveAuthorization() { + return false; + } + + /** + * @return true if NTLM supported transparently (no password needed, SSO) + */ + public static boolean supportsTransparentAuth() { + return true; + } + + /** + * Not supported. Must use the setHeaders() method + */ + @Override + public String getHeaderValue(URL url, String method) { + throw new RuntimeException ("getHeaderValue not supported"); + } + + /** + * Check if the header indicates that the current auth. parameters are stale. + * If so, then replace the relevant field with the new value + * and return true. Otherwise return false. + * returning true means the request can be retried with the same userid/password + * returning false means we have to go back to the user to ask for a new + * username password. + */ + @Override + public boolean isAuthorizationStale (String header) { + return false; /* should not be called for ntlm */ + } + + /** + * Set header(s) on the given connection. + * @param conn The connection to apply the header(s) to + * @param p A source of header values for this connection, not used because + * HeaderParser converts the fields to lower case, use raw instead + * @param raw The raw header field. + * @return true if all goes well, false if no headers were set. + */ + @Override + public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { + + try { + NTLMAuthSequence seq = (NTLMAuthSequence)conn.authObj(); + if (seq == null) { + seq = new NTLMAuthSequence (username, password, ntdomain); + conn.authObj(seq); + } + String response = "NTLM " + seq.getAuthHeader (raw.length()>6?raw.substring(5):null); + conn.setAuthenticationProperty(getHeaderName(), response); + return true; + } catch (IOException e) { + return false; + } + } + +} --- old/src/windows/native/sun/net/www/protocol/http/NTLMAuthSequence.c Wed Oct 21 15:29:11 2009 +++ /dev/null Wed Oct 21 15:29:11 2009 @@ -1,311 +0,0 @@ -/* - * Copyright 2002-2008 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. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "jni_util.h" - -#define SECURITY_WIN32 -#include "sspi.h" - - -/* - * OS calls loaded from DLL on intialization - */ - -static FREE_CREDENTIALS_HANDLE_FN pFreeCredentialsHandle; -static ACQUIRE_CREDENTIALS_HANDLE_FN pAcquireCredentialsHandle; -static FREE_CONTEXT_BUFFER_FN pFreeContextBuffer; -static INITIALIZE_SECURITY_CONTEXT_FN pInitializeSecurityContext; -static COMPLETE_AUTH_TOKEN_FN pCompleteAuthToken; -static DELETE_SECURITY_CONTEXT_FN pDeleteSecurityContext; - -static void endSequence (PCredHandle credHand, PCtxtHandle ctxHandle); - -static jfieldID ntlm_ctxHandleID; -static jfieldID ntlm_crdHandleID; - -static HINSTANCE lib = NULL; - -JNIEXPORT void JNICALL Java_sun_net_www_protocol_http_NTLMAuthSequence_initFirst -(JNIEnv *env, jclass clazz) -{ - OSVERSIONINFO version; - UCHAR libName[MAX_PATH]; - - ntlm_ctxHandleID = (*env)->GetFieldID(env, clazz, "ctxHandle", "J"); - ntlm_crdHandleID = (*env)->GetFieldID(env, clazz, "crdHandle", "J"); - - version.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); - GetVersionEx (&version); - - if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { - strcpy (libName, "security.dll" ); - } - else if (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { - strcpy (libName, "secur32.dll" ); - } - - lib = LoadLibrary (libName); - - pFreeCredentialsHandle - = (FREE_CREDENTIALS_HANDLE_FN) GetProcAddress( - lib, "FreeCredentialsHandle" ); - - pAcquireCredentialsHandle - = (ACQUIRE_CREDENTIALS_HANDLE_FN) GetProcAddress( - lib, "AcquireCredentialsHandleA" ); - - pFreeContextBuffer - = (FREE_CONTEXT_BUFFER_FN) GetProcAddress( - lib, "FreeContextBuffer" ); - - pInitializeSecurityContext - = (INITIALIZE_SECURITY_CONTEXT_FN) GetProcAddress( - lib, "InitializeSecurityContextA" ); - - pCompleteAuthToken - = (COMPLETE_AUTH_TOKEN_FN) GetProcAddress( - lib, "CompleteAuthToken" ); - - pDeleteSecurityContext - = (DELETE_SECURITY_CONTEXT_FN) GetProcAddress( - lib, "DeleteSecurityContext" ); - -} - -/* - * Class: sun_net_www_protocol_http_NTLMAuthSequence - * Method: getCredentialsHandle - * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J - */ - -JNIEXPORT jlong JNICALL Java_sun_net_www_protocol_http_NTLMAuthSequence_getCredentialsHandle -(JNIEnv *env, jobject this, jstring user, jstring domain, jstring password) -{ - SEC_WINNT_AUTH_IDENTITY AuthId; - SEC_WINNT_AUTH_IDENTITY * pAuthId; - const CHAR *pUser = 0; - const CHAR *pDomain = 0; - const CHAR *pPassword = 0; - CredHandle *pCred; - TimeStamp ltime; - jboolean isCopy; - SECURITY_STATUS ss; - - if (user != 0) { - pUser = JNU_GetStringPlatformChars(env, user, &isCopy); - if (pUser == NULL) - return 0; // pending Exception - } - if (domain != 0) { - pDomain = JNU_GetStringPlatformChars(env, domain, &isCopy); - if (pDomain == NULL) { - if (pUser != NULL) - JNU_ReleaseStringPlatformChars(env, user, pUser); - return 0; // pending Exception - } - } - if (password != 0) { - pPassword = JNU_GetStringPlatformChars(env, password, &isCopy); - if (pPassword == NULL) { - if(pUser != NULL) - JNU_ReleaseStringPlatformChars(env, user, pUser); - if(pDomain != NULL) - JNU_ReleaseStringPlatformChars(env, domain, pDomain); - return 0; // pending Exception - } - } - pCred = (CredHandle *)malloc(sizeof (CredHandle)); - - if ( ((pUser != NULL) || (pPassword != NULL)) || (pDomain != NULL)) { - pAuthId = &AuthId; - - memset( &AuthId, 0, sizeof( AuthId )); - - if ( pUser != NULL ) { - AuthId.User = (unsigned char *) pUser; - AuthId.UserLength = strlen( pUser ); - } - - if ( pPassword != NULL ) { - AuthId.Password = (unsigned char *) pPassword; - AuthId.PasswordLength = strlen( pPassword ); - } - - if ( pDomain != NULL ) { - AuthId.Domain = (unsigned char *) pDomain; - AuthId.DomainLength = strlen( pDomain ); - } - - AuthId.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; - } else { - pAuthId = NULL; - } - - ss = pAcquireCredentialsHandle( - NULL, "NTLM", SECPKG_CRED_OUTBOUND, - NULL, pAuthId, NULL, NULL, - pCred, <ime - ); - - /* Release resources held by JNU_GetStringPlatformChars */ - if (pUser != NULL) - JNU_ReleaseStringPlatformChars(env, user, pUser); - if (pPassword != NULL) - JNU_ReleaseStringPlatformChars(env, password, pPassword); - if (pDomain != NULL) - JNU_ReleaseStringPlatformChars(env, domain, pDomain); - - if (ss == 0) { - return (jlong) pCred; - } else { - return 0; - } -} - -JNIEXPORT jbyteArray JNICALL Java_sun_net_www_protocol_http_NTLMAuthSequence_getNextToken -(JNIEnv *env, jobject this, jlong crdHandle, jbyteArray lastToken) -{ - - VOID *pInput = 0; - DWORD inputLen; - CHAR buffOut[512]; - jboolean isCopy; - SECURITY_STATUS ss; - SecBufferDesc OutBuffDesc; - SecBuffer OutSecBuff; - SecBufferDesc InBuffDesc; - SecBuffer InSecBuff; - ULONG ContextAttributes; - CredHandle *pCred = (CredHandle *)crdHandle; - CtxtHandle *pCtx; - CtxtHandle *newContext; - TimeStamp ltime; - jbyteArray result; - - - pCtx = (CtxtHandle *) (*env)->GetLongField (env, this, ntlm_ctxHandleID); - if (pCtx == 0) { /* first call */ - newContext = (CtxtHandle *)malloc(sizeof(CtxtHandle)); - (*env)->SetLongField (env, this, ntlm_ctxHandleID, (jlong)newContext); - } else { - newContext = pCtx; - } - - OutBuffDesc.ulVersion = 0; - OutBuffDesc.cBuffers = 1; - OutBuffDesc.pBuffers = &OutSecBuff; - - OutSecBuff.cbBuffer = 512; - OutSecBuff.BufferType = SECBUFFER_TOKEN; - OutSecBuff.pvBuffer = buffOut; - - /* - * Prepare our Input buffer - Note the server is expecting the client's - * negotiation packet on the first call - */ - - if (lastToken != 0) - { - pInput = (VOID *)(*env)->GetByteArrayElements(env, lastToken, &isCopy); - inputLen = (*env)->GetArrayLength(env, lastToken); - - InBuffDesc.ulVersion = 0; - InBuffDesc.cBuffers = 1; - InBuffDesc.pBuffers = &InSecBuff; - - InSecBuff.cbBuffer = inputLen; - InSecBuff.BufferType = SECBUFFER_TOKEN; - InSecBuff.pvBuffer = pInput; - } - - /* - * will return success when its done but we still - * need to send the out buffer if there are bytes to send - */ - - ss = pInitializeSecurityContext( - pCred, pCtx, NULL, 0, 0, SECURITY_NATIVE_DREP, - lastToken ? &InBuffDesc : NULL, 0, newContext, &OutBuffDesc, - &ContextAttributes, <ime - ); - - if (pInput != 0) { - (*env)->ReleaseByteArrayElements(env, lastToken, pInput, JNI_ABORT); - } - - if (ss < 0) { - endSequence (pCred, pCtx); - return 0; - } - - if ((ss == SEC_I_COMPLETE_NEEDED) || (ss == SEC_I_COMPLETE_AND_CONTINUE) ) { - ss = pCompleteAuthToken( pCtx, &OutBuffDesc ); - - if (ss < 0) { - endSequence (pCred, pCtx); - return 0; - } - } - - if ( OutSecBuff.cbBuffer > 0 ) { - jbyteArray ret = (*env)->NewByteArray(env, OutSecBuff.cbBuffer); - (*env)->SetByteArrayRegion(env, ret, 0, OutSecBuff.cbBuffer, - OutSecBuff.pvBuffer); - if (lastToken != 0) // 2nd stage - endSequence (pCred, pCtx); - result = ret; - } - - if ((ss != SEC_I_CONTINUE_NEEDED) && (ss == SEC_I_COMPLETE_AND_CONTINUE)) { - endSequence (pCred, pCtx); - } - - return result; -} - -static void endSequence (PCredHandle credHand, PCtxtHandle ctxHandle) { - if (credHand != 0) { - pFreeCredentialsHandle (credHand); - free (credHand); - } - - if (ctxHandle != 0) { - pDeleteSecurityContext(ctxHandle); - free (ctxHandle); - } -} --- /dev/null Wed Oct 21 15:29:11 2009 +++ new/src/windows/native/sun/net/www/protocol/http/ntlm/NTLMAuthSequence.c Wed Oct 21 15:29:10 2009 @@ -0,0 +1,311 @@ +/* + * Copyright 2002-2008 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. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "jni_util.h" + +#define SECURITY_WIN32 +#include "sspi.h" + + +/* + * OS calls loaded from DLL on intialization + */ + +static FREE_CREDENTIALS_HANDLE_FN pFreeCredentialsHandle; +static ACQUIRE_CREDENTIALS_HANDLE_FN pAcquireCredentialsHandle; +static FREE_CONTEXT_BUFFER_FN pFreeContextBuffer; +static INITIALIZE_SECURITY_CONTEXT_FN pInitializeSecurityContext; +static COMPLETE_AUTH_TOKEN_FN pCompleteAuthToken; +static DELETE_SECURITY_CONTEXT_FN pDeleteSecurityContext; + +static void endSequence (PCredHandle credHand, PCtxtHandle ctxHandle); + +static jfieldID ntlm_ctxHandleID; +static jfieldID ntlm_crdHandleID; + +static HINSTANCE lib = NULL; + +JNIEXPORT void JNICALL Java_sun_net_www_protocol_http_ntlm_NTLMAuthSequence_initFirst +(JNIEnv *env, jclass clazz) +{ + OSVERSIONINFO version; + UCHAR libName[MAX_PATH]; + + ntlm_ctxHandleID = (*env)->GetFieldID(env, clazz, "ctxHandle", "J"); + ntlm_crdHandleID = (*env)->GetFieldID(env, clazz, "crdHandle", "J"); + + version.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + GetVersionEx (&version); + + if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { + strcpy (libName, "security.dll" ); + } + else if (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { + strcpy (libName, "secur32.dll" ); + } + + lib = LoadLibrary (libName); + + pFreeCredentialsHandle + = (FREE_CREDENTIALS_HANDLE_FN) GetProcAddress( + lib, "FreeCredentialsHandle" ); + + pAcquireCredentialsHandle + = (ACQUIRE_CREDENTIALS_HANDLE_FN) GetProcAddress( + lib, "AcquireCredentialsHandleA" ); + + pFreeContextBuffer + = (FREE_CONTEXT_BUFFER_FN) GetProcAddress( + lib, "FreeContextBuffer" ); + + pInitializeSecurityContext + = (INITIALIZE_SECURITY_CONTEXT_FN) GetProcAddress( + lib, "InitializeSecurityContextA" ); + + pCompleteAuthToken + = (COMPLETE_AUTH_TOKEN_FN) GetProcAddress( + lib, "CompleteAuthToken" ); + + pDeleteSecurityContext + = (DELETE_SECURITY_CONTEXT_FN) GetProcAddress( + lib, "DeleteSecurityContext" ); + +} + +/* + * Class: sun_net_www_protocol_http_NTLMAuthSequence + * Method: getCredentialsHandle + * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J + */ + +JNIEXPORT jlong JNICALL Java_sun_net_www_protocol_http_ntlm_NTLMAuthSequence_getCredentialsHandle +(JNIEnv *env, jobject this, jstring user, jstring domain, jstring password) +{ + SEC_WINNT_AUTH_IDENTITY AuthId; + SEC_WINNT_AUTH_IDENTITY * pAuthId; + const CHAR *pUser = 0; + const CHAR *pDomain = 0; + const CHAR *pPassword = 0; + CredHandle *pCred; + TimeStamp ltime; + jboolean isCopy; + SECURITY_STATUS ss; + + if (user != 0) { + pUser = JNU_GetStringPlatformChars(env, user, &isCopy); + if (pUser == NULL) + return 0; // pending Exception + } + if (domain != 0) { + pDomain = JNU_GetStringPlatformChars(env, domain, &isCopy); + if (pDomain == NULL) { + if (pUser != NULL) + JNU_ReleaseStringPlatformChars(env, user, pUser); + return 0; // pending Exception + } + } + if (password != 0) { + pPassword = JNU_GetStringPlatformChars(env, password, &isCopy); + if (pPassword == NULL) { + if(pUser != NULL) + JNU_ReleaseStringPlatformChars(env, user, pUser); + if(pDomain != NULL) + JNU_ReleaseStringPlatformChars(env, domain, pDomain); + return 0; // pending Exception + } + } + pCred = (CredHandle *)malloc(sizeof (CredHandle)); + + if ( ((pUser != NULL) || (pPassword != NULL)) || (pDomain != NULL)) { + pAuthId = &AuthId; + + memset( &AuthId, 0, sizeof( AuthId )); + + if ( pUser != NULL ) { + AuthId.User = (unsigned char *) pUser; + AuthId.UserLength = strlen( pUser ); + } + + if ( pPassword != NULL ) { + AuthId.Password = (unsigned char *) pPassword; + AuthId.PasswordLength = strlen( pPassword ); + } + + if ( pDomain != NULL ) { + AuthId.Domain = (unsigned char *) pDomain; + AuthId.DomainLength = strlen( pDomain ); + } + + AuthId.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; + } else { + pAuthId = NULL; + } + + ss = pAcquireCredentialsHandle( + NULL, "NTLM", SECPKG_CRED_OUTBOUND, + NULL, pAuthId, NULL, NULL, + pCred, <ime + ); + + /* Release resources held by JNU_GetStringPlatformChars */ + if (pUser != NULL) + JNU_ReleaseStringPlatformChars(env, user, pUser); + if (pPassword != NULL) + JNU_ReleaseStringPlatformChars(env, password, pPassword); + if (pDomain != NULL) + JNU_ReleaseStringPlatformChars(env, domain, pDomain); + + if (ss == 0) { + return (jlong) pCred; + } else { + return 0; + } +} + +JNIEXPORT jbyteArray JNICALL Java_sun_net_www_protocol_http_ntlm_NTLMAuthSequence_getNextToken +(JNIEnv *env, jobject this, jlong crdHandle, jbyteArray lastToken) +{ + + VOID *pInput = 0; + DWORD inputLen; + CHAR buffOut[512]; + jboolean isCopy; + SECURITY_STATUS ss; + SecBufferDesc OutBuffDesc; + SecBuffer OutSecBuff; + SecBufferDesc InBuffDesc; + SecBuffer InSecBuff; + ULONG ContextAttributes; + CredHandle *pCred = (CredHandle *)crdHandle; + CtxtHandle *pCtx; + CtxtHandle *newContext; + TimeStamp ltime; + jbyteArray result; + + + pCtx = (CtxtHandle *) (*env)->GetLongField (env, this, ntlm_ctxHandleID); + if (pCtx == 0) { /* first call */ + newContext = (CtxtHandle *)malloc(sizeof(CtxtHandle)); + (*env)->SetLongField (env, this, ntlm_ctxHandleID, (jlong)newContext); + } else { + newContext = pCtx; + } + + OutBuffDesc.ulVersion = 0; + OutBuffDesc.cBuffers = 1; + OutBuffDesc.pBuffers = &OutSecBuff; + + OutSecBuff.cbBuffer = 512; + OutSecBuff.BufferType = SECBUFFER_TOKEN; + OutSecBuff.pvBuffer = buffOut; + + /* + * Prepare our Input buffer - Note the server is expecting the client's + * negotiation packet on the first call + */ + + if (lastToken != 0) + { + pInput = (VOID *)(*env)->GetByteArrayElements(env, lastToken, &isCopy); + inputLen = (*env)->GetArrayLength(env, lastToken); + + InBuffDesc.ulVersion = 0; + InBuffDesc.cBuffers = 1; + InBuffDesc.pBuffers = &InSecBuff; + + InSecBuff.cbBuffer = inputLen; + InSecBuff.BufferType = SECBUFFER_TOKEN; + InSecBuff.pvBuffer = pInput; + } + + /* + * will return success when its done but we still + * need to send the out buffer if there are bytes to send + */ + + ss = pInitializeSecurityContext( + pCred, pCtx, NULL, 0, 0, SECURITY_NATIVE_DREP, + lastToken ? &InBuffDesc : NULL, 0, newContext, &OutBuffDesc, + &ContextAttributes, <ime + ); + + if (pInput != 0) { + (*env)->ReleaseByteArrayElements(env, lastToken, pInput, JNI_ABORT); + } + + if (ss < 0) { + endSequence (pCred, pCtx); + return 0; + } + + if ((ss == SEC_I_COMPLETE_NEEDED) || (ss == SEC_I_COMPLETE_AND_CONTINUE) ) { + ss = pCompleteAuthToken( pCtx, &OutBuffDesc ); + + if (ss < 0) { + endSequence (pCred, pCtx); + return 0; + } + } + + if ( OutSecBuff.cbBuffer > 0 ) { + jbyteArray ret = (*env)->NewByteArray(env, OutSecBuff.cbBuffer); + (*env)->SetByteArrayRegion(env, ret, 0, OutSecBuff.cbBuffer, + OutSecBuff.pvBuffer); + if (lastToken != 0) // 2nd stage + endSequence (pCred, pCtx); + result = ret; + } + + if ((ss != SEC_I_CONTINUE_NEEDED) && (ss == SEC_I_COMPLETE_AND_CONTINUE)) { + endSequence (pCred, pCtx); + } + + return result; +} + +static void endSequence (PCredHandle credHand, PCtxtHandle ctxHandle) { + if (credHand != 0) { + pFreeCredentialsHandle (credHand); + free (credHand); + } + + if (ctxHandle != 0) { + pDeleteSecurityContext(ctxHandle); + free (ctxHandle); + } +}