[SCM] Samba Shared Repository - branch master updated

Simo Sorce idra at samba.org
Tue Mar 9 13:24:23 MST 2010


The branch, master has been updated
       via  c05d13d... s4:ldb fix escape parsing
       via  9f53820... s3:tldap add own filter parsing
      from  2ee3cca... s4:winbind - use "unsigned" variables where possible

http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit c05d13d3c2c5d516c55cec133ba635f528034862
Author: Simo Sorce <idra at samba.org>
Date:   Sun Mar 7 20:20:45 2010 -0500

    s4:ldb fix escape parsing
    
    sscanf can return also on short reads, in this case an invalid escape
    sequence like '\1k' would be accepted, returning 1 as value and swallowing the
    'k'. Use an auxiliar function to validate and convert hex escapes.

commit 9f53820de731ca1a7f06341958b43fcfccf82600
Author: Simo Sorce <idra at samba.org>
Date:   Sun Mar 7 20:20:02 2010 -0500

    s3:tldap add own filter parsing
    
    Also add torture test to check filter parsing.

-----------------------------------------------------------------------

Summary of changes:
 source3/lib/tldap.c                |  714 ++++++++++++++++++++++++++++++------
 source3/torture/torture.c          |   14 +
 source4/lib/ldb/common/ldb_parse.c |   26 ++-
 3 files changed, 638 insertions(+), 116 deletions(-)


Changeset truncated at 500 lines:

diff --git a/source3/lib/tldap.c b/source3/lib/tldap.c
index fa56763..3b256c4 100644
--- a/source3/lib/tldap.c
+++ b/source3/lib/tldap.c
@@ -956,175 +956,654 @@ int tldap_simple_bind(struct tldap_context *ld, const char *dn,
 
 /*****************************************************************************/
 
-/*
- * This piece has a dependency on ldb, the ldb_parse_tree() function is used.
- * In case we want to separate out tldap, we need to copy or rewrite it.
+/* can't use isalpha() as only a strict set is valid for LDAP */
+#define TLDAP_IS_ALPHA(c) ((((c) >= 'a') && ((c) <= 'z')) || \
+			   (((c) >= 'A') && ((c) <= 'Z')))
+
+#define TLDAP_IS_ADH(c) (TLDAP_IS_ALPHA(c) || isdigit(c) || (c) == '-')
+
+#define TLDAP_FILTER_AND  ASN1_CONTEXT(0)
+#define TLDAP_FILTER_OR   ASN1_CONTEXT(1)
+#define TLDAP_FILTER_NOT  ASN1_CONTEXT(2)
+#define TLDAP_FILTER_EQ   ASN1_CONTEXT(3)
+#define TLDAP_FILTER_SUB  ASN1_CONTEXT(4)
+#define TLDAP_FILTER_LE   ASN1_CONTEXT(5)
+#define TLDAP_FILTER_GE   ASN1_CONTEXT(6)
+#define TLDAP_FILTER_PRES ASN1_CONTEXT_SIMPLE(7)
+#define TLDAP_FILTER_APX  ASN1_CONTEXT(8)
+#define TLDAP_FILTER_EXT  ASN1_CONTEXT(9)
+
+#define TLDAP_SUB_INI ASN1_CONTEXT_SIMPLE(0)
+#define TLDAP_SUB_ANY ASN1_CONTEXT_SIMPLE(1)
+#define TLDAP_SUB_FIN ASN1_CONTEXT_SIMPLE(2)
+
+
+/* oid's should be numerical only in theory,
+ * but apparently some broken servers may have alphanum aliases instead.
+ * Do like openldap libraries and allow alphanum aliases for oids, but
+ * do not allow Tagging options in that case.
  */
+static bool tldap_is_attrdesc(const char *s, int len, bool no_tagopts)
+{
+	bool is_oid = false;
+	bool dot = false;
+	int i;
+
+	/* first char has stricter rules */
+	if (isdigit(*s)) {
+		is_oid = true;
+	} else if (!TLDAP_IS_ALPHA(*s)) {
+		/* bad first char */
+		return false;
+	}
+
+	for (i = 1; i < len; i++) {
+
+		if (is_oid) {
+			if (isdigit(s[i])) {
+				dot = false;
+				continue;
+			}
+			if (s[i] == '.') {
+				if (dot) {
+					/* malformed */
+					return false;
+				}
+				dot = true;
+				continue;
+			}
+		} else {
+			if (TLDAP_IS_ADH(s[i])) {
+				continue;
+			}
+		}
+
+		if (s[i] == ';') {
+			if (no_tagopts) {
+				/* no tagging options */
+				return false;
+			}
+			if (dot) {
+				/* malformed */
+				return false;
+			}
+			if ((i + 1) == len) {
+				/* malformed */
+				return false;
+			}
 
-#include "lib/ldb/include/ldb.h"
-#include "lib/ldb/include/ldb_errors.h"
+			is_oid = false;
+			continue;
+		}
+	}
 
-static bool ldap_push_filter(struct asn1_data *data,
-			     struct ldb_parse_tree *tree)
+	if (dot) {
+		/* malformed */
+		return false;
+	}
+
+	return true;
+}
+
+/* this function copies the value until the closing parenthesis is found. */
+static char *tldap_get_val(TALLOC_CTX *memctx,
+			   const char *value, const char **_s)
 {
-	int i;
+	const char *s = value;
 
-	switch (tree->operation) {
-	case LDB_OP_AND:
-	case LDB_OP_OR:
-		asn1_push_tag(data,
-			      ASN1_CONTEXT(tree->operation==LDB_OP_AND?0:1));
-		for (i=0; i<tree->u.list.num_elements; i++) {
-			if (!ldap_push_filter(data,
-					      tree->u.list.elements[i])) {
+	/* find terminator */
+	while (*s) {
+		s = strchr(s, ')');
+		if (s && (*(s - 1) == '\\')) {
+			continue;
+		}
+		break;
+	}
+	if (!s || !(*s == ')')) {
+		/* malformed filter */
+		return NULL;
+	}
+
+	*_s = s;
+
+	return talloc_strndup(memctx, value, s - value);
+}
+
+static int tldap_hex2char(const char *x)
+{
+	if (isxdigit(x[0]) && isxdigit(x[1])) {
+		const char h1 = x[0], h2 = x[1];
+		int c;
+
+		if (h1 >= 'a') c = h1 - (int)'a' + 10;
+		else if (h1 >= 'A') c = h1 - (int)'A' + 10;
+		else if (h1 >= '0') c = h1 - (int)'0';
+		c = c << 4;
+		if (h2 >= 'a') c += h2 - (int)'a' + 10;
+		else if (h1 >= 'A') c += h2 - (int)'A' + 10;
+		else if (h1 >= '0') c += h2 - (int)'0';
+
+		return c;
+	}
+
+	return -1;
+}
+
+static bool tldap_find_first_star(const char *val, const char **star)
+{
+	const char *s;
+
+	for (s = val; *s; s++) {
+		switch (*s) {
+		case '\\':
+			if (isxdigit(s[1]) && isxdigit(s[2])) {
+				s += 2;
+				break;
+			}
+			/* not hex based escape, check older syntax */
+			switch (s[1]) {
+			case '(':
+			case ')':
+			case '*':
+			case '\\':
+				s++;
+				break;
+			default:
+				/* invalid escape sequence */
 				return false;
 			}
+			break;
+		case ')':
+			/* end of val, nothing found */
+			*star = s;
+			return true;
+
+		case '*':
+			*star = s;
+			return true;
 		}
-		asn1_pop_tag(data);
+	}
+
+	/* string ended without closing parenthesis, filter is malformed */
+	return false;
+}
+
+static bool tldap_unescape_inplace(char *value, size_t *val_len)
+{
+	int c, i, p;
+
+	for (i = 0,p = 0; i < *val_len; i++) {
+
+		switch (value[i]) {
+		case '(':
+		case ')':
+		case '*':
+			/* these must be escaped */
+			return false;
+
+		case '\\':
+			if (!value[i + 1]) {
+				/* invalid EOL */
+				return false;
+			}
+			i++;
+
+			c = tldap_hex2char(&value[i]);
+			if (c >= 0 && c < 256) {
+				value[p] = c;
+				i++;
+				p++;
+				break;
+			}
+
+			switch (value[i]) {
+			case '(':
+			case ')':
+			case '*':
+			case '\\':
+				value[p] = value[i];
+				p++;
+			default:
+				/* invalid */
+				return false;
+			}
+			break;
+
+		default:
+			value[p] = value[i];
+			p++;
+		}
+	}
+	value[p] = '\0';
+	*val_len = p;
+	return true;
+}
+
+static bool tldap_push_filter_basic(struct tldap_context *ld,
+				    struct asn1_data *data,
+				    const char **_s);
+static bool tldap_push_filter_substring(struct tldap_context *ld,
+					struct asn1_data *data,
+					const char *val,
+					const char **_s);
+static bool tldap_push_filter_int(struct tldap_context *ld,
+				  struct asn1_data *data,
+				  const char **_s)
+{
+	const char *s = *_s;
+	bool ret;
+
+	if (*s != '(') {
+		tldap_debug(ld, TLDAP_DEBUG_ERROR,
+			    "Incomplete or malformed filter\n");
+		return false;
+	}
+	s++;
+
+	/* we are right after a parenthesis,
+	 * find out what op we have at hand */
+	switch (*s) {
+	case '&':
+		tldap_debug(ld, TLDAP_DEBUG_TRACE, "Filter op: AND\n");
+		asn1_push_tag(data, TLDAP_FILTER_AND);
+		s++;
 		break;
 
-	case LDB_OP_NOT:
-		asn1_push_tag(data, ASN1_CONTEXT(2));
-		if (!ldap_push_filter(data, tree->u.isnot.child)) {
+	case '|':
+		tldap_debug(ld, TLDAP_DEBUG_TRACE, "Filter op: OR\n");
+		asn1_push_tag(data, TLDAP_FILTER_OR);
+		s++;
+		break;
+
+	case '!':
+		tldap_debug(ld, TLDAP_DEBUG_TRACE, "Filter op: NOT\n");
+		asn1_push_tag(data, TLDAP_FILTER_NOT);
+		s++;
+		ret = tldap_push_filter_int(ld, data, &s);
+		if (!ret) {
 			return false;
 		}
 		asn1_pop_tag(data);
-		break;
+		goto done;
+
+	case '(':
+	case ')':
+		tldap_debug(ld, TLDAP_DEBUG_ERROR,
+			    "Invalid parenthesis '%c'\n", *s);
+		return false;
+
+	case '\0':
+		tldap_debug(ld, TLDAP_DEBUG_ERROR,
+			    "Invalid filter termination\n");
+		return false;
 
-	case LDB_OP_EQUALITY:
-		/* equality test */
-		asn1_push_tag(data, ASN1_CONTEXT(3));
-		asn1_write_OctetString(data, tree->u.equality.attr,
-				      strlen(tree->u.equality.attr));
-		asn1_write_OctetString(data, tree->u.equality.value.data,
-				      tree->u.equality.value.length);
+	default:
+		ret = tldap_push_filter_basic(ld, data, &s);
+		if (!ret) {
+			return false;
+		}
+		goto done;
+	}
+
+	/* only and/or filters get here.
+	 * go through the list of filters */
+
+	if (*s == ')') {
+		/* RFC 4526: empty and/or */
 		asn1_pop_tag(data);
-		break;
+		goto done;
+	}
 
-	case LDB_OP_SUBSTRING:
-		/*
-		  SubstringFilter ::= SEQUENCE {
-			  type            AttributeDescription,
-			  -- at least one must be present
-			  substrings      SEQUENCE OF CHOICE {
-				  initial [0] LDAPString,
-				  any     [1] LDAPString,
-				  final   [2] LDAPString } }
-		*/
-		asn1_push_tag(data, ASN1_CONTEXT(4));
-		asn1_write_OctetString(data, tree->u.substring.attr,
-				       strlen(tree->u.substring.attr));
-		asn1_push_tag(data, ASN1_SEQUENCE(0));
-		i = 0;
-		if (!tree->u.substring.start_with_wildcard) {
-			asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(0));
-			asn1_write_DATA_BLOB_LDAPString(
-				data, tree->u.substring.chunks[i]);
-			asn1_pop_tag(data);
-			i++;
+	while (*s) {
+		ret = tldap_push_filter_int(ld, data, &s);
+		if (!ret) {
+			return false;
 		}
-		while (tree->u.substring.chunks[i]) {
-			int ctx;
 
-			if ((!tree->u.substring.chunks[i + 1]) &&
-			    (tree->u.substring.end_with_wildcard == 0)) {
-				ctx = 2;
-			} else {
-				ctx = 1;
-			}
-			asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(ctx));
-			asn1_write_DATA_BLOB_LDAPString(
-				data, tree->u.substring.chunks[i]);
+		if (*s == ')') {
+			/* end of list, return */
 			asn1_pop_tag(data);
-			i++;
+			break;
 		}
-		asn1_pop_tag(data);
-		asn1_pop_tag(data);
-		break;
+	}
 
-	case LDB_OP_GREATER:
-		/* greaterOrEqual test */
-		asn1_push_tag(data, ASN1_CONTEXT(5));
-		asn1_write_OctetString(data, tree->u.comparison.attr,
-				      strlen(tree->u.comparison.attr));
-		asn1_write_OctetString(data, tree->u.comparison.value.data,
-				      tree->u.comparison.value.length);
-		asn1_pop_tag(data);
+done:
+	if (*s != ')') {
+		tldap_debug(ld, TLDAP_DEBUG_ERROR,
+			    "Incomplete or malformed filter\n");
+		return false;
+	}
+	s++;
+
+	if (data->has_error) {
+		return false;
+	}
+
+	*_s = s;
+	return true;
+}
+
+
+static bool tldap_push_filter_basic(struct tldap_context *ld,
+				    struct asn1_data *data,
+				    const char **_s)
+{
+	TALLOC_CTX *tmpctx = talloc_tos();
+	const char *s = *_s;
+	const char *e;
+	const char *eq;
+	const char *val;
+	const char *type;
+	const char *dn;
+	const char *rule;
+	const char *star;
+	size_t type_len;
+	char *uval;
+	size_t uval_len;
+	bool write_octect = true;
+	bool ret;
+
+	eq = strchr(s, '=');
+	if (!eq) {
+		tldap_debug(ld, TLDAP_DEBUG_ERROR,
+			    "Invalid filter, missing equal sign\n");
+		return false;
+	}
+
+	val = eq + 1;
+	e = eq - 1;
+
+	switch (*e) {
+	case '<':
+		asn1_push_tag(data, TLDAP_FILTER_LE);
 		break;
 
-	case LDB_OP_LESS:
-		/* lessOrEqual test */
-		asn1_push_tag(data, ASN1_CONTEXT(6));
-		asn1_write_OctetString(data, tree->u.comparison.attr,
-				      strlen(tree->u.comparison.attr));
-		asn1_write_OctetString(data, tree->u.comparison.value.data,
-				      tree->u.comparison.value.length);
-		asn1_pop_tag(data);
+	case '>':
+		asn1_push_tag(data, TLDAP_FILTER_GE);
 		break;
 
-	case LDB_OP_PRESENT:
-		/* present test */
-		asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(7));
-		asn1_write_LDAPString(data, tree->u.present.attr);
-		asn1_pop_tag(data);
-		return !data->has_error;
-
-	case LDB_OP_APPROX:
-		/* approx test */
-		asn1_push_tag(data, ASN1_CONTEXT(8));
-		asn1_write_OctetString(data, tree->u.comparison.attr,
-				      strlen(tree->u.comparison.attr));
-		asn1_write_OctetString(data, tree->u.comparison.value.data,
-				      tree->u.comparison.value.length);
-		asn1_pop_tag(data);
+	case '~':
+		asn1_push_tag(data, TLDAP_FILTER_APX);
 		break;
 
-	case LDB_OP_EXTENDED:
+	case ':':
+		asn1_push_tag(data, TLDAP_FILTER_EXT);
+		write_octect = false;
+
+		type = NULL;
+		dn = NULL;
+		rule = NULL;
+
+		if (*s == ':') { /* [:dn]:rule:= value */
+			if (s == e) {
+				/* malformed filter */
+				return false;
+			}
+			dn = s;
+		} else { /* type[:dn][:rule]:= value */
+			type = s;
+			dn = strchr(s, ':');
+			type_len = dn - type;
+			if (dn == e) { /* type:= value */
+				dn = NULL;
+			}
+		}
+		if (dn) {
+			dn++;
+


-- 
Samba Shared Repository


More information about the samba-cvs mailing list