Repository: ambari
Updated Branches:
  refs/heads/trunk bc5155b5d -> 63b78f2e7


AMBARI-9637. Kerberos: Escape special characters in Distinguished Names used 
for queries in Active Directory (rlevas)


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

Branch: refs/heads/trunk
Commit: 63b78f2e7dbc9acf560c94c128122359a5dfe61a
Parents: bc5155b
Author: Robert Levas <rle...@hortonworks.com>
Authored: Mon Feb 16 11:08:33 2015 -0500
Committer: Robert Levas <rle...@hortonworks.com>
Committed: Mon Feb 16 11:08:46 2015 -0500

----------------------------------------------------------------------
 .../kerberos/ADKerberosOperationHandler.java    | 67 +++++++++++++++++++-
 .../kerberos/KerberosOperationHandler.java      | 24 +++++++
 .../ADKerberosOperationHandlerTest.java         | 36 +++++++++++
 .../kerberos/KerberosOperationHandlerTest.java  | 35 ++++++++++
 4 files changed, 161 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/63b78f2e/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandler.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandler.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandler.java
index 2dbd50e..faa813c 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandler.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandler.java
@@ -35,13 +35,19 @@ import javax.naming.directory.*;
 import javax.naming.ldap.Control;
 import javax.naming.ldap.InitialLdapContext;
 import javax.naming.ldap.LdapContext;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
 import java.io.StringWriter;
 import java.io.UnsupportedEncodingException;
 import java.lang.reflect.Type;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
+import java.util.Set;
 
 /**
  * Implementation of <code>KerberosOperationHandler</code> to created 
principal in Active Directory
@@ -53,6 +59,34 @@ public class ADKerberosOperationHandler extends 
KerberosOperationHandler {
   private static final String LDAP_CONTEXT_FACTORY_CLASS = 
"com.sun.jndi.ldap.LdapCtxFactory";
 
   /**
+   * A Set of special characters that need to be escaped if they exist within 
a value in a
+   * Distinguished Name.
+   *
+   * See 
http://social.technet.microsoft.com/wiki/contents/articles/5312.active-directory-characters-to-escape.aspx
+   */
+  private static final Set<Character> SPECIAL_DN_CHARACTERS = 
Collections.unmodifiableSet(
+      new HashSet<Character>() {
+        {
+          add('/');
+          add(',');
+          add('\\');
+          add('#');
+          add('+');
+          add('<');
+          add('>');
+          add(';');
+          add('"');
+          add('=');
+          add(' ');
+        }
+      });
+
+  /**
+   * The character to use to escape a special character within a value in a 
Distinguished Name
+   */
+  private static final Character DN_ESCAPE_CHARACTER = '\\';
+
+  /**
    * A String containing the URL for the LDAP interface for the relevant 
Active Directory
    */
   private String ldapUrl = null;
@@ -329,7 +363,8 @@ public class ADKerberosOperationHandler extends 
KerberosOperationHandler {
       String dn = 
findPrincipalDN(deconstructPrincipal.getNormalizedPrincipal());
 
       if (dn != null) {
-        ldapContext.modifyAttributes(dn,
+        ldapContext.modifyAttributes(
+            escapeDNCharacters(dn),
             new ModificationItem[]{
                 new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new 
BasicAttribute("unicodePwd", String.format("\"%s\"", 
password).getBytes("UTF-16LE")))
             }
@@ -559,4 +594,34 @@ public class ADKerberosOperationHandler extends 
KerberosOperationHandler {
 
     return dn;
   }
+
+  /**
+   * Iterates through the characters of the given distinguished name to escape 
special characters
+   *
+   * @param dn the distinguished name to process
+   * @return the distinguished name with escaped characters
+   * @see #escapeCharacters(String, java.util.Set, Character)
+   */
+  protected String escapeDNCharacters(String dn) throws InvalidNameException {
+    if ((dn == null) || dn.isEmpty()) {
+      return dn;
+    } else {
+      LdapName name = new LdapName(dn);
+      List<Rdn> rdns = name.getRdns();
+
+      if ((rdns == null) || rdns.isEmpty()) {
+        throw new InvalidNameException(String.format("One or more RDNs are 
expected for a DN of %s", dn));
+      }
+
+      StringBuilder builder = new StringBuilder();
+      for (Rdn rdn : rdns) {
+        builder.insert(0,
+            String.format(",%s=%s",
+                rdn.getType(),
+                escapeCharacters((String) rdn.getValue(), 
SPECIAL_DN_CHARACTERS, DN_ESCAPE_CHARACTER)));
+      }
+
+      return builder.substring(1);
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/63b78f2e/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandler.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandler.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandler.java
index c51475e..9d41691 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandler.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandler.java
@@ -613,4 +613,28 @@ public abstract class KerberosOperationHandler {
     return encryptionTypes;
   }
 
+  /**
+   * Iterates through the characters in a string to escape special characters
+   *
+   * @param string             the String to process
+   * @param charactersToEscape a Set of characters declaring the special 
characters to escape
+   * @param escapeCharacter    a character to use for escaping
+   * @return the string with escaped characters
+   */
+  protected String escapeCharacters(String string, Set<Character> 
charactersToEscape, Character escapeCharacter) {
+    if ((string == null) || string.isEmpty() || (charactersToEscape == null) 
|| charactersToEscape.isEmpty()) {
+      return string;
+    } else {
+      StringBuilder builder = new StringBuilder();
+
+      for (char character : string.toCharArray()) {
+        if (charactersToEscape.contains(character)) {
+          builder.append(escapeCharacter);
+        }
+        builder.append(character);
+      }
+
+      return builder.toString();
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/63b78f2e/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandlerTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandlerTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandlerTest.java
index e5d7505..2da692e 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandlerTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandlerTest.java
@@ -28,6 +28,7 @@ import org.junit.Test;
 
 import javax.naming.AuthenticationException;
 import javax.naming.CommunicationException;
+import javax.naming.InvalidNameException;
 import javax.naming.Name;
 import javax.naming.NamingEnumeration;
 import javax.naming.directory.Attributes;
@@ -441,6 +442,21 @@ public class ADKerberosOperationHandlerTest extends 
KerberosOperationHandlerTest
 
   }
 
+  @Test
+  public void testEscapeDistinguishedName() throws NoSuchMethodException, 
InvalidNameException {
+    ADKerberosOperationHandler handler = new ADKerberosOperationHandler();
+
+    try {
+      handler.escapeDNCharacters("nn/c6501.ambari.apache.org");
+      Assert.fail("Expected InvalidNameException");
+    } catch (InvalidNameException e) {
+      // This is expected
+    }
+
+    
Assert.assertEquals("CN=nn\\/c6501.ambari.apache.org,OU=HDP,DC=HDP01,DC=LOCAL",
+        
handler.escapeDNCharacters("CN=nn/c6501.ambari.apache.org,OU=HDP,DC=HDP01,DC=LOCAL"));
+  }
+
   /**
    * Implementation to illustrate the use of operations on this class
    *
@@ -490,6 +506,24 @@ public class ADKerberosOperationHandlerTest extends 
KerberosOperationHandlerTest
 
     handler.close();
 
+    handler.open(credentials, realm, kerberosEnvMap);
+
+    String evaluatedPrincipal;
+
+    evaluatedPrincipal = "nn/c6501.ambari.apache.org@" + DEFAULT_REALM;
+    if (handler.principalExists(evaluatedPrincipal)) {
+      handler.setPrincipalPassword(evaluatedPrincipal, 
handler.createSecurePassword());
+    } else {
+      handler.createPrincipal(evaluatedPrincipal, 
handler.createSecurePassword(), true);
+    }
+
+    evaluatedPrincipal = "hdfs@" + DEFAULT_REALM;
+    if (handler.principalExists(evaluatedPrincipal)) {
+      handler.setPrincipalPassword(evaluatedPrincipal, 
handler.createSecurePassword());
+    } else {
+      handler.createPrincipal(evaluatedPrincipal, 
handler.createSecurePassword(), true);
+    }
+
     
kerberosEnvMap.put(ADKerberosOperationHandler.KERBEROS_ENV_CREATE_ATTRIBUTES_TEMPLATE,
         "#set( $user = \"${principal_primary}-${principal_digest}\" )" +
             "{" +
@@ -511,6 +545,8 @@ public class ADKerberosOperationHandlerTest extends 
KerberosOperationHandlerTest
             "}"
     );
 
+    handler.close();
+
     handler.open(credentials, realm, kerberosEnvMap);
 
     // remove the principal

http://git-wip-us.apache.org/repos/asf/ambari/blob/63b78f2e/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandlerTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandlerTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandlerTest.java
index 8dab409..f4551d2 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandlerTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandlerTest.java
@@ -28,8 +28,10 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 
+import javax.naming.InvalidNameException;
 import java.io.File;
 import java.io.FileInputStream;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -244,6 +246,39 @@ public abstract class KerberosOperationHandlerTest extends 
EasyMockSupport {
     );
   }
 
+  @Test
+  public void testEscapeCharacters() throws KerberosOperationException {
+    KerberosOperationHandler handler = createHandler();
+
+    HashSet<Character> specialCharacters = new HashSet<Character>() {
+      {
+        add('/');
+        add(',');
+        add('\\');
+        add('#');
+        add('+');
+        add('<');
+        add('>');
+        add(';');
+        add('"');
+        add('=');
+        add(' ');
+      }
+    };
+
+    Assert.assertEquals("\\/\\,\\\\\\#\\+\\<\\>\\;\\\"\\=\\ ", 
handler.escapeCharacters("/,\\#+<>;\"= ", specialCharacters, '\\'));
+    Assert.assertNull(handler.escapeCharacters(null, specialCharacters, '\\'));
+    Assert.assertEquals("", handler.escapeCharacters("", specialCharacters, 
'\\'));
+    Assert.assertEquals("nothing_special_here", 
handler.escapeCharacters("nothing_special_here", specialCharacters, '\\'));
+    Assert.assertEquals("\\/\\,\\\\\\#\\+\\<\\>\\;\\\"\\=\\ ", 
handler.escapeCharacters("/,\\#+<>;\"= ", specialCharacters, '\\'));
+
+    Assert.assertEquals("nothing<>special#here!", 
handler.escapeCharacters("nothing<>special#here!", null, '\\'));
+    Assert.assertEquals("nothing<>special#here!", 
handler.escapeCharacters("nothing<>special#here!", 
Collections.<Character>emptySet(), '\\'));
+    Assert.assertEquals("nothing<>special#here!", 
handler.escapeCharacters("nothing<>special#here!", Collections.singleton('?'), 
'\\'));
+    Assert.assertEquals("\\A's are special!", handler.escapeCharacters("A's 
are special!", Collections.singleton('A'), '\\'));
+  }
+
+
   private KerberosOperationHandler createHandler() throws 
KerberosOperationException {
     KerberosOperationHandler handler = new KerberosOperationHandler() {
 

Reply via email to