HADOOP-10322. Add ability to read principal names from a keytab. Contributed by Benoy Antony and Daryn Sharp.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1590637 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Kihwal Lee 2014-04-28 13:53:27 +00:00
parent 39abe66822
commit 295b58bb99
4 changed files with 172 additions and 4 deletions

View File

@ -97,6 +97,12 @@
<artifactId>httpclient</artifactId> <artifactId>httpclient</artifactId>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-kerberos-codec</artifactId>
<version>2.0.0-M15</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -17,18 +17,27 @@
*/ */
package org.apache.hadoop.security.authentication.util; package org.apache.hadoop.security.authentication.util;
import static org.apache.hadoop.util.PlatformName.IBM_JAVA;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.directory.server.kerberos.shared.keytab.Keytab;
import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry;
import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSException;
import org.ietf.jgss.Oid; import org.ietf.jgss.Oid;
import static org.apache.hadoop.util.PlatformName.IBM_JAVA;
public class KerberosUtil { public class KerberosUtil {
/* Return the Kerberos login module name */ /* Return the Kerberos login module name */
@ -103,4 +112,48 @@ public static final String getServicePrincipal(String service, String hostname)
// with uppercase characters. // with uppercase characters.
return service + "/" + fqdn.toLowerCase(Locale.US); return service + "/" + fqdn.toLowerCase(Locale.US);
} }
/**
* Get all the unique principals present in the keytabfile.
*
* @param keytabFileName
* Name of the keytab file to be read.
* @return list of unique principals in the keytab.
* @throws IOException
* If keytab entries cannot be read from the file.
*/
static final String[] getPrincipalNames(String keytabFileName) throws IOException {
Keytab keytab = Keytab.read(new File(keytabFileName));
Set<String> principals = new HashSet<String>();
List<KeytabEntry> entries = keytab.getEntries();
for (KeytabEntry entry: entries){
principals.add(entry.getPrincipalName().replace("\\", "/"));
}
return principals.toArray(new String[0]);
}
/**
* Get all the unique principals from keytabfile which matches a pattern.
*
* @param keytab
* Name of the keytab file to be read.
* @param pattern
* pattern to be matched.
* @return list of unique principals which matches the pattern.
* @throws IOException
*/
public static final String[] getPrincipalNames(String keytab,
Pattern pattern) throws IOException {
String[] principals = getPrincipalNames(keytab);
if (principals.length != 0) {
List<String> matchingPrincipals = new ArrayList<String>();
for (String principal : principals) {
if (pattern.matcher(principal).matches()) {
matchingPrincipals.add(principal);
}
}
principals = matchingPrincipals.toArray(new String[0]);
}
return principals;
}
} }

View File

@ -16,13 +16,39 @@
*/ */
package org.apache.hadoop.security.authentication.util; package org.apache.hadoop.security.authentication.util;
import org.junit.Assert; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.directory.server.kerberos.shared.keytab.Keytab;
import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry;
import org.apache.directory.shared.kerberos.KerberosTime;
import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
import org.apache.directory.shared.kerberos.components.EncryptionKey;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
public class TestKerberosUtil { public class TestKerberosUtil {
static String testKeytab = "test.keytab";
static String[] testPrincipals = new String[]{
"HTTP@testRealm",
"test/testhost@testRealm",
"HTTP/testhost@testRealm",
"HTTP1/testhost@testRealm",
"HTTP/testhostanother@testRealm"
};
@After
public void deleteKeytab() {
File keytabFile = new File(testKeytab);
if (keytabFile.exists()){
keytabFile.delete();
}
}
@Test @Test
public void testGetServerPrincipal() throws IOException { public void testGetServerPrincipal() throws IOException {
@ -51,4 +77,84 @@ public void testGetServerPrincipal() throws IOException {
service + "/" + testHost.toLowerCase(), service + "/" + testHost.toLowerCase(),
KerberosUtil.getServicePrincipal(service, testHost.toLowerCase())); KerberosUtil.getServicePrincipal(service, testHost.toLowerCase()));
} }
@Test
public void testGetPrincipalNamesMissingKeytab() {
try {
KerberosUtil.getPrincipalNames(testKeytab);
Assert.fail("Exception should have been thrown");
} catch (IOException e) {
//expects exception
}
}
@Test
public void testGetPrincipalNamesMissingPattern() throws IOException {
createKeyTab(testKeytab, new String[]{"test/testhost@testRealm"});
try {
KerberosUtil.getPrincipalNames(testKeytab, null);
Assert.fail("Exception should have been thrown");
} catch (Exception e) {
//expects exception
}
}
@Test
public void testGetPrincipalNamesFromKeytab() throws IOException {
createKeyTab(testKeytab, testPrincipals);
// read all principals in the keytab file
String[] principals = KerberosUtil.getPrincipalNames(testKeytab);
Assert.assertNotNull("principals cannot be null", principals);
int expectedSize = 0;
List<String> principalList = Arrays.asList(principals);
for (String principal : testPrincipals) {
Assert.assertTrue("missing principal "+principal,
principalList.contains(principal));
expectedSize++;
}
Assert.assertEquals(expectedSize, principals.length);
}
@Test
public void testGetPrincipalNamesFromKeytabWithPattern() throws IOException {
createKeyTab(testKeytab, testPrincipals);
// read the keytab file
// look for principals with HTTP as the first part
Pattern httpPattern = Pattern.compile("HTTP/.*");
String[] httpPrincipals =
KerberosUtil.getPrincipalNames(testKeytab, httpPattern);
Assert.assertNotNull("principals cannot be null", httpPrincipals);
int expectedSize = 0;
List<String> httpPrincipalList = Arrays.asList(httpPrincipals);
for (String principal : testPrincipals) {
if (httpPattern.matcher(principal).matches()) {
Assert.assertTrue("missing principal "+principal,
httpPrincipalList.contains(principal));
expectedSize++;
}
}
Assert.assertEquals(expectedSize, httpPrincipals.length);
}
private void createKeyTab(String fileName, String[] principalNames)
throws IOException {
//create a test keytab file
List<KeytabEntry> lstEntries = new ArrayList<KeytabEntry>();
for (String principal : principalNames){
// create 3 versions of the key to ensure methods don't return
// duplicate principals
for (int kvno=1; kvno <= 3; kvno++) {
EncryptionKey key = new EncryptionKey(
EncryptionType.UNKNOWN, "samplekey1".getBytes(), kvno);
KeytabEntry keytabEntry = new KeytabEntry(
principal, 1 , new KerberosTime(), (byte) 1, key);
lstEntries.add(keytabEntry);
}
}
Keytab keytab = Keytab.getInstance();
keytab.setEntries(lstEntries);
keytab.write(new File(testKeytab));
}
} }

View File

@ -358,6 +358,9 @@ Release 2.5.0 - UNRELEASED
HADOOP-10535. Make the retry numbers in ActiveStandbyElector configurable. HADOOP-10535. Make the retry numbers in ActiveStandbyElector configurable.
(jing9) (jing9)
HADOOP-10322. Add ability to read principal names from a keytab.
(Benoy Antony and Daryn Sharp via kihwal)
OPTIMIZATIONS OPTIMIZATIONS
BUG FIXES BUG FIXES