OpenLDAP, GnuTLS and Raspberry Pi

(If you are looking for an answer and don’t want to read everything, you can just jump to the conclusion at the very end.)

First, a bit of context. I recently acquired two copies of the latest version of the Raspberry Pi (model B rev 2, to be more specific). And I decided to use one in a project involving an LDAP server.

I guess that if you’re reading this, you already know what an LDAP server is, so I’ll keep it short and simple : it’s a type of database. For more informations, please refer to Wikipedia or Google.

I settled for the OpenLDAP implementation (not uch of a choice, really), and quickly had a test database, populated and running fine. By the way, the vast majority of tutorials you can find on the web about how to configure your OpenLDAP server write about a slapd.conf file. But the recent versions of OpenLDAP make rather use of an LDAP database for configuration, named "cn=config". It took me a little time to understand that, so I share it here. You will have to learn how to put records in an LDAP database before you can configure it. All was going well, but my database was going to be requested remotely, and I decided to secure a bit the communication channel. The easiest way to do that is to use TLS.

Again, I guess that you know what TLS is. For the rest of you, poor ignorant souls, Wikipedia and Google are yet again your friends. But basically, it’s using certificates to make your communication secure. You can use TLS with a variety of protocol, like HTTPS to browse the web securely (and transmit your credit card number when buying something), FTPS to download files securely, or IMAPS to retrieve mails securely. And as you may have guessed by now, there is a version of LDAP making use of TLS adequately name LDAPS. That’s what I decided to use.

I already had a personnal PKI 1, so creating a TLS certificate (and its corresponding CA) was quite easy. I put them in under /etc/ldap/ssl, in separate directories: cas for the CA file, ldap for the server certificate and key.

The configuration parameters to modify to take those files into account are:

  • olcTLSCACertficateFile which points to the CA certificate file ;

  • olcTLSCertificateFile which points to the server certificate ;

  • olcTLSCertificateKeyFile which points to the server key.

You will then have to change the /etc/default/slapd file to add the ldaps listener when starting the service. Just add ldaps:/// to the SLAPD_SERVICES variable and you’re good to go. You can then restart the service with:

$> service slapd restart

Use netstat to check that slapd is now listening on the LDAPS port (636).

Again all went seamlessly, but when I tried to access my LDAP server on the LDAPS port, I got an error:

$> ldapsearch -x -H ldaps://ldap.lertsenem.com/ -b "dc=lertsenem,dc=com"

ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)

OpenLDAP debug

Damn. Well, what we clearly need here is more informations. The man page for ldapsearch mentions two options to get just that: -v for verbose and -d for debug. Let’s use them.

$> ldapsearch -x -H ldaps://ldap.lertsenem.com/ -b "dc=lertsenem,dc=com" -v -d -1

ldap_initialize( <DEFAULT> )
ldap_create
ldap_sasl_bind
ldap_send_initial_request
ldap_new_connection 1 1 0
ldap_int_open_connection
ldap_connect_to_host: TCP ldap.lertsenem.com:636
ldap_new_socket: 3
ldap_prepare_socket: 3
ldap_connect_to_host: Trying 192.168.0.1:636
ldap_pvt_connect: fd: 3 tm: -1 async: 0
tls_write: want=254, written=254
  0000:  16 03 00 00 f9 01 00 00  f5 03 03 55 3a 9b ea 34   ...........U:..4
  0010:  3f a0 56 af 24 ac ab c3  1d 7a 24 2c 8c 84 f1 c2   ?.V.$....z$,....
  0020:  3c 60 73 8d d1 e4 22 f5  a0 99 3e 00 00 84 c0 2b   <`s..."...>....+
  0030:  c0 2c c0 86 c0 87 c0 09  c0 23 c0 0a c0 24 c0 72   .,.......#...$.r
  0040:  c0 73 c0 08 c0 07 c0 2f  c0 30 c0 8a c0 8b c0 13   .s...../.0......
  0050:  c0 27 c0 14 c0 28 c0 76  c0 77 c0 12 c0 11 00 9c   .'...(.v.w......
  0060:  00 9d c0 7a c0 7b 00 2f  00 3c 00 35 00 3d 00 41   ...z.{./.<.5.=.A
  0070:  00 ba 00 84 00 c0 00 0a  00 05 00 04 00 9e 00 9f   ................
  0080:  c0 7c c0 7d 00 33 00 67  00 39 00 6b 00 45 00 be   .|.}.3.g.9.k.E..
  0090:  00 88 00 c4 00 16 00 a2  00 a3 c0 80 c0 81 00 32   ...............2
  00a0:  00 40 00 38 00 6a 00 44  00 bd 00 87 00 c3 00 13   .@.8.j.D........
  00b0:  00 66 01 00 00 48 00 05  00 05 01 00 00 00 00 ff   .f...H..........
  00c0:  01 00 01 00 00 23 00 00  00 0a 00 0c 00 0a 00 13   .....#..........
  00d0:  00 15 00 17 00 18 00 19  00 0b 00 02 01 00 00 0d   ................
  00e0:  00 1c 00 1a 04 01 04 02  04 03 05 01 05 03 06 01   ................
  00f0:  06 03 03 01 03 02 03 03  02 01 02 02 02 03         ..............
tls_read: want=5, got=0

TLS: can't connect: The TLS connection was non-properly terminated..
ldap_err2string
ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)

OK, maybe I went a little two far by setting the debug verbosity to the max. The important line here is this: TLS: can’t connect: The TLS connection was non-properly terminated..

What my client is apparently telling me is that the server closed the communication somewhere during the TLS negociation. We can check that using tcpdump, and we can see that ldapsearch sends a Client Hello to initiate the negociation but the LDAP server abruptly terminate the connexion. There’s a handful of reason why a TLS server would do that. The easiest way to find which one is by reading the server logs.

Unfortunately, the slapd daemon server logs default to none, which means we only have the more important logs. And a failed connection attempt does not seem to be important enough. The configuration parameter to change that is olcLogLevel. A lot of different values are available, depending on what you want to log, but fuck that I’m just debugging here so I’ll put it to any.

Then I try to connect again to generate more logs, and I reopen the log file to find what I was looking for :

connection_read(15): TLS accept failure error=-1 id=1036, closing

Hmm, OK. Not quite what I was expecting. Let’s think: obviously OpenLDAP use a third party crypto library. Maybe I can get more logs from it.

GnuTLS debug

According to the Internet, OpenLDAP can use OpenSSL or GnuTLS as crypto library. On Debian the maintainers decided to settle on GnuTLS for licencing reasons 2 and the GnuTLS documentation indicates that I can activate debug using the GNUTLS_DEBUG_LEVEL environnment variable.

I don’t wan’t to modify the service script, so this time I launch slapd manually:

$> GNUTLS_DEBUG_LEVEL=15 slapd -h "ldaps://ldap.lertsenem.com/ ldap://ldap.lertsenem.com ldapi:///" -u openldap -g openldap -F /etc/ldap/slapd.d/ -4 -d -1"

I try to connect again, and there it is:

...
TLS: can't accept: Could not negotiate a supported cipher suite..
...

So, the LDAP server does not accept any of the cipher suite proposed by the client. But why ?

The cipher suite used by the server is stored in the olcTLSCipherSuite configuration parameter. I try to set it to different values, such as SECURE256 or NORMAL, but none of them work 3.

Looking at the communication on the client side, I can see that the client propose quite a lot of different cipher suites. But it’s also using TLSv1.2… it’s the latest version of TLS, and therefore the more secure, but what if my LDAP server does not support it? To test this hypothesis, GnuTLS gives us just the tool needed with gnutls-cli-debug (package gnutls-bin).

$> gnutls-cli-debug -p 636 ldap.lertsenem.com

...
Checking for TLS 1.0 support... yes
Checking for TLS 1.1 support... yes
Checking for TLS 1.2 support... no
...

Bingo.

Conclusion

So, the slapd package on raspbian seems to use an older version of GnuTLS and I couldn’t make that version work with TLSv1.2. So I had to deactivate it, setting the TLS_CIPHER_SUITE parameter in /etc/ldap/ldap.conf (on the client side) to NORMAL:-VERS-TLS1.2:-VERS-SSL3.0 4 and now the client can speak to the server.

  1. Made with xca, which I heartfully recommend if you want to remove the (technical at least) pain of managing a PKI
  2. One of the worst reasons possible to settle for a security product in my opinion, but meh
  3. And when I make a syntac mistake, by using an OpenSSL cipher name for example, the server crashes. Oh yeah baby.
  4. Yeah, I deactivate SSLv3.0, better use TLS nowadays

Written by Lertsenem in misc on Fri 24 April 2015. Tags: technical, openldap, gnutls, raspberry pi,