Blame SOURCES/openldap-tlso-use-openssl-api-to-verify-host.patch

2ba5f0
From f2978fefa13eb92b73922e49d2f6c12b4f92ea85 Mon Sep 17 00:00:00 2001
2ba5f0
From: Christian Heimes <christian@python.org>
2ba5f0
Date: Fri, 10 Jan 2020 18:35:02 +0100
2ba5f0
Subject: [PATCH] Use OpenSSL API to verify host
2ba5f0
2ba5f0
Replace custom hostname and IP address verification with OpenSSL 1.0.2
2ba5f0
APIs.
2ba5f0
---
2ba5f0
 libraries/libldap/tls_o.c | 184 ++++++--------------------------------
2ba5f0
 1 file changed, 28 insertions(+), 156 deletions(-)
2ba5f0
2ba5f0
diff --git a/libraries/libldap/tls_o.c b/libraries/libldap/tls_o.c
2ba5f0
index e52c5507c..5adf7b74f 100644
2ba5f0
--- a/libraries/libldap/tls_o.c
2ba5f0
+++ b/libraries/libldap/tls_o.c
2ba5f0
@@ -660,25 +660,15 @@ tlso_session_peer_dn( tls_session *sess, struct berval *der_dn )
2ba5f0
 	return 0;
2ba5f0
 }
2ba5f0
 
2ba5f0
-/* what kind of hostname were we given? */
2ba5f0
-#define	IS_DNS	0
2ba5f0
-#define	IS_IP4	1
2ba5f0
-#define	IS_IP6	2
2ba5f0
-
2ba5f0
 static int
2ba5f0
 tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in )
2ba5f0
 {
2ba5f0
 	tlso_session *s = (tlso_session *)sess;
2ba5f0
-	int i, ret = LDAP_LOCAL_ERROR;
2ba5f0
+	int ret = LDAP_LOCAL_ERROR;
2ba5f0
 	X509 *x;
2ba5f0
 	const char *name;
2ba5f0
-	char *ptr;
2ba5f0
-	int ntype = IS_DNS, nlen;
2ba5f0
-#ifdef LDAP_PF_INET6
2ba5f0
-	struct in6_addr addr;
2ba5f0
-#else
2ba5f0
-	struct in_addr addr;
2ba5f0
-#endif
2ba5f0
+	int flags = X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS;
2ba5f0
+	ASN1_OCTET_STRING *ip;
2ba5f0
 
2ba5f0
 	if( ldap_int_hostname &&
2ba5f0
 		( !name_in || !strcasecmp( name_in, "localhost" ) ) )
2ba5f0
@@ -687,7 +677,6 @@ tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in )
2ba5f0
 	} else {
2ba5f0
 		name = name_in;
2ba5f0
 	}
2ba5f0
-	nlen = strlen(name);
2ba5f0
 
2ba5f0
 	x = tlso_get_cert(s);
2ba5f0
 	if (!x) {
2ba5f0
@@ -619,150 +619,32 @@ tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in )
2ba5f0
 		return LDAP_SUCCESS;
2ba5f0
 	}
2ba5f0
 
2ba5f0
-#ifdef LDAP_PF_INET6
2ba5f0
-	if (inet_pton(AF_INET6, name, &addr)) {
2ba5f0
-		ntype = IS_IP6;
2ba5f0
-	} else 
2ba5f0
-#endif
2ba5f0
-	if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) {
2ba5f0
-		if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4;
2ba5f0
-	}
2ba5f0
-	
2ba5f0
-	i = X509_get_ext_by_NID(x, NID_subject_alt_name, -1);
2ba5f0
-	if (i >= 0) {
2ba5f0
-		X509_EXTENSION *ex;
2ba5f0
-		STACK_OF(GENERAL_NAME) *alt;
2ba5f0
-
2ba5f0
-		ex = X509_get_ext(x, i);
2ba5f0
-		alt = X509V3_EXT_d2i(ex);
2ba5f0
-		if (alt) {
2ba5f0
-			int n, len2 = 0;
2ba5f0
-			char *domain = NULL;
2ba5f0
-			GENERAL_NAME *gn;
2ba5f0
-
2ba5f0
-			if (ntype == IS_DNS) {
2ba5f0
-				domain = strchr(name, '.');
2ba5f0
-				if (domain) {
2ba5f0
-					len2 = nlen - (domain-name);
2ba5f0
-				}
2ba5f0
-			}
2ba5f0
-			n = sk_GENERAL_NAME_num(alt);
2ba5f0
-			for (i=0; i
2ba5f0
-				char *sn;
2ba5f0
-				int sl;
2ba5f0
-				gn = sk_GENERAL_NAME_value(alt, i);
2ba5f0
-				if (gn->type == GEN_DNS) {
2ba5f0
-					if (ntype != IS_DNS) continue;
2ba5f0
-
2ba5f0
-					sn = (char *) ASN1_STRING_data(gn->d.ia5);
2ba5f0
-					sl = ASN1_STRING_length(gn->d.ia5);
2ba5f0
-
2ba5f0
-					/* ignore empty */
2ba5f0
-					if (sl == 0) continue;
2ba5f0
-
2ba5f0
-					/* Is this an exact match? */
2ba5f0
-					if ((nlen == sl) && !strncasecmp(name, sn, nlen)) {
2ba5f0
-						break;
2ba5f0
-					}
2ba5f0
-
2ba5f0
-					/* Is this a wildcard match? */
2ba5f0
-					if (domain && (sn[0] == '*') && (sn[1] == '.') &&
2ba5f0
-						(len2 == sl-1) && !strncasecmp(domain, &sn[1], len2))
2ba5f0
-					{
2ba5f0
-						break;
2ba5f0
-					}
2ba5f0
-
2ba5f0
-				} else if (gn->type == GEN_IPADD) {
2ba5f0
-					if (ntype == IS_DNS) continue;
2ba5f0
-
2ba5f0
-					sn = (char *) ASN1_STRING_data(gn->d.ia5);
2ba5f0
-					sl = ASN1_STRING_length(gn->d.ia5);
2ba5f0
-
2ba5f0
-#ifdef LDAP_PF_INET6
2ba5f0
-					if (ntype == IS_IP6 && sl != sizeof(struct in6_addr)) {
2ba5f0
-						continue;
2ba5f0
-					} else
2ba5f0
-#endif
2ba5f0
-					if (ntype == IS_IP4 && sl != sizeof(struct in_addr)) {
2ba5f0
-						continue;
2ba5f0
-					}
2ba5f0
-					if (!memcmp(sn, &addr, sl)) {
2ba5f0
-						break;
2ba5f0
-					}
2ba5f0
-				}
2ba5f0
-			}
2ba5f0
-
2ba5f0
-			GENERAL_NAMES_free(alt);
2ba5f0
-			if (i < n) {	/* Found a match */
2ba5f0
-				ret = LDAP_SUCCESS;
2ba5f0
-			}
2ba5f0
-		}
2ba5f0
-	}
2ba5f0
-
2ba5f0
-	if (ret != LDAP_SUCCESS) {
2ba5f0
-		X509_NAME *xn;
2ba5f0
-		X509_NAME_ENTRY *ne;
2ba5f0
-		ASN1_OBJECT *obj;
2ba5f0
-		ASN1_STRING *cn = NULL;
2ba5f0
-		int navas;
2ba5f0
-
2ba5f0
-		/* find the last CN */
2ba5f0
-		obj = OBJ_nid2obj( NID_commonName );
2ba5f0
-		if ( !obj ) goto no_cn;	/* should never happen */
2ba5f0
-
2ba5f0
-		xn = X509_get_subject_name(x);
2ba5f0
-		navas = X509_NAME_entry_count( xn );
2ba5f0
-		for ( i=navas-1; i>=0; i-- ) {
2ba5f0
-			ne = X509_NAME_get_entry( xn, i );
2ba5f0
-			if ( !OBJ_cmp( X509_NAME_ENTRY_get_object(ne), obj )) {
2ba5f0
-				cn = X509_NAME_ENTRY_get_data( ne );
2ba5f0
-				break;
2ba5f0
-			}
2ba5f0
+	/* attempt to encode name as IP address */
2ba5f0
+	ip = a2i_IPADDRESS(name);
2ba5f0
+	if (ip == NULL) {
2ba5f0
+		ERR_clear_error();
2ba5f0
+		/* it's a hostname */
2ba5f0
+		if (X509_check_host(x, name, strlen(name), flags, NULL) == 1) {
2ba5f0
+			ret = LDAP_SUCCESS;
2ba5f0
 		}
2ba5f0
-
2ba5f0
-		if( !cn )
2ba5f0
-		{
2ba5f0
-no_cn:
2ba5f0
-			Debug( LDAP_DEBUG_ANY,
2ba5f0
-				"TLS: unable to get common name from peer certificate.\n",
2ba5f0
-				0, 0, 0 );
2ba5f0
-			ret = LDAP_CONNECT_ERROR;
2ba5f0
-			if ( ld->ld_error ) {
2ba5f0
-				LDAP_FREE( ld->ld_error );
2ba5f0
-			}
2ba5f0
-			ld->ld_error = LDAP_STRDUP(
2ba5f0
-				_("TLS: unable to get CN from peer certificate"));
2ba5f0
-
2ba5f0
-		} else if ( cn->length == nlen &&
2ba5f0
-			strncasecmp( name, (char *) cn->data, nlen ) == 0 ) {
2ba5f0
+	} else {
2ba5f0
+		/* It's an IPv4 or IPv6 address */
2ba5f0
+		if (X509_check_ip(x, ASN1_STRING_data(ip),
2ba5f0
+		                  ASN1_STRING_length(ip), 0) == 1) {
2ba5f0
 			ret = LDAP_SUCCESS;
2ba5f0
-
2ba5f0
-		} else if (( cn->data[0] == '*' ) && ( cn->data[1] == '.' )) {
2ba5f0
-			char *domain = strchr(name, '.');
2ba5f0
-			if( domain ) {
2ba5f0
-				int dlen;
2ba5f0
-
2ba5f0
-				dlen = nlen - (domain-name);
2ba5f0
-
2ba5f0
-				/* Is this a wildcard match? */
2ba5f0
-				if ((dlen == cn->length-1) &&
2ba5f0
-					!strncasecmp(domain, (char *) &cn->data[1], dlen)) {
2ba5f0
-					ret = LDAP_SUCCESS;
2ba5f0
-				}
2ba5f0
-			}
2ba5f0
 		}
2ba5f0
+		ASN1_OCTET_STRING_free(ip);
2ba5f0
+	}
2ba5f0
 
2ba5f0
-		if( ret == LDAP_LOCAL_ERROR ) {
2ba5f0
-			Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
2ba5f0
-				"common name in certificate (%.*s).\n", 
2ba5f0
-				name, cn->length, cn->data );
2ba5f0
-			ret = LDAP_CONNECT_ERROR;
2ba5f0
-			if ( ld->ld_error ) {
2ba5f0
-				LDAP_FREE( ld->ld_error );
2ba5f0
-			}
2ba5f0
-			ld->ld_error = LDAP_STRDUP(
2ba5f0
-				_("TLS: hostname does not match CN in peer certificate"));
2ba5f0
+	if( ret == LDAP_LOCAL_ERROR ) {
2ba5f0
+		Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
2ba5f0
+			"peer certificate.\n", name, 0, 0);
2ba5f0
+		ret = LDAP_CONNECT_ERROR;
2ba5f0
+		if ( ld->ld_error ) {
2ba5f0
+			LDAP_FREE( ld->ld_error );
2ba5f0
 		}
2ba5f0
+		ld->ld_error = LDAP_STRDUP(
2ba5f0
+			_("TLS: hostname does not match peer certificate"));
2ba5f0
 	}
2ba5f0
 	X509_free(x);
2ba5f0
 	return ret;