diff --git a/hadoop-common-project/hadoop-auth/pom.xml b/hadoop-common-project/hadoop-auth/pom.xml
index 1d913a637c..8eeafa574a 100644
--- a/hadoop-common-project/hadoop-auth/pom.xml
+++ b/hadoop-common-project/hadoop-auth/pom.xml
@@ -97,6 +97,12 @@
httpclient
compile
+
+ org.apache.directory.server
+ apacheds-kerberos-codec
+ 2.0.0-M15
+ compile
+
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 6435e75f5e..ca0fce2251 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
@@ -17,18 +17,27 @@
*/
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.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
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.Oid;
-import static org.apache.hadoop.util.PlatformName.IBM_JAVA;
-
public class KerberosUtil {
/* Return the Kerberos login module name */
@@ -103,4 +112,48 @@ public class KerberosUtil {
// with uppercase characters.
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 principals = new HashSet();
+ List 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 matchingPrincipals = new ArrayList();
+ for (String principal : principals) {
+ if (pattern.matcher(principal).matches()) {
+ matchingPrincipals.add(principal);
+ }
+ }
+ principals = matchingPrincipals.toArray(new String[0]);
+ }
+ return principals;
+ }
}
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 7da78aa20e..b0e8f04a8f 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
@@ -16,13 +16,39 @@
*/
package org.apache.hadoop.security.authentication.util;
-import org.junit.Assert;
-
+import java.io.File;
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;
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
public void testGetServerPrincipal() throws IOException {
@@ -51,4 +77,84 @@ public class TestKerberosUtil {
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 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 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 lstEntries = new ArrayList();
+ 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));
+ }
}
\ No newline at end of file
diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt
index efb94c0be2..4d0370a0fe 100644
--- a/hadoop-common-project/hadoop-common/CHANGES.txt
+++ b/hadoop-common-project/hadoop-common/CHANGES.txt
@@ -358,6 +358,9 @@ Release 2.5.0 - UNRELEASED
HADOOP-10535. Make the retry numbers in ActiveStandbyElector configurable.
(jing9)
+ HADOOP-10322. Add ability to read principal names from a keytab.
+ (Benoy Antony and Daryn Sharp via kihwal)
+
OPTIMIZATIONS
BUG FIXES