From 938afc0809554a223224b92f1324f4320ad9f0f1 Mon Sep 17 00:00:00 2001
From: Endi Sukma Dewata <edewata@redhat.com>
Date: Thu, 20 Sep 2012 08:46:06 -0500
Subject: [PATCH] Added unescapeRDNValue() method.

A new method has been added to unescape RDN values in a DN. This
is used to get the correct group member ID from a DN stored in
the uniqueMember attribute.

Ticket #193
---
 base/common/shared/conf/log4j.properties           |   1 +
 .../com/netscape/cmscore/usrgrp/UGSubsystem.java   |   3 +-
 base/util/src/CMakeLists.txt                       |  10 +-
 .../src/com/netscape/cmsutil/ldap/LDAPUtil.java    | 116 ++++++++++++++++++---
 4 files changed, 114 insertions(+), 16 deletions(-)

diff --git a/base/common/shared/conf/log4j.properties b/base/common/shared/conf/log4j.properties
index 42c52513bced2b0a5b995344a85a251e00a039dd..c18ab1f5c5d7c75ca9a7b52a4c78e815ba13f054 100644
--- a/base/common/shared/conf/log4j.properties
+++ b/base/common/shared/conf/log4j.properties
@@ -32,3 +32,4 @@ log4j.logger.org.jboss.resteasy.mock=INFO
 
 # PKI
 log4j.logger.com.netscape.cms=INFO
+log4j.logger.com.netscape.cmsutil=INFO
diff --git a/base/common/src/com/netscape/cmscore/usrgrp/UGSubsystem.java b/base/common/src/com/netscape/cmscore/usrgrp/UGSubsystem.java
index 6b61572410fba19d788350213cd4b96a37fb9750..3b37100609d9329862eaf93abb1b5aef955b35af 100644
--- a/base/common/src/com/netscape/cmscore/usrgrp/UGSubsystem.java
+++ b/base/common/src/com/netscape/cmscore/usrgrp/UGSubsystem.java
@@ -1374,7 +1374,8 @@ public final class UGSubsystem implements IUGSubsystem {
                     (v.substring(i + 1, j)).indexOf('=') > -1 || ((v.substring(3, i)).trim()).length() > 0) {
                 log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_USRGRP_BAD_GROUP_MEMBER", groupName, v));
             } else {
-                grp.addMemberName(v.substring(v.indexOf('=') + 1, v.indexOf(',')));
+                String value = v.substring(v.indexOf('=') + 1, v.indexOf(','));
+                grp.addMemberName(LDAPUtil.unescapeRDNValue(value));
             }
         }
 
diff --git a/base/util/src/CMakeLists.txt b/base/util/src/CMakeLists.txt
index 0f8eb496fb1e0f592776eb01d63442e33b608ba0..8e752946f9c3488c16e0599b5b266c8498acb629 100644
--- a/base/util/src/CMakeLists.txt
+++ b/base/util/src/CMakeLists.txt
@@ -7,6 +7,13 @@ find_file(APACHE_COMMONS_LANG_JAR
         /usr/share/java
 )
 
+find_file(COMMONS_LOGGING_JAR
+    NAMES
+        commons-logging.jar
+    PATHS
+        /usr/share/java
+)
+
 find_file(JSS_JAR
     NAMES
         jss4.jar
@@ -93,7 +100,8 @@ javac(pki-cmsutil-classes
     SOURCES
         com/netscape/cmsutil/*.java
     CLASSPATH
-        ${APACHE_COMMONS_LANG_JAR} ${LDAPJDK_JAR} ${XALAN_JAR} ${XERCES_JAR}
+        ${APACHE_COMMONS_LANG_JAR} ${COMMONS_LOGGING_JAR}
+        ${LDAPJDK_JAR} ${XALAN_JAR} ${XERCES_JAR}
         ${JSS_JAR} ${COMMONS_CODEC_JAR}
     OUTPUT_DIR
         ${CMAKE_BINARY_DIR}/classes
diff --git a/base/util/src/com/netscape/cmsutil/ldap/LDAPUtil.java b/base/util/src/com/netscape/cmsutil/ldap/LDAPUtil.java
index ca894d87c192d01836f1bb1105274c02e707e8c7..dd8b00a2b188f7841f539cf3a10c4e33a2ebecbc 100644
--- a/base/util/src/com/netscape/cmsutil/ldap/LDAPUtil.java
+++ b/base/util/src/com/netscape/cmsutil/ldap/LDAPUtil.java
@@ -32,10 +32,18 @@ import netscape.ldap.util.LDIFContent;
 import netscape.ldap.util.LDIFModifyContent;
 import netscape.ldap.util.LDIFRecord;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
 public class LDAPUtil {
 
-    // special chars are *, (, ), \, null
-    public static String SPECIAL_CHARS = "*()\\\000";
+    public static Log log = LogFactory.getLog(LDAPUtil.class);
+
+    // Filter special chars are *, (, ), \, null according to RFC 4515.
+    public static String FILTER_SPECIAL_CHARS = "*()\\\000";
+
+    // DN special chars according to RFC 1779.
+    public static String DN_SPECIAL_CHARS = ",=\n+<>#;";
 
     /**
      * This method escapes special characters for LDAP filter (RFC 4515).
@@ -46,9 +54,12 @@ public class LDAPUtil {
      * @return escaped string
      */
     public static String escapeFilter(Object object) {
+
+        log.debug("escapeFilter(\""+object+"\")");
         StringBuilder sb = new StringBuilder();
+
         for (char c : object.toString().toCharArray()) {
-            if (SPECIAL_CHARS.indexOf(c) >= 0) {
+            if (FILTER_SPECIAL_CHARS.indexOf(c) >= 0) {
                 sb.append('\\');
                 if (c < 0x10)
                     sb.append('0'); // make sure it's 2-digit
@@ -57,7 +68,11 @@ public class LDAPUtil {
                 sb.append(c);
             }
         }
-        return sb.toString();
+
+        String result = sb.toString();
+        log.debug(" => \""+result+"\"");
+
+        return result;
     }
 
     /**
@@ -68,7 +83,9 @@ public class LDAPUtil {
     }
 
     public static String escapeDN(String v, boolean doubleEscape) {
-        StringBuffer result = new StringBuffer();
+
+        log.debug("escapeDN(\""+v+"\")");
+        StringBuilder sb = new StringBuilder();
 
         // Do we need to escape any characters
         for (int i = 0; i < v.length(); i++) {
@@ -83,27 +100,98 @@ public class LDAPUtil {
                             nextC == ';' || nextC == '\r' || nextC == '\n' ||
                             nextC == '\\' || nextC == '"')) {
                         if (doubleEscape)
-                            result.append('\\');
+                            sb.append('\\');
                     } else {
-                        result.append('\\');
+                        sb.append('\\');
                         if (doubleEscape)
-                            result.append('\\');
+                            sb.append('\\');
                     }
                 } else {
-                    result.append('\\');
+                    sb.append('\\');
                     if (doubleEscape)
-                        result.append('\\');
+                        sb.append('\\');
                 }
             }
             if (c == '\r') {
-                result.append("0D");
+                sb.append("0D");
             } else if (c == '\n') {
-                result.append("0A");
+                sb.append("0A");
             } else {
-                result.append((char) c);
+                sb.append((char) c);
             }
         }
-        return result.toString();
+
+        String result = sb.toString();
+        log.debug(" => \""+result+"\"");
+
+        return result;
+    }
+
+    public static String unescapeRDNValue(String value) {
+
+        log.debug("unescapeRDNValue(\""+value+"\")");
+        StringBuilder sb = new StringBuilder();
+
+        boolean quoted = false;
+        boolean escaped = false;
+        int hex = 0;
+        int hex1 = 0;
+        int hex2 = 0;
+
+        for (char c : value.toCharArray()) {
+
+            // add quoted chars
+            if (quoted) {
+                if (c == '"') {
+                    quoted = false;
+                } else {
+                    sb.append(c);
+                }
+
+            // check starting quote
+            } else if (c == '"') {
+                quoted = true;
+
+            // add escaped char
+            } else if (escaped) {
+                escaped = false;
+
+                if (DN_SPECIAL_CHARS.indexOf(c) >= 0) {
+                    sb.append(c);
+
+                } else { // DS uses \xx instead of #xx
+                    hex1 = Character.digit(c, 16);
+                    hex = 2;
+                }
+
+            // check escape char
+            } else if (c == '\\') {
+                escaped = true;
+
+            // keep first hex digit
+            } else if (hex == 1) {
+                hex1 = Character.digit(c, 16);
+                hex = 2;
+
+            // keep second hex digit, add combined hex digits
+            } else if (hex == 2) {
+                hex2 = Character.digit(c, 16);
+                sb.append((char)(hex1 * 16 + hex2));
+                hex = 0;
+
+            // check hex char
+            } else if (c == '#') {
+                hex = 1;
+
+            } else {
+                sb.append(c);
+            }
+        }
+
+        String result = sb.toString();
+        log.debug(" => \""+result+"\"");
+
+        return result;
     }
 
     public static void importLDIF(LDAPConnection conn, String filename, ArrayList<String> errors) throws IOException {
-- 
1.7.11.2

