Fix the mess with ldb includes.
[metze/samba/wip.git] / source4 / lib / ldb / common / ldb_dn.c
index 0a10ed6f2e5fc81bf7bab1eff7fd344ad26999a6..402d6295015d141de84211692b1131f7a0a6ff70 100644 (file)
@@ -10,7 +10,7 @@
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
-   version 2 of the License, or (at your option) any later version.
+   version 3 of the License, or (at your option) any later version.
 
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -18,8 +18,7 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with this library; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
 
 /*
@@ -34,9 +33,8 @@
  *  Author: Simo Sorce
  */
 
-#include "includes.h"
+#include "ldb_private.h"
 #include <ctype.h>
-#include "ldb/include/includes.h"
 
 #define LDB_DN_NULL_FAILED(x) if (!(x)) goto failed
 
@@ -54,6 +52,12 @@ struct ldb_dn_component {
        struct ldb_val cf_value;
 };
 
+struct ldb_dn_extended_component {
+
+       char *name;
+       struct ldb_val value;
+};
+
 struct ldb_dn {
 
        struct ldb_context *ldb;
@@ -62,47 +66,62 @@ struct ldb_dn {
        bool special;
        bool invalid;
 
-       bool valid_lin;
-       bool valid_comp;
        bool valid_case;
 
        char *linearized;
+       char *extended_linearized;
        char *casefold;
 
        unsigned int comp_num;
        struct ldb_dn_component *components;
 
+       unsigned int extended_comp_num;
+       struct ldb_dn_extended_component *extended_components;
 };
 
 /* strdn may be NULL */
-struct ldb_dn *ldb_dn_new(void *mem_ctx, struct ldb_context *ldb, const char *strdn)
+struct ldb_dn *ldb_dn_from_ldb_val(void *mem_ctx, struct ldb_context *ldb, const struct ldb_val *strdn)
 {
        struct ldb_dn *dn;
 
-       if ( (! mem_ctx) || (! ldb)) return NULL;
+       if (! ldb) return NULL;
 
        dn = talloc_zero(mem_ctx, struct ldb_dn);
        LDB_DN_NULL_FAILED(dn);
 
        dn->ldb = ldb;
 
-       if (strdn) {
-               if (strdn[0] == '@') {
-                       dn->special = true;
-               }
-               if (strncasecmp(strdn, "<GUID=", 6) == 0) {
-                       /* this is special DN returned when the
-                        * exploded_dn control is used */
+       if (strdn->data && strdn->length) {
+               if (strdn->data[0] == '@') {
                        dn->special = true;
-                       /* FIXME: add a GUID string to ldb_dn structure */
+               } 
+               dn->extended_linearized = talloc_strndup(dn, (const char *)strdn->data, strdn->length);
+               LDB_DN_NULL_FAILED(dn->extended_linearized);
+       
+               if (strdn->data[0] == '<') {
+                       const char *p_save, *p = dn->extended_linearized;
+                       do {
+                               p_save = p;
+                               p = strstr(p, ">;");
+                               if (p) {
+                                       p = p + 2;
+                               }
+                       } while (p);
+                       
+                       if (p_save == dn->extended_linearized) {
+                               dn->linearized = talloc_strdup(dn, "");
+                       } else {
+                               dn->linearized = talloc_strdup(dn, p_save);
+                       }
+                       LDB_DN_NULL_FAILED(dn->linearized);
+               } else {
+                       dn->linearized = dn->extended_linearized;
+                       dn->extended_linearized = NULL;
                }
-               dn->linearized = talloc_strdup(dn, strdn);
        } else {
                dn->linearized = talloc_strdup(dn, "");
+               LDB_DN_NULL_FAILED(dn->linearized);
        }
-       LDB_DN_NULL_FAILED(dn->linearized);
-
-       dn->valid_lin = true;
 
        return dn;
 
@@ -111,41 +130,32 @@ failed:
        return NULL;
 }
 
+/* strdn may be NULL */
+struct ldb_dn *ldb_dn_new(void *mem_ctx, struct ldb_context *ldb, const char *strdn)
+{
+       struct ldb_val blob;
+       blob.data = strdn;
+       blob.length = strdn ? strlen(strdn) : 0;
+       return ldb_dn_from_ldb_val(mem_ctx, ldb, &blob);
+}
+
 struct ldb_dn *ldb_dn_new_fmt(void *mem_ctx, struct ldb_context *ldb, const char *new_fmt, ...)
 {
-       struct ldb_dn *dn;
        char *strdn;
        va_list ap;
 
        if ( (! mem_ctx) || (! ldb)) return NULL;
 
-       dn = talloc_zero(mem_ctx, struct ldb_dn);
-       LDB_DN_NULL_FAILED(dn);
-
-       dn->ldb = ldb;
-
        va_start(ap, new_fmt);
-       strdn = talloc_vasprintf(dn, new_fmt, ap);
+       strdn = talloc_vasprintf(mem_ctx, new_fmt, ap);
        va_end(ap);
-       LDB_DN_NULL_FAILED(strdn);
 
-       if (strdn[0] == '@') {
-               dn->special = true;
-       }
-       if (strncasecmp(strdn, "<GUID=", 6) == 0) {
-               /* this is special DN returned when the
-                * exploded_dn control is used */
-               dn->special = true;
-               /* FIXME: add a GUID string to ldb_dn structure */
+       if (strdn) {
+               struct ldb_dn *dn = ldb_dn_new(mem_ctx, ldb, strdn);
+               talloc_free(strdn);
+               return dn;
        }
-       dn->linearized = strdn;
-
-       dn->valid_lin = true;
-
-       return dn;
-
-failed:
-       talloc_free(dn);
+       
        return NULL;
 }
 
@@ -203,6 +213,8 @@ char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value)
 
        ldb_dn_escape_internal(dst, (const char *)value.data, value.length);
 
+       dst = talloc_realloc(mem_ctx, dst, char, strlen(dst) + 1);
+
        return dst;
 }
 
@@ -212,29 +224,38 @@ char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value)
 */
 static bool ldb_dn_explode(struct ldb_dn *dn)
 {
-       char *p, *data, *d, *dt, *t;
+       char *p, *ex_name, *ex_value, *data, *d, *dt, *t;
        bool trim = false;
+       bool in_extended = false;
+       bool in_ex_name = false;
+       bool in_ex_value = false;
        bool in_attr = false;
        bool in_value = false;
        bool in_quote = false;
        bool is_oid = false;
        bool escape = false;
        unsigned x;
-       int l;
+       int l, ret;
+       char *parse_dn;
 
        if ( ! dn || dn->invalid) return false;
 
-       if (dn->valid_comp) {
+       if (dn->components) {
                return true;
        }
 
-       if ( ! dn->valid_lin) {
+       if (dn->extended_linearized) {
+               parse_dn = dn->extended_linearized;
+       } else {
+               parse_dn = dn->linearized;
+       }
+
+       if ( ! parse_dn ) {
                return false;
        }
 
        /* Empty DNs */
-       if (dn->linearized[0] == '\0') {
-               dn->valid_comp = true;
+       if (parse_dn[0] == '\0') {
                return true;
        }
 
@@ -243,6 +264,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);
+
+       talloc_free(dn->extended_components);
+       dn->extended_components = NULL;
+
        /* in the common case we have 3 or more components */
        /* make sure all components are zeroed, other functions depend on this */
        dn->components = talloc_zero_array(dn, struct ldb_dn_component, 3);
@@ -252,16 +279,109 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
        dn->comp_num = 0;
 
        /* Components data space is allocated here once */
-       data = talloc_array(dn->components, char, strlen(dn->linearized) + 1);
+       data = talloc_array(dn->components, char, strlen(parse_dn) + 1);
+       if (!data) {
+               return false;
+       }
 
-       p = dn->linearized;
-       in_attr = true;
+       p = parse_dn;
+       in_extended = true;
+       in_ex_name = false;
+       in_ex_value = false;
        trim = true;
        t = NULL;
        d = dt = data;
 
        while (*p) {
+               if (in_extended) {
+
+                       if (!in_ex_name && !in_ex_value) {
+
+                               if (p[0] == '<') {
+                                       p++;
+                                       ex_name = d;
+                                       in_ex_name = true;
+                                       continue;
+                               } else if (p[0] == '\0') {
+                                       p++;
+                                       continue;
+                               } else {
+                                       in_extended = false;
+                                       in_attr = true;
+                                       dt = d;
+
+                                       continue;
+                               }
+                       }
+                       
+                       if (in_ex_name && *p == '=') {
+                               *d++ = '\0';
+                               p++;
+                               ex_value = d;
+                               in_ex_name = false;
+                               in_ex_value = true;
+                               continue;
+                       }
+
+                       if (in_ex_value && *p == '>') {
+                               const struct ldb_dn_extended_syntax *extended_syntax;
+                               struct ldb_val ex_val = {
+                                       .data = ex_value,
+                                       .length = d - ex_value
+                               };
+                                       
+                               *d++ = '\0';
+                               p++;
+                               in_ex_value = false;
+
+                               /* Process name and ex_value */
+
+                               dn->extended_components = talloc_realloc(dn,
+                                                                        dn->extended_components,
+                                                                        struct ldb_dn_extended_component,
+                                                                        dn->extended_comp_num + 1);
+                               if ( ! dn->extended_components) {
+                                       /* ouch ! */
+                                       goto failed;
+                               }
+
+                               extended_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, ex_name);
+                               if (!extended_syntax) {
+                                       /* We don't know about this type of extended DN */
+                                       goto failed;
+                               }
+
+                               dn->extended_components[dn->extended_comp_num].name = talloc_strdup(dn->extended_components, ex_name);
+                               if (!dn->extended_components[dn->extended_comp_num].name) {
+                                       /* ouch */
+                                       goto failed;
+                               }
+                               ret = extended_syntax->read_fn(dn->ldb, dn->extended_components,
+                                                              &ex_val, &dn->extended_components[dn->extended_comp_num].value);
+                               if (ret != LDB_SUCCESS) {
+                                       dn->invalid = true;
+                                       goto failed;
+                               }
 
+                               dn->extended_comp_num++;
+
+                               if (*p == '\0') {
+                                       /* We have reached the end (extended component only)! */
+                                       talloc_free(data);
+                                       return true;
+
+                               } else if (*p == ';') {
+                                       p++;
+                                       continue;
+                               } else {
+                                       dn->invalid = true;
+                                       goto failed;
+                               }
+                       }
+
+                       *d++ = *p++;
+                       continue;
+               }
                if (in_attr) {
                        if (trim) {
                                if (*p == ' ') {
@@ -272,6 +392,12 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
                                /* first char */
                                trim = false;
 
+                               if (!isascii(*p)) {
+                                       /* attr names must be ascii only */
+                                       dn->invalid = true;
+                                       goto failed;
+                               }
+
                                if (isdigit(*p)) {
                                        is_oid = true;
                                } else
@@ -281,6 +407,7 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
                                        goto failed;
                                }
                                
+                               /* Copy this character across from parse_dn, now we have trimmed out spaces */
                                *d++ = *p++;
                                continue;
                        }
@@ -305,14 +432,26 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
                                trim = true;
                                l = 0;
 
+                               /* Terminate this string in d (which is a copy of parse_dn with spaces trimmed) */
                                *d++ = '\0';
-                               dn->components[dn->comp_num].name = dt;
+                               dn->components[dn->comp_num].name = talloc_strdup(dn->components, dt);
+                               if ( ! dn->components[dn->comp_num].name) {
+                                       /* ouch */
+                                       goto failed;
+                               }
+
                                dt = d;
 
                                p++;
                                continue;
                        }
 
+                       if (!isascii(*p)) {
+                               /* attr names must be ascii only */
+                               dn->invalid = true;
+                               goto failed;
+                       }
+
                        if (is_oid && ( ! (isdigit(*p) || (*p == '.')))) {
                                /* not a digit nor a dot, invalid attribute oid */
                                dn->invalid = true;
@@ -385,8 +524,13 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
 
                                p++;
                                *d++ = '\0';
-                               dn->components[dn->comp_num].value.data = (uint8_t *)dt;
+                               dn->components[dn->comp_num].value.data = (uint8_t *)talloc_strdup(dn->components, dt);
                                dn->components[dn->comp_num].value.length = l;
+                               if ( ! dn->components[dn->comp_num].value.data) {
+                                       /* ouch ! */
+                                       goto failed;
+                               }
+
                                dt = d;
 
                                dn->comp_num++;
@@ -486,15 +630,21 @@ static bool ldb_dn_explode(struct ldb_dn *dn)
        }
 
        *d++ = '\0';
-       dn->components[dn->comp_num].value.data = (uint8_t *)dt;
+       dn->components[dn->comp_num].value.data = (uint8_t *)talloc_strdup(dn->components, dt);
        dn->components[dn->comp_num].value.length = l;
 
+       if ( ! dn->components[dn->comp_num].value.data) {
+               /* ouch */
+               goto failed;
+       }
+
        dn->comp_num++;
 
-       dn->valid_comp = true;
+       talloc_free(data);
        return true;
 
 failed:
+       dn->comp_num = 0;
        talloc_free(dn->components);
        return false;
 }
@@ -511,9 +661,9 @@ const char *ldb_dn_get_linearized(struct ldb_dn *dn)
 
        if ( ! dn || ( dn->invalid)) return NULL;
 
-       if (dn->valid_lin) return dn->linearized;
+       if (dn->linearized) return dn->linearized;
 
-       if ( ! dn->valid_comp) {
+       if ( ! dn->components) {
                dn->invalid = true;
                return NULL;
        }
@@ -521,7 +671,6 @@ const char *ldb_dn_get_linearized(struct ldb_dn *dn)
        if (dn->comp_num == 0) {
                dn->linearized = talloc_strdup(dn, "");
                if ( ! dn->linearized) return NULL;
-               dn->valid_lin = true;
                return dn->linearized;
        }
 
@@ -553,15 +702,81 @@ const char *ldb_dn_get_linearized(struct ldb_dn *dn)
 
        *(--d) = '\0';
 
-       dn->valid_lin = true;
-
        /* don't waste more memory than necessary */
        dn->linearized = talloc_realloc(dn, dn->linearized, char, (d - dn->linearized + 1));
 
        return dn->linearized;
 }
 
-char *ldb_dn_linearize(void *mem_ctx, struct ldb_dn *dn)
+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;
+       int i;
+
+       if (!linearized) {
+               return NULL;
+       }
+
+       if (!ldb_dn_has_extended(dn)) {
+               return talloc_strdup(mem_ctx, linearized);
+       }
+       
+       if (!ldb_dn_validate(dn)) {
+               return NULL;
+       }
+
+       for (i=0; i < dn->extended_comp_num; i++) {
+               struct ldb_val val;
+               int ret;
+               const struct ldb_dn_extended_syntax *extended_syntax;
+               const char *name = dn->extended_components[i].name;
+               
+               extended_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, name);
+
+               if (mode == 1) {
+                       ret = extended_syntax->write_clear_fn(dn->ldb, mem_ctx,
+                                                             &dn->extended_components[i].value,
+                                                             &val);
+               } else if (mode == 0) {
+                       ret = extended_syntax->write_hex_fn(dn->ldb, mem_ctx,
+                                                             &dn->extended_components[i].value,
+                                                             &val);
+               } else {
+                       ret = -1;
+               }
+
+               if (ret != LDB_SUCCESS) {
+                       return NULL;
+               }
+
+               if (i == 0) {
+                       p = talloc_asprintf(mem_ctx, "<%s=%s>", dn->extended_components[i].name, val.data);
+               } else {
+                       p = talloc_asprintf_append(p, ";<%s=%s>",  dn->extended_components[i].name, val.data);
+               }
+
+               talloc_free(val.data);
+
+               if (!p) {
+                       return NULL;
+               }
+       }
+
+       if (dn->extended_comp_num && *linearized) {
+               p = talloc_asprintf_append(p, ";%s", linearized);
+       }
+
+       if (!p) {
+               return NULL;
+       }
+
+       return p;
+}
+
+
+
+char *ldb_dn_alloc_linearized(void *mem_ctx, struct ldb_dn *dn)
 {
        return talloc_strdup(mem_ctx, ldb_dn_get_linearized(dn));
 }
@@ -579,30 +794,37 @@ static bool ldb_dn_casefold_internal(struct ldb_dn *dn)
 
        if (dn->valid_case) return true;
 
-       if (( ! dn->valid_comp) && ( ! ldb_dn_explode(dn))) {
+       if (( ! dn->components) && ( ! ldb_dn_explode(dn))) {
                return false;
        }
 
        for (i = 0; i < dn->comp_num; i++) {
-               struct ldb_dn_component dc;
-               const struct ldb_attrib_handler *h;
+               const struct ldb_schema_attribute *a;
 
-               memset(&dc, 0, sizeof(dc));
                dn->components[i].cf_name = ldb_attr_casefold(dn->components, dn->components[i].name);
                if (!dn->components[i].cf_name) {
-                       return false;
+                       goto failed;
                }
 
-               h = ldb_attrib_handler(dn->ldb, dn->components[i].cf_name);
-               ret = h->canonicalise_fn(dn->ldb, dn->components,
-                                        &(dn->components[i].value),
-                                        &(dn->components[i].cf_value));
+               a = ldb_schema_attribute_by_name(dn->ldb, dn->components[i].cf_name);
+               ret = a->syntax->canonicalise_fn(dn->ldb, dn->components,
+                                                &(dn->components[i].value),
+                                                &(dn->components[i].cf_value));
                if (ret != 0) {
-                       return false;
+                       goto failed;
                }
        }
 
+       dn->valid_case = true;
+
        return true;
+
+failed:
+       for (i = 0; i < dn->comp_num; i++) {
+               LDB_FREE(dn->components[i].cf_name);
+               LDB_FREE(dn->components[i].cf_value.data);
+       }
+       return false;
 }
 
 const char *ldb_dn_get_casefold(struct ldb_dn *dn)
@@ -610,26 +832,28 @@ const char *ldb_dn_get_casefold(struct ldb_dn *dn)
        int i, len;
        char *d, *n;
 
+       if (dn->casefold) return dn->casefold;
+
+       if (dn->special) { 
+               dn->casefold = talloc_strdup(dn, dn->linearized);
+               if (!dn->casefold) return NULL;
+               dn->valid_case = true;
+               return dn->casefold;
+       }
+
        if ( ! ldb_dn_casefold_internal(dn)) {
                return NULL;
        }
 
-       if (dn->casefold) return dn->casefold;
-
        if (dn->comp_num == 0) {
-               if (dn->special) {
-                       len = strlen(dn->linearized);
-                       dn->casefold = talloc_array(dn, char, len * 3 + 1);
-                       if ( ! dn->casefold) return NULL;
-                       ldb_dn_escape_internal(dn->casefold, dn->linearized, len);
-                       /* don't waste more memory than necessary */
-                       dn->casefold = talloc_realloc(dn, dn->casefold, char, strlen(dn->casefold) + 1);
-               } else {
+               if (dn->linearized && dn->linearized[0] == '\0') {
+                       /* hmm a NULL dn, should we faild casefolding ? */
                        dn->casefold = talloc_strdup(dn, "");
-                       if ( ! dn->casefold) return NULL;
+                       return dn->casefold;
                }
-               dn->valid_case = true;
-               return dn->casefold;
+               /* A DN must be NULL, special, or have components */
+               dn->invalid = true;
+               return NULL;
        }
 
        /* calculate maximum possible length of DN */
@@ -659,12 +883,13 @@ const char *ldb_dn_get_casefold(struct ldb_dn *dn)
        }
        *(--d) = '\0';
 
-       dn->valid_case = true;
+       /* don't waste more memory than necessary */
+       dn->casefold = talloc_realloc(dn, dn->casefold, char, strlen(dn->casefold) + 1);
 
        return dn->casefold;
 }
 
-char *ldb_dn_casefold(void *mem_ctx, struct ldb_dn *dn)
+char *ldb_dn_alloc_casefold(void *mem_ctx, struct ldb_dn *dn)
 {
        return talloc_strdup(mem_ctx, ldb_dn_get_casefold(dn));
 }
@@ -683,7 +908,7 @@ int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
        if ( ! dn || dn->invalid) return -1;
 
        if (( ! base->valid_case) || ( ! dn->valid_case)) {
-               if (base->valid_lin && dn->valid_lin) {
+               if (base->linearized && dn->linearized) {
                        /* try with a normal compare first, if we are lucky
                         * we will avoid exploding and casfolding */
                        int dif;
@@ -711,6 +936,10 @@ int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
        if (dn->comp_num == 0) {
                if (dn->special && base->special) {
                        return strcmp(base->linearized, dn->linearized);
+               } else if (dn->special) {
+                       return -1;
+               } else if (base->special) {
+                       return 1;
                } else {
                        return 0;
                }
@@ -750,7 +979,7 @@ int ldb_dn_compare(struct ldb_dn *dn0, struct ldb_dn *dn1)
        if (( ! dn0) || dn0->invalid || ! dn1 || dn1->invalid) return -1;
 
        if (( ! dn0->valid_case) || ( ! dn1->valid_case)) {
-               if (dn0->valid_lin && dn1->valid_lin) {
+               if (dn0->linearized && dn1->linearized) {
                        /* try with a normal compare first, if we are lucky
                         * we will avoid exploding and casfolding */
                        if (strcmp(dn0->linearized, dn1->linearized) == 0) return 0;
@@ -773,6 +1002,10 @@ int ldb_dn_compare(struct ldb_dn *dn0, struct ldb_dn *dn1)
        if (dn0->comp_num == 0) {
                if (dn0->special && dn1->special) {
                        return strcmp(dn0->linearized, dn1->linearized);
+               } else if (dn0->special) {
+                       return 1;
+               } else if (dn1->special) {
+                       return -1;
                } else {
                        return 0;
                }
@@ -812,6 +1045,7 @@ static struct ldb_dn_component ldb_dn_copy_component(void *mem_ctx, struct ldb_d
        dst.name = talloc_strdup(mem_ctx, src->name);
        if (dst.name == NULL) {
                LDB_FREE(dst.value.data);
+               return dst;
        }
 
        if (src->cf_value.data) {
@@ -837,6 +1071,30 @@ static struct ldb_dn_component ldb_dn_copy_component(void *mem_ctx, struct ldb_d
        return dst;
 }
 
+static struct ldb_dn_extended_component ldb_dn_extended_copy_component(void *mem_ctx, struct ldb_dn_extended_component *src)
+{
+       struct ldb_dn_extended_component dst;
+
+       memset(&dst, 0, sizeof(dst));
+
+       if (src == NULL) {
+               return dst;
+       }
+
+       dst.value = ldb_val_dup(mem_ctx, &(src->value));
+       if (dst.value.data == NULL) {
+               return dst;
+       }
+
+       dst.name = talloc_strdup(mem_ctx, src->name);
+       if (dst.name == NULL) {
+               LDB_FREE(dst.value.data);
+               return dst;
+       }
+
+       return dst;
+}
+
 struct ldb_dn *ldb_dn_copy(void *mem_ctx, struct ldb_dn *dn)
 {
        struct ldb_dn *new_dn;
@@ -852,7 +1110,7 @@ struct ldb_dn *ldb_dn_copy(void *mem_ctx, struct ldb_dn *dn)
 
        *new_dn = *dn;
 
-       if (dn->valid_comp) {
+       if (dn->components) {
                int i;
 
                new_dn->components = talloc_zero_array(new_dn, struct ldb_dn_component, dn->comp_num);
@@ -868,17 +1126,35 @@ struct ldb_dn *ldb_dn_copy(void *mem_ctx, struct ldb_dn *dn)
                                return NULL;
                        }
                }
+       }
+
+       if (dn->extended_components) {
+               int i;
+
+               new_dn->extended_components = talloc_zero_array(new_dn, struct ldb_dn_extended_component, dn->extended_comp_num);
+               if ( ! new_dn->extended_components) {
+                       talloc_free(new_dn);
+                       return NULL;
+               }
 
-               if (dn->casefold) {
-                       new_dn->casefold = talloc_strdup(new_dn, dn->casefold);
-                       if ( ! new_dn->casefold) {
+               for (i = 0; i < dn->extended_comp_num; i++) {
+                       new_dn->extended_components[i] = ldb_dn_extended_copy_component(new_dn->extended_components, &dn->extended_components[i]);
+                       if ( ! new_dn->extended_components[i].value.data) {
                                talloc_free(new_dn);
                                return NULL;
                        }
                }
        }
 
-       if (dn->valid_lin) {
+       if (dn->casefold) {
+               new_dn->casefold = talloc_strdup(new_dn, dn->casefold);
+               if ( ! new_dn->casefold) {
+                       talloc_free(new_dn);
+                       return NULL;
+               }
+       }
+
+       if (dn->linearized) {
                new_dn->linearized = talloc_strdup(new_dn, dn->linearized);
                if ( ! new_dn->linearized) {
                        talloc_free(new_dn);
@@ -886,6 +1162,14 @@ struct ldb_dn *ldb_dn_copy(void *mem_ctx, struct ldb_dn *dn)
                }
        }
 
+       if (dn->extended_linearized) {
+               new_dn->extended_linearized = talloc_strdup(new_dn, dn->extended_linearized);
+               if ( ! new_dn->extended_linearized) {
+                       talloc_free(new_dn);
+                       return NULL;
+               }
+       }
+
        return new_dn;
 }
 
@@ -903,7 +1187,7 @@ bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base)
                return false;
        }
 
-       if (dn->valid_comp) {
+       if (dn->components) {
                int i;
 
                if ( ! ldb_dn_validate(base)) {
@@ -934,21 +1218,29 @@ bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base)
                        }
                }
 
-               if (s) {
-                       t = talloc_asprintf(dn, "%s,%s", dn->casefold, s);
+               if (dn->casefold && s) {
+                       if (*dn->casefold) {
+                               t = talloc_asprintf(dn, "%s,%s", dn->casefold, s);
+                       } else {
+                               t = talloc_strdup(dn, s);
+                       }
                        LDB_FREE(dn->casefold);
                        dn->casefold = t;
                }
        }
 
-       if (dn->valid_lin) {
+       if (dn->linearized) {
 
                s = ldb_dn_get_linearized(base);
                if ( ! s) {
                        return false;
                }
                
-               t = talloc_asprintf(dn, "%s,%s", dn->linearized, s);
+               if (*dn->linearized) {
+                       t = talloc_asprintf(dn, "%s,%s", dn->linearized, s);
+               } else {
+                       t = talloc_strdup(dn, s);
+               }
                if ( ! t) {
                        dn->invalid = true;
                        return false;
@@ -957,6 +1249,13 @@ bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base)
                dn->linearized = t;
        }
 
+       /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */
+       if (dn->extended_linearized) {
+               LDB_FREE(dn->extended_linearized);
+       }
+
+       LDB_FREE(dn->extended_components);
+       dn->extended_comp_num = 0;
        return true;
 }
 
@@ -1007,7 +1306,7 @@ bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child)
                return false;
        }
 
-       if (dn->valid_comp) {
+       if (dn->components) {
                int n, i, j;
 
                if ( ! ldb_dn_validate(child)) {
@@ -1046,14 +1345,14 @@ bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child)
 
                dn->comp_num = n;
 
-               if (s) {
+               if (dn->casefold && s) {
                        t = talloc_asprintf(dn, "%s,%s", s, dn->casefold);
                        LDB_FREE(dn->casefold);
                        dn->casefold = t;
                }
        }
 
-       if (dn->valid_lin) {
+       if (dn->linearized) {
 
                s = ldb_dn_get_linearized(child);
                if ( ! s) {
@@ -1069,6 +1368,12 @@ bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child)
                dn->linearized = t;
        }
 
+       /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */
+       LDB_FREE(dn->extended_linearized);
+
+       LDB_FREE(dn->extended_components);
+       dn->extended_comp_num = 0;
+
        return true;
 }
 
@@ -1107,6 +1412,8 @@ 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;
+
        if ( ! ldb_dn_validate(dn)) {
                return false;
        }
@@ -1115,15 +1422,33 @@ bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num)
                return false;
        }
 
+       /* free components */
+       for (i = num; i > 0; i--) {
+               LDB_FREE(dn->components[dn->comp_num - i].name);
+               LDB_FREE(dn->components[dn->comp_num - i].value.data);
+               LDB_FREE(dn->components[dn->comp_num - i].cf_name);
+               LDB_FREE(dn->components[dn->comp_num - i].cf_value.data);
+       }
+       
        dn->comp_num -= num;
 
-       dn->valid_case = false;
-
-       if (dn->valid_lin) {    
-               dn->valid_lin = false;
-               LDB_FREE(dn->linearized);
+       if (dn->valid_case) {
+               for (i = 0; i < dn->comp_num; i++) {
+                       LDB_FREE(dn->components[i].cf_name);
+                       LDB_FREE(dn->components[i].cf_value.data);
+               }
+               dn->valid_case = false;
        }
 
+       LDB_FREE(dn->casefold);
+       LDB_FREE(dn->linearized);
+
+       /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */
+       LDB_FREE(dn->extended_linearized);
+
+       LDB_FREE(dn->extended_components);
+       dn->extended_comp_num = 0;
+
        return true;
 }
 
@@ -1140,18 +1465,33 @@ bool ldb_dn_remove_child_components(struct ldb_dn *dn, unsigned int num)
        }
 
        for (i = 0, j = num; j < dn->comp_num; i++, j++) {
+               if (i < num) {
+                       LDB_FREE(dn->components[i].name);
+                       LDB_FREE(dn->components[i].value.data);
+                       LDB_FREE(dn->components[i].cf_name);
+                       LDB_FREE(dn->components[i].cf_value.data);
+               }
                dn->components[i] = dn->components[j];
        }
 
        dn->comp_num -= num;
 
-       dn->valid_case = false;
-
-       if (dn->valid_lin) {    
-               dn->valid_lin = false;
-               LDB_FREE(dn->linearized);
+       if (dn->valid_case) {
+               for (i = 0; i < dn->comp_num; i++) {
+                       LDB_FREE(dn->components[i].cf_name);
+                       LDB_FREE(dn->components[i].cf_value.data);
+               }
+               dn->valid_case = false;
        }
 
+       LDB_FREE(dn->casefold);
+       LDB_FREE(dn->linearized);
+
+       /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */
+       LDB_FREE(dn->extended_linearized);
+
+       LDB_FREE(dn->extended_components);
+       dn->extended_comp_num = 0;
        return true;
 }
 
@@ -1169,6 +1509,11 @@ struct ldb_dn *ldb_dn_get_parent(void *mem_ctx, struct ldb_dn *dn)
                return NULL;
        }
 
+       /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */
+       LDB_FREE(dn->extended_linearized);
+
+       LDB_FREE(dn->extended_components);
+       dn->extended_comp_num = 0;
        return new_dn;
 }
 
@@ -1182,55 +1527,56 @@ 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;
+       TALLOC_CTX *tmpctx;
        char *cracked = NULL;
+       const char *format = (ex_format ? "\n" : "/" );
  
        if ( ! ldb_dn_validate(dn)) {
                return NULL;
        }
+
+       tmpctx = talloc_new(mem_ctx);
+
        /* Walk backwards down the DN, grabbing 'dc' components at first */
        for (i = dn->comp_num - 1 ; i >= 0; i--) {
                if (ldb_attr_cmp(dn->components[i].name, "dc") != 0) {
                        break;
                }
                if (cracked) {
-                       cracked = talloc_asprintf(mem_ctx, "%s.%s",
-                                                 ldb_dn_escape_value(mem_ctx, dn->components[i].value),
+                       cracked = talloc_asprintf(tmpctx, "%s.%s",
+                                                 ldb_dn_escape_value(tmpctx, dn->components[i].value),
                                                  cracked);
                } else {
-                       cracked = ldb_dn_escape_value(mem_ctx, dn->components[i].value);
+                       cracked = ldb_dn_escape_value(tmpctx, dn->components[i].value);
                }
                if (!cracked) {
-                       return NULL;
+                       goto done;
                }
        }
 
        /* Only domain components?  Finish here */
        if (i < 0) {
-               if (ex_format) {
-                       cracked = talloc_asprintf(mem_ctx, "%s\n", cracked);
-               } else {
-                       cracked = talloc_asprintf(mem_ctx, "%s/", cracked);
-               }
-               return cracked;
+               cracked = talloc_strdup_append_buffer(cracked, format);
+               talloc_steal(mem_ctx, cracked);
+               goto done;
        }
 
        /* Now walk backwards appending remaining components */
        for (; i > 0; i--) {
-               cracked = talloc_asprintf(mem_ctx, "%s/%s", cracked
-                                         ldb_dn_escape_value(mem_ctx, dn->components[i].value));
+               cracked = talloc_asprintf_append_buffer(cracked, "/%s"
+                                                       ldb_dn_escape_value(tmpctx, dn->components[i].value));
                if (!cracked) {
-                       return NULL;
+                       goto done;
                }
        }
 
        /* Last one, possibly a newline for the 'ex' format */
-       if (ex_format) {
-               cracked = talloc_asprintf(mem_ctx, "%s\n%s", cracked, 
-                                         ldb_dn_escape_value(mem_ctx, dn->components[i].value));
-       } else {
-               cracked = talloc_asprintf(mem_ctx, "%s/%s", cracked, 
-                                         ldb_dn_escape_value(mem_ctx, dn->components[i].value));
-       }
+       cracked = talloc_asprintf_append_buffer(cracked, "%s%s", format,
+                                               ldb_dn_escape_value(tmpctx, dn->components[i].value));
+
+       talloc_steal(mem_ctx, cracked);
+done:
+       talloc_free(tmpctx);
        return cracked;
 }
 
@@ -1309,6 +1655,7 @@ int ldb_dn_set_component(struct ldb_dn *dn, int num, const char *name, const str
        v.length = val.length;
        v.data = (uint8_t *)talloc_memdup(dn, val.data, v.length+1);
        if ( ! v.data) {
+               talloc_free(n);
                return LDB_ERR_OTHER;
        }
 
@@ -1317,12 +1664,108 @@ int ldb_dn_set_component(struct ldb_dn *dn, int num, const char *name, const str
        dn->components[num].name = n;
        dn->components[num].value = v;
 
-       if (dn->valid_case) dn->valid_case = false;
-       if (dn->casefold) LDB_FREE(dn->casefold);
+       if (dn->valid_case) {
+               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);
+               }
+               dn->valid_case = false;
+       }
+       LDB_FREE(dn->casefold);
+       LDB_FREE(dn->linearized);
+
+       /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */
+       LDB_FREE(dn->extended_linearized);
 
+       dn->extended_comp_num = 0;
+       LDB_FREE(dn->extended_components);
        return LDB_SUCCESS;
 }
 
+const struct ldb_val *ldb_dn_get_extended_component(struct ldb_dn *dn, const char *name)
+{
+       int i;
+       if ( ! ldb_dn_validate(dn)) {
+               return NULL;
+       }
+       for (i=0; i < dn->extended_comp_num; i++) {
+               if (ldb_attr_cmp(dn->extended_components[i].name, name) == 0) {
+                       return &dn->extended_components[i].value;
+               }
+       }
+       return NULL;
+}
+
+int ldb_dn_set_extended_component(struct ldb_dn *dn, const char *name, const struct ldb_val *val)
+{
+       struct ldb_dn_extended_component *p;
+       int i;
+                               
+       if ( ! ldb_dn_validate(dn)) {
+               return LDB_ERR_OTHER;
+       }
+
+       for (i=0; i < dn->extended_comp_num; i++) {
+               if (ldb_attr_cmp(dn->extended_components[i].name, name) == 0) {
+                       if (val) {
+                               dn->extended_components[i].value = ldb_val_dup(dn->extended_components, val);
+
+                               dn->extended_components[i].name = talloc_strdup(dn->extended_components, name);
+                               if (!dn->extended_components[i].name || !dn->extended_components[i].value.data) {
+                                       dn->invalid = true;
+                                       return LDB_ERR_OPERATIONS_ERROR;
+                               }
+       
+                       } else {
+                               if (i != (dn->extended_comp_num - 1)) {
+                                       memmove(&dn->extended_components[i], &dn->extended_components[i+1],
+                                               ((dn->extended_comp_num-1) - i)*sizeof(*dn->extended_components));
+                               }
+                               dn->extended_comp_num--;
+                               
+                               dn->extended_components = talloc_realloc(dn,
+                                                  dn->extended_components,
+                                                  struct ldb_dn_extended_component,
+                                                  dn->extended_comp_num);
+                               if (!dn->extended_components) {
+                                       dn->invalid = true;
+                                       return LDB_ERR_OPERATIONS_ERROR;
+                               }
+                               return LDB_SUCCESS;
+                       }
+               }
+       }
+
+       p = dn->extended_components
+               = talloc_realloc(dn,
+                                dn->extended_components,
+                                struct ldb_dn_extended_component,
+                                dn->extended_comp_num + 1);
+       if (!dn->extended_components) {
+               dn->invalid = true;
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       p[dn->extended_comp_num].value = ldb_val_dup(dn->extended_components, val);
+       p[dn->extended_comp_num].name = talloc_strdup(p, name);
+       
+       if (!dn->extended_components[i].name || !dn->extended_components[i].value.data) {
+               dn->invalid = true;
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       dn->extended_components = p;
+       dn->extended_comp_num++;
+       
+       return LDB_SUCCESS;
+}
+
+void ldb_dn_remove_extended_components(struct ldb_dn *dn)
+{
+       dn->extended_comp_num = 0;
+       LDB_FREE(dn->extended_components);      
+}
+
 bool ldb_dn_is_valid(struct ldb_dn *dn)
 {
        if ( ! dn) return false;
@@ -1335,6 +1778,13 @@ bool ldb_dn_is_special(struct ldb_dn *dn)
        return dn->special;
 }
 
+bool ldb_dn_has_extended(struct ldb_dn *dn)
+{
+       if ( ! dn || dn->invalid) return false;
+       if (dn->extended_linearized && (dn->extended_linearized[0] == '<')) return true;
+       return dn->extended_comp_num != 0;
+}
+
 bool ldb_dn_check_special(struct ldb_dn *dn, const char *check)
 {
        if ( ! dn || dn->invalid) return false;
@@ -1344,12 +1794,7 @@ bool ldb_dn_check_special(struct ldb_dn *dn, const char *check)
 bool ldb_dn_is_null(struct ldb_dn *dn)
 {
        if ( ! dn || dn->invalid) return false;
-       if (dn->special) return false;
-       if (dn->valid_comp) {
-               if (dn->comp_num == 0) return true;
-               return false;
-       } else {
-               if (dn->linearized[0] == '\0') return true;
-       }
+       if (ldb_dn_has_extended(dn)) return false;
+       if (dn->linearized && (dn->linearized[0] == '\0')) return true;
        return false;
 }