mod_authz_ldap-0.15

An Apache LDAP Authorization module

This file documents version 0.15 of the mod_authz_ldap module. For details about the change history of the module, please consult the ChangeLog.

News

Contents

  1. Introduction
  2. Installation
  3. Configuration
  4. Examples
  5. Caveats
  6. Performance Impact
  7. Troubleshooting
  8. Configuration Directives
  9. License
  10. Open Issues

1. Introduction

1.1. What it does

This Apache LDAP authentication/authorization module tries to solve the following problems that other such modules may not solve in all cases:

  1. Map the short form of the distinguished name of a certificate and its issuer obtained from the environment of mod_ssl to a user distinguished name in an LDAP directory.
  2. Check the age of a password in an LDAP directory, denying authorization in case the password is to old.
  3. Authorize a user based on roles or an arbitrary LDAP filter expression.
  4. Authorize a user based on whether he owns a file or belongs to the group owning a file.
The module can perform an ordinary LDAP authentication using an LDAP bind call, but is incapable of verifying an SHA1 or crypt password hash from the directory, as mod_auth_ldap can.

The module also tries to do reduce LDAP connection overhead by caching a connection between requests (one per server record). This is most likely to improve performance in the case of certificate authentication, as for basic authentication a bind to the directory on a new connection is necessary with every request. Future development may add a cache to improve performance.

Version 0.8 added the ability to use the cache built into some client libraries, most notably OpenLDAP. However, it turned out that the cache for OpenLDAP 2.0.7 does not work, and only causes Apache to dump out the contents of BER buffers instead of authenticating users.

mod_authz_ldap uses some functions from libraries that are only available on Unix systems, it will most probably not work on a Win32 system. There are no plans to fix this problem.

Of course there are other modules that perform LDAP authentication. Not mentionning them here does not mean that they are insignificant, quite the contrary is true. I just happen to only have used Alex Mayrhofers solution. And as far as I know, none of the available solutions does either certificate mapping or password aging.

1.2. Download

The current version can be downloaded from mod_authz_ldap-0.15.tar.gz. The most up to date version can always be found at http://www.othello.ch/software/mod_authz_ldap.html

2. Installation

mod_authz_ldap can be installed either as a dynamic shared object (DSO) or statically linked into the apache server. It is recommended to install the module as a DSO, as this mode is much better tested. After installation, proceed to the configuration.

2.1. configure options

mod_authz_ldap uses configure to explore the system and set appropriate compiler and linker flags. There are a number of options available:

--with-debug
Include additional debugging code in the module. Use this only during testing, as the volume of debug output can be quite large and turn into a performance penalty.
--with-openssl-path=path-to-openssl
The configure script tries to find the OpenSSL libraries in /usr/local/ssl. If you have them installed in some other directory, you should use this option to help configure find them.
--with-cachesize=cachesize-in-kb
Switches on the LDAP cache with a given size. Note that the in current version of OpenLDAP, the cache does not work.
--with-timeout=timeout
Sets the default time to live of an LDAP cache entry.
--with-owner
Enable the nonportable code that bases access decisions on file owner and group.

2.2. Install as a DSO

Configure the package as a DSO

    $ ./configure --with-apxs=path-to-apxs
Set LDFLAGS appropriately if your LDAP libraries or not on the standard library search path. Then compile and install the support programs
    $ make
    $ su
    Password:
    # make install
    . . .
The module must be compiled with separate Makefile targets:
    $ make module
    $ su
    Password:
    # make module-install
    . . .
The installation does automatically activate the module in the default server configuration file. If the module is to be used in servers using some other configuration file, it should be activated by adding the following commands to the configuration file:
    LoadModule authz_ldap_module   libexec/mod_authz_ldap.so
    AddModule mod_authz_ldap.c

2.3. Install as a statically linked module

Even if you decide to install as a statically linked module, you will have to follow the steps above to compile the support programs cert2ldap and certfind. Note also that the static build is not really supported any longer, but only provided as a start for people unfortunate enough not to be able to use DSO.

To install as as statically linked module, do the following:

After apache has been rebuilt, the configuration file must be modified to activate authentication through the new module.

3. Configuration

mod_authz_ldap is configured through a set of directives on the directory level. In particular, the server creates an LDAP server connection for each directory configuration. Subsequent requests to the same directory do not create a new LDAP connection, except when Basic Authentication is done against the directory, as this is done through a bind call in a separate connection each time authentication is necessary.

If mod_authz_ldap should accept that other authorization modules may grant access to a user although mod_authz_ldap has denied it, it must be denied authority by the command

    AuthzLDAPAuthoritative off
The default is that mod_authz_ldap is authoritative.

3.1. Configuring an LDAP Server

Whenever mod_authz_ldap is supposed to authenticate or authorize (if the user was authenticated by a certificate, only the authorization part is left to the module) a user, it needs to contact an LDAP directory. The following directives configure the directory:

    AuthzLDAPServer         server[:port]
    AuthzLDAPBindDN         DN_to_bind_as
    AuthzLDAPBindPassword   Password_to_bind_with
If no server is configured, a server on the local host on the standard LDAP port 389 is assumed. If no bind credentials are defined, an anonymous bind is performed.

3.2. Configure LDAP User Authentication

User authentication through basic authentication against an LDAP directory is by default active if AuthName and AuthType are defined for a directory or location. It works as follows:

  1. If the user search scope has been set to something other than base, a query is performed from the configured user base DN for a node with the attribute uid set to the name entered by the user in the login dialog. The user's distinguished name is set to the distinguished name of the node found, provided the search returned a single node. If the user search scope was base, then the distinguished name is constructed from the userkey (normally uid), the username from the dialog and the user search base.
  2. The password is verified by binding to the directory as the user whose distinguished name was found in the previous step, with the password from the login dialog.
  3. If the authentication succeeded, additional requirements are verified:
As one can see, the module needs quite some additional information to perform all these tasks.

As one can see, mod_authz_ldap needs additional information besides the LDAP server to be able to

  1. Find the user in the directory:
        AuthzLDAPUserKey     UID_attribute_name_in_DN
        AuthzLDAPUserBase    DN_of_user_subtree
        AuthzLDAPUserScope   base|onelevel|subtree
    
    The user node in the DIT below the node specified by the AuthzLDAPUserBase directive is expected to have an attribute named by the value of AuthzLDAPUserKey, the value of which is supposed to be the user id from the basic authentication. The default search scope for a user is base. In that case, the distinguished name of the user will be of the form userkey=uid,userbase. If none of the above values are set, the user must enter his/her distinguished name in the password dialog.
  2. Find the group for the user: see the section 3.4 on group verification below for details.

After successful authentication, the distinguished name of the user is placed in the authorization header. Subsequent authorization modules must therefore be able to deal with a distinguished name as the user id.

A require user directive will search for the user according to the settings above and verify that exactly one user is returned an matches the distinguished name obtained from the LDAP authentication. If the AuthzLDAPUser* attributes are not set, a require user statement must specify the full distinguished name of a user.

3.3. Configure Certificate Mapping

Certificate mapping tries to find a user based on the subject and issuer DN. As uniqueness of distinguished names of subjects across CAs is not guaranteed, only the pair (issuer, subject) is unique. mod_ssl does not make the issuer DN directly accessible to other modules (except to CGIs and SSIs), so some minor modification is required.

The LDAP server has to be prepared to perform this mapping. It needs to contain nodes of the following type (described here in old openldap version 1 notation):

attribute	issuerDN	ces
attribute	subjectDN	ces

objectclass	certificatemap
	requires
		objectClass,
		issuerDN,
		owner
	allows
		subjectDN,
		serialNumber,
		userCertificate

Although it looks as both subjectDN and serialNumber can be omitted, one of them is needed (see the discussion of the AuthzLDAPUseSerial directive), but this cannot be expressed in the above syntax. A similar remark applies to the OpenLDAP syntax below.

A definition for the attributes issuerDN and subjectDN and for the objectclass certificatemap should be added to the directory server. Openldap version 1 users can use the file authzldap.slapd.conf file for this purpose, version 2 users will use authzldap.schema. The latter contains official object IDs.

attributetype ( 1.3.6.1.4.1.4263.5.1 NAME 'issuerDN'
	DESC 'The user friendly version of the distinguished name of the issuer of a certificate'
	EQUALITY caseExactIA5Match
	SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE )

attributetype ( 1.3.6.1.4.1.4263.5.2 NAME 'subjectDN'
	DESC 'The user friendly version of the distinguished name of the subject of a certificate'
	EQUALITY caseExactIA5Match
	SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE )

objectclass ( 1.3.6.1.4.1.4263.5.3 NAME 'authzLDAPmap'
	DESC 'Map Entry for mod_authz_ldap'
	SUP top AUXILIARY
	MUST ( issuerDN $ owner )
	MAY ( subjectDN $ serialNumber $ userCertificate )

Certificate Mapping searches for a node in a subtree of the directory that has issuerDN and subjectDN as read from the certificate. It is configured through the following directives:

    AuthzLDAPUseCertificate on|off
    AuthzLDAPUseSerial on|off
    AuthzLDAPMapBase        base_dn_for_search_for_certificate_dn
    AuthzLDAPMapScope       base|onelevel|subtree
This only makes sense, if mod_ssl has been configured to request a client certificate through the SSLRequireSSL option (see the mod_ssl documentation for details).
    SSLRequireSSL
While earlier releases of this module read the subjectDN from the basic authentication header, they now read it from the mod_ssl context, as all the variables used for the mapping.

Last, but not least, it is possible to directly match a user against her certificate. However, this requires that the directory server is able to perform an equality match for the userCertificate attribute. This is not the case by default in OpenLDAP, but can be enabled by changing the definition of userCertificate in core.schema. Change the definition from

attributetype ( 2.5.4.36 NAME 'userCertificate'
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 )
to
attributetype ( 2.5.4.36 NAME 'userCertificate'
        EQUALITY octetStringMatch
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 )
Strictly speaking, this violates some generally accepted schema standards, but it is so immensly useful.

mod_authz_ldap can now directly match the certificate if the directive AuthzLDAPDirect is enable. In this case, the module performs a search under the the base specified by AuthzLDAPUserBase with scope as set by AuthzLDAPUserScope for a user having a userCertificate attribute with the same certificate as the SSL client supplied.

Note: +FakeBasicAuth is no longer needed for certificate authentication. The module finds the subject and issuer distinguished names from the SSL context, there is no necessity to misuse a fake basic authentication any longer. If +FakeBasicAuth is specified, the basic authentication will simply be overwritten wherever the module is active.

3.4. Configure Group Verification

The require group directive in the context of the mod_authz_ldap module asks to verify membership of a user in a group defined in the LDAP directory. The group must be of objectclass groupOfNames, the members must be defined in member attributes, which have distinguished names of users as values.

    AuthzLDAPGroupBase    base_for_onlevel_group_search
    AuthzLDAPGroupKey     name_attribute_of_group
Groups are expected to have a distinguished name of the form
    groupkey=groupkeyvalue,groupbase
A user is accepted if the group has a member attribute with current user's distinguished name as value.

If neither directive is configured, the require statement must specify the full distinguished name of a group.

3.5. Configure Roles and Filters

Starting with release 0.14, mod_authz_ldap can verify roles of a user. In the most general form, a role is a required value of an attribute of the user's node. mod_authz_ldap simply needs to know the name of the attribute, which can be specified with the configuration directive

    AuthzLDAPRoleAttributeName attrname
If this is set, you can include a directive
    require role rolename
in the server configuration file, which will cause a user to be allowed only if he has an attribute of name attrname with value rolename.

Note that * is a valid value for the role name, this results in the user being accepted if he simply has that attribute. This can be used e.g. to accept all users that have a mailbox (if some attribute contains the name of his mailbox). Substring matches are also possible, on could accept a user exactly if she has a mailbox in a given domain, if an attribute mailboxhost contains the mailbox host. A requirement for a role of the form *.dom.ain will do the trick. This generality of the role mechanism can often save you from having to extend the schema and define a special role attribute.

Even greater generality is offered by the require filter directive of the form

    require filter [ SCOPE ] filter-expression
The SCOPE must be one of the strings BASE, ONLEVEL or SUBTREE. If the word after filter is not one of these strings, it is assumed that the scope was not specified. In this case, the default of BASE will be used and the rest of the line is used as a filter expression. The requirement succeeds if the filter returns something, it fails if nothing is returned by the filter, or if the filter causes an LDAP error.

3.6. Configure Authorization based on File Ownership

In some applications, it is necessary to base authorization decisions on file ownership. Unfortunately, as the apache process runs with rather low privileges, ordinary access control mechanisms are not suitable. However, using the ``require owner'' and ``require group-owner'' directives, one can restrict access to a file to the owner or members of the group of a file. This model of course assumes Unix file permissions, and is inherently not portable. This is why this functionality must be explicitely enabled using the --with-owner configuration flags.

3.7. Configure Password Expiration

For password aging, mod_authz_ldap must have a way to find out the last modification time of the password. The user entry in the directory needs to have an attribute for that. With OpenLDAP, the option lastmod on needs to be set in the slapd configuration file. The configuration directive AuthzLDAPModifyKey must be specified as follows:

    AuthzLDAPModifyKey modifytimestamp

By requireing a maximal age, only users that have modifed their password recently enough will be accepted:

    require age 3.5
This denies users access that have last changed their password more than 3.5 days ago.

3.8. Proxy Authentication

The module can also be used for proxy authentication. The module will normally recognize automatically whether proxy authentication or normal authentication is in effect. In cases where it may not be clear which kind of authentication is happening, the the flag AuthzLDAPProxyAuthentication must be set to on for proxy authentication, and to off for normal authentication. One such case is a reverse proxy, which works as a proxy, but authenticates as a server.

3.9. Miscellaneous

Logging

The mod_authz_ldap module has its own log level, which is set by default to LOG_DEBUG. If the log level of the server is set to debug, all the information available from the module will be logged to the error log. However, the log information flowing from the module can be reduced by setting a different log level with the AuthzLDAPLogLevel directive. Acceptable parameters are the same as for the LogLevel directive.

Transparent Authentication

When using certificate authentication (AuthzLDAPUseCertificate set to on), the basic authentication header is no longer necesary. However, some applications will still need a user name, which e.g. in CGI-programms is read from the basic authentication header. For these programs, the basic authentication header is overwritten with the distinguished name of the owner of the certificate. In some cases, e.g. in a reverse proxy authenticating with certificates, the application may require an additional athentication. This means that the basic authentication header may not be overwritten by mod_authz_ldap, which can be prevented with the directive

    AuthzLDAPSetAuthorization off
The default is on.

4. Examples

Two examples show how this module can be used to secure a web server or reverse proxy.

4.1. Web server authentication with X.509 certificates

This example describes how mod_authz_ldap can be used to authorize a user authenticated by an X.509 certificate. The LDAP server in use is an OpenLDAP server, for other servers, the preparatory steps (modification of the shema, user and group definitions) may be different.

  1. Prepare the directory to store short forms of certificates of a user. This involves adding suitable object types and attribute definitions to your directory, as described in above.
  2. Add nodes for the certificate mapping to the directory. You may want to modify the script addcertmap of the distribution for this purpose:
    ldapadd -c -D cn=root,dc=othello,dc=ch -w secret <<EOF
    dn: ou=AuthzLDAPCertmap,dc=othello,dc=ch
    objectclass: top
    
    dn: uid=afm,ou=AuthzLDAPCertmap,dc=othello,dc=ch
    objectclass: top
    objectclass: authzLDAPmap
    issuerDN: /C=CH/ST=Schwyz/L=Altendorf/O=Othello/CN=Othello internal CA/Email=ca@othello.ch
    subjectDN: /C=CH/ST=Schwyz/L=Altendorf/O=Othello/CN=Andreas Mueller/Email=afm@othello.ch
    owner: uid=afm,ou=People,dc=othello,dc=ch
    
    EOF
    
  3. Configure Apache to authenticate through a client certificate, to map the certificate DN to a user node in the directory and to authorize access to the directory /confidential based on the group membership of the user:
        <Location /confidential>
            SSLRequireSSL
            AuthName        AuthzLDAP
            AuthType        Basic
            AuthzLDAPServer "localhost:389"
    
            AuthzLDAPUseCertificate on
            AuthzLDAPMapBase ou=AuthzLDAPCertmap,dc=othello,dc=ch
            AuthzLDAPMapScope onelevel
    
    	require valid-user
        </Location>
    

4.2. Secure Reverse Proxy

One particularly usefull application of apache and its proxy module is as a security gateway to an internal web server. The ProxyPass and ProxyPassReverse directives in conjunction with the above configuration lead to a secure proxy gateway to some internal server. Here es an example configuration:

    <Location />
        SSLRequireSSL
        AuthName        AuthzLDAP
        AuthType        Basic
        AuthzLDAPServer "localhost:389"

        AuthzLDAPUseCertificate on
        AuthzLDAPMapBase ou=People,dc=othello,dc=ch
        AuthzLDAPMapScope onelevel

        require valid-user
    </Location>
    ProxyPass        / http://intrahost.doma.in/
    ProxyPassReverse / http://intrahost.doma.in/
This grants a user authenticated through an X.509 certificate and belonging to the group authz access to the web server on intrahost.doma.in. Note that you cannot do without the AuthName or AuthType directive.

5. Caveats

Since the module modifies the basic authentication headers of a request, a call to the authentication function in a subrequest will find an already transformed user name, which cannot be authenticated again. The authentication function only performs authentication for the initial request. As a consequence, if a subrequest leads to a directory differently authenticated than the initial request, the mapping for the subrequest may be incorrect. However, the authorization is performed so differing require clauses for the subrequest are handled correctly.

6. Performance Impact

In a large directory, authentication with mod_authz_ldap becomes slow as soon as one of the searches performed is slow. Indexes can improve the performance considerably, the following table shows the attributes that need to be indexed for the various parts of mod_authz_ldap
Search Type Attribute Index Type
User Search uid equality
Group Search cn equality
Certificate Mapping issuerDN equality
subjectDN equality

The module measures the elapsed and the cpu time for each request and logs it in log level debug to the error log. On a 233MHz Pentium, one can see that an immeasurable amount of cpu time is spent in the module (hardly ever more than the 10ms resolution). The elapsed time depends very much on whether this is the first call to the module: during the first call, an LDAP connection is established, which makes take the module around 70ms if the LDAP server resides on the same machine. Subsequent calls to the authorization function usually take just above 1ms, but calls to the authentication function less than 1ms. The performance hit by this module is therefore negligible: a simple directory listing request on the same machine takes around 150ms to complete, so the impact of the module is around 1%.

Even a somewhat dated Sun SPARCstation 10 spends only a few milliseconds in the authentication function, the authorization function is even cheaper.

All this changes for worse if the LDAP server is overloaded or otherwise slow. E. g. if the certificate issuer or subject attribute is not indexed, the search for the owner of the certficate may well be slow.

7. Troubleshooting

mod_authz_ldap makes extensive use of ap_log_rerror to log debug messages to the servers error log. All critical actions are documented there, and all errors generate an error message. The first step when debugging mod_authz_ldap is therefore to raise the server's log level to debug.

If you cannot solve the problem from the debug output, you may send a problem report to the author. Please equip the message with a succinct Subject:-line, or it may get lost between the junk mail messages (if your subject is ``Help'', it may well happen that the message is not read at all).

8. Configuration Directives


Syntax: AuthzLDAPServer host[:port]
Context: virtual host, directory
Default: as set in the LDAP client configuration file, usually something like /etc/ldap.conf
Defines the LDAP server to connect to. If the port is not set, the standard LDAP port 389 is used.
Syntax: AuthzLDAPBindDN dn
Context: virtual host, directory
Default: none
Some servers require an LDAP bind, this directive sets the distinguished name for the bind operation.
Syntax: AuthzLDAPBindPassword pw
Context: virtual host, directory
Default: none
Some servers require an LDAP bind, this directive sets the password name for the bind operation.
Syntax: AuthzLDAPUserBase dn
Context: virtual host, directory
Default: none
The user must be searched for in the directory, this directive sets the search base.
Syntax: AuthzLDAPUserKey attributename
Context: virtual host, directory
Default: none
The value of this directive defines the search filter used to search for the user.
Syntax: AuthzLDAPUserScope {base|onlevel|subtree}
Context: virtual host, directory
Default: base
If the namespace for users in the directory is flat, a onlevel search will be the most efficient way to find the user, but some organisations will have hierarchical name spaces. If the namespace ist flat and the user distinguished name can be computed from userid and search base, a base search is also possible. In this case, the userid is constructed as described above.
Syntax: AuthzLDAPGroupBase dn
Context: virtual host, directory
Default: none
Set the base for group membership requirement searches.
Syntax: AuthzLDAPGroupKey attributename
Context: virtual host, directory
Default: none
Set the name of the attribute identifying groups underneath the group search base set by the AuthzLDAPGroupBase directive.
Syntax: AuthzLDAPGroupScope {base|onlevel|subtree}
Context: virtual host, directory
Default: base
Scope to search for matching groups.
Syntax: AuthzLDAPUseCertificate {on|off}
Context: virtual host, directory
Default: off
If set to on, mod_authz_ldap tries to map the issuer and subject distinguished name from the certificate to a user distinguished name in the directory.
Syntax: AuthzLDAPDirect {on|off}
Context: virtual host, directory
Default: off
If set to on, mod_authz_ldap uses the certificate, and tries to match against a userCertificate attribute of the user. Note that the search starts at the base set by AuthzLDAPUserBase with the scope set by AuthzLDAPUserScope, so both have to be set to some reasonable value. Note also that equality matching against the userCertificate attribute is not possible by default in some directory servers, and violates some generally accepted schema definitions.
Syntax: AuthzLDAPUseSerial {on|off}
Context: virtual host, directory
Default: off
If set to on, mod_authz_ldap uses the certificate's serial number instead of the subject name to map the certificate to a user distinguished name in the directory.
Syntax: AuthzLDAPMapBase dn
Context: virtual host, directory
Default: none
Base for certificate mapping entries.
Syntax: AuthzLDAPMapScope {base|onlevel|subtree}
Context: virtual host, directory
Default: onlevel
Scope for certificate mapping entries.
Syntax: AuthzLDAPSetAuthorization {on|off}
Context: virtual host, directory
Default: on
Normally, the Authorization header or Proxy-Authorization Header is overwritten by mod_authz_ldap. This would make it impossible to build a secure reverse proxy authenticating with certificates and still authenticating to the remote server with basic authentication. Setting this option to off disables overwriting the authorization headers.
Syntax: AuthzLDAPRoleAttributeName attributename
Context: virtual host, directory
Default: none
Used to specify a attribute name to check for special values as given by require role directives.
Syntax: AuthzLDAPModifyKey attributename
Context: virtual host, directory
Default: none
Then checking a directory entry for last password modification, an attribute containing the time of last modification must be specified.
Syntax: AuthzLDAPAuthoritative {on|off}
Context: virtual host, directory
Default: on
Usually the authentication and authorization decisions of mod_authz_ldap are final. Sometimes however it is desired to have other modules do checks if mod_authz_ldap would deny a request. In these cases, this option must be set to off.
Syntax: AuthzLDAPProxyAuthentication {on|off}
Context: virtual host, directory
Default: automatically determined
In some cases the kind of authentication performed must be explicitly specified to the module. E.g. a reverse proxy will let the module believe that proxy authentication is the thing to do, but to the client the proxy appears as the server, so it should really do normal authentication. Setting this option to off forces normal authentication, on forces proxy authentication.
Syntax: AuthzLDAPLogLevel {emerg|alert|crit|error|warn|notice|info|debug}
Context: virtual host, directory
Default: debug
Reduce the volume of log messages from this module.
Syntax: AuthzLDAPCacheSize size
Context: virtual host, directory
Default: 0
Set the size of the cache the LDAP library is allowed to build. Setting the cache size to 0 disables caching. The module will not allow a cache to be created that is larger than the maximum set during configuration (128k being the default if option was given to conigure).
Syntax: AuthzLDAPCacheTimeout timeout
Context: virtual host, directory
Default: 600
Timeout in seconds for entries in the LDAP cache. As a timeout longer than one day seldom makes sense, the default timeout of 600 seconds is used whenever a timeout longer than a day or a negative timeout is specified. The default can be configured at configure time for the module.

9. License

This module is distributed under the terms of the Apache License, please check the LICENSE file in your apache distribution for the exact terms of the license.

10. Open Issues, TODOs

  1. We would like to have an LDAP cache to be able to do with less roundtrips to the directory server. The cache would be most useful for the frequent binds when doing normal authentication (without certificates). In this case the cache could be very simple: it would store the pair (userid, password) together with the information that and when it was successfully authenticated.
  2. Also the most recently used mappings of users, groups and certificates should be available in a cache, but the structure for such a case is much less clear.

© 2000 Dr. Andreas Müller, http://www.othello.ch
$Id: mod_authz_ldap.html.in,v 1.16 2001/04/21 22:12:31 afm Exp $