Commit 397a8e74 authored by Viktor Dukhovni's avatar Viktor Dukhovni Committed by Dr. Stephen Henson
Browse files

Fixes to host checking.

Fixes to host checking wild card support and add support for
setting host checking flags when verifying a certificate
chain.
parent 558c94ef
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -62,6 +62,7 @@ struct X509_VERIFY_PARAM_ID_st
	{
	{
	unsigned char *host;	/* If not NULL hostname to match */
	unsigned char *host;	/* If not NULL hostname to match */
	size_t hostlen;
	size_t hostlen;
	unsigned int hostflags;	/* Flags to control matching features */
	unsigned char *email;	/* If not NULL email address to match */
	unsigned char *email;	/* If not NULL email address to match */
	size_t emaillen;
	size_t emaillen;
	unsigned char *ip;	/* If not NULL IP address to match */
	unsigned char *ip;	/* If not NULL IP address to match */
+2 −1
Original line number Original line Diff line number Diff line
@@ -744,7 +744,8 @@ static int check_id(X509_STORE_CTX *ctx)
	X509_VERIFY_PARAM *vpm = ctx->param;
	X509_VERIFY_PARAM *vpm = ctx->param;
	X509_VERIFY_PARAM_ID *id = vpm->id;
	X509_VERIFY_PARAM_ID *id = vpm->id;
	X509 *x = ctx->cert;
	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))
		if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH))
			return 0;
			return 0;
+2 −0
Original line number Original line Diff line number Diff line
@@ -560,6 +560,8 @@ int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *param,


int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
				const unsigned char *name, size_t namelen);
				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,
int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
				const unsigned char *email, size_t emaillen);
				const unsigned char *email, size_t emaillen);
int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param,
int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param,
+8 −1
Original line number Original line Diff line number Diff line
@@ -239,6 +239,7 @@ int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest,
		{
		{
		if (!X509_VERIFY_PARAM_set1_host(dest, id->host, id->hostlen))
		if (!X509_VERIFY_PARAM_set1_host(dest, id->host, id->hostlen))
			return 0;
			return 0;
		dest->id->hostflags = id->hostflags;
		}
		}


	if (test_x509_verify_param_copy_id(email, NULL))
	if (test_x509_verify_param_copy_id(email, NULL))
@@ -402,6 +403,12 @@ int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
					name, namelen);
					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,
int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
				const unsigned char *email, size_t emaillen)
				const unsigned char *email, size_t emaillen)
	{
	{
@@ -437,7 +444,7 @@ const char *X509_VERIFY_PARAM_get0_name(const X509_VERIFY_PARAM *param)
	return param->name;
	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
#define vpm_empty_id (X509_VERIFY_PARAM_ID *)&_empty_id


+145 −61
Original line number Original line Diff line number Diff line
@@ -569,11 +569,13 @@ void X509_email_free(STACK_OF(OPENSSL_STRING) *sk)
}
}


typedef int (*equal_fn)(const unsigned char *pattern, size_t pattern_len,
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. */
/* Compare while ASCII ignoring case. */
static int equal_nocase(const unsigned char *pattern, size_t pattern_len,
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)
	if (pattern_len != subject_len)
		return 0;
		return 0;
@@ -602,7 +604,8 @@ static int equal_nocase(const unsigned char *pattern, size_t pattern_len,


/* Compare using memcmp. */
/* Compare using memcmp. */
static int equal_case(const unsigned char *pattern, size_t pattern_len,
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. */
	/* The pattern must not contain NUL characters. */
	if (memchr(pattern, '\0', pattern_len) != NULL)
	if (memchr(pattern, '\0', pattern_len) != NULL)
@@ -615,7 +618,8 @@ static int equal_case(const unsigned char *pattern, size_t pattern_len,
/* RFC 5280, section 7.5, requires that only the domain is compared in
/* RFC 5280, section 7.5, requires that only the domain is compared in
   a case-insensitive manner. */
   a case-insensitive manner. */
static int equal_email(const unsigned char *a, size_t a_len,
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;
	size_t i = a_len;
	if (a_len != b_len)
	if (a_len != b_len)
@@ -629,103 +633,177 @@ static int equal_email(const unsigned char *a, size_t a_len,
		if (a[i] == '@' || b[i] == '@')
		if (a[i] == '@' || b[i] == '@')
			{
			{
			if (!equal_nocase(a + i, a_len - i,
			if (!equal_nocase(a + i, a_len - i,
					  b + i, a_len - i))
					  b + i, a_len - i, 0))
				return 0;
				return 0;
			break;
			break;
			}
			}
		}
		}
	if (i == 0)
	if (i == 0)
		i = a_len;
		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
/* Compare the prefix and suffix with the subject, and check that the
   characters in-between are valid. */
   characters in-between are valid. */
static int wildcard_match(const unsigned char *prefix, size_t prefix_len,
static int wildcard_match(const unsigned char *prefix, size_t prefix_len,
			  const unsigned char *suffix, size_t suffix_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_start;
	const unsigned char *wildcard_end;
	const unsigned char *wildcard_end;
	const unsigned char *p;
	const unsigned char *p;
	int allow_multi = 0;
	int allow_idna = 0;

	if (subject_len < prefix_len + suffix_len)
	if (subject_len < prefix_len + suffix_len)
		return 0;
		return 0;
	if (!equal_nocase(prefix, prefix_len, subject, prefix_len))
	if (!equal_nocase(prefix, prefix_len, subject, prefix_len, flags))
		return 0;
		return 0;
	wildcard_start = subject + prefix_len;
	wildcard_start = subject + prefix_len;
	wildcard_end = subject + (subject_len - suffix_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;
		return 0;
	/* The wildcard must match at least one character. */
	/*
	 * 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)
		if (wildcard_start == wildcard_end)
			return 0;
			return 0;
	/* Check that the part matched by the wildcard contains only
		allow_idna = 1;
	   permitted characters and only matches a single label. */
		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;
	/* 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)
	for (p = wildcard_start; p != wildcard_end; ++p)
		if (!(('0' <= *p && *p <= '9') ||
		if (!(('0' <= *p && *p <= '9') ||
		      ('A' <= *p && *p <= 'Z') ||
		      ('A' <= *p && *p <= 'Z') ||
		      ('a' <= *p && *p <= 'z') ||
		      ('a' <= *p && *p <= 'z') ||
		      *p == '-'))
		      *p == '-' || (allow_multi && *p == '.')))
			return 0;
			return 0;
	return 1;
	return 1;
	}
	}


/* Checks if the memory region consistens of [0-9A-Za-z.-]. */
#define LABEL_START	(1 << 0)
static int valid_domain_characters(const unsigned char *p, size_t len)
#define LABEL_END	(1 << 1)
#define LABEL_HYPHEN	(1 << 2)
#define LABEL_IDNA	(1 << 3)

static const unsigned char *valid_star(const unsigned char *p, size_t len,
						unsigned int flags)
	{
	{
	while (len)
	const unsigned char *star = 0;
	size_t i;
	int state = LABEL_START;
	int dots = 0;
	for (i = 0; i < len; ++i)
		{
		{
		if (!(('0' <= *p && *p <= '9') ||
		/*
		      ('A' <= *p && *p <= 'Z') ||
		 * Locate first and only legal wildcard, either at the start
		      ('a' <= *p && *p <= 'z') ||
		 * or end of a non-IDNA first and not final label.
		      *p == '-' || *p == '.'))
		 */
			return 0;
		if (p[i] == '*')
		++p;
			{
		--len;
			int atstart = (state & LABEL_START);
		}
			int atend = (i == len - 1 || p[i+i] == '.');
	return 1;
			/*
			 * 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)
/* 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)
			{
			{
	const unsigned char *star = memchr(pattern, '*', pattern_len);
			/*
	size_t dot_count = 0;
			 * At the start of a label, skip any "xn--" and
	const unsigned char *suffix_start;
			 * remain in the LABEL_START state, but set the
	size_t suffix_length;
			 * IDNA label state
	if (star == NULL)
			 */
			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;
			return NULL;
	suffix_start = star + 1;
			}
	suffix_length = (pattern + pattern_len) - (star + 1);
		else if (('a' <= p[i] && p[i] <= 'z')
	if (!(valid_domain_characters(pattern, star - pattern) &&
			 || ('A' <= p[i] && p[i] <= 'Z')
	      valid_domain_characters(suffix_start, suffix_length)))
			 || ('0' <= p[i] && p[i] <= '9'))
			{
			state &= LABEL_IDNA;
			continue;
			}
		else if (p[i] == '.')
			{
			if (state & (LABEL_HYPHEN | LABEL_START))
				return NULL;
				return NULL;
	/* Check that the suffix matches at least two labels. */
			state = LABEL_START;
	while (suffix_length)
			++dots;
			}
		else if (p[i] == '-')
			{
			{
		if (*suffix_start == '.')
			if (state & LABEL_HYPHEN)
			++dot_count;
				return NULL;
		++suffix_start;
			state |= LABEL_HYPHEN;
		--suffix_length;
			}
		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 NULL;
	return star;
	return star;
	}
	}


/* Compare using wildcards. */
/* Compare using wildcards. */
static int equal_wildcard(const unsigned char *pattern, size_t pattern_len,
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)
	if (star == NULL)
		return equal_nocase(pattern, pattern_len,
		return equal_nocase(pattern, pattern_len,
				    subject, subject_len);
				    subject, subject_len, flags);
	return wildcard_match(pattern, star - pattern,
	return wildcard_match(pattern, star - pattern,
			      star + 1, (pattern + pattern_len) - star - 1,
			      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
/* Compare an ASN1_STRING to a supplied string. If they match
@@ -734,6 +812,7 @@ static int equal_wildcard(const unsigned char *pattern, size_t pattern_len,
 */
 */


static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
				unsigned int flags,
				const unsigned char *b, size_t blen)
				const unsigned char *b, size_t blen)
	{
	{
	if (!a->data || !a->length)
	if (!a->data || !a->length)
@@ -743,7 +822,7 @@ static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
		if (cmp_type != a->type)
		if (cmp_type != a->type)
			return 0;
			return 0;
		if (cmp_type == V_ASN1_IA5STRING)
		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))
		if (a->length == (int)blen && !memcmp(a->data, b, blen))
			return 1;
			return 1;
		else
		else
@@ -756,7 +835,7 @@ static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
		astrlen = ASN1_STRING_to_UTF8(&astr, a);
		astrlen = ASN1_STRING_to_UTF8(&astr, a);
		if (astrlen < 0)
		if (astrlen < 0)
			return -1;
			return -1;
		rv = equal(astr, astrlen, b, blen);
		rv = equal(astr, astrlen, b, blen, flags);
		OPENSSL_free(astr);
		OPENSSL_free(astr);
		return rv;
		return rv;
		}
		}
@@ -770,6 +849,7 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
	int i;
	int i;
	int cnid;
	int cnid;
	int alt_type;
	int alt_type;
	int san_present = 0;
	equal_fn equal;
	equal_fn equal;
	if (check_type == GEN_EMAIL)
	if (check_type == GEN_EMAIL)
		{
		{
@@ -807,13 +887,15 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
			gen = sk_GENERAL_NAME_value(gens, i);
			gen = sk_GENERAL_NAME_value(gens, i);
			if (gen->type != check_type)
			if (gen->type != check_type)
				continue;
				continue;
			san_present = 1;
			if (check_type == GEN_EMAIL)
			if (check_type == GEN_EMAIL)
				cstr = gen->d.rfc822Name;
				cstr = gen->d.rfc822Name;
			else if (check_type == GEN_DNS)
			else if (check_type == GEN_DNS)
				cstr = gen->d.dNSName;
				cstr = gen->d.dNSName;
			else
			else
				cstr = gen->d.iPAddress;
				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;
				rv = 1;
				break;
				break;
@@ -822,7 +904,9 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
		GENERAL_NAMES_free(gens);
		GENERAL_NAMES_free(gens);
		if (rv)
		if (rv)
			return 1;
			return 1;
		if (!(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) || !cnid)
		if (!cnid
		    || (san_present
		        && !(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT)))
			return 0;
			return 0;
		}
		}
	i = -1;
	i = -1;
@@ -833,7 +917,7 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
		ASN1_STRING *str;
		ASN1_STRING *str;
		ne = X509_NAME_get_entry(name, i);
		ne = X509_NAME_get_entry(name, i);
		str = X509_NAME_ENTRY_get_data(ne);
		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 1;
		}
		}
	return 0;
	return 0;
Loading