Commit message: CVE-2015-5330: ldb_dn_escape_value: use known string length, not strlen()

ldb_dn_escape_internal() reports the number of bytes it copied, so
lets use that number, rather than using strlen() and hoping a zero got
in the right place.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=11599

Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Pair-programmed-with: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
CVE: CVE-2015-5330
CWE: CWE-200
Vulnerability description: ldb before 1.1.24, as used in the AD LDAP server in Samba 4.x before 4.1.22, 4.2.x before 4.2.7, and 4.3.x before 4.3.3, mishandles string lengths, which allows remote attackers to obtain sensitive information from daemon heap memory by sending crafted packets and then reading (1) an error message or (2) a database value.

Project: samba
File: ldb_dn.c

bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base)
{
	const char *s;
	char *t;

	if ( !base || base->invalid || !dn || dn->invalid) {
		return false;
	}

	if (dn->components) {
		unsigned int i;

		if ( ! ldb_dn_validate(base)) {
			return false;
		}

		s = NULL;
		if (dn->valid_case) {
			if ( ! (s = ldb_dn_get_casefold(base))) {
				return false;
			}
		}

		dn->components = talloc_realloc(dn,
						dn->components,
						struct ldb_dn_component,
						dn->comp_num + base->comp_num);
		if ( ! dn->components) {
			ldb_dn_mark_invalid(dn);
			return false;
		}

		for (i = 0; i < base->comp_num; dn->comp_num++, i++) {
			dn->components[dn->comp_num] =
				ldb_dn_copy_component(dn->components,
							&base->components[i]);
			if (dn->components[dn->comp_num].value.data == NULL) {
				ldb_dn_mark_invalid(dn);
				return false;
			}
		}

		if (dn->casefold && s) {
			if (*dn->casefold) {
				t = talloc_asprintf(dn, "%s,%s",
						    dn->casefold, s);
			} else {
				t = talloc_strdup(dn, s);
			}
			LDB_FREE(dn->casefold);
			dn->casefold = t;
		}
	}

	if (dn->linearized) {

		s = ldb_dn_get_linearized(base);
		if ( ! s) {
			return false;
		}

		if (*dn->linearized) {
			t = talloc_asprintf(dn, "%s,%s",
					    dn->linearized, s);
		} else {
			t = talloc_strdup(dn, s);
		}
		if ( ! t) {
			ldb_dn_mark_invalid(dn);
			return false;
		}
		LDB_FREE(dn->linearized);
		dn->linearized = t;
	}

	/* Wipe the ext_linearized DN,
	 * the GUID and SID are almost certainly no longer valid */
	LDB_FREE(dn->ext_linearized);
	LDB_FREE(dn->ext_components);
	dn->ext_comp_num = 0;

	return true;
}

Project: samba
File: ldb_dn.c

bool ldb_dn_add_base_fmt(struct ldb_dn *dn, const char *base_fmt, ...)
{
	struct ldb_dn *base;
	char *base_str;
	va_list ap;
	bool ret;

	if ( !dn || dn->invalid) {
		return false;
	}

	va_start(ap, base_fmt);
	base_str = talloc_vasprintf(dn, base_fmt, ap);
	va_end(ap);

	if (base_str == NULL) {
		return false;
	}

	base = ldb_dn_new(base_str, dn->ldb, base_str);

	ret = ldb_dn_add_base(dn, base);

	talloc_free(base_str);

	return ret;
}

Project: samba
File: ldb_dn.c

bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child)
{
	const char *s;
	char *t;

	if ( !child || child->invalid || !dn || dn->invalid) {
		return false;
	}

	if (dn->components) {
		unsigned int n;
		unsigned int i, j;

		if (dn->comp_num == 0) {
			return false;
		}

		if ( ! ldb_dn_validate(child)) {
			return false;
		}

		s = NULL;
		if (dn->valid_case) {
			if ( ! (s = ldb_dn_get_casefold(child))) {
				return false;
			}
		}

		n = dn->comp_num + child->comp_num;

		dn->components = talloc_realloc(dn,
						dn->components,
						struct ldb_dn_component,
						n);
		if ( ! dn->components) {
			ldb_dn_mark_invalid(dn);
			return false;
		}

		for (i = dn->comp_num - 1, j = n - 1; i != (unsigned int) -1;
		     i--, j--) {
			dn->components[j] = dn->components[i];
		}

		for (i = 0; i < child->comp_num; i++) {
			dn->components[i] =
				ldb_dn_copy_component(dn->components,
							&child->components[i]);
			if (dn->components[i].value.data == NULL) {
				ldb_dn_mark_invalid(dn);
				return false;
			}
		}

		dn->comp_num = n;

		if (dn->casefold && s) {
			t = talloc_asprintf(dn, "%s,%s", s, dn->casefold);
			LDB_FREE(dn->casefold);
			dn->casefold = t;
		}
	}

	if (dn->linearized) {
		if (dn->linearized[0] == '\0') {
			return false;
		}

		s = ldb_dn_get_linearized(child);
		if ( ! s) {
			return false;
		}

		t = talloc_asprintf(dn, "%s,%s", s, dn->linearized);
		if ( ! t) {
			ldb_dn_mark_invalid(dn);
			return false;
		}
		LDB_FREE(dn->linearized);
		dn->linearized = t;
	}

	/* Wipe the ext_linearized DN,
	 * the GUID and SID are almost certainly no longer valid */
	LDB_FREE(dn->ext_linearized);
	LDB_FREE(dn->ext_components);
	dn->ext_comp_num = 0;

	return true;
}

Project: samba
File: ldb_dn.c

bool ldb_dn_add_child_fmt(struct ldb_dn *dn, const char *child_fmt, ...)
{
	struct ldb_dn *child;
	char *child_str;
	va_list ap;
	bool ret;

	if ( !dn || dn->invalid) {
		return false;
	}

	va_start(ap, child_fmt);
	child_str = talloc_vasprintf(dn, child_fmt, ap);
	va_end(ap);

	if (child_str == NULL) {
		return false;
	}

	child = ldb_dn_new(child_str, dn->ldb, child_str);

	ret = ldb_dn_add_child(dn, child);

	talloc_free(child_str);

	return ret;
}

Project: samba
File: ldb_dn.c

static char *ldb_dn_canonical(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, int ex_format) {
	unsigned int i;
	TALLOC_CTX *tmpctx;
	char *cracked = NULL;
	const char *format = (ex_format ? "\n" : "/" );

	if ( ! ldb_dn_validate(dn)) {
		return NULL;
	}

	tmpctx = talloc_new(mem_ctx);

	/* Walk backwards down the DN, grabbing 'dc' components at first */
	for (i = dn->comp_num - 1; i != (unsigned int) -1; i--) {
		if (ldb_attr_cmp(dn->components[i].name, "dc") != 0) {
			break;
		}
		if (cracked) {
			cracked = talloc_asprintf(tmpctx, "%s.%s",
						  ldb_dn_escape_value(tmpctx,
							dn->components[i].value),
						  cracked);
		} else {
			cracked = ldb_dn_escape_value(tmpctx,
							dn->components[i].value);
		}
		if (!cracked) {
			goto done;
		}
	}

	/* Only domain components?  Finish here */
	if (i == (unsigned int) -1) {
		cracked = talloc_strdup_append_buffer(cracked, format);
		talloc_steal(mem_ctx, cracked);
		goto done;
	}

	/* Now walk backwards appending remaining components */
	for (; i > 0; i--) {
		cracked = talloc_asprintf_append_buffer(cracked, "/%s",
							ldb_dn_escape_value(tmpctx,
							dn->components[i].value));
		if (!cracked) {
			goto done;
		}
	}

	/* Last one, possibly a newline for the 'ex' format */
	cracked = talloc_asprintf_append_buffer(cracked, "%s%s", format,
						ldb_dn_escape_value(tmpctx,
							dn->components[i].value));

	talloc_steal(mem_ctx, cracked);
done:
	talloc_free(tmpctx);
	return cracked;
}

Project: samba
File: ldb_dn.c

char *ldb_dn_canonical_ex_string(TALLOC_CTX *mem_ctx, struct ldb_dn *dn) {
	return ldb_dn_canonical(mem_ctx, dn, 1);
}

Project: samba
File: ldb_dn.c

static bool ldb_dn_casefold_internal(struct ldb_dn *dn)
{
	unsigned int i;
	int ret;

	if ( ! dn || dn->invalid) return false;

	if (dn->valid_case) return true;

	if (( ! dn->components) && ( ! ldb_dn_explode(dn))) {
		return false;
	}

	for (i = 0; i < dn->comp_num; i++) {
		const struct ldb_schema_attribute *a;

		dn->components[i].cf_name =
			ldb_attr_casefold(dn->components,
					  dn->components[i].name);
		if (!dn->components[i].cf_name) {
			goto failed;
		}

		a = ldb_schema_attribute_by_name(dn->ldb,
						 dn->components[i].cf_name);

		ret = a->syntax->canonicalise_fn(dn->ldb, dn->components,
						 &(dn->components[i].value),
						 &(dn->components[i].cf_value));
		if (ret != 0) {
			goto failed;
		}
	}

	dn->valid_case = true;

	return true;

failed:
	for (i = 0; i < dn->comp_num; i++) {
		LDB_FREE(dn->components[i].cf_name);
		LDB_FREE(dn->components[i].cf_value.data);
	}
	return false;
}

Project: samba
File: ldb_dn.c

bool ldb_dn_check_special(struct ldb_dn *dn, const char *check)
{
	if ( ! dn || dn->invalid) return false;
	return ! strcmp(dn->linearized, check);
}

Project: samba
File: ldb_dn.c

int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
{
	int ret;
	unsigned int n_base, n_dn;

	if ( ! base || base->invalid) return 1;
	if ( ! dn || dn->invalid) return -1;

	if (( ! base->valid_case) || ( ! dn->valid_case)) {
		if (base->linearized && dn->linearized && dn->special == base->special) {
			/* try with a normal compare first, if we are lucky
			 * we will avoid exploding and casfolding */
			int dif;
			dif = strlen(dn->linearized) - strlen(base->linearized);
			if (dif < 0) {
				return dif;
			}
			if (strcmp(base->linearized,
				   &dn->linearized[dif]) == 0) {
				return 0;
			}
		}

		if ( ! ldb_dn_casefold_internal(base)) {
			return 1;
		}

		if ( ! ldb_dn_casefold_internal(dn)) {
			return -1;
		}

	}

	/* if base has more components,
	 * they don't have the same base */
	if (base->comp_num > dn->comp_num) {
		return (dn->comp_num - base->comp_num);
	}

	if ((dn->comp_num == 0) || (base->comp_num == 0)) {
		if (dn->special && base->special) {
			return strcmp(base->linearized, dn->linearized);
		} else if (dn->special) {
			return -1;
		} else if (base->special) {
			return 1;
		} else {
			return 0;
		}
	}

	n_base = base->comp_num - 1;
	n_dn = dn->comp_num - 1;

	while (n_base != (unsigned int) -1) {
		char *b_name = base->components[n_base].cf_name;
		char *dn_name = dn->components[n_dn].cf_name;

		char *b_vdata = (char *)base->components[n_base].cf_value.data;
		char *dn_vdata = (char *)dn->components[n_dn].cf_value.data;

		size_t b_vlen = base->components[n_base].cf_value.length;
		size_t dn_vlen = dn->components[n_dn].cf_value.length;

		/* compare attr names */
		ret = strcmp(b_name, dn_name);
		if (ret != 0) return ret;

		/* compare attr.cf_value. */
		if (b_vlen != dn_vlen) {
			return b_vlen - dn_vlen;
		}
		ret = strncmp(b_vdata, dn_vdata, b_vlen);
		if (ret != 0) return ret;

		n_base--;
		n_dn--;
	}

	return 0;
}

Project: samba
File: ldb_dn.c

struct ldb_dn *ldb_dn_copy(TALLOC_CTX *mem_ctx, struct ldb_dn *dn)
{
	struct ldb_dn *new_dn;

	if (!dn || dn->invalid) {
		return NULL;
	}

	new_dn = talloc_zero(mem_ctx, struct ldb_dn);
	if ( !new_dn) {
		return NULL;
	}

	*new_dn = *dn;

	if (dn->components) {
		unsigned int i;

		new_dn->components =
			talloc_zero_array(new_dn,
					  struct ldb_dn_component,
					  dn->comp_num);
		if ( ! new_dn->components) {
			talloc_free(new_dn);
			return NULL;
		}

		for (i = 0; i < dn->comp_num; i++) {
			new_dn->components[i] =
				ldb_dn_copy_component(new_dn->components,
						      &dn->components[i]);
			if ( ! new_dn->components[i].value.data) {
				talloc_free(new_dn);
				return NULL;
			}
		}
	}

	if (dn->ext_components) {
		unsigned int i;

		new_dn->ext_components =
			talloc_zero_array(new_dn,
					  struct ldb_dn_ext_component,
					  dn->ext_comp_num);
		if ( ! new_dn->ext_components) {
			talloc_free(new_dn);
			return NULL;
		}

		for (i = 0; i < dn->ext_comp_num; i++) {
			new_dn->ext_components[i] =
				 ldb_dn_ext_copy_component(
						new_dn->ext_components,
						&dn->ext_components[i]);
			if ( ! new_dn->ext_components[i].value.data) {
				talloc_free(new_dn);
				return NULL;
			}
		}
	}

	if (dn->casefold) {
		new_dn->casefold = talloc_strdup(new_dn, dn->casefold);
		if ( ! new_dn->casefold) {
			talloc_free(new_dn);
			return NULL;
		}
	}

	if (dn->linearized) {
		new_dn->linearized = talloc_strdup(new_dn, dn->linearized);
		if ( ! new_dn->linearized) {
			talloc_free(new_dn);
			return NULL;
		}
	}

	if (dn->ext_linearized) {
		new_dn->ext_linearized = talloc_strdup(new_dn,
							dn->ext_linearized);
		if ( ! new_dn->ext_linearized) {
			talloc_free(new_dn);
			return NULL;
		}
	}

	return new_dn;
}

Project: samba
File: ldb_dn.c

static struct ldb_dn_component ldb_dn_copy_component(
						TALLOC_CTX *mem_ctx,
						struct ldb_dn_component *src)
{
	struct ldb_dn_component dst;

	memset(&dst, 0, sizeof(dst));

	if (src == NULL) {
		return dst;
	}

	dst.value = ldb_val_dup(mem_ctx, &(src->value));
	if (dst.value.data == NULL) {
		return dst;
	}

	dst.name = talloc_strdup(mem_ctx, src->name);
	if (dst.name == NULL) {
		LDB_FREE(dst.value.data);
		return dst;
	}

	if (src->cf_value.data) {
		dst.cf_value = ldb_val_dup(mem_ctx, &(src->cf_value));
		if (dst.cf_value.data == NULL) {
			LDB_FREE(dst.value.data);
			LDB_FREE(dst.name);
			return dst;
		}

		dst.cf_name = talloc_strdup(mem_ctx, src->cf_name);
		if (dst.cf_name == NULL) {
			LDB_FREE(dst.cf_name);
			LDB_FREE(dst.value.data);
			LDB_FREE(dst.name);
			return dst;
		}
	} else {
		dst.cf_value.data = NULL;
		dst.cf_name = NULL;
	}

	return dst;
}

Project: samba
File: ldb_dn.c

static bool ldb_dn_explode(struct ldb_dn *dn)
{
	char *p, *ex_name = NULL, *ex_value = NULL, *data, *d, *dt, *t;
	bool trim = true;
	bool in_extended = true;
	bool in_ex_name = false;
	bool in_ex_value = false;
	bool in_attr = false;
	bool in_value = false;
	bool in_quote = false;
	bool is_oid = false;
	bool escape = false;
	unsigned int x;
	size_t l = 0;
	int ret;
	char *parse_dn;
	bool is_index;

	if ( ! dn || dn->invalid) return false;

	if (dn->components) {
		return true;
	}

	if (dn->ext_linearized) {
		parse_dn = dn->ext_linearized;
	} else {
		parse_dn = dn->linearized;
	}

	if ( ! parse_dn ) {
		return false;
	}

	is_index = (strncmp(parse_dn, "DN=@INDEX:", 10) == 0);

	/* Empty DNs */
	if (parse_dn[0] == '\0') {
		return true;
	}

	/* Special DNs case */
	if (dn->special) {
		return true;
	}

	/* make sure we free this if allocated previously before replacing */
	LDB_FREE(dn->components);
	dn->comp_num = 0;

	LDB_FREE(dn->ext_components);
	dn->ext_comp_num = 0;

	/* in the common case we have 3 or more components */
	/* make sure all components are zeroed, other functions depend on it */
	dn->components = talloc_zero_array(dn, struct ldb_dn_component, 3);
	if ( ! dn->components) {
		return false;
	}

	/* Components data space is allocated here once */
	data = talloc_array(dn->components, char, strlen(parse_dn) + 1);
	if (!data) {
		return false;
	}

	p = parse_dn;
	t = NULL;
	d = dt = data;

	while (*p) {
		if (in_extended) {

			if (!in_ex_name && !in_ex_value) {

				if (p[0] == '<') {
					p++;
					ex_name = d;
					in_ex_name = true;
					continue;
				} else 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 (in_ex_value && *p == '>') {
				const struct ldb_dn_extended_syntax *ext_syntax;
				struct ldb_val ex_val = {
					.data = (uint8_t *)ex_value,
					.length = d - ex_value
				};

				*d++ = '\0';
				p++;
				in_ex_value = false;

				/* Process name and ex_value */

				dn->ext_components = talloc_realloc(dn,
								    dn->ext_components,
								    struct ldb_dn_ext_component,
								    dn->ext_comp_num + 1);
				if ( ! dn->ext_components) {
					/* ouch ! */
					goto failed;
				}

				ext_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, ex_name);
				if (!ext_syntax) {
					/* We don't know about this type of extended DN */
					goto failed;
				}

				dn->ext_components[dn->ext_comp_num].name = talloc_strdup(dn->ext_components, ex_name);
				if (!dn->ext_components[dn->ext_comp_num].name) {
					/* ouch */
					goto failed;
				}
				ret = ext_syntax->read_fn(dn->ldb, dn->ext_components,
							  &ex_val, &dn->ext_components[dn->ext_comp_num].value);
				if (ret != LDB_SUCCESS) {
					ldb_dn_mark_invalid(dn);
					goto failed;
				}

				dn->ext_comp_num++;

				if (*p == '\0') {
					/* We have reached the end (extended component only)! */
					talloc_free(data);
					return true;

				} else if (*p == ';') {
					p++;
					continue;
				} else {
					ldb_dn_mark_invalid(dn);
					goto failed;
				}
			}

			*d++ = *p++;
			continue;
		}
		if (in_attr) {
			if (trim) {
				if (*p == ' ') {
					p++;
					continue;
				}

				/* first char */
				trim = false;

				if (!isascii(*p)) {
					/* attr names must be ascii only */
					ldb_dn_mark_invalid(dn);
					goto failed;
				}

				if (isdigit(*p)) {
					is_oid = true;
				} else
				if ( ! isalpha(*p)) {
					/* not a digit nor an alpha,
 					 * invalid attribute name */
					ldb_dn_mark_invalid(dn);
					goto failed;
				}

				/* Copy this character across from parse_dn,
				 * now we have trimmed out spaces */
				*d++ = *p++;
				continue;
			}

			if (*p == ' ') {
				p++;
				/* valid only if we are at the end */
				trim = true;
				continue;
			}

			if (trim && (*p != '=')) {
				/* spaces/tabs are not allowed */
				ldb_dn_mark_invalid(dn);
				goto failed;
			}

			if (*p == '=') {
				/* attribute terminated */
				in_attr = false;
				in_value = true;
				trim = true;
				l = 0;

				/* Terminate this string in d
				 * (which is a copy of parse_dn
				 *  with spaces trimmed) */
				*d++ = '\0';
				dn->components[dn->comp_num].name = talloc_strdup(dn->components, dt);
				if ( ! dn->components[dn->comp_num].name) {
					/* ouch */
					goto failed;
				}

				dt = d;

				p++;
				continue;
			}

			if (!isascii(*p)) {
				/* attr names must be ascii only */
				ldb_dn_mark_invalid(dn);
				goto failed;
			}

			if (is_oid && ( ! (isdigit(*p) || (*p == '.')))) {
				/* not a digit nor a dot,
				 * invalid attribute oid */
				ldb_dn_mark_invalid(dn);
				goto failed;
			} else
			if ( ! (isalpha(*p) || isdigit(*p) || (*p == '-'))) {
				/* not ALPHA, DIGIT or HYPHEN */
				ldb_dn_mark_invalid(dn);
				goto failed;
			}

			*d++ = *p++;
			continue;
		}

		if (in_value) {
			if (in_quote) {
				if (*p == '\"') {
					if (p[-1] != '\\') {
						p++;
						in_quote = false;
						continue;
					}
				}
				*d++ = *p++;
				l++;
				continue;
			}

			if (trim) {
				if (*p == ' ') {
					p++;
					continue;
				}

				/* first char */
				trim = false;

				if (*p == '\"') {
					in_quote = true;
					p++;
					continue;
				}
			}

			switch (*p) {

			/* TODO: support ber encoded values
			case '#':
			*/

			case ',':
				if (escape) {
					*d++ = *p++;
					l++;
					escape = false;
					continue;
				}
				/* ok found value terminator */

				if ( t ) {
					/* trim back */
					d -= (p - t);
					l -= (p - t);
				}

				in_attr = true;
				in_value = false;
				trim = true;

				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));
				}

				continue;

			case '+':
			case '=':
				/* to main compatibility with earlier
				versions of ldb indexing, we have to
				accept the base64 encoded binary index
				values, which contain a '+' or '='
				which should normally be escaped */
				if (is_index) {
					if ( t ) t = NULL;
					*d++ = *p++;
					l++;
					break;
				}
				/* fall through */
			case '\"':
			case '<':
			case '>':
			case ';':
				/* a string with not escaped specials is invalid (tested) */
				if ( ! escape) {
					ldb_dn_mark_invalid(dn);
					goto failed;
				}
				escape = false;

				*d++ = *p++;
				l++;

				if ( t ) t = NULL;
				break;

			case '\\':
				if ( ! escape) {
					escape = true;
					p++;
					continue;
				}
				escape = false;

				*d++ = *p++;
				l++;

				if ( t ) t = NULL;
				break;

			default:
				if (escape) {
					if (isxdigit(p[0]) && isxdigit(p[1])) {
						if (sscanf(p, "%02x", &x) != 1) {
							/* invalid escaping sequence */
							ldb_dn_mark_invalid(dn);
							goto failed;
						}
						p += 2;
						*d++ = (unsigned char)x;
					} else {
						*d++ = *p++;
					}

					escape = false;
					l++;
					if ( t ) t = NULL;
					break;
				}

				if (*p == ' ') {
					if ( ! t) t = p;
				} else {
					if ( t ) t = NULL;
				}

				*d++ = *p++;
				l++;

				break;
			}

		}
	}

	if (in_attr || in_quote) {
		/* invalid dn */
		ldb_dn_mark_invalid(dn);
		goto failed;
	}

	/* save last element */
	if ( t ) {
		/* trim back */
		d -= (p - t);
		l -= (p - t);
	}

	*d++ = '\0';
	dn->components[dn->comp_num].value.length = l;
	dn->components[dn->comp_num].value.data =
				(uint8_t *)talloc_strdup(dn->components, dt);
	if ( ! dn->components[dn->comp_num].value.data) {
		/* ouch */
		goto failed;
	}

	dn->comp_num++;

	talloc_free(data);
	return true;

failed:
	LDB_FREE(dn->components); /* "data" is implicitly free'd */
	dn->comp_num = 0;
	LDB_FREE(dn->ext_components);
	dn->ext_comp_num = 0;

	return false;
}

Project: samba
File: ldb_dn.c

static struct ldb_dn_ext_component ldb_dn_ext_copy_component(
						TALLOC_CTX *mem_ctx,
						struct ldb_dn_ext_component *src)
{
	struct ldb_dn_ext_component dst;

	memset(&dst, 0, sizeof(dst));

	if (src == NULL) {
		return dst;
	}

	dst.value = ldb_val_dup(mem_ctx, &(src->value));
	if (dst.value.data == NULL) {
		return dst;
	}

	dst.name = talloc_strdup(mem_ctx, src->name);
	if (dst.name == NULL) {
		LDB_FREE(dst.value.data);
		return dst;
	}

	return dst;
}

Project: samba
File: ldb_dn.c

static int ldb_dn_extended_component_compare(const void *p1, const void *p2)
{
	const struct ldb_dn_ext_component *ec1 = (const struct ldb_dn_ext_component *)p1;
	const struct ldb_dn_ext_component *ec2 = (const struct ldb_dn_ext_component *)p2;
	return strcmp(ec1->name, ec2->name);
}

Project: samba
File: ldb_dn.c

void ldb_dn_extended_filter(struct ldb_dn *dn, const char * const *accept_list)
{
	unsigned int i;
	for (i=0; i<dn->ext_comp_num; i++) {
		if (!ldb_attr_in_list(accept_list, dn->ext_components[i].name)) {
			memmove(&dn->ext_components[i],
				&dn->ext_components[i+1],
				(dn->ext_comp_num-(i+1))*sizeof(dn->ext_components[0]));
			dn->ext_comp_num--;
			i--;
		}
	}
	LDB_FREE(dn->ext_linearized);
}

Project: samba
File: ldb_dn.c

int ldb_dn_get_comp_num(struct ldb_dn *dn)
{
	if ( ! ldb_dn_validate(dn)) {
		return -1;
	}
	return dn->comp_num;
}

Project: samba
File: ldb_dn.c

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;
}

Project: samba
File: ldb_dn.c

int ldb_dn_get_extended_comp_num(struct ldb_dn *dn)
{
	if ( ! ldb_dn_validate(dn)) {
		return -1;
	}
	return dn->ext_comp_num;
}

Project: samba
File: ldb_dn.c

const struct ldb_val *ldb_dn_get_extended_component(struct ldb_dn *dn,
						    const char *name)
{
	unsigned int i;
	if ( ! ldb_dn_validate(dn)) {
		return NULL;
	}
	for (i=0; i < dn->ext_comp_num; i++) {
		if (ldb_attr_cmp(dn->ext_components[i].name, name) == 0) {
			return &dn->ext_components[i].value;
		}
	}
	return NULL;
}

Project: samba
File: ldb_dn.c

char *ldb_dn_get_extended_linearized(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, int mode)
{
	const char *linearized = ldb_dn_get_linearized(dn);
	char *p = NULL;
	unsigned int i;

	if (!linearized) {
		return NULL;
	}

	if (!ldb_dn_has_extended(dn)) {
		return talloc_strdup(mem_ctx, linearized);
	}

	if (!ldb_dn_validate(dn)) {
		return NULL;
	}

	/* sort the extended components by name. The idea is to make
	 * the resulting DNs consistent, plus to ensure that we put
	 * 'DELETED' first, so it can be very quickly recognised
	 */
	TYPESAFE_QSORT(dn->ext_components, dn->ext_comp_num,
		       ldb_dn_extended_component_compare);

	for (i = 0; i < dn->ext_comp_num; i++) {
		const struct ldb_dn_extended_syntax *ext_syntax;
		const char *name = dn->ext_components[i].name;
		struct ldb_val ec_val = dn->ext_components[i].value;
		struct ldb_val val;
		int ret;

		ext_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, name);
		if (!ext_syntax) {
			return NULL;
		}

		if (mode == 1) {
			ret = ext_syntax->write_clear_fn(dn->ldb, mem_ctx,
							&ec_val, &val);
		} else if (mode == 0) {
			ret = ext_syntax->write_hex_fn(dn->ldb, mem_ctx,
							&ec_val, &val);
		} else {
			ret = -1;
		}

		if (ret != LDB_SUCCESS) {
			return NULL;
		}

		if (i == 0) {
			p = talloc_asprintf(mem_ctx, "<%s=%s>", 
					    name, val.data);
		} else {
			p = talloc_asprintf_append_buffer(p, ";<%s=%s>",
							  name, val.data);
		}

		talloc_free(val.data);

		if (!p) {
			return NULL;
		}
	}

	if (dn->ext_comp_num && *linearized) {
		p = talloc_asprintf_append_buffer(p, ";%s", linearized);
	}

	if (!p) {
		return NULL;
	}

	return p;
}

Project: samba
File: ldb_dn.c

struct ldb_context *ldb_dn_get_ldb_context(struct ldb_dn *dn)
{
	return dn->ldb;
}

Project: samba
File: ldb_dn.c

const char *ldb_dn_get_linearized(struct ldb_dn *dn)
{
	unsigned int i;
	size_t len;
	char *d, *n;

	if ( ! dn || ( dn->invalid)) return NULL;

	if (dn->linearized) return dn->linearized;

	if ( ! dn->components) {
		ldb_dn_mark_invalid(dn);
		return NULL;
	}

	if (dn->comp_num == 0) {
		dn->linearized = talloc_strdup(dn, "");
		if ( ! dn->linearized) return NULL;
		return dn->linearized;
	}

	/* calculate maximum possible length of DN */
	for (len = 0, i = 0; i < dn->comp_num; i++) {
		/* name len */
		len += strlen(dn->components[i].name);
		/* max escaped data len */
		len += (dn->components[i].value.length * 3);
		len += 2; /* '=' and ',' */
	}
	dn->linearized = talloc_array(dn, char, len);
	if ( ! dn->linearized) return NULL;

	d = dn->linearized;

	for (i = 0; i < dn->comp_num; i++) {

		/* copy the name */
		n = dn->components[i].name;
		while (*n) *d++ = *n++;

		*d++ = '=';

		/* and the value */
		d += ldb_dn_escape_internal( d,
				(char *)dn->components[i].value.data,
				dn->components[i].value.length);
		*d++ = ',';
	}

	*(--d) = '\0';

	/* don't waste more memory than necessary */
	dn->linearized = talloc_realloc(dn, dn->linearized,
					char, (d - dn->linearized + 1));

	return dn->linearized;
}

Project: samba
File: ldb_dn.c

struct ldb_dn *ldb_dn_get_parent(TALLOC_CTX *mem_ctx, struct ldb_dn *dn)
{
	struct ldb_dn *new_dn;

	new_dn = ldb_dn_copy(mem_ctx, dn);
	if ( !new_dn ) {
		return NULL;
	}

	if ( ! ldb_dn_remove_child_components(new_dn, 1)) {
		talloc_free(new_dn);
		return NULL;
	}

	return new_dn;
}

Project: samba
File: ldb_dn.c

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;
}

Project: samba
File: ldb_dn.c

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;
}

Project: samba
File: ldb_dn.c

bool ldb_dn_has_extended(struct ldb_dn *dn)
{
	if ( ! dn || dn->invalid) return false;
	if (dn->ext_linearized && (dn->ext_linearized[0] == '<')) return true;
	return dn->ext_comp_num != 0;
}

Project: samba
File: ldb_dn.c

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;
}

Project: samba
File: ldb_dn.c

bool ldb_dn_is_special(struct ldb_dn *dn)
{
	if ( ! dn || dn->invalid) return false;
	return dn->special;
}

Project: samba
File: ldb_dn.c

bool ldb_dn_is_valid(struct ldb_dn *dn)
{
	if ( ! dn) return false;
	return ! dn->invalid;
}

Project: samba
File: ldb_dn.c

bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num)
{
	unsigned int i;

	if ( ! ldb_dn_validate(dn)) {
		return false;
	}

	if (dn->comp_num < num) {
		return false;
	}

	/* free components */
	for (i = dn->comp_num - num; i < dn->comp_num; i++) {
		LDB_FREE(dn->components[i].name);
		LDB_FREE(dn->components[i].value.data);
		LDB_FREE(dn->components[i].cf_name);
		LDB_FREE(dn->components[i].cf_value.data);
	}

	dn->comp_num -= num;

	if (dn->valid_case) {
		for (i = 0; i < dn->comp_num; i++) {
			LDB_FREE(dn->components[i].cf_name);
			LDB_FREE(dn->components[i].cf_value.data);
		}
		dn->valid_case = false;
	}

	LDB_FREE(dn->casefold);
	LDB_FREE(dn->linearized);

	/* Wipe the ext_linearized DN,
	 * the GUID and SID are almost certainly no longer valid */
	LDB_FREE(dn->ext_linearized);
	LDB_FREE(dn->ext_components);
	dn->ext_comp_num = 0;

	return true;
}

Project: samba
File: ldb_dn.c

bool ldb_dn_remove_child_components(struct ldb_dn *dn, unsigned int num)
{
	unsigned int i, j;

	if ( ! ldb_dn_validate(dn)) {
		return false;
	}

	if (dn->comp_num < num) {
		return false;
	}

	for (i = 0, j = num; j < dn->comp_num; i++, j++) {
		if (i < num) {
			LDB_FREE(dn->components[i].name);
			LDB_FREE(dn->components[i].value.data);
			LDB_FREE(dn->components[i].cf_name);
			LDB_FREE(dn->components[i].cf_value.data);
		}
		dn->components[i] = dn->components[j];
	}

	dn->comp_num -= num;

	if (dn->valid_case) {
		for (i = 0; i < dn->comp_num; i++) {
			LDB_FREE(dn->components[i].cf_name);
			LDB_FREE(dn->components[i].cf_value.data);
		}
		dn->valid_case = false;
	}

	LDB_FREE(dn->casefold);
	LDB_FREE(dn->linearized);

	/* Wipe the ext_linearized DN,
	 * the GUID and SID are almost certainly no longer valid */
	LDB_FREE(dn->ext_linearized);
	LDB_FREE(dn->ext_components);
	dn->ext_comp_num = 0;

	return true;
}

Project: samba
File: ldb_dn.c

void ldb_dn_remove_extended_components(struct ldb_dn *dn)
{
	LDB_FREE(dn->ext_linearized);
	LDB_FREE(dn->ext_components);
	dn->ext_comp_num = 0;
}

Project: samba
File: ldb_dn.c

bool ldb_dn_replace_components(struct ldb_dn *dn, struct ldb_dn *new_dn)
{
	int i;

	if ( ! ldb_dn_validate(dn) || ! ldb_dn_validate(new_dn)) {
		return false;
	}

	/* free components */
	for (i = 0; i < dn->comp_num; i++) {
		LDB_FREE(dn->components[i].name);
		LDB_FREE(dn->components[i].value.data);
		LDB_FREE(dn->components[i].cf_name);
		LDB_FREE(dn->components[i].cf_value.data);
	}

	dn->components = talloc_realloc(dn,
					dn->components,
					struct ldb_dn_component,
					new_dn->comp_num);
	if (dn->components == NULL) {
		ldb_dn_mark_invalid(dn);
		return false;
	}

	dn->comp_num = new_dn->comp_num;
	dn->valid_case = new_dn->valid_case;

	for (i = 0; i < dn->comp_num; i++) {
		dn->components[i] = ldb_dn_copy_component(dn->components, &new_dn->components[i]);
		if (dn->components[i].name == NULL) {
			ldb_dn_mark_invalid(dn);
			return false;
		}
	}
	if (new_dn->linearized == NULL) {
		dn->linearized = NULL;
	} else {
		dn->linearized = talloc_strdup(dn, new_dn->linearized);
		if (dn->linearized == NULL) {
			ldb_dn_mark_invalid(dn);
			return false;
		}
	}

	return true;
}

Project: samba
File: ldb_dn.c

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) {
		unsigned int i;
		for (i = 0; i < dn->comp_num; i++) {
			LDB_FREE(dn->components[i].cf_name);
			LDB_FREE(dn->components[i].cf_value.data);
		}
		dn->valid_case = false;
	}
	LDB_FREE(dn->casefold);
	LDB_FREE(dn->linearized);

	/* Wipe the ext_linearized DN,
	 * the GUID and SID are almost certainly no longer valid */
	LDB_FREE(dn->ext_linearized);
	LDB_FREE(dn->ext_components);
	dn->ext_comp_num = 0;

	return LDB_SUCCESS;
}

Project: samba
File: ldb_dn.c

int ldb_dn_set_extended_component(struct ldb_dn *dn,
				  const char *name, const struct ldb_val *val)
{
	struct ldb_dn_ext_component *p;
	unsigned int i;
	struct ldb_val v2;

	if ( ! ldb_dn_validate(dn)) {
		return LDB_ERR_OTHER;
	}

	if (!ldb_dn_extended_syntax_by_name(dn->ldb, name)) {
		/* We don't know how to handle this type of thing */
		return LDB_ERR_INVALID_DN_SYNTAX;
	}

	for (i=0; i < dn->ext_comp_num; i++) {
		if (ldb_attr_cmp(dn->ext_components[i].name, name) == 0) {
			if (val) {
				dn->ext_components[i].value =
					ldb_val_dup(dn->ext_components, val);

				dn->ext_components[i].name =
					talloc_strdup(dn->ext_components, name);
				if (!dn->ext_components[i].name ||
				    !dn->ext_components[i].value.data) {
					ldb_dn_mark_invalid(dn);
					return LDB_ERR_OPERATIONS_ERROR;
				}
			} else {
				if (i != (dn->ext_comp_num - 1)) {
					memmove(&dn->ext_components[i],
						&dn->ext_components[i+1],
						((dn->ext_comp_num-1) - i) *
						  sizeof(*dn->ext_components));
				}
				dn->ext_comp_num--;

				dn->ext_components = talloc_realloc(dn,
						   dn->ext_components,
						   struct ldb_dn_ext_component,
						   dn->ext_comp_num);
				if (!dn->ext_components) {
					ldb_dn_mark_invalid(dn);
					return LDB_ERR_OPERATIONS_ERROR;
				}
			}
			LDB_FREE(dn->ext_linearized);

			return LDB_SUCCESS;
		}
	}

	if (val == NULL) {
		/* removing a value that doesn't exist is not an error */
		return LDB_SUCCESS;
	}

	v2 = *val;

	p = dn->ext_components
		= talloc_realloc(dn,
				 dn->ext_components,
				 struct ldb_dn_ext_component,
				 dn->ext_comp_num + 1);
	if (!dn->ext_components) {
		ldb_dn_mark_invalid(dn);
		return LDB_ERR_OPERATIONS_ERROR;
	}

	p[dn->ext_comp_num].value = ldb_val_dup(dn->ext_components, &v2);
	p[dn->ext_comp_num].name = talloc_strdup(p, name);

	if (!dn->ext_components[i].name || !dn->ext_components[i].value.data) {
		ldb_dn_mark_invalid(dn);
		return LDB_ERR_OPERATIONS_ERROR;
	}
	dn->ext_components = p;
	dn->ext_comp_num++;

	LDB_FREE(dn->ext_linearized);

	return LDB_SUCCESS;
}