Ian Kent 3685ec
From jmoyer@redhat.com Thu Jul 17 10:12:13 2008
Ian Kent 3685ec
Ian Kent 3685ec
From: Ian Kent <raven@themaw.net>
Ian Kent 3685ec
Ian Kent 3685ec
Hi, Ian,
Ian Kent 3685ec
Ian Kent 3685ec
As I mentioned, the encode and decode routines for the % hack were
Ian Kent 3685ec
corrupting heap space.  I put together a patch to fix things as they
Ian Kent 3685ec
stand today.  The comments document the assumptions I made.
Ian Kent 3685ec
Ian Kent 3685ec
I tested this code by compiling a program that only included
Ian Kent 3685ec
these functions and passing them arbitrary input and validating the
Ian Kent 3685ec
reuslts.  I then compiled them into the automounter and verified that
Ian Kent 3685ec
no heap corruption occurred (by running automount -f on a fedora
Ian Kent 3685ec
system where such things are reported).
Ian Kent 3685ec
Ian Kent 3685ec
Now, I don't think that we should ever have to encode the percent
Ian Kent 3685ec
hack.  Shouldn't we just need to decode it, walking through all of the
Ian Kent 3685ec
returned entries until we find an exact match for the key being looked
Ian Kent 3685ec
up?
Ian Kent 3685ec
Ian Kent 3685ec
Comments welcome.
Ian Kent 3685ec
Ian Kent 3685ec
IMK: You're right, but we'll keep it for the moment.
Ian Kent 3685ec
Ian Kent 3685ec
Cheers,
Ian Kent 3685ec
Jeff
Ian Kent 3685ec
---
Ian Kent 3685ec
Ian Kent 3685ec
 modules/lookup_ldap.c |  267 +++++++++++++++++++++++++++++++++++--------------
Ian Kent 3685ec
 1 files changed, 193 insertions(+), 74 deletions(-)
Ian Kent 3685ec
Ian Kent 3685ec
Ian Kent 3685ec
diff --git a/modules/lookup_ldap.c b/modules/lookup_ldap.c
Ian Kent 3685ec
index 7777e90..3990f8c 100644
Ian Kent 3685ec
--- a/modules/lookup_ldap.c
Ian Kent 3685ec
+++ b/modules/lookup_ldap.c
Ian Kent 3685ec
@@ -1512,6 +1512,65 @@ next:
Ian Kent 3685ec
 	return NSS_STATUS_SUCCESS;
Ian Kent 3685ec
 }
Ian Kent 3685ec
 
Ian Kent 3685ec
+static int get_percent_decoded_len(const char *name)
Ian Kent 3685ec
+{
Ian Kent 3685ec
+	int escapes = 0;
Ian Kent 3685ec
+	int escaped = 0;
Ian Kent 3685ec
+	char *tmp = name;
Ian Kent 3685ec
+	int look_for_close = 0;
Ian Kent 3685ec
+
Ian Kent 3685ec
+	while (*tmp) {
Ian Kent 3685ec
+		if (*tmp == '%') {
Ian Kent 3685ec
+			/* assume escapes aren't interpreted inside brackets */
Ian Kent 3685ec
+			if (look_for_close) {
Ian Kent 3685ec
+				tmp++;
Ian Kent 3685ec
+				continue;
Ian Kent 3685ec
+			}
Ian Kent 3685ec
+			/* check for escaped % */
Ian Kent 3685ec
+			if (escaped) {
Ian Kent 3685ec
+				tmp++;
Ian Kent 3685ec
+				escaped = 0;
Ian Kent 3685ec
+				continue;
Ian Kent 3685ec
+			}
Ian Kent 3685ec
+			escapes++;
Ian Kent 3685ec
+			tmp++;
Ian Kent 3685ec
+			if (*tmp == '[') {
Ian Kent 3685ec
+				escapes++;
Ian Kent 3685ec
+				tmp++;
Ian Kent 3685ec
+				look_for_close = 1;
Ian Kent 3685ec
+			} else
Ian Kent 3685ec
+				escaped = 1;
Ian Kent 3685ec
+		} else if (*tmp == ']' && look_for_close) {
Ian Kent 3685ec
+			escaped = 0;
Ian Kent 3685ec
+			escapes++;
Ian Kent 3685ec
+			tmp++;
Ian Kent 3685ec
+			look_for_close = 0;
Ian Kent 3685ec
+		} else {
Ian Kent 3685ec
+			tmp++;
Ian Kent 3685ec
+			escaped = 0;
Ian Kent 3685ec
+		}
Ian Kent 3685ec
+	}
Ian Kent 3685ec
+
Ian Kent 3685ec
+	assert(strlen(name) > escapes);
Ian Kent 3685ec
+	return strlen(name) - escapes;
Ian Kent 3685ec
+}
Ian Kent 3685ec
+
Ian Kent 3685ec
+/*
Ian Kent 3685ec
+ * Try to catch heap corruption if our logic happens to be incorrect.
Ian Kent 3685ec
+ */
Ian Kent 3685ec
+static void validate_string_len(const char *orig, char *start,
Ian Kent 3685ec
+				char *end, unsigned int len)
Ian Kent 3685ec
+{
Ian Kent 3685ec
+	debug(LOGOPT_NONE, MODPREFIX "string %s encoded as %s", orig, start);
Ian Kent 3685ec
+	/* make sure we didn't overflow the allocated space */
Ian Kent 3685ec
+	if (end - start > len + 1) {
Ian Kent 3685ec
+		crit(LOGOPT_ANY, MODPREFIX "orig %s, len %d", orig, len);
Ian Kent 3685ec
+		crit(LOGOPT_ANY, MODPREFIX "en/decoded %s, len %d", start,
Ian Kent 3685ec
+		     end - start);
Ian Kent 3685ec
+	}
Ian Kent 3685ec
+	assert(end-start <= len + 1);
Ian Kent 3685ec
+}
Ian Kent 3685ec
+
Ian Kent 3685ec
 /*
Ian Kent 3685ec
  * Deal with encode and decode of % hack.
Ian Kent 3685ec
  * Return
Ian Kent 3685ec
@@ -1519,131 +1578,191 @@ next:
Ian Kent 3685ec
  * -1 => syntax error or alloc fail.
Ian Kent 3685ec
  * 1 transofrmed value returned.
Ian Kent 3685ec
  */
Ian Kent 3685ec
+/*
Ian Kent 3685ec
+ * Assumptions: %'s must be escaped by %'s.  %'s are not used to escape
Ian Kent 3685ec
+ * anything else except capital letters (so you can't escape a closing
Ian Kent 3685ec
+ * bracket, for example).
Ian Kent 3685ec
+ */
Ian Kent 3685ec
 static int decode_percent_hack(const char *name, char **key)
Ian Kent 3685ec
 {
Ian Kent 3685ec
 	const char *tmp;
Ian Kent 3685ec
 	char *ptr, *new;
Ian Kent 3685ec
+	unsigned int len;
Ian Kent 3685ec
+	int escaped = 0, look_for_close = 0;
Ian Kent 3685ec
 
Ian Kent 3685ec
 	if (!key)
Ian Kent 3685ec
 		return -1;
Ian Kent 3685ec
 
Ian Kent 3685ec
 	*key = NULL;
Ian Kent 3685ec
 
Ian Kent 3685ec
-	tmp = name;
Ian Kent 3685ec
-	while (*tmp && *tmp != '%' && *tmp != '[' && *tmp != ']')
Ian Kent 3685ec
-		tmp++;
Ian Kent 3685ec
-	if (!*tmp)
Ian Kent 3685ec
-		return 0;
Ian Kent 3685ec
+	len = get_percent_decoded_len(name);
Ian Kent 3685ec
+	new = malloc(len + 1);
Ian Kent 3685ec
+	if (!new)
Ian Kent 3685ec
+		return -1;
Ian Kent 3685ec
 
Ian Kent 3685ec
+	ptr = new;
Ian Kent 3685ec
 	tmp = name;
Ian Kent 3685ec
 	while (*tmp) {
Ian Kent 3685ec
 		if (*tmp == '%') {
Ian Kent 3685ec
-			tmp++;
Ian Kent 3685ec
-			if (!*tmp)
Ian Kent 3685ec
-				return -1;
Ian Kent 3685ec
-			if (*tmp != '[')
Ian Kent 3685ec
+			if (escaped) {
Ian Kent 3685ec
+				*ptr++ = *tmp++;
Ian Kent 3685ec
+				if (!look_for_close)
Ian Kent 3685ec
+					escaped = 0;
Ian Kent 3685ec
 				continue;
Ian Kent 3685ec
+			}
Ian Kent 3685ec
 			tmp++;
Ian Kent 3685ec
-			while (*tmp && *tmp != ']') {
Ian Kent 3685ec
-				if (*tmp == '%')
Ian Kent 3685ec
-					tmp++;
Ian Kent 3685ec
+			if (*tmp == '[') {
Ian Kent 3685ec
 				tmp++;
Ian Kent 3685ec
-			}
Ian Kent 3685ec
-			if (!tmp)
Ian Kent 3685ec
-				return -1;
Ian Kent 3685ec
-		}
Ian Kent 3685ec
-		tmp++;
Ian Kent 3685ec
-	}
Ian Kent 3685ec
-
Ian Kent 3685ec
-	new = malloc(strlen(name) + 1);
Ian Kent 3685ec
-	if (!new)
Ian Kent 3685ec
-		return -1;
Ian Kent 3685ec
-
Ian Kent 3685ec
-	ptr = new;
Ian Kent 3685ec
-	tmp = name;
Ian Kent 3685ec
-	while (*tmp) {
Ian Kent 3685ec
-		if (*tmp == '%' || *tmp == '[' || *tmp == ']') {
Ian Kent 3685ec
+				look_for_close = 1;
Ian Kent 3685ec
+				escaped = 1;
Ian Kent 3685ec
+			} else
Ian Kent 3685ec
+				escaped = 1;
Ian Kent 3685ec
+		} else if (*tmp == ']' && look_for_close) {
Ian Kent 3685ec
 			tmp++;
Ian Kent 3685ec
-			if (*tmp && *tmp != '%')
Ian Kent 3685ec
-				continue;
Ian Kent 3685ec
+			look_for_close = 0;
Ian Kent 3685ec
+		} else {
Ian Kent 3685ec
+			escaped = 0;
Ian Kent 3685ec
+			*ptr++ = *tmp++;
Ian Kent 3685ec
 		}
Ian Kent 3685ec
-		*ptr++ = *tmp++;
Ian Kent 3685ec
 	}
Ian Kent 3685ec
 	*ptr = '\0';
Ian Kent 3685ec
-
Ian Kent 3685ec
 	*key = new;
Ian Kent 3685ec
 
Ian Kent 3685ec
+	validate_string_len(name, new, ptr, len);
Ian Kent 3685ec
 	return strlen(new);
Ian Kent 3685ec
 }
Ian Kent 3685ec
 
Ian Kent 3685ec
-static int encode_percent_hack(const char *name, char **key, unsigned int use_class)
Ian Kent 3685ec
+/*
Ian Kent 3685ec
+ * Calculate the length of a string replacing all capital letters with %letter.
Ian Kent 3685ec
+ * For example:
Ian Kent 3685ec
+ * Sale -> %Sale
Ian Kent 3685ec
+ * SALE -> %S%A%L%E
Ian Kent 3685ec
+ */
Ian Kent 3685ec
+static int get_encoded_len_escaping_every_cap(const char *name)
Ian Kent 3685ec
 {
Ian Kent 3685ec
 	const char *tmp;
Ian Kent 3685ec
-	unsigned int len = 0;
Ian Kent 3685ec
-	char *ptr, *new;
Ian Kent 3685ec
+	unsigned int escapes = 0; /* number of % escape characters */
Ian Kent 3685ec
 
Ian Kent 3685ec
-	if (!key)
Ian Kent 3685ec
-		return -1;
Ian Kent 3685ec
+	tmp = name;
Ian Kent 3685ec
+	while (*tmp) {
Ian Kent 3685ec
+		/* We'll need to escape percents */
Ian Kent 3685ec
+		if (*tmp == '%' || isupper(*tmp))
Ian Kent 3685ec
+			escapes++;
Ian Kent 3685ec
+		tmp++;
Ian Kent 3685ec
+	}
Ian Kent 3685ec
 
Ian Kent 3685ec
-	*key = NULL;
Ian Kent 3685ec
+	return strlen(name) + escapes;
Ian Kent 3685ec
+}
Ian Kent 3685ec
+
Ian Kent 3685ec
+/*
Ian Kent 3685ec
+ * Calculate the length of a string replacing sequences (1 or more) of capital
Ian Kent 3685ec
+ * letters with %[letters].  For example:
Ian Kent 3685ec
+ * FOO ->  %[FOO]
Ian Kent 3685ec
+ * Work -> %[W]ork
Ian Kent 3685ec
+ * WorksForMe -> %[W]orks%[F]or%[M]e
Ian Kent 3685ec
+ * aaBBaa -> aa%[BB]aa
Ian Kent 3685ec
+ */
Ian Kent 3685ec
+static int get_encoded_len_escaping_sequences(const char *name)
Ian Kent 3685ec
+{
Ian Kent 3685ec
+	const char *tmp;
Ian Kent 3685ec
+	unsigned int escapes = 0;
Ian Kent 3685ec
 
Ian Kent 3685ec
 	tmp = name;
Ian Kent 3685ec
 	while (*tmp) {
Ian Kent 3685ec
+		/* escape percents */
Ian Kent 3685ec
 		if (*tmp == '%')
Ian Kent 3685ec
-			len++;
Ian Kent 3685ec
+			escapes++;
Ian Kent 3685ec
 		else if (isupper(*tmp)) {
Ian Kent 3685ec
-			tmp++;
Ian Kent 3685ec
-			len++;
Ian Kent 3685ec
-			if (!use_class)
Ian Kent 3685ec
-				len++;
Ian Kent 3685ec
-			else {
Ian Kent 3685ec
-				if (*tmp && isupper(*tmp))
Ian Kent 3685ec
-					len += 2;
Ian Kent 3685ec
-				else
Ian Kent 3685ec
-					return 0;
Ian Kent 3685ec
-				while (*tmp && isupper(*tmp)) {
Ian Kent 3685ec
-					len++;
Ian Kent 3685ec
-					tmp++;
Ian Kent 3685ec
-				}
Ian Kent 3685ec
-			}
Ian Kent 3685ec
+			/* start an escape block %[...] */
Ian Kent 3685ec
+			escapes += 3;  /* %[] */
Ian Kent 3685ec
+			while (*tmp && isupper(*tmp))
Ian Kent 3685ec
+				tmp++;
Ian Kent 3685ec
 			continue;
Ian Kent 3685ec
 		}
Ian Kent 3685ec
-		len++;
Ian Kent 3685ec
 		tmp++;
Ian Kent 3685ec
 	}
Ian Kent 3685ec
-	if (len == strlen(name))
Ian Kent 3685ec
-		return 0;
Ian Kent 3685ec
 
Ian Kent 3685ec
-	new = malloc(len + 1);
Ian Kent 3685ec
-	if (!new)
Ian Kent 3685ec
-		return -1;
Ian Kent 3685ec
+	return strlen(name) + escapes;
Ian Kent 3685ec
+}
Ian Kent 3685ec
+
Ian Kent 3685ec
+static void encode_individual(const char *name, char *new, unsigned int len)
Ian Kent 3685ec
+{
Ian Kent 3685ec
+	const char *tmp;
Ian Kent 3685ec
+	char *ptr;
Ian Kent 3685ec
 
Ian Kent 3685ec
 	ptr = new;
Ian Kent 3685ec
 	tmp = name;
Ian Kent 3685ec
 	while (*tmp) {
Ian Kent 3685ec
-		if (*tmp == '%')
Ian Kent 3685ec
+		if (*tmp == '%' || isupper(*tmp))
Ian Kent 3685ec
 			*ptr++ = '%';
Ian Kent 3685ec
-		else if (isupper(*tmp)) {
Ian Kent 3685ec
-			char next = *tmp++;
Ian Kent 3685ec
+		*ptr++ = *tmp++;
Ian Kent 3685ec
+	}
Ian Kent 3685ec
+	*ptr = '\0';
Ian Kent 3685ec
+	validate_string_len(name, new, ptr, len);
Ian Kent 3685ec
+}
Ian Kent 3685ec
+
Ian Kent 3685ec
+static void encode_sequence(const char *name, char *new, unsigned int len)
Ian Kent 3685ec
+{
Ian Kent 3685ec
+	const char *tmp;
Ian Kent 3685ec
+	char *ptr;
Ian Kent 3685ec
+
Ian Kent 3685ec
+	ptr = new;
Ian Kent 3685ec
+	tmp = name;
Ian Kent 3685ec
+	while (*tmp) {
Ian Kent 3685ec
+		if (*tmp == '%') {
Ian Kent 3685ec
 			*ptr++ = '%';
Ian Kent 3685ec
-			if (*tmp && (!isupper(*tmp) || !use_class))
Ian Kent 3685ec
-				*ptr++ = next;
Ian Kent 3685ec
-			else {
Ian Kent 3685ec
-				*ptr++ = '[';
Ian Kent 3685ec
-				*ptr++ = next;
Ian Kent 3685ec
-				while (*tmp && isupper(*tmp))
Ian Kent 3685ec
-					*ptr++ = *tmp++;
Ian Kent 3685ec
-				*ptr++ = ']';
Ian Kent 3685ec
+			*ptr++ = *tmp++;
Ian Kent 3685ec
+		} else if (isupper(*tmp)) {
Ian Kent 3685ec
+			*ptr++ = '%';
Ian Kent 3685ec
+			*ptr++ = '[';
Ian Kent 3685ec
+			*ptr++ = *tmp++;
Ian Kent 3685ec
+
Ian Kent 3685ec
+			while (*tmp && isupper(*tmp)) {
Ian Kent 3685ec
+				*ptr++ = *tmp;
Ian Kent 3685ec
+				tmp++;
Ian Kent 3685ec
 			}
Ian Kent 3685ec
-			continue;
Ian Kent 3685ec
-		}
Ian Kent 3685ec
-		*ptr++ = *tmp++;
Ian Kent 3685ec
+			*ptr++ = ']';
Ian Kent 3685ec
+		} else
Ian Kent 3685ec
+			*ptr++ = *tmp++;
Ian Kent 3685ec
 	}
Ian Kent 3685ec
 	*ptr = '\0';
Ian Kent 3685ec
+	validate_string_len(name, new, ptr, len);
Ian Kent 3685ec
+}
Ian Kent 3685ec
 
Ian Kent 3685ec
-	*key = new;
Ian Kent 3685ec
+/*
Ian Kent 3685ec
+ * use_class:  1 means encode string as %[CAPITALS], 0 means encode as
Ian Kent 3685ec
+ * %C%A%P%I%T%A%L%S
Ian Kent 3685ec
+ */
Ian Kent 3685ec
+static int encode_percent_hack(const char *name, char **key, unsigned int use_class)
Ian Kent 3685ec
+{
Ian Kent 3685ec
+	unsigned int len = 0;
Ian Kent 3685ec
 
Ian Kent 3685ec
-	return strlen(new);
Ian Kent 3685ec
+	if (!key)
Ian Kent 3685ec
+		return -1;
Ian Kent 3685ec
+
Ian Kent 3685ec
+	if (use_class)
Ian Kent 3685ec
+		len = get_encoded_len_escaping_sequences(name);
Ian Kent 3685ec
+	else
Ian Kent 3685ec
+		len = get_encoded_len_escaping_every_cap(name);
Ian Kent 3685ec
+
Ian Kent 3685ec
+	/* If there is no escaping to be done, return 0 */
Ian Kent 3685ec
+	if (len == strlen(name))
Ian Kent 3685ec
+		return 0;
Ian Kent 3685ec
+
Ian Kent 3685ec
+	*key = malloc(len + 1);
Ian Kent 3685ec
+	if (!*key)
Ian Kent 3685ec
+		return -1;
Ian Kent 3685ec
+
Ian Kent 3685ec
+	if (use_class)
Ian Kent 3685ec
+		encode_sequence(name, *key, len);
Ian Kent 3685ec
+	else
Ian Kent 3685ec
+		encode_individual(name, *key, len);
Ian Kent 3685ec
+
Ian Kent 3685ec
+	if (strlen(*key) != len)
Ian Kent 3685ec
+		crit(LOGOPT_ANY, MODPREFIX "encoded key length mismatch: key "
Ian Kent 3685ec
+		     "%s len %d strlen %d", *key, len, strlen(*key));
Ian Kent 3685ec
+
Ian Kent 3685ec
+	return strlen(*key);
Ian Kent 3685ec
 }
Ian Kent 3685ec
 
Ian Kent 3685ec
 static int do_paged_query(struct ldap_search_params *sp, struct lookup_context *ctxt)