diff options
Diffstat (limited to 'lib/ldb/common/ldb_dn.c')
-rw-r--r-- | lib/ldb/common/ldb_dn.c | 2238 |
1 files changed, 2238 insertions, 0 deletions
diff --git a/lib/ldb/common/ldb_dn.c b/lib/ldb/common/ldb_dn.c new file mode 100644 index 0000000..fb7a95b --- /dev/null +++ b/lib/ldb/common/ldb_dn.c @@ -0,0 +1,2238 @@ +/* + 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 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 + 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb dn creation and manipulation utility functions + * + * Description: - explode a dn into it's own basic elements + * and put them in a structure (only if necessary) + * - manipulate ldb_dn structures + * + * Author: Simo Sorce + */ + +#include "ldb_private.h" +#include <ctype.h> + +#define LDB_DN_NULL_FAILED(x) if (!(x)) goto failed + +#define LDB_FREE(x) do { talloc_free(x); x = NULL; } while(0) + +/** + internal ldb exploded dn structures +*/ +struct ldb_dn_component { + + char *name; + struct ldb_val value; + + char *cf_name; + struct ldb_val cf_value; +}; + +struct ldb_dn_ext_component { + + const 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; + + 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; +} + +/* strdn may be NULL */ +struct ldb_dn *ldb_dn_from_ldb_val(TALLOC_CTX *mem_ctx, + struct ldb_context *ldb, + const struct ldb_val *strdn) +{ + struct ldb_dn *dn; + + if (ldb == NULL || strdn == NULL) { + return NULL; + } + if (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 = talloc_get_type(ldb, struct ldb_context); + if (dn->ldb == NULL) { + /* the caller probably got the arguments to + ldb_dn_new() mixed up */ + talloc_free(dn); + return NULL; + } + + if (strdn->data && strdn->length) { + const char *data = (const char *)strdn->data; + size_t length = strdn->length; + + if (data[0] == '@') { + dn->special = true; + } + 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; + } + } else { + dn->linearized = talloc_strdup(dn, ""); + LDB_DN_NULL_FAILED(dn->linearized); + } + + return dn; + +failed: + talloc_free(dn); + return NULL; +} + +/* strdn may be NULL */ +struct ldb_dn *ldb_dn_new(TALLOC_CTX *mem_ctx, + struct ldb_context *ldb, + const char *strdn) +{ + 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); +} + +struct ldb_dn *ldb_dn_new_fmt(TALLOC_CTX *mem_ctx, + struct ldb_context *ldb, + const char *new_fmt, ...) +{ + char *strdn; + va_list ap; + + if (! ldb) return NULL; + + va_start(ap, new_fmt); + strdn = talloc_vasprintf(mem_ctx, new_fmt, ap); + va_end(ap); + + if (strdn) { + struct ldb_dn *dn = ldb_dn_new(mem_ctx, ldb, strdn); + talloc_free(strdn); + return dn; + } + + return NULL; +} + +/* see RFC2253 section 2.4 */ +static int ldb_dn_escape_internal(char *dst, const char *src, int len) +{ + char c; + char *d; + int i; + d = dst; + + for (i = 0; i < len; i++){ + c = src[i]; + switch (c) { + case ' ': + if (i == 0 || i == len - 1) { + /* if at the beginning or end + * of the string then escape */ + *d++ = '\\'; + *d++ = c; + } else { + /* otherwise don't escape */ + *d++ = c; + } + break; + + 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++ = c; + break; + + case ';': + case '\r': + case '\n': + case '=': + case '\0': { + /* any others get \XX form */ + unsigned char v; + const char *hexbytes = "0123456789ABCDEF"; + v = (const unsigned char)c; + *d++ = '\\'; + *d++ = hexbytes[v>>4]; + *d++ = hexbytes[v&0xF]; + break; + } + default: + *d++ = c; + } + } + + /* return the length of the resulting string */ + return (d - dst); +} + +char *ldb_dn_escape_value(TALLOC_CTX *mem_ctx, struct ldb_val value) +{ + char *dst; + size_t len; + if (!value.length) + return NULL; + + /* 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; + } + + len = ldb_dn_escape_internal(dst, (const char *)value.data, value.length); + + dst = talloc_realloc(mem_ctx, dst, char, len + 1); + if ( ! dst) { + talloc_free(dst); + return NULL; + } + dst[len] = '\0'; + return dst; +} + +/* + explode a DN string into a ldb_dn structure + based on RFC4514 except that we don't support multiple valued RDNs + + 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 = NULL, *ex_value = NULL, *data, *d, *dt, *t; + bool trim = true; + bool in_extended = true; + 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 int x; + size_t l = 0; + int ret; + char *parse_dn; + bool is_index; + + if (dn == NULL || dn->invalid) { + return false; + } + + if (dn->components != NULL) { + return true; + } + + if (dn->ext_linearized != NULL) { + parse_dn = dn->ext_linearized; + } else { + parse_dn = dn->linearized; + } + + if (parse_dn == NULL) { + return false; + } + + is_index = (strncmp(parse_dn, "DN=@INDEX:", 10) == 0); + + /* Empty DNs */ + if (parse_dn[0] == '\0') { + return true; + } + + /* Special DNs case */ + if (dn->special) { + return true; + } + + LDB_FREE(dn->ext_components); + dn->ext_comp_num = 0; + dn->comp_num = 0; + + /* 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 == NULL) { + return false; + } + + /* Components data space is allocated here once */ + data = talloc_array(dn->components, char, strlen(parse_dn) + 1); + if (data == NULL) { + goto failed; + } + + p = parse_dn; + 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 { + 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 == '>') { + struct ldb_dn_ext_component *ext_comp = NULL; + const struct ldb_dn_extended_syntax *ext_syntax; + struct ldb_val ex_val = { + .data = (uint8_t *)ex_value, + .length = d - ex_value + }; + + *d++ = '\0'; + p++; + in_ex_value = false; + + /* Process name and ex_value */ + + ext_comp = talloc_realloc( + dn, + dn->ext_components, + struct ldb_dn_ext_component, + dn->ext_comp_num + 1); + + if (ext_comp == NULL) { + /* ouch ! */ + goto failed; + } + + dn->ext_components = ext_comp; + + ext_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, ex_name); + if (ext_syntax == NULL) { + /* We don't know about this type of extended DN */ + goto failed; + } + + dn->ext_components[dn->ext_comp_num].name = ext_syntax->name; + 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; + } + } + + *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; + } + + if (*p == ' ') { + p++; + /* valid only if we are at the end */ + trim = true; + continue; + } + + 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 == NULL) { + /* ouch */ + goto failed; + } + + dt = d; + + p++; + continue; + } + + if (!isascii(*p)) { + /* attr names must be ascii only */ + ldb_dn_mark_invalid(dn); + goto failed; + } + + 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; + } + + *d++ = *p++; + continue; + } + + if (in_value) { + if (in_quote) { + if (*p == '\"') { + if (p[-1] != '\\') { + p++; + in_quote = false; + continue; + } + } + *d++ = *p++; + l++; + continue; + } + + if (trim) { + if (*p == ' ') { + p++; + continue; + } + + /* first char */ + trim = false; + + if (*p == '\"') { + in_quote = true; + p++; + continue; + } + } + + switch (*p) { + + /* TODO: support ber encoded values + case '#': + */ + + case ',': + if (escape) { + *d++ = *p++; + l++; + escape = false; + continue; + } + /* ok found value terminator */ + + if (t != NULL) { + /* trim back */ + d -= (p - t); + l -= (p - t); + t = NULL; + } + + in_attr = true; + in_value = false; + trim = true; + + p++; + *d++ = '\0'; + + /* + * This talloc_memdup() is OK with the + * +1 because *d has been set to '\0' + * just above + */ + dn->components[dn->comp_num].value.data = \ + (uint8_t *)talloc_memdup(dn->components, dt, l + 1); + dn->components[dn->comp_num].value.length = l; + if (dn->components[dn->comp_num].value.data == NULL) { + /* ouch ! */ + goto failed; + } + talloc_set_name_const(dn->components[dn->comp_num].value.data, + (const char *)dn->components[dn->comp_num].value.data); + + 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 == NULL) { + /* 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)); + } + + continue; + + case '+': + case '=': + /* to main compatibility with earlier + versions of ldb indexing, we have to + accept the base64 encoded binary index + values, which contain a '+' or '=' + which should normally be escaped */ + if (is_index) { + if (t != NULL) { + 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 != NULL) { + t = NULL; + } + break; + + case '\\': + if (!escape) { + escape = true; + p++; + continue; + } + escape = false; + + *d++ = *p++; + l++; + + if (t != NULL) { + 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 != NULL) { + t = NULL; + } + break; + } + + if (*p == ' ') { + if (t == NULL) { + t = p; + } + } else { + if (t != NULL) { + t = NULL; + } + } + + *d++ = *p++; + l++; + + break; + } + + } + } + + if (in_attr || in_quote) { + /* invalid dn */ + ldb_dn_mark_invalid(dn); + goto failed; + } + + if (in_value) { + /* save last element */ + if (t != NULL) { + /* trim back */ + d -= (p - t); + l -= (p - t); + } + + *d++ = '\0'; + /* + * This talloc_memdup() is OK with the + * +1 because *d has been set to '\0' + * just above. + */ + dn->components[dn->comp_num].value.length = l; + dn->components[dn->comp_num].value.data = + (uint8_t *)talloc_memdup(dn->components, dt, l + 1); + if (dn->components[dn->comp_num].value.data == NULL) { + /* ouch */ + goto failed; + } + talloc_set_name_const(dn->components[dn->comp_num].value.data, + (const char *)dn->components[dn->comp_num].value.data); + + dn->comp_num++; + } + talloc_free(data); + return true; + +failed: + LDB_FREE(dn->components); /* "data" is implicitly free'd */ + dn->comp_num = 0; + LDB_FREE(dn->ext_components); + dn->ext_comp_num = 0; + + return false; +} + +bool ldb_dn_validate(struct ldb_dn *dn) +{ + return ldb_dn_explode(dn); +} + +const char *ldb_dn_get_linearized(struct ldb_dn *dn) +{ + unsigned int i; + size_t len; + char *d, *n; + + if ( ! dn || ( dn->invalid)) return NULL; + + if (dn->linearized) return dn->linearized; + + if ( ! dn->components) { + ldb_dn_mark_invalid(dn); + return NULL; + } + + if (dn->comp_num == 0) { + dn->linearized = talloc_strdup(dn, ""); + if ( ! dn->linearized) return NULL; + return dn->linearized; + } + + /* 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; + + d = dn->linearized; + + for (i = 0; i < dn->comp_num; i++) { + + /* copy the name */ + n = dn->components[i].name; + while (*n) *d++ = *n++; + + *d++ = '='; + + /* and the value */ + d += ldb_dn_escape_internal( d, + (char *)dn->components[i].value.data, + dn->components[i].value.length); + *d++ = ','; + } + + *(--d) = '\0'; + + /* don't waste more memory than necessary */ + dn->linearized = talloc_realloc(dn, dn->linearized, + char, (d - dn->linearized + 1)); + + return dn->linearized; +} + +static int ldb_dn_extended_component_compare(const void *p1, const void *p2) +{ + const struct ldb_dn_ext_component *ec1 = (const struct ldb_dn_ext_component *)p1; + const struct ldb_dn_ext_component *ec2 = (const struct ldb_dn_ext_component *)p2; + return strcmp(ec1->name, ec2->name); +} + +char *ldb_dn_get_extended_linearized(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, int mode) +{ + const char *linearized = ldb_dn_get_linearized(dn); + char *p = NULL; + unsigned 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; + } + + /* sort the extended components by name. The idea is to make + * the resulting DNs consistent, plus to ensure that we put + * 'DELETED' first, so it can be very quickly recognised + */ + TYPESAFE_QSORT(dn->ext_components, dn->ext_comp_num, + ldb_dn_extended_component_compare); + + for (i = 0; i < dn->ext_comp_num; i++) { + const struct ldb_dn_extended_syntax *ext_syntax; + const char *name = dn->ext_components[i].name; + struct ldb_val ec_val = dn->ext_components[i].value; + struct ldb_val val; + int ret; + + ext_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, name); + if (!ext_syntax) { + return NULL; + } + + if (mode == 1) { + ret = ext_syntax->write_clear_fn(dn->ldb, mem_ctx, + &ec_val, &val); + } else if (mode == 0) { + ret = ext_syntax->write_hex_fn(dn->ldb, mem_ctx, + &ec_val, &val); + } else { + ret = -1; + } + + if (ret != LDB_SUCCESS) { + return NULL; + } + + if (i == 0) { + p = talloc_asprintf(mem_ctx, "<%s=%.*s>", + name, + (int)val.length, + val.data); + } else { + p = talloc_asprintf_append_buffer(p, ";<%s=%.*s>", + name, + (int)val.length, + val.data); + } + + talloc_free(val.data); + + if (!p) { + return NULL; + } + } + + if (dn->ext_comp_num && *linearized) { + p = talloc_asprintf_append_buffer(p, ";%s", linearized); + } + + if (!p) { + return NULL; + } + + return p; +} + +/* + filter out all but an acceptable list of extended DN components + */ +void ldb_dn_extended_filter(struct ldb_dn *dn, const char * const *accept_list) +{ + unsigned int i; + for (i=0; i<dn->ext_comp_num; i++) { + if (!ldb_attr_in_list(accept_list, dn->ext_components[i].name)) { + ARRAY_DEL_ELEMENT( + dn->ext_components, i, dn->ext_comp_num); + dn->ext_comp_num--; + i--; + } + } + LDB_FREE(dn->ext_linearized); +} + + +char *ldb_dn_alloc_linearized(TALLOC_CTX *mem_ctx, struct ldb_dn *dn) +{ + return talloc_strdup(mem_ctx, ldb_dn_get_linearized(dn)); +} + +/* + casefold a dn. We need to casefold the attribute names, and canonicalize + attribute values of case insensitive attributes. +*/ + +static bool ldb_dn_casefold_internal(struct ldb_dn *dn) +{ + unsigned int i; + int ret; + + if ( ! dn || dn->invalid) return false; + + if (dn->valid_case) return true; + + if (( ! dn->components) && ( ! ldb_dn_explode(dn))) { + return false; + } + + for (i = 0; i < dn->comp_num; i++) { + const struct ldb_schema_attribute *a; + + dn->components[i].cf_name = + ldb_attr_casefold(dn->components, + dn->components[i].name); + if (!dn->components[i].cf_name) { + goto failed; + } + + 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; + } + } + + 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) +{ + unsigned int i; + size_t 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->comp_num == 0) { + dn->casefold = talloc_strdup(dn, ""); + return dn->casefold; + } + + /* 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; + + d = dn->casefold; + + for (i = 0; i < dn->comp_num; i++) { + + /* 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; +} + +char *ldb_dn_alloc_casefold(TALLOC_CTX *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; + unsigned 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 && dn->special == base->special) { + /* 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) || (base->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 != (unsigned int) -1) { + 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 = strncmp(b_vdata, dn_vdata, b_vlen); + 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) +{ + unsigned int i; + int 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 = strncmp(dn0_vdata, dn1_vdata, dn0_vlen); + if (ret != 0) { + return ret; + } + } + + return 0; +} + +static struct ldb_dn_component ldb_dn_copy_component( + TALLOC_CTX *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) { + 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; +} + +static struct ldb_dn_ext_component ldb_dn_ext_copy_component( + TALLOC_CTX *mem_ctx, + struct ldb_dn_ext_component *src) +{ + struct ldb_dn_ext_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(TALLOC_CTX *mem_ctx, struct ldb_dn *dn) +{ + struct ldb_dn *new_dn; + + if (!dn || dn->invalid) { + return NULL; + } + + new_dn = talloc_zero(mem_ctx, struct ldb_dn); + if ( !new_dn) { + return NULL; + } + + *new_dn = *dn; + + if (dn->components) { + unsigned int i; + + 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; + } + } + } + + if (dn->ext_components) { + unsigned 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; + } + } + } + + 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); + 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; +} + +/* 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) +{ + const char *s; + char *t; + + if ( !base || base->invalid || !dn || dn->invalid) { + return false; + } + + if (dn == base) { + return false; /* or we will visit infinity */ + } + + if (dn->components) { + unsigned 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; + } + } + + 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->linearized) { + + s = ldb_dn_get_linearized(base); + if ( ! s) { + return false; + } + + 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; + } + + /* 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; +} + +/* 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, ...) +{ + 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); + + talloc_free(base_str); + + return ret; +} + +/* 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) +{ + const char *s; + char *t; + + if ( !child || child->invalid || !dn || dn->invalid) { + return false; + } + + if (dn->components) { + unsigned int n; + unsigned int i, j; + + if (dn->comp_num == 0) { + return false; + } + + 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 != (unsigned int) -1; + i--, j--) { + dn->components[j] = dn->components[i]; + } + + 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; + } + } + + if (dn->linearized) { + if (dn->linearized[0] == '\0') { + return false; + } + + 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; + } + + /* 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; +} + +/* 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 *child; + char *child_str; + va_list ap; + bool ret; + + if ( !dn || dn->invalid) { + return false; + } + + va_start(ap, child_fmt); + child_str = talloc_vasprintf(dn, child_fmt, ap); + va_end(ap); + + if (child_str == NULL) { + return false; + } + + child = ldb_dn_new(child_str, dn->ldb, child_str); + + ret = ldb_dn_add_child(dn, child); + + talloc_free(child_str); + + return ret; +} + +/* modify the given dn by adding a single child element. + * + * return true if successful and false if not + * if false is returned the dn may be marked invalid + */ +bool ldb_dn_add_child_val(struct ldb_dn *dn, + const char *rdn, + struct ldb_val value) +{ + bool ret; + int ldb_ret; + struct ldb_dn *child = NULL; + + if ( !dn || dn->invalid) { + return false; + } + + child = ldb_dn_new(dn, dn->ldb, "X=Y"); + ret = ldb_dn_add_child(dn, child); + + if (ret == false) { + return false; + } + + ldb_ret = ldb_dn_set_component(dn, + 0, + rdn, + value); + if (ldb_ret != LDB_SUCCESS) { + return false; + } + + return true; +} + +bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num) +{ + unsigned int i; + + if ( ! ldb_dn_validate(dn)) { + return false; + } + + if (dn->comp_num < num) { + return false; + } + + /* free components */ + for (i = dn->comp_num - num; i < dn->comp_num; i++) { + 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->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; +} + +bool ldb_dn_remove_child_components(struct ldb_dn *dn, unsigned int num) +{ + unsigned 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; +} + + +/* replace the components of a DN with those from another DN, without + * touching the extended components + * + * return true if successful and false if not + * if false is returned the dn may be marked invalid + */ +bool ldb_dn_replace_components(struct ldb_dn *dn, struct ldb_dn *new_dn) +{ + unsigned int i; + + if ( ! ldb_dn_validate(dn) || ! ldb_dn_validate(new_dn)) { + return false; + } + + /* free components */ + for (i = 0; i < dn->comp_num; i++) { + 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 = talloc_realloc(dn, + dn->components, + struct ldb_dn_component, + new_dn->comp_num); + if (dn->components == NULL) { + ldb_dn_mark_invalid(dn); + return false; + } + + dn->comp_num = new_dn->comp_num; + dn->valid_case = new_dn->valid_case; + + for (i = 0; i < dn->comp_num; i++) { + dn->components[i] = ldb_dn_copy_component(dn->components, &new_dn->components[i]); + if (dn->components[i].name == NULL) { + ldb_dn_mark_invalid(dn); + return false; + } + } + if (new_dn->linearized == NULL) { + dn->linearized = NULL; + } else { + dn->linearized = talloc_strdup(dn, new_dn->linearized); + if (dn->linearized == NULL) { + ldb_dn_mark_invalid(dn); + return false; + } + } + + return true; +} + + +struct ldb_dn *ldb_dn_get_parent(TALLOC_CTX *mem_ctx, struct ldb_dn *dn) +{ + struct ldb_dn *new_dn; + + new_dn = ldb_dn_copy(mem_ctx, dn); + if ( !new_dn ) { + return NULL; + } + + if ( ! ldb_dn_remove_child_components(new_dn, 1)) { + talloc_free(new_dn); + return NULL; + } + + 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). + +*/ +static char *ldb_dn_canonical(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, int ex_format) { + unsigned 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 != (unsigned int) -1; i--) { + if (ldb_attr_cmp(dn->components[i].name, "dc") != 0) { + break; + } + if (cracked) { + cracked = talloc_asprintf(tmpctx, "%s.%s", + ldb_dn_escape_value(tmpctx, + dn->components[i].value), + cracked); + } else { + cracked = ldb_dn_escape_value(tmpctx, + dn->components[i].value); + } + if (!cracked) { + goto done; + } + } + + /* Only domain components? Finish here */ + if (i == (unsigned int) -1) { + 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_append_buffer(cracked, "/%s", + ldb_dn_escape_value(tmpctx, + dn->components[i].value)); + if (!cracked) { + goto done; + } + } + + /* Last one, possibly a newline for the 'ex' format */ + 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(TALLOC_CTX *mem_ctx, struct ldb_dn *dn) { + return ldb_dn_canonical(mem_ctx, dn, 0); + +} + +char *ldb_dn_canonical_ex_string(TALLOC_CTX *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; +} + +int ldb_dn_get_extended_comp_num(struct ldb_dn *dn) +{ + if ( ! ldb_dn_validate(dn)) { + return -1; + } + return dn->ext_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 < 0) { + return LDB_ERR_OTHER; + } + + if ((unsigned)num >= dn->comp_num) { + return LDB_ERR_OTHER; + } + + if (val.length > val.length + 1) { + return LDB_ERR_OTHER; + } + + n = talloc_strdup(dn, name); + if ( ! n) { + return LDB_ERR_OTHER; + } + + v.length = val.length; + + /* + * This is like talloc_memdup(dn, v.data, v.length + 1), but + * avoids the over-read + */ + v.data = (uint8_t *)talloc_size(dn, v.length+1); + if ( ! v.data) { + talloc_free(n); + return LDB_ERR_OTHER; + } + memcpy(v.data, val.data, val.length); + + /* + * Enforce NUL termination outside the stated length, as is + * traditional in LDB + */ + v.data[v.length] = '\0'; + + 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) { + unsigned int i; + for (i = 0; i < dn->comp_num; i++) { + LDB_FREE(dn->components[i].cf_name); + LDB_FREE(dn->components[i].cf_value.data); + } + 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 LDB_SUCCESS; +} + +const struct ldb_val *ldb_dn_get_extended_component(struct ldb_dn *dn, + const char *name) +{ + unsigned 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; + unsigned int i; + struct ldb_val v2; + const struct ldb_dn_extended_syntax *ext_syntax; + + if ( ! ldb_dn_validate(dn)) { + return LDB_ERR_OTHER; + } + + ext_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, name); + if (ext_syntax == NULL) { + /* 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 = ext_syntax->name; + if (!dn->ext_components[i].value.data) { + ldb_dn_mark_invalid(dn); + return LDB_ERR_OPERATIONS_ERROR; + } + } else { + ARRAY_DEL_ELEMENT( + dn->ext_components, + i, + dn->ext_comp_num); + 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; + } + } + LDB_FREE(dn->ext_linearized); + + return LDB_SUCCESS; + } + } + + if (val == NULL) { + /* removing a value that doesn't exist is not an error */ + return LDB_SUCCESS; + } + + v2 = *val; + + p = dn->ext_components + = talloc_realloc(dn, + dn->ext_components, + 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, &v2); + 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++; + + LDB_FREE(dn->ext_linearized); + + return LDB_SUCCESS; +} + +void ldb_dn_remove_extended_components(struct ldb_dn *dn) +{ + LDB_FREE(dn->ext_linearized); + LDB_FREE(dn->ext_components); + dn->ext_comp_num = 0; +} + +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; +} + +/* + this updates dn->components, taking the components from ref_dn. + This is used by code that wants to update the DN path of a DN + while not impacting on the extended DN components + */ +int ldb_dn_update_components(struct ldb_dn *dn, const struct ldb_dn *ref_dn) +{ + dn->components = talloc_realloc(dn, dn->components, + struct ldb_dn_component, ref_dn->comp_num); + if (!dn->components) { + return LDB_ERR_OPERATIONS_ERROR; + } + memcpy(dn->components, ref_dn->components, + sizeof(struct ldb_dn_component)*ref_dn->comp_num); + dn->comp_num = ref_dn->comp_num; + + LDB_FREE(dn->casefold); + LDB_FREE(dn->linearized); + LDB_FREE(dn->ext_linearized); + + return LDB_SUCCESS; +} + +/* + minimise a DN. The caller must pass in a validated DN. + + If the DN has an extended component then only the first extended + component is kept, the DN string is stripped. + + The existing dn is modified + */ +bool ldb_dn_minimise(struct ldb_dn *dn) +{ + unsigned int i; + + if (!ldb_dn_validate(dn)) { + return false; + } + if (dn->ext_comp_num == 0) { + return true; + } + + /* free components */ + for (i = 0; i < dn->comp_num; i++) { + 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->comp_num = 0; + dn->valid_case = false; + + LDB_FREE(dn->casefold); + LDB_FREE(dn->linearized); + + /* note that we don't free dn->components as this there are + * several places in ldb_dn.c that rely on it being non-NULL + * for an exploded DN + */ + + for (i = 1; i < dn->ext_comp_num; i++) { + LDB_FREE(dn->ext_components[i].value.data); + } + dn->ext_comp_num = 1; + + dn->ext_components = talloc_realloc(dn, dn->ext_components, struct ldb_dn_ext_component, 1); + if (dn->ext_components == NULL) { + ldb_dn_mark_invalid(dn); + return false; + } + + LDB_FREE(dn->ext_linearized); + + return true; +} + +struct ldb_context *ldb_dn_get_ldb_context(struct ldb_dn *dn) +{ + return dn->ldb; +} |