How to force Python LDAP to validate / verify SSL certificate when using .start_tls_s ()

I am trying to use Python-LDAP (version 2.4.19) under MacOS X 10.9.5 and Python 2.7.9

I want to test my connection to this LDAP server after I call .start_tls_s() (or so that the method will raise and exclude if the certificate cannot be verified). (I would also like to check the CRL, but that is another matter).

Here is my code:

 #!python #!/usr/bin/env python import ConfigParser, os, sys import ldap CACERTFILE='./ca_ldap.bad' ## CACERTFILE='./ca_ldap.crt' config = ConfigParser.ConfigParser() config.read(os.path.expanduser('~/.ssh/creds.ini')) uid = config.get('LDAP', 'uid') pwd = config.get('LDAP', 'pwd') svr = config.get('LDAP', 'svr') bdn = config.get('LDAP', 'bdn') ld = ldap.initialize(svr) ld.protocol_version=ldap.VERSION3 ld.set_option(ldap.OPT_DEBUG_LEVEL, 255 ) ld.set_option(ldap.OPT_PROTOCOL_VERSION, 3) ld.set_option(ldap.OPT_X_TLS_CACERTFILE, CACERTFILE) ld.set_option(ldap.OPT_X_TLS_DEMAND, True ) ld.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_HARD) ## From: /questions/475316/pythonldapssl/2050886#2050886 ## and : http://python-ldap.cvs.sourceforge.net/viewvc/python-ldap/python-ldap/Demo/initialize.py?revision=1.14&view=markup ld.start_tls_s() for each in dir(ldap): if 'OPT_X_TLS' in each: try: print '\t*** %s: %s' % (each, ld.get_option((getattr(ldap, each)))) except Exception, e: print >> sys.stderr, '... Except %s: %s\n' % (each, e) ld.simple_bind_s(uid, pwd) results = ld.search_s(bdn, ldap.SCOPE_SUBTREE) print 'Found %s entries under %s' % (len(results), bdn) sys.exit() 

As noted in the comments, I copied most of this from https://stackoverflow.com/a/464316/ ... and from http://python-ldap.cvs.sourceforge.net/viewvc/python-ldap/python-ldap/Demo/ initialize.py?revision=1.14&view=markup ... although I have tried many variations and sequences of this.

As shown, I have two files that represent a bad certificate, and one that should work (it is actually taken from one of our systems, which is configured to run sssd (Daemon System Security), which is supposed to check correctly .

In a β€œbad” copy, I simply replaced the first character of each line of the line with the letter β€œx” to suggest that this would damage the CA key and cause some code to try to verify the signature chain.

However, it looks like the LDAP Python code ignores this; even if I set it to /dev/null or the completely dummy path that my code still works, still binds to the LDAP server and still completes my search request.

So, the question is, how can I make this β€œfail” for its intended purpose (or, more broadly, how can I prevent my code from being vulnerable to MITM (Mallory) attacks?

If this makes any difference in this discussion, here is my version of OpenSSL:

 $ openssl version OpenSSL 0.9.8za 5 Jun 2014 

OpenLDAP runs on the LDAP server, but I don’t know the details about its version and configuration.

Here is an example output from my code:

  *** OPT_X_TLS: 0 *** OPT_X_TLS_ALLOW: 0 *** OPT_X_TLS_CACERTDIR: None *** OPT_X_TLS_CACERTFILE: /bogus/null *** OPT_X_TLS_CERTFILE: None *** OPT_X_TLS_CIPHER_SUITE: None *** OPT_X_TLS_CRLCHECK: 0 *** OPT_X_TLS_CRLFILE: None *** OPT_X_TLS_CRL_ALL: 1 *** OPT_X_TLS_CRL_NONE: {'info_version': 1, 'extensions': ('X_OPENLDAP', 'THREAD_SAFE', 'SESSION_THREAD_SAFE', 'OPERATION_THREAD_SAFE', 'X_OPENLDAP_THREAD_SAFE'), 'vendor_version': 20428, 'protocol_version': 3, 'vendor_name': 'OpenLDAP', 'api_version': 3001} *** OPT_X_TLS_CRL_PEER: 3 ... Except OPT_X_TLS_CTX: unknown option 24577 *** OPT_X_TLS_DEMAND: 1 *** OPT_X_TLS_DHFILE: None *** OPT_X_TLS_HARD: 3 *** OPT_X_TLS_KEYFILE: None *** OPT_X_TLS_NEVER: {'info_version': 1, 'extensions': ('X_OPENLDAP', 'THREAD_SAFE', 'SESSION_THREAD_SAFE', 'OPERATION_THREAD_SAFE', 'X_OPENLDAP_THREAD_SAFE'), 'vendor_version': 20428, 'protocol_version': 3, 'vendor_name': 'OpenLDAP', 'api_version': 3001} ... Except OPT_X_TLS_NEWCTX: unknown option 24591 *** OPT_X_TLS_PACKAGE: OpenSSL *** OPT_X_TLS_PROTOCOL_MIN: 0 *** OPT_X_TLS_RANDOM_FILE: None *** OPT_X_TLS_REQUIRE_CERT: 1 *** OPT_X_TLS_TRY: 0 Found 883 entries under [... redacted ...] 
+6
source share
1 answer

Your code works for me, as expected. In fact, I had the exact opposite problem when I first executed your code. He always said that certificate verification failed. Adding the following lines fixed:

 # Force libldap to create a new SSL context (must be last TLS option!) ld.set_option(ldap.OPT_X_TLS_NEWCTX, 0) 

Now, when I use the wrong CA certificate or the one that was changed as it was described, the result is this error message:

 Traceback (most recent call last): File "ldap_ssl.py", line 28, in <module> ld.start_tls_s() File "/Library/Python/2.7/site-packages/python_ldap-2.4.19-py2.7-macosx-10.10-intel.egg/ldap/ldapobject.py", line 571, in start_tls_s return self._ldap_call(self._l.start_tls_s) File "/Library/Python/2.7/site-packages/python_ldap-2.4.19-py2.7-macosx-10.10-intel.egg/ldap/ldapobject.py", line 106, in _ldap_call result = func(*args,**kwargs) ldap.CONNECT_ERROR: {'info': 'error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (unable to get local issuer certificate)', 'desc': 'Connect error'} 

When I use the correct CA certificate, the output will be similar to yours.


Now an interesting question: what are the differences between our installations and especially what difference causes this strange behavior on your machine?

My setup:

  • Mac OS X 10.10
  • Python 2.7.6
  • python-ldap 2.4.19 (manual installation)
  • OpenLDAP 2.4.39 (via Homebrew)
  • OpenSSL 1.0.1l (via Homebrew)

I have a local OpenLDAP job installed with Homebrew:

 brew install homebrew/dupes/openldap --with-berkeley-db 

In Yosemite, python-ldap works pretty poorly when installed using pip (see Python-ldap set_option does not work on Yosemite ), so I had to download tarball and compile / install it, fortunately, it’s pretty easy, because I already had the installation OpenLDAP with current libraries / headers:

First edit the [_ldap] section in setup.cfg as follows:

 [_ldap] library_dirs = /usr/local/opt/openldap/lib /usr/lib /usr/local/lib include_dirs = /usr/local/opt/openldap/include /usr/include/sasl /usr/include /usr/local/include extra_compile_args = -g -arch x86_64 extra_objects = libs = ldap_r lber sasl2 ssl crypto 

Some header files are in the Mac OS SDK, bind the directory (change the path according to your version) to / usr / include:

 sudo ln -s /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include/ /usr/include 

Then create and install:

 python setup.py build sudo python setup.py install 

The otool output shows that python-ldap is now linked with the OpenLDAP 2.4.39 and OpenSSL 0.9.8 libraries:

 $ otool -L /Library/Python/2.7/site-packages/python_ldap-2.4.19-py2.7-macosx-10.10-intel.egg/_ldap.so /Library/Python/2.7/site-packages/python_ldap-2.4.19-py2.7-macosx-10.10-intel.egg/_ldap.so: /usr/local/lib/libldap_r-2.4.2.dylib (compatibility version 13.0.0, current version 13.2.0) /usr/local/lib/liblber-2.4.2.dylib (compatibility version 13.0.0, current version 13.2.0) /usr/lib/libsasl2.2.dylib (compatibility version 3.0.0, current version 3.15.0) /usr/lib/libssl.0.9.8.dylib (compatibility version 0.9.8, current version 0.9.8) /usr/lib/libcrypto.0.9.8.dylib (compatibility version 0.9.8, current version 0.9.8) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0) 

An alternative approach to building python-ldap is to install only the OpenLDAP libraries and headers needed to build: http://projects.skurfer.com/posts/2011/python_ldap_lion/

All these steps should work with Mavericks, and I assume that using the latest OpenLDAP and OpenSSL libraries will solve your problem.

+7
source

Source: https://habr.com/ru/post/984878/


All Articles