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