New functions to check a hostname email or IP address against a
certificate. Add options to s_client, s_server and x509 utilities
to print results of checks.
diff --git a/CHANGES b/CHANGES
index be586a2..79d31f2 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,11 @@
 
  Changes between 1.0.x and 1.1.0  [xx XXX xxxx]
 
+  *) New functions to check a hostname email or IP address against a
+     certificate. Add options to s_client, s_server and x509 utilities
+     to print results of checks against a certificate.
+     [Steve Henson]
+
   *) Add -rev test option to s_server to just reverse order of characters
      received by client and send back to server. Also prints an abbreviated
      summary of the connection parameters.
diff --git a/apps/apps.c b/apps/apps.c
index 490ae3b..0ce0af5 100644
--- a/apps/apps.c
+++ b/apps/apps.c
@@ -2791,6 +2791,35 @@
 	}
 #endif  /* !OPENSSL_NO_TLSEXT && !OPENSSL_NO_NEXTPROTONEG */
 
+void print_cert_checks(BIO *bio, X509 *x,
+				const unsigned char *checkhost,
+				const unsigned char *checkemail,
+				const char *checkip)
+	{
+	if (x == NULL)
+		return;
+	if (checkhost)
+		{
+		BIO_printf(bio, "Hostname %s does%s match certificate\n",
+				checkhost, X509_check_host(x, checkhost, 0, 0)
+						? "" : " NOT");
+		}
+
+	if (checkemail)
+		{
+		BIO_printf(bio, "Email %s does%s match certificate\n",
+				checkemail, X509_check_email(x, checkemail, 0,
+						0) ? "" : " NOT");
+		}
+
+	if (checkip)
+		{
+		BIO_printf(bio, "IP %s does%s match certificate\n",
+				checkip, X509_check_ip_asc(x, checkip,
+						0) ? "" : " NOT");
+		}
+	}
+
 /*
  * Platform-specific sections
  */
diff --git a/apps/apps.h b/apps/apps.h
index c1ca99d..4c9f95a 100644
--- a/apps/apps.h
+++ b/apps/apps.h
@@ -335,6 +335,11 @@
 unsigned char *next_protos_parse(unsigned short *outlen, const char *in);
 #endif  /* !OPENSSL_NO_TLSEXT && !OPENSSL_NO_NEXTPROTONEG */
 
+void print_cert_checks(BIO *bio, X509 *x,
+				const unsigned char *checkhost,
+				const unsigned char *checkemail,
+				const char *checkip);
+
 #define FORMAT_UNDEF    0
 #define FORMAT_ASN1     1
 #define FORMAT_TEXT     2
diff --git a/apps/s_apps.h b/apps/s_apps.h
index b1ae531..31b8923 100644
--- a/apps/s_apps.h
+++ b/apps/s_apps.h
@@ -191,3 +191,7 @@
 			int *badarg, BIO *err, SSL_EXCERT **pexc);
 int load_excert(SSL_EXCERT **pexc, BIO *err);
 void print_ssl_summary(BIO *bio, SSL *s);
+void print_ssl_cert_checks(BIO *bio, SSL *s,
+				const unsigned char *checkhost,
+				const unsigned char *checkemail,
+				const char *checkip);
diff --git a/apps/s_cb.c b/apps/s_cb.c
index e339a6c..5a2222e 100644
--- a/apps/s_cb.c
+++ b/apps/s_cb.c
@@ -1533,3 +1533,16 @@
 		ssl_print_tmp_key(bio, s);
 	}
 
+void print_ssl_cert_checks(BIO *bio, SSL *s,
+				const unsigned char *checkhost,
+				const unsigned char *checkemail,
+				const char *checkip)
+	{
+	X509 *peer;
+	peer = SSL_get_peer_certificate(s);
+	if (peer)
+		{
+		print_cert_checks(bio, peer, checkhost, checkemail, checkip);
+		X509_free(peer);
+		}
+	}
diff --git a/apps/s_client.c b/apps/s_client.c
index a7b150b..5ecc957 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -634,6 +634,9 @@
 #endif
 	SSL_EXCERT *exc = NULL;
 
+	unsigned char *checkhost = NULL, *checkemail = NULL;
+	char *checkip = NULL;
+
 	meth=SSLv23_client_method();
 
 	apps_startup();
@@ -993,6 +996,21 @@
 			client_sigalgs= *(++argv);
 			}
 #endif
+		else if (strcmp(*argv,"-checkhost") == 0)
+			{
+			if (--argc < 1) goto bad;
+			checkhost=(unsigned char *)*(++argv);
+			}
+		else if (strcmp(*argv,"-checkemail") == 0)
+			{
+			if (--argc < 1) goto bad;
+			checkemail=(unsigned char *)*(++argv);
+			}
+		else if (strcmp(*argv,"-checkip") == 0)
+			{
+			if (--argc < 1) goto bad;
+			checkip=*(++argv);
+			}
 #ifndef OPENSSL_NO_JPAKE
 		else if (strcmp(*argv,"-jpake") == 0)
 			{
@@ -1628,6 +1646,8 @@
 						"CONNECTION ESTABLISHED\n");
 					print_ssl_summary(bio_err, con);
 					}
+				print_ssl_cert_checks(bio_err, con, checkhost,
+							checkemail, checkip);
 				print_stuff(bio_c_out,con,full_log);
 				if (full_log > 0) full_log--;
 
diff --git a/apps/s_server.c b/apps/s_server.c
index e89f057..00dc219 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -1003,6 +1003,10 @@
 	char *srp_verifier_file = NULL;
 #endif
 	SSL_EXCERT *exc = NULL;
+
+	unsigned char *checkhost = NULL, *checkemail = NULL;
+	char *checkip = NULL;
+
 	meth=SSLv23_server_method();
 
 	local_argc=argc;
@@ -1260,6 +1264,21 @@
 			client_sigalgs= *(++argv);
 			}
 #endif
+		else if (strcmp(*argv,"-checkhost") == 0)
+			{
+			if (--argc < 1) goto bad;
+			checkhost=(unsigned char *)*(++argv);
+			}
+		else if (strcmp(*argv,"-checkemail") == 0)
+			{
+			if (--argc < 1) goto bad;
+			checkemail=(unsigned char *)*(++argv);
+			}
+		else if (strcmp(*argv,"-checkip") == 0)
+			{
+			if (--argc < 1) goto bad;
+			checkip=*(++argv);
+			}
 		else if	(strcmp(*argv,"-msg") == 0)
 			{ s_msg=1; }
 		else if	(strcmp(*argv,"-msgfile") == 0)
@@ -2661,6 +2680,8 @@
 	if (s_brief)
 		print_ssl_summary(bio_err, con);
 
+	print_ssl_cert_checks(bio_err, con, checkhost, checkemail, checkip);
+
 	PEM_write_bio_SSL_SESSION(bio_s_out,SSL_get_session(con));
 
 	peer=SSL_get_peer_certificate(con);
diff --git a/apps/x509.c b/apps/x509.c
index e9f1163..788eb7b 100644
--- a/apps/x509.c
+++ b/apps/x509.c
@@ -208,6 +208,8 @@
 	int need_rand = 0;
 	int checkend=0,checkoffset=0;
 	unsigned long nmflag = 0, certflag = 0;
+	unsigned char *checkhost = NULL, *checkemail = NULL;
+	char *checkip = NULL;
 #ifndef OPENSSL_NO_ENGINE
 	char *engine=NULL;
 #endif
@@ -456,6 +458,21 @@
 			checkoffset=atoi(*(++argv));
 			checkend=1;
 			}
+		else if (strcmp(*argv,"-checkhost") == 0)
+			{
+			if (--argc < 1) goto bad;
+			checkhost=(unsigned char *)*(++argv);
+			}
+		else if (strcmp(*argv,"-checkemail") == 0)
+			{
+			if (--argc < 1) goto bad;
+			checkemail=(unsigned char *)*(++argv);
+			}
+		else if (strcmp(*argv,"-checkip") == 0)
+			{
+			if (--argc < 1) goto bad;
+			checkip=*(++argv);
+			}
 		else if (strcmp(*argv,"-noout") == 0)
 			noout= ++num;
 		else if (strcmp(*argv,"-trustout") == 0)
@@ -1061,6 +1078,8 @@
 		goto end;
 		}
 
+	print_cert_checks(STDout, x, checkhost, checkemail, checkip);
+
 	if (noout)
 		{
 		ret=0;
diff --git a/crypto/x509v3/v3_utl.c b/crypto/x509v3/v3_utl.c
index e030234..db9f05e 100644
--- a/crypto/x509v3/v3_utl.c
+++ b/crypto/x509v3/v3_utl.c
@@ -568,6 +568,134 @@
 	sk_OPENSSL_STRING_pop_free(sk, str_free);
 }
 
+/* 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,
+				const unsigned char *b, size_t blen)
+	{
+	if (!a->data || !a->length)
+		return 0;
+	if (cmp_type > 0)
+		{
+		if (cmp_type != a->type)
+			return 0;
+		if (a->length == (int)blen && !memcmp(a->data, b, blen))
+			return 1;
+		else
+			return 0;
+		}
+	else
+		{
+		int astrlen, rv;
+		unsigned char *astr;
+		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;
+		OPENSSL_free(astr);
+		return rv;
+		}
+	}
+
+static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
+					unsigned int flags, int check_type)
+	{
+	GENERAL_NAMES *gens = NULL;
+	X509_NAME *name = NULL;
+	int i;
+	int cnid;
+	if (check_type == GEN_EMAIL)
+		cnid = NID_pkcs9_emailAddress;
+	else if (check_type == GEN_DNS)
+		cnid = NID_commonName;
+	else
+		cnid = 0;
+
+	if (chklen == 0)
+		chklen = strlen((const char *)chk);
+
+	gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
+	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;
+			ASN1_STRING *cstr;
+			gen = sk_GENERAL_NAME_value(gens, i);
+			if(gen->type != check_type)
+				continue;
+			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, chk, chklen))
+				{
+				rv = 1;
+				break;
+				}
+			}
+		GENERAL_NAMES_free(gens);
+		if (rv)
+			return 1;
+		if (!(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) || !cnid)
+			return 0;
+		}
+	i = -1;
+	name = X509_get_subject_name(x);
+	while((i = X509_NAME_get_index_by_NID(name, cnid, i)) >= 0)
+		{
+		X509_NAME_ENTRY *ne;
+		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))
+			return 1;
+		}
+	return 0;
+	}
+
+int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen,
+					unsigned int flags)
+	{
+	return do_x509_check(x, chk, chklen, flags, GEN_DNS);
+	}
+
+int X509_check_email(X509 *x, const unsigned char *chk, size_t chklen,
+					unsigned int flags)
+	{
+	return do_x509_check(x, chk, chklen, flags, GEN_EMAIL);
+	}
+
+int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen,
+					unsigned int flags)
+	{
+	return do_x509_check(x, chk, chklen, flags, GEN_IPADD);
+	}
+
+int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags)
+	{
+	unsigned char ipout[16];
+	int iplen;
+	iplen = a2i_ipadd(ipout, ipasc);
+	if (iplen == 0)
+		return 0;
+	return do_x509_check(x, ipout, (size_t)iplen, flags, GEN_IPADD);
+	}
+
 /* Convert IP addresses both IPv4 and IPv6 into an 
  * OCTET STRING compatible with RFC3280.
  */
diff --git a/crypto/x509v3/x509v3.h b/crypto/x509v3/x509v3.h
index bf40999..23f7091 100644
--- a/crypto/x509v3/x509v3.h
+++ b/crypto/x509v3/x509v3.h
@@ -700,6 +700,18 @@
 STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email(X509_REQ *x);
 void X509_email_free(STACK_OF(OPENSSL_STRING) *sk);
 STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x);
+/* Flags for X509_check_* functions */
+
+/* Always check subject name for host match even if subject alt names present */
+#define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT	0x1
+
+int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen,
+					unsigned int flags);
+int X509_check_email(X509 *x, const unsigned char *chk, size_t chklen,
+					unsigned int flags);
+int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen,
+					unsigned int flags);
+int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags);
 
 ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc);
 ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc);