Multiple verifier reference identities.

(Imported from upstream's 8abffa4a73fcbf6536e0a42d736ed9211a8204ea,
9624b50d51de25bb2e3a72e81fe45032d80ea5c2 and
41e3ebd5abacfdf98461cdeb6fa97a4175b7aad3.)

Change-Id: Ic9099eb5704b19b4500229e89351371cc6184f9d
diff --git a/crypto/x509/vpm_int.h b/crypto/x509/vpm_int.h
index dd33f88..6b670af 100644
--- a/crypto/x509/vpm_int.h
+++ b/crypto/x509/vpm_int.h
@@ -60,7 +60,7 @@
 
 struct X509_VERIFY_PARAM_ID_st
 	{
-	unsigned char *host;	/* If not NULL hostname to match */
+	STACK_OF(OPENSSL_STRING) *hosts;	/* Set of acceptable names */
 	unsigned int hostflags;	/* Flags to control matching features */
 	unsigned char *email;	/* If not NULL email address to match */
 	size_t emaillen;
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index 1650b77..29a2545 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -704,12 +704,27 @@
 	return ctx->verify_cb(0, ctx);
 	}
 
+static int check_hosts(X509 *x, X509_VERIFY_PARAM_ID *id)
+	{
+	size_t i;
+	size_t n = sk_OPENSSL_STRING_num(id->hosts);
+	unsigned char *name;
+
+	for (i = 0; i < n; ++i)
+		{
+		name = (unsigned char *)sk_OPENSSL_STRING_value(id->hosts, i);
+		if (X509_check_host(x, name, strlen((const char*) name), id->hostflags) > 0)
+			return 1;
+		}
+	return n == 0;
+	}
+
 static int check_id(X509_STORE_CTX *ctx)
 	{
 	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, strlen((const char*) id->host), id->hostflags) <= 0)
+	if (id->hosts && check_hosts(x, id) <= 0)
 		{
 		if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH))
 			return 0;
diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c
index 7f646d8..d326637 100644
--- a/crypto/x509/x509_vpm.c
+++ b/crypto/x509/x509_vpm.c
@@ -58,6 +58,7 @@
 #include <openssl/lhash.h>
 #include <openssl/mem.h>
 #include <openssl/obj.h>
+#include <openssl/stack.h>
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
@@ -66,6 +67,59 @@
 
 /* X509_VERIFY_PARAM functions */
 
+#define SET_HOST 0
+#define ADD_HOST 1
+
+static char *str_copy(char *s) { return OPENSSL_strdup(s); }
+static void str_free(char *s) { OPENSSL_free(s); }
+
+#define string_stack_free(sk) sk_OPENSSL_STRING_pop_free(sk, str_free)
+
+static int int_x509_param_set_hosts(X509_VERIFY_PARAM_ID *id, int mode,
+				    const unsigned char *name, size_t namelen)
+	{
+	char *copy;
+
+	/*
+	 * Refuse names with embedded NUL bytes.
+	 * XXX: Do we need to push an error onto the error stack?
+	 */
+	if (name && memchr(name, '\0', namelen))
+		 return 0;
+
+	if (mode == SET_HOST && id->hosts)
+		{
+		string_stack_free(id->hosts);
+		id->hosts = NULL;
+		}
+	if (name == NULL || namelen == 0)
+		return 1;
+
+	copy = BUF_strndup((char *)name, namelen);
+	if (copy == NULL)
+		return 0;
+
+	if (id->hosts == NULL &&
+	    (id->hosts = sk_OPENSSL_STRING_new_null()) == NULL)
+		{
+		OPENSSL_free(copy);
+		return 0;
+		}
+
+	if (!sk_OPENSSL_STRING_push(id->hosts, copy))
+		{
+		OPENSSL_free(copy);
+		if (sk_OPENSSL_STRING_num(id->hosts) == 0)
+			{
+			sk_OPENSSL_STRING_free(id->hosts);
+			id->hosts = NULL;
+			}
+		return 0;
+		}
+
+	return 1;
+	}
+
 static void x509_verify_param_zero(X509_VERIFY_PARAM *param)
 	{
 	X509_VERIFY_PARAM_ID *paramid;
@@ -84,10 +138,10 @@
 		param->policies = NULL;
 		}
 	paramid = param->id;
-	if (paramid->host)
+	if (paramid->hosts)
 		{
-		OPENSSL_free(paramid->host);
-		paramid->host = NULL;
+		string_stack_free(paramid->hosts);
+		paramid->hosts = NULL;
 		}
 	if (paramid->email)
 		{
@@ -231,11 +285,23 @@
 			return 0;
 		}
 
-	if (test_x509_verify_param_copy_id(host, NULL))
+	/* Copy the host flags if and only if we're copying the host list */
+	if (test_x509_verify_param_copy_id(hosts, NULL))
 		{
-		if (!X509_VERIFY_PARAM_set1_host(dest, id->host, strlen((const char*) id->host)))
-			return 0;
-		dest->id->hostflags = id->hostflags;
+		if (dest->id->hosts)
+			{
+			string_stack_free(dest->id->hosts);
+			dest->id->hosts = NULL;
+			}
+		if (id->hosts)
+			{
+			dest->id->hosts =
+			    sk_OPENSSL_STRING_deep_copy(id->hosts,
+							str_copy, str_free);
+			if (dest->id->hosts == NULL)
+				return 0;
+			dest->id->hostflags = id->hostflags;
+			}
 		}
 
 	if (test_x509_verify_param_copy_id(email, NULL))
@@ -395,7 +461,13 @@
 int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
 				const unsigned char *name, size_t namelen)
 	{
-	return int_x509_param_set1(&param->id->host, NULL, name, namelen);
+	return int_x509_param_set_hosts(param->id, SET_HOST, name, namelen);
+	}
+
+int X509_VERIFY_PARAM_add1_host(X509_VERIFY_PARAM *param,
+				const unsigned char *name, size_t namelen)
+	{
+	return int_x509_param_set_hosts(param->id, ADD_HOST, name, namelen);
 	}
 
 void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param,
diff --git a/include/openssl/x509_vfy.h b/include/openssl/x509_vfy.h
index c65bfde..bd84bc8 100644
--- a/include/openssl/x509_vfy.h
+++ b/include/openssl/x509_vfy.h
@@ -555,6 +555,9 @@
 
 OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
 				const unsigned char *name, size_t namelen);
+OPENSSL_EXPORT int X509_VERIFY_PARAM_add1_host(X509_VERIFY_PARAM *param,
+					       const unsigned char *name,
+					       size_t namelen);
 OPENSSL_EXPORT void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param,
 					unsigned int flags);
 OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,