Why does ldapsearch fail to accept Subject Alternative Name of LDAP server SSL certificate?
Environment
- Red Hat Enterprise Linux 6.
Issue
-
A LDAP server can be reached by multiple DNS names (e.g.
ldaptest.example.com
andldapprod.example.com
). -
The SSL certificate for the LDAP server includes Subject Alternative Name (
subjectAltName
) extension using the*
wildcard character for a partial match of the left-most DNS label (e.g.ldap*.example.com
).
$ echo "" | openssl s_client -CAfile ExampleRootCA.pem -connect 1.2.3.4:636 2>/dev/null | openssl x509 -text -noout | egrep -A 1 "(Subject Alternative Name|Subject:)"
Subject: CN=ldap.example.com, C=COM, O=EXAMPLE
Subject Public Key Info:
--
X509v3 Subject Alternative Name:
DNS:ldap.example.com, DNS:ldap*.example.com, DNS:ldap*
ldapsearch
using SSL (LDAPS) fails to reach LDAP server when providing a hostname included in the Subject Alternative Name of the SSL certificate:
# LDAPTLS_CACERT=ExampleRootCA.pem ldapsearch -x -H ldaps://ldaptest.example.com:636 -d 1 -b ou=exampleuser,o=example,c=com cn=m123456 displayname | grep displayname
ldap_url_parse_ext(ldaps://ldaptest.example.com:636)
ldap_create
ldap_url_parse_ext(ldaps://ldaptest.example.com:636/??base)
ldap_sasl_bind
ldap_send_initial_request
ldap_new_connection 1 1 0
ldap_int_open_connection
ldap_connect_to_host: TCP ldaptest.example.com:636
ldap_new_socket: 3
ldap_prepare_socket: 3
ldap_connect_to_host: Trying 1.2.3.4:636
ldap_pvt_connect: fd: 3 tm: -1 async: 0
TLS: certdb config: configDir='/etc/openldap' tokenDescription='ldap(0)' certPrefix='cacerts' keyPrefix='cacerts' flags=readOnly
TLS: cannot open certdb '/etc/openldap', error -8018:Unknown PKCS #11 error.
TLS: loaded CA certificate file ExampleRootCA.pem.
TLS: could not get info about the CA certificate directory /etc/openldap/cacerts - error -5950:File not found.
TLS: certificate [O=EXAMPLE,C=COM,CN=ldap.example.com] is valid
TLS certificate verification: subject: O=EXAMPLE,C=COM,CN=ldap.example.com, issuer: O=EXAMPLE,C=COM,CN=Services CA, cipher: AES-256, security level: high, secret key bits: 256, total key bits: 256, cache hits: 0, cache misses: 0, cache not reusable: 0
TLS: hostname (ldaptest.example.com) does not match common name in certificate (ldap.example.com).
TLS: can't connect: TLS: hostname does not match CN in peer certificate.
ldap_err2string
ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)
Resolution
The *
wildcard character can only be used to match the left-most DNS label entirely (no partial matching).
Consequently *.example.com
is a valid usage of the *
wildcard character but ldap*.example.com
is not.
Please consider either:
- specifying the full list of all hostnames:
X509v3 Subject Alternative Name:
DNS:ldaptest.example.com, DNS:ldapprod.example.com
- adding an additional domain level such as
.ldap.example.com
thus allowing matching of all hostnamestest.ldap.example.com
andprod.ldap.example.com
:
X509v3 Subject Alternative Name:
DNS:*.ldap.example.com
Root Cause
- As per section "3.1.3.1. Comparison of DNS Names" of RFC 4513:
The '*' (ASCII 42) wildcard character is allowed in subjectAltName
values of type dNSName, and then only as the left-most (least
significant) DNS label in that value. This wildcard matches any
left-most DNS label in the server name. That is, the subject
*.example.com matches the server names a.example.com and
b.example.com, but does not match example.com or a.b.example.com.
- Source code of OpenLDAP reveals it expects the wildcard to be a match for a full subdomain rather than a partial one:
2746 /* is this an exact match? */
2747 if ( nlen == hlen && !strncasecmp( name, host, nlen )) {
2748 ret = LDAP_SUCCESS;
2749 break;
2750 }
2751
2752 /* is this a wildcard match? */
2753 if ( domain && host[0] == '*' && host[1] == '.' &&
2754 dlen == hlen-1 && !strncasecmp( domain, host+1, dlen )) {
2755 ret = LDAP_SUCCESS;
2756 break;
2757 }
Line 2753 reveals OpenLDAP expects first character in host
(0) to be the wildcard "*" and next character (1) to be a dot "." so the *
wildcard must be the first character in the subjectAltName
entry and must be followed by a dot "." thus matching the entire left-most DNS label.
- The same applies to the subject CN (as per line 2800):
2798 if ( av->len == nlen && !strncasecmp( name, (char *)av->data, nlen )) {
2799 ret = LDAP_SUCCESS;
2800 } else if ( av->data[0] == '*' && av->data[1] == '.' &&
2801 domain && dlen == av->len - 1 && !strncasecmp( domain,
2802 (char *)(av->data+1), dlen )) {
2803 ret = LDAP_SUCCESS;
2804 } else {
2805 int len = av->len;
2806 if ( len >= sizeof(buf) )
2807 len = sizeof(buf)-1;
2808 memcpy( buf, av->data, len );
2809 buf[len] = '\0';
2810 }
This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.
Comments