summaryrefslogtreecommitdiff
path: root/lib/ldb/common/ldb_parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ldb/common/ldb_parse.c')
-rw-r--r--lib/ldb/common/ldb_parse.c72
1 files changed, 58 insertions, 14 deletions
diff --git a/lib/ldb/common/ldb_parse.c b/lib/ldb/common/ldb_parse.c
index 452c5830ed5..7e15206b168 100644
--- a/lib/ldb/common/ldb_parse.c
+++ b/lib/ldb/common/ldb_parse.c
@@ -43,6 +43,16 @@
#include "ldb_private.h"
#include "system/locale.h"
+/*
+ * Maximum depth of the filter parse tree, the value chosen is small enough to
+ * avoid triggering ASAN stack overflow checks. But large enough to be useful.
+ *
+ * On Windows clients the maximum number of levels of recursion allowed is 100.
+ * In the LDAP server, Windows restricts clients to 512 nested
+ * (eg) OR statements.
+ */
+#define LDB_MAX_PARSE_TREE_DEPTH 128
+
static int ldb_parse_hex2char(const char *x)
{
if (isxdigit(x[0]) && isxdigit(x[1])) {
@@ -231,7 +241,11 @@ static struct ldb_val **ldb_wildcard_decode(TALLOC_CTX *mem_ctx, const char *str
return ret;
}
-static struct ldb_parse_tree *ldb_parse_filter(TALLOC_CTX *mem_ctx, const char **s);
+static struct ldb_parse_tree *ldb_parse_filter(
+ TALLOC_CTX *mem_ctx,
+ const char **s,
+ unsigned depth,
+ unsigned max_depth);
/*
@@ -498,7 +512,11 @@ static struct ldb_parse_tree *ldb_parse_simple(TALLOC_CTX *mem_ctx, const char *
<or> ::= '|' <filterlist>
<filterlist> ::= <filter> | <filter> <filterlist>
*/
-static struct ldb_parse_tree *ldb_parse_filterlist(TALLOC_CTX *mem_ctx, const char **s)
+static struct ldb_parse_tree *ldb_parse_filterlist(
+ TALLOC_CTX *mem_ctx,
+ const char **s,
+ unsigned depth,
+ unsigned max_depth)
{
struct ldb_parse_tree *ret, *next;
enum ldb_parse_op op;
@@ -533,7 +551,8 @@ static struct ldb_parse_tree *ldb_parse_filterlist(TALLOC_CTX *mem_ctx, const ch
return NULL;
}
- ret->u.list.elements[0] = ldb_parse_filter(ret->u.list.elements, &p);
+ ret->u.list.elements[0] =
+ ldb_parse_filter(ret->u.list.elements, &p, depth, max_depth);
if (!ret->u.list.elements[0]) {
talloc_free(ret);
return NULL;
@@ -547,7 +566,8 @@ static struct ldb_parse_tree *ldb_parse_filterlist(TALLOC_CTX *mem_ctx, const ch
break;
}
- next = ldb_parse_filter(ret->u.list.elements, &p);
+ next = ldb_parse_filter(
+ ret->u.list.elements, &p, depth, max_depth);
if (next == NULL) {
/* an invalid filter element */
talloc_free(ret);
@@ -576,7 +596,11 @@ static struct ldb_parse_tree *ldb_parse_filterlist(TALLOC_CTX *mem_ctx, const ch
/*
<not> ::= '!' <filter>
*/
-static struct ldb_parse_tree *ldb_parse_not(TALLOC_CTX *mem_ctx, const char **s)
+static struct ldb_parse_tree *ldb_parse_not(
+ TALLOC_CTX *mem_ctx,
+ const char **s,
+ unsigned depth,
+ unsigned max_depth)
{
struct ldb_parse_tree *ret;
const char *p = *s;
@@ -593,7 +617,7 @@ static struct ldb_parse_tree *ldb_parse_not(TALLOC_CTX *mem_ctx, const char **s)
}
ret->operation = LDB_OP_NOT;
- ret->u.isnot.child = ldb_parse_filter(ret, &p);
+ ret->u.isnot.child = ldb_parse_filter(ret, &p, depth, max_depth);
if (!ret->u.isnot.child) {
talloc_free(ret);
return NULL;
@@ -608,7 +632,11 @@ static struct ldb_parse_tree *ldb_parse_not(TALLOC_CTX *mem_ctx, const char **s)
parse a filtercomp
<filtercomp> ::= <and> | <or> | <not> | <simple>
*/
-static struct ldb_parse_tree *ldb_parse_filtercomp(TALLOC_CTX *mem_ctx, const char **s)
+static struct ldb_parse_tree *ldb_parse_filtercomp(
+ TALLOC_CTX *mem_ctx,
+ const char **s,
+ unsigned depth,
+ unsigned max_depth)
{
struct ldb_parse_tree *ret;
const char *p = *s;
@@ -617,15 +645,15 @@ static struct ldb_parse_tree *ldb_parse_filtercomp(TALLOC_CTX *mem_ctx, const ch
switch (*p) {
case '&':
- ret = ldb_parse_filterlist(mem_ctx, &p);
+ ret = ldb_parse_filterlist(mem_ctx, &p, depth, max_depth);
break;
case '|':
- ret = ldb_parse_filterlist(mem_ctx, &p);
+ ret = ldb_parse_filterlist(mem_ctx, &p, depth, max_depth);
break;
case '!':
- ret = ldb_parse_not(mem_ctx, &p);
+ ret = ldb_parse_not(mem_ctx, &p, depth, max_depth);
break;
case '(':
@@ -641,21 +669,34 @@ static struct ldb_parse_tree *ldb_parse_filtercomp(TALLOC_CTX *mem_ctx, const ch
return ret;
}
-
/*
<filter> ::= '(' <filtercomp> ')'
*/
-static struct ldb_parse_tree *ldb_parse_filter(TALLOC_CTX *mem_ctx, const char **s)
+static struct ldb_parse_tree *ldb_parse_filter(
+ TALLOC_CTX *mem_ctx,
+ const char **s,
+ unsigned depth,
+ unsigned max_depth)
{
struct ldb_parse_tree *ret;
const char *p = *s;
+ /*
+ * Check the depth of the parse tree, and reject the input if
+ * max_depth exceeded. This avoids stack overflow
+ * issues.
+ */
+ if (depth > max_depth) {
+ return NULL;
+ }
+ depth++;
+
if (*p != '(') {
return NULL;
}
p++;
- ret = ldb_parse_filtercomp(mem_ctx, &p);
+ ret = ldb_parse_filtercomp(mem_ctx, &p, depth, max_depth);
if (*p != ')') {
return NULL;
@@ -679,6 +720,8 @@ static struct ldb_parse_tree *ldb_parse_filter(TALLOC_CTX *mem_ctx, const char *
*/
struct ldb_parse_tree *ldb_parse_tree(TALLOC_CTX *mem_ctx, const char *s)
{
+ unsigned depth = 0;
+
while (s && isspace((unsigned char)*s)) s++;
if (s == NULL || *s == 0) {
@@ -686,7 +729,8 @@ struct ldb_parse_tree *ldb_parse_tree(TALLOC_CTX *mem_ctx, const char *s)
}
if (*s == '(') {
- return ldb_parse_filter(mem_ctx, &s);
+ return ldb_parse_filter(
+ mem_ctx, &s, depth, LDB_MAX_PARSE_TREE_DEPTH);
}
return ldb_parse_simple(mem_ctx, &s);