ldb:ldb_dn.c - "ldb_dn_set_extended_component" - free the linearized string when...
[kamenim/samba.git] / source4 / lib / ldb / common / ldb_dn.c
index 639e8b2837f003f85046b9807dc521b5b4d3cd8a..11de31ed6b0fcdfc3b9fdad4643401469f511c09 100644 (file)
@@ -77,9 +77,6 @@ struct ldb_dn {
 
        unsigned int ext_comp_num;
        struct ldb_dn_ext_component *ext_components;
-
-       char extra_type;
-       struct ldb_val extra_val;
 };
 
 /* it is helpful to be able to break on this in gdb */
@@ -103,17 +100,16 @@ struct ldb_dn *ldb_dn_from_ldb_val(void *mem_ctx,
                return NULL;
        }
 
-       /* if the DN starts with B: then it has a binary blob
-        * attached. Called the binary dn parser, which will call back
-        * here for the rest of the DN */
-       if (strdn->data && strncmp((char *)strdn->data, "B:", 2) == 0) {
-               return ldb_dn_binary_from_ldb_val(mem_ctx, ldb, strdn);
-       }
-
        dn = talloc_zero(mem_ctx, struct ldb_dn);
        LDB_DN_NULL_FAILED(dn);
 
-       dn->ldb = ldb;
+       dn->ldb = talloc_get_type(ldb, struct ldb_context);
+       if (dn->ldb == NULL) {
+               /* the caller probably got the arguments to
+                  ldb_dn_new() mixed up */
+               talloc_free(dn);
+               return NULL;
+       }
 
        if (strdn->data && strdn->length) {
                const char *data = (const char *)strdn->data;
@@ -157,173 +153,6 @@ failed:
        return NULL;
 }
 
-/*
-  a version of strhex_to_str internal to ldb, for use by the binary
-  ldb code
- */
-static size_t ldb_strhex_to_str(char *p, size_t p_len, const char *strhex, 
-                               size_t strhex_len)
-{
-       size_t i;
-       size_t num_chars = 0;
-       uint8_t   lonybble, hinybble;
-       const char     *hexchars = "0123456789ABCDEF";
-       char           *p1 = NULL, *p2 = NULL;
-
-       for (i = 0; i < strhex_len && strhex[i] != 0; i++) {
-               if (!(p1 = strchr(hexchars, toupper((unsigned char)strhex[i]))))
-                       break;
-
-               i++; /* next hex digit */
-
-               if (!(p2 = strchr(hexchars, toupper((unsigned char)strhex[i]))))
-                       break;
-
-               /* get the two nybbles */
-               hinybble = PTR_DIFF(p1, hexchars);
-               lonybble = PTR_DIFF(p2, hexchars);
-
-               if (num_chars >= p_len) {
-                       break;
-               }
-
-               p[num_chars] = (hinybble << 4) | lonybble;
-               num_chars++;
-
-               p1 = NULL;
-               p2 = NULL;
-       }
-       return num_chars;
-}
-
-/* strdn may be NULL */
-struct ldb_dn *ldb_dn_binary_from_ldb_val(void *mem_ctx,
-                                         struct ldb_context *ldb,
-                                         const struct ldb_val *strdn)
-{
-       struct ldb_dn *dn;
-       const char *data;
-       size_t len;
-       char *linearized;
-       TALLOC_CTX *tmp_ctx;
-       char *p1;
-       char *p2;
-       uint32_t blen;
-       struct ldb_val bval;
-       struct ldb_val dval;
-       char *dn_str;
-       char *old;
-
-       if (strdn && strdn->data
-           && (strlen((const char*)strdn->data) != strdn->length)) {
-               /* The RDN must not contain a character with value 0x0 */
-               return NULL;
-       }
-
-       if (!strdn->data || strdn->length == 0) {
-               return NULL;
-
-       }
-
-       tmp_ctx = talloc_new(mem_ctx);
-       if (tmp_ctx == NULL) {
-               return NULL;
-       }
-
-       data = (const char *)strdn->data;
-
-       if (data[0] != 'B') {
-               ldb_debug(ldb, LDB_DEBUG_TRACE, __location__ ": no prefix?\n");
-               return NULL;
-       }
-
-       len = strdn->length;
-       linearized = talloc_strndup(tmp_ctx, data, len);
-       if (linearized == NULL) {
-               goto failed;
-       }
-
-       ldb_debug(ldb, LDB_DEBUG_TRACE, __location__ ": processing DN '%s'\n", linearized);
-
-       p1 = linearized;
-
-       p1++; len--;
-       
-       if (p1[0] != ':') {
-               goto failed;
-       }
-       p1++;
-       len--;
-       
-       errno = 0;
-       blen = strtoul(p1, &p2, 10);
-       if (errno != 0) {
-               ldb_debug(ldb, LDB_DEBUG_TRACE, __location__ ": failed\n");
-               goto failed;
-       }
-       if (p2 == NULL) {
-               ldb_debug(ldb, LDB_DEBUG_TRACE, __location__ ": failed\n");
-               goto failed;
-       }
-       if (p2[0] != ':') {
-               ldb_debug(ldb, LDB_DEBUG_TRACE, __location__ ": failed\n");
-               goto failed;
-       }
-       len -= PTR_DIFF(p2,p1);//???
-       p1 = p2+1;
-       len--;
-       
-       if (blen >= len) {
-               ldb_debug(ldb, LDB_DEBUG_TRACE, __location__ ": blen=%u len=%u\n", (unsigned)blen, (unsigned)len);
-               goto failed;
-       }
-
-       p2 = p1 + blen;
-       if (p2[0] != ':') {
-               ldb_debug(ldb, LDB_DEBUG_TRACE, __location__ ": %s", p2);
-               goto failed;
-       }
-       dn_str = p2+1;
-       
-       bval.length = (blen/2)+1;
-       bval.data = talloc_size(tmp_ctx, bval.length);
-       if (bval.data == NULL) {
-               ldb_debug(ldb, LDB_DEBUG_TRACE, __location__ ": err\n");
-               goto failed;
-       }
-       bval.data[bval.length-1] = 0;
-       
-       bval.length = ldb_strhex_to_str((char *)bval.data, bval.length,
-                                       p1, blen);
-       
-       dval.data = (uint8_t *)dn_str;
-       dval.length = strlen(dn_str);
-       
-       dn = ldb_dn_from_ldb_val(mem_ctx, ldb, &dval);
-       if (dn == NULL) {
-               ldb_debug(ldb, LDB_DEBUG_TRACE, __location__ ": err\n");
-               goto failed;
-       }
-       dn->extra_type = data[0];
-       dn->extra_val = bval;
-       talloc_steal(dn, bval.data);
-
-       *dn_str = '\0';
-       old = dn->linearized;
-       dn->linearized = talloc_asprintf(dn, "%s%s", linearized, dn->linearized);
-       talloc_free(old);
-       if (dn->ext_linearized) {
-               old = dn->ext_linearized;
-               dn->ext_linearized = talloc_asprintf(dn, "%s%s", linearized, dn->ext_linearized);
-               talloc_free(old);
-       }
-       
-       return dn;
-failed:
-       talloc_free(tmp_ctx);
-       return NULL;
-}
-
 /* strdn may be NULL */
 struct ldb_dn *ldb_dn_new(void *mem_ctx,
                          struct ldb_context *ldb,
@@ -357,18 +186,18 @@ 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;
        char *d;
-       int l;
+       size_t l;
 
        p = s = src;
        d = dst;
 
        while (p - src < len) {
-
-               p += strcspn(p, ",=\n+<>#;\\\"");
+               p += strcspn(p, ",=\n\r+<>#;\\\" ");
 
                if (p - src == len) /* found no escapable chars */
                        break;
@@ -376,14 +205,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 = *(const unsigned char *)p;
+                       *d++ = '\\';
+                       *d++ = hexbytes[v>>4];
+                       *d++ = hexbytes[v&0xF];
+                       p++;
+                       break;
+               }
                }
                s = p; /* move forward */
        }
@@ -436,8 +297,9 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
        bool in_quote = false;
        bool is_oid = false;
        bool escape = false;
-       unsigned x;
-       int l, ret;
+       unsigned int x;
+       size_t l;
+       int ret;
        char *parse_dn;
        bool is_index;
 
@@ -459,13 +321,6 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
 
        is_index = (strncmp(parse_dn, "DN=@INDEX:", 10) == 0);
 
-       if (strncmp(parse_dn, "B:", 2) == 0) {
-               parse_dn = strchr(parse_dn, ':');
-               parse_dn = strchr(parse_dn+1, ':');
-               parse_dn = strchr(parse_dn+1, ':');
-               parse_dn++;
-       }
-
        /* Empty DNs */
        if (parse_dn[0] == '\0') {
                return true;
@@ -476,11 +331,12 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
                return true;
        }
 
-       /* make sure we free this if alloced previously before replacing */
-       talloc_free(dn->components);
+       /* make sure we free this if allocated previously before replacing */
+       LDB_FREE(dn->components);
+       dn->comp_num = 0;
 
-       talloc_free(dn->ext_components);
-       dn->ext_components = NULL;
+       LDB_FREE(dn->ext_components);
+       dn->ext_comp_num = 0;
 
        /* in the common case we have 3 or more components */
        /* make sure all components are zeroed, other functions depend on it */
@@ -488,7 +344,6 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
        if ( ! dn->components) {
                return false;
        }
-       dn->comp_num = 0;
 
        /* Components data space is allocated here once */
        data = talloc_array(dn->components, char, strlen(parse_dn) + 1);
@@ -766,11 +621,13 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
 
                                continue;
 
+                       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++;
@@ -778,13 +635,10 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
                                        break;
                                }
                                /* fall through */
-                       case '\n':
-                       case '+':
+                       case '\"':
                        case '<':
                        case '>':
-                       case '#':
                        case ';':
-                       case '\"':
                                /* a string with not escaped specials is invalid (tested) */
                                if ( ! escape) {
                                        ldb_dn_mark_invalid(dn);
@@ -814,17 +668,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;
                                }
@@ -882,29 +739,11 @@ bool ldb_dn_validate(struct ldb_dn *dn)
        return ldb_dn_explode(dn);
 }
 
-static char *data_blob_hex_string_upper(TALLOC_CTX *mem_ctx, const struct ldb_val *blob)
-{
-       int i;
-       char *hex_string;
-
-       hex_string = talloc_array(mem_ctx, char, (blob->length*2)+1);
-       if (!hex_string) {
-               return NULL;
-       }
-
-       for (i = 0; i < blob->length; i++)
-               slprintf(&hex_string[i*2], 3, "%02X", blob->data[i]);
-
-       hex_string[(blob->length*2)] = '\0';
-       return hex_string;
-}
-
-
 const char *ldb_dn_get_linearized(struct ldb_dn *dn)
 {
-       int i, len;
+       unsigned int i;
+       size_t len;
        char *d, *n;
-       char *extra_prefix = NULL;
 
        if ( ! dn || ( dn->invalid)) return NULL;
 
@@ -915,12 +754,6 @@ const char *ldb_dn_get_linearized(struct ldb_dn *dn)
                return NULL;
        }
 
-       if (dn->extra_type == 'B') {
-               char *hexstr = data_blob_hex_string_upper(dn, &dn->extra_val);
-               extra_prefix = talloc_asprintf(dn, "B:%u:%s:", (unsigned)(dn->extra_val.length*2), hexstr);
-               talloc_free(hexstr);
-       }
-
        if (dn->comp_num == 0) {
                dn->linearized = talloc_strdup(dn, "");
                if ( ! dn->linearized) return NULL;
@@ -961,21 +794,21 @@ const char *ldb_dn_get_linearized(struct ldb_dn *dn)
        dn->linearized = talloc_realloc(dn, dn->linearized,
                                        char, (d - dn->linearized + 1));
 
-       if (extra_prefix) {
-               char *old = dn->linearized;
-               dn->linearized = talloc_asprintf(dn, "%s%s", extra_prefix, old);
-               talloc_free(old);
-               talloc_free(extra_prefix);
-       }
-
        return dn->linearized;
 }
 
+static int ldb_dn_extended_component_compare(const void *p1, const void *p2)
+{
+       const struct ldb_dn_ext_component *ec1 = (const struct ldb_dn_ext_component *)p1;
+       const struct ldb_dn_ext_component *ec2 = (const struct ldb_dn_ext_component *)p2;
+       return strcmp(ec1->name, ec2->name);
+}
+
 char *ldb_dn_get_extended_linearized(void *mem_ctx, struct ldb_dn *dn, int mode)
 {
        const char *linearized = ldb_dn_get_linearized(dn);
        char *p = NULL;
-       int i;
+       unsigned int i;
 
        if (!linearized) {
                return NULL;
@@ -989,13 +822,12 @@ char *ldb_dn_get_extended_linearized(void *mem_ctx, struct ldb_dn *dn, int mode)
                return NULL;
        }
 
-       if (dn->extra_type == 'B') {
-               char *hexstr = data_blob_hex_string_upper(mem_ctx, &dn->extra_val);
-               p = talloc_asprintf(mem_ctx, "B:%u:%s:", (unsigned)(dn->extra_val.length*2), hexstr);
-               talloc_free(hexstr);
-       } else {
-               p = talloc_strdup(mem_ctx, "");
-       }
+       /* sort the extended components by name. The idea is to make
+        * the resulting DNs consistent, plus to ensure that we put
+        * 'DELETED' first, so it can be very quickly recognised
+        */
+       TYPESAFE_QSORT(dn->ext_components, dn->ext_comp_num,
+                      ldb_dn_extended_component_compare);
 
        for (i = 0; i < dn->ext_comp_num; i++) {
                const struct ldb_dn_extended_syntax *ext_syntax;
@@ -1005,6 +837,9 @@ char *ldb_dn_get_extended_linearized(void *mem_ctx, struct ldb_dn *dn, int mode)
                int ret;
 
                ext_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, name);
+               if (!ext_syntax) {
+                       return NULL;
+               }
 
                if (mode == 1) {
                        ret = ext_syntax->write_clear_fn(dn->ldb, mem_ctx,
@@ -1021,9 +856,11 @@ char *ldb_dn_get_extended_linearized(void *mem_ctx, struct ldb_dn *dn, int mode)
                }
 
                if (i == 0) {
-                       p = talloc_asprintf_append_buffer(p, "<%s=%s>", name, val.data);
+                       p = talloc_asprintf(mem_ctx, "<%s=%s>", 
+                                           name, val.data);
                } else {
-                       p = talloc_asprintf_append_buffer(p, ";<%s=%s>",name, val.data);
+                       p = talloc_asprintf_append_buffer(p, ";<%s=%s>",
+                                                         name, val.data);
                }
 
                talloc_free(val.data);
@@ -1034,12 +871,6 @@ char *ldb_dn_get_extended_linearized(void *mem_ctx, struct ldb_dn *dn, int mode)
        }
 
        if (dn->ext_comp_num && *linearized) {
-               if (strncmp(linearized, "B:", 2) == 0) {
-                       linearized = strchr(linearized, ':');
-                       linearized = strchr(linearized+1, ':');
-                       linearized = strchr(linearized+1, ':');
-                       linearized++;
-               }
                p = talloc_asprintf_append_buffer(p, ";%s", linearized);
        }
 
@@ -1050,6 +881,23 @@ char *ldb_dn_get_extended_linearized(void *mem_ctx, struct ldb_dn *dn, int mode)
        return p;
 }
 
+/*
+  filter out all but an acceptable list of extended DN components
+ */
+void ldb_dn_extended_filter(struct ldb_dn *dn, const char * const *accept)
+{
+       unsigned int i;
+       for (i=0; i<dn->ext_comp_num; i++) {
+               if (!ldb_attr_in_list(accept, dn->ext_components[i].name)) {
+                       memmove(&dn->ext_components[i],
+                               &dn->ext_components[i+1],
+                               (dn->ext_comp_num-(i+1))*sizeof(dn->ext_components[0]));
+                       dn->ext_comp_num--;
+                       i--;
+               }
+       }
+       LDB_FREE(dn->ext_linearized);
+}
 
 
 char *ldb_dn_alloc_linearized(void *mem_ctx, struct ldb_dn *dn)
@@ -1064,7 +912,8 @@ char *ldb_dn_alloc_linearized(void *mem_ctx, struct ldb_dn *dn)
 
 static bool ldb_dn_casefold_internal(struct ldb_dn *dn)
 {
-       int i, ret;
+       unsigned int i;
+       int ret;
 
        if ( ! dn || dn->invalid) return false;
 
@@ -1109,7 +958,8 @@ failed:
 
 const char *ldb_dn_get_casefold(struct ldb_dn *dn)
 {
-       int i, len;
+       unsigned int i;
+       size_t len;
        char *d, *n;
 
        if (dn->casefold) return dn->casefold;
@@ -1179,7 +1029,7 @@ char *ldb_dn_alloc_casefold(void *mem_ctx, struct ldb_dn *dn)
 int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
 {
        int ret;
-       int n_base, n_dn;
+       long long int n_base, n_dn;
 
        if ( ! base || base->invalid) return 1;
        if ( ! dn || dn->invalid) return -1;
@@ -1215,7 +1065,7 @@ int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
                return (dn->comp_num - base->comp_num);
        }
 
-       if (dn->comp_num == 0) {
+       if ((dn->comp_num == 0) || (base->comp_num == 0)) {
                if (dn->special && base->special) {
                        return strcmp(base->linearized, dn->linearized);
                } else if (dn->special) {
@@ -1265,7 +1115,8 @@ int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
 
 int ldb_dn_compare(struct ldb_dn *dn0, struct ldb_dn *dn1)
 {
-       int i, ret;
+       unsigned int i;
+       int ret;
 
        if (( ! dn0) || dn0->invalid || ! dn1 || dn1->invalid) {
                return -1;
@@ -1423,7 +1274,7 @@ struct ldb_dn *ldb_dn_copy(void *mem_ctx, struct ldb_dn *dn)
        *new_dn = *dn;
 
        if (dn->components) {
-               int i;
+               unsigned int i;
 
                new_dn->components =
                        talloc_zero_array(new_dn,
@@ -1446,7 +1297,7 @@ struct ldb_dn *ldb_dn_copy(void *mem_ctx, struct ldb_dn *dn)
        }
 
        if (dn->ext_components) {
-               int i;
+               unsigned int i;
 
                new_dn->ext_components =
                        talloc_zero_array(new_dn,
@@ -1512,7 +1363,7 @@ bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base)
        }
 
        if (dn->components) {
-               int i;
+               unsigned int i;
 
                if ( ! ldb_dn_validate(base)) {
                        return false;
@@ -1579,12 +1430,10 @@ bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base)
 
        /* Wipe the ext_linearized DN,
         * the GUID and SID are almost certainly no longer valid */
-       if (dn->ext_linearized) {
-               LDB_FREE(dn->ext_linearized);
-       }
-
+       LDB_FREE(dn->ext_linearized);
        LDB_FREE(dn->ext_components);
        dn->ext_comp_num = 0;
+
        return true;
 }
 
@@ -1636,7 +1485,12 @@ bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child)
        }
 
        if (dn->components) {
-               int n, i, j;
+               unsigned int n;
+               long long int i, j;
+
+               if (dn->comp_num == 0) {
+                       return false;
+               }
 
                if ( ! ldb_dn_validate(child)) {
                        return false;
@@ -1684,6 +1538,9 @@ bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child)
        }
 
        if (dn->linearized) {
+               if (dn->linearized[0] == '\0') {
+                       return false;
+               }
 
                s = ldb_dn_get_linearized(child);
                if ( ! s) {
@@ -1702,7 +1559,6 @@ bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child)
        /* Wipe the ext_linearized DN,
         * the GUID and SID are almost certainly no longer valid */
        LDB_FREE(dn->ext_linearized);
-
        LDB_FREE(dn->ext_components);
        dn->ext_comp_num = 0;
 
@@ -1744,7 +1600,7 @@ bool ldb_dn_add_child_fmt(struct ldb_dn *dn, const char *child_fmt, ...)
 
 bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num)
 {
-       int i;
+       long long int i;
 
        if ( ! ldb_dn_validate(dn)) {
                return false;
@@ -1778,7 +1634,6 @@ bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num)
        /* Wipe the ext_linearized DN,
         * the GUID and SID are almost certainly no longer valid */
        LDB_FREE(dn->ext_linearized);
-
        LDB_FREE(dn->ext_components);
        dn->ext_comp_num = 0;
 
@@ -1787,7 +1642,7 @@ bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num)
 
 bool ldb_dn_remove_child_components(struct ldb_dn *dn, unsigned int num)
 {
-       int i, j;
+       unsigned int i, j;
 
        if ( ! ldb_dn_validate(dn)) {
                return false;
@@ -1823,9 +1678,9 @@ bool ldb_dn_remove_child_components(struct ldb_dn *dn, unsigned int num)
        /* Wipe the ext_linearized DN,
         * the GUID and SID are almost certainly no longer valid */
        LDB_FREE(dn->ext_linearized);
-
        LDB_FREE(dn->ext_components);
        dn->ext_comp_num = 0;
+
        return true;
 }
 
@@ -1846,9 +1701,9 @@ struct ldb_dn *ldb_dn_get_parent(void *mem_ctx, struct ldb_dn *dn)
        /* Wipe the ext_linearized DN,
         * the GUID and SID are almost certainly no longer valid */
        LDB_FREE(dn->ext_linearized);
-
        LDB_FREE(dn->ext_components);
        dn->ext_comp_num = 0;
+
        return new_dn;
 }
 
@@ -1862,7 +1717,7 @@ struct ldb_dn *ldb_dn_get_parent(void *mem_ctx, struct ldb_dn *dn)
 
 */
 static char *ldb_dn_canonical(void *mem_ctx, struct ldb_dn *dn, int ex_format) {
-       int i;
+       long long int i;
        TALLOC_CTX *tmpctx;
        char *cracked = NULL;
        const char *format = (ex_format ? "\n" : "/" );
@@ -1874,7 +1729,7 @@ static char *ldb_dn_canonical(void *mem_ctx, struct ldb_dn *dn, int ex_format) {
        tmpctx = talloc_new(mem_ctx);
 
        /* Walk backwards down the DN, grabbing 'dc' components at first */
-       for (i = dn->comp_num - 1 ; i >= 0; i--) {
+       for (i = dn->comp_num - 1; i >= 0; i--) {
                if (ldb_attr_cmp(dn->components[i].name, "dc") != 0) {
                        break;
                }
@@ -2007,7 +1862,7 @@ int ldb_dn_set_component(struct ldb_dn *dn, int num,
        dn->components[num].value = v;
 
        if (dn->valid_case) {
-               int i;
+               unsigned int i;
                for (i = 0; i < dn->comp_num; i++) {
                        LDB_FREE(dn->components[i].cf_name);
                        LDB_FREE(dn->components[i].cf_value.data);
@@ -2020,16 +1875,16 @@ int ldb_dn_set_component(struct ldb_dn *dn, int num,
        /* Wipe the ext_linearized DN,
         * the GUID and SID are almost certainly no longer valid */
        LDB_FREE(dn->ext_linearized);
-
-       dn->ext_comp_num = 0;
        LDB_FREE(dn->ext_components);
+       dn->ext_comp_num = 0;
+
        return LDB_SUCCESS;
 }
 
 const struct ldb_val *ldb_dn_get_extended_component(struct ldb_dn *dn,
                                                    const char *name)
 {
-       int i;
+       unsigned int i;
        if ( ! ldb_dn_validate(dn)) {
                return NULL;
        }
@@ -2045,12 +1900,18 @@ int ldb_dn_set_extended_component(struct ldb_dn *dn,
                                  const char *name, const struct ldb_val *val)
 {
        struct ldb_dn_ext_component *p;
-       int i;
+       unsigned int i;
+       struct ldb_val v2;
 
        if ( ! ldb_dn_validate(dn)) {
                return LDB_ERR_OTHER;
        }
 
+       if (!ldb_dn_extended_syntax_by_name(dn->ldb, name)) {
+               /* We don't know how to handle this type of thing */
+               return LDB_ERR_INVALID_DN_SYNTAX;
+       }
+
        for (i=0; i < dn->ext_comp_num; i++) {
                if (ldb_attr_cmp(dn->ext_components[i].name, name) == 0) {
                        if (val) {
@@ -2064,7 +1925,6 @@ int ldb_dn_set_extended_component(struct ldb_dn *dn,
                                        ldb_dn_mark_invalid(dn);
                                        return LDB_ERR_OPERATIONS_ERROR;
                                }
-
                        } else {
                                if (i != (dn->ext_comp_num - 1)) {
                                        memmove(&dn->ext_components[i],
@@ -2082,11 +1942,20 @@ int ldb_dn_set_extended_component(struct ldb_dn *dn,
                                        ldb_dn_mark_invalid(dn);
                                        return LDB_ERR_OPERATIONS_ERROR;
                                }
-                               return LDB_SUCCESS;
                        }
+                       LDB_FREE(dn->ext_linearized);
+
+                       return LDB_SUCCESS;
                }
        }
 
+       if (val == NULL) {
+               /* removing a value that doesn't exist is not an error */
+               return LDB_SUCCESS;
+       }
+
+       v2 = *val;
+
        p = dn->ext_components
                = talloc_realloc(dn,
                                 dn->ext_components,
@@ -2097,7 +1966,7 @@ int ldb_dn_set_extended_component(struct ldb_dn *dn,
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       p[dn->ext_comp_num].value = ldb_val_dup(dn->ext_components, val);
+       p[dn->ext_comp_num].value = ldb_val_dup(dn->ext_components, &v2);
        p[dn->ext_comp_num].name = talloc_strdup(p, name);
 
        if (!dn->ext_components[i].name || !dn->ext_components[i].value.data) {
@@ -2107,13 +1976,16 @@ int ldb_dn_set_extended_component(struct ldb_dn *dn,
        dn->ext_components = p;
        dn->ext_comp_num++;
 
+       LDB_FREE(dn->ext_linearized);
+
        return LDB_SUCCESS;
 }
 
 void ldb_dn_remove_extended_components(struct ldb_dn *dn)
 {
-       dn->ext_comp_num = 0;
+       LDB_FREE(dn->ext_linearized);
        LDB_FREE(dn->ext_components);
+       dn->ext_comp_num = 0;
 }
 
 bool ldb_dn_is_valid(struct ldb_dn *dn)
@@ -2131,7 +2003,7 @@ bool ldb_dn_is_special(struct ldb_dn *dn)
 bool ldb_dn_has_extended(struct ldb_dn *dn)
 {
        if ( ! dn || dn->invalid) return false;
-       if (dn->ext_linearized && strchr(dn->ext_linearized,'<')) return true;
+       if (dn->ext_linearized && (dn->ext_linearized[0] == '<')) return true;
        return dn->ext_comp_num != 0;
 }
 
@@ -2149,25 +2021,26 @@ bool ldb_dn_is_null(struct ldb_dn *dn)
        return false;
 }
 
-int ldb_dn_get_binary(struct ldb_dn *dn, struct ldb_val *val)
+/*
+  this updates dn->components, taking the components from ref_dn.
+  This is used by code that wants to update the DN path of a DN
+  while not impacting on the extended DN components
+ */
+int ldb_dn_update_components(struct ldb_dn *dn, const struct ldb_dn *ref_dn)
 {
-       ZERO_STRUCTP(val);
-       if (dn->extra_type != 'B') {
-               return LDB_SUCCESS;
+       dn->components = talloc_realloc(dn, dn->components,
+                                       struct ldb_dn_component, ref_dn->comp_num);
+       if (!dn->components) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
-       *val = dn->extra_val;
-       return LDB_SUCCESS;
-}
-
-int ldb_dn_set_binary(struct ldb_dn *dn, struct ldb_val *val)
-{
-       dn->extra_type = 'B';
-       dn->extra_val.data = talloc_memdup(dn, val->data, val->length);
-       dn->extra_val.length = val->length;
+       memcpy(dn->components, ref_dn->components,
+              sizeof(struct ldb_dn_component)*ref_dn->comp_num);
+       dn->comp_num = ref_dn->comp_num;
 
        talloc_free(dn->linearized);
        talloc_free(dn->ext_linearized);
-       dn->linearized = NULL;
        dn->ext_linearized = NULL;
+       dn->linearized = NULL;
+
        return LDB_SUCCESS;
 }