From 8f46fbacd83e2e2e6fc41f415be61f3c7df4b88e Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Mon, 10 Oct 2011 15:17:26 +0000 Subject: [PATCH] Better state handling in NegotiateScheme#authenticate git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1181015 13f79535-47bb-0310-9956-ffa450edef68 --- .../http/impl/auth/NegotiateScheme.java | 213 +++++++++--------- 1 file changed, 109 insertions(+), 104 deletions(-) diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateScheme.java index eb0142c3e..990c9e09f 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateScheme.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateScheme.java @@ -152,124 +152,129 @@ public class NegotiateScheme extends AuthSchemeBase { if (request == null) { throw new IllegalArgumentException("HTTP request may not be null"); } - if (state != State.CHALLENGE_RECEIVED) { - throw new IllegalStateException( - "Negotiation authentication process has not been initiated"); - } - try { - String key = null; - if (isProxy()) { - key = ExecutionContext.HTTP_PROXY_HOST; - } else { - key = ExecutionContext.HTTP_TARGET_HOST; - } - HttpHost host = (HttpHost) context.getAttribute(key); - if (host == null) { - throw new AuthenticationException("Authentication host is not set " + - "in the execution context"); - } - String authServer; - if (!this.stripPort && host.getPort() > 0) { - authServer = host.toHostString(); - } else { - authServer = host.getHostName(); - } - - if (log.isDebugEnabled()) { - log.debug("init " + authServer); - } - /* Using the SPNEGO OID is the correct method. - * Kerberos v5 works for IIS but not JBoss. Unwrapping - * the initial token when using SPNEGO OID looks like what is - * described here... - * - * http://msdn.microsoft.com/en-us/library/ms995330.aspx - * - * Another helpful URL... - * - * http://publib.boulder.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/tsec_SPNEGO_token.html - * - * Unfortunately SPNEGO is JRE >=1.6. - */ - - /** Try SPNEGO by default, fall back to Kerberos later if error */ - negotiationOid = new Oid(SPNEGO_OID); - - boolean tryKerberos = false; + switch (state) { + case UNINITIATED: + throw new AuthenticationException("SPNEGO authentication has not been initiated"); + case FAILED: + throw new AuthenticationException("SPNEGO authentication has failed"); + case CHALLENGE_RECEIVED: try { - GSSManager manager = getManager(); - GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE); - gssContext = manager.createContext( - serverName.canonicalize(negotiationOid), negotiationOid, null, - GSSContext.DEFAULT_LIFETIME); - gssContext.requestMutualAuth(true); - gssContext.requestCredDeleg(true); - } catch (GSSException ex){ - // BAD MECH means we are likely to be using 1.5, fall back to Kerberos MECH. - // Rethrow any other exception. - if (ex.getMajor() == GSSException.BAD_MECH ){ - log.debug("GSSException BAD_MECH, retry with Kerberos MECH"); - tryKerberos = true; + String key = null; + if (isProxy()) { + key = ExecutionContext.HTTP_PROXY_HOST; } else { - throw ex; + key = ExecutionContext.HTTP_TARGET_HOST; + } + HttpHost host = (HttpHost) context.getAttribute(key); + if (host == null) { + throw new AuthenticationException("Authentication host is not set " + + "in the execution context"); + } + String authServer; + if (!this.stripPort && host.getPort() > 0) { + authServer = host.toHostString(); + } else { + authServer = host.getHostName(); } - } - if (tryKerberos){ - /* Kerberos v5 GSS-API mechanism defined in RFC 1964.*/ - log.debug("Using Kerberos MECH " + KERBEROS_OID); - negotiationOid = new Oid(KERBEROS_OID); - GSSManager manager = getManager(); - GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE); - gssContext = manager.createContext( - serverName.canonicalize(negotiationOid), negotiationOid, null, - GSSContext.DEFAULT_LIFETIME); - gssContext.requestMutualAuth(true); - gssContext.requestCredDeleg(true); - } - if (token == null) { - token = new byte[0]; - } - token = gssContext.initSecContext(token, 0, token.length); - if (token == null) { + if (log.isDebugEnabled()) { + log.debug("init " + authServer); + } + /* Using the SPNEGO OID is the correct method. + * Kerberos v5 works for IIS but not JBoss. Unwrapping + * the initial token when using SPNEGO OID looks like what is + * described here... + * + * http://msdn.microsoft.com/en-us/library/ms995330.aspx + * + * Another helpful URL... + * + * http://publib.boulder.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/tsec_SPNEGO_token.html + * + * Unfortunately SPNEGO is JRE >=1.6. + */ + + /** Try SPNEGO by default, fall back to Kerberos later if error */ + negotiationOid = new Oid(SPNEGO_OID); + + boolean tryKerberos = false; + try { + GSSManager manager = getManager(); + GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE); + gssContext = manager.createContext( + serverName.canonicalize(negotiationOid), negotiationOid, null, + GSSContext.DEFAULT_LIFETIME); + gssContext.requestMutualAuth(true); + gssContext.requestCredDeleg(true); + } catch (GSSException ex){ + // BAD MECH means we are likely to be using 1.5, fall back to Kerberos MECH. + // Rethrow any other exception. + if (ex.getMajor() == GSSException.BAD_MECH ){ + log.debug("GSSException BAD_MECH, retry with Kerberos MECH"); + tryKerberos = true; + } else { + throw ex; + } + + } + if (tryKerberos){ + /* Kerberos v5 GSS-API mechanism defined in RFC 1964.*/ + log.debug("Using Kerberos MECH " + KERBEROS_OID); + negotiationOid = new Oid(KERBEROS_OID); + GSSManager manager = getManager(); + GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE); + gssContext = manager.createContext( + serverName.canonicalize(negotiationOid), negotiationOid, null, + GSSContext.DEFAULT_LIFETIME); + gssContext.requestMutualAuth(true); + gssContext.requestCredDeleg(true); + } + if (token == null) { + token = new byte[0]; + } + token = gssContext.initSecContext(token, 0, token.length); + if (token == null) { + state = State.FAILED; + throw new AuthenticationException("GSS security context initialization failed"); + } + + /* + * IIS accepts Kerberos and SPNEGO tokens. Some other servers Jboss, Glassfish? + * seem to only accept SPNEGO. Below wraps Kerberos into SPNEGO token. + */ + if (spengoGenerator != null && negotiationOid.toString().equals(KERBEROS_OID)) { + token = spengoGenerator.generateSpnegoDERObject(token); + } + + state = State.TOKEN_GENERATED; + } catch (GSSException gsse) { state = State.FAILED; - throw new AuthenticationException("GSS security context initialization failed"); + if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL + || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED) + throw new InvalidCredentialsException(gsse.getMessage(), gsse); + if (gsse.getMajor() == GSSException.NO_CRED ) + throw new InvalidCredentialsException(gsse.getMessage(), gsse); + if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN + || gsse.getMajor() == GSSException.DUPLICATE_TOKEN + || gsse.getMajor() == GSSException.OLD_TOKEN) + throw new AuthenticationException(gsse.getMessage(), gsse); + // other error + throw new AuthenticationException(gsse.getMessage()); + } catch (IOException ex){ + state = State.FAILED; + throw new AuthenticationException(ex.getMessage()); } - - /* - * IIS accepts Kerberos and SPNEGO tokens. Some other servers Jboss, Glassfish? - * seem to only accept SPNEGO. Below wraps Kerberos into SPNEGO token. - */ - if (spengoGenerator != null && negotiationOid.toString().equals(KERBEROS_OID)) { - token = spengoGenerator.generateSpnegoDERObject(token); - } - - state = State.TOKEN_GENERATED; + case TOKEN_GENERATED: String tokenstr = new String(Base64.encodeBase64(token, false)); if (log.isDebugEnabled()) { log.debug("Sending response '" + tokenstr + "' back to the auth server"); } return new BasicHeader("Authorization", "Negotiate " + tokenstr); - } catch (GSSException gsse) { - state = State.FAILED; - if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL - || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED) - throw new InvalidCredentialsException(gsse.getMessage(), gsse); - if (gsse.getMajor() == GSSException.NO_CRED ) - throw new InvalidCredentialsException(gsse.getMessage(), gsse); - if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN - || gsse.getMajor() == GSSException.DUPLICATE_TOKEN - || gsse.getMajor() == GSSException.OLD_TOKEN) - throw new AuthenticationException(gsse.getMessage(), gsse); - // other error - throw new AuthenticationException(gsse.getMessage()); - } catch (IOException ex){ - state = State.FAILED; - throw new AuthenticationException(ex.getMessage()); + default: + throw new IllegalStateException("Illegal state: " + state); } } - /** * Returns the authentication parameter with the given name, if available. *