IT Services



Using LDAP Client Software With the Oak LDAP Service


Contents



1. Introduction



1.1. About This Document

This document describes how to use a variety of client software with Oak LDAP. Any client supporting SASL / GSSAPI authentication to the LDAP server, and TLS or SSL connection encryption, should be usable with Oak LDAP. The idea is to provide tips for a variety of programming languages, tools, and operating systems. We welcome information allowing us to expand this variety.



1.2. Using This Document

Start by seeing which parts of the common configuration you require. A good next step is to get the command line tools working, if they are available for your platform. Then move on to the specific section for your programming language or runtime environment.



1.3. Generic Service Information

See the top-level Oak LDAP Service page for general information about the service, including how to register to use it.

The service is provided via the ldap.oak.ox.ac.uk DNS name. TLS can be used as the transport layer by connecting to port 389. Alternatively, SSL can be used by connecting to port 636. For both TLS and SSL, one root certificates must be trusted. This is the Addtrust External CA Root (this may already be provided as part of your operating system). Prior to 10th May 2011, the GTE CyberTrust Global Root CA was used instead.

We recommend that you configure your LDAP clients in such a way that new CAs can be easily added in the future, should the CA change again.

Currently, authentication can only be performed via SASL, using the GSSAPI mechanism. This utilises our existing Kerberos single sign-on infrastructure. We plan to add plain password-based authentication in due course, but need to do some work to enable this.

The Oak LDAP Schema can be a useful reference, as a companion to the examples in this document.



2. Common Client Configuration

Some pieces of client configuration are the same across all, or most, of the runtime environments dealt with in the remainder of this document. These common elements are described in this section.



2.1. Ensure Oak LDAP's SSL Certificates Will be Trusted

As discussed above, one CA root must be trusted. The exact details of configuration will vary according to OS, LDAP libraries and clients, but one common case is a single file containing the CA roots (it could include more; this is the minimum requirement). In Debian, for example, the ca-certificates package manages the file /etc/ssl/certs/ca-certificates.crt which may be used for this purpose. The directory /etc/ssl/certs may also be configured as a CA directory for some applications.

Any application using the OpenLDAP libldap libraries for making LDAP connections (examples of this include PHP ldap support, the OpenLDAP command line tools (ldapsearch, ldapcompare, etc), and mod_webauthldap) require the following to exist in an ldaprc; commonly, a system-wide file exists; for example on Debian this is /etc/ldap/ldap.conf. This file should contain the following:

TLS_CACERT <path to CA certificates file>



2.2. Install Packages to Enable SASL GSSAPI

You will also need to have the SASL libraries installed, and in particular the SASL GSSAPI module. In Debian this is provided by libsasl2-modules-gssapi-mit.



2.3. Fetch Kerberos Keys Into Keytab Files

When you registered for the service, you requested access for specific principal(s). You can fetch newly generated keys for these principals using the kadmin utility or equivalent (unfortunately, there is no way to fetch a key over the kadmin protocol without triggering generation of a new key, so be careful if this principal is already in use elsewhere). An example session:

$ kadmin -p USER/itss@OX.AC.UK
kadmin: ktadd -k /PATH/TO/KEYTAB host/example.ox.ac.uk@OX.AC.UK
Entry for principal oak-ldap/example.ox.ac.uk with kvno 3, encryption type Triple DES cbc mode with HMAC/sha1 added to keytab WRFILE:/PATH/TO/KEYTAB.
Entry for principal oak-ldap/example.ox.ac.uk with kvno 3, encryption type DES cbc mode with CRC-32 added to keytab WRFILE:/PATH/TO/KEYTAB.

You can then check it has created something useful with:
$ klist -e -k /PATH/TO/KEYTAB



2.4. Make the Kerberos credentials cache available to the client

For the MIT or Heimdal Kerberos implementations, the KRB5CCNAME environment variable controls where the application looks for the credentials cache. If you're using Apache, you can use the SetEnv directive from mod_env. Alternatively, use the way of setting this environment variable provided by the runtime system or programming language you're using.

The credentials cache has a short lifetime (order of hours), so to keep it fresh you will need to periodically update it with new tickets fetched using the key in your keytab. The user updating the credentials cache based on the keytab will require read access to the keytab, and write access to the credentials cache. The credentials cache will then need to be readable by the user running the LDAP client, for example the web server.

One utility designed to assist with this is k5start, available in Debian in the package kstart. An example invocation is:

k5start -v -f /PATH/TO/KEYTAB -k /PATH/TO/CCACHE YOURPRINCNAME@OX.AC.UK
You should arrange for k5start to run as a continually-running process (daemon) on the system.

Another way to keep a fresh credentials cache derived from a keytab is by periodically running kinit.



3. Querying Oak LDAP with the ldap* Command-Line Utilities

The instructions in this section assume that you have configured your system according to Common Client Configuration.

ldapsearch and ldapcompare, part of the OpenLDAP client tools, may be used to perform initial testing of access to the LDAP service. In Debian, they are contained in the ldap-utils package.

Assuming you have the keytab of the principal allowed access to the LDAP service accessible, you can do:

$ export KRB5CCNAME=<PATH_TO_CREDENTIALS_CACHE>
$ kinit -k -t <PATH_TO_KEYTAB> <PRINCIPAL_NAME>
$ ldapsearch -H ldaps://ldap.oak.ox.ac.uk \
    -b ou=people,dc=oak,dc=ox,dc=ac,dc=uk \
    '(sn=<your surname>)'
This should return results including (assuming the principal in question is permitted access to view your record) information about you and other people with your surname.

    ldapcompare -H ldaps://ldap.oak.ox.ac.uk \
    oakPrimaryPersonID=<yourpersonid (from above)>,ou=people,dc=oak,dc=ox,dc=ac,dc=uk \
    eduPersonOrgUnitDN:oakUnitCode=oucs,ou=units,dc=oak,dc=ox,dc=ac,dc=uk
This will return the string "TRUE" or "FALSE" depending on whether you have an affiliation with the unit (in this case oucs).

We recommend that you ensure that you can perform the above steps successfully on your server using the keytab/principal registered for access before going any further.



4. Querying Oak LDAP from mod_webauthldap

The instructions in this section assume that you have configured your system according to Common Client Configuration.

mod_webauthldap is an Apache httpd module will allow you to make basic access control decisions independently of any application which you are hosting. For example, you can restrict access to members of a given unit or units. mod_webauthldap is very simple to configure, especially if you are already using webauth; however, it is not possible to use it for access policies much more complicated to those described above.

This section assumes that you already have mod_webauth installed and working on your Apache server. If not, please see http://www.oucs.ox.ac.uk/webauth for details.

Reference documentation for mod_webauthldap is available at http://webauth.stanford.edu/manual/mod/mod_webauthldap.html.

This section is not intended to be exhaustive, but gives examples of some common configurations. Refer to the Oak LDAP schema documentation for a full list of possible queries and attributes.



4.1. Basic configuration

The following config line should appear in the main part of your Apache configuration (i.e. outside any VirtualHost blocks). You may need to adjust the path to the module, depending on where you or the package installed it:

    LoadModule webauthldap_module /usr/lib/apache2/modules/mod_webauthldap.so
These lines may appear in the main configuration, or inside a VirtualHost block:
    WebAuthLdapKeytab /etc/webauth/yourldapkeytab
    WebAuthLdapTktCache /var/lib/webauth/krb5cc_ldap
    WebAuthLdapHost ldap.oak.ox.ac.uk
    WebAuthLdapBase ou=people,dc=oak,dc=ox,dc=ac,dc=uk
    WebAuthLdapSSL on

Usually this will complement WebAuth Kerberos authentication as described in the WebAuth documentation



4.2. Restricting access based on affiliation with a University unit

To configure your web server to allow restricting access to members of a given unit or units, add the following lines to those you have already specified in Common Client Configuration.

    WebAuthLdapFilter (oakPrincipal=krbPrincipalName=USER@OX.AC.UK,cn=OX.AC.UK,cn=KerberosRealms,dc=oak,dc=ox,dc=ac,dc=uk)
    WebAuthLdapAuthorizationAttribute eduPersonOrgUnitDN
Then, in a block matching the content you wish to protect (e.g. a Location block):
    AuthType WebAuth
    Require privgroup oakUnitCode=oucs,ou=units,dc=oak,dc=ox,dc=ac,dc=uk
To restrict access to members of either OUCS or Magdalen, do:
    AuthType WebAuth
    Require privgroup oakUnitCode=oucs,ou=units,dc=oak,dc=ox,dc=ac,dc=uk
    Require privgroup oakUnitCode=magd,ou=units,dc=oak,dc=ox,dc=ac,dc=uk
It is not possible, using mod_webauthldap, to AND privgroup memberships.



4.3. Restricting access to members of a given unit with a given status

Add the following lines to the configuration specified in "Basic Configuration":

    WebAuthLdapFilter &(oakPrincipal=krbPrincipalName=USER@OX.AC.UK,cn=OX.AC.UK,cn=KerberosRealms,dc=oak,dc=ox,dc=ac,dc=uk)(eduPersonOrgUnitDN=oakUnitCode=oucs,ou=units,dc=oak,dc=ox,dc=ac,dc=uk)
    WebAuthLdapAuthorizationAttribute oakStatus
Then, in a block matching the content you wish to protect (e.g. a Location block):
    AuthType WebAuth
    Require privgroup staff
To restrict access to staff or senior members, do:
    AuthType WebAuth
    Require privgroup staff
    Require privgroup senmem



4.4. Providing additional attributes to the application

mod_webauthldap also provides the ability to export extra LDAP attributes relating to the authenticated user to the OS environment, which may be useful to CGI scripts, etc. Refer to the mod_webauthldap documentation for further details.



5. Querying Oak LDAP From Perl

The instructions in this section assume that you have configured your system according to Common Client Configuration.



5.1. Using Standard CPAN Modules

If you are using a Debian-based distribution, install these packages:

For other distributions, use the appropriate method to install these CPAN modules:

(An alternative to the native Perl GSSAPI module is Authen::SASL::Cyrus which is based on the SASL libraries described above in section 2.2, but in our tests there appeared to be some problems with its handling of the round-robin DNS hostname of the Oak LDAP service.)

You will also need be running with access to suitable Kerberos credentials (eg by running kinit as described in the introductory documentation above).

Sample Perl code to bind to the Oak LDAP service and retrieve information about a user:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    # Need Net::LDAP 0.37 or above
    use Net::LDAP;
    # Replace Perl with Cyrus below to use Authen::SASL::Cyrus
    use Authen::SASL qw( Perl );
    use Readonly;
    
    Readonly my $base => 'ou=people,dc=oak,dc=ox,dc=ac,dc=uk';
    Readonly my $cafile =>
      '/etc/ssl/certs/ca-certificates.crt';
    
    my $sasl = Authen::SASL->new( mechanism => 'GSSAPI' );

    my $ldap = Net::LDAP->new(
        'ldap.oak.ox.ac.uk',
        onerror               => 'die',
        multihomed            => 1
    );

    $ldap->start_tls( verify => 'require', cafile => $cafile );
    $ldap->bind( sasl => $sasl );
    
    my $search = $ldap->search( base => $base, filter => '(sn=hargreaves)' );
    
    foreach my $entry ( $search->entries ) {
        $entry->dump;
    }

Note that, for Perl, we recommend the use of TLS rather than SSL, (ldap:// rather than ldaps://) since IO::Socket::SSL does not support round-robin addresses correctly.



5.2. Using Oak::LDAP

We hope to provide a simplified object-oriented interface to Oak LDAP from Perl; this is not yet available.



6. Querying Oak LDAP from PHP

The instructions in this section assume that you have configured your system according to Common Client Configuration.

Install PHP support for SASL and LDAP. On Debian, these are provided by the packages php5-sasl and php5-ldap.

You will also need to be running with access to the kerberos credentials (eg by running kinit as described in the introductory documentation above).

If running with a web server, you could use k5start to maintain a credentials cache separately, and set the KRB5CCNAME environment variable in the PHP code.

Sample code:

    #!/usr/bin/php
    <?php
    $base = 'ou=people,dc=oak,dc=ox,dc=ac,dc=uk';
    $ldap = false;
    $ldap = ldap_connect('ldaps://ldap.oak.ox.ac.uk');
    if ( $ldap === false ) {
        print "Could not contact LDAP server\n";
        die;
    }
    ldap_set_option($ldap,LDAP_OPT_PROTOCOL_VERSION,3);
    putenv("KRB5CCNAME=/PATH/TO/YOUR/service_ccache");
    $ldap_bind_result = ldap_sasl_bind($ldap, NULL, NULL, 'GSSAPI');
    if ( $ldap_bind_result === false ) {
        print "Could not bind to LDAP server\n";
        die;
    }
    $search = ldap_search($ldap, $base, '(sn=hargreaves)');
    if ($search) {
        $entries = ldap_get_entries($ldap,$search);
        for($i=0; $i < $entries["count"];$i++) {
            print "dn is: " . $entries[$i]["dn"] . "\n";
            print "first cn entry is: " . $entries[$i]["cn"][0] . "\n";
            print "first email is: " . $entries[$i]["mail"][0] . "\n";
        }
    }
    ?>

Note that PHP's ldap_get_entries function returns an associative array where the LDAP attributes are given in lower case. For example, the LDAP attribute givenName is obtained using

    $entries[$i]["givenname"][0]
rather than
    $entries[$i]["givenName"][0]



7. Querying Oak LDAP from Python

The instructions in this section assume that you have configured your system according to Common Client Configuration.

You will need python-ldap (on Debian-based systems, aptitude install python-ldap should be sufficient).

You will also need to be running with access to Kerberos credentials (eg by running kinit as described in the introductory documentation above, or by setting the KRB5CCNAME environment variable to point to a suitable credentials cache).

Sample code:

   #!/usr/bin/python
   
   import ldap, ldap.sasl, sys, pprint
   
   try:
     auth = ldap.sasl.gssapi("")
     oakldap = ldap.initialize("ldap://ldap.oak.ox.ac.uk:389")
     oakldap.start_tls_s()
     oakldap.sasl_interactive_bind_s("",auth)
     results = oakldap.search_s( "ou=people,dc=oak,dc=ox,dc=ac,dc=uk",
                                 ldap.SCOPE_SUBTREE,
                                 "(sn=Howes)" )
   except ldap.LDAPError, error:
     print "LDAP error: %s" % error
     sys.exit()
   
   pp = pprint.PrettyPrinter()
   pp.pprint(results)



8. Querying Oak LDAP from Java

The instructions in this section assume that you have configured your system according to Common Client Configuration.



8.1. Using JNDI

Example JAAS configuration:

OakGSSAPI {
    com.sun.security.auth.module.Krb5LoginModule required 
        keyTab="conf/keytab"
        useKeyTab=true
        doNotPrompt=true
        principal="webauth/HOSTNAME@OX.AC.UK"
        debug=TRUE;
};
Example Java VM Invocation Arguments:
java -Djava.security.auth.login.config=conf/jaas.conf -Djava.security.krb5.conf=/etc/krb5.conf
Example code for querying Oak LDAP:
import java.security.PrivilegedAction;
import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;


public class LdapGssapi {

    /**
     * @param args
     */
    public static void main(String[] args) {

        try {

            LoginContext lc = new LoginContext("OakGSSAPI");
            lc.login();

            Subject.doAs(lc.getSubject(), new PrivilegedAction() {

                public Object run() {

                    // Set up environment for creating initial context
                    Hashtable env = new Hashtable(3);

                    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

                    // Must use fully qualified hostname
                    env.put(Context.PROVIDER_URL, "ldaps://ldap.oak.ox.ac.uk");

                    // Request the use of the "GSSAPI" SASL mechanism
                    // Authenticate by using already established Kerberos credentials
                    env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");

                    try {
                        /* Create initial context */
                        DirContext ctx = new InitialDirContext(env);

                        NamingEnumeration units = ctx.list("ou=units,dc=oak,dc=ox,dc=ac,dc=uk");

                        while(units.hasMore()) {
                            System.out.println(units.next());
                        }

                        // Close the context when we're done
                        ctx.close();
                    } catch (NamingException e) {
                        System.err.println("LDAP exception: " + e.getMessage());
                    }


                    return null;

                }

            });

        } catch (LoginException e) {
            System.err.println("Login exception: " + e.getMessage());
        }

    }

}

The following references may be useful

When using GSSAPI or LDAP over SSL pooling has to be done manually.



8.2. Using JLDAP

JLDAP doesn't support GSSAPI. Since GSSAPI is currently the only authentication mechanism supported by the Oak LDAP service, it's not currently possible to use JLDAP with Oak LDAP.



9. Querying Oak LDAP from Ruby

The instructions in this section assume that you have configured your system according to Common Client Configuration.



9.1. Synchronisation to SQL Database

Christopher Hoskin (St Antony's College) has developed some Ruby code to synchronise a local database with unit data in Oak LDAP. As with other examples, this will need access to a suitable Kerberos credentials cache so would typically be run under k5start. Heavily trimmed example code:

# main.rb
# Extremely trimmed example to retrieve and print attributes of a person entry
#
# Supplied by Christopher Hoskin, St Antony's College, 22nd July 2010

require 'ldap'

conn = LDAP::SSLConn.new(host='ldap.oak.ox.ac.uk', port=636)
conn.sasl_quiet=true
conn.sasl_bind('','')

conn.search('ou=people,dc=oak,dc=ox,dc=ac,dc=uk', LDAP::LDAP_SCOPE_ONELEVEL, "(sn=Howes)", nil) { |entry|
    entry.to_hash.each { |key, value| puts "%s : %s" % [key, value[0]]}
}

conn.unbind



10. Performance



10.1. Paging Results for Bulk Transfers

If your query returns more than a few hundred entries, throughput will be improved by using paged results, which is a feature of the LDAP protocol.



10.1.1. with the ldap* Command-Line Utilities

Example: use the ldapsearch option -E pr=1000/noprompt for paged results with 1000 entries per page.