Hi,

the attached patch implements wildcard matching and introduces the X509_CHECK_FLAG_NO_WILDCARDS flag to disable it if necessary.

In addition, it implements case-insensitive comparison of host names and email address domain parts, as required by RFC 5280. Domain names and email addresses which contain NUL characters are now rejected, to cope with some mis-issued certificates.

I have also added a manual page. The test case does not have full coverage, but it is better than nothing.

It might make sense to change the API so that 0 means success, 1 match failure, and -1 an internal error. Right now, it is not possible to tell match failures and internal errors apart.

Florian
--
Florian Weimer / Red Hat Product Security Team
diff --git a/crypto/x509v3/Makefile b/crypto/x509v3/Makefile
index 07e2df4..b54f540 100644
--- a/crypto/x509v3/Makefile
+++ b/crypto/x509v3/Makefile
@@ -13,7 +13,7 @@ AR=		ar r
 CFLAGS= $(INCLUDES) $(CFLAG)
 
 GENERAL=Makefile README
-TEST=
+TEST=v3nametest.c
 APPS=
 
 LIB=$(TOP)/libcrypto.a
diff --git a/crypto/x509v3/v3_utl.c b/crypto/x509v3/v3_utl.c
index db9f05e..8f88e69 100644
--- a/crypto/x509v3/v3_utl.c
+++ b/crypto/x509v3/v3_utl.c
@@ -568,12 +568,177 @@ void X509_email_free(STACK_OF(OPENSSL_STRING) *sk)
 	sk_OPENSSL_STRING_pop_free(sk, str_free);
 }
 
+typedef int (*equal_fn)(const unsigned char *pattern, size_t pattern_len,
+			const unsigned char *subject, size_t subject_len);
+
+/* 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)
+	{
+	if (pattern_len != subject_len)
+		return 0;
+	while (pattern_len)
+		{
+		unsigned char l = *pattern;
+		unsigned char r = *subject;
+		/* The pattern must not contain NUL characters. */
+		if (l == 0)
+			return 0;
+		if (l != r)
+			{
+			if ('A' <= l && l <= 'Z')
+				l = (l - 'A') + 'a';
+			if ('A' <= r && r <= 'Z')
+				r = (r - 'A') + 'a';
+			if (l != r)
+				return 0;
+			}
+		++pattern;
+		++subject;
+		--pattern_len;
+		}
+	return 1;
+	}
+
+/* Compare using memcmp. */
+static int equal_case(const unsigned char *pattern, size_t pattern_len,
+		      const unsigned char *subject, size_t subject_len)
+{
+	/* The pattern must not contain NUL characters. */
+	if (memchr(pattern, '\0', pattern_len) != NULL)
+		return 0;
+	if (pattern_len != subject_len)
+		return 0;
+	return !memcmp(pattern, subject, pattern_len);
+}
+
+/* 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)
+	{
+	if (a_len != b_len)
+		return 0;
+	size_t i = a_len;
+	/* We search backwards for the '@' character, so that we do
+	   not have to deal with quoted local-parts.  The domain part
+	   is compared in a case-insensitive manner. */
+	while (i > 0)
+		{
+		--i;
+		if (a[i] == '@' || b[i] == '@')
+			{
+			if (!equal_nocase(a + i, a_len - i,
+					  b + i, a_len - i))
+				return 0;
+			break;
+			}
+		}
+	if (i == 0)
+		i = a_len;
+	return equal_case(a, i, b, i);
+	}
+
+/* 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 *wildcard_start;
+	const unsigned char *wildcard_end;
+	const unsigned char *p;
+	if (subject_len < prefix_len + suffix_len)
+		return 0;
+	if (!equal_nocase(prefix, prefix_len, subject, prefix_len))
+		return 0;
+	wildcard_start = subject + prefix_len;
+	wildcard_end = subject + (subject_len - suffix_len);
+	if (!equal_nocase(wildcard_end, suffix_len, suffix, suffix_len))
+		return 0;
+	/* The wildcard must match at least one character. */
+	if (wildcard_start == wildcard_end)
+		return 0;
+	/* Check that the part matched by the wildcard contains only
+	   permitted characters and only matches a single label. */
+	for (p = wildcard_start; p != wildcard_end; ++p)
+		if (!(('0' <= *p && *p <= '9') ||
+		      ('A' <= *p && *p <= 'Z') ||
+		      ('a' <= *p && *p <= 'z') ||
+		      *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;
+	}
+
+/* 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;
+	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)
+		{
+		if (*suffix_start == '.')
+			++dot_count;
+		++suffix_start;
+		--suffix_length;
+		}
+	if (dot_count < 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 *star;
+	/* Do not match IDNA names. */
+	if (subject_len >=4 && memcmp(subject, "xn--", 4) == 0)
+		star = NULL;
+	else
+		star = wildcard_find_star(pattern, pattern_len);
+	if (star == NULL)
+		return equal_nocase(pattern, pattern_len,
+				    subject, subject_len);
+	return wildcard_match(pattern, star - pattern,
+			      star + 1, (pattern + pattern_len) - star - 1,
+			      subject, subject_len);
+	}
+
 /* Compare an ASN1_STRING to a supplied string. If they match
  * return 1. If cmp_type > 0 only compare if string matches the
  * type, otherwise convert it to UTF8.
  */
 
-static int do_check_string(ASN1_STRING *a, int cmp_type,
+static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
 				const unsigned char *b, size_t blen)
 	{
 	if (!a->data || !a->length)
@@ -582,6 +747,8 @@ static int do_check_string(ASN1_STRING *a, int cmp_type,
 		{
 		if (cmp_type != a->type)
 			return 0;
+		if (cmp_type == V_ASN1_IA5STRING)
+			return equal(a->data, a->length, b, blen);
 		if (a->length == (int)blen && !memcmp(a->data, b, blen))
 			return 1;
 		else
@@ -594,10 +761,7 @@ static int do_check_string(ASN1_STRING *a, int cmp_type,
 		astrlen = ASN1_STRING_to_UTF8(&astr, a);
 		if (astrlen < 0)
 			return 0;
-		if (astrlen == (int)blen && !memcmp(astr, b, blen))
-			rv = 1;
-		else
-			rv = 0;
+		rv = equal(astr, astrlen, b, blen);
 		OPENSSL_free(astr);
 		return rv;
 		}
@@ -610,12 +774,29 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
 	X509_NAME *name = NULL;
 	int i;
 	int cnid;
+	int alt_type;
+	equal_fn equal;
 	if (check_type == GEN_EMAIL)
+		{
 		cnid = NID_pkcs9_emailAddress;
+		alt_type = V_ASN1_IA5STRING;
+		equal = equal_email;
+		}
 	else if (check_type == GEN_DNS)
+		{
 		cnid = NID_commonName;
+		alt_type = V_ASN1_IA5STRING;
+		if (flags & X509_CHECK_FLAG_NO_WILDCARDS)
+			equal = equal_nocase;
+		else
+			equal = equal_wildcard;
+		}
 	else
+		{
 		cnid = 0;
+		alt_type = V_ASN1_OCTET_STRING;
+		equal = equal_case;
+		}
 
 	if (chklen == 0)
 		chklen = strlen((const char *)chk);
@@ -624,11 +805,6 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
 	if (gens)
 		{
 		int rv = 0;
-		int alt_type;
-		if (cnid)
-			alt_type = V_ASN1_IA5STRING;
-		else
-			alt_type = V_ASN1_OCTET_STRING;
 		for (i = 0; i < sk_GENERAL_NAME_num(gens); i++)
 			{
 			GENERAL_NAME *gen;
@@ -642,7 +818,7 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
 				cstr = gen->d.dNSName;
 			else
 				cstr = gen->d.iPAddress;
-			if (do_check_string(cstr, alt_type, chk, chklen))
+			if (do_check_string(cstr, alt_type, equal, chk, chklen))
 				{
 				rv = 1;
 				break;
@@ -662,7 +838,7 @@ static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
 		ASN1_STRING *str;
 		ne = X509_NAME_get_entry(name, i);
 		str = X509_NAME_ENTRY_get_data(ne);
-		if (do_check_string(str, -1, chk, chklen))
+		if (do_check_string(str, -1, equal, chk, chklen))
 			return 1;
 		}
 	return 0;
diff --git a/crypto/x509v3/v3nametest.c b/crypto/x509v3/v3nametest.c
new file mode 100644
index 0000000..da42506
--- /dev/null
+++ b/crypto/x509v3/v3nametest.c
@@ -0,0 +1,370 @@
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <string.h>
+
+static const char *const names[] =
+	{
+	"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",
+	"example.net", "xn--rger-koa.example.com",
+	"a.example.com", "b.example.com",
+	"postmas...@example.com", "postmas...@example.com",
+	"postmas...@example.com",
+	NULL
+	};
+
+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: [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: [postmas...@example.com] does not match [postmas...@example.com]",
+	"set emailAddress: email: [postmas...@example.com] does not match [postmas...@example.com]",
+	"set emailAddress: email: [postmas...@example.com] does not match [postmas...@example.com]",
+	"set emailAddress: email: [postmas...@example.com] does not match [postmas...@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: [*.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: [postmas...@example.com] does not match [postmas...@example.com]",
+	"set rfc822Name: email: [postmas...@example.com] does not match [postmas...@example.com]",
+	"set rfc822Name: email: [postmas...@example.com] does not match [postmas...@example.com]",
+	"set rfc822Name: email: [postmas...@example.com] does not match [postmas...@example.com]",
+	NULL
+	};
+
+int
+is_exception(const char *msg)
+	{
+	const char *const *p;
+	for (p = exceptions; *p; ++p)
+		if (strcmp(msg, *p) == 0)
+			return 1;
+	return 0;
+	}
+
+static int set_cn(X509 *crt, ...)
+	{
+	int ret = 0;
+	X509_NAME *n = NULL;
+	va_list ap;
+	va_start(ap, crt);
+	n = X509_NAME_new();
+	if (n == NULL)
+		goto out;
+	while (1) {
+		int nid;
+		const char *name;
+		nid = va_arg(ap, int);
+		if (nid == 0)
+			break;
+		name = va_arg(ap, const char *);
+		if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_ASC,
+							(unsigned char *)name,
+						-1, -1, 1))
+			goto out;
+	}
+	if (!X509_set_subject_name(crt, n))
+		goto out;
+	ret = 1;
+ out:
+	X509_NAME_free(n);
+	va_end(ap);
+	return ret;
+	}
+
+/*
+int		X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc);
+X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex,
+			int nid, int crit, ASN1_OCTET_STRING *data);
+int		X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc);
+*/
+
+static int set_altname(X509 *crt, ...)
+	{
+	int ret = 0;
+	GENERAL_NAMES *gens = NULL;
+	GENERAL_NAME *gen = NULL;
+	ASN1_IA5STRING *ia5 = NULL;
+	unsigned char *gensbytes = NULL;
+	int gensbyteslen;
+	ASN1_OCTET_STRING *abytes = NULL;
+	X509_EXTENSION *ext = NULL;
+	va_list ap;
+	va_start(ap, crt);
+	gens = sk_GENERAL_NAME_new_null();
+	if (gens == NULL)
+		goto out;
+	while (1) {
+		int type;
+		const char *name;
+		type = va_arg(ap, int);
+		if (type == 0)
+			break;
+		name = va_arg(ap, const char *);
+
+		gen = GENERAL_NAME_new();
+		if (gen == NULL)
+			goto out;
+		ia5 = ASN1_IA5STRING_new();
+		if (ia5 == NULL)
+			goto out;
+		if (!ASN1_STRING_set(ia5, name, strlen(name)))
+			goto out;
+		switch (type)
+			{
+			case GEN_EMAIL:
+				gen->type = type;
+				gen->d.rfc822Name = ia5;
+				ia5 = NULL;
+				break;
+			case GEN_DNS:
+				gen->type = type;
+				gen->d.dNSName = ia5;
+				ia5 = NULL;
+				break;
+			default:
+				abort();
+			}
+		sk_GENERAL_NAME_push(gens, gen);
+		gen = NULL;
+	}
+	gensbyteslen = i2d_GENERAL_NAMES(gens, &gensbytes);
+	if (gensbyteslen <= 0)
+		goto out;
+	abytes = ASN1_OCTET_STRING_new();
+	if (abytes == NULL)
+		goto out;
+	if (!ASN1_OCTET_STRING_set(abytes, gensbytes, gensbyteslen))
+		goto out;
+	ext = X509_EXTENSION_create_by_NID(NULL, NID_subject_alt_name, -1, abytes);
+	if (ext == NULL)
+		goto out;
+	if (!X509_add_ext(crt, ext, -1))
+		goto out;
+	ret = 1;
+ out:
+	X509_EXTENSION_free(ext);
+	ASN1_OCTET_STRING_free(abytes);
+	OPENSSL_free(gensbytes);
+	ASN1_IA5STRING_free(ia5);
+	GENERAL_NAME_free(gen);
+	GENERAL_NAMES_free(gens);
+	va_end(ap);
+	return ret;
+	}
+
+static int set_cn1(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_commonName, name, 0);
+	}
+
+
+static int set_cn_and_email(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_commonName, name,
+		      NID_pkcs9_emailAddress, "du...@example.com", 0);
+	}
+
+static int set_cn2(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_commonName, "dummy value",
+		      NID_commonName, name, 0);
+	}
+
+static int set_cn3(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_commonName, name,
+		      NID_commonName, "dummy value", 0);
+	}
+
+static int set_email1(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_pkcs9_emailAddress, name, 0);
+	}
+
+static int set_email2(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_pkcs9_emailAddress, "du...@example.com",
+		      NID_pkcs9_emailAddress, name, 0);
+	}
+
+static int set_email3(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_pkcs9_emailAddress, name,
+		      NID_pkcs9_emailAddress, "du...@example.com", 0);
+	}
+
+static int set_email_and_cn(X509 *crt, const char *name)
+	{
+	return set_cn(crt, NID_pkcs9_emailAddress, name,
+		      NID_commonName, "www.example.org", 0);
+	}
+
+static int set_altname_dns(X509 *crt, const char *name)
+	{
+	return set_altname(crt, GEN_DNS, name, 0);
+	}
+
+static int set_altname_email(X509 *crt, const char *name)
+	{
+	return set_altname(crt, GEN_EMAIL, name, 0);
+	}
+
+struct set_name_fn
+	{
+	int (*fn)(X509 *, const char *);
+	const char *name;
+	int host;
+	int email;
+	};
+
+static const struct set_name_fn name_fns[] =
+	{
+	{set_cn1, "set CN", 1, 0},
+	{set_cn2, "set CN", 1, 0},
+	{set_cn3, "set CN", 1, 0},
+	{set_cn_and_email, "set CN", 1, 0},
+	{set_email1, "set emailAddress", 0, 1},
+	{set_email2, "set emailAddress", 0, 1},
+	{set_email3, "set emailAddress", 0, 1},
+	{set_email_and_cn, "set emailAddress", 0, 1},
+	{set_altname_dns, "set dnsName", 1, 0},
+	{set_altname_email, "set rfc822Name", 0, 1},
+	{NULL, NULL, 0}
+	};
+
+static X509 *make_cert()
+	{
+	X509 *ret = NULL;
+	X509 *crt = NULL;
+	X509_NAME *issuer = NULL;
+	crt = X509_new();
+	if (crt == NULL)
+		goto out;
+	if (!X509_set_version(crt, 3))
+		goto out;
+	ret = crt;
+	crt = NULL;
+ out:
+	X509_NAME_free(issuer);
+	return ret;
+	}
+
+static int errors;
+
+static void check_message(const struct set_name_fn *fn, const char *op,
+			  const char *nameincert, int match, const char *name)
+	{
+	char msg[1024];
+	if (match < 0)
+		return;
+	snprintf(msg, sizeof(msg), "%s: %s: [%s] %s [%s]",
+		 fn->name, op, nameincert,
+		 match ? "matches" : "does not match", name);
+	if (is_exception(msg))
+		return;
+	puts(msg);
+	++errors;
+	}
+
+static void run_cert(X509 *crt, const char *nameincert,
+		     const struct set_name_fn *fn)
+	{
+	const char *const *pname = names;
+	while (*pname)
+		{
+		int samename = strcasecmp(nameincert, *pname) == 0;
+		size_t namelen = strlen(*pname);
+		char *name = malloc(namelen);
+		int match, ret;
+		memcpy(name, *pname, namelen);
+
+		ret = X509_check_host(crt, (const unsigned char *)name,
+				      namelen, 0);
+		match = -1;
+		if (fn->host)
+			{
+			if (ret && !samename)
+				match = 1;
+			if (!ret && samename)
+				match = 0;
+			}
+		else if (ret)
+			match = 1;
+		check_message(fn, "host", nameincert, match, *pname);
+
+		ret = X509_check_host(crt, (const unsigned char *)name,
+				      namelen, X509_CHECK_FLAG_NO_WILDCARDS);
+		match = -1;
+		if (fn->host)
+			{
+			if (ret && !samename)
+				match = 1;
+			if (!ret && samename)
+				match = 0;
+			}
+		else if (ret)
+			match = 1;
+		check_message(fn, "host-no-wildcards",
+			      nameincert, match, *pname);
+
+		ret = X509_check_email(crt, (const unsigned char *)name,
+				       namelen, 0);
+		match = -1;
+		if (fn->email)
+			{
+			if (ret && !samename)
+				match = 1;
+			if (!ret && samename && strchr(nameincert, '@') != NULL)
+				match = 0;
+			}
+		else if (ret)
+			match = 1;
+		check_message(fn, "email", nameincert, match, *pname);
+		++pname;
+		free(name);
+		}
+	}
+
+int
+main(void)
+	{
+	const struct set_name_fn *pfn = name_fns;
+	while (pfn->name) {
+		const char *const *pname = names;
+		while (*pname)
+			{
+			X509 *crt = make_cert();
+			if (crt == NULL)
+				{
+				fprintf(stderr, "make_cert failed\n");
+				return 1;
+				}
+			if (!pfn->fn(crt, *pname))
+				{
+				fprintf(stderr, "X509 name setting failed\n");
+				return 1;
+				}
+			run_cert(crt, *pname, pfn);
+			X509_free(crt);
+			++pname;
+			}
+		++pfn;
+	}
+	return errors > 0 ? 1 : 0;
+	}
diff --git a/crypto/x509v3/x509v3.h b/crypto/x509v3/x509v3.h
index 23f7091..272374e 100644
--- a/crypto/x509v3/x509v3.h
+++ b/crypto/x509v3/x509v3.h
@@ -704,6 +704,8 @@ STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x);
 
 /* 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. */
+#define X509_CHECK_FLAG_NO_WILDCARDS	0x2
 
 int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen,
 					unsigned int flags);
diff --git a/doc/crypto/X509_check_host.pod b/doc/crypto/X509_check_host.pod
new file mode 100644
index 0000000..e8d74d6
--- /dev/null
+++ b/doc/crypto/X509_check_host.pod
@@ -0,0 +1,73 @@
+=pod
+
+=head1 NAME
+
+X509_check_host, X509_check_email, X509_check_ip,
+X509_check_ip_asc - X.509 certificate matching
+
+=head1 SYNOPSIS
+
+ #include <openssl/x509.h>
+
+ int X509_check_host(X509 *, const unsigned char *name,
+                     size_t namelen, unsigned int flags);
+ int X509_check_email(X509 *, const unsigned char *address,
+                     size_t addresslen, unsigned int flags);
+ int X509_check_ip(X509 *, const unsigned char *address,
+                   size_t addresslen, unsigned int flags);
+ int X509_check_ip_asc(X509 *, const char *address, unsigned int flags);
+
+=head1 DESCRIPTION
+
+The certificate matching functions are intended to be called to check
+if a certificate matches a given host name, email address, or IP
+address.  The validity of the certificate and its trust level has to
+be checked by other means.
+
+B<X509_check_host> checks if the certificate matches the specified
+host name, which must be encoded in the preferred name syntax
+described in section 3.5 of RFC 1034.  The B<namelen> argument must be
+the number of characters in the name string.
+
+B<X509_check_email> checks if the certificate matches the specified
+email address.  Only the mailbox syntax of RFC 822 is supported,
+comments are not allowed, and no attempt is made to normalize quoted
+characters.  The B<addresslen> argument must be the number of
+characters in the address string.
+
+B<X509_check_ip> checks if the certificate matches a specified IPv4 or
+IPv6 address.  The B<address> array is in binary format, in network
+byte order.  The length is either 4 (IPv4) or 16 (IPv6).  Only
+explicitly marked addresses in the certificates are considered; IP
+addresses stored in DNS names and Common Names are ignored.
+
+The B<X509_check_ip_asc> is similar, except that the NUL-terminated
+string B<address> is first converted to the internal representation.
+
+The B<flags> argument is usually 0.  It can be the bitwise OR of the
+flags B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT>,
+B<X509_CHECK_FLAG_NO_WILDCARDS>.
+
+The B<X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT> flag causes the function
+to check the subject DN even if the certificate contains a subject
+alternative name extension is present; the default is to ignore the
+subject DN in preference of the extension.
+
+If present, B<X509_CHECK_FLAG_NO_WILDCARDS> disables wildcard
+expansion; this only applies to B<X509_check_host>.
+
+=head1 RETURN VALUES
+
+The functions return 1 on success, and 0 on failure.  Failure can be
+caused by a mismatch an internal error (most likely a memory
+allocation failure), or a malformed IP address string.
+
+=head1 SEE ALSO
+
+L<SSL_get_verify_result(3)|SSL_get_verify_result(3)>
+
+=head1 HISTORY
+
+These functions were added in OpenSSL 1.1.0.
+
+=cut
diff --git a/test/Makefile b/test/Makefile
index 77caa25..7210165 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -64,6 +64,7 @@ EVPTEST=	evp_test
 IGETEST=	igetest
 JPAKETEST=	jpaketest
 SRPTEST=	srptest
+V3NAMETEST=	v3nametest
 FIPS_SHATEST=	fips_shatest
 FIPS_DESTEST=	fips_desmovs
 FIPS_RANDTEST=	fips_randtest
@@ -94,7 +95,8 @@ EXE=	$(BNTEST)$(EXE_EXT) $(ECTEST)$(EXE_EXT)  $(ECDSATEST)$(EXE_EXT) $(ECDHTEST)
 	$(RANDTEST)$(EXE_EXT) $(DHTEST)$(EXE_EXT) $(ENGINETEST)$(EXE_EXT) \
 	$(BFTEST)$(EXE_EXT) $(CASTTEST)$(EXE_EXT) $(SSLTEST)$(EXE_EXT) \
 	$(EXPTEST)$(EXE_EXT) $(DSATEST)$(EXE_EXT) $(RSATEST)$(EXE_EXT) \
-	$(EVPTEST)$(EXE_EXT) $(IGETEST)$(EXE_EXT) $(JPAKETEST)$(EXE_EXT) $(SRPTEST)$(EXE_EXT)
+	$(EVPTEST)$(EXE_EXT) $(IGETEST)$(EXE_EXT) $(JPAKETEST)$(EXE_EXT) $(SRPTEST)$(EXE_EXT) \
+	$(V3NAMETEST)$(EXE_EXT)
 
 FIPSEXE=$(FIPS_SHATEST)$(EXE_EXT) $(FIPS_DESTEST)$(EXE_EXT) \
 	$(FIPS_RANDTEST)$(EXE_EXT) $(FIPS_AESTEST)$(EXE_EXT) \
@@ -122,7 +124,7 @@ OBJ=	$(BNTEST).o $(ECTEST).o  $(ECDSATEST).o $(ECDHTEST).o $(IDEATEST).o \
 	$(FIPS_DSSVS).o $(FIPS_DSATEST).o $(FIPS_RNGVS).o $(FIPS_DRBGVS).o \
 	$(FIPS_TEST_SUITE).o $(FIPS_DHVS).o $(FIPS_ECDSAVS).o \
 	$(FIPS_ECDHVS).o $(FIPS_CMACTEST).o $(FIPS_ALGVS).o \
-	$(EVPTEST).o $(IGETEST).o $(JPAKETEST).o
+	$(EVPTEST).o $(IGETEST).o $(JPAKETEST).o $(V3NAMETEST).o
 SRC=	$(BNTEST).c $(ECTEST).c  $(ECDSATEST).c $(ECDHTEST).c $(IDEATEST).c \
 	$(MD2TEST).c  $(MD4TEST).c $(MD5TEST).c \
 	$(HMACTEST).c $(WPTEST).c \
@@ -136,7 +138,7 @@ SRC=	$(BNTEST).c $(ECTEST).c  $(ECDSATEST).c $(ECDHTEST).c $(IDEATEST).c \
 	$(FIPS_DSSVS).c $(FIPS_DSATEST).c $(FIPS_RNGVS).c $(FIPS_DRBGVS).c \
 	$(FIPS_TEST_SUITE).c $(FIPS_DHVS).c $(FIPS_ECDSAVS).c \
 	$(FIPS_ECDHVS).c $(FIPS_CMACTEST).c $(FIPS_ALGVS).c \
-	$(EVPTEST).c $(IGETEST).c $(JPAKETEST).c
+	$(EVPTEST).c $(IGETEST).c $(JPAKETEST).c $(V3NAMETEST).c
 
 EXHEADER= 
 HEADER=	$(EXHEADER)
@@ -183,7 +185,7 @@ alltests: \
 	test_enc test_x509 test_rsa test_crl test_sid \
 	test_gen test_req test_pkcs7 test_verify test_dh test_dsa \
 	test_ss test_ca test_engine test_evp test_ssl test_tsa test_ige \
-	test_jpake test_srp test_cms
+	test_jpake test_srp test_cms test_v3name
 
 test_evp:
 	../util/shlib_wrap.sh ./$(EVPTEST) evptests.txt
@@ -361,6 +363,10 @@ test_srp: $(SRPTEST)$(EXE_EXT)
 	@echo "Test SRP"
 	../util/shlib_wrap.sh ./srptest
 
+test_v3name: $(V3NAMETEST)$(EXE_EXT)
+	@echo "Test X509v3_check_*"
+	../util/shlib_wrap.sh ./$(V3NAMETEST)
+
 lint:
 	lint -DLINT $(INCLUDES) $(SRC)>fluff
 
@@ -583,6 +589,9 @@ $(JPAKETEST)$(EXE_EXT): $(JPAKETEST).o $(DLIBCRYPTO)
 $(SRPTEST)$(EXE_EXT): $(SRPTEST).o $(DLIBCRYPTO)
 	@target=$(SRPTEST); $(BUILD_CMD)
 
+$(V3NAMETEST)$(EXE_EXT): $(V3NAMETEST).o $(DLIBCRYPTO)
+	@target=$(V3NAMETEST); $(BUILD_CMD)
+
 #$(AESTEST).o: $(AESTEST).c
 #	$(CC) -c $(CFLAGS) -DINTERMEDIATE_VALUE_KAT -DTRACE_KAT_MCT $(AESTEST).c
 

Reply via email to