summaryrefslogtreecommitdiff
path: root/source3/lib/ldb/common/ldb_dn.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/lib/ldb/common/ldb_dn.c')
-rw-r--r--source3/lib/ldb/common/ldb_dn.c944
1 files changed, 944 insertions, 0 deletions
diff --git a/source3/lib/ldb/common/ldb_dn.c b/source3/lib/ldb/common/ldb_dn.c
new file mode 100644
index 0000000000..a0f48723ab
--- /dev/null
+++ b/source3/lib/ldb/common/ldb_dn.c
@@ -0,0 +1,944 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ 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.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ 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
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb dn explode and utility functions
+ *
+ * Description: - explode a dn into it's own basic elements
+ * and put them in a structure
+ * - manipulate ldb_dn structures
+ *
+ * Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#define LDB_DN_NULL_FAILED(x) if (!(x)) goto failed
+
+#define LDB_SPECIAL "@SPECIAL"
+
+int ldb_dn_is_special(const struct ldb_dn *dn)
+{
+ if (dn == NULL || dn->comp_num != 1) return 0;
+
+ return ! strcmp(dn->components[0].name, LDB_SPECIAL);
+}
+
+int ldb_dn_check_special(const struct ldb_dn *dn, const char *check)
+{
+ if (dn == NULL || dn->comp_num != 1) return 0;
+
+ return ! strcmp((char *)dn->components[0].value.data, check);
+}
+
+char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value)
+{
+ const char *p, *s, *src;
+ char *d, *dst;
+ int len;
+
+ if (!value.length)
+ return NULL;
+
+ p = s = src = (const char *)value.data;
+ len = value.length;
+
+ /* allocate destination string, it will be at most 3 times the source */
+ dst = d = talloc_array(mem_ctx, char, len * 3 + 1);
+ LDB_DN_NULL_FAILED(dst);
+
+ while (p - src < len) {
+
+ p += strcspn(p, ",=\n+<>#;\\\"");
+
+ if (p - src == len) /* found no escapable chars */
+ break;
+
+ memcpy(d, s, p - s); /* copy the part of the string before the stop */
+ d += (p - s); /* move to current position */
+
+ if (*p) { /* it is a normal escapable character */
+ *d++ = '\\';
+ *d++ = *p++;
+ } else { /* we have a zero byte in the string */
+ strncpy(d, "\00", 3); /* escape the zero */
+ d = d + 3;
+ p++; /* skip the zero */
+ }
+ s = p; /* move forward */
+ }
+
+ /* copy the last part (with zero) and return */
+ memcpy(d, s, &src[len] - s + 1);
+
+ return dst;
+
+failed:
+ talloc_free(dst);
+ return NULL;
+}
+
+static struct ldb_val ldb_dn_unescape_value(void *mem_ctx, const char *src)
+{
+ struct ldb_val value;
+ unsigned x;
+ char *p, *dst = NULL, *end;
+
+ memset(&value, 0, sizeof(value));
+
+ LDB_DN_NULL_FAILED(src);
+
+ dst = p = talloc_memdup(mem_ctx, src, strlen(src) + 1);
+ LDB_DN_NULL_FAILED(dst);
+
+ end = &dst[strlen(dst)];
+
+ while (*p) {
+ p += strcspn(p, ",=\n+<>#;\\\"");
+
+ if (*p == '\\') {
+ if (strchr(",=\n+<>#;\\\"", p[1])) {
+ memmove(p, p + 1, end - (p + 1) + 1);
+ end--;
+ p++;
+ continue;
+ }
+
+ if (sscanf(p + 1, "%02x", &x) == 1) {
+ *p = (unsigned char)x;
+ memmove(p + 1, p + 3, end - (p + 3) + 1);
+ end -= 2;
+ p++;
+ continue;
+ }
+ }
+
+ /* a string with not escaped specials is invalid (tested) */
+ if (*p != '\0') {
+ goto failed;
+ }
+ }
+
+ value.length = end - dst;
+ value.data = (uint8_t *)dst;
+ return value;
+
+failed:
+ talloc_free(dst);
+ return value;
+}
+
+/* check if the string contains quotes
+ * skips leading and trailing spaces
+ * - returns 0 if no quotes found
+ * - returns 1 if quotes are found and put their position
+ * in *quote_start and *quote_end parameters
+ * - return -1 if there are open quotes
+ */
+
+static int get_quotes_position(const char *source, int *quote_start, int *quote_end)
+{
+ const char *p;
+
+ if (source == NULL || quote_start == NULL || quote_end == NULL) return -1;
+
+ p = source;
+
+ /* check if there are quotes surrounding the value */
+ p += strspn(p, " \n"); /* skip white spaces */
+
+ if (*p == '\"') {
+ *quote_start = p - source;
+
+ p++;
+ while (*p != '\"') {
+ p = strchr(p, '\"');
+ LDB_DN_NULL_FAILED(p);
+
+ if (*(p - 1) == '\\')
+ p++;
+ }
+
+ *quote_end = p - source;
+ return 1;
+ }
+
+ return 0;
+
+failed:
+ return -1;
+}
+
+static char *seek_to_separator(char *string, const char *separators)
+{
+ char *p, *q;
+ int ret, qs, qe, escaped;
+
+ if (string == NULL || separators == NULL) return NULL;
+
+ p = strchr(string, '=');
+ LDB_DN_NULL_FAILED(p);
+
+ p++;
+
+ /* check if there are quotes surrounding the value */
+
+ ret = get_quotes_position(p, &qs, &qe);
+ if (ret == -1)
+ return NULL;
+
+ if (ret == 1) { /* quotes found */
+
+ p += qe; /* positioning after quotes */
+ p += strspn(p, " \n"); /* skip white spaces after the quote */
+
+ if (strcspn(p, separators) != 0) /* if there are characters between quotes */
+ return NULL; /* and separators, the dn is invalid */
+
+ return p; /* return on the separator */
+ }
+
+ /* no quotes found seek to separators */
+ q = p;
+ do {
+ escaped = 0;
+ ret = strcspn(q, separators);
+
+ if (q[ret - 1] == '\\') {
+ escaped = 1;
+ q = q + ret + 1;
+ }
+ } while (escaped);
+
+ if (ret == 0 && p == q) /* no separators ?! bail out */
+ return NULL;
+
+ return q + ret;
+
+failed:
+ return NULL;
+}
+
+static char *ldb_dn_trim_string(char *string, const char *edge)
+{
+ char *s, *p;
+
+ /* seek out edge from start of string */
+ s = string + strspn(string, edge);
+
+ /* backwards skip from end of string */
+ p = &s[strlen(s) - 1];
+ while (p > s && strchr(edge, *p)) {
+ *p = '\0';
+ p--;
+ }
+
+ return s;
+}
+
+/* we choosed to not support multpile valued components */
+static struct ldb_dn_component ldb_dn_explode_component(void *mem_ctx, char *raw_component)
+{
+ struct ldb_dn_component dc;
+ char *p;
+ int ret, qs, qe;
+
+ memset(&dc, 0, sizeof(dc));
+
+ if (raw_component == NULL) {
+ return dc;
+ }
+
+ /* find attribute type/value separator */
+ p = strchr(raw_component, '=');
+ LDB_DN_NULL_FAILED(p);
+
+ *p++ = '\0'; /* terminate name and point to value */
+
+ /* copy and trim name in the component */
+ dc.name = talloc_strdup(mem_ctx, ldb_dn_trim_string(raw_component, " \n"));
+ if (!dc.name)
+ return dc;
+
+ if (! ldb_valid_attr_name(dc.name)) {
+ goto failed;
+ }
+
+ ret = get_quotes_position(p, &qs, &qe);
+
+ switch (ret) {
+ case 0: /* no quotes trim the string */
+ p = ldb_dn_trim_string(p, " \n");
+ dc.value = ldb_dn_unescape_value(mem_ctx, p);
+ break;
+
+ case 1: /* quotes found get the unquoted string */
+ p[qe] = '\0';
+ p = p + qs + 1;
+ dc.value.length = strlen(p);
+ dc.value.data = talloc_memdup(mem_ctx, p, dc.value.length + 1);
+ break;
+
+ default: /* mismatched quotes ot other error, bail out */
+ goto failed;
+ }
+
+ if (dc.value.length == 0) {
+ goto failed;
+ }
+
+ return dc;
+
+failed:
+ talloc_free(dc.name);
+ dc.name = NULL;
+ return dc;
+}
+
+struct ldb_dn *ldb_dn_new(void *mem_ctx)
+{
+ struct ldb_dn *edn;
+
+ edn = talloc(mem_ctx, struct ldb_dn);
+ LDB_DN_NULL_FAILED(edn);
+
+ /* Initially there are no components */
+ edn->comp_num = 0;
+ edn->components = NULL;
+
+ return edn;
+
+failed:
+ return NULL;
+}
+
+struct ldb_dn *ldb_dn_explode(void *mem_ctx, const char *dn)
+{
+ struct ldb_dn *edn; /* the exploded dn */
+ char *pdn, *p;
+
+ if (dn == NULL) return NULL;
+
+ /* Allocate a structure to hold the exploded DN */
+ edn = ldb_dn_new(mem_ctx);
+ pdn = NULL;
+
+ /* Empty DNs */
+ if (dn[0] == '\0') {
+ return edn;
+ }
+
+ /* Special DNs case */
+ if (dn[0] == '@') {
+ edn->comp_num = 1;
+ edn->components = talloc(edn, struct ldb_dn_component);
+ if (edn->components == NULL) goto failed;
+ edn->components[0].name = talloc_strdup(edn->components, LDB_SPECIAL);
+ if (edn->components[0].name == NULL) goto failed;
+ edn->components[0].value.data = (uint8_t *)talloc_strdup(edn->components, dn);
+ if (edn->components[0].value.data== NULL) goto failed;
+ edn->components[0].value.length = strlen(dn);
+ return edn;
+ }
+
+ pdn = p = talloc_strdup(edn, dn);
+ LDB_DN_NULL_FAILED(pdn);
+
+ /* get the components */
+ do {
+ char *t;
+
+ /* terminate the current component and return pointer to the next one */
+ t = seek_to_separator(p, ",;");
+ LDB_DN_NULL_FAILED(t);
+
+ if (*t) { /* here there is a separator */
+ *t = '\0'; /*terminate */
+ t++; /* a separtor means another component follows */
+ }
+
+ /* allocate space to hold the dn component */
+ edn->components = talloc_realloc(edn, edn->components,
+ struct ldb_dn_component,
+ edn->comp_num + 1);
+ if (edn->components == NULL)
+ goto failed;
+
+ /* store the exploded component in the main structure */
+ edn->components[edn->comp_num] = ldb_dn_explode_component(edn, p);
+ LDB_DN_NULL_FAILED(edn->components[edn->comp_num].name);
+
+ edn->comp_num++;
+
+ /* jump to the next component if any */
+ p = t;
+
+ } while(*p);
+
+ talloc_free(pdn);
+ return edn;
+
+failed:
+ talloc_free(pdn);
+ talloc_free(edn);
+ return NULL;
+}
+
+struct ldb_dn *ldb_dn_explode_or_special(void *mem_ctx, const char *dn)
+{
+ struct ldb_dn *edn; /* the exploded dn */
+
+ if (dn == NULL) return NULL;
+
+ if (strncasecmp(dn, "<GUID=", 6) == 0) {
+ /* this is special DN returned when the
+ * exploded_dn control is used
+ */
+
+ /* Allocate a structure to hold the exploded DN */
+ edn = ldb_dn_new(mem_ctx);
+
+ edn->comp_num = 1;
+ edn->components = talloc(edn, struct ldb_dn_component);
+ if (edn->components == NULL) goto failed;
+ edn->components[0].name = talloc_strdup(edn->components, LDB_SPECIAL);
+ if (edn->components[0].name == NULL) goto failed;
+ edn->components[0].value.data = (uint8_t *)talloc_strdup(edn->components, dn);
+ if (edn->components[0].value.data== NULL) goto failed;
+ edn->components[0].value.length = strlen(dn);
+ return edn;
+
+ }
+
+ return ldb_dn_explode(mem_ctx, dn);
+
+failed:
+ talloc_free(edn);
+ return NULL;
+}
+
+char *ldb_dn_linearize(void *mem_ctx, const struct ldb_dn *edn)
+{
+ char *dn, *value;
+ int i;
+
+ if (edn == NULL) return NULL;
+
+ /* Special DNs */
+ if (ldb_dn_is_special(edn)) {
+ dn = talloc_strdup(mem_ctx, (char *)edn->components[0].value.data);
+ return dn;
+ }
+
+ dn = talloc_strdup(mem_ctx, "");
+ LDB_DN_NULL_FAILED(dn);
+
+ for (i = 0; i < edn->comp_num; i++) {
+ value = ldb_dn_escape_value(dn, edn->components[i].value);
+ LDB_DN_NULL_FAILED(value);
+
+ if (i == 0) {
+ dn = talloc_asprintf_append(dn, "%s=%s", edn->components[i].name, value);
+ } else {
+ dn = talloc_asprintf_append(dn, ",%s=%s", edn->components[i].name, value);
+ }
+ LDB_DN_NULL_FAILED(dn);
+
+ talloc_free(value);
+ }
+
+ return dn;
+
+failed:
+ talloc_free(dn);
+ return NULL;
+}
+
+/* Determine if dn is below base, in the ldap tree. Used for
+ * evaluating a subtree search.
+ * 0 if they match, otherwise non-zero
+ */
+
+int ldb_dn_compare_base(struct ldb_context *ldb,
+ const struct ldb_dn *base,
+ const struct ldb_dn *dn)
+{
+ int ret;
+ int n0, n1;
+
+ if (base == NULL || base->comp_num == 0) return 0;
+ if (dn == NULL || dn->comp_num == 0) return -1;
+
+ /* if the base has more componts than the dn, then they differ */
+ if (base->comp_num > dn->comp_num) {
+ return (dn->comp_num - base->comp_num);
+ }
+
+ n0 = base->comp_num - 1;
+ n1 = dn->comp_num - 1;
+ while (n0 >= 0 && n1 >= 0) {
+ const struct ldb_attrib_handler *h;
+
+ /* compare names (attribute names are guaranteed to be ASCII only) */
+ ret = ldb_attr_cmp(base->components[n0].name,
+ dn->components[n1].name);
+ if (ret) {
+ return ret;
+ }
+
+ /* names match, compare values */
+ h = ldb_attrib_handler(ldb, base->components[n0].name);
+ ret = h->comparison_fn(ldb, ldb, &(base->components[n0].value),
+ &(dn->components[n1].value));
+ if (ret) {
+ return ret;
+ }
+ n1--;
+ n0--;
+ }
+
+ return 0;
+}
+
+/* compare DNs using casefolding compare functions.
+
+ If they match, then return 0
+ */
+
+int ldb_dn_compare(struct ldb_context *ldb,
+ const struct ldb_dn *edn0,
+ const struct ldb_dn *edn1)
+{
+ if (edn0 == NULL || edn1 == NULL) return edn1 - edn0;
+
+ if (edn0->comp_num != edn1->comp_num)
+ return (edn1->comp_num - edn0->comp_num);
+
+ return ldb_dn_compare_base(ldb, edn0, edn1);
+}
+
+int ldb_dn_cmp(struct ldb_context *ldb, const char *dn0, const char *dn1)
+{
+ struct ldb_dn *edn0;
+ struct ldb_dn *edn1;
+ int ret;
+
+ if (dn0 == NULL || dn1 == NULL) return dn1 - dn0;
+
+ edn0 = ldb_dn_explode_casefold(ldb, dn0);
+ if (edn0 == NULL) return 1;
+
+ edn1 = ldb_dn_explode_casefold(ldb, dn1);
+ if (edn1 == NULL) {
+ talloc_free(edn0);
+ return -1;
+ }
+
+ ret = ldb_dn_compare(ldb, edn0, edn1);
+
+ talloc_free(edn0);
+ talloc_free(edn1);
+
+ return ret;
+}
+
+/*
+ casefold a dn. We need to casefold the attribute names, and canonicalize
+ attribute values of case insensitive attributes.
+*/
+struct ldb_dn *ldb_dn_casefold(struct ldb_context *ldb, const struct ldb_dn *edn)
+{
+ struct ldb_dn *cedn;
+ int i;
+
+ if (edn == NULL) return NULL;
+
+ cedn = ldb_dn_new(ldb);
+ if (!cedn) {
+ return NULL;
+ }
+
+ cedn->comp_num = edn->comp_num;
+ cedn->components = talloc_array(cedn, struct ldb_dn_component, edn->comp_num);
+ if (!cedn->components) {
+ talloc_free(cedn);
+ return NULL;
+ }
+
+ for (i = 0; i < edn->comp_num; i++) {
+ struct ldb_dn_component dc;
+ const struct ldb_attrib_handler *h;
+
+ dc.name = ldb_attr_casefold(cedn, edn->components[i].name);
+ if (!dc.name) {
+ talloc_free(cedn);
+ return NULL;
+ }
+
+ h = ldb_attrib_handler(ldb, dc.name);
+ if (h->canonicalise_fn(ldb, cedn, &(edn->components[i].value), &(dc.value)) != 0) {
+ talloc_free(cedn);
+ return NULL;
+ }
+
+ cedn->components[i] = dc;
+ }
+
+ return cedn;
+}
+
+struct ldb_dn *ldb_dn_explode_casefold(struct ldb_context *ldb, const char *dn)
+{
+ struct ldb_dn *edn, *cdn;
+
+ if (dn == NULL) return NULL;
+
+ edn = ldb_dn_explode(ldb, dn);
+ if (edn == NULL) return NULL;
+
+ cdn = ldb_dn_casefold(ldb, edn);
+
+ talloc_free(edn);
+ return cdn;
+}
+
+char *ldb_dn_linearize_casefold(struct ldb_context *ldb, const struct ldb_dn *edn)
+{
+ struct ldb_dn *cdn;
+ char *dn;
+
+ if (edn == NULL) return NULL;
+
+ /* Special DNs */
+ if (ldb_dn_is_special(edn)) {
+ dn = talloc_strdup(ldb, (char *)edn->components[0].value.data);
+ return dn;
+ }
+
+ cdn = ldb_dn_casefold(ldb, edn);
+ if (cdn == NULL) return NULL;
+
+ dn = ldb_dn_linearize(ldb, cdn);
+ if (dn == NULL) {
+ talloc_free(cdn);
+ return NULL;
+ }
+
+ talloc_free(cdn);
+ return dn;
+}
+
+static struct ldb_dn_component ldb_dn_copy_component(void *mem_ctx, struct ldb_dn_component *src)
+{
+ struct ldb_dn_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) {
+ talloc_free(dst.value.data);
+ }
+
+ return dst;
+}
+
+/* copy specified number of elements of a dn into a new one
+ element are copied from top level up to the unique rdn
+ num_el may be greater than dn->comp_num (see ldb_dn_make_child)
+*/
+struct ldb_dn *ldb_dn_copy_partial(void *mem_ctx, const struct ldb_dn *dn, int num_el)
+{
+ struct ldb_dn *newdn;
+ int i, n, e;
+
+ if (dn == NULL) return NULL;
+ if (num_el <= 0) return NULL;
+
+ newdn = ldb_dn_new(mem_ctx);
+ LDB_DN_NULL_FAILED(newdn);
+
+ newdn->comp_num = num_el;
+ n = newdn->comp_num - 1;
+ newdn->components = talloc_array(newdn, struct ldb_dn_component, newdn->comp_num);
+
+ if (dn->comp_num == 0) return newdn;
+ e = dn->comp_num - 1;
+
+ for (i = 0; i < newdn->comp_num; i++) {
+ newdn->components[n - i] = ldb_dn_copy_component(newdn->components,
+ &(dn->components[e - i]));
+ if ((e - i) == 0) {
+ return newdn;
+ }
+ }
+
+ return newdn;
+
+failed:
+ talloc_free(newdn);
+ return NULL;
+}
+
+struct ldb_dn *ldb_dn_copy(void *mem_ctx, const struct ldb_dn *dn)
+{
+ if (dn == NULL) return NULL;
+ return ldb_dn_copy_partial(mem_ctx, dn, dn->comp_num);
+}
+
+struct ldb_dn *ldb_dn_get_parent(void *mem_ctx, const struct ldb_dn *dn)
+{
+ if (dn == NULL) return NULL;
+ return ldb_dn_copy_partial(mem_ctx, dn, dn->comp_num - 1);
+}
+
+struct ldb_dn_component *ldb_dn_build_component(void *mem_ctx, const char *attr,
+ const char *val)
+{
+ struct ldb_dn_component *dc;
+
+ if (attr == NULL || val == NULL) return NULL;
+
+ dc = talloc(mem_ctx, struct ldb_dn_component);
+ if (dc == NULL) return NULL;
+
+ dc->name = talloc_strdup(dc, attr);
+ if (dc->name == NULL) {
+ talloc_free(dc);
+ return NULL;
+ }
+
+ dc->value.data = (uint8_t *)talloc_strdup(dc, val);
+ if (dc->value.data == NULL) {
+ talloc_free(dc);
+ return NULL;
+ }
+
+ dc->value.length = strlen(val);
+
+ return dc;
+}
+
+struct ldb_dn *ldb_dn_build_child(void *mem_ctx, const char *attr,
+ const char * value,
+ const struct ldb_dn *base)
+{
+ struct ldb_dn *newdn;
+ if (! ldb_valid_attr_name(attr)) return NULL;
+ if (value == NULL || value == '\0') return NULL;
+
+ if (base != NULL) {
+ newdn = ldb_dn_copy_partial(mem_ctx, base, base->comp_num + 1);
+ LDB_DN_NULL_FAILED(newdn);
+ } else {
+ newdn = ldb_dn_new(mem_ctx);
+ LDB_DN_NULL_FAILED(newdn);
+
+ newdn->comp_num = 1;
+ newdn->components = talloc_array(newdn, struct ldb_dn_component, newdn->comp_num);
+ }
+
+ newdn->components[0].name = talloc_strdup(newdn->components, attr);
+ LDB_DN_NULL_FAILED(newdn->components[0].name);
+
+ newdn->components[0].value.data = (uint8_t *)talloc_strdup(newdn->components, value);
+ LDB_DN_NULL_FAILED(newdn->components[0].value.data);
+ newdn->components[0].value.length = strlen((char *)newdn->components[0].value.data);
+
+ return newdn;
+
+failed:
+ talloc_free(newdn);
+ return NULL;
+
+}
+
+struct ldb_dn *ldb_dn_make_child(void *mem_ctx, const struct ldb_dn_component *component,
+ const struct ldb_dn *base)
+{
+ if (component == NULL) return NULL;
+
+ return ldb_dn_build_child(mem_ctx, component->name,
+ (char *)component->value.data, base);
+}
+
+struct ldb_dn *ldb_dn_compose(void *mem_ctx, const struct ldb_dn *dn1, const struct ldb_dn *dn2)
+{
+ int i;
+ struct ldb_dn *newdn;
+
+ if (dn2 == NULL && dn1 == NULL) {
+ return NULL;
+ }
+
+ if (dn2 == NULL) {
+ newdn = ldb_dn_new(mem_ctx);
+ LDB_DN_NULL_FAILED(newdn);
+
+ newdn->comp_num = dn1->comp_num;
+ newdn->components = talloc_array(newdn, struct ldb_dn_component, newdn->comp_num);
+ } else {
+ int comp_num = dn2->comp_num;
+ if (dn1 != NULL) comp_num += dn1->comp_num;
+ newdn = ldb_dn_copy_partial(mem_ctx, dn2, comp_num);
+ }
+
+ if (dn1 == NULL) {
+ return newdn;
+ }
+
+ for (i = 0; i < dn1->comp_num; i++) {
+ newdn->components[i] = ldb_dn_copy_component(newdn->components,
+ &(dn1->components[i]));
+ }
+
+ return newdn;
+
+failed:
+ talloc_free(newdn);
+ return NULL;
+}
+
+struct ldb_dn *ldb_dn_string_compose(void *mem_ctx, const struct ldb_dn *base, const char *child_fmt, ...)
+{
+ struct ldb_dn *dn;
+ char *child_str;
+ va_list ap;
+
+ if (child_fmt == NULL) return NULL;
+
+ va_start(ap, child_fmt);
+ child_str = talloc_vasprintf(mem_ctx, child_fmt, ap);
+ va_end(ap);
+
+ if (child_str == NULL) return NULL;
+
+ dn = ldb_dn_compose(mem_ctx, ldb_dn_explode(mem_ctx, child_str), base);
+
+ talloc_free(child_str);
+
+ return dn;
+}
+
+struct ldb_dn_component *ldb_dn_get_rdn(void *mem_ctx, const struct ldb_dn *dn)
+{
+ struct ldb_dn_component *rdn;
+
+ if (dn == NULL) return NULL;
+
+ if (dn->comp_num < 1) {
+ return NULL;
+ }
+
+ rdn = talloc(mem_ctx, struct ldb_dn_component);
+ if (rdn == NULL) return NULL;
+
+ *rdn = ldb_dn_copy_component(mem_ctx, &dn->components[0]);
+ if (rdn->name == NULL) {
+ talloc_free(rdn);
+ return NULL;
+ }
+
+ return rdn;
+}
+
+/* Create a 'canonical name' string from a DN:
+
+ ie dc=samba,dc=org -> samba.org/
+ uid=administrator,ou=users,dc=samba,dc=org = samba.org/users/administrator
+
+ There are two formats, the EX format has the last / replaced with a newline (\n).
+
+*/
+static char *ldb_dn_canonical(void *mem_ctx, const struct ldb_dn *dn, int ex_format) {
+ int i;
+ char *cracked = NULL;
+
+ /* 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);
+ } else {
+ cracked = ldb_dn_escape_value(mem_ctx, dn->components[i].value);
+ }
+ if (!cracked) {
+ return NULL;
+ }
+ }
+
+ /* 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;
+ }
+
+ /* 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));
+ if (!cracked) {
+ return NULL;
+ }
+ }
+
+ /* 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));
+ }
+ return cracked;
+}
+
+/* Wrapper functions for the above, for the two different string formats */
+char *ldb_dn_canonical_string(void *mem_ctx, const struct ldb_dn *dn) {
+ return ldb_dn_canonical(mem_ctx, dn, 0);
+
+}
+
+char *ldb_dn_canonical_ex_string(void *mem_ctx, const struct ldb_dn *dn) {
+ return ldb_dn_canonical(mem_ctx, dn, 1);
+}