-/*
+/*
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.
+ 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
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/>.
*/
/*
* Name: ldb
*
- * Component: ldb dn explode and utility functions
+ * Component: ldb dn creation and manipulation utility functions
*
* Description: - explode a dn into it's own basic elements
- * and put them in a structure
+ * and put them in a structure (only if necessary)
* - manipulate ldb_dn structures
*
* Author: Simo Sorce
*/
-#include "includes.h"
-#include "ldb/include/ldb.h"
-#include "ldb/include/ldb_private.h"
+#include "ldb_private.h"
#include <ctype.h>
#define LDB_DN_NULL_FAILED(x) if (!(x)) goto failed
-#define LDB_SPECIAL "@SPECIAL"
+#define LDB_FREE(x) do { talloc_free(x); x = NULL; } while(0)
-int ldb_dn_is_special(const struct ldb_dn *dn)
-{
- if (dn == NULL || dn->comp_num != 1) return 0;
+/**
+ internal ldb exploded dn structures
+*/
+struct ldb_dn_component {
- return ! strcmp(dn->components[0].name, LDB_SPECIAL);
-}
+ char *name;
+ struct ldb_val value;
-int ldb_dn_check_special(const struct ldb_dn *dn, const char *check)
-{
- if (dn == NULL || dn->comp_num != 1) return 0;
+ char *cf_name;
+ struct ldb_val cf_value;
+};
+
+struct ldb_dn_ext_component {
+
+ char *name;
+ struct ldb_val value;
+};
+
+struct ldb_dn {
+
+ struct ldb_context *ldb;
+
+ /* Special DNs are always linearized */
+ bool special;
+ bool invalid;
+
+ bool valid_case;
+
+ char *linearized;
+ char *ext_linearized;
+ char *casefold;
+
+ unsigned int comp_num;
+ struct ldb_dn_component *components;
- return ! strcmp((char *)dn->components[0].value.data, check);
+ unsigned int ext_comp_num;
+ struct ldb_dn_ext_component *ext_components;
+};
+
+/* it is helpful to be able to break on this in gdb */
+static void ldb_dn_mark_invalid(struct ldb_dn *dn)
+{
+ dn->invalid = true;
}
-static int ldb_dn_is_valid_attribute_name(const char *name)
+/* strdn may be NULL */
+struct ldb_dn *ldb_dn_from_ldb_val(void *mem_ctx,
+ struct ldb_context *ldb,
+ const struct ldb_val *strdn)
{
- if (name == NULL) return 0;
+ struct ldb_dn *dn;
- while (*name) {
- if (! isascii(*name)) {
- return 0;
+ if (! ldb) return NULL;
+
+ if (strdn && strdn->data
+ && (strnlen((const char*)strdn->data, strdn->length) != strdn->length)) {
+ /* The RDN must not contain a character with value 0x0 */
+ return NULL;
+ }
+
+ dn = talloc_zero(mem_ctx, struct ldb_dn);
+ LDB_DN_NULL_FAILED(dn);
+
+ dn->ldb = ldb;
+
+ if (strdn->data && strdn->length) {
+ const char *data = (const char *)strdn->data;
+ size_t length = strdn->length;
+
+ if (data[0] == '@') {
+ dn->special = true;
}
- if (! (isalnum((unsigned char)*name) || *name == '-')) {
- return 0;
+ dn->ext_linearized = talloc_strndup(dn, data, length);
+ LDB_DN_NULL_FAILED(dn->ext_linearized);
+
+ if (data[0] == '<') {
+ const char *p_save, *p = dn->ext_linearized;
+ do {
+ p_save = p;
+ p = strstr(p, ">;");
+ if (p) {
+ p = p + 2;
+ }
+ } while (p);
+
+ if (p_save == dn->ext_linearized) {
+ dn->linearized = talloc_strdup(dn, "");
+ } else {
+ dn->linearized = talloc_strdup(dn, p_save);
+ }
+ LDB_DN_NULL_FAILED(dn->linearized);
+ } else {
+ dn->linearized = dn->ext_linearized;
+ dn->ext_linearized = NULL;
}
- name++;
+ } else {
+ dn->linearized = talloc_strdup(dn, "");
+ LDB_DN_NULL_FAILED(dn->linearized);
}
- return 1;
+ return dn;
+
+failed:
+ talloc_free(dn);
+ return NULL;
}
-char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value)
+/* strdn may be NULL */
+struct ldb_dn *ldb_dn_new(void *mem_ctx,
+ struct ldb_context *ldb,
+ const char *strdn)
{
- const char *p, *s, *src;
- char *d, *dst;
- int len;
+ struct ldb_val blob;
+ blob.data = discard_const_p(uint8_t, strdn);
+ blob.length = strdn ? strlen(strdn) : 0;
+ return ldb_dn_from_ldb_val(mem_ctx, ldb, &blob);
+}
- if (!value.length)
- return NULL;
+struct ldb_dn *ldb_dn_new_fmt(void *mem_ctx,
+ struct ldb_context *ldb,
+ const char *new_fmt, ...)
+{
+ char *strdn;
+ va_list ap;
- p = s = src = (const char *)value.data;
- len = value.length;
+ if ( (! mem_ctx) || (! ldb)) return NULL;
- /* 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);
+ va_start(ap, new_fmt);
+ strdn = talloc_vasprintf(mem_ctx, new_fmt, ap);
+ va_end(ap);
- while (p - src < len) {
+ if (strdn) {
+ struct ldb_dn *dn = ldb_dn_new(mem_ctx, ldb, strdn);
+ talloc_free(strdn);
+ return dn;
+ }
- p += strcspn(p, ",=\n+<>#;\\\"");
+ 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;
+
+ p = s = src;
+ d = dst;
+
+ while (p - src < len) {
+ p += strcspn(p, ",=\n\r+<>#;\\\" ");
if (p - src == len) /* found no escapable chars */
break;
- memcpy(d, s, p - s); /* copy the part of the string before the stop */
+ /* 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 = d + 3;
- p++; /* skip the zero */
+ break;
+
+ default: {
+ /* any others get \XX form */
+ unsigned char v;
+ const char *hexbytes = "0123456789ABCDEF";
+ v = *(unsigned char *)p;
+ *d++ = '\\';
+ *d++ = hexbytes[v>>4];
+ *d++ = hexbytes[v&0xF];
+ p++;
+ break;
+ }
}
s = p; /* move forward */
}
/* copy the last part (with zero) and return */
- memcpy(d, s, &src[len] - s + 1);
-
- return dst;
+ l = len - (s - src);
+ memcpy(d, s, l + 1);
-failed:
- talloc_free(dst);
- return NULL;
+ /* return the length of the resulting string */
+ return (l + (d - dst));
}
-static struct ldb_val ldb_dn_unescape_value(void *mem_ctx, const char *src)
+char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value)
{
- struct ldb_val value;
- unsigned x;
- char *p, *dst = NULL, *end;
+ char *dst;
- memset(&value, 0, sizeof(value));
-
- LDB_DN_NULL_FAILED(src);
+ if (!value.length)
+ return NULL;
- dst = p = talloc_memdup(mem_ctx, src, strlen(src) + 1);
- LDB_DN_NULL_FAILED(dst);
+ /* allocate destination string, it will be at most 3 times the source */
+ dst = talloc_array(mem_ctx, char, value.length * 3 + 1);
+ if ( ! dst) {
+ talloc_free(dst);
+ return NULL;
+ }
- end = &dst[strlen(dst)];
+ ldb_dn_escape_internal(dst, (const char *)value.data, value.length);
- while (*p) {
- p += strcspn(p, ",=\n+<>#;\\\"");
+ dst = talloc_realloc(mem_ctx, dst, char, strlen(dst) + 1);
- if (*p == '\\') {
- if (strchr(",=\n+<>#;\\\"", p[1])) {
- memmove(p, p + 1, end - (p + 1) + 1);
- end--;
- p++;
- continue;
- }
+ return dst;
+}
- if (sscanf(p + 1, "%02x", &x) == 1) {
- *p = (unsigned char)x;
- memmove(p + 1, p + 3, end - (p + 3) + 1);
- end -= 2;
- p++;
- continue;
- }
- }
+/*
+ explode a DN string into a ldb_dn structure
+ based on RFC4514 except that we don't support multiple valued RDNs
- /* a string with not escaped specials is invalid (tested) */
- if (*p != '\0') {
- goto failed;
- }
- }
+ TODO: according to MS-ADTS:3.1.1.5.2 Naming Constraints
+ DN must be compliant with RFC2253
+*/
+static bool ldb_dn_explode(struct ldb_dn *dn)
+{
+ 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, ret;
+ char *parse_dn;
+ bool is_index;
- value.length = end - dst;
- value.data = (uint8_t *)dst;
- return value;
+ if ( ! dn || dn->invalid) return false;
-failed:
- talloc_free(dst);
- return value;
-}
+ if (dn->components) {
+ return true;
+ }
-/* 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
- */
+ if (dn->ext_linearized) {
+ parse_dn = dn->ext_linearized;
+ } else {
+ parse_dn = dn->linearized;
+ }
-static int get_quotes_position(const char *source, int *quote_start, int *quote_end)
-{
- const char *p;
+ if ( ! parse_dn ) {
+ return false;
+ }
- if (source == NULL || quote_start == NULL || quote_end == NULL) return -1;
+ is_index = (strncmp(parse_dn, "DN=@INDEX:", 10) == 0);
- p = source;
+ /* Empty DNs */
+ if (parse_dn[0] == '\0') {
+ return true;
+ }
- /* check if there are quotes surrounding the value */
- p += strspn(p, " \n"); /* skip white spaces */
+ /* Special DNs case */
+ if (dn->special) {
+ return true;
+ }
- if (*p == '\"') {
- *quote_start = p - source;
+ /* make sure we free this if alloced previously before replacing */
+ talloc_free(dn->components);
- p++;
- while (*p != '\"') {
- p = strchr(p, '\"');
- LDB_DN_NULL_FAILED(p);
+ talloc_free(dn->ext_components);
+ dn->ext_components = NULL;
- if (*(p - 1) == '\\')
- p++;
- }
+ /* in the common case we have 3 or more components */
+ /* make sure all components are zeroed, other functions depend on it */
+ dn->components = talloc_zero_array(dn, struct ldb_dn_component, 3);
+ if ( ! dn->components) {
+ return false;
+ }
+ dn->comp_num = 0;
- *quote_end = p - source;
- return 1;
+ /* Components data space is allocated here once */
+ data = talloc_array(dn->components, char, strlen(parse_dn) + 1);
+ if (!data) {
+ return false;
}
- return 0;
+ p = parse_dn;
+ in_extended = true;
+ in_ex_name = false;
+ in_ex_value = false;
+ trim = true;
+ t = NULL;
+ d = dt = data;
-failed:
- return -1;
-}
+ 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;
+ }
+ }
-static char *seek_to_separator(char *string, const char *separators)
-{
- char *p, *q;
- int ret, qs, qe, escaped;
+ if (in_ex_name && *p == '=') {
+ *d++ = '\0';
+ p++;
+ ex_value = d;
+ in_ex_name = false;
+ in_ex_value = true;
+ continue;
+ }
- if (string == NULL || separators == NULL) return NULL;
+ if (in_ex_value && *p == '>') {
+ const struct ldb_dn_extended_syntax *ext_syntax;
+ struct ldb_val ex_val = {
+ .data = (uint8_t *)ex_value,
+ .length = d - ex_value
+ };
- p = strchr(string, '=');
- LDB_DN_NULL_FAILED(p);
+ *d++ = '\0';
+ p++;
+ in_ex_value = false;
+
+ /* Process name and ex_value */
+
+ dn->ext_components = talloc_realloc(dn,
+ dn->ext_components,
+ struct ldb_dn_ext_component,
+ dn->ext_comp_num + 1);
+ if ( ! dn->ext_components) {
+ /* ouch ! */
+ goto failed;
+ }
+
+ ext_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, ex_name);
+ if (!ext_syntax) {
+ /* We don't know about this type of extended DN */
+ goto failed;
+ }
+
+ dn->ext_components[dn->ext_comp_num].name = talloc_strdup(dn->ext_components, ex_name);
+ if (!dn->ext_components[dn->ext_comp_num].name) {
+ /* ouch */
+ goto failed;
+ }
+ ret = ext_syntax->read_fn(dn->ldb, dn->ext_components,
+ &ex_val, &dn->ext_components[dn->ext_comp_num].value);
+ if (ret != LDB_SUCCESS) {
+ ldb_dn_mark_invalid(dn);
+ goto failed;
+ }
+
+ dn->ext_comp_num++;
+
+ if (*p == '\0') {
+ /* We have reached the end (extended component only)! */
+ talloc_free(data);
+ return true;
+
+ } else if (*p == ';') {
+ p++;
+ continue;
+ } else {
+ ldb_dn_mark_invalid(dn);
+ goto failed;
+ }
+ }
- p++;
+ *d++ = *p++;
+ continue;
+ }
+ if (in_attr) {
+ if (trim) {
+ if (*p == ' ') {
+ p++;
+ continue;
+ }
+
+ /* first char */
+ trim = false;
+
+ if (!isascii(*p)) {
+ /* attr names must be ascii only */
+ ldb_dn_mark_invalid(dn);
+ goto failed;
+ }
+
+ if (isdigit(*p)) {
+ is_oid = true;
+ } else
+ if ( ! isalpha(*p)) {
+ /* not a digit nor an alpha,
+ * invalid attribute name */
+ ldb_dn_mark_invalid(dn);
+ goto failed;
+ }
+
+ /* Copy this character across from parse_dn,
+ * now we have trimmed out spaces */
+ *d++ = *p++;
+ continue;
+ }
- /* check if there are quotes surrounding the value */
+ if (*p == ' ') {
+ p++;
+ /* valid only if we are at the end */
+ trim = true;
+ continue;
+ }
- ret = get_quotes_position(p, &qs, &qe);
- if (ret == -1)
- return NULL;
+ if (trim && (*p != '=')) {
+ /* spaces/tabs are not allowed */
+ ldb_dn_mark_invalid(dn);
+ goto failed;
+ }
- if (ret == 1) { /* quotes found */
+ if (*p == '=') {
+ /* attribute terminated */
+ in_attr = false;
+ in_value = true;
+ 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 = talloc_strdup(dn->components, dt);
+ if ( ! dn->components[dn->comp_num].name) {
+ /* ouch */
+ goto failed;
+ }
+
+ dt = d;
- p += qe; /* positioning after quotes */
- p += strspn(p, " \n"); /* skip white spaces after the quote */
+ p++;
+ continue;
+ }
- if (strcspn(p, separators) != 0) /* if there are characters between quotes */
- return NULL; /* and separators, the dn is invalid */
+ if (!isascii(*p)) {
+ /* attr names must be ascii only */
+ ldb_dn_mark_invalid(dn);
+ goto failed;
+ }
- return p; /* return on the separator */
- }
+ if (is_oid && ( ! (isdigit(*p) || (*p == '.')))) {
+ /* not a digit nor a dot,
+ * invalid attribute oid */
+ ldb_dn_mark_invalid(dn);
+ goto failed;
+ } else
+ if ( ! (isalpha(*p) || isdigit(*p) || (*p == '-'))) {
+ /* not ALPHA, DIGIT or HYPHEN */
+ ldb_dn_mark_invalid(dn);
+ goto failed;
+ }
- /* 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;
+ *d++ = *p++;
+ continue;
}
- } while (escaped);
- if (ret == 0 && p == q) /* no separators ?! bail out */
- return NULL;
+ if (in_value) {
+ if (in_quote) {
+ if (*p == '\"') {
+ if (p[-1] != '\\') {
+ p++;
+ in_quote = false;
+ continue;
+ }
+ }
+ *d++ = *p++;
+ l++;
+ continue;
+ }
- return q + ret;
+ if (trim) {
+ if (*p == ' ') {
+ p++;
+ continue;
+ }
-failed:
- return NULL;
-}
+ /* first char */
+ trim = false;
-static char *ldb_dn_trim_string(char *string, const char *edge)
-{
- char *s, *p;
+ if (*p == '\"') {
+ in_quote = true;
+ p++;
+ continue;
+ }
+ }
- /* seek out edge from start of string */
- s = string + strspn(string, edge);
+ switch (*p) {
- /* backwards skip from end of string */
- p = &s[strlen(s) - 1];
- while (p > s && strchr(edge, *p)) {
- *p = '\0';
- p--;
- }
+ /* TODO: support ber encoded values
+ case '#':
+ */
- return s;
-}
+ case ',':
+ if (escape) {
+ *d++ = *p++;
+ l++;
+ escape = false;
+ continue;
+ }
+ /* ok found value terminator */
-/* 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;
+ if ( t ) {
+ /* trim back */
+ d -= (p - t);
+ l -= (p - t);
+ }
- memset(&dc, 0, sizeof(dc));
+ in_attr = true;
+ in_value = false;
+ trim = true;
- if (raw_component == NULL) {
- return dc;
- }
+ p++;
+ *d++ = '\0';
+ 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++;
+ if (dn->comp_num > 2) {
+ dn->components = talloc_realloc(dn,
+ dn->components,
+ struct ldb_dn_component,
+ dn->comp_num + 1);
+ if ( ! dn->components) {
+ /* ouch ! */
+ goto failed;
+ }
+ /* make sure all components are zeroed, other functions depend on this */
+ memset(&dn->components[dn->comp_num], '\0', sizeof(struct ldb_dn_component));
+ }
- /* find attribute type/value separator */
- p = strchr(raw_component, '=');
- LDB_DN_NULL_FAILED(p);
+ continue;
- *p++ = '\0'; /* terminate name and point to value */
+ case '+':
+ case '=':
+ /* to main compatibility with earlier
+ versions of ldb indexing, we have to
+ accept the base64 encoded binary index
+ values, which contain a '+' or '='
+ which should normally be escaped */
+ if (is_index) {
+ if ( t ) t = NULL;
+ *d++ = *p++;
+ l++;
+ break;
+ }
+ /* fall through */
+ case '\"':
+ case '<':
+ case '>':
+ case ';':
+ /* a string with not escaped specials is invalid (tested) */
+ if ( ! escape) {
+ ldb_dn_mark_invalid(dn);
+ goto failed;
+ }
+ escape = false;
+
+ *d++ = *p++;
+ l++;
+
+ if ( t ) t = NULL;
+ break;
+
+ case '\\':
+ if ( ! escape) {
+ escape = true;
+ p++;
+ continue;
+ }
+ escape = false;
+
+ *d++ = *p++;
+ l++;
+
+ if ( t ) t = NULL;
+ break;
+
+ default:
+ if (escape) {
+ 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;
+ l++;
+ if ( t ) t = NULL;
+ break;
+ }
+
+ if (*p == ' ') {
+ if ( ! t) t = p;
+ } else {
+ if ( t ) t = NULL;
+ }
+
+ *d++ = *p++;
+ l++;
+
+ break;
+ }
- /* 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_dn_is_valid_attribute_name(dc.name)) {
+ if (in_attr || in_quote) {
+ /* invalid dn */
+ ldb_dn_mark_invalid(dn);
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;
+ /* save last element */
+ if ( t ) {
+ /* trim back */
+ d -= (p - t);
+ l -= (p - t);
}
- if (dc.value.length == 0) {
+ *d++ = '\0';
+ dn->components[dn->comp_num].value.length = l;
+ dn->components[dn->comp_num].value.data =
+ (uint8_t *)talloc_strdup(dn->components, dt);
+ if ( ! dn->components[dn->comp_num].value.data) {
+ /* ouch */
goto failed;
}
- return dc;
+ dn->comp_num++;
+
+ talloc_free(data);
+ return true;
failed:
- talloc_free(dc.name);
- dc.name = NULL;
- return dc;
+ dn->comp_num = 0;
+ talloc_free(dn->components);
+ return false;
}
-struct ldb_dn *ldb_dn_new(void *mem_ctx)
+bool ldb_dn_validate(struct ldb_dn *dn)
{
- 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;
+ return ldb_dn_explode(dn);
}
-struct ldb_dn *ldb_dn_explode(void *mem_ctx, const char *dn)
+const char *ldb_dn_get_linearized(struct ldb_dn *dn)
{
- struct ldb_dn *edn; /* the exploded dn */
- char *pdn, *p;
+ int i, len;
+ char *d, *n;
- if (dn == NULL) return NULL;
+ if ( ! dn || ( dn->invalid)) return NULL;
- /* Allocate a structure to hold the exploded DN */
- edn = ldb_dn_new(mem_ctx);
- pdn = NULL;
+ if (dn->linearized) return dn->linearized;
- /* Empty DNs */
- if (dn[0] == '\0') {
- return edn;
+ if ( ! dn->components) {
+ ldb_dn_mark_invalid(dn);
+ return NULL;
}
- /* 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);
+ if (dn->comp_num == 0) {
+ dn->linearized = talloc_strdup(dn, "");
+ if ( ! dn->linearized) return NULL;
+ return dn->linearized;
+ }
- talloc_free(pdn);
- return edn;
+ /* calculate maximum possible length of DN */
+ for (len = 0, i = 0; i < dn->comp_num; i++) {
+ /* name len */
+ len += strlen(dn->components[i].name);
+ /* max escaped data len */
+ len += (dn->components[i].value.length * 3);
+ len += 2; /* '=' and ',' */
+ }
+ dn->linearized = talloc_array(dn, char, len);
+ if ( ! dn->linearized) return NULL;
-failed:
- talloc_free(pdn);
- talloc_free(edn);
- return NULL;
-}
+ d = dn->linearized;
-struct ldb_dn *ldb_dn_explode_or_special(void *mem_ctx, const char *dn)
-{
- struct ldb_dn *edn; /* the exploded dn */
+ for (i = 0; i < dn->comp_num; i++) {
- if (dn == NULL) return NULL;
+ /* copy the name */
+ n = dn->components[i].name;
+ while (*n) *d++ = *n++;
- if (strncasecmp(dn, "<GUID=", 6) == 0) {
- /* this is special DN returned when the
- * exploded_dn control is used
- */
+ *d++ = '=';
- /* Allocate a structure to hold the exploded DN */
- edn = ldb_dn_new(mem_ctx);
+ /* and the value */
+ d += ldb_dn_escape_internal( d,
+ (char *)dn->components[i].value.data,
+ dn->components[i].value.length);
+ *d++ = ',';
+ }
- 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;
+ *(--d) = '\0';
- }
-
- return ldb_dn_explode(mem_ctx, dn);
+ /* don't waste more memory than necessary */
+ dn->linearized = talloc_realloc(dn, dn->linearized,
+ char, (d - dn->linearized + 1));
-failed:
- talloc_free(edn);
- return NULL;
+ return dn->linearized;
}
-char *ldb_dn_linearize(void *mem_ctx, const struct ldb_dn *edn)
+char *ldb_dn_get_extended_linearized(void *mem_ctx, struct ldb_dn *dn, int mode)
{
- char *dn, *value;
+ const char *linearized = ldb_dn_get_linearized(dn);
+ char *p;
int i;
- if (edn == NULL) return NULL;
+ if (!linearized) {
+ return NULL;
+ }
- /* Special DNs */
- if (ldb_dn_is_special(edn)) {
- dn = talloc_strdup(mem_ctx, (char *)edn->components[0].value.data);
- return dn;
+ if (!ldb_dn_has_extended(dn)) {
+ return talloc_strdup(mem_ctx, linearized);
}
- dn = talloc_strdup(mem_ctx, "");
- LDB_DN_NULL_FAILED(dn);
+ if (!ldb_dn_validate(dn)) {
+ return NULL;
+ }
- for (i = 0; i < edn->comp_num; i++) {
- value = ldb_dn_escape_value(dn, edn->components[i].value);
- LDB_DN_NULL_FAILED(value);
+ for (i = 0; i < dn->ext_comp_num; i++) {
+ const struct ldb_dn_extended_syntax *ext_syntax;
+ const char *name = dn->ext_components[i].name;
+ struct ldb_val ec_val = dn->ext_components[i].value;
+ struct ldb_val val;
+ int ret;
- 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);
+ ext_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, name);
+ if (!ext_syntax) {
+ return NULL;
}
- LDB_DN_NULL_FAILED(dn);
-
- talloc_free(value);
- }
- return dn;
+ if (mode == 1) {
+ ret = ext_syntax->write_clear_fn(dn->ldb, mem_ctx,
+ &ec_val, &val);
+ } else if (mode == 0) {
+ ret = ext_syntax->write_hex_fn(dn->ldb, mem_ctx,
+ &ec_val, &val);
+ } else {
+ ret = -1;
+ }
-failed:
- talloc_free(dn);
- return NULL;
-}
+ if (ret != LDB_SUCCESS) {
+ return NULL;
+ }
-/* compare DNs using casefolding compare functions */
+ if (i == 0) {
+ p = talloc_asprintf(mem_ctx, "<%s=%s>",
+ name, val.data);
+ } else {
+ p = talloc_asprintf_append_buffer(p, ";<%s=%s>",
+ name, val.data);
+ }
-int ldb_dn_compare_base(struct ldb_context *ldb,
- const struct ldb_dn *base,
- const struct ldb_dn *dn)
-{
- int ret;
- int n0, n1;
+ talloc_free(val.data);
- if (base->comp_num > dn->comp_num) {
- return (dn->comp_num - base->comp_num);
+ if (!p) {
+ return NULL;
+ }
}
- if (base == NULL || base->comp_num == 0) return 0;
- if (dn == NULL || dn->comp_num == 0) return -1;
- if (base->comp_num > dn->comp_num) return -1;
-
- /* if the number of components doesn't match they differ */
- 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_caseless_cmp(base->components[n0].name,
- dn->components[n1].name);
- if (ret) {
- return ret;
- }
+ if (dn->ext_comp_num && *linearized) {
+ p = talloc_asprintf_append_buffer(p, ";%s", linearized);
+ }
- /* 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--;
+ if (!p) {
+ return NULL;
}
- return 0;
+ return p;
}
-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)
+char *ldb_dn_alloc_linearized(void *mem_ctx, struct ldb_dn *dn)
{
- 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;
+ return talloc_strdup(mem_ctx, ldb_dn_get_linearized(dn));
}
/*
- casefold a dn. We need to casefold the attribute names, and canonicalize
+ 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;
+static bool ldb_dn_casefold_internal(struct ldb_dn *dn)
+{
+ int i, ret;
- cedn = ldb_dn_new(ldb);
- LDB_DN_NULL_FAILED(cedn);
+ if ( ! dn || dn->invalid) return false;
- cedn->comp_num = edn->comp_num;
- cedn->components = talloc_array(cedn, struct ldb_dn_component, edn->comp_num);
- LDB_DN_NULL_FAILED(cedn->components);
+ if (dn->valid_case) return true;
- for (i = 0; i < edn->comp_num; i++) {
- struct ldb_dn_component dc;
- const struct ldb_attrib_handler *h;
+ if (( ! dn->components) && ( ! ldb_dn_explode(dn))) {
+ return false;
+ }
- dc.name = ldb_casefold(cedn, edn->components[i].name);
- LDB_DN_NULL_FAILED(dc.name);
+ for (i = 0; i < dn->comp_num; i++) {
+ const struct ldb_schema_attribute *a;
- h = ldb_attrib_handler(ldb, dc.name);
- if (h->canonicalise_fn(ldb, cedn, &(edn->components[i].value), &(dc.value)) != 0) {
+ dn->components[i].cf_name =
+ ldb_attr_casefold(dn->components,
+ dn->components[i].name);
+ if (!dn->components[i].cf_name) {
goto failed;
}
- cedn->components[i] = dc;
+ 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) {
+ goto failed;
+ }
}
- return cedn;
+ dn->valid_case = true;
+
+ return true;
failed:
- talloc_free(cedn);
- return NULL;
+ 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;
}
-struct ldb_dn *ldb_dn_explode_casefold(struct ldb_context *ldb, const char *dn)
+const char *ldb_dn_get_casefold(struct ldb_dn *dn)
{
- struct ldb_dn *edn, *cdn;
-
- if (dn == NULL) return NULL;
+ int i, len;
+ char *d, *n;
- edn = ldb_dn_explode(ldb, dn);
- if (edn == NULL) return NULL;
+ if (dn->casefold) return dn->casefold;
- cdn = ldb_dn_casefold(ldb, edn);
-
- talloc_free(edn);
- return cdn;
-}
+ if (dn->special) {
+ dn->casefold = talloc_strdup(dn, dn->linearized);
+ if (!dn->casefold) return NULL;
+ dn->valid_case = true;
+ return dn->casefold;
+ }
-char *ldb_dn_linearize_casefold(struct ldb_context *ldb, const struct ldb_dn *edn)
-{
- struct ldb_dn *cdn;
- char *dn;
+ if ( ! ldb_dn_casefold_internal(dn)) {
+ return NULL;
+ }
- if (edn == NULL) return NULL;
+ if (dn->comp_num == 0) {
+ dn->casefold = talloc_strdup(dn, "");
+ return dn->casefold;
+ }
- /* Special DNs */
- if (ldb_dn_is_special(edn)) {
- dn = talloc_strdup(ldb, (char *)edn->components[0].value.data);
- return dn;
+ /* calculate maximum possible length of DN */
+ for (len = 0, i = 0; i < dn->comp_num; i++) {
+ /* name len */
+ len += strlen(dn->components[i].cf_name);
+ /* max escaped data len */
+ len += (dn->components[i].cf_value.length * 3);
+ len += 2; /* '=' and ',' */
}
+ dn->casefold = talloc_array(dn, char, len);
+ if ( ! dn->casefold) return NULL;
- cdn = ldb_dn_casefold(ldb, edn);
- if (cdn == NULL) return NULL;
+ d = dn->casefold;
- dn = ldb_dn_linearize(ldb, cdn);
- if (dn == NULL) {
- talloc_free(cdn);
- return NULL;
- }
+ for (i = 0; i < dn->comp_num; i++) {
- talloc_free(cdn);
- return dn;
+ /* copy the name */
+ n = dn->components[i].cf_name;
+ while (*n) *d++ = *n++;
+
+ *d++ = '=';
+
+ /* and the value */
+ d += ldb_dn_escape_internal( d,
+ (char *)dn->components[i].cf_value.data,
+ dn->components[i].cf_value.length);
+ *d++ = ',';
+ }
+ *(--d) = '\0';
+
+ /* don't waste more memory than necessary */
+ dn->casefold = talloc_realloc(dn, dn->casefold,
+ char, strlen(dn->casefold) + 1);
+
+ return dn->casefold;
}
-static struct ldb_dn_component ldb_dn_copy_component(void *mem_ctx, struct ldb_dn_component *src)
+char *ldb_dn_alloc_casefold(void *mem_ctx, struct ldb_dn *dn)
+{
+ return talloc_strdup(mem_ctx, ldb_dn_get_casefold(dn));
+}
+
+/* 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_dn *base, struct ldb_dn *dn)
+{
+ int ret;
+ int n_base, n_dn;
+
+ if ( ! base || base->invalid) return 1;
+ if ( ! dn || dn->invalid) return -1;
+
+ if (( ! base->valid_case) || ( ! dn->valid_case)) {
+ if (base->linearized && dn->linearized) {
+ /* try with a normal compare first, if we are lucky
+ * we will avoid exploding and casfolding */
+ int dif;
+ dif = strlen(dn->linearized) - strlen(base->linearized);
+ if (dif < 0) {
+ return dif;
+ }
+ if (strcmp(base->linearized,
+ &dn->linearized[dif]) == 0) {
+ return 0;
+ }
+ }
+
+ if ( ! ldb_dn_casefold_internal(base)) {
+ return 1;
+ }
+
+ if ( ! ldb_dn_casefold_internal(dn)) {
+ return -1;
+ }
+
+ }
+
+ /* if base has more components,
+ * they don't have the same base */
+ if (base->comp_num > dn->comp_num) {
+ return (dn->comp_num - base->comp_num);
+ }
+
+ 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;
+ }
+ }
+
+ n_base = base->comp_num - 1;
+ n_dn = dn->comp_num - 1;
+
+ while (n_base >= 0) {
+ char *b_name = base->components[n_base].cf_name;
+ char *dn_name = dn->components[n_dn].cf_name;
+
+ char *b_vdata = (char *)base->components[n_base].cf_value.data;
+ char *dn_vdata = (char *)dn->components[n_dn].cf_value.data;
+
+ size_t b_vlen = base->components[n_base].cf_value.length;
+ size_t dn_vlen = dn->components[n_dn].cf_value.length;
+
+ /* compare attr names */
+ ret = strcmp(b_name, dn_name);
+ if (ret != 0) return ret;
+
+ /* compare attr.cf_value. */
+ if (b_vlen != dn_vlen) {
+ return b_vlen - dn_vlen;
+ }
+ ret = strcmp(b_vdata, dn_vdata);
+ if (ret != 0) return ret;
+
+ n_base--;
+ n_dn--;
+ }
+
+ return 0;
+}
+
+/* compare DNs using casefolding compare functions.
+
+ If they match, then return 0
+ */
+
+int ldb_dn_compare(struct ldb_dn *dn0, struct ldb_dn *dn1)
+{
+ int i, ret;
+
+ if (( ! dn0) || dn0->invalid || ! dn1 || dn1->invalid) {
+ return -1;
+ }
+
+ if (( ! dn0->valid_case) || ( ! dn1->valid_case)) {
+ 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;
+ }
+ }
+
+ if ( ! ldb_dn_casefold_internal(dn0)) {
+ return 1;
+ }
+
+ if ( ! ldb_dn_casefold_internal(dn1)) {
+ return -1;
+ }
+
+ }
+
+ if (dn0->comp_num != dn1->comp_num) {
+ return (dn1->comp_num - dn0->comp_num);
+ }
+
+ 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;
+ }
+ }
+
+ for (i = 0; i < dn0->comp_num; i++) {
+ char *dn0_name = dn0->components[i].cf_name;
+ char *dn1_name = dn1->components[i].cf_name;
+
+ char *dn0_vdata = (char *)dn0->components[i].cf_value.data;
+ char *dn1_vdata = (char *)dn1->components[i].cf_value.data;
+
+ size_t dn0_vlen = dn0->components[i].cf_value.length;
+ size_t dn1_vlen = dn1->components[i].cf_value.length;
+
+ /* compare attr names */
+ ret = strcmp(dn0_name, dn1_name);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* compare attr.cf_value. */
+ if (dn0_vlen != dn1_vlen) {
+ return dn0_vlen - dn1_vlen;
+ }
+ ret = strcmp(dn0_vdata, dn1_vdata);
+ if (ret != 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static struct ldb_dn_component ldb_dn_copy_component(
+ void *mem_ctx,
+ struct ldb_dn_component *src)
{
struct ldb_dn_component dst;
dst.name = talloc_strdup(mem_ctx, src->name);
if (dst.name == NULL) {
- talloc_free(dst.value.data);
+ LDB_FREE(dst.value.data);
+ return dst;
+ }
+
+ if (src->cf_value.data) {
+ dst.cf_value = ldb_val_dup(mem_ctx, &(src->cf_value));
+ if (dst.cf_value.data == NULL) {
+ LDB_FREE(dst.value.data);
+ LDB_FREE(dst.name);
+ return dst;
+ }
+
+ dst.cf_name = talloc_strdup(mem_ctx, src->cf_name);
+ if (dst.cf_name == NULL) {
+ LDB_FREE(dst.cf_name);
+ LDB_FREE(dst.value.data);
+ LDB_FREE(dst.name);
+ return dst;
+ }
+ } else {
+ dst.cf_value.data = NULL;
+ dst.cf_name = NULL;
}
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 then 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)
+static struct ldb_dn_ext_component ldb_dn_ext_copy_component(
+ void *mem_ctx,
+ struct ldb_dn_ext_component *src)
{
- struct ldb_dn *new;
- int i, n, e;
-
- if (dn == NULL) return NULL;
- if (num_el <= 0) return NULL;
+ struct ldb_dn_ext_component dst;
- new = ldb_dn_new(mem_ctx);
- LDB_DN_NULL_FAILED(new);
-
- new->comp_num = num_el;
- n = new->comp_num - 1;
- new->components = talloc_array(new, struct ldb_dn_component, new->comp_num);
+ memset(&dst, 0, sizeof(dst));
- if (dn->comp_num == 0) return new;
- e = dn->comp_num - 1;
+ if (src == NULL) {
+ return dst;
+ }
- for (i = 0; i < new->comp_num; i++) {
- new->components[n - i] = ldb_dn_copy_component(new->components,
- &(dn->components[e - i]));
- if ((e - i) == 0) {
- return new;
- }
+ dst.value = ldb_val_dup(mem_ctx, &(src->value));
+ if (dst.value.data == NULL) {
+ return dst;
}
- return new;
+ dst.name = talloc_strdup(mem_ctx, src->name);
+ if (dst.name == NULL) {
+ LDB_FREE(dst.value.data);
+ return dst;
+ }
-failed:
- talloc_free(new);
- return NULL;
+ return dst;
}
-struct ldb_dn *ldb_dn_copy(void *mem_ctx, const struct ldb_dn *dn)
+struct ldb_dn *ldb_dn_copy(void *mem_ctx, struct ldb_dn *dn)
{
- if (dn == NULL) return NULL;
- return ldb_dn_copy_partial(mem_ctx, dn, dn->comp_num);
-}
+ struct ldb_dn *new_dn;
-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);
-}
+ if (!dn || dn->invalid) {
+ return NULL;
+ }
-struct ldb_dn_component *ldb_dn_build_component(void *mem_ctx, const char *attr,
- const char *val)
-{
- struct ldb_dn_component *dc;
+ new_dn = talloc_zero(mem_ctx, struct ldb_dn);
+ if ( !new_dn) {
+ return NULL;
+ }
- if (attr == NULL || val == NULL) return NULL;
+ *new_dn = *dn;
- dc = talloc(mem_ctx, struct ldb_dn_component);
- if (dc == NULL) return NULL;
+ if (dn->components) {
+ int i;
- dc->name = talloc_strdup(dc, attr);
- if (dc->name == NULL) {
- talloc_free(dc);
- return NULL;
+ new_dn->components =
+ talloc_zero_array(new_dn,
+ struct ldb_dn_component,
+ dn->comp_num);
+ if ( ! new_dn->components) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ for (i = 0; i < dn->comp_num; i++) {
+ new_dn->components[i] =
+ ldb_dn_copy_component(new_dn->components,
+ &dn->components[i]);
+ if ( ! new_dn->components[i].value.data) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+ }
}
- dc->value.data = (uint8_t *)talloc_strdup(dc, val);
- if (dc->value.data == NULL) {
- talloc_free(dc);
- return NULL;
+ if (dn->ext_components) {
+ int i;
+
+ new_dn->ext_components =
+ talloc_zero_array(new_dn,
+ struct ldb_dn_ext_component,
+ dn->ext_comp_num);
+ if ( ! new_dn->ext_components) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ for (i = 0; i < dn->ext_comp_num; i++) {
+ new_dn->ext_components[i] =
+ ldb_dn_ext_copy_component(
+ new_dn->ext_components,
+ &dn->ext_components[i]);
+ if ( ! new_dn->ext_components[i].value.data) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+ }
}
- dc->value.length = strlen(val);
+ if (dn->casefold) {
+ new_dn->casefold = talloc_strdup(new_dn, dn->casefold);
+ if ( ! new_dn->casefold) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+ }
- return dc;
+ if (dn->linearized) {
+ new_dn->linearized = talloc_strdup(new_dn, dn->linearized);
+ if ( ! new_dn->linearized) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+ }
+
+ if (dn->ext_linearized) {
+ new_dn->ext_linearized = talloc_strdup(new_dn,
+ dn->ext_linearized);
+ if ( ! new_dn->ext_linearized) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+ }
+
+ return new_dn;
}
-struct ldb_dn *ldb_dn_build_child(void *mem_ctx, const char *attr,
- const char * value,
- const struct ldb_dn *base)
+/* modify the given dn by adding a base.
+ *
+ * return true if successful and false if not
+ * if false is returned the dn may be marked invalid
+ */
+bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base)
{
- struct ldb_dn *new;
- if (! ldb_dn_is_valid_attribute_name(attr)) return NULL;
- if (value == NULL || value == '\0') return NULL;
+ const char *s;
+ char *t;
- if (base != NULL) {
- new = ldb_dn_copy_partial(mem_ctx, base, base->comp_num + 1);
- LDB_DN_NULL_FAILED(new);
- } else {
- new = ldb_dn_new(mem_ctx);
- LDB_DN_NULL_FAILED(new);
+ if ( !base || base->invalid || !dn || dn->invalid) {
+ return false;
+ }
+
+ if (dn->components) {
+ int i;
+
+ if ( ! ldb_dn_validate(base)) {
+ return false;
+ }
+
+ s = NULL;
+ if (dn->valid_case) {
+ if ( ! (s = ldb_dn_get_casefold(base))) {
+ return false;
+ }
+ }
+
+ dn->components = talloc_realloc(dn,
+ dn->components,
+ struct ldb_dn_component,
+ dn->comp_num + base->comp_num);
+ if ( ! dn->components) {
+ ldb_dn_mark_invalid(dn);
+ return false;
+ }
+
+ for (i = 0; i < base->comp_num; dn->comp_num++, i++) {
+ dn->components[dn->comp_num] =
+ ldb_dn_copy_component(dn->components,
+ &base->components[i]);
+ if (dn->components[dn->comp_num].value.data == NULL) {
+ ldb_dn_mark_invalid(dn);
+ return false;
+ }
+ }
- new->comp_num = 1;
- new->components = talloc_array(new, struct ldb_dn_component, new->comp_num);
+ 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;
+ }
}
- new->components[0].name = talloc_strdup(new->components, attr);
- LDB_DN_NULL_FAILED(new->components[0].name);
+ if (dn->linearized) {
- new->components[0].value.data = (uint8_t *)talloc_strdup(new->components, value);
- LDB_DN_NULL_FAILED(new->components[0].value.data);
- new->components[0].value.length = strlen((char *)new->components[0].value.data);
+ s = ldb_dn_get_linearized(base);
+ if ( ! s) {
+ return false;
+ }
- return new;
+ if (*dn->linearized) {
+ t = talloc_asprintf(dn, "%s,%s",
+ dn->linearized, s);
+ } else {
+ t = talloc_strdup(dn, s);
+ }
+ if ( ! t) {
+ ldb_dn_mark_invalid(dn);
+ return false;
+ }
+ LDB_FREE(dn->linearized);
+ dn->linearized = t;
+ }
-failed:
- talloc_free(new);
- return NULL;
+ /* 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_components);
+ dn->ext_comp_num = 0;
+ return true;
}
-struct ldb_dn *ldb_dn_make_child(void *mem_ctx, const struct ldb_dn_component *component,
- const struct ldb_dn *base)
+/* modify the given dn by adding a base.
+ *
+ * return true if successful and false if not
+ * if false is returned the dn may be marked invalid
+ */
+bool ldb_dn_add_base_fmt(struct ldb_dn *dn, const char *base_fmt, ...)
{
- if (component == NULL) return NULL;
+ struct ldb_dn *base;
+ char *base_str;
+ va_list ap;
+ bool ret;
+
+ if ( !dn || dn->invalid) {
+ return false;
+ }
+
+ va_start(ap, base_fmt);
+ base_str = talloc_vasprintf(dn, base_fmt, ap);
+ va_end(ap);
+
+ if (base_str == NULL) {
+ return false;
+ }
+
+ base = ldb_dn_new(base_str, dn->ldb, base_str);
+
+ ret = ldb_dn_add_base(dn, base);
- return ldb_dn_build_child(mem_ctx, component->name,
- (char *)component->value.data, base);
+ talloc_free(base_str);
+
+ return ret;
}
-struct ldb_dn *ldb_dn_compose(void *mem_ctx, const struct ldb_dn *dn1, const struct ldb_dn *dn2)
+/* modify the given dn by adding children elements.
+ *
+ * return true if successful and false if not
+ * if false is returned the dn may be marked invalid
+ */
+bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child)
{
- int i;
- struct ldb_dn *new;
+ const char *s;
+ char *t;
- if (dn2 == NULL && dn1 == NULL) {
- return NULL;
+ if ( !child || child->invalid || !dn || dn->invalid) {
+ return false;
}
- if (dn2 == NULL) {
- new = ldb_dn_new(mem_ctx);
- LDB_DN_NULL_FAILED(new);
+ if (dn->components) {
+ int n, i, j;
- new->comp_num = dn1->comp_num;
- new->components = talloc_array(new, struct ldb_dn_component, new->comp_num);
- } else {
- int comp_num = dn2->comp_num;
- if (dn1 != NULL) comp_num += dn1->comp_num;
- new = ldb_dn_copy_partial(mem_ctx, dn2, comp_num);
- }
+ if ( ! ldb_dn_validate(child)) {
+ return false;
+ }
+
+ s = NULL;
+ if (dn->valid_case) {
+ if ( ! (s = ldb_dn_get_casefold(child))) {
+ return false;
+ }
+ }
+
+ n = dn->comp_num + child->comp_num;
+
+ dn->components = talloc_realloc(dn,
+ dn->components,
+ struct ldb_dn_component,
+ n);
+ if ( ! dn->components) {
+ ldb_dn_mark_invalid(dn);
+ return false;
+ }
+
+ for (i = dn->comp_num - 1, j = n - 1; i >= 0; i--, j--) {
+ dn->components[j] = dn->components[i];
+ }
- if (dn1 == NULL) {
- return new;
+ for (i = 0; i < child->comp_num; i++) {
+ dn->components[i] =
+ ldb_dn_copy_component(dn->components,
+ &child->components[i]);
+ if (dn->components[i].value.data == NULL) {
+ ldb_dn_mark_invalid(dn);
+ return false;
+ }
+ }
+
+ dn->comp_num = n;
+
+ if (dn->casefold && s) {
+ t = talloc_asprintf(dn, "%s,%s", s, dn->casefold);
+ LDB_FREE(dn->casefold);
+ dn->casefold = t;
+ }
}
- for (i = 0; i < dn1->comp_num; i++) {
- new->components[i] = ldb_dn_copy_component(new->components,
- &(dn1->components[i]));
+ if (dn->linearized) {
+
+ s = ldb_dn_get_linearized(child);
+ if ( ! s) {
+ return false;
+ }
+
+ t = talloc_asprintf(dn, "%s,%s", s, dn->linearized);
+ if ( ! t) {
+ ldb_dn_mark_invalid(dn);
+ return false;
+ }
+ LDB_FREE(dn->linearized);
+ dn->linearized = t;
}
- return new;
+ /* Wipe the ext_linearized DN,
+ * the GUID and SID are almost certainly no longer valid */
+ LDB_FREE(dn->ext_linearized);
-failed:
- talloc_free(new);
- return NULL;
+ LDB_FREE(dn->ext_components);
+ dn->ext_comp_num = 0;
+
+ return true;
}
-struct ldb_dn *ldb_dn_string_compose(void *mem_ctx, const struct ldb_dn *base, const char *child_fmt, ...)
+/* modify the given dn by adding children elements.
+ *
+ * return true if successful and false if not
+ * if false is returned the dn may be marked invalid
+ */
+bool ldb_dn_add_child_fmt(struct ldb_dn *dn, const char *child_fmt, ...)
{
- struct ldb_dn *dn;
+ struct ldb_dn *child;
char *child_str;
va_list ap;
- int ret;
-
- if (child_fmt == NULL) return NULL;
+ bool ret;
+
+ if ( !dn || dn->invalid) {
+ return false;
+ }
va_start(ap, child_fmt);
- ret = vasprintf(&child_str, child_fmt, ap);
+ child_str = talloc_vasprintf(dn, child_fmt, ap);
va_end(ap);
- if (ret <= 0) return NULL;
+ if (child_str == NULL) {
+ return false;
+ }
+
+ child = ldb_dn_new(child_str, dn->ldb, child_str);
- dn = ldb_dn_compose(mem_ctx, ldb_dn_explode(mem_ctx, child_str), base);
+ ret = ldb_dn_add_child(dn, child);
- free(child_str);
+ talloc_free(child_str);
- return dn;
+ return ret;
}
-struct ldb_dn_component *ldb_dn_get_rdn(void *mem_ctx, const struct ldb_dn *dn)
+bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num)
{
- struct ldb_dn_component *rdn;
+ int i;
- if (dn == NULL) return NULL;
+ if ( ! ldb_dn_validate(dn)) {
+ return false;
+ }
- if (dn->comp_num < 1) {
- return NULL;
+ if (dn->comp_num < 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);
}
- rdn = talloc(mem_ctx, struct ldb_dn_component);
- if (rdn == NULL) return NULL;
+ dn->comp_num -= num;
- *rdn = ldb_dn_copy_component(mem_ctx, &dn->components[0]);
- if (rdn->name == NULL) {
- talloc_free(rdn);
+ 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 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;
+}
+
+bool ldb_dn_remove_child_components(struct ldb_dn *dn, unsigned int num)
+{
+ int i, j;
+
+ if ( ! ldb_dn_validate(dn)) {
+ return false;
+ }
+
+ if (dn->comp_num < num) {
+ return false;
+ }
+
+ 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;
+
+ 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 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;
+}
+
+struct ldb_dn *ldb_dn_get_parent(void *mem_ctx, struct ldb_dn *dn)
+{
+ struct ldb_dn *new_dn;
+
+ new_dn = ldb_dn_copy(mem_ctx, dn);
+ if ( !new_dn ) {
return NULL;
}
- return rdn;
+ if ( ! ldb_dn_remove_child_components(new_dn, 1)) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ /* 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;
}
/* 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).
+ 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) {
+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--) {
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;
}
/* Wrapper functions for the above, for the two different string formats */
-char *ldb_dn_canonical_string(void *mem_ctx, const struct ldb_dn *dn) {
+char *ldb_dn_canonical_string(void *mem_ctx, 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) {
+char *ldb_dn_canonical_ex_string(void *mem_ctx, struct ldb_dn *dn) {
return ldb_dn_canonical(mem_ctx, dn, 1);
}
+
+int ldb_dn_get_comp_num(struct ldb_dn *dn)
+{
+ if ( ! ldb_dn_validate(dn)) {
+ return -1;
+ }
+ return dn->comp_num;
+}
+
+const char *ldb_dn_get_component_name(struct ldb_dn *dn, unsigned int num)
+{
+ if ( ! ldb_dn_validate(dn)) {
+ return NULL;
+ }
+ if (num >= dn->comp_num) return NULL;
+ return dn->components[num].name;
+}
+
+const struct ldb_val *ldb_dn_get_component_val(struct ldb_dn *dn,
+ unsigned int num)
+{
+ if ( ! ldb_dn_validate(dn)) {
+ return NULL;
+ }
+ if (num >= dn->comp_num) return NULL;
+ return &dn->components[num].value;
+}
+
+const char *ldb_dn_get_rdn_name(struct ldb_dn *dn)
+{
+ if ( ! ldb_dn_validate(dn)) {
+ return NULL;
+ }
+ if (dn->comp_num == 0) return NULL;
+ return dn->components[0].name;
+}
+
+const struct ldb_val *ldb_dn_get_rdn_val(struct ldb_dn *dn)
+{
+ if ( ! ldb_dn_validate(dn)) {
+ return NULL;
+ }
+ if (dn->comp_num == 0) return NULL;
+ return &dn->components[0].value;
+}
+
+int ldb_dn_set_component(struct ldb_dn *dn, int num,
+ const char *name, const struct ldb_val val)
+{
+ char *n;
+ struct ldb_val v;
+
+ if ( ! ldb_dn_validate(dn)) {
+ return LDB_ERR_OTHER;
+ }
+
+ if (num >= dn->comp_num) {
+ return LDB_ERR_OTHER;
+ }
+
+ n = talloc_strdup(dn, name);
+ if ( ! n) {
+ return LDB_ERR_OTHER;
+ }
+
+ 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;
+ }
+
+ talloc_free(dn->components[num].name);
+ talloc_free(dn->components[num].value.data);
+ dn->components[num].name = n;
+ dn->components[num].value = v;
+
+ 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 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);
+ 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->ext_comp_num; i++) {
+ if (ldb_attr_cmp(dn->ext_components[i].name, name) == 0) {
+ return &dn->ext_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_ext_component *p;
+ int i;
+
+ 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) {
+ dn->ext_components[i].value =
+ ldb_val_dup(dn->ext_components, val);
+
+ dn->ext_components[i].name =
+ talloc_strdup(dn->ext_components, name);
+ if (!dn->ext_components[i].name ||
+ !dn->ext_components[i].value.data) {
+ ldb_dn_mark_invalid(dn);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ } else {
+ if (i != (dn->ext_comp_num - 1)) {
+ memmove(&dn->ext_components[i],
+ &dn->ext_components[i+1],
+ ((dn->ext_comp_num-1) - i) *
+ sizeof(*dn->ext_components));
+ }
+ dn->ext_comp_num--;
+
+ dn->ext_components = talloc_realloc(dn,
+ dn->ext_components,
+ struct ldb_dn_ext_component,
+ dn->ext_comp_num);
+ if (!dn->ext_components) {
+ ldb_dn_mark_invalid(dn);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return LDB_SUCCESS;
+ }
+ }
+ }
+
+ p = dn->ext_components
+ = talloc_realloc(dn,
+ dn->ext_components,
+ struct ldb_dn_ext_component,
+ dn->ext_comp_num + 1);
+ if (!dn->ext_components) {
+ ldb_dn_mark_invalid(dn);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ p[dn->ext_comp_num].value = ldb_val_dup(dn->ext_components, val);
+ p[dn->ext_comp_num].name = talloc_strdup(p, name);
+
+ if (!dn->ext_components[i].name || !dn->ext_components[i].value.data) {
+ ldb_dn_mark_invalid(dn);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ dn->ext_components = p;
+ dn->ext_comp_num++;
+
+ return LDB_SUCCESS;
+}
+
+void ldb_dn_remove_extended_components(struct ldb_dn *dn)
+{
+ dn->ext_comp_num = 0;
+ LDB_FREE(dn->ext_components);
+}
+
+bool ldb_dn_is_valid(struct ldb_dn *dn)
+{
+ if ( ! dn) return false;
+ return ! dn->invalid;
+}
+
+bool ldb_dn_is_special(struct ldb_dn *dn)
+{
+ if ( ! dn || dn->invalid) return false;
+ return dn->special;
+}
+
+bool ldb_dn_has_extended(struct ldb_dn *dn)
+{
+ if ( ! dn || dn->invalid) return false;
+ if (dn->ext_linearized && (dn->ext_linearized[0] == '<')) return true;
+ return dn->ext_comp_num != 0;
+}
+
+bool ldb_dn_check_special(struct ldb_dn *dn, const char *check)
+{
+ if ( ! dn || dn->invalid) return false;
+ return ! strcmp(dn->linearized, check);
+}
+
+bool ldb_dn_is_null(struct ldb_dn *dn)
+{
+ if ( ! dn || dn->invalid) return false;
+ if (ldb_dn_has_extended(dn)) return false;
+ if (dn->linearized && (dn->linearized[0] == '\0')) return true;
+ return false;
+}
+