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