Apache lookup module

From RavenWiki
Jump to navigationJump to search

This is an Apache2 module (which appears to work in Apache 2.0 and Apache 2.2, but not later versions) designed to perform authorization functions for an existing REMOTE_USER (as derived from Raven or otherwise) by querying lookup.

Please send questions about this module to lookup-support@ucs.cam.ac.uk.

If you are planning to build and use this then be sure to read and understands the sections 'Portability' and 'Bugs and potential enhancements' below.

Note also that this module doesn't (and won't) work with Apache 2.4 and later. Similar functionality has been available built in to Apache since Apache 2.2 so continued use of this module is now deprecated. See seperate information in this wiki on how to do this, and for information on how to move away from using this module..

Building the module

To build the module from source and copy it into the correct Apache directory, first unpack the distribution tar file and cd into the directory it creates:

 tar zxf Ucam_lookupquery-0.90.tar.gz
 cd Ucam_lookupquery

then issue the following command as root:

 apxs -c -i mod_ucam_lookupquery.c

apxs may not be on root's default path and may be renamed, e.g. to apxs2, on some platforms. apxs and related files are sometimes provided by a httpd-devel, apache-devel, or apache2-devel package which will need to be installed. You will also need the OpenLDAP libraries and development files which will probably be provided by openldap and openldap2-devel packages, or similar. You may or may not also need to explicitly include the ldap libraries when building the module (the error message "undefined symbol: ldap_first_attribute" or similar when restarting Apache after adding the module suggests that you do and you haven't), in which case try

 apxs -c -i -lldap -llber mod_ucam_lookupquery.

Building under MacOS is more complicated still, because MacOS supports multiple architectures (e.g. i386 and x86_64) and you need to build the module so that it matches the architecture under which Apache runs. For most recent version (at least Leopard & Snow Leopard (10.5/10.6)) the following incantation should work (it builds a 2-way fat binary supporting both i386 and x86_64 architectures):

 apxs -c -i -lldap -llber -Wc,'-arch i386 -arch x86_64' -Wl,'-arch i386 -arch x86_64' mod_ucam_lookupquery.c

[Note that most earlier versions of MacOS shipped with Apache version 1 with which this module is not compatible] Under MacOS, the edit below for line 575 is also needed to get the module to compile.


Version 0.90 of the module isn't as portable as one might like. Depending on your platform details (Apache version, OpenLDAP version, C compiler, etc.) you may need to make some or all of the following changes. Some, though not all, of these changes are included in the source RPM above but won't be included by default except when built on RedHat systems.

  • Add, around line 26:
#include "http_request.h"  /* for ap_hook_auth_checker */
#include "apr_lib.h"       /* for apr_isspace.h */
  • At line 150, change
int error_code;


int error_code = 0;
  • For some older versions of OpenLDAP, Change lines 187/8 from
else if (strcmp(scope, "one") == 0) intscope = LDAP_SCOPE_ONE;
else if (strcmp(scope, "sub") == 0) intscope = LDAP_SCOPE_SUB;


else if (strcmp(scope, "one") == 0) intscope = LDAP_SCOPE_ONELEVEL;
else if (strcmp(scope, "sub") == 0) intscope = LDAP_SCOPE_SUBTREE;
  • Change line 575 from
regex_t *r = ap_pregcomp(p, w, REG_ICASE);


ap_regex_t *r = ap_pregcomp(p, w, AP_REG_ICASE);
  • Change line 829 from
while (s > fakeuser && isspace(s[-1])) s--;


while (s > fakeuser && apr_isspace(s[-1])) s--;

In addition you might want to change a number of instances (e.g. at lines 877, 910, and 196) of APLOG_ERR to APLOG_INFO to reduce the noise in your Apache error log.

Basic documentation

Load the module by including

 LoadModule ucam_lookupquery_module modules/mod_ucam_lookupquery.so

in the Apache configuration file, replacing 'modules/mod_ucamlookupquery.so' if necessary with the path to the the file containing the module, relative to ServerRoot if it doesn't start '/'. You can work out that this path should be by looking at other LoadModule directives in the file.

To use it, include something like

 Require LookupInst CS

in a protection block (except that due to a design mistake, 'Require LookupInst CS' probably won't actually do what you want - see below). For example to protect URLs starting /restricted to members of the institution lookup knows as 'CS' as identified by Raven you might want

 <Location /restricted>
     AuthType Uam-WebAuth
     Require LookupInst CS

You'd also need the ucam_webauth module loaded and an AACookieKey and perhaps a AAKeyDir directive elsewhere in the configuration (see the mod_ucam_webauth documentation). See also the official Apache documenation on <Location>, <Directory>, and Authentication, Authorization and Access Control in general.

Problem with 'Require LookupInst CS': the problem is that this actually implements the rule 'is a member of CS and nothing else'. While this will be OK for most people in lookup (people normally only belong to one institution), anyone in more than one institution will not get access. This might be fixed in a future version of the module, but in the meantime as a work around you can use the equivalent:

 Require LookupQuery ou=people sub (&(uid=%u)(instID=CS))



  Value:    on|off                 
  Default:  on

  Value:    space-separated list   
  Default:  ldap.lookup.cam.ac.uk:389        (actually uses LDAP_PORT)
(1) IPv6 literal addresses can be enclosed in []. 
(2) It tries each one till one answers, one way or another.
  Value:    string
  Default:  "o=University of Cambridge,dc=cam,dc=ac,dc=uk"


  Value:    string
  Default:  unset
  Value:    string
  Default:  unset    
If both of these are set, a non-anonymous LDAP bind is done, first setting up 
an encrypted (TLS) session. If either one is not set, a non-encrypted, 
anonymous bind is done.



Apart from LookupQuery, all these come in pairs, LookupXxx and LookupXxxMatch.
The former do exact matches on the data, the latter do regular expression
matches. Multiple data values for a Require implement OR. Multiple Requires 
implement AND. It is not clear if this is the True Apache Way or not - I have
failed to find a definitive statement - but it seems to me that whether
multiple Requires do AND or OR should be controlled from a higher level
(compare "satisfy any" for Require+Allow) because each run of the module just
deals with a single Require and returns yes/no. There would have to be a much 
more elaborate memory scheme to implement OR at this level.

Require LookupInst[Match] name1 [name2 ...]
  Runs:     ou=people sub (uid=%u) inst
  Needs:    (a) Exactly one entry
            (b) Exactly one attribute value, must match one of the names 
  Example:  Require LookupInst CS CL 
Require LookupAttr[Match] comma-separated-attrnames value1 [value2 ...]
  Runs:     ou=people sub (uid=%u) comma-separated-attrnames
  Needs:    (a) Exactly one entry
            (b) At least one attribute value must match something in the list
  Example:  Require LookupAttr cn,displayName "Jon Warbrick" "Philip Hazel"
Require LookupParentInst[Match] name1 [name2 ...]
  Runs:     ou=people sub (uid=%u) inst  
  Needs:    (a) Exactly one entry
            (b) Exactly one attribute value, must match one of the names 
  Else:     ou=insts sub (instID=inst) parentInstID
  Needs:    (a) Exactly one entry
            (b) Exactly one attribute value, must match one of the names
  Else:     Loop up the tree till ROOT (which is checked).
  Example:  Require LookupParentInst COLL
Require LookupUserInGroup[Match] name1 [name2 ...]
  Runs:     ou=groups sub (uid=%u) groupID
  Needs:    (a) One or more entries
            (b) At least one of the groupIDs to match one of the names.
  Example:  Require LookupUserInGroup 100001 100656
Require LookupQuery baseplus scope filter
  Runs:     baseplus scope filter
  Needs:    One or more entries (no attributes are looked up)
  Example:  RequireLookupQuery ou=groups sub 
              (&(uid=%u)(groupTitle=*Computing Service*))
The last rather artificial example checks that the user is a member of at least
one group that has "Computing Service" in its title. To run a query on the base 
itself, the first argument can be given as an empty string in quotes.

In the filter, literal characters * ( ) \ must be escaped as follows:

  *  =>  \2A
  (  =>  \28
  )  =>  \29
  \  =>  \5C

The sequence %u is replaced by the userid. To include a literal % before a 
lower case u, use \75.

Bugs and potential enhancements

  • What about Apache 1.3?
  • Is there any way to keep a cached connection to ldap.lookup.cam.ac.uk? If so, should it be done?
  • 'Require LookupInst[Match]' expects exactly one attribute value, which won't be the case for someone affiliated to multiple institutions. To confuse matters, it reports a 'Temporary failure accessing the database' (because it gets an unexpected number of attributes). It should require at least one attribute value, which must match one of the names. As a work around, you can use something like
Require LookupQuery ou=people sub (&(uid=%u)(instID=CS))
  • The same applies (without the convenient work around) to LookupParentInst[Match]
  • There is no scope for matching against the allUid when checking group membership using a privileged account. Probably needs to run
ou=groups sub (&(uid=%u)(allUid=%u)) groupID
  • Authenticated binds force the use of TLS and require that the OpenLDAP client libraries are configured to verify the JANET certificate used by lookup. Error messages in the Apache log such as 'ldap_start_tls_s: Connect error (-11)' indicate that this isn't the case.
    • The module doesn't provide any way to customise the location of trusted CA certificates. For OpenLDAP the default location of a file listing trusted CA certificates and/or a directory containing 'OpenSSL-hashed' trusted CA certificates can be set per-user from ~/.ldaprc or system-wide from ldap.conf (perhaps in /etc/openlap or /etc/ldap) using the TLS_CACERT and TLS_CACERTDIR directives. See 'man ldap.conf'.
    • The CA certificate curently (July 2011) required for loolup is one for the 'AddTrust External CA Root' - it is included by default in many Linux distributions, but not SuSE. It can be obtained from Comodo at https://support.comodo.com/index.php?_m=downloads&_a=viewdownload&downloaditemid=11&nav=0,1
    • Setting 'TLS_REQCERT allow' in ldap.conf/.ldaprc will disable all certificate checking, which is fine for testing but a BAD IDEA otherwise.
    • Versions of OpenLDAP linked against GnuTLS (rather than OpenSSL - Debian/Ubuntu in particular) don't support TLS_CACERTDIR. Further some (esp. that included with Ubuntu 8.04 'Hardy Herron') don't seem to be able to successfully verify lookup's certificate at all.