1
0
mirror of https://github.com/samba-team/samba.git synced 2025-11-26 04:23:49 +03:00
Files
samba-mirror/source/lib/ldb/common/ldb_explode_dn.c

528 lines
17 KiB
C

/*
ldb database library
Copyright (C) Simo Sorce 2004
Copyright (C) Derrell Lipman 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 parsing
*
* Description: explode a dn into it's own basic elements
* and put them in a structure
*
* Author: Simo Sorce
* Author: Derrell Lipman
*/
#include <ctype.h>
#include "includes.h"
#include "ldb/include/ldb.h"
#include "ldb/include/ldb_private.h"
#include "ldb/include/ldb_explode_dn.h"
#define LDB_PARSE_DN_INVALID(x) do { \
if (x) { \
goto failed; \
} \
} while(0)
/*
* Forward declarations
*/
static char *
parse_slash(char *p,
char *end);
/*
* Public functions
*/
/*
* ldb_explode_dn()
*
* Explode, normalize, and optionally case-fold a DN string. The result is a
* structure containing arrays of the constituent RDNs.
*
* Parameters:
* mem_ctx -
* talloc context on which all allocated memory is hung
*
* orig_dn -
* The distinguished name, in string format, to be exploded
*
* hUserData -
* Data handle provided by the caller, and passed back to the caller in
* the case_fold_attr_fn callback function. This handle is not otherwise
* used within this function.
*
* case_fold_attr_fn -
* Pointer to a callback function which will be called to determine if a
* particular attribute type (name) requires case-folding of its values.
* If this function pointer is non-null, then attribute names will always
* be case-folded. Additionally, the function pointed to by
* case_fold_attr_fn will be called with the data handle (hUserData) and
* an attribute name as its parameters, and should return TRUE or FALSE to
* indicate whether values of that attribute type should be case-folded.
*
* If case_fold_attr_fn is null, then neither attribute names nor
* attribute values will be case-folded.
*
* Returns:
* Upon success, an ldb_dn structure pointer is returned, containing the
* exploded DN.
*
* If memory could not be allocated or if the DN was improperly formatted,
* NULL is returned.
*/
struct ldb_dn *
ldb_explode_dn(void * mem_ctx,
const char * orig_dn,
void * hUserData,
int (*case_fold_attr_fn)(void * hUserData,
char * attr))
{
struct ldb_dn * dn;
struct ldb_dn_component * component;
struct ldb_dn_attribute * attribute;
char * p;
char * start;
char * separator;
char * src;
char * dest;
char * dn_copy;
char * dn_end;
int i;
int size;
int orig_len;
/* Allocate a structure to hold the exploded DN */
if ((dn = talloc(mem_ctx, struct ldb_dn)) == NULL) {
return NULL;
}
/* Initially there are no components */
dn->comp_num = 0;
/* Allocate the component array, with space for one component */
if ((dn->components =
talloc_array(dn, struct ldb_dn_component *, 1)) == NULL) {
goto failed;
}
/* Allocate the first component */
if ((component = talloc(dn, struct ldb_dn_component)) == NULL) {
goto failed;
}
/* This component has no attributes yet */
component->attr_num = 0;
/* Get the length of the provided DN */
if ((orig_len = strlen(orig_dn)) == 0) {
/* We found a zero-length DN. Return it. */
if ((dn->dn = talloc_strdup(dn, orig_dn)) == NULL) {
goto failed;
}
return dn;
}
/* Copy the provided DN so we can manipulate it */
if ((p = ldb_dn_fold(mem_ctx, orig_dn,
hUserData, case_fold_attr_fn)) == NULL) {
goto failed;
}
dn_copy = p;
/* Our copy may end shorter than the original as we unescape chars */
dn_end = dn_copy + orig_len + 1;
/* For each attribute/value pair... */
do {
/* Allocate an array to hold the attributes, initially len 1 */
if ((component->attributes =
talloc_array(component,
struct ldb_dn_attribute *, 1)) == NULL) {
goto failed;
}
/* Allocate this attribute */
if ((attribute =
talloc(component, struct ldb_dn_attribute)) == NULL) {
goto failed;
}
/* skip white space */
while (*p == ' ' || *p == '\n') {
p++;
}
/* start parsing this component */
do {
/* Save pointer to beginning of attribute name */
start = p;
/* find our attribute/value separator '=' */
while (*p != '\0' && *p != '=') {
if (*p == '\\') {
if ((dn_end =
parse_slash(p, dn_end)) == NULL) {
goto failed;
}
}
p++;
}
/* Ensure we found the attribute/value separator */
if (*p != '=') {
goto failed;
}
/* Save pointer to separator */
separator = p;
/* remove trailing white space from attribute name */
while (p > start &&
(*(p - 1) == ' ' || *(p - 1) == '\n')) {
p--;
}
LDB_PARSE_DN_INVALID((p - start) < 1);
/* save attribute name */
if ((attribute->name =
talloc_strndup(attribute,
start,
p - start)) == NULL) {
goto failed;
}
/* attribute names are always case-folded */
p = attribute->name;
if ((attribute->name =
ldb_casefold(attribute, p)) == NULL) {
goto failed;
}
talloc_free(p);
ldb_debug(mem_ctx,
LDB_DEBUG_TRACE,
"attribute name: [%s]\n", attribute->name);
/* skip white space after the separator */
p = separator + 1;
p += strspn(p, " \n");
/* ensure there's a value here */
if (*p == '\0') {
goto failed;
}
/* check if the value is enclosed in QUOTATION */
if (*p == '"') {
/* save pointer to beginning of attr value */
start = p + 1;
/* find the trailing QUOTE */
while (*p != '\0' && *p != '"') {
if (*p == '\\') {
if ((dn_end =
parse_slash(p, dn_end)) == NULL) {
goto failed;
}
}
p++;
}
/* skip spaces until the separator */
if (*p == '\0') {
/* We're already at end of string */
separator = p;
} else {
/* Skip spaces */
separator = p + 1 + strspn(p+1, " \n");
}
/* must be end of string or a separator here */
if (*separator != '\0' &&
*separator != ',' &&
*separator != ';' &&
*separator != '+') {
/* Error Malformed DN */
goto failed;
}
} else {
/*
* Value is not quouted.
*/
/* save pointer to beginning of value */
start = p;
/* find end of value */
while (*p != '\0' &&
*p != ',' &&
*p != ';' &&
*p != '+') {
if (*p == '\\') {
if ((dn_end =
parse_slash(p, dn_end)) == NULL) {
goto failed;
}
}
p++;
}
/* save pointer to the terminating separator */
separator = p;
/* remove trailing whitespace */
while (p > start &&
(*(p - 1) == ' ' ||
*(p - 1) == '\n')) {
p--;
}
}
LDB_PARSE_DN_INVALID((p - start) < 1);
/* save the value */
if ((attribute->value =
talloc_strndup(attribute,
start,
p - start)) == NULL) {
goto failed;
}
/* see if this attribute value needs case folding */
if (case_fold_attr_fn != NULL &&
(* case_fold_attr_fn)(hUserData,
attribute->name)) {
/* yup, case-fold it. */
p = attribute->value;
if ((attribute->value =
ldb_casefold(attribute, p)) == NULL) {
goto failed;
}
talloc_free(p);
}
ldb_debug(mem_ctx,
LDB_DEBUG_TRACE,
"attribute value: [%s]\n", attribute->value);
/* save the entire RDN */
if ((attribute->rdn =
talloc_asprintf(attribute,
"%s=%s",
attribute->name,
attribute->value)) == NULL) {
goto failed;
}
ldb_debug(mem_ctx,
LDB_DEBUG_TRACE,
"attribute: [%s]\n", attribute->rdn);
/* add this attribute to the attribute list */
component->attributes[component->attr_num] = attribute;
component->attr_num++;
/* is this a multi-valued attribute? */
if (*separator == '+') {
/* Yup. prepare for the next value. */
if ((component->attributes =
talloc_realloc(component,
component->attributes,
struct ldb_dn_attribute *,
component->attr_num + 1)) == NULL) {
goto failed;
}
/* allocate new attribute structure */
if ((attribute =
talloc(component,
struct ldb_dn_attribute)) == NULL) {
goto failed;
}
}
/* if we're not at end of string, skip white space */
if (*separator != '\0') {
/* skip spaces past the separator */
p = separator + 1;
p += strspn(p, " \n");
}
} while (*separator == '+');
/* find total length of all attributes */
for (i = 0, size = 0; i < component->attr_num; i++) {
size += strlen(component->attributes[i]->rdn) + 1;
}
/*
* rebuild the normalized component
*/
/* allocate space for the normalized component */
if ((dest = talloc_size(component, size)) == NULL) {
goto failed;
}
/* Save the pointer to the beginning of the string */
component->component = dest;
/* copy each of the attributes to the normalized component */
for (i = 0; i < component->attr_num; i++) {
if (i != 0) {
*dest++ = '+';
}
src = component->attributes[i]->rdn;
/* we are guaranteed to have enough space in dest */
size = strlen(src);
strncpy(dest, src, size + 1);
dest += size;
}
ldb_debug(mem_ctx,
LDB_DEBUG_TRACE,
"component: [%s]\n", component->component);
/* insert the component into the component list */
dn->components[dn->comp_num] = component;
dn->comp_num++;
/* if there are additional components... */
if (*separator == ',' || *separator == ';') {
/* ... then prepare to parse them */
if ((dn->components =
talloc_realloc(dn,
dn->components,
struct ldb_dn_component *,
dn->comp_num + 1)) == NULL ||
(component =
talloc(dn, struct ldb_dn_component)) == NULL) {
goto failed;
}
component->attr_num = 0;
}
/* update pointer to after the separator */
p = separator + 1;
} while(*separator == ',' || *separator == ';');
/* find total length of all components */
for (i = 0, size = 0; i < dn->comp_num; i++) {
size = size + strlen(dn->components[i]->component) + 1;
}
/* rebuild the normalized DN */
if ((dest = talloc_size(dn, size)) == NULL) {
goto failed;
}
/* Save the pointer to the beginning of the */
dn->dn = dest;
/* copy the normalized components into the DN */
for (i = 0; i < dn->comp_num; i++) {
/* add a separator between DN components */
if (i != 0) {
*dest++ = ',';
}
/* point to this component of the DN */
src = dn->components[i]->component;
/* we are guaranteed to have enough space in dest */
size = strlen(src);
strncpy(dest, src, size + 1);
dest += size;
}
ldb_debug(mem_ctx, LDB_DEBUG_TRACE, "dn: [%s]\n", dn->dn);
/* we don't need the copy of the DN any more */
talloc_free(dn_copy);
/* give 'em what they came for! */
return dn;
failed:
/* something went wrong. free memory and tell 'em it failed */
talloc_free(dn);
ldb_debug(mem_ctx, LDB_DEBUG_TRACE, "Failed to parse %s\n", orig_dn);
return NULL;
}
static char *
parse_slash(char *p,
char *end)
{
switch (*(p + 1)) {
case ',':
case '=':
case '\n':
case '+':
case '<':
case '>':
case '#':
case ';':
case '\\':
case '"':
memmove(p, p + 1, end - (p + 1));
return (end - 1);
default:
if (isxdigit(p[1]) && isxdigit(p[2])) {
int x;
sscanf(p + 1, "%02x", &x);
*p = (char) x;
memmove(p + 1, p + 3, end - (p + 3));
return (end - 2);
} else {
return NULL;
}
}
}