X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source4%2Flib%2Fldb%2Fcommon%2Fldb_dn.c;h=402d6295015d141de84211692b1131f7a0a6ff70;hb=380874ef863866c94c999ef53252b9d30df65e88;hp=d035b0d3c2e21ed471b4b21e66bb07faf9cd7513;hpb=bbc056e067e1b721dc2ef6958ab653c1eddec3a1;p=metze%2Fsamba%2Fwip.git diff --git a/source4/lib/ldb/common/ldb_dn.c b/source4/lib/ldb/common/ldb_dn.c index d035b0d3c2e2..402d6295015d 100644 --- a/source4/lib/ldb/common/ldb_dn.c +++ b/source4/lib/ldb/common/ldb_dn.c @@ -10,7 +10,7 @@ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. + version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -18,58 +18,155 @@ 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 . */ /* * 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/includes.h" +#include "ldb_private.h" +#include #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) +/** + 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_extended_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 *extended_linearized; + char *casefold; + + unsigned int comp_num; + struct ldb_dn_component *components; + + unsigned int extended_comp_num; + struct ldb_dn_extended_component *extended_components; +}; + +/* strdn may be NULL */ +struct ldb_dn *ldb_dn_from_ldb_val(void *mem_ctx, struct ldb_context *ldb, const struct ldb_val *strdn) { - if (dn == NULL || dn->comp_num != 1) return 0; + struct ldb_dn *dn; - return ! strcmp(dn->components[0].name, LDB_SPECIAL); + if (! ldb) return NULL; + + dn = talloc_zero(mem_ctx, struct ldb_dn); + LDB_DN_NULL_FAILED(dn); + + dn->ldb = ldb; + + if (strdn->data && strdn->length) { + if (strdn->data[0] == '@') { + dn->special = true; + } + dn->extended_linearized = talloc_strndup(dn, (const char *)strdn->data, strdn->length); + LDB_DN_NULL_FAILED(dn->extended_linearized); + + if (strdn->data[0] == '<') { + const char *p_save, *p = dn->extended_linearized; + do { + p_save = p; + p = strstr(p, ">;"); + if (p) { + p = p + 2; + } + } while (p); + + if (p_save == dn->extended_linearized) { + dn->linearized = talloc_strdup(dn, ""); + } else { + dn->linearized = talloc_strdup(dn, p_save); + } + LDB_DN_NULL_FAILED(dn->linearized); + } else { + dn->linearized = dn->extended_linearized; + dn->extended_linearized = NULL; + } + } else { + dn->linearized = talloc_strdup(dn, ""); + LDB_DN_NULL_FAILED(dn->linearized); + } + + return dn; + +failed: + talloc_free(dn); + return NULL; } -int ldb_dn_check_special(const struct ldb_dn *dn, const char *check) +/* strdn may be NULL */ +struct ldb_dn *ldb_dn_new(void *mem_ctx, struct ldb_context *ldb, const char *strdn) { - if (dn == NULL || dn->comp_num != 1) return 0; - - return ! strcmp((char *)dn->components[0].value.data, check); + struct ldb_val blob; + blob.data = strdn; + blob.length = strdn ? strlen(strdn) : 0; + return ldb_dn_from_ldb_val(mem_ctx, ldb, &blob); } -char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value) +struct ldb_dn *ldb_dn_new_fmt(void *mem_ctx, struct ldb_context *ldb, const char *new_fmt, ...) { - const char *p, *s, *src; - char *d, *dst; - int len; + char *strdn; + va_list ap; - if (!value.length) - return NULL; + if ( (! mem_ctx) || (! ldb)) return NULL; - p = s = src = (const char *)value.data; - len = value.length; + va_start(ap, new_fmt); + strdn = talloc_vasprintf(mem_ctx, new_fmt, ap); + va_end(ap); - /* 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); + if (strdn) { + struct ldb_dn *dn = ldb_dn_new(mem_ctx, ldb, strdn); + talloc_free(strdn); + return dn; + } + + return NULL; +} + +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) { @@ -86,401 +183,715 @@ char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value) *d++ = *p++; } else { /* we have a zero byte in the string */ strncpy(d, "\00", 3); /* escape the zero */ - d = d + 3; + d += 3; p++; /* skip the zero */ } s = p; /* move forward */ } /* copy the last part (with zero) and return */ - memcpy(d, s, &src[len] - s + 1); + l = len - (s - src); + memcpy(d, s, l + 1); - return dst; + /* return the length of the resulting string */ + return (l + (d - dst)); +} -failed: - talloc_free(dst); - return NULL; +char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value) +{ + char *dst; + + 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; + } + + ldb_dn_escape_internal(dst, (const char *)value.data, value.length); + + dst = talloc_realloc(mem_ctx, dst, char, strlen(dst) + 1); + + return dst; } -static struct ldb_val ldb_dn_unescape_value(void *mem_ctx, const char *src) +/* + explode a DN string into a ldb_dn structure + based on RFC4514 except that we don't support multiple valued RDNs +*/ +static bool ldb_dn_explode(struct ldb_dn *dn) { - struct ldb_val value; + 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; - char *p, *dst = NULL, *end; + int l, ret; + char *parse_dn; - memset(&value, 0, sizeof(value)); + if ( ! dn || dn->invalid) return false; - LDB_DN_NULL_FAILED(src); + if (dn->components) { + return true; + } - dst = p = (char *)talloc_memdup(mem_ctx, src, strlen(src) + 1); - LDB_DN_NULL_FAILED(dst); + if (dn->extended_linearized) { + parse_dn = dn->extended_linearized; + } else { + parse_dn = dn->linearized; + } - end = &dst[strlen(dst)]; + if ( ! parse_dn ) { + return false; + } - while (*p) { - p += strcspn(p, ",=\n+<>#;\\\""); + /* Empty DNs */ + if (parse_dn[0] == '\0') { + return true; + } + + /* Special DNs case */ + if (dn->special) { + return true; + } + + /* make sure we free this if alloced previously before replacing */ + talloc_free(dn->components); + + talloc_free(dn->extended_components); + dn->extended_components = NULL; + + /* in the common case we have 3 or more components */ + /* make sure all components are zeroed, other functions depend on this */ + dn->components = talloc_zero_array(dn, struct ldb_dn_component, 3); + if ( ! dn->components) { + return false; + } + dn->comp_num = 0; + + /* Components data space is allocated here once */ + data = talloc_array(dn->components, char, strlen(parse_dn) + 1); + if (!data) { + return false; + } + + p = parse_dn; + in_extended = true; + in_ex_name = false; + in_ex_value = false; + trim = true; + t = NULL; + d = dt = data; - if (*p == '\\') { - if (strchr(",=\n+<>#;\\\"", p[1])) { - memmove(p, p + 1, end - (p + 1) + 1); - end--; + while (*p) { + if (in_extended) { + + if (!in_ex_name && !in_ex_value) { + + if (p[0] == '<') { + p++; + ex_name = d; + in_ex_name = true; + continue; + } else if (p[0] == '\0') { + p++; + continue; + } else { + in_extended = false; + in_attr = true; + dt = d; + + continue; + } + } + + if (in_ex_name && *p == '=') { + *d++ = '\0'; p++; + ex_value = d; + in_ex_name = false; + in_ex_value = true; continue; } - if (sscanf(p + 1, "%02x", &x) == 1) { - *p = (unsigned char)x; - memmove(p + 1, p + 3, end - (p + 3) + 1); - end -= 2; + if (in_ex_value && *p == '>') { + const struct ldb_dn_extended_syntax *extended_syntax; + struct ldb_val ex_val = { + .data = ex_value, + .length = d - ex_value + }; + + *d++ = '\0'; p++; - continue; + in_ex_value = false; + + /* Process name and ex_value */ + + dn->extended_components = talloc_realloc(dn, + dn->extended_components, + struct ldb_dn_extended_component, + dn->extended_comp_num + 1); + if ( ! dn->extended_components) { + /* ouch ! */ + goto failed; + } + + extended_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, ex_name); + if (!extended_syntax) { + /* We don't know about this type of extended DN */ + goto failed; + } + + dn->extended_components[dn->extended_comp_num].name = talloc_strdup(dn->extended_components, ex_name); + if (!dn->extended_components[dn->extended_comp_num].name) { + /* ouch */ + goto failed; + } + ret = extended_syntax->read_fn(dn->ldb, dn->extended_components, + &ex_val, &dn->extended_components[dn->extended_comp_num].value); + if (ret != LDB_SUCCESS) { + dn->invalid = true; + goto failed; + } + + dn->extended_comp_num++; + + if (*p == '\0') { + /* We have reached the end (extended component only)! */ + talloc_free(data); + return true; + + } else if (*p == ';') { + p++; + continue; + } else { + dn->invalid = true; + goto failed; + } } - } - /* a string with not escaped specials is invalid (tested) */ - if (*p != '\0') { - goto failed; + *d++ = *p++; + continue; } - } - - value.length = end - dst; - value.data = (uint8_t *)dst; - return value; + if (in_attr) { + if (trim) { + if (*p == ' ') { + p++; + continue; + } + + /* first char */ + trim = false; + + if (!isascii(*p)) { + /* attr names must be ascii only */ + dn->invalid = true; + goto failed; + } + + if (isdigit(*p)) { + is_oid = true; + } else + if ( ! isalpha(*p)) { + /* not a digit nor an alpha, invalid attribute name */ + dn->invalid = true; + goto failed; + } + + /* Copy this character across from parse_dn, now we have trimmed out spaces */ + *d++ = *p++; + continue; + } -failed: - talloc_free(dst); - return value; -} + if (*p == ' ') { + p++; + /* valid only if we are at the end */ + trim = true; + continue; + } -/* 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 (trim && (*p != '=')) { + /* spaces/tabs are not allowed in attribute names */ + dn->invalid = true; + goto failed; + } -static int get_quotes_position(const char *source, int *quote_start, int *quote_end) -{ - const char *p; + if (*p == '=') { + /* attribute terminated */ + in_attr = false; + in_value = true; + trim = true; + l = 0; - if (source == NULL || quote_start == NULL || quote_end == NULL) return -1; + /* 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; + } - p = source; + dt = d; - /* check if there are quotes surrounding the value */ - p += strspn(p, " \n"); /* skip white spaces */ + p++; + continue; + } - if (*p == '\"') { - *quote_start = p - source; + if (!isascii(*p)) { + /* attr names must be ascii only */ + dn->invalid = true; + goto failed; + } - p++; - while (*p != '\"') { - p = strchr(p, '\"'); - LDB_DN_NULL_FAILED(p); + if (is_oid && ( ! (isdigit(*p) || (*p == '.')))) { + /* not a digit nor a dot, invalid attribute oid */ + dn->invalid = true; + goto failed; + } else + if ( ! (isalpha(*p) || isdigit(*p) || (*p == '-'))) { + /* not ALPHA, DIGIT or HYPHEN */ + dn->invalid = true; + goto failed; + } - if (*(p - 1) == '\\') - p++; + *d++ = *p++; + continue; } - *quote_end = p - source; - return 1; - } - - return 0; + if (in_value) { + if (in_quote) { + if (*p == '\"') { + if (p[-1] != '\\') { + p++; + in_quote = false; + continue; + } + } + *d++ = *p++; + l++; + continue; + } -failed: - return -1; -} + if (trim) { + if (*p == ' ') { + p++; + continue; + } -static char *seek_to_separator(char *string, const char *separators) -{ - char *p, *q; - int ret, qs, qe, escaped; + /* first char */ + trim = false; - if (string == NULL || separators == NULL) return NULL; + if (*p == '\"') { + in_quote = true; + p++; + continue; + } + } - p = strchr(string, '='); - LDB_DN_NULL_FAILED(p); + switch (*p) { - p++; + /* TODO: support ber encoded values + case '#': + */ - /* check if there are quotes surrounding the value */ + case ',': + if (escape) { + *d++ = *p++; + l++; + escape = false; + continue; + } + /* ok found value terminator */ - ret = get_quotes_position(p, &qs, &qe); - if (ret == -1) - return NULL; + if ( t ) { + /* trim back */ + d -= (p - t); + l -= (p - t); + } - if (ret == 1) { /* quotes found */ + in_attr = true; + in_value = false; + trim = true; - p += qe; /* positioning after quotes */ - p += strspn(p, " \n"); /* skip white spaces after the quote */ + 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)); + } - if (strcspn(p, separators) != 0) /* if there are characters between quotes */ - return NULL; /* and separators, the dn is invalid */ + continue; - return p; /* return on the separator */ - } + case '=': + case '\n': + case '+': + case '<': + case '>': + case '#': + case ';': + case '\"': + /* a string with not escaped specials is invalid (tested) */ + if ( ! escape) { + dn->invalid = true; + 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 (sscanf(p, "%02x", &x) != 1) { + /* invalid escaping sequence */ + dn->invalid = true; + goto failed; + } + escape = false; + + p += 2; + *d++ = (unsigned char)x; + l++; + + if ( t ) t = NULL; + break; + } + + if (*p == ' ') { + if ( ! t) t = p; + } else { + if ( t ) t = NULL; + } + + *d++ = *p++; + l++; + + break; + } - /* no quotes found seek to separators */ - q = p; - do { - escaped = 0; - ret = strcspn(q, separators); - - if (q[ret - 1] == '\\') { - escaped = 1; - q = q + ret + 1; } - } while (escaped); + } - if (ret == 0 && p == q) /* no separators ?! bail out */ - return NULL; + if (in_attr || in_quote) { + /* invalid dn */ + dn->invalid = true; + goto failed; + } - return q + ret; + /* save last element */ + if ( t ) { + /* trim back */ + d -= (p - t); + l -= (p - t); + } -failed: - return NULL; -} + *d++ = '\0'; + dn->components[dn->comp_num].value.data = (uint8_t *)talloc_strdup(dn->components, dt); + dn->components[dn->comp_num].value.length = l; -static char *ldb_dn_trim_string(char *string, const char *edge) -{ - char *s, *p; + if ( ! dn->components[dn->comp_num].value.data) { + /* ouch */ + goto failed; + } - /* seek out edge from start of string */ - s = string + strspn(string, edge); + dn->comp_num++; - /* backwards skip from end of string */ - p = &s[strlen(s) - 1]; - while (p > s && strchr(edge, *p)) { - *p = '\0'; - p--; - } + talloc_free(data); + return true; - return s; +failed: + dn->comp_num = 0; + talloc_free(dn->components); + return false; } -/* we choosed to not support multpile valued components */ -static struct ldb_dn_component ldb_dn_explode_component(void *mem_ctx, char *raw_component) +bool ldb_dn_validate(struct ldb_dn *dn) { - struct ldb_dn_component dc; - char *p; - int ret, qs, qe; + return ldb_dn_explode(dn); +} - memset(&dc, 0, sizeof(dc)); +const char *ldb_dn_get_linearized(struct ldb_dn *dn) +{ + int i, len; + char *d, *n; - if (raw_component == NULL) { - return dc; - } + if ( ! dn || ( dn->invalid)) return NULL; - /* find attribute type/value separator */ - p = strchr(raw_component, '='); - LDB_DN_NULL_FAILED(p); + if (dn->linearized) return dn->linearized; - *p++ = '\0'; /* terminate name and point to value */ + if ( ! dn->components) { + dn->invalid = true; + return NULL; + } - /* 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 (dn->comp_num == 0) { + dn->linearized = talloc_strdup(dn, ""); + if ( ! dn->linearized) return NULL; + return dn->linearized; + } - if (! ldb_valid_attr_name(dc.name)) { - goto failed; + /* calculate maximum possible length of DN */ + for (len = 0, i = 0; i < dn->comp_num; i++) { + len += strlen(dn->components[i].name); /* name len */ + len += (dn->components[i].value.length * 3); /* max escaped data len */ + len += 2; /* '=' and ',' */ } + dn->linearized = talloc_array(dn, char, len); + if ( ! dn->linearized) return NULL; - ret = get_quotes_position(p, &qs, &qe); + d = dn->linearized; - 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; + for (i = 0; i < dn->comp_num; i++) { - case 1: /* quotes found get the unquoted string */ - p[qe] = '\0'; - p = p + qs + 1; - dc.value.length = strlen(p); - dc.value.data = (uint8_t *)talloc_memdup(mem_ctx, p, - dc.value.length + 1); - break; + /* copy the name */ + n = dn->components[i].name; + while (*n) *d++ = *n++; - default: /* mismatched quotes ot other error, bail out */ - goto failed; - } + *d++ = '='; - if (dc.value.length == 0) { - goto failed; + /* and the value */ + d += ldb_dn_escape_internal( d, + (char *)dn->components[i].value.data, + dn->components[i].value.length); + *d++ = ','; } - return dc; + *(--d) = '\0'; -failed: - talloc_free(dc.name); - dc.name = NULL; - return dc; + /* don't waste more memory than necessary */ + dn->linearized = talloc_realloc(dn, dn->linearized, char, (d - dn->linearized + 1)); + + return dn->linearized; } -struct ldb_dn *ldb_dn_new(void *mem_ctx) +char *ldb_dn_get_extended_linearized(void *mem_ctx, struct ldb_dn *dn, int mode) { - struct ldb_dn *edn; + const char *linearized = ldb_dn_get_linearized(dn); + char *p; + int i; - edn = talloc(mem_ctx, struct ldb_dn); - LDB_DN_NULL_FAILED(edn); + if (!linearized) { + return NULL; + } - /* Initially there are no components */ - edn->comp_num = 0; - edn->components = NULL; + if (!ldb_dn_has_extended(dn)) { + return talloc_strdup(mem_ctx, linearized); + } + + if (!ldb_dn_validate(dn)) { + return NULL; + } - return edn; + for (i=0; i < dn->extended_comp_num; i++) { + struct ldb_val val; + int ret; + const struct ldb_dn_extended_syntax *extended_syntax; + const char *name = dn->extended_components[i].name; + + extended_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, name); + + if (mode == 1) { + ret = extended_syntax->write_clear_fn(dn->ldb, mem_ctx, + &dn->extended_components[i].value, + &val); + } else if (mode == 0) { + ret = extended_syntax->write_hex_fn(dn->ldb, mem_ctx, + &dn->extended_components[i].value, + &val); + } else { + ret = -1; + } -failed: - return NULL; -} + if (ret != LDB_SUCCESS) { + return NULL; + } -struct ldb_dn *ldb_dn_explode(void *mem_ctx, const char *dn) -{ - struct ldb_dn *edn; /* the exploded dn */ - char *pdn, *p; + if (i == 0) { + p = talloc_asprintf(mem_ctx, "<%s=%s>", dn->extended_components[i].name, val.data); + } else { + p = talloc_asprintf_append(p, ";<%s=%s>", dn->extended_components[i].name, val.data); + } - if (dn == NULL) return NULL; + talloc_free(val.data); - /* Allocate a structure to hold the exploded DN */ - edn = ldb_dn_new(mem_ctx); - if (edn == NULL) { - return NULL; + if (!p) { + return NULL; + } } - pdn = NULL; - - /* Empty DNs */ - if (dn[0] == '\0') { - return edn; + if (dn->extended_comp_num && *linearized) { + p = talloc_asprintf_append(p, ";%s", linearized); } - /* 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++; + if (!p) { + return NULL; + } - /* jump to the next component if any */ - p = t; + return p; +} - } while(*p); - talloc_free(pdn); - return edn; -failed: - talloc_free(pdn); - talloc_free(edn); - return NULL; +char *ldb_dn_alloc_linearized(void *mem_ctx, struct ldb_dn *dn) +{ + return talloc_strdup(mem_ctx, ldb_dn_get_linearized(dn)); } -struct ldb_dn *ldb_dn_explode_or_special(void *mem_ctx, const char *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) { - struct ldb_dn *edn; /* the exploded dn */ + int i, ret; + + if ( ! dn || dn->invalid) return false; - if (dn == NULL) return NULL; + if (dn->valid_case) return true; - if (strncasecmp(dn, "components) && ( ! ldb_dn_explode(dn))) { + return false; + } - /* Allocate a structure to hold the exploded DN */ - edn = ldb_dn_new(mem_ctx); + for (i = 0; i < dn->comp_num; i++) { + const struct ldb_schema_attribute *a; - 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; + 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; + } } - - return ldb_dn_explode(mem_ctx, dn); + + dn->valid_case = true; + + return true; failed: - talloc_free(edn); - 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; } -char *ldb_dn_linearize(void *mem_ctx, const struct ldb_dn *edn) +const char *ldb_dn_get_casefold(struct ldb_dn *dn) { - char *dn, *value; - int i; + int i, len; + char *d, *n; - if (edn == NULL) return NULL; + if (dn->casefold) return dn->casefold; - /* Special DNs */ - if (ldb_dn_is_special(edn)) { - dn = talloc_strdup(mem_ctx, (char *)edn->components[0].value.data); - return dn; + if (dn->special) { + dn->casefold = talloc_strdup(dn, dn->linearized); + if (!dn->casefold) return NULL; + dn->valid_case = true; + return dn->casefold; } - dn = talloc_strdup(mem_ctx, ""); - LDB_DN_NULL_FAILED(dn); - - for (i = 0; i < edn->comp_num; i++) { - value = ldb_dn_escape_value(dn, edn->components[i].value); - LDB_DN_NULL_FAILED(value); + if ( ! ldb_dn_casefold_internal(dn)) { + return NULL; + } - 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); + if (dn->comp_num == 0) { + if (dn->linearized && dn->linearized[0] == '\0') { + /* hmm a NULL dn, should we faild casefolding ? */ + dn->casefold = talloc_strdup(dn, ""); + return dn->casefold; } - LDB_DN_NULL_FAILED(dn); + /* A DN must be NULL, special, or have components */ + dn->invalid = true; + return NULL; + } - talloc_free(value); + /* calculate maximum possible length of DN */ + for (len = 0, i = 0; i < dn->comp_num; i++) { + len += strlen(dn->components[i].cf_name); /* name len */ + len += (dn->components[i].cf_value.length * 3); /* max escaped data len */ + len += 2; /* '=' and ',' */ } + dn->casefold = talloc_array(dn, char, len); + if ( ! dn->casefold) return NULL; - return dn; + d = dn->casefold; -failed: - talloc_free(dn); - return NULL; + 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(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 @@ -488,42 +899,69 @@ failed: * 0 if they match, otherwise non-zero */ -int ldb_dn_compare_base(struct ldb_context *ldb, - const struct ldb_dn *base, - const struct ldb_dn *dn) +int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn) { int ret; - int n0, n1; + 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 == NULL || base->comp_num == 0) return 0; - if (dn == NULL || dn->comp_num == 0) return -1; + } - /* if the base has more componts than the dn, then they differ */ + /* if base has more components, + * they don't have the same base */ if (base->comp_num > dn->comp_num) { return (dn->comp_num - base->comp_num); } - n0 = base->comp_num - 1; - n1 = dn->comp_num - 1; - while (n0 >= 0 && n1 >= 0) { - const struct ldb_attrib_handler *h; - - /* compare names (attribute names are guaranteed to be ASCII only) */ - ret = ldb_attr_cmp(base->components[n0].name, - dn->components[n1].name); - if (ret) { - return ret; + 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; - /* 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; + while (n_base >= 0) { + /* compare attr names */ + ret = strcmp(base->components[n_base].cf_name, dn->components[n_dn].cf_name); + if (ret != 0) return ret; + + /* compare attr.cf_value. */ + if (base->components[n_base].cf_value.length != dn->components[n_dn].cf_value.length) { + return base->components[n_base].cf_value.length - dn->components[n_dn].cf_value.length; } - n1--; - n0--; + ret = strcmp((char *)base->components[n_base].cf_value.data, (char *)dn->components[n_dn].cf_value.data); + if (ret != 0) return ret; + + n_base--; + n_dn--; } return 0; @@ -534,359 +972,549 @@ int ldb_dn_compare_base(struct ldb_context *ldb, If they match, then return 0 */ -int ldb_dn_compare(struct ldb_context *ldb, - const struct ldb_dn *edn0, - const struct ldb_dn *edn1) +int ldb_dn_compare(struct ldb_dn *dn0, struct ldb_dn *dn1) { - if (edn0 == NULL || edn1 == NULL) return edn1 - edn0; + int i, ret; - if (edn0->comp_num != edn1->comp_num) - return (edn1->comp_num - edn0->comp_num); + if (( ! dn0) || dn0->invalid || ! dn1 || dn1->invalid) return -1; - return ldb_dn_compare_base(ldb, edn0, edn1); -} + 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; + } -int ldb_dn_cmp(struct ldb_context *ldb, const char *dn0, const char *dn1) -{ - struct ldb_dn *edn0; - struct ldb_dn *edn1; - int ret; + if ( ! ldb_dn_casefold_internal(dn0)) { + return 1; + } - if (dn0 == NULL || dn1 == NULL) return dn1 - dn0; + if ( ! ldb_dn_casefold_internal(dn1)) { + return -1; + } - edn0 = ldb_dn_explode_casefold(ldb, ldb, dn0); - if (edn0 == NULL) return 1; + } - edn1 = ldb_dn_explode_casefold(ldb, ldb, dn1); - if (edn1 == NULL) { - talloc_free(edn0); - 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; + } } - ret = ldb_dn_compare(ldb, edn0, edn1); + for (i = 0; i < dn0->comp_num; i++) { + /* compare attr names */ + ret = strcmp(dn0->components[i].cf_name, dn1->components[i].cf_name); + if (ret != 0) return ret; - talloc_free(edn0); - talloc_free(edn1); + /* compare attr.cf_value. */ + if (dn0->components[i].cf_value.length != dn1->components[i].cf_value.length) { + return dn0->components[i].cf_value.length - dn1->components[i].cf_value.length; + } + ret = strcmp((char *)dn0->components[i].cf_value.data, (char *)dn1->components[i].cf_value.data); + if (ret != 0) return ret; + } - return ret; + return 0; } -/* - 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, void *mem_ctx, const struct ldb_dn *edn) +static struct ldb_dn_component ldb_dn_copy_component(void *mem_ctx, struct ldb_dn_component *src) { - struct ldb_dn *cedn; - int i, ret; + struct ldb_dn_component dst; - if (edn == NULL) return NULL; + memset(&dst, 0, sizeof(dst)); - cedn = ldb_dn_new(mem_ctx); - if (!cedn) { - return NULL; + if (src == NULL) { + return dst; } - cedn->comp_num = edn->comp_num; - cedn->components = talloc_array(cedn, struct ldb_dn_component, edn->comp_num); - if (!cedn->components) { - talloc_free(cedn); - return NULL; + dst.value = ldb_val_dup(mem_ctx, &(src->value)); + if (dst.value.data == NULL) { + return dst; } - for (i = 0; i < edn->comp_num; i++) { - struct ldb_dn_component dc; - const struct ldb_attrib_handler *h; + dst.name = talloc_strdup(mem_ctx, src->name); + if (dst.name == NULL) { + LDB_FREE(dst.value.data); + return dst; + } - memset(&dc, 0, sizeof(dc)); - dc.name = ldb_attr_casefold(cedn->components, edn->components[i].name); - if (!dc.name) { - talloc_free(cedn); - return NULL; + 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; } - h = ldb_attrib_handler(ldb, dc.name); - ret = h->canonicalise_fn(ldb, cedn->components, - &(edn->components[i].value), - &(dc.value)); - if (ret != 0) { - talloc_free(cedn); - return NULL; + 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; } - - cedn->components[i] = dc; + } else { + dst.cf_value.data = NULL; + dst.cf_name = NULL; } - return cedn; + return dst; } -struct ldb_dn *ldb_dn_explode_casefold(struct ldb_context *ldb, void *mem_ctx, const char *dn) +static struct ldb_dn_extended_component ldb_dn_extended_copy_component(void *mem_ctx, struct ldb_dn_extended_component *src) { - struct ldb_dn *edn, *cdn; + struct ldb_dn_extended_component dst; - if (dn == NULL) return NULL; + memset(&dst, 0, sizeof(dst)); - edn = ldb_dn_explode(ldb, dn); - if (edn == NULL) return NULL; + if (src == NULL) { + return dst; + } - cdn = ldb_dn_casefold(ldb, mem_ctx, edn); - - talloc_free(edn); - return cdn; + 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; } -char *ldb_dn_linearize_casefold(struct ldb_context *ldb, void *mem_ctx, const struct ldb_dn *edn) +struct ldb_dn *ldb_dn_copy(void *mem_ctx, struct ldb_dn *dn) { - struct ldb_dn *cdn; - char *dn; + struct ldb_dn *new_dn; - if (edn == NULL) return NULL; + if (!dn || dn->invalid) { + return NULL; + } - /* Special DNs */ - if (ldb_dn_is_special(edn)) { - dn = talloc_strdup(mem_ctx, (char *)edn->components[0].value.data); - return dn; + new_dn = talloc_zero(mem_ctx, struct ldb_dn); + if ( !new_dn) { + return NULL; } - cdn = ldb_dn_casefold(ldb, mem_ctx, edn); - if (cdn == NULL) return NULL; + *new_dn = *dn; - dn = ldb_dn_linearize(ldb, cdn); - if (dn == NULL) { - talloc_free(cdn); - return NULL; + if (dn->components) { + 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; + } + } } - talloc_free(cdn); - return dn; -} + if (dn->extended_components) { + int i; -static struct ldb_dn_component ldb_dn_copy_component(void *mem_ctx, struct ldb_dn_component *src) -{ - struct ldb_dn_component dst; + new_dn->extended_components = talloc_zero_array(new_dn, struct ldb_dn_extended_component, dn->extended_comp_num); + if ( ! new_dn->extended_components) { + talloc_free(new_dn); + return NULL; + } - memset(&dst, 0, sizeof(dst)); + for (i = 0; i < dn->extended_comp_num; i++) { + new_dn->extended_components[i] = ldb_dn_extended_copy_component(new_dn->extended_components, &dn->extended_components[i]); + if ( ! new_dn->extended_components[i].value.data) { + talloc_free(new_dn); + return NULL; + } + } + } - if (src == NULL) { - return dst; + if (dn->casefold) { + new_dn->casefold = talloc_strdup(new_dn, dn->casefold); + if ( ! new_dn->casefold) { + talloc_free(new_dn); + return NULL; + } } - dst.value = ldb_val_dup(mem_ctx, &(src->value)); - if (dst.value.data == NULL) { - return dst; + if (dn->linearized) { + new_dn->linearized = talloc_strdup(new_dn, dn->linearized); + if ( ! new_dn->linearized) { + talloc_free(new_dn); + return NULL; + } } - dst.name = talloc_strdup(mem_ctx, src->name); - if (dst.name == NULL) { - talloc_free(dst.value.data); - dst.value.data = NULL; + if (dn->extended_linearized) { + new_dn->extended_linearized = talloc_strdup(new_dn, dn->extended_linearized); + if ( ! new_dn->extended_linearized) { + talloc_free(new_dn); + return NULL; + } } - return dst; + return new_dn; } -/* copy specified number of elements of a dn into a new one - element are copied from top level up to the unique rdn - num_el may be greater than dn->comp_num (see ldb_dn_make_child) -*/ -struct ldb_dn *ldb_dn_copy_partial(void *mem_ctx, const struct ldb_dn *dn, int num_el) +/* 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 *newdn; - int i, n, e; + const char *s; + char *t; - if (dn == NULL) return NULL; - if (num_el <= 0) return NULL; + if ( !base || base->invalid || !dn || dn->invalid) { + return false; + } - newdn = ldb_dn_new(mem_ctx); - LDB_DN_NULL_FAILED(newdn); + if (dn->components) { + int i; - newdn->comp_num = num_el; - n = newdn->comp_num - 1; - newdn->components = talloc_array(newdn, struct ldb_dn_component, newdn->comp_num); - if (newdn->components == NULL) goto failed; + if ( ! ldb_dn_validate(base)) { + return false; + } - if (dn->comp_num == 0) return newdn; - e = dn->comp_num - 1; + s = NULL; + if (dn->valid_case) { + if ( ! (s = ldb_dn_get_casefold(base))) { + return false; + } + } - for (i = 0; i < newdn->comp_num; i++) { - newdn->components[n - i] = ldb_dn_copy_component(newdn->components, - &(dn->components[e - i])); - if ((e - i) == 0) { - return newdn; + dn->components = talloc_realloc(dn, + dn->components, + struct ldb_dn_component, + dn->comp_num + base->comp_num); + if ( ! dn->components) { + dn->invalid = true; + 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) { + dn->invalid = true; + 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; } } - return newdn; + if (dn->linearized) { -failed: - talloc_free(newdn); - return NULL; -} + 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) { + dn->invalid = true; + return false; + } + LDB_FREE(dn->linearized); + dn->linearized = t; + } -struct ldb_dn *ldb_dn_copy(void *mem_ctx, const struct ldb_dn *dn) -{ - if (dn == NULL) return NULL; - return ldb_dn_copy_partial(mem_ctx, dn, dn->comp_num); -} + /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */ + if (dn->extended_linearized) { + LDB_FREE(dn->extended_linearized); + } -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); + LDB_FREE(dn->extended_components); + dn->extended_comp_num = 0; + return true; } -struct ldb_dn_component *ldb_dn_build_component(void *mem_ctx, const char *attr, - const char *val) +/* 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_component *dc; + struct ldb_dn *base; + char *base_str; + va_list ap; + bool ret; - if (attr == NULL || val == NULL) return NULL; + if ( !dn || dn->invalid) { + return false; + } - dc = talloc(mem_ctx, struct ldb_dn_component); - if (dc == NULL) return NULL; + va_start(ap, base_fmt); + base_str = talloc_vasprintf(dn, base_fmt, ap); + va_end(ap); - dc->name = talloc_strdup(dc, attr); - if (dc->name == NULL) { - talloc_free(dc); - return NULL; + if (base_str == NULL) { + return false; } - dc->value.data = (uint8_t *)talloc_strdup(dc, val); - if (dc->value.data == NULL) { - talloc_free(dc); - return NULL; - } + base = ldb_dn_new(base_str, dn->ldb, base_str); - dc->value.length = strlen(val); + ret = ldb_dn_add_base(dn, base); - return dc; -} + talloc_free(base_str); + + return ret; +} -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 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) { - struct ldb_dn *newdn; - if (! ldb_valid_attr_name(attr)) return NULL; - if (value == NULL || value == '\0') return NULL; + const char *s; + char *t; - if (base != NULL) { - newdn = ldb_dn_copy_partial(mem_ctx, base, base->comp_num + 1); - LDB_DN_NULL_FAILED(newdn); - } else { - newdn = ldb_dn_new(mem_ctx); - LDB_DN_NULL_FAILED(newdn); + if ( !child || child->invalid || !dn || dn->invalid) { + return false; + } + + if (dn->components) { + int n, i, j; + + if ( ! ldb_dn_validate(child)) { + return false; + } + + s = NULL; + if (dn->valid_case) { + if ( ! (s = ldb_dn_get_casefold(child))) { + return false; + } + } - newdn->comp_num = 1; - newdn->components = talloc_array(newdn, struct ldb_dn_component, newdn->comp_num); + n = dn->comp_num + child->comp_num; + + dn->components = talloc_realloc(dn, + dn->components, + struct ldb_dn_component, + n); + if ( ! dn->components) { + dn->invalid = true; + return false; + } + + for (i = dn->comp_num - 1, j = n - 1; i >= 0; 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) { + dn->invalid = true; + 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; + } } - newdn->components[0].name = talloc_strdup(newdn->components, attr); - LDB_DN_NULL_FAILED(newdn->components[0].name); + if (dn->linearized) { - newdn->components[0].value.data = (uint8_t *)talloc_strdup(newdn->components, value); - LDB_DN_NULL_FAILED(newdn->components[0].value.data); - newdn->components[0].value.length = strlen((char *)newdn->components[0].value.data); + s = ldb_dn_get_linearized(child); + if ( ! s) { + return false; + } + + t = talloc_asprintf(dn, "%s,%s", s, dn->linearized); + if ( ! t) { + dn->invalid = true; + return false; + } + LDB_FREE(dn->linearized); + dn->linearized = t; + } - return newdn; + /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */ + LDB_FREE(dn->extended_linearized); -failed: - talloc_free(newdn); - return NULL; + LDB_FREE(dn->extended_components); + dn->extended_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 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, ...) { - if (component == NULL) return NULL; + 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 ldb_dn_build_child(mem_ctx, component->name, - (char *)component->value.data, base); + return ret; } -struct ldb_dn *ldb_dn_compose(void *mem_ctx, const struct ldb_dn *dn1, const struct ldb_dn *dn2) +bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num) { int i; - struct ldb_dn *newdn; - if (dn2 == NULL && dn1 == NULL) { - return NULL; + if ( ! ldb_dn_validate(dn)) { + return false; } - if (dn2 == NULL) { - newdn = ldb_dn_new(mem_ctx); - LDB_DN_NULL_FAILED(newdn); - - newdn->comp_num = dn1->comp_num; - newdn->components = talloc_array(newdn, struct ldb_dn_component, newdn->comp_num); - } else { - int comp_num = dn2->comp_num; - if (dn1 != NULL) comp_num += dn1->comp_num; - newdn = ldb_dn_copy_partial(mem_ctx, dn2, comp_num); - LDB_DN_NULL_FAILED(newdn); + if (dn->comp_num < num) { + return false; } - if (dn1 == NULL) { - return newdn; + /* free components */ + for (i = num; i > 0; i--) { + LDB_FREE(dn->components[dn->comp_num - i].name); + LDB_FREE(dn->components[dn->comp_num - i].value.data); + LDB_FREE(dn->components[dn->comp_num - i].cf_name); + LDB_FREE(dn->components[dn->comp_num - i].cf_value.data); } + + dn->comp_num -= num; - for (i = 0; i < dn1->comp_num; i++) { - newdn->components[i] = ldb_dn_copy_component(newdn->components, - &(dn1->components[i])); - if (newdn->components[i].value.data == NULL) { - goto failed; + 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; } - return newdn; + LDB_FREE(dn->casefold); + LDB_FREE(dn->linearized); -failed: - talloc_free(newdn); - return NULL; + /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */ + LDB_FREE(dn->extended_linearized); + + LDB_FREE(dn->extended_components); + dn->extended_comp_num = 0; + + return true; } -struct ldb_dn *ldb_dn_string_compose(void *mem_ctx, const struct ldb_dn *base, const char *child_fmt, ...) +bool ldb_dn_remove_child_components(struct ldb_dn *dn, unsigned int num) { - struct ldb_dn *dn; - char *child_str; - va_list ap; - - if (child_fmt == NULL) return NULL; + int i, j; - va_start(ap, child_fmt); - child_str = talloc_vasprintf(mem_ctx, child_fmt, ap); - va_end(ap); + if ( ! ldb_dn_validate(dn)) { + return false; + } - if (child_str == NULL) return NULL; + if (dn->comp_num < num) { + return false; + } - dn = ldb_dn_compose(mem_ctx, ldb_dn_explode(mem_ctx, child_str), base); + 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]; + } - talloc_free(child_str); + dn->comp_num -= num; - return dn; + if (dn->valid_case) { + for (i = 0; i < dn->comp_num; i++) { + LDB_FREE(dn->components[i].cf_name); + LDB_FREE(dn->components[i].cf_value.data); + } + dn->valid_case = false; + } + + LDB_FREE(dn->casefold); + LDB_FREE(dn->linearized); + + /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */ + LDB_FREE(dn->extended_linearized); + + LDB_FREE(dn->extended_components); + dn->extended_comp_num = 0; + return true; } -struct ldb_dn_component *ldb_dn_get_rdn(void *mem_ctx, const struct ldb_dn *dn) +struct ldb_dn *ldb_dn_get_parent(void *mem_ctx, struct ldb_dn *dn) { - struct ldb_dn_component *rdn; - - if (dn == NULL) return NULL; + struct ldb_dn *new_dn; - if (dn->comp_num < 1) { + new_dn = ldb_dn_copy(mem_ctx, dn); + if ( !new_dn ) { return NULL; } - rdn = talloc(mem_ctx, struct ldb_dn_component); - if (rdn == NULL) return NULL; - - *rdn = ldb_dn_copy_component(mem_ctx, &dn->components[0]); - if (rdn->name == NULL) { - talloc_free(rdn); + if ( ! ldb_dn_remove_child_components(new_dn, 1)) { + talloc_free(new_dn); return NULL; } - return rdn; + /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */ + LDB_FREE(dn->extended_linearized); + + LDB_FREE(dn->extended_components); + dn->extended_comp_num = 0; + return new_dn; } /* Create a 'canonical name' string from a DN: @@ -897,9 +1525,17 @@ struct ldb_dn_component *ldb_dn_get_rdn(void *mem_ctx, const struct ldb_dn *dn) 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--) { @@ -907,53 +1543,258 @@ static char *ldb_dn_canonical(void *mem_ctx, const struct ldb_dn *dn, int ex_for 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 extended_linearized DN, as the GUID and SID are almost certainly no longer valid */ + LDB_FREE(dn->extended_linearized); + + dn->extended_comp_num = 0; + LDB_FREE(dn->extended_components); + return LDB_SUCCESS; +} + +const struct ldb_val *ldb_dn_get_extended_component(struct ldb_dn *dn, const char *name) +{ + int i; + if ( ! ldb_dn_validate(dn)) { + return NULL; + } + for (i=0; i < dn->extended_comp_num; i++) { + if (ldb_attr_cmp(dn->extended_components[i].name, name) == 0) { + return &dn->extended_components[i].value; + } + } + return NULL; +} + +int ldb_dn_set_extended_component(struct ldb_dn *dn, const char *name, const struct ldb_val *val) +{ + struct ldb_dn_extended_component *p; + int i; + + if ( ! ldb_dn_validate(dn)) { + return LDB_ERR_OTHER; + } + + for (i=0; i < dn->extended_comp_num; i++) { + if (ldb_attr_cmp(dn->extended_components[i].name, name) == 0) { + if (val) { + dn->extended_components[i].value = ldb_val_dup(dn->extended_components, val); + + dn->extended_components[i].name = talloc_strdup(dn->extended_components, name); + if (!dn->extended_components[i].name || !dn->extended_components[i].value.data) { + dn->invalid = true; + return LDB_ERR_OPERATIONS_ERROR; + } + + } else { + if (i != (dn->extended_comp_num - 1)) { + memmove(&dn->extended_components[i], &dn->extended_components[i+1], + ((dn->extended_comp_num-1) - i)*sizeof(*dn->extended_components)); + } + dn->extended_comp_num--; + + dn->extended_components = talloc_realloc(dn, + dn->extended_components, + struct ldb_dn_extended_component, + dn->extended_comp_num); + if (!dn->extended_components) { + dn->invalid = true; + return LDB_ERR_OPERATIONS_ERROR; + } + return LDB_SUCCESS; + } + } + } + + p = dn->extended_components + = talloc_realloc(dn, + dn->extended_components, + struct ldb_dn_extended_component, + dn->extended_comp_num + 1); + if (!dn->extended_components) { + dn->invalid = true; + return LDB_ERR_OPERATIONS_ERROR; + } + + p[dn->extended_comp_num].value = ldb_val_dup(dn->extended_components, val); + p[dn->extended_comp_num].name = talloc_strdup(p, name); + + if (!dn->extended_components[i].name || !dn->extended_components[i].value.data) { + dn->invalid = true; + return LDB_ERR_OPERATIONS_ERROR; + } + dn->extended_components = p; + dn->extended_comp_num++; + + return LDB_SUCCESS; +} + +void ldb_dn_remove_extended_components(struct ldb_dn *dn) +{ + dn->extended_comp_num = 0; + LDB_FREE(dn->extended_components); +} + +bool ldb_dn_is_valid(struct ldb_dn *dn) +{ + if ( ! dn) return false; + 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->extended_linearized && (dn->extended_linearized[0] == '<')) return true; + return dn->extended_comp_num != 0; +} + +bool ldb_dn_check_special(struct ldb_dn *dn, const char *check) +{ + if ( ! dn || dn->invalid) return false; + 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; +}