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

Reply via email to