May 20th, 2009 posted by Bender Rodríguez
The trials and tribulations of connecting to an LDAP directory server using python-ldap over OpenSSL
I had the need to connect to an LDAP directory server to authenticate users for access to a custom CMS built on top of Django. So I installed OpenLDAP and the python-ldap module, along with the OpenLDAP utilties and a few other tid bits that everything needed to function properly. After everything was in place, I created a simple test script to determine if I could connect to the LDAP server through the firewall, which had recently been opened up to allow access to it from the development server.
import ldap
AUTH_LDAP_SERVER = 'example.com'
AUTH_LDAP_BASE_USER = "cn=xxx, o=XXX"
AUTH_LDAP_BASE_PASS = "password"
username = 'test'
base = "o=TEST"
scope = ldap.SCOPE_SUBTREE
filter = "(&(objectclass=person) (cn=%s))" % username
ret = ['dn']
l = ldap.initialize('ldaps://example.com:636')
l.protocol_version = ldap.VERSION3
l.simple_bind_s(AUTH_LDAP_BASE_USER,AUTH_LDAP_BASE_PASS)
result_id = l.search(base, scope, filter, ret)
print result_id
Unfortunately, the test failed from development with the following error:
ldap.SERVER_DOWN: {'desc': "Can't contact LDAP server"}
I tried the test from our production server, which I knew could connect to the LDAP server, and it worked perfectly well. So there was something wrong with the handshake between development and the LDAP server. I fired up the ldapsearch utilitiy with the following command:
ldapsearch -v -d5 -H ldaps://example.com:636 -D "cn=xxx, o=XXX" -w password
and it returned the telltale sign of why the handshake was failing:
TLS: peer cert untrusted or revoked (0x42)
In order to take a look at the certificate from command line, I executed the following:
openssl s_client -connect example.com:636
which showed me that the certificate was self-signed, and might be the source of the problem. Using gnutls-cli:
gnutls-cli --print-cert -p 636 example.com
I saw similar results, again self-signed, but a self-signed cert should not present a problem, so I copied the contents of the cert into a .crt file and included it in the ldap.conf file, like so:
TLS_CACERT /etc/ssl/certs/mycert.crt
I restarted slapd, but again I was unable to connect to the LDAP server. I also tried to add the collection of certs from openssl:
TLS_CACERT /etc/ssl/certs/ca-certificates.crt
and even added my cert to it, but to no avail. after googling around for a while, it turns out that this might be a bug in the debian OpenLDAP code. By adding:
TLS_REQCERT allow
to the ldap.conf file, I was finally able to connect. This is, however, an insecure connection, as it ignores the certificate verification errors and continues on with the connection, so I decided to read through the entire bug thread, and found out that I needed to include the entire certificate chain in the .crt file. As noted in the OpenLDAP admin guide:
"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."
So I added the other certificate that was revealed in the openssl connect command, and now I can connect without a problem.
See this thread for details
"What can be asserted without proof can be dismissed without proof."
Christopher Hitchens