HADOOP-12617. SPNEGO authentication request to non-default realm gets default realm name inserted in target server principal. (mattf)
This commit is contained in:
parent
7e4715186d
commit
ada9c2c410
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue