Fixes to host checking.

Fixes to host checking wild card support and add support for setting
host checking flags when verifying a certificate chain.

(Imported from upstream's a2219f6be36d12f02b6420dd95f819cf364baf1d)
diff --git a/crypto/x509/vpm_int.h b/crypto/x509/vpm_int.h
index af99821..d18a4d4 100644
--- a/crypto/x509/vpm_int.h
+++ b/crypto/x509/vpm_int.h
@@ -62,6 +62,7 @@
 	{
 	unsigned char *host;	/* If not NULL hostname to match */
 	size_t hostlen;
+	unsigned int hostflags;	/* Flags to control matching features */
 	unsigned char *email;	/* If not NULL email address to match */
 	size_t emaillen;
 	unsigned char *ip;	/* If not NULL IP address to match */
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index f4fb3d5..c1910c4 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -713,7 +713,8 @@
 	X509_VERIFY_PARAM *vpm = ctx->param;
 	X509_VERIFY_PARAM_ID *id = vpm->id;
 	X509 *x = ctx->cert;
-	if (id->host && !X509_check_host(x, id->host, id->hostlen, 0))
+	if (id->host && !X509_check_host(x, id->host, id->hostlen,
+					 id->hostflags))
 		{
 		if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH))
 			return 0;
diff --git a/crypto/x509/x509_vfy.h b/crypto/x509/x509_vfy.h
index 6054250..7675ed6 100644
--- a/crypto/x509/x509_vfy.h
+++ b/crypto/x509/x509_vfy.h
@@ -555,6 +555,8 @@
 
 int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
 				const unsigned char *name, size_t namelen);
+void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param,
+					unsigned int flags);
 int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
 				const unsigned char *email, size_t emaillen);
 int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param,
diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c
index cf9bed0..1841335 100644
--- a/crypto/x509/x509_vpm.c
+++ b/crypto/x509/x509_vpm.c
@@ -233,6 +233,7 @@
 		{
 		if (!X509_VERIFY_PARAM_set1_host(dest, id->host, id->hostlen))
 			return 0;
+		dest->id->hostflags = id->hostflags;
 		}
 
 	if (test_x509_verify_param_copy_id(email, NULL))
@@ -396,6 +397,12 @@
 					name, namelen);
 	}
 
+void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param,
+					unsigned int flags)
+	{
+	param->id->hostflags = flags;
+	}
+
 int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
 				const unsigned char *email, size_t emaillen)
 	{
@@ -431,7 +438,7 @@
 	return param->name;
 	}
 
-static X509_VERIFY_PARAM_ID _empty_id = {NULL, 0, NULL, 0, NULL, 0};
+static X509_VERIFY_PARAM_ID _empty_id = {NULL, 0, 0U, NULL, 0, NULL, 0};
 
 #define vpm_empty_id (X509_VERIFY_PARAM_ID *)&_empty_id
 
diff --git a/crypto/x509v3/v3_utl.c b/crypto/x509v3/v3_utl.c
index a4df78c..e1f8ef1 100644
--- a/crypto/x509v3/v3_utl.c
+++ b/crypto/x509v3/v3_utl.c
@@ -58,8 +58,9 @@
 /* X509 v3 extension utilities */
 
 
-#include <stdio.h>
 #include <ctype.h>
+#include <stdio.h>
+#include <strings.h>
 
 #include <openssl/bn.h>
 #include <openssl/buf.h>
@@ -575,11 +576,13 @@
 }
 
 typedef int (*equal_fn)(const unsigned char *pattern, size_t pattern_len,
-			const unsigned char *subject, size_t subject_len);
+			const unsigned char *subject, size_t subject_len,
+			unsigned int flags);
 
 /* Compare while ASCII ignoring case. */
 static int equal_nocase(const unsigned char *pattern, size_t pattern_len,
-			const unsigned char *subject, size_t subject_len)
+			const unsigned char *subject, size_t subject_len,
+			unsigned int unused_flags)
 	{
 	if (pattern_len != subject_len)
 		return 0;
@@ -608,7 +611,8 @@
 
 /* Compare using memcmp. */
 static int equal_case(const unsigned char *pattern, size_t pattern_len,
-		      const unsigned char *subject, size_t subject_len)
+		      const unsigned char *subject, size_t subject_len,
+		      unsigned int unused_flags)
 {
 	/* The pattern must not contain NUL characters. */
 	if (memchr(pattern, '\0', pattern_len) != NULL)
@@ -621,7 +625,8 @@
 /* RFC 5280, section 7.5, requires that only the domain is compared in
    a case-insensitive manner. */
 static int equal_email(const unsigned char *a, size_t a_len,
-		       const unsigned char *b, size_t b_len)
+		       const unsigned char *b, size_t b_len,
+		       unsigned int unused_flags)
 	{
 	size_t i = a_len;
 	if (a_len != b_len)
@@ -635,103 +640,177 @@
 		if (a[i] == '@' || b[i] == '@')
 			{
 			if (!equal_nocase(a + i, a_len - i,
-					  b + i, a_len - i))
+					  b + i, a_len - i, 0))
 				return 0;
 			break;
 			}
 		}
 	if (i == 0)
 		i = a_len;
-	return equal_case(a, i, b, i);
+	return equal_case(a, i, b, i, 0);
 	}
 
 /* Compare the prefix and suffix with the subject, and check that the
    characters in-between are valid. */
 static int wildcard_match(const unsigned char *prefix, size_t prefix_len,
 			  const unsigned char *suffix, size_t suffix_len,
-			  const unsigned char *subject, size_t subject_len)
+			  const unsigned char *subject, size_t subject_len,
+			  unsigned int flags)
 	{
 	const unsigned char *wildcard_start;
 	const unsigned char *wildcard_end;
 	const unsigned char *p;
+	int allow_multi = 0;
+	int allow_idna = 0;
+
 	if (subject_len < prefix_len + suffix_len)
 		return 0;
-	if (!equal_nocase(prefix, prefix_len, subject, prefix_len))
+	if (!equal_nocase(prefix, prefix_len, subject, prefix_len, flags))
 		return 0;
 	wildcard_start = subject + prefix_len;
 	wildcard_end = subject + (subject_len - suffix_len);
-	if (!equal_nocase(wildcard_end, suffix_len, suffix, suffix_len))
+	if (!equal_nocase(wildcard_end, suffix_len, suffix, suffix_len, flags))
 		return 0;
-	/* The wildcard must match at least one character. */
-	if (wildcard_start == wildcard_end)
+	/*
+	 * If the wildcard makes up the entire first label, it must match at
+	 * least one character.
+	 */
+	if (prefix_len == 0 && *suffix == '.')
+		{
+		if (wildcard_start == wildcard_end)
+			return 0;
+		allow_idna = 1;
+		if (flags & X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS)
+			allow_multi = 1;
+		}
+	/* IDNA labels cannot match partial wildcards */
+	if (!allow_idna &&
+	    subject_len >= 4 && strncasecmp((char *)subject, "xn--", 4) == 0)
 		return 0;
-	/* Check that the part matched by the wildcard contains only
-	   permitted characters and only matches a single label. */
+	/* The wildcard may match a literal '*' */
+	if (wildcard_end == wildcard_start + 1 && *wildcard_start == '*')
+		return 1;
+	/*
+	 * Check that the part matched by the wildcard contains only
+	 * permitted characters and only matches a single label unless
+	 * allow_multi is set.
+	 */
 	for (p = wildcard_start; p != wildcard_end; ++p)
 		if (!(('0' <= *p && *p <= '9') ||
 		      ('A' <= *p && *p <= 'Z') ||
 		      ('a' <= *p && *p <= 'z') ||
-		      *p == '-'))
+		      *p == '-' || (allow_multi && *p == '.')))
 			return 0;
 	return 1;
 	}
 
-/* Checks if the memory region consistens of [0-9A-Za-z.-]. */
-static int valid_domain_characters(const unsigned char *p, size_t len)
-	{
-	while (len)
-		{
-		if (!(('0' <= *p && *p <= '9') ||
-		      ('A' <= *p && *p <= 'Z') ||
-		      ('a' <= *p && *p <= 'z') ||
-		      *p == '-' || *p == '.'))
-			return 0;
-		++p;
-		--len;
-		}
-	return 1;
-	}
+#define LABEL_START	(1 << 0)
+#define LABEL_END	(1 << 1)
+#define LABEL_HYPHEN	(1 << 2)
+#define LABEL_IDNA	(1 << 3)
 
-/* Find the '*' in a wildcard pattern.  If no such character is found
-   or the pattern is otherwise invalid, returns NULL. */
-static const unsigned char *wildcard_find_star(const unsigned char *pattern,
-					       size_t pattern_len)
+static const unsigned char *valid_star(const unsigned char *p, size_t len,
+						unsigned int flags)
 	{
-	const unsigned char *star = memchr(pattern, '*', pattern_len);
-	size_t dot_count = 0;
-	const unsigned char *suffix_start;
-	size_t suffix_length;
-	if (star == NULL)
-		return NULL;
-	suffix_start = star + 1;
-	suffix_length = (pattern + pattern_len) - (star + 1);
-	if (!(valid_domain_characters(pattern, star - pattern) &&
-	      valid_domain_characters(suffix_start, suffix_length)))
-		return NULL;
-	/* Check that the suffix matches at least two labels. */
-	while (suffix_length)
+	const unsigned char *star = 0;
+	size_t i;
+	int state = LABEL_START;
+	int dots = 0;
+	for (i = 0; i < len; ++i)
 		{
-		if (*suffix_start == '.')
-			++dot_count;
-		++suffix_start;
-		--suffix_length;
+		/*
+		 * Locate first and only legal wildcard, either at the start
+		 * or end of a non-IDNA first and not final label.
+		 */
+		if (p[i] == '*')
+			{
+			int atstart = (state & LABEL_START);
+			int atend = (i == len - 1 || p[i+i] == '.');
+			/*
+			 * At most one wildcard per pattern.
+			 * No wildcards in IDNA labels.
+			 * No wildcards after the first label.
+			 */
+			if (star != NULL || (state & LABEL_IDNA) != 0 || dots)
+				return NULL;
+			/* Only full-label '*.example.com' wildcards? */
+			if ((flags & X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS)
+			    && (!atstart || !atend))
+				return NULL;
+			/* No 'foo*bar' wildcards */
+			if (!atstart && !atend)
+				return NULL;
+			star = &p[i];
+			state &= ~LABEL_START;
+			}
+		else if ((state & LABEL_START) != 0)
+			{
+			/*
+			 * At the start of a label, skip any "xn--" and
+			 * remain in the LABEL_START state, but set the
+			 * IDNA label state
+			 */
+			if ((state & LABEL_IDNA) == 0 && len - i >= 4
+			    && strncasecmp((char *)&p[i], "xn--", 4) == 0)
+				{
+				i += 3;
+				state |= LABEL_IDNA;
+				continue;
+				}
+			/* Labels must start with a letter or digit */
+			state &= ~LABEL_START;
+			if (('a' <= p[i] && p[i] <= 'z')
+			    || ('A' <= p[i] && p[i] <= 'Z')
+			    || ('0' <= p[i] && p[i] <= '9'))
+				continue;
+			return NULL;
+			}
+		else if (('a' <= p[i] && p[i] <= 'z')
+			 || ('A' <= p[i] && p[i] <= 'Z')
+			 || ('0' <= p[i] && p[i] <= '9'))
+			{
+			state &= LABEL_IDNA;
+			continue;
+			}
+		else if (p[i] == '.')
+			{
+			if (state & (LABEL_HYPHEN | LABEL_START))
+				return NULL;
+			state = LABEL_START;
+			++dots;
+			}
+		else if (p[i] == '-')
+			{
+			if (state & LABEL_HYPHEN)
+				return NULL;
+			state |= LABEL_HYPHEN;
+			}
+		else
+			return NULL;
 		}
-	if (dot_count < 2)
+
+	/*
+	 * The final label must not end in a hyphen or ".", and
+	 * there must be at least two dots after the star.
+	 */
+	if ((state & (LABEL_START | LABEL_HYPHEN)) != 0
+	    || dots < 2)
 		return NULL;
 	return star;
 	}
 
 /* Compare using wildcards. */
 static int equal_wildcard(const unsigned char *pattern, size_t pattern_len,
-			  const unsigned char *subject, size_t subject_len)
+			  const unsigned char *subject, size_t subject_len,
+			  unsigned int flags)
 	{
-	const unsigned char *star = wildcard_find_star(pattern, pattern_len);
+	const unsigned char *star = valid_star(pattern, pattern_len, flags);
 	if (star == NULL)
 		return equal_nocase(pattern, pattern_len,
-				    subject, subject_len);
+				    subject, subject_len, flags);
 	return wildcard_match(pattern, star - pattern,
 			      star + 1, (pattern + pattern_len) - star - 1,
-			      subject, subject_len);
+			      subject, subject_len, flags);
 	}
 
 /* Compare an ASN1_STRING to a supplied string. If they match
@@ -740,6 +819,7 @@
  */
 
 static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
+				unsigned int flags,
 				const unsigned char *b, size_t blen)
 	{
 	if (!a->data || !a->length)
@@ -749,7 +829,7 @@
 		if (cmp_type != a->type)
 			return 0;
 		if (cmp_type == V_ASN1_IA5STRING)
-			return equal(a->data, a->length, b, blen);
+			return equal(a->data, a->length, b, blen, flags);
 		if (a->length == (int)blen && !memcmp(a->data, b, blen))
 			return 1;
 		else
@@ -762,7 +842,7 @@
 		astrlen = ASN1_STRING_to_UTF8(&astr, a);
 		if (astrlen < 0)
 			return -1;
-		rv = equal(astr, astrlen, b, blen);
+		rv = equal(astr, astrlen, b, blen, flags);
 		OPENSSL_free(astr);
 		return rv;
 		}
@@ -777,6 +857,7 @@
 	int j;
 	int cnid;
 	int alt_type;
+	int san_present = 0;
 	equal_fn equal;
 	if (check_type == GEN_EMAIL)
 		{
@@ -812,15 +893,17 @@
 			GENERAL_NAME *gen;
 			ASN1_STRING *cstr;
 			gen = sk_GENERAL_NAME_value(gens, i);
-			if(gen->type != check_type)
+			if (gen->type != check_type)
 				continue;
+			san_present = 1;
 			if (check_type == GEN_EMAIL)
 				cstr = gen->d.rfc822Name;
 			else if (check_type == GEN_DNS)
 				cstr = gen->d.dNSName;
 			else
 				cstr = gen->d.iPAddress;
-			if (do_check_string(cstr, alt_type, equal, chk, chklen))
+			if (do_check_string(cstr, alt_type, equal, flags,
+					    chk, chklen))
 				{
 				rv = 1;
 				break;
@@ -829,7 +912,9 @@
 		GENERAL_NAMES_free(gens);
 		if (rv)
 			return 1;
-		if (!(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) || !cnid)
+		if (!cnid
+		    || (san_present
+		        && !(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT)))
 			return 0;
 		}
 	j = -1;
@@ -840,7 +925,7 @@
 		ASN1_STRING *str;
 		ne = X509_NAME_get_entry(name, j);
 		str = X509_NAME_ENTRY_get_data(ne);
-		if (do_check_string(str, -1, equal, chk, chklen))
+		if (do_check_string(str, -1, equal, flags, chk, chklen))
 			return 1;
 		}
 	return 0;
diff --git a/crypto/x509v3/v3nametest.c b/crypto/x509v3/v3nametest.c
index 15d3e10..bdba29b 100644
--- a/crypto/x509v3/v3nametest.c
+++ b/crypto/x509v3/v3nametest.c
@@ -63,11 +63,10 @@
 	{
 	"a", "b", ".", "*", "@",
 	".a", "a.", ".b", "b.", ".*", "*.", "*@", "@*", "a@", "@a", "b@", "..",
-	"@@", "**",
-	"*.com", "*com", "*.*.com", "*com", "com*", "*example.com",
-	"*@example.com", "test@*.example.com",
-	"example.com", "www.example.com", "test.www.example.com",
-	"*.example.com", "*.www.example.com", "test.*.example.com", "www.*.com",
+	"@@", "**", "*.com", "*com", "*.*.com", "*com", "com*", "*example.com",
+	"*@example.com", "test@*.example.com", "example.com", "www.example.com",
+	"test.www.example.com", "*.example.com", "*.www.example.com",
+	"test.*.example.com", "www.*.com",
 	"example.net", "xn--rger-koa.example.com",
 	"a.example.com", "b.example.com",
 	"postmaster@example.com", "Postmaster@example.com",
@@ -77,28 +76,20 @@
 
 static const char *const exceptions[] =
 	{
-	"set CN: host: [*.example.com] does not match [*.example.com]",
 	"set CN: host: [*.example.com] matches [a.example.com]",
 	"set CN: host: [*.example.com] matches [b.example.com]",
 	"set CN: host: [*.example.com] matches [www.example.com]",
 	"set CN: host: [*.example.com] matches [xn--rger-koa.example.com]",
-	"set CN: host: [test.*.example.com] does not match [test.*.example.com]",
-	"set CN: host: [test.*.example.com] matches [test.www.example.com]",
-	"set CN: host: [*.www.example.com] does not match [*.www.example.com]",
 	"set CN: host: [*.www.example.com] matches [test.www.example.com]",
 	"set emailAddress: email: [postmaster@example.com] does not match [Postmaster@example.com]",
 	"set emailAddress: email: [postmaster@EXAMPLE.COM] does not match [Postmaster@example.com]",
 	"set emailAddress: email: [Postmaster@example.com] does not match [postmaster@example.com]",
 	"set emailAddress: email: [Postmaster@example.com] does not match [postmaster@EXAMPLE.COM]",
 	"set dnsName: host: [*.example.com] matches [www.example.com]",
-	"set dnsName: host: [*.example.com] does not match [*.example.com]",
 	"set dnsName: host: [*.example.com] matches [a.example.com]",
 	"set dnsName: host: [*.example.com] matches [b.example.com]",
 	"set dnsName: host: [*.example.com] matches [xn--rger-koa.example.com]",
 	"set dnsName: host: [*.www.example.com] matches [test.www.example.com]",
-	"set dnsName: host: [*.www.example.com] does not match [*.www.example.com]",
-	"set dnsName: host: [test.*.example.com] matches [test.www.example.com]",
-	"set dnsName: host: [test.*.example.com] does not match [test.*.example.com]",
 	"set rfc822Name: email: [postmaster@example.com] does not match [Postmaster@example.com]",
 	"set rfc822Name: email: [Postmaster@example.com] does not match [postmaster@example.com]",
 	"set rfc822Name: email: [Postmaster@example.com] does not match [postmaster@EXAMPLE.COM]",
diff --git a/crypto/x509v3/x509v3.h b/crypto/x509v3/x509v3.h
index 4215793..e789083 100644
--- a/crypto/x509v3/x509v3.h
+++ b/crypto/x509v3/x509v3.h
@@ -698,8 +698,12 @@
 
 /* Always check subject name for host match even if subject alt names present */
 #define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT	0x1
-/* Disable wild-card matching for dnsName fields and common name. */
+/* Disable wildcard matching for dnsName fields and common name. */
 #define X509_CHECK_FLAG_NO_WILDCARDS	0x2
+/* Wildcards must not match a partial label. */
+#define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0x4
+/* Allow (non-partial) wildcards to match multiple labels. */
+#define X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS 0x8
 
 int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen,
 					unsigned int flags);