Ssl – How to one force Open Directory server to provide its full certificate chain to connecting clients

mac-osxmac-osx-serveropenldapslapdssl

The Problem

We've created an Open Directory master on OSX 10.10 Yosemite + Server.app v4:

$ sudo slapconfig -createldapmasterandadmin admin Administrator 1000

Which generates a root CA, an intermediate CA and a host SSL certificate (all correctly placed in both the system keychain and in the /etc/certificates directory). However, when connecting over SSL, slapd provides only the host certificate, rather than the entire certificate chain:

$ openssl s_client -connect a.b.c:636                        
CONNECTED(00000003)
depth=0 CN = a.b.c, C = GB, emailAddress = a@b.c.
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = a.b.c, C = GB, emailAddress = a@b.c
verify error:num=27:certificate not trusted
verify return:1
depth=0 CN = a.b.c, C = GB, emailAddress = a@b.c
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:/CN=a.b.c/C=GB/emailAddress=a@b.c
   i:/CN=IntermediateCA_A.B.C_1/O=b/OU=MACOSX OpenDirectory Intermediate CA/emailAddress=a@b.c
---

This is a problem, of course, because clients (who trust only the root CA) fail to validate the host certificate and abort the connection.

Documented solutions

According to OpenLDAP Software 2.4 Administrator's Guide, Chapter 16 "Using TLS":

16.2.1.1. TLSCACertificateFile <filename>

This directive specifies the PEM-format file containing certificates for the CA's that slapd will trust. The certificate for the CA that signed the server certificate must be included among these certificates. If the signing CA was not a top-level (root) CA, certificates for the entire sequence of CA's from the signing CA to the top-level CA should be present. Multiple certificates are simply appended to the file; the order is not significant.

(For the avoidance of doubt, the fact that slapd will then provide the certificate chain to connecting clients was recently confirmed on the openldap-technical mailing list—although it has been previously noted that this gives rise to a problematic conflict where different trust anchors are used for TLS client certificates).

Apple peculiarities

Since Apple's build uses slapd.d, one would normally expect this option to be configured via olcTLSCACertificateFile—however, according to slapd-config(5) (emphasis added):

olcTLSCACertificateFile: <filename>

Specifies the file that contains certificates for all of the Certificate Authorities that slapd will recognize.

When using SecureTransport this option is not valid. Instead use the olcTLSTrustedCerts option.

[ deletia ]

olcTLSTrustedCerts

Lists the trusted certificates in the system keychain separated by '|'. For example: olcTLSTrustedCerts Frobozz, Inc.|Widgets R Us|www.example.com

Used by SecureTransport instead of olcTLSCACertificateFile and olcTLSCACertificatePath. Ignored by OpenSSL, GnuTLS and Mozilla NSS.

(SecureTransport is Apple's SSL library).

Our attempts…

Surprisingly, olcTLSTrustedCerts was not created in our directory by slapconfig, although the host certificate was named in (the related) olcTLSIdentity. That said, slapd was in any event ignoring olcTLSIdentity in favour of the OPENDIRECTORY_SSL_IDENTITY preference in the system keychain:

TLS: OPENDIRECTORY_SSL_IDENTITY identity preference overrode configured olcTLSIdentity "a.b.c"

So, we have tried the following (both independently and together):

  1. Adding olcTLSTrustedCerts. slapd clearly parses the CNs listed in this option and locates the CA certificates in the system keychain, as it logs cases where a deliberately incorrect value is provided:

    TLS: SecItemCopyMatching(foo.bar) failed (check olcTLSTrustedCerts setting): The specified item could not be found in the keychain. (-25300)

  2. Removing the OPENDIRECTORY_SSL_IDENTITY preference from the system keychain. slapd no longer complains that olcTLSIdentity has been overridden (and it only continues to support SSL so long as the value of that configuration option matches the CN of a certificate in the system keychain, else it complains similarly to the error quoted above—suggesting that it's using that configuration option as expected).

Yet the full certificate chain is still not provided to connecting clients. How can this be fixed?

Best Answer

The problem is twofold:

  1. Secure Transport uses the certificate chain exactly as provided to it by the API client. Both the API documentation and the source code comments imply (without being explicit) that this is a bug: in such circumstances, it appears that Secure Transport should attempt to build the certificate chain from the system keychain.

  2. Apple's slapd always supplies Secure Transport with the host identity certificate only and never a certificate chain. See the following snippets extracted from libraries/libldap/tls_st.c:

    ctx->identity_certs = /*
    */ CFArrayCreate(NULL, (const void **) &identRef, 1, &kCFTypeArrayCallBacks);
    
    SSLSetCertificate(ssl, ctx->identity_certs);
    

So, as things stand, Apple's slapd cannot send a full certificate chain.