diff --git a/api/src/main/java/com/ning/http/client/ProxyServer.java b/api/src/main/java/com/ning/http/client/ProxyServer.java index f0551329a5..2ab060c309 100644 --- a/api/src/main/java/com/ning/http/client/ProxyServer.java +++ b/api/src/main/java/com/ning/http/client/ProxyServer.java @@ -53,7 +53,7 @@ public String toString() { private int port; private String ntlmDomain = System.getProperty("http.auth.ntlm.domain", ""); - private boolean isBasic = true; + /*private boolean isBasic = true; public boolean isBasic() { return isBasic; @@ -61,7 +61,7 @@ public boolean isBasic() { public void setBasic(boolean isBasic) { this.isBasic = isBasic; - } + }*/ public ProxyServer(final Protocol protocol, final String host, final int port, String principal, String password) { this.protocol = protocol; diff --git a/providers/grizzly/src/main/java/com/ning/http/client/providers/grizzly/GSSSPNEGOWrapper.java b/providers/grizzly/src/main/java/com/ning/http/client/providers/grizzly/GSSSPNEGOWrapper.java new file mode 100644 index 0000000000..ce06498932 --- /dev/null +++ b/providers/grizzly/src/main/java/com/ning/http/client/providers/grizzly/GSSSPNEGOWrapper.java @@ -0,0 +1,49 @@ +package com.ning.http.client.providers.grizzly; + +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; + +import com.ning.http.util.Base64; + +public class GSSSPNEGOWrapper { + + private static final String KERBEROS_OID = "1.2.840.113554.1.2.2"; + + static GSSManager getManager() { + return GSSManager.getInstance(); + } + + static byte[] generateGSSToken( + final byte[] input, final Oid oid, final String authServer) throws GSSException { + byte[] token = input; + if (token == null) { + token = new byte[0]; + } + GSSManager manager = getManager(); + GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE); + GSSContext gssContext = manager.createContext( + serverName.canonicalize(oid), oid, null, GSSContext.DEFAULT_LIFETIME); + gssContext.requestMutualAuth(true); + gssContext.requestCredDeleg(true); + return gssContext.initSecContext(token, 0, token.length); + } + + public static String generateToken(String authServer) + { + String returnVal = ""; + Oid oid; + try { + oid = new Oid(KERBEROS_OID); + byte[] token = GSSSPNEGOWrapper.generateGSSToken(null, oid, authServer); + returnVal = new String(Base64.encode(token)); + } catch (GSSException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return returnVal; + } +} diff --git a/providers/grizzly/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProvider.java b/providers/grizzly/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProvider.java index 0ec44f4f30..2bf916764c 100644 --- a/providers/grizzly/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProvider.java +++ b/providers/grizzly/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProvider.java @@ -13,44 +13,34 @@ package com.ning.http.client.providers.grizzly; -import com.ning.http.client.AsyncHandler; -import com.ning.http.client.AsyncHttpClientConfig; -import com.ning.http.client.AsyncHttpProvider; -import com.ning.http.client.Body; -import com.ning.http.client.BodyGenerator; -import com.ning.http.client.ConnectionsPool; -import com.ning.http.client.Cookie; -import com.ning.http.client.FluentCaseInsensitiveStringsMap; -import com.ning.http.client.FluentStringsMap; -import com.ning.http.client.HttpResponseBodyPart; -import com.ning.http.client.HttpResponseHeaders; -import com.ning.http.client.HttpResponseStatus; -import com.ning.http.client.ListenableFuture; -import com.ning.http.client.MaxRedirectException; -import com.ning.http.client.Part; -import com.ning.http.client.PerRequestConfig; -import com.ning.http.client.ProxyServer; -import com.ning.http.client.Realm; -import com.ning.http.client.Request; -import com.ning.http.client.RequestBuilder; -import com.ning.http.client.Response; -import com.ning.http.client.UpgradeHandler; -import com.ning.http.client.filter.FilterContext; -import com.ning.http.client.filter.ResponseFilter; -import com.ning.http.client.listener.TransferCompletionHandler; -import com.ning.http.client.websocket.WebSocket; -import com.ning.http.client.websocket.WebSocketByteListener; -import com.ning.http.client.websocket.WebSocketCloseCodeReasonListener; -import com.ning.http.client.websocket.WebSocketListener; -import com.ning.http.client.websocket.WebSocketPingListener; -import com.ning.http.client.websocket.WebSocketPongListener; -import com.ning.http.client.websocket.WebSocketTextListener; -import com.ning.http.client.websocket.WebSocketUpgradeHandler; -import com.ning.http.multipart.MultipartRequestEntity; -import com.ning.http.util.AsyncHttpProviderUtils; -import com.ning.http.util.AuthenticatorUtils; -import com.ning.http.util.ProxyUtils; -import com.ning.http.util.SslUtils; +import static com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProviderConfig.Property.MAX_HTTP_PACKET_HEADER_SIZE; +import static com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProviderConfig.Property.TRANSPORT_CUSTOMIZER; + +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.security.NoSuchAlgorithmException; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import javax.net.ssl.SSLContext; import org.glassfish.grizzly.Buffer; import org.glassfish.grizzly.CompletionHandler; @@ -77,13 +67,12 @@ import org.glassfish.grizzly.http.HttpResponsePacket; import org.glassfish.grizzly.http.Method; import org.glassfish.grizzly.http.Protocol; -import org.glassfish.grizzly.impl.FutureImpl; -import org.glassfish.grizzly.utils.Charsets; import org.glassfish.grizzly.http.util.CookieSerializerUtils; import org.glassfish.grizzly.http.util.DataChunk; import org.glassfish.grizzly.http.util.Header; import org.glassfish.grizzly.http.util.HttpStatus; import org.glassfish.grizzly.http.util.MimeHeaders; +import org.glassfish.grizzly.impl.FutureImpl; import org.glassfish.grizzly.impl.SafeFutureImpl; import org.glassfish.grizzly.memory.Buffers; import org.glassfish.grizzly.memory.MemoryManager; @@ -95,6 +84,7 @@ import org.glassfish.grizzly.strategies.SameThreadIOStrategy; import org.glassfish.grizzly.strategies.WorkerThreadIOStrategy; import org.glassfish.grizzly.utils.BufferOutputStream; +import org.glassfish.grizzly.utils.Charsets; import org.glassfish.grizzly.utils.DelayedExecutor; import org.glassfish.grizzly.utils.Futures; import org.glassfish.grizzly.utils.IdleTimeoutFilter; @@ -110,33 +100,45 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.net.ssl.SSLContext; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.InetSocketAddress; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URLEncoder; -import java.security.NoSuchAlgorithmException; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -import static com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProviderConfig.Property.MAX_HTTP_PACKET_HEADER_SIZE; -import static com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProviderConfig.Property.TRANSPORT_CUSTOMIZER; +import com.ning.http.client.AsyncHandler; +import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.AsyncHttpProvider; +import com.ning.http.client.Body; +import com.ning.http.client.BodyGenerator; +import com.ning.http.client.ConnectionsPool; +import com.ning.http.client.Cookie; +import com.ning.http.client.FluentCaseInsensitiveStringsMap; +import com.ning.http.client.FluentStringsMap; +import com.ning.http.client.HttpResponseBodyPart; +import com.ning.http.client.HttpResponseHeaders; +import com.ning.http.client.HttpResponseStatus; +import com.ning.http.client.ListenableFuture; +import com.ning.http.client.MaxRedirectException; +import com.ning.http.client.Part; +import com.ning.http.client.PerRequestConfig; +import com.ning.http.client.ProxyServer; +import com.ning.http.client.Realm; +import com.ning.http.client.Request; +import com.ning.http.client.RequestBuilder; +import com.ning.http.client.Response; +import com.ning.http.client.UpgradeHandler; +import com.ning.http.client.filter.FilterContext; +import com.ning.http.client.filter.ResponseFilter; +import com.ning.http.client.listener.TransferCompletionHandler; +import com.ning.http.client.ntlm.NTLMEngine; +import com.ning.http.client.websocket.WebSocket; +import com.ning.http.client.websocket.WebSocketByteListener; +import com.ning.http.client.websocket.WebSocketCloseCodeReasonListener; +import com.ning.http.client.websocket.WebSocketListener; +import com.ning.http.client.websocket.WebSocketPingListener; +import com.ning.http.client.websocket.WebSocketPongListener; +import com.ning.http.client.websocket.WebSocketTextListener; +import com.ning.http.client.websocket.WebSocketUpgradeHandler; +import com.ning.http.multipart.MultipartRequestEntity; +import com.ning.http.util.AsyncHttpProviderUtils; +import com.ning.http.util.AuthenticatorUtils; +import com.ning.http.util.ProxyUtils; +import com.ning.http.util.SslUtils; /** * A Grizzly 2.0-based implementation of {@link AsyncHttpProvider}. @@ -164,8 +166,7 @@ public class GrizzlyAsyncHttpProvider implements AsyncHttpProvider { DelayedExecutor.Resolver resolver; private DelayedExecutor timeoutExecutor; - - + private final static NTLMEngine ntlmEngine = new NTLMEngine(); // ------------------------------------------------------------ Constructors @@ -914,7 +915,8 @@ private boolean sendAsGrizzlyRequest(final Request request, requestPacket.setHeader(Header.ProxyConnection, "keep-alive"); } - if (proxy.getPrincipal() != null && proxy.isBasic()) { + if(null == requestPacket.getHeader(Header.ProxyAuthorization) ) + { requestPacket.setHeader(Header.ProxyAuthorization, AuthenticatorUtils.computeBasicAuthentication(proxy)); } @@ -1385,10 +1387,19 @@ protected void onHttpHeadersParsed(HttpHeader httpHeader, protected boolean onHttpPacketParsed(HttpHeader httpHeader, FilterChainContext ctx) { boolean result; - if (httpHeader.isSkipRemainder()) { - clearResponse(ctx.getConnection()); - cleanup(ctx, provider); - return false; + final String proxy_auth = httpHeader.getHeader(Header.ProxyAuthenticate); + + if (httpHeader.isSkipRemainder() ) { + if(!ProxyAuthorizationHandler.isNTLMSecondHandShake(proxy_auth)) + { + clearResponse(ctx.getConnection()); + cleanup(ctx, provider); + return false; + }else{ + super.onHttpPacketParsed(httpHeader, ctx); + httpHeader.getProcessingState().setKeepAlive(true); + return false; + } } result = super.onHttpPacketParsed(httpHeader, ctx); @@ -1631,36 +1642,123 @@ public boolean handleStatus(final HttpResponsePacket responsePacket, } catch (UnsupportedEncodingException e) { throw new IllegalStateException("Unsupported encoding.", e); } - } else { + }else if (proxy_auth.toLowerCase().startsWith("ntlm")) { + + req.getHeaders().remove(Header.ProxyAuthenticate.toString()); + req.getHeaders().remove(Header.ProxyAuthorization.toString()); + + String msg = null; + try { + + if(isNTLMFirstHandShake(proxy_auth)) + { + msg = ntlmEngine.generateType1Msg(proxyServer.getNtlmDomain(), ""); + }else { + String serverChallenge = proxy_auth.trim().substring("NTLM ".length()); + msg = ntlmEngine.generateType3Msg(principal, password, proxyServer.getNtlmDomain(), proxyServer.getHost(), serverChallenge); + } + + req.getHeaders().add(Header.ProxyAuthorization.toString(), "NTLM " + msg); + } catch (Exception e1) { + e1.printStackTrace(); + } + } else if (proxy_auth.toLowerCase().startsWith("negotiate")){ + //this is for kerberos + req.getHeaders().remove(Header.ProxyAuthenticate.toString()); + req.getHeaders().remove(Header.ProxyAuthorization.toString()); + + }else { throw new IllegalStateException("Unsupported authorization method: " + proxy_auth); } final ConnectionManager m = httpTransactionContext.provider.connectionManager; + InvocationStatus tempInvocationStatus = InvocationStatus.STOP; + try { - final Connection c = m.obtainConnection(req, - httpTransactionContext.future); - final HttpTransactionContext newContext = - httpTransactionContext.copy(); - httpTransactionContext.future = null; - httpTransactionContext.provider.setHttpTransactionContext(c, newContext); - newContext.invocationStatus = InvocationStatus.STOP; - try { - httpTransactionContext.provider.execute(c, - req, - httpTransactionContext.handler, - httpTransactionContext.future); - return false; - } catch (IOException ioe) { - newContext.abort(ioe); - return false; + + if(isNTLMFirstHandShake(proxy_auth)) + { + tempInvocationStatus = InvocationStatus.CONTINUE; + + } + + if(proxy_auth.toLowerCase().startsWith("negotiate")) + { + final Connection c = m.obtainConnection(req, httpTransactionContext.future); + final HttpTransactionContext newContext = httpTransactionContext.copy(); + httpTransactionContext.future = null; + httpTransactionContext.provider.setHttpTransactionContext(c, newContext); + + newContext.invocationStatus = tempInvocationStatus; + + String challengeHeader = null; + String server = proxyServer.getHost(); + + challengeHeader = GSSSPNEGOWrapper.generateToken(server); + + req.getHeaders().add(Header.ProxyAuthorization.toString(), "Negotiate " + challengeHeader); + + + return exceuteRequest(httpTransactionContext, req, c, + newContext); + }else if(isNTLMSecondHandShake(proxy_auth)) + { + final Connection c = ctx.getConnection(); + final HttpTransactionContext newContext = httpTransactionContext.copy(); + + httpTransactionContext.future = null; + httpTransactionContext.provider.setHttpTransactionContext(c, newContext); + + newContext.invocationStatus = tempInvocationStatus; + httpTransactionContext.establishingTunnel = true; + + return exceuteRequest(httpTransactionContext, req, c, + newContext); + + } + else{ + final Connection c = m.obtainConnection(req, httpTransactionContext.future); + final HttpTransactionContext newContext = httpTransactionContext.copy(); + httpTransactionContext.future = null; + httpTransactionContext.provider.setHttpTransactionContext(c, newContext); + + newContext.invocationStatus = tempInvocationStatus; + + //NTLM needs the same connection to be used for exchange of tokens + return exceuteRequest(httpTransactionContext, req, c, + newContext); } } catch (Exception e) { httpTransactionContext.abort(e); } - httpTransactionContext.invocationStatus = InvocationStatus.STOP; + httpTransactionContext.invocationStatus = tempInvocationStatus; return false; } + private boolean exceuteRequest( + final HttpTransactionContext httpTransactionContext, + final Request req, final Connection c, + final HttpTransactionContext newContext) { + try { + httpTransactionContext.provider.execute(c, + req, + httpTransactionContext.handler, + httpTransactionContext.future); + return false; + } catch (IOException ioe) { + newContext.abort(ioe); + return false; + } + } + + public static boolean isNTLMSecondHandShake(final String proxy_auth) { + return (proxy_auth.toLowerCase().startsWith("ntlm") && !proxy_auth.equalsIgnoreCase("ntlm")); + } + public static boolean isNTLMFirstHandShake(final String proxy_auth) { + return (proxy_auth.equalsIgnoreCase("ntlm")); + } + + } // END AuthorizationHandler