From 4cb0bd8a43a8f09f8d7a1684870c5e6797f428d6 Mon Sep 17 00:00:00 2001 From: Laurent Goujon Date: Mon, 24 Feb 2014 14:47:51 -0800 Subject: [PATCH] Adds support for SPNEGO Adds support for Negotiate(SPNEGO) HTTP authentication method. This method is set to have a higher priority as Digest HTTP authentication method. Bug: 428836 Change-Id: Ib181096d39f538df1dd7d3f36516843777bf12ae Signed-off-by: Laurent Goujon Signed-off-by: Chris Aniszczyk --- .../eclipse/jgit/transport/HttpAuthTest.java | 9 +++ .../jgit/transport/HttpAuthMethod.java | 63 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java index 5a64b458f..523301358 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java @@ -64,22 +64,31 @@ public class HttpAuthTest { private static String bearerHeader = "WWW-Authenticate: Bearer"; + private static String negotiateHeader = "WWW-Authenticate: Negotiate"; + private static String URL_SAMPLE = "http://everyones.loves.git/u/2"; private static String BASIC = "Basic"; private static String DIGEST = "Digest"; + private static String NEGOTIATE = "Negotiate"; + @Test public void testHttpAuthScanResponse() { checkResponse(new String[] { basicHeader }, BASIC); checkResponse(new String[] { digestHeader }, DIGEST); + checkResponse(new String[] { negotiateHeader }, NEGOTIATE); checkResponse(new String[] { basicHeader, digestHeader }, DIGEST); checkResponse(new String[] { digestHeader, basicHeader }, DIGEST); + checkResponse(new String[] { digestHeader, negotiateHeader }, NEGOTIATE); + checkResponse(new String[] { negotiateHeader, digestHeader }, NEGOTIATE); checkResponse(new String[] { ntlmHeader, basicHeader, digestHeader, bearerHeader }, DIGEST); checkResponse(new String[] { ntlmHeader, basicHeader, bearerHeader }, BASIC); + checkResponse(new String[] { ntlmHeader, basicHeader, digestHeader, + negotiateHeader, bearerHeader }, NEGOTIATE); } private static void checkResponse(String[] headers, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java index 4d80acc53..6c30824e6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java @@ -61,6 +61,12 @@ import java.util.Random; import org.eclipse.jgit.transport.http.HttpConnection; import org.eclipse.jgit.util.Base64; +import org.eclipse.jgit.util.GSSManagerFactory; +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; /** * Support class to populate user authentication data on a connection. @@ -91,6 +97,12 @@ abstract class HttpAuthMethod { public HttpAuthMethod method(String hdr) { return new Digest(hdr); } + }, + NEGOTIATE { + @Override + public HttpAuthMethod method(String hdr) { + return new Negotiate(hdr); + } }; /** * Creates a HttpAuthMethod instance configured with the provided HTTP @@ -458,4 +470,55 @@ abstract class HttpAuthMethod { return p; } } + + private static class Negotiate extends HttpAuthMethod { + private static final GSSManagerFactory GSS_MANAGER_FACTORY = GSSManagerFactory + .detect(); + + private static final Oid OID; + static { + try { + // OID for SPNEGO + OID = new Oid("1.3.6.1.5.5.2"); //$NON-NLS-1$ + } catch (GSSException e) { + throw new Error("Cannot create NEGOTIATE oid.", e); //$NON-NLS-1$ + } + } + + private final byte[] prevToken; + + public Negotiate(String hdr) { + super(Type.NEGOTIATE); + prevToken = Base64.decode(hdr); + } + + @Override + void authorize(String user, String pass) { + // not used + } + + @Override + void configureRequest(HttpConnection conn) throws IOException { + GSSManager gssManager = GSS_MANAGER_FACTORY.newInstance(conn + .getURL()); + String host = conn.getURL().getHost(); + String peerName = "HTTP@" + host.toLowerCase(); //$NON-NLS-1$ + try { + GSSName gssName = gssManager.createName(peerName, + GSSName.NT_HOSTBASED_SERVICE); + GSSContext context = gssManager.createContext(gssName, OID, + null, GSSContext.DEFAULT_LIFETIME); + // Respect delegation policy in HTTP/SPNEGO. + context.requestCredDeleg(true); + + byte[] token = context.initSecContext(prevToken, 0, + prevToken.length); + + conn.setRequestProperty(HDR_AUTHORIZATION, getType().name() + + " " + Base64.encodeBytes(token)); //$NON-NLS-1$ + } catch (GSSException e) { + throw new IOException(e); + } + } + } }