[asterisk-commits] mmichelson: branch group/dns_naptr r433361 - in /team/group/dns_naptr: main/ ...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Mar 24 17:03:39 CDT 2015


Author: mmichelson
Date: Tue Mar 24 17:03:37 2015
New Revision: 433361

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=433361
Log:
Add off-nominal test for regexps.

This test ensures that invalid regexps in NAPTR
records are not accepted.


Modified:
    team/group/dns_naptr/main/dns_naptr.c
    team/group/dns_naptr/tests/test_dns_naptr.c

Modified: team/group/dns_naptr/main/dns_naptr.c
URL: http://svnview.digium.com/svn/asterisk/team/group/dns_naptr/main/dns_naptr.c?view=diff&rev=433361&r1=433360&r2=433361
==============================================================================
--- team/group/dns_naptr/main/dns_naptr.c (original)
+++ team/group/dns_naptr/main/dns_naptr.c Tue Mar 24 17:03:37 2015
@@ -33,6 +33,7 @@
 
 #include <arpa/nameser.h>
 #include <resolv.h>
+#include <regex.h>
 
 #include "asterisk/dns_core.h"
 #include "asterisk/dns_naptr.h"
@@ -158,6 +159,203 @@
 	return 0;
 }
 
+/*!
+ * \brief Determine if flags in the regexp are invalid
+ *
+ * A NAPTR regexp is structured like so
+ * /pattern/repl/FLAGS
+ *
+ * This ensures that the flags on the regex are valid. Regexp
+ * flags can either be zero or one character long. If the flags
+ * are one character long, that character must be "i" to indicate
+ * the regex evaluation is case-insensitive.
+ *
+ * \note The flags string passed to this function is not NULL-terminated
+ * \param flags The regexp flags from the NAPTR record
+ * \param end A pointer to the end of the flags string
+ * \retval 0 Flags are valid
+ * \retval -1 Flags are invalid
+ */
+static int regexp_flags_invalid(const char *flags, const char *end)
+{
+	if (flags >= end) {
+		return 0;
+	}
+
+	if (end - flags > 1) {
+		return -1;
+	}
+
+	if (*flags != 'i') {
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Determine if the replacement in the regexp is invalid
+ *
+ * A NAPTR regexp is structured like so
+ * /pattern/REPL/flags
+ *
+ * This ensures that the replacement on the regexp is valid. The regexp
+ * replacement is free to use any character it wants, plus backreferences
+ * and an escaped regexp delimiter.
+ *
+ * This function does not attempt to ensure that the backreferences refer
+ * to valid portions of the regexp's regex pattern.
+ *
+ * \note The repl string passed to this function is NOT NULL-terminated
+ *
+ * \param repl The regexp replacement string
+ * \param end Pointer to the end of the replacement string
+ * \param delim The delimiter character for the regexp
+ *
+ * \retval 0 Replacement is valid
+ * \retval -1 Replacement is invalid
+ */
+static int regexp_repl_invalid(const char *repl, const char *end, char delim)
+{
+	const char *ptr = repl;
+
+	if (repl == end) {
+		/* Kind of weird, but this is fine */
+		return 0;
+	}
+
+	while (1) {
+		char *backslash_pos = memchr(ptr, '\\', end - ptr);
+		if (!backslash_pos) {
+			break;
+		}
+
+		ast_assert(backslash_pos < end - 1);
+
+		/* XXX RFC 3402 is unclear about whether a backslash-escaped backslash is
+		 * acceptable.
+		 */
+		if (!strchr("12345689", backslash_pos[1]) && backslash_pos[1] != delim) {
+			return -1;
+		}
+
+		ptr = backslash_pos + 1;
+	}
+	
+	return 0;
+}
+
+/*!
+ * \brief Determine if the pattern in a regexp is invalid
+ *
+ * A NAPTR regexp is structured like so
+ * /PATTERN/repl/flags
+ *
+ * This ensures that the pattern on the regexp is valid. The pattern is
+ * passed to a regex compiler to determine its validity.
+ *
+ * \note The pattern string passed to this function is NOT NULL-terminated
+ *
+ * \param pattern The pattern from the NAPTR record
+ * \param end A pointer to the end of the pattern
+ *
+ * \retval 0 Pattern is valid
+ * \retval non-zero Pattern is invalid
+ */
+static int regexp_pattern_invalid(const char *pattern, const char *end)
+{
+	int pattern_size = end - pattern;
+	char pattern_str[pattern_size + 1];
+	regex_t reg;
+	int res;
+
+	memcpy(pattern_str, pattern, pattern_size);
+	pattern_str[pattern_size] = '\0';
+
+	res = regcomp(&reg, pattern_str, REG_EXTENDED);
+
+	regfree(&reg);
+
+	return res;
+}
+
+/*!
+ * \brief Determine if the regexp in a NAPTR record is invalid
+ *
+ * The goal of this function is to divide the regexp into its
+ * constituent parts and then let validation subroutines determine
+ * if each part is valid. If all parts are valid, then the entire
+ * regexp is valid.
+ *
+ * \note The regexp string passed to this function is NOT NULL-terminated
+ *
+ * \param regexp The regexp from the NAPTR record
+ * \param regexp_size The size of the regexp string
+ *
+ * \retval 0 regexp is valid
+ * \retval non-zero regexp is invalid
+ */
+static int regexp_invalid(const char *regexp, uint8_t regexp_size)
+{
+	char delim;
+	const char *delim2_pos;
+	const char *delim3_pos;
+	const char *ptr = regexp;
+	const char *end_of_regexp = regexp + regexp_size;
+	const char *regex_pos;
+	const char *repl_pos;
+	const char *flags_pos;
+
+	if (regexp_size == 0) {
+		return 0;
+	}
+
+	delim = *ptr;
+	if (strchr("123456789\\i", delim)) {
+		return -1;
+	}
+	++ptr;
+	regex_pos = ptr;
+
+	/* Find the other two delimiters. If the delim is escaped with a backslash, it doesn't count */
+	while (1) {
+		delim2_pos = memchr(ptr, delim, end_of_regexp - ptr);
+		if (!delim2_pos) {
+			return -1;
+		}
+		ptr = delim2_pos + 1;
+		if (delim2_pos[-1] != '\\') {
+			break;
+		}
+	}
+
+	if (ptr >= end_of_regexp) {
+		return -1;
+	}
+
+	repl_pos = ptr;
+
+	while (1) {
+		delim3_pos = memchr(ptr, delim, end_of_regexp - ptr);
+		if (!delim3_pos) {
+			return -1;
+		}
+		ptr = delim3_pos + 1;
+		if (delim3_pos[-1] != '\\') {
+			break;
+		}
+	}
+	flags_pos = ptr;
+
+	if (regexp_flags_invalid(flags_pos, end_of_regexp) ||
+			regexp_repl_invalid(repl_pos, delim3_pos, delim) ||
+			regexp_pattern_invalid(regex_pos, delim2_pos)) {
+		return -1;
+	}
+
+	return 0;
+}
+
 struct ast_dns_record *ast_dns_naptr_alloc(struct ast_dns_query *query, const char *data, const size_t size)
 {
 	struct ast_dns_naptr_record *naptr;
@@ -295,6 +493,11 @@
 		return NULL;
 	}
 
+	if (regexp_invalid(regexp, regexp_size)) {
+		ast_log(LOG_ERROR, "NAPTR record contained invalid regexp %.*s\n", regexp_size, regexp);
+		return NULL;
+	}
+
 	naptr = ast_calloc(1, sizeof(*naptr) + size + flags_size + 1 + services_size + 1 + regexp_size + 1 + replacement_size + 1);
 	if (!naptr) {
 		return NULL;

Modified: team/group/dns_naptr/tests/test_dns_naptr.c
URL: http://svnview.digium.com/svn/asterisk/team/group/dns_naptr/tests/test_dns_naptr.c?view=diff&rev=433361&r1=433360&r2=433361
==============================================================================
--- team/group/dns_naptr/tests/test_dns_naptr.c (original)
+++ team/group/dns_naptr/tests/test_dns_naptr.c Tue Mar 24 17:03:37 2015
@@ -474,12 +474,49 @@
 	return off_nominal_test(test, records, ARRAY_LEN(records));
 }
 
+AST_TEST_DEFINE(naptr_resolve_off_nominal_regexp)
+{
+	struct naptr_record records[] = {
+		/* Invalid delim-char */
+		{ 100, 100, {1, "A"}, {4, "BLAH"}, {15, "1.*1horse.mane1"}, ""},
+		/* Not enough delim-chars */
+		{ 100, 100, {1, "A"}, {4, "BLAH"}, {14, "!.*!horse.mane"}, ""},
+		/* Not enough delim-chars, part 2 */
+		{ 100, 100, {1, "A"}, {4, "BLAH"}, {16, "!.*!horse.mane\\!"}, ""},
+		/* Too many delim-chars */
+		{ 100, 100, {1, "A"}, {4, "BLAH"}, {15, "!.*!horse!mane!"}, ""},
+		/* Invalid regex flag */
+		{ 100, 100, {1, "A"}, {4, "BLAH"}, {16, "!.*!horse.mane!o"}, ""},
+		/* Invalid backreference */
+		{ 100, 100, {1, "A"}, {4, "BLAH"}, {14, "!.*!horse.\\0!"}, ""},
+		/* Invalid regex */
+		{ 100, 100, {1, "A"}, {4, "BLAH"}, {16, "!(.*!horse.mane!"}, ""},
+	};
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "naptr_resolve_off_nominal_regexp";
+		info->category = "/main/dns/naptr/";
+		info->summary = "Ensure that NAPTR records with invalid services are not presented in results";
+		info->description = "This test defines a set of records where the services provided are\n"
+			"invalid in some way. This may be due to providing non-alphanumeric characters, providing\n"
+			"protocols or resolution services that start with a non-alphabetic character, or\n"
+			"providing fields that are too long.\n";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	return off_nominal_test(test, records, ARRAY_LEN(records));
+}
+
 static int unload_module(void)
 {
 	AST_TEST_UNREGISTER(naptr_resolve_nominal);
 	AST_TEST_UNREGISTER(naptr_resolve_off_nominal_length);
 	AST_TEST_UNREGISTER(naptr_resolve_off_nominal_flags);
 	AST_TEST_UNREGISTER(naptr_resolve_off_nominal_services);
+	AST_TEST_UNREGISTER(naptr_resolve_off_nominal_regexp);
 
 	return 0;
 }
@@ -490,6 +527,7 @@
 	AST_TEST_REGISTER(naptr_resolve_off_nominal_length);
 	AST_TEST_REGISTER(naptr_resolve_off_nominal_flags);
 	AST_TEST_REGISTER(naptr_resolve_off_nominal_services);
+	AST_TEST_REGISTER(naptr_resolve_off_nominal_regexp);
 
 	return AST_MODULE_LOAD_SUCCESS;
 }




More information about the asterisk-commits mailing list