mirror of
https://github.com/samba-team/samba.git
synced 2025-11-26 04:23:49 +03:00
528 lines
17 KiB
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;
|
|
}
|
|
}
|
|
}
|
|
|