s4-ldb: make DN escaping/unescaping consistent
authorAndrew Tridgell <tridge@samba.org>
Fri, 13 Nov 2009 06:48:35 +0000 (17:48 +1100)
committerAndrew Tridgell <tridge@samba.org>
Fri, 13 Nov 2009 08:32:46 +0000 (19:32 +1100)
The DN escape function was using the form \c where c is any
character. The unescape function was using \XX where XX is a 2 digit
hex number. The asymmetry led to quite a few problems when we start to
deal with DNs containing escape chars, such as CN=foo\0ADEL:XXX. The
result was a DN that was not accessible.

This patch changes the escaping to follow RFC2253 much more
closely. We accept either type of escape, and produce the two types of
escape, depending on the character being escaped

source4/lib/ldb/common/ldb_dn.c

index 9ac654973a4a6949cbea53d89efaf041d537c153..3721fa733352de3e7c7009271591d3bfd6686d9f 100644 (file)
@@ -180,6 +180,7 @@ struct ldb_dn *ldb_dn_new_fmt(void *mem_ctx,
        return NULL;
 }
 
+/* see RFC2253 section 2.4 */
 static int ldb_dn_escape_internal(char *dst, const char *src, int len)
 {
        const char *p, *s;
@@ -190,8 +191,7 @@ static int ldb_dn_escape_internal(char *dst, const char *src, int len)
        d = dst;
 
        while (p - src < len) {
-
-               p += strcspn(p, ",=\n+<>#;\\\"");
+               p += strcspn(p, ",=\n\r+<>#;\\\" ");
 
                if (p - src == len) /* found no escapable chars */
                        break;
@@ -199,14 +199,46 @@ static int ldb_dn_escape_internal(char *dst, const char *src, int len)
                /* copy the part of the string before the stop */
                memcpy(d, s, p - s);
                d += (p - s); /* move to current position */
+               
+               switch (*p) {
+               case ' ':
+                       if (p == src || (p-src)==(len-1)) {
+                               /* if at the beginning or end
+                                * of the string then escape */
+                               *d++ = '\\';
+                               *d++ = *p++;                                     
+                       } else {
+                               /* otherwise don't escape */
+                               *d++ = *p++;
+                       }
+                       break;
 
-               if (*p) { /* it is a normal escapable character */
+               case '#':
+                       /* despite the RFC, windows escapes a #
+                          anywhere in the string */
+               case ',':
+               case '+':
+               case '"':
+               case '\\':
+               case '<':
+               case '>':
+               case '?':
+                       /* these must be escaped using \c form */
                        *d++ = '\\';
                        *d++ = *p++;
-               } else { /* we have a zero byte in the string */
-                       strncpy(d, "\00", 3); /* escape the zero */
-                       d += 3;
-                       p++; /* skip the zero */
+                       break;
+
+               default: {
+                       /* any others get \XX form */
+                       unsigned char v;
+                       const char *hexbytes = "0123456789ABCDEF";
+                       v = *(unsigned char *)p;
+                       *d++ = '\\';
+                       *d++ = hexbytes[v>>4];
+                       *d++ = hexbytes[v&0xF];
+                       p++;
+                       break;
+               }
                }
                s = p; /* move forward */
        }
@@ -582,12 +614,13 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
 
                                continue;
 
-                       case '=':
                        case '+':
+                       case '=':
                                /* to main compatibility with earlier
                                versions of ldb indexing, we have to
                                accept the base64 encoded binary index
-                               values, which contain a '=' */
+                               values, which contain a '+' or '='
+                               which should normally be escaped */
                                if (is_index) {
                                        if ( t ) t = NULL;
                                        *d++ = *p++;
@@ -595,12 +628,10 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
                                        break;
                                }
                                /* fall through */
-                       case '\n':
+                       case '\"':
                        case '<':
                        case '>':
-                       case '#':
                        case ';':
-                       case '\"':
                                /* a string with not escaped specials is invalid (tested) */
                                if ( ! escape) {
                                        ldb_dn_mark_invalid(dn);
@@ -630,17 +661,20 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
 
                        default:
                                if (escape) {
-                                       if (sscanf(p, "%02x", &x) != 1) {
-                                               /* invalid escaping sequence */
-                                               ldb_dn_mark_invalid(dn);
-                                               goto failed;
+                                       if (isxdigit(p[0]) && isxdigit(p[1])) {
+                                               if (sscanf(p, "%02x", &x) != 1) {
+                                                       /* invalid escaping sequence */
+                                                       ldb_dn_mark_invalid(dn);
+                                                       goto failed;
+                                               }
+                                               p += 2;
+                                               *d++ = (unsigned char)x;
+                                       } else {
+                                               *d++ = *p++;
                                        }
-                                       escape = false;
 
-                                       p += 2;
-                                       *d++ = (unsigned char)x;
+                                       escape = false;
                                        l++;
-
                                        if ( t ) t = NULL;
                                        break;
                                }