You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by ma...@apache.org on 2015/12/09 02:29:16 UTC

hadoop git commit: HADOOP-12617. SPNEGO authentication request to non-default realm gets default realm name inserted in target server principal. (mattf)

Repository: hadoop
Updated Branches:
  refs/heads/trunk 7e4715186 -> ada9c2c41


HADOOP-12617. SPNEGO authentication request to non-default realm gets default realm name inserted in target server principal. (mattf)


Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/ada9c2c4
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/ada9c2c4
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/ada9c2c4

Branch: refs/heads/trunk
Commit: ada9c2c410c15e95d0a21ea2941986195606aad8
Parents: 7e47151
Author: mattf <ma...@apache.org>
Authored: Tue Dec 8 17:27:50 2015 -0800
Committer: mattf <ma...@apache.org>
Committed: Tue Dec 8 17:27:50 2015 -0800

----------------------------------------------------------------------
 .../authentication/util/KerberosUtil.java       | 87 ++++++++++++++++++--
 .../authentication/util/TestKerberosUtil.java   | 31 +++++--
 hadoop-common-project/hadoop-common/CHANGES.txt |  3 +
 3 files changed, 107 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hadoop/blob/ada9c2c4/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/KerberosUtil.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/KerberosUtil.java b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/KerberosUtil.java
index 0e8d8db..3d7b00d 100644
--- a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/KerberosUtil.java
+++ b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/KerberosUtil.java
@@ -42,7 +42,7 @@ public class KerberosUtil {
 
   /* Return the Kerberos login module name */
   public static String getKrb5LoginModuleName() {
-    return System.getProperty("java.vendor").contains("IBM")
+    return (IBM_JAVA)
       ? "com.ibm.security.auth.module.Krb5LoginModule"
       : "com.sun.security.auth.module.Krb5LoginModule";
   }
@@ -72,7 +72,7 @@ public class KerberosUtil {
     Class<?> classRef;
     Method getInstanceMethod;
     Method getDefaultRealmMethod;
-    if (System.getProperty("java.vendor").contains("IBM")) {
+    if (IBM_JAVA) {
       classRef = Class.forName("com.ibm.security.krb5.internal.Config");
     } else {
       classRef = Class.forName("sun.security.krb5.Config");
@@ -83,17 +83,79 @@ public class KerberosUtil {
          new Class[0]);
     return (String)getDefaultRealmMethod.invoke(kerbConf, new Object[0]);
   }
-  
+
+  public static String getDefaultRealmProtected() {
+    String realmString = null;
+    try {
+      realmString = getDefaultRealm();
+    } catch (RuntimeException rte) {
+      //silently catch everything
+    } catch (Exception e) {
+      //silently return null
+    }
+    return realmString;
+  }
+
+  /*
+   * For a Service Host Principal specification, map the host's domain
+   * to kerberos realm, as specified by krb5.conf [domain_realm] mappings.
+   * Unfortunately the mapping routines are private to the security.krb5
+   * package, so have to construct a PrincipalName instance to derive the realm.
+   *
+   * Many things can go wrong with Kerberos configuration, and this is not
+   * the place to be throwing exceptions to help debug them.  Nor do we choose
+   * to make potentially voluminous logs on every call to a communications API.
+   * So we simply swallow all exceptions from the underlying libraries and
+   * return null if we can't get a good value for the realmString.
+   *
+   * @param shortprinc A service principal name with host fqdn as instance, e.g.
+   *     "HTTP/myhost.mydomain"
+   * @return String value of Kerberos realm, mapped from host fqdn
+   *     May be default realm, or may be null.
+   */
+  public static String getDomainRealm(String shortprinc) {
+    Class<?> classRef;
+    Object principalName; //of type sun.security.krb5.PrincipalName or IBM equiv
+    String realmString = null;
+    try {
+      if (IBM_JAVA) {
+        classRef = Class.forName("com.ibm.security.krb5.PrincipalName");
+      } else {
+        classRef = Class.forName("sun.security.krb5.PrincipalName");
+      }
+      int tKrbNtSrvHst = classRef.getField("KRB_NT_SRV_HST").getInt(null);
+      principalName = classRef.getConstructor(String.class, int.class).
+          newInstance(shortprinc, tKrbNtSrvHst);
+      realmString = (String)classRef.getMethod("getRealmString", new Class[0]).
+          invoke(principalName, new Object[0]);
+    } catch (RuntimeException rte) {
+      //silently catch everything
+    } catch (Exception e) {
+      //silently return default realm (which may itself be null)
+    }
+    if (null == realmString || realmString.equals("")) {
+      return getDefaultRealmProtected();
+    } else {
+      return realmString;
+    }
+  }
+
   /* Return fqdn of the current host */
   static String getLocalHostName() throws UnknownHostException {
     return InetAddress.getLocalHost().getCanonicalHostName();
   }
   
   /**
-   * Create Kerberos principal for a given service and hostname. It converts
+   * Create Kerberos principal for a given service and hostname,
+   * inferring realm from the fqdn of the hostname. It converts
    * hostname to lower case. If hostname is null or "0.0.0.0", it uses
    * dynamically looked-up fqdn of the current host instead.
-   * 
+   * If domain_realm mappings are inadequately specified, it will
+   * use default_realm, per usual Kerberos behavior.
+   * If default_realm also gives a null value, then a principal
+   * without realm will be returned, which by Kerberos definitions is
+   * just another way to specify default realm.
+   *
    * @param service
    *          Service for which you want to generate the principal.
    * @param hostname
@@ -102,15 +164,26 @@ public class KerberosUtil {
    * @throws UnknownHostException
    *           If no IP address for the local host could be found.
    */
-  public static final String getServicePrincipal(String service, String hostname)
+  public static final String getServicePrincipal(String service,
+      String hostname)
       throws UnknownHostException {
     String fqdn = hostname;
+    String shortprinc = null;
+    String realmString = null;
     if (null == fqdn || fqdn.equals("") || fqdn.equals("0.0.0.0")) {
       fqdn = getLocalHostName();
     }
     // convert hostname to lowercase as kerberos does not work with hostnames
     // with uppercase characters.
-    return service + "/" + fqdn.toLowerCase(Locale.US);
+    fqdn = fqdn.toLowerCase(Locale.US);
+    shortprinc = service + "/" + fqdn;
+    // Obtain the realm name inferred from the domain of the host
+    realmString = getDomainRealm(shortprinc);
+    if (null == realmString || realmString.equals("")) {
+      return shortprinc;
+    } else {
+      return shortprinc + "@" + realmString;
+    }
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/hadoop/blob/ada9c2c4/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestKerberosUtil.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestKerberosUtil.java b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestKerberosUtil.java
index 89e07d1..a0ae025 100644
--- a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestKerberosUtil.java
+++ b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestKerberosUtil.java
@@ -18,6 +18,7 @@ package org.apache.hadoop.security.authentication.util;
 
 import java.io.File;
 import java.io.IOException;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -52,32 +53,48 @@ public class TestKerberosUtil {
   }
 
   @Test
-  public void testGetServerPrincipal() throws IOException {
+  public void testGetServerPrincipal()
+      throws IOException, UnknownHostException {
     String service = "TestKerberosUtil";
     String localHostname = KerberosUtil.getLocalHostName();
     String testHost = "FooBar";
+    String defaultRealm = KerberosUtil.getDefaultRealmProtected();
+
+    String atDefaultRealm;
+    if (defaultRealm == null || defaultRealm.equals("")) {
+      atDefaultRealm = "";
+    } else {
+      atDefaultRealm = "@" + defaultRealm;
+    }
+    // check that the test environment is as expected
+    Assert.assertEquals("testGetServerPrincipal assumes localhost realm is default",
+        KerberosUtil.getDomainRealm(service + "/" + localHostname.toLowerCase(Locale.US)),
+        defaultRealm);
+    Assert.assertEquals("testGetServerPrincipal assumes realm of testHost 'FooBar' is default",
+        KerberosUtil.getDomainRealm(service + "/" + testHost.toLowerCase(Locale.US)),
+        defaultRealm);
 
     // send null hostname
     Assert.assertEquals("When no hostname is sent",
-        service + "/" + localHostname.toLowerCase(Locale.ENGLISH),
+        service + "/" + localHostname.toLowerCase(Locale.US) + atDefaultRealm,
         KerberosUtil.getServicePrincipal(service, null));
     // send empty hostname
     Assert.assertEquals("When empty hostname is sent",
-        service + "/" + localHostname.toLowerCase(Locale.ENGLISH),
+        service + "/" + localHostname.toLowerCase(Locale.US) + atDefaultRealm,
         KerberosUtil.getServicePrincipal(service, ""));
     // send 0.0.0.0 hostname
     Assert.assertEquals("When 0.0.0.0 hostname is sent",
-        service + "/" + localHostname.toLowerCase(Locale.ENGLISH),
+        service + "/" + localHostname.toLowerCase(Locale.US) + atDefaultRealm,
         KerberosUtil.getServicePrincipal(service, "0.0.0.0"));
     // send uppercase hostname
     Assert.assertEquals("When uppercase hostname is sent",
-        service + "/" + testHost.toLowerCase(Locale.ENGLISH),
+        service + "/" + testHost.toLowerCase(Locale.US) + atDefaultRealm,
         KerberosUtil.getServicePrincipal(service, testHost));
     // send lowercase hostname
     Assert.assertEquals("When lowercase hostname is sent",
-        service + "/" + testHost.toLowerCase(Locale.ENGLISH),
+        service + "/" + testHost.toLowerCase(Locale.US) + atDefaultRealm,
         KerberosUtil.getServicePrincipal(
-            service, testHost.toLowerCase(Locale.ENGLISH)));
+            service, testHost.toLowerCase(Locale.US)));
   }
   
   @Test

http://git-wip-us.apache.org/repos/asf/hadoop/blob/ada9c2c4/hadoop-common-project/hadoop-common/CHANGES.txt
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt
index 4cd295e..0f505dd 100644
--- a/hadoop-common-project/hadoop-common/CHANGES.txt
+++ b/hadoop-common-project/hadoop-common/CHANGES.txt
@@ -264,6 +264,9 @@ Trunk (Unreleased)
 
   BUG FIXES
 
+    HADOOP-12617. SPNEGO authentication request to non-default realm gets
+    default realm name inserted in target server principal. (mattf)
+
     HADOOP-11473. test-patch says "-1 overall" even when all checks are +1
     (Jason Lowe via raviprak)