How do I solve ldap_start_tls() "Unable to start TLS: Connect error" in PHP?
The specific scenario presented in the question--with an expired certificate that can't be changed--does appear to require disabling certificate validation on the LDAP client.
However, I suspect a lot of people, like me, reach this page for other root causes of receiving opaque LDAP TLS errors, where disabling validation of TLS certificates is not an appropriate answer.
In my case--using the LDAP Authentication extension for Mediawiki on an Ubuntu 18.04 LTS server, and authenticating against Active Directory on a Windows Server 2012 server--authentication stopped working in January/February 2020. The server certificate and the CA certificate were still both valid, and openssl s_client -verify 2 -connect <AD server>:636
from the Mediawiki server passed just fine.
Eventually I noticed that the signature algorithm in the SSL certificate served by AD/LDAP was SHA1, which I remembered recently suffered from the first known chosen-prefix collision exploit. This led me to investigate the changelog for packages that had recently been updated on the system, which turned up "Mark SHA1 as insecure for certificate signing" in the gnutls28 changelog circa January 8th, 2020. (The chain of dependencies from the php-ldap package in Ubuntu 18.04 goes to php7.2-ldap -> libldap-2.4-2 -> libgnutls30, whose source package is gnutls28.)
I followed some instructions to update the Windows CA to use SHA256 and then selectively followed instructions to renew the AD/LDAP cert, installed the new CA cert on my Mediawiki server, and the problem was solved! Briefly, these steps included:
- In an Admin PowerShell on the AD server, run
certutil -setreg ca\csp\CNGHashAlgorithm SHA256
- In the Certification Authority MMC, right click on the CA -> All Tasks -> Renew CA Certificate
- In a blank MMC, add snap-in for Certificates; select Local Computer
- Under Personal -> Certificates, find the current entry used by LDAPS (Kerberos Authentication template type) -> All Tasks -> Advanced Options -> Renew This Certificate with the Same Key
- In the same window, open the new CA certificate -> Details -> Copy to file -> no private key -> base64-encoded X.509
- Copy the resulting file to /usr/share/ca-certificates/ on the Mediawiki server, then run
sudo dpkg-reconfigure ca-certificates
and select the new CA cert for inclusion.
P.S. For SEO purposes, depending on the mode I was using, error messages included:
ldap_start_tls(): Unable to start TLS: Connect error in /var/www/mediawiki/extensions/LdapAuthentication/LdapAuthenticationPlugin.php
in the HTTP error logldap_start_tls(): Unable to start TLS: Can't contact LDAP server in [...]
Failed to start TLS.
in the Mediawiki debug log (when usingwgLDAPEncryptionType
=ssl
, i.e. encrypted LDAP port, 636)Failed to bind as CN=foobar,CN=Users,DC=myOrgName,DC=local
in the Mediwiki debug log (when usingwgLDAPEncryptionType
=tls
, i.e. STARTTLS on the unencrypted LDAP port, 389)
You can ignore the validity in windows by issuing
putenv('LDAPTLS_REQCERT=never');
in your php code. In *nix you need to edit your /etc/ldap.conf
to contain
TLS_REQCERT never
Another thing to be aware of is that it requires version 3 (version 2 is php default):
//$hostnameSSL example would be "ldaps://just.example.com:636" , just make sure it has ldaps://
$con = ldap_connect($hostnameSSL);
ldap_set_option($con, LDAP_OPT_PROTOCOL_VERSION, 3);
To get a better idea of what's going on, you can enable debug logging by:
ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7);
This can be done before the ldap_connect
takes place.
My solution/workaround is to use
/etc/ldap/ldap.conf:
#TLS_CACERT /etc/ssl/certs/ca.crt
TLS_REQCERT never
If you have any better idea, please post another answer.