|
|
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;
|