Repository: calcite-avatica Updated Branches: refs/heads/master 0e69017d3 -> d19740921
[CALCITE-1922] Allow kerberos v5 OID in SPNEGO authentication Closes #15 Project: http://git-wip-us.apache.org/repos/asf/calcite-avatica/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite-avatica/commit/d1974092 Tree: http://git-wip-us.apache.org/repos/asf/calcite-avatica/tree/d1974092 Diff: http://git-wip-us.apache.org/repos/asf/calcite-avatica/diff/d1974092 Branch: refs/heads/master Commit: d19740921dfab7fae981bc10c07fd1bcbd9de56b Parents: bc0e8bf Author: Josh Elser <[email protected]> Authored: Thu Aug 3 22:06:43 2017 -0400 Committer: Josh Elser <[email protected]> Committed: Thu Aug 3 22:10:11 2017 -0400 ---------------------------------------------------------------------- .../server/PropertyBasedSpnegoLoginService.java | 63 ++++++++++++++++++++ 1 file changed, 63 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/d1974092/server/src/main/java/org/apache/calcite/avatica/server/PropertyBasedSpnegoLoginService.java ---------------------------------------------------------------------- diff --git a/server/src/main/java/org/apache/calcite/avatica/server/PropertyBasedSpnegoLoginService.java b/server/src/main/java/org/apache/calcite/avatica/server/PropertyBasedSpnegoLoginService.java index 9e373fb..c5126c3 100644 --- a/server/src/main/java/org/apache/calcite/avatica/server/PropertyBasedSpnegoLoginService.java +++ b/server/src/main/java/org/apache/calcite/avatica/server/PropertyBasedSpnegoLoginService.java @@ -17,16 +17,30 @@ package org.apache.calcite.avatica.server; import org.eclipse.jetty.security.SpnegoLoginService; +import org.eclipse.jetty.security.SpnegoUserPrincipal; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.B64Code; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.reflect.Field; import java.util.Objects; +import javax.security.auth.Subject; + /** * A customization of {@link SpnegoLoginService} which directly specifies the server's * principal instead of requiring a file to exist. Known to work with Jetty-9.2.x, any other * version would require testing/inspection to ensure the logic is still sound. */ public class PropertyBasedSpnegoLoginService extends SpnegoLoginService { + private static final Logger LOG = LoggerFactory.getLogger(PropertyBasedSpnegoLoginService.class); private static final String TARGET_NAME_FIELD_NAME = "_targetName"; private final String serverPrincipal; @@ -45,6 +59,55 @@ public class PropertyBasedSpnegoLoginService extends SpnegoLoginService { targetNameField.setAccessible(true); targetNameField.set(this, serverPrincipal); } + + @Override public UserIdentity login(String username, Object credentials) { + String encodedAuthToken = (String) credentials; + byte[] authToken = B64Code.decode(encodedAuthToken); + + GSSManager manager = GSSManager.getInstance(); + try { + // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html + Oid spnegoOid = new Oid("1.3.6.1.5.5.2"); + Oid krb5Oid = new Oid("1.2.840.113554.1.2.2"); + GSSName gssName = manager.createName(serverPrincipal, null); + // CALCITE-1922 Providing both OIDs is the bug in Jetty we're working around. By specifying + // only one, we're requiring that clients *must* provide us the SPNEGO OID to authenticate + // via Kerberos which is wrong. Best as I can tell, the SPNEGO OID is meant as another + // layer of indirection (essentially is equivalent to setting the Kerberos OID). + GSSCredential serverCreds = manager.createCredential(gssName, + GSSCredential.INDEFINITE_LIFETIME, new Oid[] {krb5Oid, spnegoOid}, + GSSCredential.ACCEPT_ONLY); + GSSContext gContext = manager.createContext(serverCreds); + + if (gContext == null) { + LOG.debug("SpnegoUserRealm: failed to establish GSSContext"); + } else { + while (!gContext.isEstablished()) { + authToken = gContext.acceptSecContext(authToken, 0, authToken.length); + } + if (gContext.isEstablished()) { + String clientName = gContext.getSrcName().toString(); + String role = clientName.substring(clientName.indexOf('@') + 1); + + LOG.debug("SpnegoUserRealm: established a security context"); + LOG.debug("Client Principal is: {}", gContext.getSrcName()); + LOG.debug("Server Principal is: {}", gContext.getTargName()); + LOG.debug("Client Default Role: {}", role); + + SpnegoUserPrincipal user = new SpnegoUserPrincipal(clientName, authToken); + + Subject subject = new Subject(); + subject.getPrincipals().add(user); + + return _identityService.newUserIdentity(subject, user, new String[]{role}); + } + } + } catch (GSSException gsse) { + LOG.warn("Caught GSSException trying to authenticate the client", gsse); + } + + return null; + } } // End PropertyBasedSpnegoLoginService.java
