Wherein we write down some stuff that we know.

HOWTO: “Fix” Secure LDAP in PHP

Preface: I am not an expert in encryption, SSL, or LDAP. Your install may be functioning just fine and you don’t need any of this information. You use this at your own risk as it may be completely wrong. That being said, it worked for me.

Making a secure (ldaps) connection in PHP (php-4.3.9-3.8) on Red Hat Enterprise Linux AS release 4 (Nahant Update 1) will fail if on ldap_connect (“Error -1: Can’t connect to LDAP server”) if the certificate cannot be verified. Due to the release of a new intermediate certificate from Verisign, it is likely that your install of openssl will not have access to that intermediate cert. Thus openssl will tell you that there is a self-signed certificate in the chain (“Error -19”). If you recently bought a certificate from Verisign you will not find much in the way of help for dealing with LDAP, PHP, or openssl.

The answer with web servers is generally well documented, and the intermediate certificate is made available to the server to send to the client. This is good because it means that 8 trillion web browsers don’t generally need to be updated to use SSL.

It should also be noted that it is probably best to “fix” this issue at the server level rather than the client because each and every client would need to be fixed as opposed to just fixing the server once. If you do not have access to the server to fix it, this should work for you.

  1. Obtain a copy of the Verisign intermediate certificate. Save it as a text file on a system where you can run openssl binaries.

  2. Convert from PEM to ca-bundle format (*Update:* There is a updated script for BSD sed if you are on OS X). Save this output as you may need to do the next few steps on multiple servers.

    CODE:
    1. #!/bin/sh
    2. # Friendly Name
    3. openssl x509 -in $1 -text -noout | \
    4. sed -n -e ’/^[ ]\+Subject:/{s/^.*CN=\([^,]*\).*/\1/;p}’
    5. # Underline Friendly Name with equal signs
    6. openssl x509 -in $1 -text -noout | \
    7. sed -n -e ’/^[ ]\+Subject:/{s/^.*CN=\([^,]*\).*/\1/;p}’ | \
    8. sed -e ’s/./=/g’
    9. # Output Fingerprint and swap = for :
    10. openssl x509 -in $1 -noout -fingerprint | sed -e ’s/=/: /’
    11. # Output PEM Data:
    12. echo ‘PEM Data:’
    13. # Output Certificate
    14. openssl x509 -in $1
    15. # Output Cettificate text swapping Certificate with Certificate Ingredients
    16. openssl x509 -in $1 -text -noout | sed -e ’s/^Certificate:/Certificate Ingredients:/’

  3. Locate and backup your ca-bundle.crt

    locate ca-bundle.crt should show you where on your system this file lives. On RHEL /usr/share/ssl/cert.pem is also symlinked to your ca-bundle.crt.

  4. Append the converted intermediate certificate to your ca-bundle.crt file.

    You can now test using the openssl command:

    openssl s_client -host your.ldap.edu -port 636 -CAfile /usr/share/ssl/certs/ca-bundle.crt.

    A Verify return code: 0 (ok) is what you are looking for.

  5. Configure OpenLDAP on the system that PHP is running on to use your ca-bundle.crt.

    Locate your ldap.conf for OpenLDAP. On RHEL it is /etc/openldap/ldap.conf.

    Add the following: TLS_CACERT /usr/share/ssl/cert.pem (which on RHEL is a symlink to ca-bundle.crt). Thanks to Rutgers for this tidbit.

  6. Restart httpd.

PHP should now successfully connect securely to your LDAP server.

Errata

Added restart of httpd (2005-09-10 11:52:00)

6 Responses to “HOWTO: “Fix” Secure LDAP in PHP”

  1. Stephen Says:

    I was able to append the cert data and conect from the client to the server. I am still unable to get php to connect securely to the ldap server. I am running RHEL4. Any ideas?

  2. pberry Says:

    Stephen, are you still getting certificate chain errors in your connection calls? If not, what is the LDAP error that is being reported by php?

    http://www.php.net/manual/en/function.ldap-error.php

  3. Stephen Says:

    I haven’t been able to get a specfic error is the strange part. I believe I have php error reporting on. Regular ldap auth connections work fine. The test also seems to work fine. I’m not sure what i’m missing.
    $ openssl s_client -host ad.ufl.edu -port 636 -CAfile /usr/share/ssl/certs/ca-bundle.crt

    SSL handshake has read 13838 bytes and written 336 bytes
    —-
    New, TLSv1/SSLv3, Cipher is RC4-MD5
    Server public key is 1024 bit
    SSL-Session:
    Protocol : TLSv1
    Cipher : RC4-MD5
    Session-ID: 152000001A3BD3700A84268615C8E86BA3259163E30CED81D0B43FC49278332B
    Session-ID-ctx:
    Master-Key: 196DC7FF35D5B2FEA31F9D2C08897F8136DF72C11 Key-Arg : None
    Krb5 Principal: None
    Start Time: 1178039514
    Timeout : 300 (sec)
    Verify return code: 0 (ok)

    Here’s some test code:

    \n”;

    ?>

    As the code sits I get “Could not bind to ldaps://ad.ufl.edu” though as mentioned ldap:// work fine.

  4. Stephen Says:

    The comment field didn’t like the code in the previous post so here’s a link: http://www.fred.ifas.ufl.edu/~rsreese/tmp/login.txt

  5. pberry Says:

    Definitely do a check for ldap_errno

    http://www.php.net/manual/en/function.ldap-errno.php

    This will at least tell you why the ldap server is not letting you in and hopefully put you on the right path. The message that you are getting is hard coded in your script, so that’s of no real help unfortunately.

    The other thing to make sure of is that you did step 5. I don’t have the proper command line test right now, but you should try connecting using ldapsearch from that box as well.

  6. Stephen Says:

    I setup php ldap_errno and I was getting the following:

    LDAP-Errno: -1
    LDAP-Error: Can’t contact LDAP server

    So even though the following command worked.

    $ openssl s_client -host ad.domain.edu -port 636 -CAfile /usr/share/ssl/certs/ca-bundle.crt

    I found that ldapsearch wasn’t. =(

    $ ldapsearch -H ldaps://ad.ufl.edu:636 -x -D rsreese@ad.domain.edu -W -b ou=users,dc=ad,dc=domain,dc=edu -s sub “cn=rsreese”
    Enter LDAP Password:
    ldap_bind: Can’t contact LDAP server (-1)
    additional info: TLS: unable to get CN from peer certificate

    After much confusion a single entry fixed the problem. =)
    # Add the following line to the /etc/openldap/ldap.conf
    TLS_REQCERT never