From 19f8f2d3e47501c94f28260ad6ae3b2c3bb4d92f Mon Sep 17 00:00:00 2001
From: Quanah Gibson-Mount <quanah@openldap.org>
Date: Mon, 10 Nov 2008 19:03:48 +0000
Subject: [PATCH] ITS#5750

---
 CHANGES                     |  2 ++
 servers/slapd/schema_init.c | 44 ++++++++++---------------------------
 tests/data/dn.out           | 27 ++++++++++++-----------
 tests/data/test-dn.ldif     |  4 ++--
 tests/scripts/test026-dn    | 15 ++++++++++++-
 5 files changed, 43 insertions(+), 49 deletions(-)

diff --git a/CHANGES b/CHANGES
index 9ec02b99f1..5aa73d1e32 100644
--- a/CHANGES
+++ b/CHANGES
@@ -3,8 +3,10 @@ OpenLDAP 2.4 Change Log
 OpenLDAP 2.4.13 Engineering
 	Fixed liblutil hex conversion (ITS#5699)
 	Added slapd GSSAPI refactoring (ITS#5369)
+	Fixed slapd nameUIDPretty bitstring parsing (ITS#5750)
 	Fixed slapd overlay/database open with real structure (ITS#5724)
 	Fixed slapd parsing of read entry control (ITS#5741)
+	Added slapd PMI schema (ITS#5695)
 	Added slapd private databases in global overlays (ITS#5735,ITS#5736)
 	Added slapd support for certificateListExactMatch (ITS#5700)
 	Fixed slapd-bdb/hdb invalid db crash (ITS#5698)
diff --git a/servers/slapd/schema_init.c b/servers/slapd/schema_init.c
index 28f2498a42..ca508cfdfe 100644
--- a/servers/slapd/schema_init.c
+++ b/servers/slapd/schema_init.c
@@ -1105,12 +1105,7 @@ bitStringValidate(
   ...
       
  *
- * Note: normalization strips any leading "0"s, unless the
- * bit string is exactly "'0'B", so the normalized example,
- * in slapd, would result in
- * 
- * 1.3.6.1.4.1.1466.0=#04024869,o=test,c=gb#'101'B
- * 
+ * Note:
  * RFC 4514 clarifies that SHARP, i.e. "#", doesn't have to
  * be escaped except when at the beginning of a value, the
  * definition of Name and Optional UID appears to be flawed,
@@ -1134,11 +1129,11 @@ bitStringValidate(
  *
  * in fact "com#'1'B" is a valid IA5 string.
  *
- * As a consequence, current slapd code assumes that the
- * presence of portions of a BitString at the end of the string 
- * representation of a NameAndOptionalUID means a BitString
- * is expected, and cause an error otherwise.  This is quite
- * arbitrary, and might change in the future.
+ * As a consequence, current slapd code takes the presence of
+ * #<valid BitString> at the end of the string representation
+ * of a NameAndOptionalUID to mean this is indeed a BitString.
+ * This is quite arbitrary - it has changed the past and might
+ * change in the future.
  */
 
 
@@ -1209,7 +1204,8 @@ nameUIDPretty(
 
 			if ( rc == LDAP_SUCCESS ) {
 				ber_dupbv_x( &dnval, val, ctx );
-				dnval.bv_len -= uidval.bv_len + 1;
+				uidval.bv_val--;
+				dnval.bv_len -= ++uidval.bv_len;
 				dnval.bv_val[dnval.bv_len] = '\0';
 
 			} else {
@@ -1226,36 +1222,18 @@ nameUIDPretty(
 		}
 
 		if( !BER_BVISNULL( &uidval ) ) {
-			int	i, c, got1;
 			char	*tmp;
 
 			tmp = slap_sl_realloc( out->bv_val, out->bv_len 
-				+ STRLENOF( "#" ) + uidval.bv_len + 1,
+				+ uidval.bv_len + 1,
 				ctx );
 			if( tmp == NULL ) {
 				ber_memfree_x( out->bv_val, ctx );
 				return LDAP_OTHER;
 			}
 			out->bv_val = tmp;
-			out->bv_val[out->bv_len++] = '#';
-			out->bv_val[out->bv_len++] = '\'';
-
-			got1 = uidval.bv_len < sizeof("'0'B"); 
-			for( i = 1; i < uidval.bv_len - 2; i++ ) {
-				c = uidval.bv_val[i];
-				switch(c) {
-					case '0':
-						if( got1 ) out->bv_val[out->bv_len++] = c;
-						break;
-					case '1':
-						got1 = 1;
-						out->bv_val[out->bv_len++] = c;
-						break;
-				}
-			}
-
-			out->bv_val[out->bv_len++] = '\'';
-			out->bv_val[out->bv_len++] = 'B';
+			memcpy( out->bv_val + out->bv_len, uidval.bv_val, uidval.bv_len );
+			out->bv_len += uidval.bv_len;
 			out->bv_val[out->bv_len] = '\0';
 		}
 	}
diff --git a/tests/data/dn.out b/tests/data/dn.out
index 5cc8214c50..24019e58c3 100644
--- a/tests/data/dn.out
+++ b/tests/data/dn.out
@@ -78,15 +78,15 @@ objectClass: groupOfUniqueNames
 cn: Name and Optional UID
 uniqueMember: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
 uniqueMember: #'1'B
-uniqueMember: #'10'B
+uniqueMember: #'0010'B
 uniqueMember: dc=example,dc=com#'1000'B
-uniqueMember: dc=example,dc=com#'0'B
+uniqueMember: dc=example,dc=com#''B
 description: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com //
   only DN portion
 description: #'1'B // empty "" DN
 description: #'0010'B // empty "" DN with leading '0's
 description: dc=example,dc=com#'1000'B // with DN portion
-description: dc=example,dc=com#'0'B // with DN portion and just one '0'
+description: dc=example,dc=com#''B // with DN portion + bitstring with no bits
 
 dn: ou=Related Syntaxes,dc=example,dc=com
 objectClass: organizationalUnit
@@ -183,20 +183,21 @@ description: testUUID=597ae2f6-16a6-1027-98f4-abcdefABCDEF,DC=Example
 
 # Searching database for nameAndOptionalUID="dc=example,dc=com"...
 # Searching database for nameAndOptionalUID="dc=example,dc=com#'001000'B"...
+# Searching database for nameAndOptionalUID="dc=example,dc=com#'1000'B"...
 dn: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
 objectClass: groupOfUniqueNames
 cn: Name and Optional UID
 uniqueMember: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
 uniqueMember: #'1'B
-uniqueMember: #'10'B
+uniqueMember: #'0010'B
 uniqueMember: dc=example,dc=com#'1000'B
-uniqueMember: dc=example,dc=com#'0'B
+uniqueMember: dc=example,dc=com#''B
 description: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com //
   only DN portion
 description: #'1'B // empty "" DN
 description: #'0010'B // empty "" DN with leading '0's
 description: dc=example,dc=com#'1000'B // with DN portion
-description: dc=example,dc=com#'0'B // with DN portion and just one '0'
+description: dc=example,dc=com#''B // with DN portion + bitstring with no bits
 
 # Searching database for uniqueMember~="dc=example,dc=com" (approx)...
 dn: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
@@ -204,29 +205,29 @@ objectClass: groupOfUniqueNames
 cn: Name and Optional UID
 uniqueMember: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
 uniqueMember: #'1'B
-uniqueMember: #'10'B
+uniqueMember: #'0010'B
 uniqueMember: dc=example,dc=com#'1000'B
-uniqueMember: dc=example,dc=com#'0'B
+uniqueMember: dc=example,dc=com#''B
 description: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com //
   only DN portion
 description: #'1'B // empty "" DN
 description: #'0010'B // empty "" DN with leading '0's
 description: dc=example,dc=com#'1000'B // with DN portion
-description: dc=example,dc=com#'0'B // with DN portion and just one '0'
+description: dc=example,dc=com#''B // with DN portion + bitstring with no bits
 
-# Searching database for uniqueMember~="dc=example,dc=com#'001000'B" (approx)...
+# Searching database for uniqueMember~="dc=example,dc=com#'1000'B" (approx)...
 dn: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
 objectClass: groupOfUniqueNames
 cn: Name and Optional UID
 uniqueMember: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
 uniqueMember: #'1'B
-uniqueMember: #'10'B
+uniqueMember: #'0010'B
 uniqueMember: dc=example,dc=com#'1000'B
-uniqueMember: dc=example,dc=com#'0'B
+uniqueMember: dc=example,dc=com#''B
 description: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com //
   only DN portion
 description: #'1'B // empty "" DN
 description: #'0010'B // empty "" DN with leading '0's
 description: dc=example,dc=com#'1000'B // with DN portion
-description: dc=example,dc=com#'0'B // with DN portion and just one '0'
+description: dc=example,dc=com#''B // with DN portion + bitstring with no bits
 
diff --git a/tests/data/test-dn.ldif b/tests/data/test-dn.ldif
index 412a546a23..15cc678137 100644
--- a/tests/data/test-dn.ldif
+++ b/tests/data/test-dn.ldif
@@ -226,12 +226,12 @@ uniqueMember: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
 uniqueMember: #'1'B
 uniqueMember: #'0010'B
 uniqueMember: dc=example,dc=com#'1000'B
-uniqueMember: dc=example,dc=com#'0'B
+uniqueMember: dc=example,dc=com#''B
 description: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com // only DN portion
 description: #'1'B // empty "" DN
 description: #'0010'B // empty "" DN with leading '0's
 description: dc=example,dc=com#'1000'B // with DN portion
-description: dc=example,dc=com#'0'B // with DN portion and just one '0'
+description: dc=example,dc=com#''B // with DN portion + bitstring with no bits
 
 dn: cn=Should Fail 1,cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
 objectClass: groupOfUniqueNames
diff --git a/tests/scripts/test026-dn b/tests/scripts/test026-dn
index 02d2afc833..32e2e8b81f 100755
--- a/tests/scripts/test026-dn
+++ b/tests/scripts/test026-dn
@@ -117,6 +117,19 @@ if test $RC != 0 ; then
 	exit $RC
 fi
 
+DN="dc=example,dc=com#'1000'B"
+echo "Searching database for nameAndOptionalUID=\"$DN\"..."
+echo "# Searching database for nameAndOptionalUID=\"$DN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT1 \
+	"(uniqueMember=$DN)" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+	echo "ldapsearch failed ($RC)!"
+	test $KILLSERVERS != no && kill -HUP $KILLPIDS
+	exit $RC
+fi
+
 DN="dc=example,dc=com"
 echo "Searching database for uniqueMember~=\"$DN\" (approx)..."
 echo "# Searching database for uniqueMember~=\"$DN\" (approx)..." >> $SEARCHOUT
@@ -130,7 +143,7 @@ if test $RC != 0 ; then
 	exit $RC
 fi
 
-DN="dc=example,dc=com#'001000'B"
+DN="dc=example,dc=com#'1000'B"
 echo "Searching database for uniqueMember~=\"$DN\" (approx)..."
 echo "# Searching database for uniqueMember~=\"$DN\" (approx)..." >> $SEARCHOUT
 $LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT1 \
-- 
GitLab