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
7013f9d6cd
commit
472541291b
|
@ -84,15 +84,77 @@ public class KerberosUtil {
|
||||||
return (String)getDefaultRealmMethod.invoke(kerbConf, new Object[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 (System.getProperty("java.vendor").contains("IBM")) {
|
||||||
|
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 */
|
/* Return fqdn of the current host */
|
||||||
static String getLocalHostName() throws UnknownHostException {
|
static String getLocalHostName() throws UnknownHostException {
|
||||||
return InetAddress.getLocalHost().getCanonicalHostName();
|
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
|
* hostname to lower case. If hostname is null or "0.0.0.0", it uses
|
||||||
* dynamically looked-up fqdn of the current host instead.
|
* 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
|
* @param service
|
||||||
* Service for which you want to generate the principal.
|
* Service for which you want to generate the principal.
|
||||||
|
@ -102,15 +164,26 @@ public class KerberosUtil {
|
||||||
* @throws UnknownHostException
|
* @throws UnknownHostException
|
||||||
* If no IP address for the local host could be found.
|
* 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 {
|
throws UnknownHostException {
|
||||||
String fqdn = hostname;
|
String fqdn = hostname;
|
||||||
|
String shortprinc = null;
|
||||||
|
String realmString = null;
|
||||||
if (null == fqdn || fqdn.equals("") || fqdn.equals("0.0.0.0")) {
|
if (null == fqdn || fqdn.equals("") || fqdn.equals("0.0.0.0")) {
|
||||||
fqdn = getLocalHostName();
|
fqdn = getLocalHostName();
|
||||||
}
|
}
|
||||||
// convert hostname to lowercase as kerberos does not work with hostnames
|
// convert hostname to lowercase as kerberos does not work with hostnames
|
||||||
// with uppercase characters.
|
// with uppercase characters.
|
||||||
return service + "/" + fqdn.toLowerCase(Locale.ENGLISH);
|
fqdn = fqdn.toLowerCase(Locale.ENGLISH);
|
||||||
|
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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -52,30 +53,46 @@ public class TestKerberosUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetServerPrincipal() throws IOException {
|
public void testGetServerPrincipal()
|
||||||
|
throws IOException, UnknownHostException {
|
||||||
String service = "TestKerberosUtil";
|
String service = "TestKerberosUtil";
|
||||||
String localHostname = KerberosUtil.getLocalHostName();
|
String localHostname = KerberosUtil.getLocalHostName();
|
||||||
String testHost = "FooBar";
|
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.ENGLISH)),
|
||||||
|
defaultRealm);
|
||||||
|
Assert.assertEquals("testGetServerPrincipal assumes realm of testHost 'FooBar' is default",
|
||||||
|
KerberosUtil.getDomainRealm(service + "/" + testHost.toLowerCase(Locale.ENGLISH)),
|
||||||
|
defaultRealm);
|
||||||
|
|
||||||
// send null hostname
|
// send null hostname
|
||||||
Assert.assertEquals("When no hostname is sent",
|
Assert.assertEquals("When no hostname is sent",
|
||||||
service + "/" + localHostname.toLowerCase(Locale.ENGLISH),
|
service + "/" + localHostname.toLowerCase(Locale.ENGLISH) + atDefaultRealm,
|
||||||
KerberosUtil.getServicePrincipal(service, null));
|
KerberosUtil.getServicePrincipal(service, null));
|
||||||
// send empty hostname
|
// send empty hostname
|
||||||
Assert.assertEquals("When empty hostname is sent",
|
Assert.assertEquals("When empty hostname is sent",
|
||||||
service + "/" + localHostname.toLowerCase(Locale.ENGLISH),
|
service + "/" + localHostname.toLowerCase(Locale.ENGLISH) + atDefaultRealm,
|
||||||
KerberosUtil.getServicePrincipal(service, ""));
|
KerberosUtil.getServicePrincipal(service, ""));
|
||||||
// send 0.0.0.0 hostname
|
// send 0.0.0.0 hostname
|
||||||
Assert.assertEquals("When 0.0.0.0 hostname is sent",
|
Assert.assertEquals("When 0.0.0.0 hostname is sent",
|
||||||
service + "/" + localHostname.toLowerCase(Locale.ENGLISH),
|
service + "/" + localHostname.toLowerCase(Locale.ENGLISH) + atDefaultRealm,
|
||||||
KerberosUtil.getServicePrincipal(service, "0.0.0.0"));
|
KerberosUtil.getServicePrincipal(service, "0.0.0.0"));
|
||||||
// send uppercase hostname
|
// send uppercase hostname
|
||||||
Assert.assertEquals("When uppercase hostname is sent",
|
Assert.assertEquals("When uppercase hostname is sent",
|
||||||
service + "/" + testHost.toLowerCase(Locale.ENGLISH),
|
service + "/" + testHost.toLowerCase(Locale.ENGLISH) + atDefaultRealm,
|
||||||
KerberosUtil.getServicePrincipal(service, testHost));
|
KerberosUtil.getServicePrincipal(service, testHost));
|
||||||
// send lowercase hostname
|
// send lowercase hostname
|
||||||
Assert.assertEquals("When lowercase hostname is sent",
|
Assert.assertEquals("When lowercase hostname is sent",
|
||||||
service + "/" + testHost.toLowerCase(Locale.ENGLISH),
|
service + "/" + testHost.toLowerCase(Locale.ENGLISH) + atDefaultRealm,
|
||||||
KerberosUtil.getServicePrincipal(
|
KerberosUtil.getServicePrincipal(
|
||||||
service, testHost.toLowerCase(Locale.ENGLISH)));
|
service, testHost.toLowerCase(Locale.ENGLISH)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -412,6 +412,9 @@ Release 2.8.0 - UNRELEASED
|
||||||
|
|
||||||
BUG FIXES
|
BUG FIXES
|
||||||
|
|
||||||
|
HADOOP-12617. SPNEGO authentication request to non-default realm gets
|
||||||
|
default realm name inserted in target server principal. (mattf)
|
||||||
|
|
||||||
HADOOP-12352. Delay in checkpointing Trash can leave trash for 2 intervals
|
HADOOP-12352. Delay in checkpointing Trash can leave trash for 2 intervals
|
||||||
before deleting (Casey Brotherton via harsh)
|
before deleting (Casey Brotherton via harsh)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue