diff options
Diffstat (limited to 'modules/ldap')
-rw-r--r-- | modules/ldap/Makefile.in | 3 | ||||
-rw-r--r-- | modules/ldap/NWGNUmakefile | 264 | ||||
-rw-r--r-- | modules/ldap/README.ldap | 47 | ||||
-rw-r--r-- | modules/ldap/config.m4 | 25 | ||||
-rw-r--r-- | modules/ldap/mod_ldap.dep | 192 | ||||
-rw-r--r-- | modules/ldap/mod_ldap.dsp | 127 | ||||
-rw-r--r-- | modules/ldap/mod_ldap.mak | 371 | ||||
-rw-r--r-- | modules/ldap/util_ldap.c | 3221 | ||||
-rw-r--r-- | modules/ldap/util_ldap_cache.c | 467 | ||||
-rw-r--r-- | modules/ldap/util_ldap_cache.h | 206 | ||||
-rw-r--r-- | modules/ldap/util_ldap_cache_mgr.c | 901 |
11 files changed, 5824 insertions, 0 deletions
diff --git a/modules/ldap/Makefile.in b/modules/ldap/Makefile.in new file mode 100644 index 0000000..7c5c149 --- /dev/null +++ b/modules/ldap/Makefile.in @@ -0,0 +1,3 @@ +# a modules Makefile has no explicit targets -- they will be defined by +# whatever modules are enabled. just grab special.mk to deal with this. +include $(top_srcdir)/build/special.mk diff --git a/modules/ldap/NWGNUmakefile b/modules/ldap/NWGNUmakefile new file mode 100644 index 0000000..ef83025 --- /dev/null +++ b/modules/ldap/NWGNUmakefile @@ -0,0 +1,264 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(NWOS) \ + $(LDAPSDK)/inc \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +#LDAP client requires the use of Winsock +# +ifdef USE_STDSOCKETS +XDEFINES += -DUSE_WINSOCK \ + $(EOLIST) +endif + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = utilldap + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) LDAP Authentication Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = UtilLDAP Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/utilldap.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/util_ldap.o \ + $(OBJDIR)/util_ldap_cache.o \ + $(OBJDIR)/util_ldap_cache_mgr.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + lldapsdk \ + lldapssl \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @aprlib.imp \ + @httpd.imp \ + @libc.imp \ + @lldapsdk.imp \ + @lldapssl.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + ldap_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + $(call COPY,$(OBJDIR)/*.nlm, $(INSTALLBASE)/modules/) + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/ldap/README.ldap b/modules/ldap/README.ldap new file mode 100644 index 0000000..116707e --- /dev/null +++ b/modules/ldap/README.ldap @@ -0,0 +1,47 @@ +Quick installation instructions (UNIX): + +- Building on generic Unix: + + Add generic ldap support and the TWO ldap modules to the build, like this: + + ./configure --with-ldap --enable-ldap --enable-authnz-ldap + + The --with-ldap switches on LDAP library linking in apr-util. Make + sure that you have an LDAP client library available such as those + from Netscape/iPlanet/Sun One or the OpenLDAP project. + + The --enable-ldap option switches on the LDAP caching module. This + module is a support module for other LDAP modules, and is not useful + on its own. This module is required, but caching can be disabled + via the configuration directive LDAPCacheEntries. + + The --enable-auth-ldap option switches on the LDAP authentication + module. + +- Building on AIX: + + The following ./configure line is reported to work for AIX: + + CC=cc_r; export CC + CPPFLAGS=-qcpluscmt;export CPPFLAGS + ./configure --with-mpm=worker --prefix=/usr/local/apache \ + --enable-dav=static --enable-dav_fs=static --enable-ssl=static + --with-ldap=yes --with-ldap-include=/usr/local/include + --with-ldap-lib=/usr/local/lib --enable-ldap=static + --enable-authnz-ldap=static + + +Quick installation instructions (win32): + +1. copy the file srclib\apr-util\include\apr_ldap.hw to apr_ldap.h +2. the netscape/iplanet ldap libraries are installed in srclib\ldap +3. Compile the two modules util_ldap and mod_authnz_ldap using the dsp files +4. You get a mod_authnz_ldap.so and a mod_ldap.so module +5. Put them in the modules directory, don't forget to copy the + nsldap32v50.dll somewhere where httpd.exe will find it +6. Load the two modules in your httpd.conf, like below: + LoadModule ldap_module modules/mod_ldap.so + LoadModule authnz_ldap_module modules/mod_authnz_ldap.so +7. Configure the directories as described in the docus. + + diff --git a/modules/ldap/config.m4 b/modules/ldap/config.m4 new file mode 100644 index 0000000..3345bcd --- /dev/null +++ b/modules/ldap/config.m4 @@ -0,0 +1,25 @@ + +dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]]) + +APACHE_MODPATH_INIT(ldap) + +ldap_objects="util_ldap.lo util_ldap_cache.lo util_ldap_cache_mgr.lo" +APACHE_MODULE(ldap, LDAP caching and connection pooling services, $ldap_objects, , most , [ + APACHE_CHECK_APR_HAS_LDAP + if test "$ac_cv_APR_HAS_LDAP" = "yes" ; then + if test -z "$apu_config" ; then + LDAP_LIBS="`$apr_config --ldap-libs`" + else + LDAP_LIBS="`$apu_config --ldap-libs`" + fi + APR_ADDTO(MOD_LDAP_LDADD, [$LDAP_LIBS]) + AC_SUBST(MOD_LDAP_LDADD) + else + AC_MSG_WARN([apr/apr-util is compiled without ldap support]) + enable_ldap=no + fi +]) + +APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current]) + +APACHE_MODPATH_FINISH diff --git a/modules/ldap/mod_ldap.dep b/modules/ldap/mod_ldap.dep new file mode 100644 index 0000000..37dfd9e --- /dev/null +++ b/modules/ldap/mod_ldap.dep @@ -0,0 +1,192 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_ldap.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\util_ldap.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_ldap.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_anylock.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_ldap.h"\ + "..\..\srclib\apr-util\include\apr_ldap_init.h"\ + "..\..\srclib\apr-util\include\apr_ldap_option.h"\ + "..\..\srclib\apr-util\include\apr_ldap_rebind.h"\ + "..\..\srclib\apr-util\include\apr_ldap_url.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_rmm.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_thread_rwlock.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_version.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\util_ldap_cache.h"\ + + +.\util_ldap_cache.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_ldap.h"\ + "..\..\srclib\apr-util\include\apr_anylock.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_ldap.h"\ + "..\..\srclib\apr-util\include\apr_ldap_init.h"\ + "..\..\srclib\apr-util\include\apr_ldap_option.h"\ + "..\..\srclib\apr-util\include\apr_ldap_rebind.h"\ + "..\..\srclib\apr-util\include\apr_ldap_url.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_rmm.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_thread_rwlock.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_version.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\util_ldap_cache.h"\ + + +.\util_ldap_cache_mgr.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_ldap.h"\ + "..\..\srclib\apr-util\include\apr_anylock.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_ldap.h"\ + "..\..\srclib\apr-util\include\apr_ldap_init.h"\ + "..\..\srclib\apr-util\include\apr_ldap_option.h"\ + "..\..\srclib\apr-util\include\apr_ldap_rebind.h"\ + "..\..\srclib\apr-util\include\apr_ldap_url.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_rmm.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_thread_rwlock.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_version.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\util_ldap_cache.h"\ + diff --git a/modules/ldap/mod_ldap.dsp b/modules/ldap/mod_ldap.dsp new file mode 100644 index 0000000..db08757 --- /dev/null +++ b/modules/ldap/mod_ldap.dsp @@ -0,0 +1,127 @@ +# Microsoft Developer Studio Project File - Name="mod_ldap" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_ldap - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_ldap.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_ldap.mak" CFG="mod_ldap - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_ldap - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_ldap - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_ldap - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "LDAP_DECLARE_EXPORT" /Fd"Release\mod_ldap_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_ldap.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_ldap.so" /d LONG_NAME="ldap_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:".\Release\mod_ldap.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ldap.so +# ADD LINK32 kernel32.lib wldap32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_ldap.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ldap.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_ldap.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_ldap - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "LDAP_DECLARE_EXPORT" /Fd"Debug\mod_ldap_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_ldap.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_ldap.so" /d LONG_NAME="ldap_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_ldap.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ldap.so +# ADD LINK32 kernel32.lib wldap32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_ldap.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ldap.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_ldap.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_ldap - Win32 Release" +# Name "mod_ldap - Win32 Debug" +# Begin Source File + +SOURCE=.\util_ldap.c +# End Source File +# Begin Source File + +SOURCE=.\util_ldap.h +# End Source File +# Begin Source File + +SOURCE=.\util_ldap_cache.c +# End Source File +# Begin Source File + +SOURCE=.\util_ldap_cache.h +# End Source File +# Begin Source File + +SOURCE=.\util_ldap_cache_mgr.c +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/ldap/mod_ldap.mak b/modules/ldap/mod_ldap.mak new file mode 100644 index 0000000..23ab7fe --- /dev/null +++ b/modules/ldap/mod_ldap.mak @@ -0,0 +1,371 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_ldap.dsp +!IF "$(CFG)" == "" +CFG=mod_ldap - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_ldap - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_ldap - Win32 Release" && "$(CFG)" != "mod_ldap - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_ldap.mak" CFG="mod_ldap - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_ldap - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_ldap - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_ldap - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_ldap.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_ldap.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_ldap.res" + -@erase "$(INTDIR)\mod_ldap_src.idb" + -@erase "$(INTDIR)\mod_ldap_src.pdb" + -@erase "$(INTDIR)\util_ldap.obj" + -@erase "$(INTDIR)\util_ldap_cache.obj" + -@erase "$(INTDIR)\util_ldap_cache_mgr.obj" + -@erase "$(OUTDIR)\mod_ldap.exp" + -@erase "$(OUTDIR)\mod_ldap.lib" + -@erase "$(OUTDIR)\mod_ldap.pdb" + -@erase "$(OUTDIR)\mod_ldap.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "LDAP_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_ldap_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_ldap.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_ldap.so" /d LONG_NAME="ldap_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_ldap.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib wldap32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_ldap.pdb" /debug /out:"$(OUTDIR)\mod_ldap.so" /implib:"$(OUTDIR)\mod_ldap.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_ldap.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\util_ldap.obj" \ + "$(INTDIR)\util_ldap_cache.obj" \ + "$(INTDIR)\util_ldap_cache_mgr.obj" \ + "$(INTDIR)\mod_ldap.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_ldap.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_ldap.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_ldap.so" + if exist .\Release\mod_ldap.so.manifest mt.exe -manifest .\Release\mod_ldap.so.manifest -outputresource:.\Release\mod_ldap.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_ldap - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_ldap.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_ldap.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_ldap.res" + -@erase "$(INTDIR)\mod_ldap_src.idb" + -@erase "$(INTDIR)\mod_ldap_src.pdb" + -@erase "$(INTDIR)\util_ldap.obj" + -@erase "$(INTDIR)\util_ldap_cache.obj" + -@erase "$(INTDIR)\util_ldap_cache_mgr.obj" + -@erase "$(OUTDIR)\mod_ldap.exp" + -@erase "$(OUTDIR)\mod_ldap.lib" + -@erase "$(OUTDIR)\mod_ldap.pdb" + -@erase "$(OUTDIR)\mod_ldap.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "LDAP_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_ldap_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_ldap.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_ldap.so" /d LONG_NAME="ldap_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_ldap.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib wldap32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_ldap.pdb" /debug /out:"$(OUTDIR)\mod_ldap.so" /implib:"$(OUTDIR)\mod_ldap.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_ldap.so +LINK32_OBJS= \ + "$(INTDIR)\util_ldap.obj" \ + "$(INTDIR)\util_ldap_cache.obj" \ + "$(INTDIR)\util_ldap_cache_mgr.obj" \ + "$(INTDIR)\mod_ldap.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_ldap.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_ldap.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_ldap.so" + if exist .\Debug\mod_ldap.so.manifest mt.exe -manifest .\Debug\mod_ldap.so.manifest -outputresource:.\Debug\mod_ldap.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_ldap.dep") +!INCLUDE "mod_ldap.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_ldap.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_ldap - Win32 Release" || "$(CFG)" == "mod_ldap - Win32 Debug" + +!IF "$(CFG)" == "mod_ldap - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\ldap" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\ldap" + +!ELSEIF "$(CFG)" == "mod_ldap - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\ldap" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\ldap" + +!ENDIF + +!IF "$(CFG)" == "mod_ldap - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\ldap" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\ldap" + +!ELSEIF "$(CFG)" == "mod_ldap - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\ldap" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\ldap" + +!ENDIF + +!IF "$(CFG)" == "mod_ldap - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\ldap" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\ldap" + +!ELSEIF "$(CFG)" == "mod_ldap - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\ldap" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\ldap" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_ldap - Win32 Release" + + +"$(INTDIR)\mod_ldap.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_ldap.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_ldap.so" /d LONG_NAME="ldap_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_ldap - Win32 Debug" + + +"$(INTDIR)\mod_ldap.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_ldap.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_ldap.so" /d LONG_NAME="ldap_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\util_ldap.c + +"$(INTDIR)\util_ldap.obj" : $(SOURCE) "$(INTDIR)" + + +SOURCE=.\util_ldap_cache.c + +"$(INTDIR)\util_ldap_cache.obj" : $(SOURCE) "$(INTDIR)" + + +SOURCE=.\util_ldap_cache_mgr.c + +"$(INTDIR)\util_ldap_cache_mgr.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + diff --git a/modules/ldap/util_ldap.c b/modules/ldap/util_ldap.c new file mode 100644 index 0000000..08f986c --- /dev/null +++ b/modules/ldap/util_ldap.c @@ -0,0 +1,3221 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * util_ldap.c: LDAP things + * + * Original code from auth_ldap module for Apache v1.3: + * Copyright 1998, 1999 Enbridge Pipelines Inc. + * Copyright 1999-2001 Dave Carrigan + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#include "http_request.h" +#include "util_mutex.h" +#include "util_ldap.h" +#include "util_ldap_cache.h" + +#include <apr_strings.h> + +#if APR_HAVE_UNISTD_H +#include <unistd.h> +#endif + +#if !APR_HAS_LDAP +#error mod_ldap requires APR-util to have LDAP support built in +#endif + +/* Default define for ldap functions that need a SIZELIMIT but + * do not have the define + * XXX This should be removed once a supporting #define is + * released through APR-Util. + */ +#ifndef APR_LDAP_SIZELIMIT +#define APR_LDAP_SIZELIMIT -1 +#endif + +#ifdef LDAP_OPT_DEBUG_LEVEL +#define AP_LDAP_OPT_DEBUG LDAP_OPT_DEBUG_LEVEL +#else +#ifdef LDAP_OPT_DEBUG +#define AP_LDAP_OPT_DEBUG LDAP_OPT_DEBUG +#endif +#endif + +#define AP_LDAP_HOPLIMIT_UNSET -1 +#define AP_LDAP_CHASEREFERRALS_SDKDEFAULT -1 +#define AP_LDAP_CHASEREFERRALS_OFF 0 +#define AP_LDAP_CHASEREFERRALS_ON 1 + +#define AP_LDAP_CONNPOOL_DEFAULT -1 +#define AP_LDAP_CONNPOOL_INFINITE -2 + +#if !defined(LDAP_OPT_NETWORK_TIMEOUT) && defined(LDAP_OPT_CONNECT_TIMEOUT) +#define LDAP_OPT_NETWORK_TIMEOUT LDAP_OPT_CONNECT_TIMEOUT +#endif + +module AP_MODULE_DECLARE_DATA ldap_module; +static const char *ldap_cache_mutex_type = "ldap-cache"; +static apr_status_t uldap_connection_unbind(void *param); + +#define LDAP_CACHE_LOCK() do { \ + if (st->util_ldap_cache_lock) \ + apr_global_mutex_lock(st->util_ldap_cache_lock); \ +} while (0) + +#define LDAP_CACHE_UNLOCK() do { \ + if (st->util_ldap_cache_lock) \ + apr_global_mutex_unlock(st->util_ldap_cache_lock); \ +} while (0) + +static void util_ldap_strdup (char **str, const char *newstr) +{ + if (*str) { + free(*str); + *str = NULL; + } + + if (newstr) { + *str = strdup(newstr); + } +} + +/* + * Status Handler + * -------------- + * + * This handler generates a status page about the current performance of + * the LDAP cache. It is enabled as follows: + * + * <Location /ldap-status> + * SetHandler ldap-status + * </Location> + * + */ +static int util_ldap_handler(request_rec *r) +{ + util_ldap_state_t *st; + + r->allowed |= (1 << M_GET); + if (r->method_number != M_GET) { + return DECLINED; + } + + if (strcmp(r->handler, "ldap-status")) { + return DECLINED; + } + + st = (util_ldap_state_t *) ap_get_module_config(r->server->module_config, + &ldap_module); + + ap_set_content_type(r, "text/html; charset=ISO-8859-1"); + + if (r->header_only) + return OK; + + ap_rputs(DOCTYPE_HTML_3_2 + "<html><head><title>LDAP Cache Information</title></head>\n", r); + ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information" + "</h1>\n", r); + + util_ald_cache_display(r, st); + + return OK; +} + + + +/* ------------------------------------------------------------------ */ +/* + * Closes an LDAP connection by unlocking it. The next time + * uldap_connection_find() is called this connection will be + * available for reuse. + */ +static void uldap_connection_close(util_ldap_connection_t *ldc) +{ + + /* We leave bound LDAP connections floating around in our pool, + * but always check/fix the binddn/bindpw when we take them out + * of the pool + */ + if (!ldc->keep) { + uldap_connection_unbind(ldc); + ldc->r = NULL; + } + else { + /* mark our connection as available for reuse */ + ldc->freed = apr_time_now(); + ldc->r = NULL; + } + +#if APR_HAS_THREADS + apr_thread_mutex_unlock(ldc->lock); +#endif +} + + +/* + * Destroys an LDAP connection by unbinding and closing the connection to + * the LDAP server. It is used to bring the connection back to a known + * state after an error. + */ +static apr_status_t uldap_connection_unbind(void *param) +{ + util_ldap_connection_t *ldc = param; + + if (ldc) { + if (ldc->ldap) { + if (ldc->r) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, ldc->r, "LDC %pp unbind", ldc); + } + ldap_unbind_s(ldc->ldap); + ldc->ldap = NULL; + } + ldc->bound = 0; + + /* forget the rebind info for this conn */ + if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) { + apr_ldap_rebind_remove(ldc->ldap); + apr_pool_clear(ldc->rebind_pool); + } + } + + return APR_SUCCESS; +} + +/* not presently used, not part of the API */ +#if 0 +/* + * util_ldap_connection_remove frees all storage associated with the LDAP + * connection and removes it completely from the per-virtualhost list of + * connections + * + * The caller should hold the lock for this connection + */ +static apr_status_t util_ldap_connection_remove (void *param) +{ + util_ldap_connection_t *ldc = param, *l = NULL, *prev = NULL; + util_ldap_state_t *st; + + if (!ldc) return APR_SUCCESS; + + st = ldc->st; + + uldap_connection_unbind(ldc); + +#if APR_HAS_THREADS + apr_thread_mutex_lock(st->mutex); +#endif + + /* Remove ldc from the list */ + for (l=st->connections; l; l=l->next) { + if (l == ldc) { + if (prev) { + prev->next = l->next; + } + else { + st->connections = l->next; + } + break; + } + prev = l; + } + + if (ldc->bindpw) { + free((void*)ldc->bindpw); + } + if (ldc->binddn) { + free((void*)ldc->binddn); + } + +#if APR_HAS_THREADS + apr_thread_mutex_unlock(ldc->lock); + apr_thread_mutex_unlock(st->mutex); +#endif + + /* Destory the pool associated with this connection */ + + apr_pool_destroy(ldc->pool); + + return APR_SUCCESS; +} +#endif + +static int uldap_connection_init(request_rec *r, + util_ldap_connection_t *ldc) +{ + int rc = 0, ldap_option = 0; + int version = LDAP_VERSION3; + apr_ldap_err_t *result = NULL; +#ifdef LDAP_OPT_NETWORK_TIMEOUT + struct timeval connectionTimeout = {0}; +#endif + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(r->server->module_config, + &ldap_module); + int have_client_certs = !apr_is_empty_array(ldc->client_certs); +#if !APR_HAS_SOLARIS_LDAPSDK + /* + * Normally we enable SSL/TLS with apr_ldap_set_option(), except + * with Solaris LDAP, where this is broken. + */ + int secure = APR_LDAP_NONE; +#else + /* + * With Solaris LDAP, we enable TSL via the secure argument + * to apr_ldap_init(). This requires a fix from apr-util >= 1.4.0. + * + * Just in case client certificates ever get supported, we + * handle those as with the other LDAP SDKs. + */ + int secure = have_client_certs ? APR_LDAP_NONE : ldc->secure; +#endif + + /* Since the host will include a port if the default port is not used, + * always specify the default ports for the port parameter. This will + * allow a host string that contains multiple hosts the ability to mix + * some hosts with ports and some without. All hosts which do not + * specify a port will use the default port. + */ + apr_ldap_init(r->pool, &(ldc->ldap), + ldc->host, + APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT, + secure, &(result)); + + if (NULL == result) { + /* something really bad happened */ + ldc->bound = 0; + if (NULL == ldc->reason) { + ldc->reason = "LDAP: ldap initialization failed"; + } + return(APR_EGENERAL); + } + + if (result->rc) { + ldc->reason = result->reason; + ldc->bound = 0; + return result->rc; + } + + if (NULL == ldc->ldap) + { + ldc->bound = 0; + if (NULL == ldc->reason) { + ldc->reason = "LDAP: ldap initialization failed"; + } + else { + ldc->reason = result->reason; + } + return(result->rc); + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "LDC %pp init", ldc); + + if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) { + /* Now that we have an ldap struct, add it to the referral list for rebinds. */ + rc = apr_ldap_rebind_add(ldc->rebind_pool, ldc->ldap, ldc->binddn, ldc->bindpw); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server, APLOGNO(01277) + "LDAP: Unable to add rebind cross reference entry. Out of memory?"); + uldap_connection_unbind(ldc); + ldc->reason = "LDAP: Unable to add rebind cross reference entry."; + return(rc); + } + } + + /* always default to LDAP V3 */ + ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version); + + /* set client certificates */ + if (have_client_certs) { + apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT, + ldc->client_certs, &(result)); + if (LDAP_SUCCESS != result->rc) { + uldap_connection_unbind( ldc ); + ldc->reason = result->reason; + return(result->rc); + } + } + + /* switch on SSL/TLS */ + if (APR_LDAP_NONE != ldc->secure +#if APR_HAS_SOLARIS_LDAPSDK + /* See comments near apr_ldap_init() above */ + && have_client_certs +#endif + ) { + apr_ldap_set_option(r->pool, ldc->ldap, + APR_LDAP_OPT_TLS, &ldc->secure, &(result)); + if (LDAP_SUCCESS != result->rc) { + uldap_connection_unbind( ldc ); + ldc->reason = result->reason; + return(result->rc); + } + } + + /* Set the alias dereferencing option */ + ldap_option = ldc->deref; + ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &ldap_option); + + if (ldc->ChaseReferrals != AP_LDAP_CHASEREFERRALS_SDKDEFAULT) { + /* Set options for rebind and referrals. */ + ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, r->server, APLOGNO(01278) + "LDAP: Setting referrals to %s.", + ((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? "On" : "Off")); + apr_ldap_set_option(r->pool, ldc->ldap, + APR_LDAP_OPT_REFERRALS, + (void *)((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? + LDAP_OPT_ON : LDAP_OPT_OFF), + &(result)); + if (result->rc != LDAP_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01279) + "Unable to set LDAP_OPT_REFERRALS option to %s: %d.", + ((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? "On" : "Off"), + result->rc); + result->reason = "Unable to set LDAP_OPT_REFERRALS."; + ldc->reason = result->reason; + uldap_connection_unbind(ldc); + return(result->rc); + } + } + + if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) { + if ((ldc->ReferralHopLimit != AP_LDAP_HOPLIMIT_UNSET) && ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) { + /* Referral hop limit - only if referrals are enabled and a hop limit is explicitly requested */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01280) + "Setting referral hop limit to %d.", + ldc->ReferralHopLimit); + apr_ldap_set_option(r->pool, ldc->ldap, + APR_LDAP_OPT_REFHOPLIMIT, + (void *)&ldc->ReferralHopLimit, + &(result)); + if (result->rc != LDAP_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01281) + "Unable to set LDAP_OPT_REFHOPLIMIT option to %d: %d.", + ldc->ReferralHopLimit, + result->rc); + result->reason = "Unable to set LDAP_OPT_REFHOPLIMIT."; + ldc->reason = result->reason; + uldap_connection_unbind(ldc); + return(result->rc); + } + } + } + +/*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */ +#ifdef APR_LDAP_OPT_VERIFY_CERT + apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_VERIFY_CERT, + &(st->verify_svr_cert), &(result)); +#else +#if defined(LDAPSSL_VERIFY_SERVER) + if (st->verify_svr_cert) { + result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER); + } + else { + result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE); + } +#elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT) + /* This is not a per-connection setting so just pass NULL for the + Ldap connection handle */ + if (st->verify_svr_cert) { + int i = LDAP_OPT_X_TLS_DEMAND; + result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i); + } + else { + int i = LDAP_OPT_X_TLS_NEVER; + result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i); + } +#endif +#endif + +#ifdef LDAP_OPT_NETWORK_TIMEOUT + if (st->connectionTimeout > 0) { + connectionTimeout.tv_sec = st->connectionTimeout; + } + + if (connectionTimeout.tv_sec > 0) { + rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT, + (void *)&connectionTimeout, &(result)); + if (APR_SUCCESS != rc) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(01282) + "LDAP: Could not set the connection timeout"); + } + } +#endif + +#ifdef LDAP_OPT_TIMEOUT + /* + * LDAP_OPT_TIMEOUT is not portable, but it influences all synchronous ldap + * function calls and not just ldap_search_ext_s(), which accepts a timeout + * parameter. + * XXX: It would be possible to simulate LDAP_OPT_TIMEOUT by replacing all + * XXX: synchronous ldap function calls with asynchronous calls and using + * XXX: ldap_result() with a timeout. + */ + if (st->opTimeout) { + rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_TIMEOUT, + st->opTimeout, &(result)); + if (APR_SUCCESS != rc) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(01283) + "LDAP: Could not set LDAP_OPT_TIMEOUT"); + } + } +#endif + + return(rc); +} + +static int uldap_ld_errno(util_ldap_connection_t *ldc) +{ + int ldaprc; +#ifdef LDAP_OPT_ERROR_NUMBER + if (LDAP_SUCCESS == ldap_get_option(ldc->ldap, LDAP_OPT_ERROR_NUMBER, &ldaprc)) return ldaprc; +#endif +#ifdef LDAP_OPT_RESULT_CODE + if (LDAP_SUCCESS == ldap_get_option(ldc->ldap, LDAP_OPT_RESULT_CODE, &ldaprc)) return ldaprc; +#endif + return LDAP_OTHER; +} + +/* + * Replacement function for ldap_simple_bind_s() with a timeout. + * To do this in a portable way, we have to use ldap_simple_bind() and + * ldap_result(). + * + * Returns LDAP_SUCCESS on success; and an error code on failure + */ +static int uldap_simple_bind(util_ldap_connection_t *ldc, char *binddn, + char* bindpw, struct timeval *timeout) +{ + LDAPMessage *result; + int rc; + int msgid = ldap_simple_bind(ldc->ldap, binddn, bindpw); + if (msgid == -1) { + ldc->reason = "LDAP: ldap_simple_bind() failed"; + return uldap_ld_errno(ldc); + } + rc = ldap_result(ldc->ldap, msgid, 0, timeout, &result); + if (rc == -1) { + ldc->reason = "LDAP: ldap_simple_bind() result retrieval failed"; + /* -1 is LDAP_SERVER_DOWN in openldap, use something else */ + return uldap_ld_errno(ldc); + } + else if (rc == 0) { + ldc->reason = "LDAP: ldap_simple_bind() timed out"; + rc = LDAP_TIMEOUT; + } else if (ldap_parse_result(ldc->ldap, result, &rc, NULL, NULL, NULL, + NULL, 1) == -1) { + ldc->reason = "LDAP: ldap_simple_bind() parse result failed"; + return uldap_ld_errno(ldc); + } + else { + ldc->last_backend_conn = ldc->r->request_time; + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, ldc->r, "LDC %pp bind", ldc); + } + return rc; +} + +/* + * Connect to the LDAP server and binds. Does not connect if already + * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound. + * + * Returns LDAP_SUCCESS on success; and an error code on failure + */ +static int uldap_connection_open(request_rec *r, + util_ldap_connection_t *ldc) +{ + int rc = 0; + int failures = 0; + int new_connection = 0; + util_ldap_state_t *st; + + /* sanity check for NULL */ + if (!ldc) { + return -1; + } + + /* If the connection is already bound, return + */ + if (ldc->bound && !ldc->must_rebind) + { + ldc->reason = "LDAP: connection open successful (already bound)"; + return LDAP_SUCCESS; + } + + /* create the ldap session handle + */ + if (NULL == ldc->ldap) + { + new_connection = 1; + rc = uldap_connection_init( r, ldc ); + if (LDAP_SUCCESS != rc) + { + return rc; + } + } + + + st = (util_ldap_state_t *)ap_get_module_config(r->server->module_config, + &ldap_module); + + /* loop trying to bind up to st->retries times if LDAP_SERVER_DOWN or LDAP_TIMEOUT + * are returned. Close the connection before the first retry, and then on every + * other retry. + * + * On Success or any other error, break out of the loop. + * + * NOTE: Looping is probably not a great idea. If the server isn't + * responding the chances it will respond after a few tries are poor. + * However, the original code looped and it only happens on + * the error condition. + */ + + while (failures <= st->retries) { + if (failures > 0 && st->retry_delay > 0) { + apr_sleep(st->retry_delay); + } + rc = uldap_simple_bind(ldc, (char *)ldc->binddn, (char *)ldc->bindpw, + st->opTimeout); + + if (rc == LDAP_SUCCESS) break; + + failures++; + + if (AP_LDAP_IS_SERVER_DOWN(rc)) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "ldap_simple_bind() failed with server down " + "(try %d)", failures); + } + else if (rc == LDAP_TIMEOUT) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01284) + "ldap_simple_bind() timed out on %s " + "connection, dropped by firewall?", + new_connection ? "new" : "reused"); + } + else { + /* Other errors not retryable */ + break; + } + + if (!(failures % 2)) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "attempt to re-init the connection"); + uldap_connection_unbind(ldc); + if (LDAP_SUCCESS != uldap_connection_init(r, ldc)) { + /* leave rc as the initial bind return code */ + break; + } + } + } + + /* free the handle if there was an error + */ + if (LDAP_SUCCESS != rc) + { + uldap_connection_unbind(ldc); + ldc->reason = "LDAP: ldap_simple_bind() failed"; + } + else { + ldc->bound = 1; + ldc->must_rebind = 0; + ldc->reason = "LDAP: connection open successful"; + } + + return(rc); +} + + +/* + * Compare client certificate arrays. + * + * Returns 1 on compare failure, 0 otherwise. + */ +static int compare_client_certs(apr_array_header_t *srcs, + apr_array_header_t *dests) +{ + int i = 0; + struct apr_ldap_opt_tls_cert_t *src, *dest; + + /* arrays both NULL? if so, then equal */ + if (srcs == NULL && dests == NULL) { + return 0; + } + + /* arrays different length or either NULL? If so, then not equal */ + if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) { + return 1; + } + + /* run an actual comparison */ + src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts; + dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts; + for (i = 0; i < srcs->nelts; i++) { + if ((strcmp(src[i].path, dest[i].path)) || + (src[i].type != dest[i].type) || + /* One is passwordless? If so, then not equal */ + ((src[i].password == NULL) ^ (dest[i].password == NULL)) || + (src[i].password != NULL && dest[i].password != NULL && + strcmp(src[i].password, dest[i].password))) { + return 1; + } + } + + /* if we got here, the cert arrays were identical */ + return 0; + +} + + +/* + * Find an existing ldap connection struct that matches the + * provided ldap connection parameters. + * + * If not found in the cache, a new ldc structure will be allocated + * from st->pool and returned to the caller. If found in the cache, + * a pointer to the existing ldc structure will be returned. + */ +static util_ldap_connection_t * + uldap_connection_find(request_rec *r, + const char *host, int port, + const char *binddn, const char *bindpw, + deref_options deref, int secure) +{ + struct util_ldap_connection_t *l, *p; /* To traverse the linked list */ + int secureflag = secure; + apr_time_t now = apr_time_now(); + + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(r->server->module_config, + &ldap_module); + util_ldap_config_t *dc = + (util_ldap_config_t *) ap_get_module_config(r->per_dir_config, &ldap_module); + +#if APR_HAS_THREADS + /* mutex lock this function */ + apr_thread_mutex_lock(st->mutex); +#endif + + if (secure < APR_LDAP_NONE) { + secureflag = st->secure; + } + + /* Search for an exact connection match in the list that is not + * being used. + */ + for (l=st->connections,p=NULL; l; l=l->next) { +#if APR_HAS_THREADS + if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) { +#endif + if ( (l->port == port) && (strcmp(l->host, host) == 0) + && ((!l->binddn && !binddn) || (l->binddn && binddn + && !strcmp(l->binddn, binddn))) + && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw + && !strcmp(l->bindpw, bindpw))) + && (l->deref == deref) && (l->secure == secureflag) + && !compare_client_certs(dc->client_certs, l->client_certs)) + { + if (st->connection_pool_ttl > 0) { + if (l->bound && (now - l->last_backend_conn) > st->connection_pool_ttl) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "Removing LDAP connection last used %" APR_TIME_T_FMT " seconds ago", + (now - l->last_backend_conn) / APR_USEC_PER_SEC); + l->r = r; + uldap_connection_unbind(l); + /* Go ahead (by falling through) and use it, so we don't create more just to unbind some other old ones */ + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, + "Reuse %s LDC %pp", + l->bound ? "bound" : "unbound", l); + } + break; + } +#if APR_HAS_THREADS + /* If this connection didn't match the criteria, then we + * need to unlock the mutex so it is available to be reused. + */ + apr_thread_mutex_unlock(l->lock); + } +#endif + p = l; + } + + /* If nothing found, search again, but we don't care about the + * binddn and bindpw this time. + */ + if (!l) { + for (l=st->connections,p=NULL; l; l=l->next) { +#if APR_HAS_THREADS + if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) { + +#endif + if ((l->port == port) && (strcmp(l->host, host) == 0) && + (l->deref == deref) && (l->secure == secureflag) && + !compare_client_certs(dc->client_certs, l->client_certs)) + { + if (st->connection_pool_ttl > 0) { + if (l->bound && (now - l->last_backend_conn) > st->connection_pool_ttl) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "Removing LDAP connection last used %" APR_TIME_T_FMT " seconds ago", + (now - l->last_backend_conn) / APR_USEC_PER_SEC); + l->r = r; + uldap_connection_unbind(l); + /* Go ahead (by falling through) and use it, so we don't create more just to unbind some other old ones */ + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, + "Reuse %s LDC %pp (will rebind)", + l->bound ? "bound" : "unbound", l); + } + + /* the bind credentials have changed */ + l->must_rebind = 1; + util_ldap_strdup((char**)&(l->binddn), binddn); + util_ldap_strdup((char**)&(l->bindpw), bindpw); + + break; + } +#if APR_HAS_THREADS + /* If this connection didn't match the criteria, then we + * need to unlock the mutex so it is available to be reused. + */ + apr_thread_mutex_unlock(l->lock); + } +#endif + p = l; + } + } + +/* artificially disable cache */ +/* l = NULL; */ + + /* If no connection was found after the second search, we + * must create one. + */ + if (!l) { + apr_pool_t *newpool; + if (apr_pool_create(&newpool, NULL) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01285) + "util_ldap: Failed to create memory pool"); +#if APR_HAS_THREADS + apr_thread_mutex_unlock(st->mutex); +#endif + return NULL; + } + + /* + * Add the new connection entry to the linked list. Note that we + * don't actually establish an LDAP connection yet; that happens + * the first time authentication is requested. + */ + + /* create the details of this connection in the new pool */ + l = apr_pcalloc(newpool, sizeof(util_ldap_connection_t)); + l->pool = newpool; + l->st = st; + +#if APR_HAS_THREADS + apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, l->pool); + apr_thread_mutex_lock(l->lock); +#endif + l->bound = 0; + l->host = apr_pstrdup(l->pool, host); + l->port = port; + l->deref = deref; + util_ldap_strdup((char**)&(l->binddn), binddn); + util_ldap_strdup((char**)&(l->bindpw), bindpw); + l->ChaseReferrals = dc->ChaseReferrals; + l->ReferralHopLimit = dc->ReferralHopLimit; + + /* The security mode after parsing the URL will always be either + * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://). + * If the security setting is NONE, override it to the security + * setting optionally supplied by the admin using LDAPTrustedMode + */ + l->secure = secureflag; + + /* save away a copy of the client cert list that is presently valid */ + l->client_certs = apr_array_copy_hdr(l->pool, dc->client_certs); + + /* whether or not to keep this connection in the pool when it's returned */ + l->keep = (st->connection_pool_ttl == 0) ? 0 : 1; + + if (l->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) { + if (apr_pool_create(&(l->rebind_pool), l->pool) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01286) + "util_ldap: Failed to create memory pool"); +#if APR_HAS_THREADS + apr_thread_mutex_unlock(st->mutex); +#endif + return NULL; + } + } + + if (p) { + p->next = l; + } + else { + st->connections = l; + } + } + +#if APR_HAS_THREADS + apr_thread_mutex_unlock(st->mutex); +#endif + l->r = r; + return l; +} + +/* ------------------------------------------------------------------ */ + +/* + * Compares two DNs to see if they're equal. The only way to do this correctly + * is to search for the dn and then do ldap_get_dn() on the result. This should + * match the initial dn, since it would have been also retrieved with + * ldap_get_dn(). This is expensive, so if the configuration value + * compare_dn_on_server is false, just does an ordinary strcmp. + * + * The lock for the ldap cache should already be acquired. + */ +static int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc, + const char *url, const char *dn, + const char *reqdn, int compare_dn_on_server) +{ + int result = 0; + util_url_node_t *curl; + util_url_node_t curnode; + util_dn_compare_node_t *node; + util_dn_compare_node_t newnode; + int failures = 0; + LDAPMessage *res, *entry; + char *searchdn; + + util_ldap_state_t *st = (util_ldap_state_t *) + ap_get_module_config(r->server->module_config, + &ldap_module); + + /* get cache entry (or create one) */ + LDAP_CACHE_LOCK(); + + curnode.url = url; + curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode); + if (curl == NULL) { + curl = util_ald_create_caches(st, url); + } + LDAP_CACHE_UNLOCK(); + + /* a simple compare? */ + if (!compare_dn_on_server) { + /* unlock this read lock */ + if (strcmp(dn, reqdn)) { + ldc->reason = "DN Comparison FALSE (direct strcmp())"; + return LDAP_COMPARE_FALSE; + } + else { + ldc->reason = "DN Comparison TRUE (direct strcmp())"; + return LDAP_COMPARE_TRUE; + } + } + + if (curl) { + /* no - it's a server side compare */ + LDAP_CACHE_LOCK(); + + /* is it in the compare cache? */ + newnode.reqdn = (char *)reqdn; + node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode); + if (node != NULL) { + /* If it's in the cache, it's good */ + /* unlock this read lock */ + LDAP_CACHE_UNLOCK(); + ldc->reason = "DN Comparison TRUE (cached)"; + return LDAP_COMPARE_TRUE; + } + + /* unlock this read lock */ + LDAP_CACHE_UNLOCK(); + } + +start_over: + if (failures > st->retries) { + return result; + } + + if (failures > 0 && st->retry_delay > 0) { + apr_sleep(st->retry_delay); + } + + /* make a server connection */ + if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) { + /* connect to server failed */ + return result; + } + + /* search for reqdn */ + result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE, + "(objectclass=*)", NULL, 1, + NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res); + if (AP_LDAP_IS_SERVER_DOWN(result)) + { + ldc->reason = "DN Comparison ldap_search_ext_s() " + "failed with server down"; + uldap_connection_unbind(ldc); + failures++; + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures); + goto start_over; + } + if (result == LDAP_TIMEOUT && failures == 0) { + /* + * we are reusing a connection that doesn't seem to be active anymore + * (firewall state drop?), let's try a new connection. + */ + ldc->reason = "DN Comparison ldap_search_ext_s() " + "failed with timeout"; + uldap_connection_unbind(ldc); + failures++; + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures); + goto start_over; + } + if (result != LDAP_SUCCESS) { + /* search for reqdn failed - no match */ + ldc->reason = "DN Comparison ldap_search_ext_s() failed"; + return result; + } + + ldc->last_backend_conn = r->request_time; + entry = ldap_first_entry(ldc->ldap, res); + searchdn = ldap_get_dn(ldc->ldap, entry); + + ldap_msgfree(res); + if (strcmp(dn, searchdn) != 0) { + /* compare unsuccessful */ + ldc->reason = "DN Comparison FALSE (checked on server)"; + result = LDAP_COMPARE_FALSE; + } + else { + if (curl) { + /* compare successful - add to the compare cache */ + LDAP_CACHE_LOCK(); + newnode.reqdn = (char *)reqdn; + newnode.dn = (char *)dn; + + node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode); + if ( (node == NULL) + || (strcmp(reqdn, node->reqdn) != 0) + || (strcmp(dn, node->dn) != 0)) + { + util_ald_cache_insert(curl->dn_compare_cache, &newnode); + } + LDAP_CACHE_UNLOCK(); + } + ldc->reason = "DN Comparison TRUE (checked on server)"; + result = LDAP_COMPARE_TRUE; + } + ldap_memfree(searchdn); + return result; + +} + +/* + * Does an generic ldap_compare operation. It accepts a cache that it will use + * to lookup the compare in the cache. We cache two kinds of compares + * (require group compares) and (require user compares). Each compare has a + * different cache node: require group includes the DN; require user does not + * because the require user cache is owned by the + * + */ +static int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc, + const char *url, const char *dn, + const char *attrib, const char *value) +{ + int result = 0; + util_url_node_t *curl; + util_url_node_t curnode; + util_compare_node_t *compare_nodep; + util_compare_node_t the_compare_node; + apr_time_t curtime = 0; /* silence gcc -Wall */ + int failures = 0; + + util_ldap_state_t *st = (util_ldap_state_t *) + ap_get_module_config(r->server->module_config, + &ldap_module); + + /* get cache entry (or create one) */ + LDAP_CACHE_LOCK(); + curnode.url = url; + curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode); + if (curl == NULL) { + curl = util_ald_create_caches(st, url); + } + LDAP_CACHE_UNLOCK(); + + if (curl) { + /* make a comparison to the cache */ + LDAP_CACHE_LOCK(); + curtime = apr_time_now(); + + the_compare_node.dn = (char *)dn; + the_compare_node.attrib = (char *)attrib; + the_compare_node.value = (char *)value; + the_compare_node.result = 0; + the_compare_node.sgl_processed = 0; + the_compare_node.subgroupList = NULL; + + compare_nodep = util_ald_cache_fetch(curl->compare_cache, + &the_compare_node); + + if (compare_nodep != NULL) { + /* found it... */ + if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) { + /* ...but it is too old */ + util_ald_cache_remove(curl->compare_cache, compare_nodep); + } + else { + /* ...and it is good */ + if (LDAP_COMPARE_TRUE == compare_nodep->result) { + ldc->reason = "Comparison true (cached)"; + } + else if (LDAP_COMPARE_FALSE == compare_nodep->result) { + ldc->reason = "Comparison false (cached)"; + } + else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) { + ldc->reason = "Comparison no such attribute (cached)"; + } + else { + ldc->reason = apr_psprintf(r->pool, + "Comparison undefined: (%d): %s (adding to cache)", + result, ldap_err2string(result)); + } + + /* record the result code to return with the reason... */ + result = compare_nodep->result; + /* and unlock this read lock */ + LDAP_CACHE_UNLOCK(); + + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, + "ldap_compare_s(%pp, %s, %s, %s) = %s (cached)", + ldc->ldap, dn, attrib, value, ldap_err2string(result)); + return result; + } + } + /* unlock this read lock */ + LDAP_CACHE_UNLOCK(); + } + +start_over: + if (failures > st->retries) { + return result; + } + + if (failures > 0 && st->retry_delay > 0) { + apr_sleep(st->retry_delay); + } + + if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) { + /* connect failed */ + return result; + } + + result = ldap_compare_s(ldc->ldap, + (char *)dn, + (char *)attrib, + (char *)value); + if (AP_LDAP_IS_SERVER_DOWN(result)) { + /* connection failed - try again */ + ldc->reason = "ldap_compare_s() failed with server down"; + uldap_connection_unbind(ldc); + failures++; + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures); + goto start_over; + } + if (result == LDAP_TIMEOUT && failures == 0) { + /* + * we are reusing a connection that doesn't seem to be active anymore + * (firewall state drop?), let's try a new connection. + */ + ldc->reason = "ldap_compare_s() failed with timeout"; + uldap_connection_unbind(ldc); + failures++; + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures); + goto start_over; + } + + ldc->last_backend_conn = r->request_time; + ldc->reason = "Comparison complete"; + if ((LDAP_COMPARE_TRUE == result) || + (LDAP_COMPARE_FALSE == result) || + (LDAP_NO_SUCH_ATTRIBUTE == result)) { + if (curl) { + /* compare completed; caching result */ + LDAP_CACHE_LOCK(); + the_compare_node.lastcompare = curtime; + the_compare_node.result = result; + the_compare_node.sgl_processed = 0; + the_compare_node.subgroupList = NULL; + + /* If the node doesn't exist then insert it, otherwise just update + * it with the last results + */ + compare_nodep = util_ald_cache_fetch(curl->compare_cache, + &the_compare_node); + if ( (compare_nodep == NULL) + || (strcmp(the_compare_node.dn, compare_nodep->dn) != 0) + || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0) + || (strcmp(the_compare_node.value, compare_nodep->value) != 0)) + { + void *junk; + + junk = util_ald_cache_insert(curl->compare_cache, + &the_compare_node); + if (junk == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01287) + "cache_compare: Cache insertion failure."); + } + } + else { + compare_nodep->lastcompare = curtime; + compare_nodep->result = result; + } + LDAP_CACHE_UNLOCK(); + } + + if (LDAP_COMPARE_TRUE == result) { + ldc->reason = "Comparison true (adding to cache)"; + } + else if (LDAP_COMPARE_FALSE == result) { + ldc->reason = "Comparison false (adding to cache)"; + } + else if (LDAP_NO_SUCH_ATTRIBUTE == result) { + ldc->reason = "Comparison no such attribute (adding to cache)"; + } + else { + ldc->reason = apr_psprintf(r->pool, + "Comparison undefined: (%d): %s (adding to cache)", + result, ldap_err2string(result)); + } + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, + "ldap_compare_s(%pp, %s, %s, %s) = %s", + ldc->ldap, dn, attrib, value, ldap_err2string(result)); + return result; +} + + +static util_compare_subgroup_t* uldap_get_subgroups(request_rec *r, + util_ldap_connection_t *ldc, + const char *url, + const char *dn, + char **subgroupAttrs, + apr_array_header_t *subgroupclasses) +{ + int failures = 0; + int result = LDAP_COMPARE_FALSE; + util_compare_subgroup_t *res = NULL; + LDAPMessage *sga_res, *entry; + struct mod_auth_ldap_groupattr_entry_t *sgc_ents; + apr_array_header_t *subgroups = apr_array_make(r->pool, 20, sizeof(char *)); + util_ldap_state_t *st = (util_ldap_state_t *) + ap_get_module_config(r->server->module_config, + &ldap_module); + + sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts; + + if (!subgroupAttrs) { + return res; + } + +start_over: + /* + * 3.B. The cache didn't have any subgrouplist yet. Go check for subgroups. + */ + if (failures > st->retries) { + return res; + } + + if (failures > 0 && st->retry_delay > 0) { + apr_sleep(st->retry_delay); + } + + + if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) { + /* connect failed */ + return res; + } + + /* try to do the search */ + result = ldap_search_ext_s(ldc->ldap, (char *)dn, LDAP_SCOPE_BASE, + NULL, subgroupAttrs, 0, + NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &sga_res); + if (AP_LDAP_IS_SERVER_DOWN(result)) { + ldc->reason = "ldap_search_ext_s() for subgroups failed with server" + " down"; + uldap_connection_unbind(ldc); + failures++; + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures); + goto start_over; + } + if (result == LDAP_TIMEOUT && failures == 0) { + /* + * we are reusing a connection that doesn't seem to be active anymore + * (firewall state drop?), let's try a new connection. + */ + ldc->reason = "ldap_search_ext_s() for subgroups failed with timeout"; + uldap_connection_unbind(ldc); + failures++; + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures); + goto start_over; + } + + /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */ + if (result != LDAP_SUCCESS) { + ldc->reason = "ldap_search_ext_s() for subgroups failed"; + return res; + } + + ldc->last_backend_conn = r->request_time; + entry = ldap_first_entry(ldc->ldap, sga_res); + + /* + * Get values for the provided sub-group attributes. + */ + if (subgroupAttrs) { + int indx = 0, tmp_sgcIndex; + + while (subgroupAttrs[indx]) { + char **values; + int val_index = 0; + + /* Get *all* matching "member" values from this group. */ + values = ldap_get_values(ldc->ldap, entry, subgroupAttrs[indx]); + + if (values) { + val_index = 0; + /* + * Now we are going to pare the subgroup members of this group + * to *just* the subgroups, add them to the compare_nodep, and + * then proceed to check the new level of subgroups. + */ + while (values[val_index]) { + /* Check if this entry really is a group. */ + tmp_sgcIndex = 0; + result = LDAP_COMPARE_FALSE; + while ((tmp_sgcIndex < subgroupclasses->nelts) + && (result != LDAP_COMPARE_TRUE)) { + result = uldap_cache_compare(r, ldc, url, + values[val_index], + "objectClass", + sgc_ents[tmp_sgcIndex].name + ); + + if (result != LDAP_COMPARE_TRUE) { + tmp_sgcIndex++; + } + } + /* It's a group, so add it to the array. */ + if (result == LDAP_COMPARE_TRUE) { + char **newgrp = (char **) apr_array_push(subgroups); + *newgrp = apr_pstrdup(r->pool, values[val_index]); + } + val_index++; + } + ldap_value_free(values); + } + indx++; + } + } + + ldap_msgfree(sga_res); + + if (subgroups->nelts > 0) { + /* We need to fill in tmp_local_subgroups using the data from LDAP */ + int sgindex; + char **group; + res = apr_pcalloc(r->pool, sizeof(util_compare_subgroup_t)); + res->subgroupDNs = apr_palloc(r->pool, + sizeof(char *) * (subgroups->nelts)); + for (sgindex = 0; (group = apr_array_pop(subgroups)); sgindex++) { + res->subgroupDNs[sgindex] = apr_pstrdup(r->pool, *group); + } + res->len = sgindex; + } + + return res; +} + + +/* + * Does a recursive lookup operation to try to find a user within (cached) + * nested groups. It accepts a cache that it will use to lookup previous + * compare attempts. We cache two kinds of compares (require group compares) + * and (require user compares). Each compare has a different cache node: + * require group includes the DN; require user does not because the require + * user cache is owned by the + * + * DON'T CALL THIS UNLESS YOU CALLED uldap_cache_compare FIRST!!!!! + * + * + * 1. Call uldap_cache_compare for each subgroupclass value to check the + * generic, user-agnostic, cached group entry. This will create a new generic + * cache entry if there + * wasn't one. If nothing returns LDAP_COMPARE_TRUE skip to step 5 since we + * have no groups. + * 2. Lock The cache and get the generic cache entry. + * 3. Check if there is already a subgrouplist in this generic group's cache + * entry. + * A. If there is, go to step 4. + * B. If there isn't: + * i) Use ldap_search to get the full list + * of subgroup "members" (which may include non-group "members"). + * ii) Use uldap_cache_compare to strip the list down to just groups. + * iii) Lock and add this stripped down list to the cache of the generic + * group. + * 4. Loop through the sgl and call uldap_cache_compare (using the user info) + * for each + * subgroup to see if the subgroup contains the user and to get the subgroups + * added to the + * cache (with user-afinity, if they aren't already there). + * A. If the user is in the subgroup, then we'll be returning + * LDAP_COMPARE_TRUE. + * B. if the user isn't in the subgroup (LDAP_COMPARE_FALSE via + * uldap_cache_compare) then recursively call this function to get the + * sub-subgroups added... + * 5. Cleanup local allocations. + * 6. Return the final result. + */ + +static int uldap_cache_check_subgroups(request_rec *r, + util_ldap_connection_t *ldc, + const char *url, const char *dn, + const char *attrib, const char *value, + char **subgroupAttrs, + apr_array_header_t *subgroupclasses, + int cur_subgroup_depth, + int max_subgroup_depth) +{ + int result = LDAP_COMPARE_FALSE; + util_url_node_t *curl; + util_url_node_t curnode; + util_compare_node_t *compare_nodep; + util_compare_node_t the_compare_node; + util_compare_subgroup_t *tmp_local_sgl = NULL; + int sgl_cached_empty = 0, sgindex = 0, base_sgcIndex = 0; + struct mod_auth_ldap_groupattr_entry_t *sgc_ents = + (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts; + util_ldap_state_t *st = (util_ldap_state_t *) + ap_get_module_config(r->server->module_config, + &ldap_module); + + /* + * Stop looking at deeper levels of nested groups if we have reached the + * max. Since we already checked the top-level group in uldap_cache_compare, + * we don't need to check it again here - so if max_subgroup_depth is set + * to 0, we won't check it (i.e. that is why we check < rather than <=). + * We'll be calling uldap_cache_compare from here to check if the user is + * in the next level before we recurse into that next level looking for + * more subgroups. + */ + if (cur_subgroup_depth >= max_subgroup_depth) { + return LDAP_COMPARE_FALSE; + } + + /* + * 1. Check the "groupiness" of the specified basedn. Stopping at the first + * TRUE return. + */ + while ((base_sgcIndex < subgroupclasses->nelts) + && (result != LDAP_COMPARE_TRUE)) { + result = uldap_cache_compare(r, ldc, url, dn, "objectClass", + sgc_ents[base_sgcIndex].name); + if (result != LDAP_COMPARE_TRUE) { + base_sgcIndex++; + } + } + + if (result != LDAP_COMPARE_TRUE) { + ldc->reason = "DN failed group verification."; + return result; + } + + /* + * 2. Find previously created cache entry and check if there is already a + * subgrouplist. + */ + LDAP_CACHE_LOCK(); + curnode.url = url; + curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode); + LDAP_CACHE_UNLOCK(); + + if (curl && curl->compare_cache) { + /* make a comparison to the cache */ + LDAP_CACHE_LOCK(); + + the_compare_node.dn = (char *)dn; + the_compare_node.attrib = (char *)"objectClass"; + the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name; + the_compare_node.result = 0; + the_compare_node.sgl_processed = 0; + the_compare_node.subgroupList = NULL; + + compare_nodep = util_ald_cache_fetch(curl->compare_cache, + &the_compare_node); + + if (compare_nodep != NULL) { + /* + * Found the generic group entry... but the user isn't in this + * group or we wouldn't be here. + */ + if (compare_nodep->sgl_processed) { + if (compare_nodep->subgroupList) { + /* Make a local copy of the subgroup list */ + int i; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01288) + "Making local copy of SGL for " + "group (%s)(objectClass=%s) ", + dn, (char *)sgc_ents[base_sgcIndex].name); + tmp_local_sgl = apr_pcalloc(r->pool, + sizeof(util_compare_subgroup_t)); + tmp_local_sgl->len = compare_nodep->subgroupList->len; + tmp_local_sgl->subgroupDNs = + apr_palloc(r->pool, + sizeof(char *) * compare_nodep->subgroupList->len); + for (i = 0; i < compare_nodep->subgroupList->len; i++) { + tmp_local_sgl->subgroupDNs[i] = + apr_pstrdup(r->pool, + compare_nodep->subgroupList->subgroupDNs[i]); + } + } + else { + sgl_cached_empty = 1; + } + } + } + LDAP_CACHE_UNLOCK(); + } + + if (!tmp_local_sgl && !sgl_cached_empty) { + /* No Cached SGL, retrieve from LDAP */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01289) + "no cached SGL for %s, retrieving from LDAP", dn); + tmp_local_sgl = uldap_get_subgroups(r, ldc, url, dn, subgroupAttrs, + subgroupclasses); + if (!tmp_local_sgl) { + /* No SGL aailable via LDAP either */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01290) "no subgroups for %s", + dn); + } + + if (curl && curl->compare_cache) { + /* + * Find the generic group cache entry and add the sgl we just retrieved. + */ + LDAP_CACHE_LOCK(); + + the_compare_node.dn = (char *)dn; + the_compare_node.attrib = (char *)"objectClass"; + the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name; + the_compare_node.result = 0; + the_compare_node.sgl_processed = 0; + the_compare_node.subgroupList = NULL; + + compare_nodep = util_ald_cache_fetch(curl->compare_cache, + &the_compare_node); + + if (compare_nodep == NULL) { + /* + * The group entry we want to attach our SGL to doesn't exist. + * We only got here if we verified this DN was actually a group + * based on the objectClass, but we can't call the compare function + * while we already hold the cache lock -- only the insert. + */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01291) + "Cache entry for %s doesn't exist", dn); + the_compare_node.result = LDAP_COMPARE_TRUE; + util_ald_cache_insert(curl->compare_cache, &the_compare_node); + compare_nodep = util_ald_cache_fetch(curl->compare_cache, + &the_compare_node); + if (compare_nodep == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01292) + "util_ldap: Couldn't retrieve group entry " + "for %s from cache", + dn); + } + } + + /* + * We have a valid cache entry and a locally generated SGL. + * Attach the SGL to the cache entry + */ + if (compare_nodep && !compare_nodep->sgl_processed) { + if (!tmp_local_sgl) { + /* We looked up an SGL for a group and found it to be empty */ + if (compare_nodep->subgroupList == NULL) { + compare_nodep->sgl_processed = 1; + } + } + else { + util_compare_subgroup_t *sgl_copy = + util_ald_sgl_dup(curl->compare_cache, tmp_local_sgl); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01293) + "Copying local SGL of len %d for group %s into cache", + tmp_local_sgl->len, dn); + if (sgl_copy) { + if (compare_nodep->subgroupList) { + util_ald_sgl_free(curl->compare_cache, + &(compare_nodep->subgroupList)); + } + compare_nodep->subgroupList = sgl_copy; + compare_nodep->sgl_processed = 1; + } + else { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(01294) + "Copy of SGL failed to obtain shared memory, " + "couldn't update cache"); + } + } + } + LDAP_CACHE_UNLOCK(); + } + } + + /* + * tmp_local_sgl has either been created, or copied out of the cache + * If tmp_local_sgl is NULL, there are no subgroups to process and we'll + * return false + */ + result = LDAP_COMPARE_FALSE; + if (!tmp_local_sgl) { + return result; + } + + while ((result != LDAP_COMPARE_TRUE) && (sgindex < tmp_local_sgl->len)) { + const char *group = NULL; + group = tmp_local_sgl->subgroupDNs[sgindex]; + /* + * 4. Now loop through the subgroupList and call uldap_cache_compare + * to check for the user. + */ + result = uldap_cache_compare(r, ldc, url, group, attrib, value); + if (result == LDAP_COMPARE_TRUE) { + /* + * 4.A. We found the user in the subgroup. Return + * LDAP_COMPARE_TRUE. + */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01295) + "Found user %s in a subgroup (%s) at level %d of %d.", + r->user, group, cur_subgroup_depth+1, + max_subgroup_depth); + } + else { + /* + * 4.B. We didn't find the user in this subgroup, so recurse into + * it and keep looking. + */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01296) + "User %s not found in subgroup (%s) at level %d of " + "%d.", r->user, group, cur_subgroup_depth+1, + max_subgroup_depth); + result = uldap_cache_check_subgroups(r, ldc, url, group, attrib, + value, subgroupAttrs, + subgroupclasses, + cur_subgroup_depth+1, + max_subgroup_depth); + } + sgindex++; + } + + return result; +} + + +static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc, + const char *url, const char *basedn, + int scope, char **attrs, const char *filter, + const char *bindpw, const char **binddn, + const char ***retvals) +{ + const char **vals = NULL; + int numvals = 0; + int result = 0; + LDAPMessage *res, *entry; + char *dn; + int count; + int failures = 0; + util_url_node_t *curl; /* Cached URL node */ + util_url_node_t curnode; + util_search_node_t *search_nodep; /* Cached search node */ + util_search_node_t the_search_node; + apr_time_t curtime; + + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(r->server->module_config, + &ldap_module); + + /* Get the cache node for this url */ + LDAP_CACHE_LOCK(); + curnode.url = url; + curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache, + &curnode); + if (curl == NULL) { + curl = util_ald_create_caches(st, url); + } + LDAP_CACHE_UNLOCK(); + + if (curl) { + LDAP_CACHE_LOCK(); + the_search_node.username = filter; + search_nodep = util_ald_cache_fetch(curl->search_cache, + &the_search_node); + if (search_nodep != NULL) { + + /* found entry in search cache... */ + curtime = apr_time_now(); + + /* + * Remove this item from the cache if its expired. If the sent + * password doesn't match the storepassword, the entry will + * be removed and readded later if the credentials pass + * authentication. + */ + if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) { + /* ...but entry is too old */ + util_ald_cache_remove(curl->search_cache, search_nodep); + } + else if ( (search_nodep->bindpw) + && (search_nodep->bindpw[0] != '\0') + && (strcmp(search_nodep->bindpw, bindpw) == 0)) + { + /* ...and entry is valid */ + *binddn = apr_pstrdup(r->pool, search_nodep->dn); + if (attrs) { + int i; + *retvals = apr_palloc(r->pool, sizeof(char *) * search_nodep->numvals); + for (i = 0; i < search_nodep->numvals; i++) { + (*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]); + } + } + LDAP_CACHE_UNLOCK(); + ldc->reason = "Authentication successful (cached)"; + return LDAP_SUCCESS; + } + } + /* unlock this read lock */ + LDAP_CACHE_UNLOCK(); + } + + /* + * At this point, there is no valid cached search, so lets do the search. + */ + + /* + * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here. + */ +start_over: + if (failures > st->retries) { + return result; + } + + if (failures > 0 && st->retry_delay > 0) { + apr_sleep(st->retry_delay); + } + + if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) { + return result; + } + + /* try do the search */ + result = ldap_search_ext_s(ldc->ldap, + (char *)basedn, scope, + (char *)filter, attrs, 0, + NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res); + if (AP_LDAP_IS_SERVER_DOWN(result)) + { + ldc->reason = "ldap_search_ext_s() for user failed with server down"; + uldap_connection_unbind(ldc); + failures++; + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures); + goto start_over; + } + + if (result == LDAP_TIMEOUT) { + ldc->reason = "ldap_search_ext_s() for user failed with timeout"; + uldap_connection_unbind(ldc); + failures++; + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures); + goto start_over; + } + + + /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */ + if (result != LDAP_SUCCESS) { + ldc->reason = "ldap_search_ext_s() for user failed"; + return result; + } + + /* + * We should have found exactly one entry; to find a different + * number is an error. + */ + ldc->last_backend_conn = r->request_time; + count = ldap_count_entries(ldc->ldap, res); + if (count != 1) + { + if (count == 0 ) + ldc->reason = "User not found"; + else + ldc->reason = "User is not unique (search found two " + "or more matches)"; + ldap_msgfree(res); + return LDAP_NO_SUCH_OBJECT; + } + + entry = ldap_first_entry(ldc->ldap, res); + + /* Grab the dn, copy it into the pool, and free it again */ + dn = ldap_get_dn(ldc->ldap, entry); + *binddn = apr_pstrdup(r->pool, dn); + ldap_memfree(dn); + + /* + * A bind to the server with an empty password always succeeds, so + * we check to ensure that the password is not empty. This implies + * that users who actually do have empty passwords will never be + * able to authenticate with this module. I don't see this as a big + * problem. + */ + if (!bindpw || strlen(bindpw) <= 0) { + ldap_msgfree(res); + ldc->reason = "Empty password not allowed"; + return LDAP_INVALID_CREDENTIALS; + } + + /* + * Attempt to bind with the retrieved dn and the password. If the bind + * fails, it means that the password is wrong (the dn obviously + * exists, since we just retrieved it) + */ + result = uldap_simple_bind(ldc, (char *)*binddn, (char *)bindpw, + st->opTimeout); + if (AP_LDAP_IS_SERVER_DOWN(result) || + (result == LDAP_TIMEOUT && failures == 0)) { + if (AP_LDAP_IS_SERVER_DOWN(result)) + ldc->reason = "ldap_simple_bind() to check user credentials " + "failed with server down"; + else + ldc->reason = "ldap_simple_bind() to check user credentials " + "timed out"; + ldap_msgfree(res); + uldap_connection_unbind(ldc); + failures++; + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures); + goto start_over; + } + + /* failure? if so - return */ + if (result != LDAP_SUCCESS) { + ldc->reason = "ldap_simple_bind() to check user credentials failed"; + ldap_msgfree(res); + uldap_connection_unbind(ldc); + return result; + } + else { + /* + * We have just bound the connection to a different user and password + * combination, which might be reused unintentionally next time this + * connection is used from the connection pool. + */ + ldc->must_rebind = 1; + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "LDC %pp used for authn, must be rebound", ldc); + } + + /* + * Get values for the provided attributes. + */ + if (attrs) { + int k = 0; + int i = 0; + while (attrs[k++]); + vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1)); + numvals = k; + while (attrs[i]) { + char **values; + int j = 0; + char *str = NULL; + /* get values */ + values = ldap_get_values(ldc->ldap, entry, attrs[i]); + while (values && values[j]) { + str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL) + : apr_pstrdup(r->pool, values[j]); + j++; + } + ldap_value_free(values); + vals[i] = str; + i++; + } + *retvals = vals; + } + + /* + * Add the new username to the search cache. + */ + if (curl) { + LDAP_CACHE_LOCK(); + the_search_node.username = filter; + the_search_node.dn = *binddn; + the_search_node.bindpw = bindpw; + the_search_node.lastbind = apr_time_now(); + the_search_node.vals = vals; + the_search_node.numvals = numvals; + + /* Search again to make sure that another thread didn't ready insert + * this node into the cache before we got here. If it does exist then + * update the lastbind + */ + search_nodep = util_ald_cache_fetch(curl->search_cache, + &the_search_node); + if ((search_nodep == NULL) || + (strcmp(*binddn, search_nodep->dn) != 0)) { + + /* Nothing in cache, insert new entry */ + util_ald_cache_insert(curl->search_cache, &the_search_node); + } + else if ((!search_nodep->bindpw) || + (strcmp(bindpw, search_nodep->bindpw) != 0)) { + + /* Entry in cache is invalid, remove it and insert new one */ + util_ald_cache_remove(curl->search_cache, search_nodep); + util_ald_cache_insert(curl->search_cache, &the_search_node); + } + else { + /* Cache entry is valid, update lastbind */ + search_nodep->lastbind = the_search_node.lastbind; + } + LDAP_CACHE_UNLOCK(); + } + ldap_msgfree(res); + + ldc->reason = "Authentication successful"; + return LDAP_SUCCESS; +} + +/* + * This function will return the DN of the entry matching userid. + * It is used to get the DN in case some other module than mod_auth_ldap + * has authenticated the user. + * The function is basically a copy of uldap_cache_checkuserid + * with password checking removed. + */ +static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc, + const char *url, const char *basedn, + int scope, char **attrs, const char *filter, + const char **binddn, const char ***retvals) +{ + const char **vals = NULL; + int numvals = 0; + int result = 0; + LDAPMessage *res, *entry; + char *dn; + int count; + int failures = 0; + util_url_node_t *curl; /* Cached URL node */ + util_url_node_t curnode; + util_search_node_t *search_nodep; /* Cached search node */ + util_search_node_t the_search_node; + apr_time_t curtime; + + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(r->server->module_config, + &ldap_module); + + /* Get the cache node for this url */ + LDAP_CACHE_LOCK(); + curnode.url = url; + curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache, + &curnode); + if (curl == NULL) { + curl = util_ald_create_caches(st, url); + } + LDAP_CACHE_UNLOCK(); + + if (curl) { + LDAP_CACHE_LOCK(); + the_search_node.username = filter; + search_nodep = util_ald_cache_fetch(curl->search_cache, + &the_search_node); + if (search_nodep != NULL) { + + /* found entry in search cache... */ + curtime = apr_time_now(); + + /* + * Remove this item from the cache if its expired. + */ + if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) { + /* ...but entry is too old */ + util_ald_cache_remove(curl->search_cache, search_nodep); + } + else { + /* ...and entry is valid */ + *binddn = apr_pstrdup(r->pool, search_nodep->dn); + if (attrs) { + int i; + *retvals = apr_palloc(r->pool, sizeof(char *) * search_nodep->numvals); + for (i = 0; i < search_nodep->numvals; i++) { + (*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]); + } + } + LDAP_CACHE_UNLOCK(); + ldc->reason = "Search successful (cached)"; + return LDAP_SUCCESS; + } + } + /* unlock this read lock */ + LDAP_CACHE_UNLOCK(); + } + + /* + * At this point, there is no valid cached search, so lets do the search. + */ + + /* + * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here. + */ +start_over: + if (failures > st->retries) { + return result; + } + + if (failures > 0 && st->retry_delay > 0) { + apr_sleep(st->retry_delay); + } + + if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) { + return result; + } + + /* try do the search */ + result = ldap_search_ext_s(ldc->ldap, + (char *)basedn, scope, + (char *)filter, attrs, 0, + NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res); + if (AP_LDAP_IS_SERVER_DOWN(result)) + { + ldc->reason = "ldap_search_ext_s() for user failed with server down"; + uldap_connection_unbind(ldc); + failures++; + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures); + goto start_over; + } + + /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */ + if (result != LDAP_SUCCESS) { + ldc->reason = "ldap_search_ext_s() for user failed"; + return result; + } + + /* + * We should have found exactly one entry; to find a different + * number is an error. + */ + ldc->last_backend_conn = r->request_time; + count = ldap_count_entries(ldc->ldap, res); + if (count != 1) + { + if (count == 0 ) + ldc->reason = "User not found"; + else + ldc->reason = "User is not unique (search found two " + "or more matches)"; + ldap_msgfree(res); + return LDAP_NO_SUCH_OBJECT; + } + + entry = ldap_first_entry(ldc->ldap, res); + + /* Grab the dn, copy it into the pool, and free it again */ + dn = ldap_get_dn(ldc->ldap, entry); + *binddn = apr_pstrdup(r->pool, dn); + ldap_memfree(dn); + + /* + * Get values for the provided attributes. + */ + if (attrs) { + int k = 0; + int i = 0; + while (attrs[k++]); + vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1)); + numvals = k; + while (attrs[i]) { + char **values; + int j = 0; + char *str = NULL; + /* get values */ + values = ldap_get_values(ldc->ldap, entry, attrs[i]); + while (values && values[j]) { + str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL) + : apr_pstrdup(r->pool, values[j]); + j++; + } + ldap_value_free(values); + vals[i] = str; + i++; + } + *retvals = vals; + } + + /* + * Add the new username to the search cache. + */ + if (curl) { + LDAP_CACHE_LOCK(); + the_search_node.username = filter; + the_search_node.dn = *binddn; + the_search_node.bindpw = NULL; + the_search_node.lastbind = apr_time_now(); + the_search_node.vals = vals; + the_search_node.numvals = numvals; + + /* Search again to make sure that another thread didn't ready insert + * this node into the cache before we got here. If it does exist then + * update the lastbind + */ + search_nodep = util_ald_cache_fetch(curl->search_cache, + &the_search_node); + if ((search_nodep == NULL) || + (strcmp(*binddn, search_nodep->dn) != 0)) { + + /* Nothing in cache, insert new entry */ + util_ald_cache_insert(curl->search_cache, &the_search_node); + } + /* + * Don't update lastbind on entries with bindpw because + * we haven't verified that password. It's OK to update + * the entry if there is no password in it. + */ + else if (!search_nodep->bindpw) { + /* Cache entry is valid, update lastbind */ + search_nodep->lastbind = the_search_node.lastbind; + } + LDAP_CACHE_UNLOCK(); + } + + ldap_msgfree(res); + + ldc->reason = "Search successful"; + return LDAP_SUCCESS; +} + +/* + * Reports if ssl support is enabled + * + * 1 = enabled, 0 = not enabled + */ +static int uldap_ssl_supported(request_rec *r) +{ + util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config( + r->server->module_config, &ldap_module); + + return(st->ssl_supported); +} + + +/* ---------------------------------------- */ +/* config directives */ + + +static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy, + const char *bytes) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + st->cache_bytes = atol(bytes); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01297) + "ldap cache: Setting shared memory cache size to " + "%" APR_SIZE_T_FMT " bytes.", + st->cache_bytes); + + return NULL; +} + +static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy, + const char *file) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + if (file) { + st->cache_file = ap_server_root_relative(st->pool, file); + } + else { + st->cache_file = NULL; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01298) + "LDAP cache: Setting shared memory cache file to %s.", + st->cache_file); + + return NULL; +} + +static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy, + const char *ttl) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + st->search_cache_ttl = atol(ttl) * 1000000; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01299) + "ldap cache: Setting cache TTL to %ld microseconds.", + st->search_cache_ttl); + + return NULL; +} + +static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy, + const char *size) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + st->search_cache_size = atol(size); + if (st->search_cache_size < 0) { + st->search_cache_size = 0; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01300) + "ldap cache: Setting search cache size to %ld entries.", + st->search_cache_size); + + return NULL; +} + +static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy, + const char *ttl) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + st->compare_cache_ttl = atol(ttl) * APR_USEC_PER_SEC; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01301) + "ldap cache: Setting operation cache TTL to %ld microseconds.", + st->compare_cache_ttl); + + return NULL; +} + +static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy, + const char *size) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + st->compare_cache_size = atol(size); + if (st->compare_cache_size < 0) { + st->compare_cache_size = 0; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01302) + "ldap cache: Setting operation cache size to %ld entries.", + st->compare_cache_size); + + return NULL; +} + + +/** + * Parse the certificate type. + * + * The type can be one of the following: + * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, + * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64 + * + * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned. + */ +static int util_ldap_parse_cert_type(const char *type) +{ + /* Authority file in binary DER format */ + if (0 == strcasecmp("CA_DER", type)) { + return APR_LDAP_CA_TYPE_DER; + } + + /* Authority file in Base64 format */ + else if (0 == strcasecmp("CA_BASE64", type)) { + return APR_LDAP_CA_TYPE_BASE64; + } + + /* Netscape certificate database file/directory */ + else if (0 == strcasecmp("CA_CERT7_DB", type)) { + return APR_LDAP_CA_TYPE_CERT7_DB; + } + + /* Netscape secmod file/directory */ + else if (0 == strcasecmp("CA_SECMOD", type)) { + return APR_LDAP_CA_TYPE_SECMOD; + } + + /* Client cert file in DER format */ + else if (0 == strcasecmp("CERT_DER", type)) { + return APR_LDAP_CERT_TYPE_DER; + } + + /* Client cert file in Base64 format */ + else if (0 == strcasecmp("CERT_BASE64", type)) { + return APR_LDAP_CERT_TYPE_BASE64; + } + + /* Client cert file in PKCS#12 format */ + else if (0 == strcasecmp("CERT_PFX", type)) { + return APR_LDAP_CERT_TYPE_PFX; + } + + /* Netscape client cert database file/directory */ + else if (0 == strcasecmp("CERT_KEY3_DB", type)) { + return APR_LDAP_CERT_TYPE_KEY3_DB; + } + + /* Netscape client cert nickname */ + else if (0 == strcasecmp("CERT_NICKNAME", type)) { + return APR_LDAP_CERT_TYPE_NICKNAME; + } + + /* Client cert key file in DER format */ + else if (0 == strcasecmp("KEY_DER", type)) { + return APR_LDAP_KEY_TYPE_DER; + } + + /* Client cert key file in Base64 format */ + else if (0 == strcasecmp("KEY_BASE64", type)) { + return APR_LDAP_KEY_TYPE_BASE64; + } + + /* Client cert key file in PKCS#12 format */ + else if (0 == strcasecmp("KEY_PFX", type)) { + return APR_LDAP_KEY_TYPE_PFX; + } + + else { + return APR_LDAP_CA_TYPE_UNKNOWN; + } + +} + + +/** + * Set LDAPTrustedGlobalCert. + * + * This directive takes either two or three arguments: + * - certificate type + * - certificate file / directory / nickname + * - certificate password (optional) + * + * This directive may only be used globally. + */ +static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd, + void *dummy, + const char *type, + const char *file, + const char *password) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + apr_finfo_t finfo; + apr_status_t rv; + int cert_type = 0; + apr_ldap_opt_tls_cert_t *cert; + + if (err != NULL) { + return err; + } + + /* handle the certificate type */ + if (type) { + cert_type = util_ldap_parse_cert_type(type); + if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) { + return apr_psprintf(cmd->pool, "The certificate type %s is " + "not recognised. It should be one " + "of CA_DER, CA_BASE64, CA_CERT7_DB, " + "CA_SECMOD, CERT_DER, CERT_BASE64, " + "CERT_KEY3_DB, CERT_NICKNAME, " + "KEY_DER, KEY_BASE64", type); + } + } + else { + return "Certificate type was not specified."; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01303) + "LDAP: SSL trusted global cert - %s (type %s)", + file, type); + + /* add the certificate to the global array */ + cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs); + cert->type = cert_type; + cert->path = file; + cert->password = password; + + /* if file is a file or path, fix the path */ + if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN && + cert_type != APR_LDAP_CERT_TYPE_NICKNAME) { + + cert->path = ap_server_root_relative(cmd->pool, file); + if (cert->path && + ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool)) + != APR_SUCCESS)) + { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server, APLOGNO(01304) + "LDAP: Could not open SSL trusted certificate " + "authority file - %s", + cert->path == NULL ? file : cert->path); + return "Invalid global certificate file path"; + } + } + + return(NULL); +} + + +/** + * Set LDAPTrustedClientCert. + * + * This directive takes either two or three arguments: + * - certificate type + * - certificate file / directory / nickname + * - certificate password (optional) + */ +static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd, + void *config, + const char *type, + const char *file, + const char *password) +{ + util_ldap_config_t *dc = config; + apr_finfo_t finfo; + apr_status_t rv; + int cert_type = 0; + apr_ldap_opt_tls_cert_t *cert; + + /* handle the certificate type */ + if (type) { + cert_type = util_ldap_parse_cert_type(type); + if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) { + return apr_psprintf(cmd->pool, "The certificate type \"%s\" is " + "not recognised. It should be one " + "of CA_DER, CA_BASE64, " + "CERT_DER, CERT_BASE64, " + "CERT_NICKNAME, CERT_PFX, " + "KEY_DER, KEY_BASE64, KEY_PFX", + type); + } + else if ( APR_LDAP_CA_TYPE_CERT7_DB == cert_type || + APR_LDAP_CA_TYPE_SECMOD == cert_type || + APR_LDAP_CERT_TYPE_PFX == cert_type || + APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) { + return apr_psprintf(cmd->pool, "The certificate type \"%s\" is " + "only valid within a " + "LDAPTrustedGlobalCert directive. " + "Only CA_DER, CA_BASE64, " + "CERT_DER, CERT_BASE64, " + "CERT_NICKNAME, KEY_DER, and " + "KEY_BASE64 may be used.", type); + } + } + else { + return "Certificate type was not specified."; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01305) + "LDAP: SSL trusted client cert - %s (type %s)", + file, type); + + /* add the certificate to the client array */ + cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(dc->client_certs); + cert->type = cert_type; + cert->path = file; + cert->password = password; + + /* if file is a file or path, fix the path */ + if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN && + cert_type != APR_LDAP_CERT_TYPE_NICKNAME) { + + cert->path = ap_server_root_relative(cmd->pool, file); + if (cert->path && + ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool)) + != APR_SUCCESS)) + { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server, APLOGNO(01306) + "LDAP: Could not open SSL client certificate " + "file - %s", + cert->path == NULL ? file : cert->path); + return "Invalid client certificate file path"; + } + + } + + return(NULL); +} + + +/** + * Set LDAPTrustedMode. + * + * This directive sets what encryption mode to use on a connection: + * - None (No encryption) + * - SSL (SSL encryption) + * - STARTTLS (TLS encryption) + */ +static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy, + const char *mode) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01307) + "LDAP: SSL trusted mode - %s", + mode); + + if (0 == strcasecmp("NONE", mode)) { + st->secure = APR_LDAP_NONE; + } + else if (0 == strcasecmp("SSL", mode)) { + st->secure = APR_LDAP_SSL; + } + else if ( (0 == strcasecmp("TLS", mode)) + || (0 == strcasecmp("STARTTLS", mode))) { + st->secure = APR_LDAP_STARTTLS; + } + else { + return "Invalid LDAPTrustedMode setting: must be one of NONE, " + "SSL, or TLS/STARTTLS"; + } + + st->secure_set = 1; + return(NULL); +} + +static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd, + void *dummy, + int mode) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01308) + "LDAP: SSL verify server certificate - %s", + mode?"TRUE":"FALSE"); + + st->verify_svr_cert = mode; + + return(NULL); +} + + +static const char *util_ldap_set_connection_timeout(cmd_parms *cmd, + void *dummy, + const char *ttl) +{ +#ifdef LDAP_OPT_NETWORK_TIMEOUT + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); +#endif + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + +#ifdef LDAP_OPT_NETWORK_TIMEOUT + st->connectionTimeout = atol(ttl); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01309) + "ldap connection: Setting connection timeout to %ld seconds.", + st->connectionTimeout); +#else + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server, APLOGNO(01310) + "LDAP: Connection timeout option not supported by the " + "LDAP SDK in use." ); +#endif + + return NULL; +} + + +static const char *util_ldap_set_chase_referrals(cmd_parms *cmd, + void *config, + const char *arg) +{ + util_ldap_config_t *dc = config; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01311) + "LDAP: Setting referral chasing %s", arg); + + if (0 == strcasecmp(arg, "on")) { + dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_ON; + } + else if (0 == strcasecmp(arg, "off")) { + dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_OFF; + } + else if (0 == strcasecmp(arg, "default")) { + dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_SDKDEFAULT; + } + else { + return "LDAPReferrals must be 'on', 'off', or 'default'"; + } + + return(NULL); +} + +static const char *util_ldap_set_debug_level(cmd_parms *cmd, + void *config, + const char *arg) { +#ifdef AP_LDAP_OPT_DEBUG + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); +#endif + + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + +#ifndef AP_LDAP_OPT_DEBUG + return "This directive is not supported with the currently linked LDAP library"; +#else + st->debug_level = atoi(arg); + return NULL; +#endif +} + +static const char *util_ldap_set_referral_hop_limit(cmd_parms *cmd, + void *config, + const char *hop_limit) +{ + util_ldap_config_t *dc = config; + + dc->ReferralHopLimit = atol(hop_limit); + + if (dc->ReferralHopLimit <= 0) { + return "LDAPReferralHopLimit must be greater than zero (Use 'LDAPReferrals Off' to disable referral chasing)"; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01312) + "LDAP: Limit chased referrals to maximum of %d hops.", + dc->ReferralHopLimit); + + return NULL; +} + +static void *util_ldap_create_dir_config(apr_pool_t *p, char *d) +{ + util_ldap_config_t *dc = + (util_ldap_config_t *) apr_pcalloc(p,sizeof(util_ldap_config_t)); + + /* defaults are AP_LDAP_CHASEREFERRALS_ON and AP_LDAP_DEFAULT_HOPLIMIT */ + dc->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t)); + dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_ON; + dc->ReferralHopLimit = AP_LDAP_HOPLIMIT_UNSET; + + return dc; +} + +static const char *util_ldap_set_op_timeout(cmd_parms *cmd, + void *dummy, + const char *val) +{ + long timeout; + char *endptr; + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + timeout = strtol(val, &endptr, 10); + if ((val == endptr) || (*endptr != '\0')) { + return "Timeout not numerical"; + } + if (timeout < 0) { + return "Timeout must be non-negative"; + } + + if (timeout) { + if (!st->opTimeout) { + st->opTimeout = apr_pcalloc(cmd->pool, sizeof(struct timeval)); + } + st->opTimeout->tv_sec = timeout; + } + else { + st->opTimeout = NULL; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01313) + "ldap connection: Setting op timeout to %ld seconds.", + timeout); + +#ifndef LDAP_OPT_TIMEOUT + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01314) + "LDAP: LDAP_OPT_TIMEOUT option not supported by the " + "LDAP library in use. Using LDAPTimeout value as search " + "timeout only." ); +#endif + + return NULL; +} + +static const char *util_ldap_set_conn_ttl(cmd_parms *cmd, + void *dummy, + const char *val) +{ + apr_interval_time_t timeout; + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + + if (ap_timeout_parameter_parse(val, &timeout, "s") != APR_SUCCESS) { + return "LDAPConnectionPoolTTL has wrong format"; + } + + if (timeout < 0) { + /* reserve -1 for default value */ + timeout = AP_LDAP_CONNPOOL_INFINITE; + } + st->connection_pool_ttl = timeout; + return NULL; +} + +static const char *util_ldap_set_retry_delay(cmd_parms *cmd, + void *dummy, + const char *val) +{ + apr_interval_time_t timeout; + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + if (ap_timeout_parameter_parse(val, &timeout, "s") != APR_SUCCESS) { + return "LDAPRetryDelay has wrong format"; + } + + if (timeout < 0) { + return "LDAPRetryDelay must be >= 0"; + } + + st->retry_delay = timeout; + return NULL; +} + +static const char *util_ldap_set_retries(cmd_parms *cmd, + void *dummy, + const char *val) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + st->retries = atoi(val); + if (st->retries < 0) { + return "LDAPRetries must be >= 0"; + } + + return NULL; +} + +static void *util_ldap_create_config(apr_pool_t *p, server_rec *s) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t)); + + /* Create a per vhost pool for mod_ldap to use, serialized with + * st->mutex (also one per vhost). both are replicated by fork(), + * no shared memory managed by either. + */ + apr_pool_create(&st->pool, p); +#if APR_HAS_THREADS + apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool); +#endif + + st->cache_bytes = 500000; + st->search_cache_ttl = 600 * APR_USEC_PER_SEC; /* 10 minutes */ + st->search_cache_size = 1024; + st->compare_cache_ttl = 600 * APR_USEC_PER_SEC; /* 10 minutes */ + st->compare_cache_size = 1024; + st->connections = NULL; + st->ssl_supported = 0; + st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t)); + st->secure = APR_LDAP_NONE; + st->secure_set = 0; + st->connectionTimeout = 10; + st->opTimeout = apr_pcalloc(p, sizeof(struct timeval)); + st->opTimeout->tv_sec = 60; + st->verify_svr_cert = 1; + st->connection_pool_ttl = AP_LDAP_CONNPOOL_DEFAULT; /* no limit */ + st->retries = 3; + st->retry_delay = 0; /* no delay */ + + return st; +} + +/* cache-related settings are not merged here, but in the post_config hook, + * since the cache has not yet sprung to life + */ +static void *util_ldap_merge_config(apr_pool_t *p, void *basev, + void *overridesv) +{ + util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t)); + util_ldap_state_t *base = (util_ldap_state_t *) basev; + util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv; + + st->pool = overrides->pool; +#if APR_HAS_THREADS + st->mutex = overrides->mutex; +#endif + + /* The cache settings can not be modified in a + virtual host since all server use the same + shared memory cache. */ + st->cache_bytes = base->cache_bytes; + st->search_cache_ttl = base->search_cache_ttl; + st->search_cache_size = base->search_cache_size; + st->compare_cache_ttl = base->compare_cache_ttl; + st->compare_cache_size = base->compare_cache_size; + + st->connections = NULL; + st->ssl_supported = 0; /* not known until post-config and re-merged */ + st->global_certs = apr_array_append(p, base->global_certs, + overrides->global_certs); + st->secure = (overrides->secure_set == 0) ? base->secure + : overrides->secure; + + /* These LDAP connection settings can not be overwritten in + a virtual host. Once set in the base server, they must + remain the same. None of the LDAP SDKs seem to be able + to handle setting the verify_svr_cert flag on a + per-connection basis. The OpenLDAP client appears to be + able to handle the connection timeout per-connection + but the Novell SDK cannot. Allowing the timeout to + be set by each vhost is of little value so rather than + trying to make special expections for one LDAP SDK, GLOBAL_ONLY + is being enforced on this setting as well. */ + st->connectionTimeout = base->connectionTimeout; + st->opTimeout = base->opTimeout; + st->verify_svr_cert = base->verify_svr_cert; + st->debug_level = base->debug_level; + + st->connection_pool_ttl = (overrides->connection_pool_ttl == AP_LDAP_CONNPOOL_DEFAULT) ? + base->connection_pool_ttl : overrides->connection_pool_ttl; + + st->retries = base->retries; + st->retry_delay = base->retry_delay; + + return st; +} + +static apr_status_t util_ldap_cleanup_module(void *data) +{ + server_rec *s = data; + util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config( + s->module_config, &ldap_module); + + if (st->ssl_supported) { + apr_ldap_ssl_deinit(); + } + + return APR_SUCCESS; +} + +static int util_ldap_pre_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp) +{ + apr_status_t result; + + result = ap_mutex_register(pconf, ldap_cache_mutex_type, NULL, + APR_LOCK_DEFAULT, 0); + if (result != APR_SUCCESS) { + return result; + } + + return OK; +} + +static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + apr_status_t result; + server_rec *s_vhost; + util_ldap_state_t *st_vhost; + + util_ldap_state_t *st = (util_ldap_state_t *) + ap_get_module_config(s->module_config, + &ldap_module); + + apr_ldap_err_t *result_err = NULL; + int rc; + + /* util_ldap_post_config() will be called twice. Don't bother + * going through all of the initialization on the first call + * because it will just be thrown away.*/ + if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) { + +#if APR_HAS_SHARED_MEMORY + /* + * If we are using shared memory caching and the cache file already + * exists then delete it. Otherwise we are going to run into problems + * creating the shared memory. + */ + if (st->cache_file && st->cache_bytes > 0) { + char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck", + NULL); + apr_file_remove(lck_file, ptemp); + } +#endif + return OK; + } + +#if APR_HAS_SHARED_MEMORY + /* + * initializing cache if we don't already have a shm address + */ + if (!st->cache_shm) { +#endif + result = util_ldap_cache_init(p, st); + if (result != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, result, s, APLOGNO(01315) + "LDAP cache: could not create shared memory segment"); + return DONE; + } + + result = ap_global_mutex_create(&st->util_ldap_cache_lock, NULL, + ldap_cache_mutex_type, NULL, s, p, 0); + if (result != APR_SUCCESS) { + return result; + } + + /* merge config in all vhost */ + s_vhost = s->next; + while (s_vhost) { + st_vhost = (util_ldap_state_t *) + ap_get_module_config(s_vhost->module_config, + &ldap_module); + st_vhost->util_ldap_cache = st->util_ldap_cache; + st_vhost->util_ldap_cache_lock = st->util_ldap_cache_lock; +#if APR_HAS_SHARED_MEMORY + st_vhost->cache_shm = st->cache_shm; + st_vhost->cache_rmm = st->cache_rmm; + st_vhost->cache_file = st->cache_file; + ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s, APLOGNO(01316) + "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp " + "for VHOST: %s", st->cache_shm, st->cache_rmm, + s_vhost->server_hostname); +#endif + s_vhost = s_vhost->next; + } +#if APR_HAS_SHARED_MEMORY + } + else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01317) + "LDAP cache: LDAPSharedCacheSize is zero, disabling " + "shared memory cache"); + } +#endif + + /* log the LDAP SDK used + */ + { + apr_ldap_err_t *result = NULL; + apr_ldap_info(p, &(result)); + if (result != NULL) { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01318) "%s", result->reason); + } + } + + apr_pool_cleanup_register(p, s, util_ldap_cleanup_module, + util_ldap_cleanup_module); + + /* + * Initialize SSL support, and log the result for the benefit of the admin. + * + * If SSL is not supported it is not necessarily an error, as the + * application may not want to use it. + */ + rc = apr_ldap_ssl_init(p, + NULL, + 0, + &(result_err)); + if (APR_SUCCESS == rc) { + rc = apr_ldap_set_option(ptemp, NULL, APR_LDAP_OPT_TLS_CERT, + (void *)st->global_certs, &(result_err)); + } + + if (APR_SUCCESS == rc) { + st->ssl_supported = 1; + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01319) + "LDAP: SSL support available" ); + } + else { + st->ssl_supported = 0; + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01320) + "LDAP: SSL support unavailable%s%s", + result_err ? ": " : "", + result_err ? result_err->reason : ""); + } + + /* ssl_supported is really a global setting */ + s_vhost = s->next; + while (s_vhost) { + st_vhost = (util_ldap_state_t *) + ap_get_module_config(s_vhost->module_config, + &ldap_module); + + st_vhost->ssl_supported = st->ssl_supported; + s_vhost = s_vhost->next; + } + + /* Initialize the rebind callback's cross reference list. */ + apr_ldap_rebind_init (p); + +#ifdef AP_LDAP_OPT_DEBUG + if (st->debug_level > 0) { + result = ldap_set_option(NULL, AP_LDAP_OPT_DEBUG, &st->debug_level); + if (result != LDAP_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01321) + "LDAP: Could not set the LDAP library debug level to %d:(%d) %s", + st->debug_level, result, ldap_err2string(result)); + } + } +#endif + + return(OK); +} + +static void util_ldap_child_init(apr_pool_t *p, server_rec *s) +{ + apr_status_t sts; + util_ldap_state_t *st = ap_get_module_config(s->module_config, + &ldap_module); + + if (!st->util_ldap_cache_lock) return; + + sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock, + apr_global_mutex_lockfile(st->util_ldap_cache_lock), p); + if (sts != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s, APLOGNO(01322) + "Failed to initialise global mutex %s in child process", + ldap_cache_mutex_type); + } +} + +static const command_rec util_ldap_cmds[] = { + AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes, + NULL, RSRC_CONF, + "Set the size of the shared memory cache (in bytes). Use " + "0 to disable the shared memory cache. (default: 500000)"), + + AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file, + NULL, RSRC_CONF, + "Set the file name for the shared memory cache."), + + AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries, + NULL, RSRC_CONF, + "Set the maximum number of entries that are possible in the " + "LDAP search cache. Use 0 or -1 to disable the search cache " + "(default: 1024)"), + + AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl, + NULL, RSRC_CONF, + "Set the maximum time (in seconds) that an item can be " + "cached in the LDAP search cache. Use 0 for no limit. " + "(default 600)"), + + AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries, + NULL, RSRC_CONF, + "Set the maximum number of entries that are possible " + "in the LDAP compare cache. Use 0 or -1 to disable the compare cache " + "(default: 1024)"), + + AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl, + NULL, RSRC_CONF, + "Set the maximum time (in seconds) that an item is cached " + "in the LDAP operation cache. Use 0 for no limit. " + "(default: 600)"), + + AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert, + NULL, RSRC_CONF, + "Takes three arguments; the first argument is the cert " + "type of the second argument, one of CA_DER, CA_BASE64, " + "CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, " + "CERT_NICKNAME, KEY_DER, or KEY_BASE64. The second argument " + "specifes the file and/or directory containing the trusted CA " + "certificates (and global client certs for Netware) used to " + "validate the LDAP server. The third argument is an optional " + "passphrase if applicable."), + + AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert, + NULL, OR_AUTHCFG, + "Takes three arguments: the first argument is the certificate " + "type of the second argument, one of CA_DER, CA_BASE64, " + "CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, " + "CERT_NICKNAME, KEY_DER, or KEY_BASE64. The second argument " + "specifies the file and/or directory containing the client " + "certificate, or certificate ID used to validate this LDAP " + "client. The third argument is an optional passphrase if " + "applicable."), + + AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode, + NULL, RSRC_CONF, + "Specify the type of security that should be applied to " + "an LDAP connection. One of; NONE, SSL or STARTTLS."), + + AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert, + NULL, RSRC_CONF, + "Set to 'ON' requires that the server certificate be verified" + " before a secure LDAP connection can be establish. Default" + " 'ON'"), + + AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout, + NULL, RSRC_CONF, + "Specify the LDAP socket connection timeout in seconds " + "(default: 10)"), + + AP_INIT_TAKE1("LDAPReferrals", util_ldap_set_chase_referrals, + NULL, OR_AUTHCFG, + "Choose whether referrals are chased ['ON'|'OFF'|'DEFAULT']. Default 'ON'"), + + AP_INIT_TAKE1("LDAPReferralHopLimit", util_ldap_set_referral_hop_limit, + NULL, OR_AUTHCFG, + "Limit the number of referral hops that LDAP can follow. " + "(Integer value, Consult LDAP SDK documentation for applicability and defaults"), + + AP_INIT_TAKE1("LDAPLibraryDebug", util_ldap_set_debug_level, + NULL, RSRC_CONF, + "Enable debugging in LDAP SDK (Default: off, values: SDK specific"), + + AP_INIT_TAKE1("LDAPTimeout", util_ldap_set_op_timeout, + NULL, RSRC_CONF, + "Specify the LDAP bind/search timeout in seconds " + "(0 = no limit). Default: 60"), + AP_INIT_TAKE1("LDAPConnectionPoolTTL", util_ldap_set_conn_ttl, + NULL, RSRC_CONF, + "Specify the maximum amount of time a bound connection can sit " + "idle and still be considered valid for reuse" + "(0 = no pool, -1 = no limit, n = time in seconds). Default: -1"), + AP_INIT_TAKE1("LDAPRetries", util_ldap_set_retries, + NULL, RSRC_CONF, + "Specify the number of times a failed LDAP operation should be retried " + "(0 = no retries). Default: 3"), + AP_INIT_TAKE1("LDAPRetryDelay", util_ldap_set_retry_delay, + NULL, RSRC_CONF, + "Specify the delay between retries of a failed LDAP operation " + "(0 = no delay). Default: 0"), + + + {NULL} +}; + +static void util_ldap_register_hooks(apr_pool_t *p) +{ + APR_REGISTER_OPTIONAL_FN(uldap_connection_open); + APR_REGISTER_OPTIONAL_FN(uldap_connection_close); + APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind); + APR_REGISTER_OPTIONAL_FN(uldap_connection_find); + APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn); + APR_REGISTER_OPTIONAL_FN(uldap_cache_compare); + APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid); + APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn); + APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported); + APR_REGISTER_OPTIONAL_FN(uldap_cache_check_subgroups); + + ap_hook_pre_config(util_ldap_pre_config, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE); + ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE); +} + +AP_DECLARE_MODULE(ldap) = { + STANDARD20_MODULE_STUFF, + util_ldap_create_dir_config, /* create dir config */ + NULL, /* merge dir config */ + util_ldap_create_config, /* create server config */ + util_ldap_merge_config, /* merge server config */ + util_ldap_cmds, /* command table */ + util_ldap_register_hooks, /* set up request processing hooks */ +}; diff --git a/modules/ldap/util_ldap_cache.c b/modules/ldap/util_ldap_cache.c new file mode 100644 index 0000000..774a76e --- /dev/null +++ b/modules/ldap/util_ldap_cache.c @@ -0,0 +1,467 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * util_ldap_cache.c: LDAP cache things + * + * Original code from auth_ldap module for Apache v1.3: + * Copyright 1998, 1999 Enbridge Pipelines Inc. + * Copyright 1999-2001 Dave Carrigan + */ + +#include "httpd.h" +#include "util_ldap.h" +#include "util_ldap_cache.h" +#include <apr_strings.h> + +#if APR_HAS_LDAP + +/* ------------------------------------------------------------------ */ + +unsigned long util_ldap_url_node_hash(void *n) +{ + util_url_node_t *node = n; + return util_ald_hash_string(1, node->url); +} + +int util_ldap_url_node_compare(void *a, void *b) +{ + util_url_node_t *na = a; + util_url_node_t *nb = b; + + return (strcmp(na->url, nb->url) == 0); +} + +void *util_ldap_url_node_copy(util_ald_cache_t *cache, void *c) +{ + util_url_node_t *n = c; + util_url_node_t *node = util_ald_alloc(cache, sizeof *node); + + if (node) { + if (!(node->url = util_ald_strdup(cache, n->url))) { + util_ald_free(cache, node); + return NULL; + } + node->search_cache = n->search_cache; + node->compare_cache = n->compare_cache; + node->dn_compare_cache = n->dn_compare_cache; + return node; + } + else { + return NULL; + } +} + +void util_ldap_url_node_free(util_ald_cache_t *cache, void *n) +{ + util_url_node_t *node = n; + + util_ald_free(cache, node->url); + util_ald_destroy_cache(node->search_cache); + util_ald_destroy_cache(node->compare_cache); + util_ald_destroy_cache(node->dn_compare_cache); + util_ald_free(cache, node); +} + +void util_ldap_url_node_display(request_rec *r, util_ald_cache_t *cache, void *n) +{ + util_url_node_t *node = n; + char date_str[APR_CTIME_LEN]; + const char *type_str; + util_ald_cache_t *cache_node; + int x; + + for (x=0;x<3;x++) { + switch (x) { + case 0: + cache_node = node->search_cache; + type_str = "Searches"; + break; + case 1: + cache_node = node->compare_cache; + type_str = "Compares"; + break; + case 2: + default: + cache_node = node->dn_compare_cache; + type_str = "DN Compares"; + break; + } + + if (cache_node->marktime) { + apr_ctime(date_str, cache_node->marktime); + } + else + date_str[0] = 0; + + ap_rprintf(r, + "<tr valign='top'>" + "<td nowrap>%s (%s)</td>" + "<td nowrap>%ld</td>" + "<td nowrap>%ld</td>" + "<td nowrap>%ld</td>" + "<td nowrap>%" APR_TIME_T_FMT "</td>" + "<td nowrap>%ld</td>" + "<td nowrap>%s</td>" + "</tr>", + node->url, + type_str, + cache_node->size, + cache_node->maxentries, + cache_node->numentries, + apr_time_sec(cache_node->ttl), + cache_node->fullmark, + date_str); + } + +} + +/* ------------------------------------------------------------------ */ + +/* Cache functions for search nodes */ +unsigned long util_ldap_search_node_hash(void *n) +{ + util_search_node_t *node = n; + return util_ald_hash_string(1, node->username); +} + +int util_ldap_search_node_compare(void *a, void *b) +{ + util_search_node_t *na = a; + util_search_node_t *nb = b; + + return (strcmp(na->username, nb->username) == 0); +} + +void *util_ldap_search_node_copy(util_ald_cache_t *cache, void *c) +{ + util_search_node_t *node = c; + util_search_node_t *newnode = util_ald_alloc(cache, sizeof *newnode); + + /* safety check */ + if (newnode) { + + /* copy vals */ + if (node->vals) { + int k = node->numvals; + int i = 0; + if (!(newnode->vals = util_ald_alloc(cache, sizeof(char *) * (k+1)))) { + util_ldap_search_node_free(cache, newnode); + return NULL; + } + newnode->numvals = node->numvals; + for (;k;k--) { + if (node->vals[i]) { + if (!(newnode->vals[i] = util_ald_strdup(cache, node->vals[i]))) { + util_ldap_search_node_free(cache, newnode); + return NULL; + } + } + else + newnode->vals[i] = NULL; + i++; + } + } + else { + newnode->vals = NULL; + } + if (!(newnode->username = util_ald_strdup(cache, node->username)) || + !(newnode->dn = util_ald_strdup(cache, node->dn)) ) { + util_ldap_search_node_free(cache, newnode); + return NULL; + } + if (node->bindpw) { + if (!(newnode->bindpw = util_ald_strdup(cache, node->bindpw))) { + util_ldap_search_node_free(cache, newnode); + return NULL; + } + } else { + newnode->bindpw = NULL; + } + newnode->lastbind = node->lastbind; + + } + return (void *)newnode; +} + +void util_ldap_search_node_free(util_ald_cache_t *cache, void *n) +{ + int i = 0; + util_search_node_t *node = n; + int k = node->numvals; + + if (node->vals) { + for (;k;k--,i++) { + if (node->vals[i]) { + util_ald_free(cache, node->vals[i]); + } + } + util_ald_free(cache, node->vals); + } + util_ald_free(cache, node->username); + util_ald_free(cache, node->dn); + util_ald_free(cache, node->bindpw); + util_ald_free(cache, node); +} + +void util_ldap_search_node_display(request_rec *r, util_ald_cache_t *cache, void *n) +{ + util_search_node_t *node = n; + char date_str[APR_CTIME_LEN]; + + apr_ctime(date_str, node->lastbind); + + ap_rprintf(r, + "<tr valign='top'>" + "<td nowrap>%s</td>" + "<td nowrap>%s</td>" + "<td nowrap>%s</td>" + "</tr>", + node->username, + node->dn, + date_str); +} + +/* ------------------------------------------------------------------ */ + +unsigned long util_ldap_compare_node_hash(void *n) +{ + util_compare_node_t *node = n; + return util_ald_hash_string(3, node->dn, node->attrib, node->value); +} + +int util_ldap_compare_node_compare(void *a, void *b) +{ + util_compare_node_t *na = a; + util_compare_node_t *nb = b; + + return (strcmp(na->dn, nb->dn) == 0 && + strcmp(na->attrib, nb->attrib) == 0 && + strcmp(na->value, nb->value) == 0); +} + +void *util_ldap_compare_node_copy(util_ald_cache_t *cache, void *c) +{ + util_compare_node_t *n = c; + util_compare_node_t *node = util_ald_alloc(cache, sizeof *node); + + if (node) { + if (!(node->dn = util_ald_strdup(cache, n->dn)) || + !(node->attrib = util_ald_strdup(cache, n->attrib)) || + !(node->value = util_ald_strdup(cache, n->value)) || + ((n->subgroupList) && !(node->subgroupList = util_ald_sgl_dup(cache, n->subgroupList)))) { + util_ldap_compare_node_free(cache, node); + return NULL; + } + node->lastcompare = n->lastcompare; + node->result = n->result; + node->sgl_processed = n->sgl_processed; + return node; + } + else { + return NULL; + } +} + +void util_ldap_compare_node_free(util_ald_cache_t *cache, void *n) +{ + util_compare_node_t *node = n; + + util_ald_sgl_free(cache, &(node->subgroupList)); + util_ald_free(cache, node->dn); + util_ald_free(cache, node->attrib); + util_ald_free(cache, node->value); + util_ald_free(cache, node); +} + +void util_ldap_compare_node_display(request_rec *r, util_ald_cache_t *cache, void *n) +{ + util_compare_node_t *node = n; + char date_str[APR_CTIME_LEN]; + char *cmp_result; + char *sub_groups_val; + char *sub_groups_checked; + + apr_ctime(date_str, node->lastcompare); + + if (node->result == LDAP_COMPARE_TRUE) { + cmp_result = "LDAP_COMPARE_TRUE"; + } + else if (node->result == LDAP_COMPARE_FALSE) { + cmp_result = "LDAP_COMPARE_FALSE"; + } + else { + cmp_result = apr_itoa(r->pool, node->result); + } + + if (node->subgroupList) { + sub_groups_val = "Yes"; + } + else { + sub_groups_val = "No"; + } + + if (node->sgl_processed) { + sub_groups_checked = "Yes"; + } + else { + sub_groups_checked = "No"; + } + + ap_rprintf(r, + "<tr valign='top'>" + "<td nowrap>%s</td>" + "<td nowrap>%s</td>" + "<td nowrap>%s</td>" + "<td nowrap>%s</td>" + "<td nowrap>%s</td>" + "<td nowrap>%s</td>" + "<td nowrap>%s</td>" + "</tr>", + node->dn, + node->attrib, + node->value, + date_str, + cmp_result, + sub_groups_val, + sub_groups_checked); +} + +/* ------------------------------------------------------------------ */ + +unsigned long util_ldap_dn_compare_node_hash(void *n) +{ + util_dn_compare_node_t *node = n; + return util_ald_hash_string(1, node->reqdn); +} + +int util_ldap_dn_compare_node_compare(void *a, void *b) +{ + util_dn_compare_node_t *na = a; + util_dn_compare_node_t *nb = b; + + return (strcmp(na->reqdn, nb->reqdn) == 0); +} + +void *util_ldap_dn_compare_node_copy(util_ald_cache_t *cache, void *c) +{ + util_dn_compare_node_t *n = c; + util_dn_compare_node_t *node = util_ald_alloc(cache, sizeof *node); + + if (node) { + if (!(node->reqdn = util_ald_strdup(cache, n->reqdn)) || + !(node->dn = util_ald_strdup(cache, n->dn))) { + util_ldap_dn_compare_node_free(cache, node); + return NULL; + } + return node; + } + else { + return NULL; + } +} + +void util_ldap_dn_compare_node_free(util_ald_cache_t *cache, void *n) +{ + util_dn_compare_node_t *node = n; + util_ald_free(cache, node->reqdn); + util_ald_free(cache, node->dn); + util_ald_free(cache, node); +} + +void util_ldap_dn_compare_node_display(request_rec *r, util_ald_cache_t *cache, void *n) +{ + util_dn_compare_node_t *node = n; + + ap_rprintf(r, + "<tr valign='top'>" + "<td nowrap>%s</td>" + "<td nowrap>%s</td>" + "</tr>", + node->reqdn, + node->dn); +} + + +/* ------------------------------------------------------------------ */ +static apr_status_t util_ldap_cache_module_kill(void *data) +{ + util_ldap_state_t *st = data; + + util_ald_destroy_cache(st->util_ldap_cache); +#if APR_HAS_SHARED_MEMORY + if (st->cache_rmm != NULL) { + apr_rmm_destroy (st->cache_rmm); + st->cache_rmm = NULL; + } + if (st->cache_shm != NULL) { + apr_status_t result = apr_shm_destroy(st->cache_shm); + st->cache_shm = NULL; + return result; + } +#endif + return APR_SUCCESS; +} + +apr_status_t util_ldap_cache_init(apr_pool_t *pool, util_ldap_state_t *st) +{ +#if APR_HAS_SHARED_MEMORY + apr_status_t result; + apr_size_t size; + + if (st->cache_bytes > 0) { + if (st->cache_file) { + /* Remove any existing shm segment with this name. */ + apr_shm_remove(st->cache_file, st->pool); + } + + size = APR_ALIGN_DEFAULT(st->cache_bytes); + + result = apr_shm_create(&st->cache_shm, size, st->cache_file, st->pool); + if (result != APR_SUCCESS) { + return result; + } + + /* Determine the usable size of the shm segment. */ + size = apr_shm_size_get(st->cache_shm); + + /* This will create a rmm "handler" to get into the shared memory area */ + result = apr_rmm_init(&st->cache_rmm, NULL, + apr_shm_baseaddr_get(st->cache_shm), size, + st->pool); + if (result != APR_SUCCESS) { + return result; + } + } + +#endif + + apr_pool_cleanup_register(st->pool, st , util_ldap_cache_module_kill, apr_pool_cleanup_null); + + st->util_ldap_cache = + util_ald_create_cache(st, + st->search_cache_size, + st->search_cache_ttl, + util_ldap_url_node_hash, + util_ldap_url_node_compare, + util_ldap_url_node_copy, + util_ldap_url_node_free, + util_ldap_url_node_display); + return APR_SUCCESS; +} + + +#endif /* APR_HAS_LDAP */ diff --git a/modules/ldap/util_ldap_cache.h b/modules/ldap/util_ldap_cache.h new file mode 100644 index 0000000..3a98454 --- /dev/null +++ b/modules/ldap/util_ldap_cache.h @@ -0,0 +1,206 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef APU_LDAP_CACHE_H +#define APU_LDAP_CACHE_H + +/** + * @file util_ldap_cache.h + * @brief This switches LDAP support on or off. + */ + +/* this whole thing disappears if LDAP is not enabled */ +#if APR_HAS_LDAP + + +/* + * LDAP Cache Manager + */ + +#include "util_ldap.h" + +typedef struct util_cache_node_t { + void *payload; /* Pointer to the payload */ + apr_time_t add_time; /* Time node was added to cache */ + struct util_cache_node_t *next; +} util_cache_node_t; + +typedef struct util_ald_cache util_ald_cache_t; + +struct util_ald_cache { + unsigned long size; /* Size of cache array */ + unsigned long maxentries; /* Maximum number of cache entries */ + unsigned long numentries; /* Current number of cache entries */ + unsigned long fullmark; /* Used to keep track of when cache becomes 3/4 full */ + apr_time_t marktime; /* Time that the cache became 3/4 full */ + unsigned long ttl; /* Time to live for items in cache */ + unsigned long (*hash)(void *); /* Func to hash the payload */ + int (*compare)(void *, void *); /* Func to compare two payloads */ + void * (*copy)(util_ald_cache_t *cache, void *); /* Func to alloc mem and copy payload to new mem */ + void (*free)(util_ald_cache_t *cache, void *); /* Func to free mem used by the payload */ + void (*display)(request_rec *r, util_ald_cache_t *cache, void *); /* Func to display the payload contents */ + util_cache_node_t **nodes; + + unsigned long numpurges; /* No. of times the cache has been purged */ + double avg_purgetime; /* Average time to purge the cache */ + apr_time_t last_purge; /* Time of the last purge */ + unsigned long npurged; /* Number of elements purged in last purge. This is not + obvious: it won't be 3/4 the size of the cache if + there were a lot of expired entries. */ + + unsigned long fetches; /* Number of fetches */ + unsigned long hits; /* Number of cache hits */ + unsigned long inserts; /* Number of inserts */ + unsigned long removes; /* Number of removes */ + +#if APR_HAS_SHARED_MEMORY + apr_shm_t *shm_addr; + apr_rmm_t *rmm_addr; +#endif + +}; + +#ifndef WIN32 +#define ALD_MM_FILE_MODE ( S_IRUSR|S_IWUSR ) +#else +#define ALD_MM_FILE_MODE ( _S_IREAD|_S_IWRITE ) +#endif + + +/* + * LDAP Cache + */ + +/* + * Maintain a cache of LDAP URLs that the server handles. Each node in + * the cache contains the search cache for that URL, and a compare cache + * for the URL. The compare cash is populated when doing require group + * compares. + */ +typedef struct util_url_node_t { + const char *url; + util_ald_cache_t *search_cache; + util_ald_cache_t *compare_cache; + util_ald_cache_t *dn_compare_cache; +} util_url_node_t; + +/* + * When a group is found, subgroups are stored in the group's cache entry. + */ +typedef struct util_compare_subgroup_t { + const char **subgroupDNs; + int len; +} util_compare_subgroup_t; + +/* + * We cache every successful search and bind operation, using the username + * as the key. Each node in the cache contains the returned DN, plus the + * password used to bind. + */ +typedef struct util_search_node_t { + const char *username; /* Cache key */ + const char *dn; /* DN returned from search */ + const char *bindpw; /* The most recently used bind password; + NULL if the bind failed */ + apr_time_t lastbind; /* Time of last successful bind */ + const char **vals; /* Values of queried attributes */ + int numvals; /* Number of queried attributes */ +} util_search_node_t; + +/* + * We cache every successful compare operation, using the DN, attrib, and + * value as the key. + */ +typedef struct util_compare_node_t { + const char *dn; /* DN, attrib and value combine to be the key */ + const char *attrib; + const char *value; + apr_time_t lastcompare; + int result; + int sgl_processed; /* 0 if no sgl processing yet. 1 if sgl has been processed (even if SGL is NULL). Saves repeat work on leaves. */ + struct util_compare_subgroup_t *subgroupList; +} util_compare_node_t; + +/* + * We cache every successful compare dn operation, using the dn in the require + * statement and the dn fetched based on the client-provided username. + */ +typedef struct util_dn_compare_node_t { + const char *reqdn; /* The DN in the require dn statement */ + const char *dn; /* The DN found in the search */ +} util_dn_compare_node_t; + + +/* + * Function prototypes for LDAP cache + */ + +/* util_ldap_cache.c */ +unsigned long util_ldap_url_node_hash(void *n); +int util_ldap_url_node_compare(void *a, void *b); +void *util_ldap_url_node_copy(util_ald_cache_t *cache, void *c); +void util_ldap_url_node_free(util_ald_cache_t *cache, void *n); +void util_ldap_url_node_display(request_rec *r, util_ald_cache_t *cache, void *n); + +unsigned long util_ldap_search_node_hash(void *n); +int util_ldap_search_node_compare(void *a, void *b); +void *util_ldap_search_node_copy(util_ald_cache_t *cache, void *c); +void util_ldap_search_node_free(util_ald_cache_t *cache, void *n); +void util_ldap_search_node_display(request_rec *r, util_ald_cache_t *cache, void *n); + +unsigned long util_ldap_compare_node_hash(void *n); +int util_ldap_compare_node_compare(void *a, void *b); +void *util_ldap_compare_node_copy(util_ald_cache_t *cache, void *c); +void util_ldap_compare_node_free(util_ald_cache_t *cache, void *n); +void util_ldap_compare_node_display(request_rec *r, util_ald_cache_t *cache, void *n); + +unsigned long util_ldap_dn_compare_node_hash(void *n); +int util_ldap_dn_compare_node_compare(void *a, void *b); +void *util_ldap_dn_compare_node_copy(util_ald_cache_t *cache, void *c); +void util_ldap_dn_compare_node_free(util_ald_cache_t *cache, void *n); +void util_ldap_dn_compare_node_display(request_rec *r, util_ald_cache_t *cache, void *n); + + +/* util_ldap_cache_mgr.c */ + +/* Cache alloc and free function, dealing or not with shm */ +void util_ald_free(util_ald_cache_t *cache, const void *ptr); +void *util_ald_alloc(util_ald_cache_t *cache, unsigned long size); +const char *util_ald_strdup(util_ald_cache_t *cache, const char *s); +util_compare_subgroup_t *util_ald_sgl_dup(util_ald_cache_t *cache, util_compare_subgroup_t *sgl); +void util_ald_sgl_free(util_ald_cache_t *cache, util_compare_subgroup_t **sgl); + +/* Cache managing function */ +unsigned long util_ald_hash_string(int nstr, ...); +void util_ald_cache_purge(util_ald_cache_t *cache); +util_url_node_t *util_ald_create_caches(util_ldap_state_t *s, const char *url); +util_ald_cache_t *util_ald_create_cache(util_ldap_state_t *st, + long cache_size, + long cache_ttl, + unsigned long (*hashfunc)(void *), + int (*comparefunc)(void *, void *), + void * (*copyfunc)(util_ald_cache_t *cache, void *), + void (*freefunc)(util_ald_cache_t *cache, void *), + void (*displayfunc)(request_rec *r, util_ald_cache_t *cache, void *)); + +void util_ald_destroy_cache(util_ald_cache_t *cache); +void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload); +void *util_ald_cache_insert(util_ald_cache_t *cache, void *payload); +void util_ald_cache_remove(util_ald_cache_t *cache, void *payload); +char *util_ald_cache_display_stats(request_rec *r, util_ald_cache_t *cache, char *name, char *id); + +#endif /* APR_HAS_LDAP */ +#endif /* APU_LDAP_CACHE_H */ diff --git a/modules/ldap/util_ldap_cache_mgr.c b/modules/ldap/util_ldap_cache_mgr.c new file mode 100644 index 0000000..9bef3f8 --- /dev/null +++ b/modules/ldap/util_ldap_cache_mgr.c @@ -0,0 +1,901 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * util_ldap_cache_mgr.c: LDAP cache manager things + * + * Original code from auth_ldap module for Apache v1.3: + * Copyright 1998, 1999 Enbridge Pipelines Inc. + * Copyright 1999-2001 Dave Carrigan + */ + +#include "httpd.h" +#include "util_ldap.h" +#include "util_ldap_cache.h" +#include <apr_strings.h> + +APLOG_USE_MODULE(ldap); + +#if APR_HAS_LDAP + +/* only here until strdup is gone */ +#include <string.h> + +/* here till malloc is gone */ +#include <stdlib.h> + +static const unsigned long primes[] = +{ + 11, + 19, + 37, + 73, + 109, + 163, + 251, + 367, + 557, + 823, + 1237, + 1861, + 2777, + 4177, + 6247, + 9371, + 14057, + 21089, + 31627, + 47431, + 71143, + 106721, + 160073, + 240101, + 360163, + 540217, + 810343, + 1215497, + 1823231, + 2734867, + 4102283, + 6153409, + 9230113, + 13845163, + 0 +}; + +void util_ald_free(util_ald_cache_t *cache, const void *ptr) +{ +#if APR_HAS_SHARED_MEMORY + if (cache->rmm_addr) { + if (ptr) + /* Free in shared memory */ + apr_rmm_free(cache->rmm_addr, apr_rmm_offset_get(cache->rmm_addr, (void *)ptr)); + } + else { + if (ptr) + /* Cache shm is not used */ + free((void *)ptr); + } +#else + if (ptr) + free((void *)ptr); +#endif +} + +void *util_ald_alloc(util_ald_cache_t *cache, unsigned long size) +{ + if (0 == size) + return NULL; +#if APR_HAS_SHARED_MEMORY + if (cache->rmm_addr) { + /* allocate from shared memory */ + apr_rmm_off_t block = apr_rmm_calloc(cache->rmm_addr, size); + return block ? (void *)apr_rmm_addr_get(cache->rmm_addr, block) : NULL; + } + else { + /* Cache shm is not used */ + return (void *)calloc(sizeof(char), size); + } +#else + return (void *)calloc(sizeof(char), size); +#endif +} + +const char *util_ald_strdup(util_ald_cache_t *cache, const char *s) +{ +#if APR_HAS_SHARED_MEMORY + if (cache->rmm_addr) { + /* allocate from shared memory */ + apr_rmm_off_t block = apr_rmm_calloc(cache->rmm_addr, strlen(s)+1); + char *buf = block ? (char *)apr_rmm_addr_get(cache->rmm_addr, block) : NULL; + if (buf) { + strcpy(buf, s); + return buf; + } + else { + return NULL; + } + } + else { + /* Cache shm is not used */ + return strdup(s); + } +#else + return strdup(s); +#endif +} + +/* + * Duplicate a subgroupList from one compare entry to another. + * Returns: ptr to a new copy of the subgroupList or NULL if allocation failed. + */ +util_compare_subgroup_t *util_ald_sgl_dup(util_ald_cache_t *cache, util_compare_subgroup_t *sgl_in) +{ + int i = 0; + util_compare_subgroup_t *sgl_out = NULL; + + if (!sgl_in) { + return NULL; + } + + sgl_out = (util_compare_subgroup_t *) util_ald_alloc(cache, sizeof(util_compare_subgroup_t)); + if (sgl_out) { + sgl_out->subgroupDNs = util_ald_alloc(cache, sizeof(char *) * sgl_in->len); + if (sgl_out->subgroupDNs) { + for (i = 0; i < sgl_in->len; i++) { + sgl_out->subgroupDNs[i] = util_ald_strdup(cache, sgl_in->subgroupDNs[i]); + if (!sgl_out->subgroupDNs[i]) { + /* We ran out of SHM, delete the strings we allocated for the SGL */ + for (i = (i - 1); i >= 0; i--) { + util_ald_free(cache, sgl_out->subgroupDNs[i]); + } + util_ald_free(cache, sgl_out->subgroupDNs); + util_ald_free(cache, sgl_out); + sgl_out = NULL; + break; + } + } + /* We were able to allocate new strings for all the subgroups */ + if (sgl_out != NULL) { + sgl_out->len = sgl_in->len; + } + } + } + + return sgl_out; +} + +/* + * Delete an entire subgroupList. + */ +void util_ald_sgl_free(util_ald_cache_t *cache, util_compare_subgroup_t **sgl) +{ + int i = 0; + if (sgl == NULL || *sgl == NULL) { + return; + } + + for (i = 0; i < (*sgl)->len; i++) { + util_ald_free(cache, (*sgl)->subgroupDNs[i]); + } + util_ald_free(cache, *sgl); +} + +/* + * Computes the hash on a set of strings. The first argument is the number + * of strings to hash, the rest of the args are strings. + * Algorithm taken from glibc. + */ +unsigned long util_ald_hash_string(int nstr, ...) +{ + int i; + va_list args; + unsigned long h=0, g; + char *str, *p; + + va_start(args, nstr); + for (i=0; i < nstr; ++i) { + str = va_arg(args, char *); + for (p = str; *p; ++p) { + h = ( h << 4 ) + *p; + if ( ( g = h & 0xf0000000 ) ) { + h = h ^ (g >> 24); + h = h ^ g; + } + } + } + va_end(args); + + return h; +} + + +/* + Purges a cache that has gotten full. We keep track of the time that we + added the entry that made the cache 3/4 full, then delete all entries + that were added before that time. It's pretty simplistic, but time to + purge is only O(n), which is more important. +*/ +void util_ald_cache_purge(util_ald_cache_t *cache) +{ + unsigned long i; + util_cache_node_t *p, *q, **pp; + apr_time_t now; + + if (!cache) + return; + + now = cache->last_purge = apr_time_now(); + cache->npurged = 0; + cache->numpurges++; + + /* If the marktime is farther back than TTL from now, + move the marktime forward to include additional expired entries. + */ + if (now - cache->ttl > cache->marktime) { + cache->marktime = now - cache->ttl; + } + + for (i=0; i < cache->size; ++i) { + pp = cache->nodes + i; + p = *pp; + while (p != NULL) { + if (p->add_time < cache->marktime) { + q = p->next; + (*cache->free)(cache, p->payload); + util_ald_free(cache, p); + cache->numentries--; + cache->npurged++; + p = *pp = q; + } + else { + pp = &(p->next); + p = *pp; + } + } + } + + now = apr_time_now(); + cache->avg_purgetime = + ((now - cache->last_purge) + (cache->avg_purgetime * (cache->numpurges-1))) / + cache->numpurges; +} + + +/* + * create caches + */ +util_url_node_t *util_ald_create_caches(util_ldap_state_t *st, const char *url) +{ + util_url_node_t curl, *newcurl = NULL; + util_ald_cache_t *search_cache; + util_ald_cache_t *compare_cache; + util_ald_cache_t *dn_compare_cache; + + /* create the three caches */ + search_cache = util_ald_create_cache(st, + st->search_cache_size, + st->search_cache_ttl, + util_ldap_search_node_hash, + util_ldap_search_node_compare, + util_ldap_search_node_copy, + util_ldap_search_node_free, + util_ldap_search_node_display); + compare_cache = util_ald_create_cache(st, + st->compare_cache_size, + st->compare_cache_ttl, + util_ldap_compare_node_hash, + util_ldap_compare_node_compare, + util_ldap_compare_node_copy, + util_ldap_compare_node_free, + util_ldap_compare_node_display); + dn_compare_cache = util_ald_create_cache(st, + st->compare_cache_size, + st->compare_cache_ttl, + util_ldap_dn_compare_node_hash, + util_ldap_dn_compare_node_compare, + util_ldap_dn_compare_node_copy, + util_ldap_dn_compare_node_free, + util_ldap_dn_compare_node_display); + + /* check that all the caches initialised successfully */ + if (search_cache && compare_cache && dn_compare_cache) { + + /* The contents of this structure will be duplicated in shared + memory during the insert. So use stack memory rather than + pool memory to avoid a memory leak. */ + memset (&curl, 0, sizeof(util_url_node_t)); + curl.url = url; + curl.search_cache = search_cache; + curl.compare_cache = compare_cache; + curl.dn_compare_cache = dn_compare_cache; + + newcurl = util_ald_cache_insert(st->util_ldap_cache, &curl); + + } + + return newcurl; +} + + +util_ald_cache_t *util_ald_create_cache(util_ldap_state_t *st, + long cache_size, + long cache_ttl, + unsigned long (*hashfunc)(void *), + int (*comparefunc)(void *, void *), + void * (*copyfunc)(util_ald_cache_t *cache, void *), + void (*freefunc)(util_ald_cache_t *cache, void *), + void (*displayfunc)(request_rec *r, util_ald_cache_t *cache, void *)) +{ + util_ald_cache_t *cache; + unsigned long i; +#if APR_HAS_SHARED_MEMORY + apr_rmm_off_t block; +#endif + + if (cache_size <= 0) + return NULL; + +#if APR_HAS_SHARED_MEMORY + if (!st->cache_rmm) { + cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1); + } + else { + block = apr_rmm_calloc(st->cache_rmm, sizeof(util_ald_cache_t)); + cache = block ? (util_ald_cache_t *)apr_rmm_addr_get(st->cache_rmm, block) : NULL; + } +#else + cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1); +#endif + if (!cache) + return NULL; + +#if APR_HAS_SHARED_MEMORY + cache->rmm_addr = st->cache_rmm; + cache->shm_addr = st->cache_shm; +#endif + cache->maxentries = cache_size; + cache->numentries = 0; + cache->size = cache_size / 3; + if (cache->size < 64) + cache->size = 64; + for (i = 0; primes[i] && primes[i] < cache->size; ++i) + ; + cache->size = primes[i] ? primes[i] : primes[i-1]; + + cache->nodes = (util_cache_node_t **)util_ald_alloc(cache, cache->size * sizeof(util_cache_node_t *)); + if (!cache->nodes) { + /* This frees cache in the right way even if !APR_HAS_SHARED_MEMORY or !st->cache_rmm */ + util_ald_free(cache, cache); + return NULL; + } + + for (i=0; i < cache->size; ++i) + cache->nodes[i] = NULL; + + cache->hash = hashfunc; + cache->compare = comparefunc; + cache->copy = copyfunc; + cache->free = freefunc; + cache->display = displayfunc; + + + cache->fullmark = cache->maxentries / 4 * 3; + cache->marktime = 0; + cache->ttl = cache_ttl; + cache->avg_purgetime = 0.0; + cache->numpurges = 0; + cache->last_purge = 0; + cache->npurged = 0; + + cache->fetches = 0; + cache->hits = 0; + cache->inserts = 0; + cache->removes = 0; + + return cache; +} + +void util_ald_destroy_cache(util_ald_cache_t *cache) +{ + unsigned long i; + util_cache_node_t *p, *q; + + if (cache == NULL) + return; + + for (i = 0; i < cache->size; ++i) { + p = cache->nodes[i]; + q = NULL; + while (p != NULL) { + q = p->next; + (*cache->free)(cache, p->payload); + util_ald_free(cache, p); + p = q; + } + } + util_ald_free(cache, cache->nodes); + util_ald_free(cache, cache); +} + +void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload) +{ + unsigned long hashval; + util_cache_node_t *p; + + if (cache == NULL) + return NULL; + + cache->fetches++; + + hashval = (*cache->hash)(payload) % cache->size; + + for (p = cache->nodes[hashval]; + p && !(*cache->compare)(p->payload, payload); + p = p->next) ; + + if (p != NULL) { + cache->hits++; + return p->payload; + } + else { + return NULL; + } +} + +/* + * Insert an item into the cache. + * *** Does not catch duplicates!!! *** + */ +void *util_ald_cache_insert(util_ald_cache_t *cache, void *payload) +{ + unsigned long hashval; + void *tmp_payload; + util_cache_node_t *node; + + /* sanity check */ + if (cache == NULL || payload == NULL) { + return NULL; + } + + /* check if we are full - if so, try purge */ + if (cache->numentries >= cache->maxentries) { + util_ald_cache_purge(cache); + if (cache->numentries >= cache->maxentries) { + /* if the purge was not effective, we leave now to avoid an overflow */ + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01323) + "Purge of LDAP cache failed"); + return NULL; + } + } + + node = (util_cache_node_t *)util_ald_alloc(cache, + sizeof(util_cache_node_t)); + if (node == NULL) { + /* + * XXX: The cache management should be rewritten to work + * properly when LDAPSharedCacheSize is too small. + */ + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(01324) + "LDAPSharedCacheSize is too small. Increase it or " + "reduce LDAPCacheEntries/LDAPOpCacheEntries!"); + if (cache->numentries < cache->fullmark) { + /* + * We have not even reached fullmark, trigger a complete purge. + * This is still better than not being able to add new entries + * at all. + */ + cache->marktime = apr_time_now(); + } + util_ald_cache_purge(cache); + node = (util_cache_node_t *)util_ald_alloc(cache, + sizeof(util_cache_node_t)); + if (node == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01325) + "Could not allocate memory for LDAP cache entry"); + return NULL; + } + } + + /* Take a copy of the payload before proceeding. */ + tmp_payload = (*cache->copy)(cache, payload); + if (tmp_payload == NULL) { + /* + * XXX: The cache management should be rewritten to work + * properly when LDAPSharedCacheSize is too small. + */ + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(01326) + "LDAPSharedCacheSize is too small. Increase it or " + "reduce LDAPCacheEntries/LDAPOpCacheEntries!"); + if (cache->numentries < cache->fullmark) { + /* + * We have not even reached fullmark, trigger a complete purge. + * This is still better than not being able to add new entries + * at all. + */ + cache->marktime = apr_time_now(); + } + util_ald_cache_purge(cache); + tmp_payload = (*cache->copy)(cache, payload); + if (tmp_payload == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01327) + "Could not allocate memory for LDAP cache value"); + util_ald_free(cache, node); + return NULL; + } + } + payload = tmp_payload; + + /* populate the entry */ + cache->inserts++; + hashval = (*cache->hash)(payload) % cache->size; + node->add_time = apr_time_now(); + node->payload = payload; + node->next = cache->nodes[hashval]; + cache->nodes[hashval] = node; + + /* if we reach the full mark, note the time we did so + * for the benefit of the purge function + */ + if (++cache->numentries == cache->fullmark) { + cache->marktime=apr_time_now(); + } + + return node->payload; +} + +void util_ald_cache_remove(util_ald_cache_t *cache, void *payload) +{ + unsigned long hashval; + util_cache_node_t *p, *q; + + if (cache == NULL) + return; + + cache->removes++; + hashval = (*cache->hash)(payload) % cache->size; + for (p = cache->nodes[hashval], q=NULL; + p && !(*cache->compare)(p->payload, payload); + p = p->next) { + q = p; + } + + /* If p is null, it means that we couldn't find the node, so just return */ + if (p == NULL) + return; + + if (q == NULL) { + /* We found the node, and it's the first in the list */ + cache->nodes[hashval] = p->next; + } + else { + /* We found the node and it's not the first in the list */ + q->next = p->next; + } + (*cache->free)(cache, p->payload); + util_ald_free(cache, p); + cache->numentries--; +} + +char *util_ald_cache_display_stats(request_rec *r, util_ald_cache_t *cache, char *name, char *id) +{ + unsigned long i; + int totchainlen = 0; + int nchains = 0; + double chainlen; + util_cache_node_t *n; + char *buf, *buf2; + apr_pool_t *p = r->pool; + + if (cache == NULL) { + return ""; + } + + for (i=0; i < cache->size; ++i) { + if (cache->nodes[i] != NULL) { + nchains++; + for (n = cache->nodes[i]; + n != NULL && n != n->next; + n = n->next) { + totchainlen++; + } + } + } + chainlen = nchains? (double)totchainlen / (double)nchains : 0; + + if (id) { + buf2 = apr_psprintf(p, + "<a href=\"%s?%s\">%s</a>", + ap_escape_html(r->pool, ap_escape_uri(r->pool, r->uri)), + id, + name); + } + else { + buf2 = name; + } + + buf = apr_psprintf(p, + "<tr valign='top'>" + "<td nowrap>%s</td>" + "<td align='right' nowrap>%lu (%.0f%% full)</td>" + "<td align='right'>%.1f</td>" + "<td align='right'>%lu/%lu</td>" + "<td align='right'>%.0f%%</td>" + "<td align='right'>%lu/%lu</td>", + buf2, + cache->numentries, + (double)cache->numentries / (double)cache->maxentries * 100.0, + chainlen, + cache->hits, + cache->fetches, + (cache->fetches > 0 ? (double)(cache->hits) / (double)(cache->fetches) * 100.0 : 100.0), + cache->inserts, + cache->removes); + + if (cache->numpurges) { + char str_ctime[APR_CTIME_LEN]; + + apr_ctime(str_ctime, cache->last_purge); + buf = apr_psprintf(p, + "%s" + "<td align='right'>%lu</td>\n" + "<td align='right' nowrap>%s</td>\n", + buf, + cache->numpurges, + str_ctime); + } + else { + buf = apr_psprintf(p, + "%s<td colspan='2' align='center'>(none)</td>\n", + buf); + } + + buf = apr_psprintf(p, "%s<td align='right'>%.2gms</td>\n</tr>", buf, cache->avg_purgetime); + + return buf; +} + +char *util_ald_cache_display(request_rec *r, util_ldap_state_t *st) +{ + unsigned long i,j; + char *buf, *t1, *t2, *t3; + char *id1, *id2, *id3; + char *argfmt = "cache=%s&id=%d&off=%d"; + char *scanfmt = "cache=%4s&id=%u&off=%u%1s"; + apr_pool_t *pool = r->pool; + util_cache_node_t *p = NULL; + util_url_node_t *n = NULL; + + util_ald_cache_t *util_ldap_cache = st->util_ldap_cache; + + + if (!util_ldap_cache) { + ap_rputs("<tr valign='top'><td nowrap colspan=7>Cache has not been enabled/initialised.</td></tr>", r); + return NULL; + } + + if (r->args && strlen(r->args)) { + char cachetype[5], lint[2]; + unsigned int id, off; + char date_str[APR_CTIME_LEN]; + + if ((3 == sscanf(r->args, scanfmt, cachetype, &id, &off, lint)) && + (id < util_ldap_cache->size)) { + + if ((p = util_ldap_cache->nodes[id]) != NULL) { + n = (util_url_node_t *)p->payload; + buf = (char*)n->url; + } + else { + buf = ""; + } + + ap_rprintf(r, + "<p>\n" + "<table border='0'>\n" + "<tr>\n" + "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name:</b></font></td>" + "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s (%s)</b></font></td>" + "</tr>\n" + "</table>\n</p>\n", + buf, + cachetype[0] == 'm'? "Main" : + (cachetype[0] == 's' ? "Search" : + (cachetype[0] == 'c' ? "Compares" : "DNCompares"))); + + switch (cachetype[0]) { + case 'm': + if (util_ldap_cache->marktime) { + apr_ctime(date_str, util_ldap_cache->marktime); + } + else + date_str[0] = 0; + + ap_rprintf(r, + "<p>\n" + "<table border='0'>\n" + "<tr>\n" + "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size:</b></font></td>" + "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>" + "</tr>\n" + "<tr>\n" + "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries:</b></font></td>" + "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>" + "</tr>\n" + "<tr>\n" + "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries:</b></font></td>" + "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>" + "</tr>\n" + "<tr>\n" + "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>TTL (sec):</b></font></td>" + "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%" APR_TIME_T_FMT "</b></font></td>" + "</tr>\n" + "<tr>\n" + "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark:</b></font></td>" + "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>" + "</tr>\n" + "<tr>\n" + "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time:</b></font></td>" + "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s</b></font></td>" + "</tr>\n" + "</table>\n</p>\n", + util_ldap_cache->size, + util_ldap_cache->maxentries, + util_ldap_cache->numentries, + apr_time_sec(util_ldap_cache->ttl), + util_ldap_cache->fullmark, + date_str); + + ap_rputs("<p>\n" + "<table border='0'>\n" + "<tr bgcolor='#000000'>\n" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP URL</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>TTL (sec)</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time</b></font></td>" + "</tr>\n", r + ); + for (i=0; i < util_ldap_cache->size; ++i) { + for (p = util_ldap_cache->nodes[i]; p != NULL; p = p->next) { + + (*util_ldap_cache->display)(r, util_ldap_cache, p->payload); + } + } + ap_rputs("</table>\n</p>\n", r); + + + break; + case 's': + ap_rputs("<p>\n" + "<table border='0'>\n" + "<tr bgcolor='#000000'>\n" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP Filter</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>User Name</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Bind</b></font></td>" + "</tr>\n", r + ); + if (n) { + for (i=0; i < n->search_cache->size; ++i) { + for (p = n->search_cache->nodes[i]; p != NULL; p = p->next) { + + (*n->search_cache->display)(r, n->search_cache, p->payload); + } + } + } + ap_rputs("</table>\n</p>\n", r); + break; + case 'c': + ap_rputs("<p>\n" + "<table border='0'>\n" + "<tr bgcolor='#000000'>\n" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>DN</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Attribute</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Value</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Compare</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Result</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Sub-groups?</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>S-G Checked?</b></font></td>" + "</tr>\n", r + ); + if (n) { + for (i=0; i < n->compare_cache->size; ++i) { + for (p = n->compare_cache->nodes[i]; p != NULL; p = p->next) { + + (*n->compare_cache->display)(r, n->compare_cache, p->payload); + } + } + } + ap_rputs("</table>\n</p>\n", r); + break; + case 'd': + ap_rputs("<p>\n" + "<table border='0'>\n" + "<tr bgcolor='#000000'>\n" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Require DN</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Actual DN</b></font></td>" + "</tr>\n", r + ); + if (n) { + for (i=0; i < n->dn_compare_cache->size; ++i) { + for (p = n->dn_compare_cache->nodes[i]; p != NULL; p = p->next) { + + (*n->dn_compare_cache->display)(r, n->dn_compare_cache, p->payload); + } + } + } + ap_rputs("</table>\n</p>\n", r); + break; + default: + break; + } + + } + else { + buf = ""; + } + } + else { + ap_rputs("<p>\n" + "<table border='0'>\n" + "<tr bgcolor='#000000'>\n" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Entries</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg. Chain Len.</b></font></td>" + "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Hits</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Ins/Rem</b></font></td>" + "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Purges</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg Purge Time</b></font></td>" + "</tr>\n", r + ); + + + id1 = apr_psprintf(pool, argfmt, "main", 0, 0); + buf = util_ald_cache_display_stats(r, st->util_ldap_cache, "LDAP URL Cache", id1); + + for (i=0; i < util_ldap_cache->size; ++i) { + for (p = util_ldap_cache->nodes[i],j=0; p != NULL; p = p->next,j++) { + + n = (util_url_node_t *)p->payload; + + t1 = apr_psprintf(pool, "%s (Searches)", n->url); + t2 = apr_psprintf(pool, "%s (Compares)", n->url); + t3 = apr_psprintf(pool, "%s (DNCompares)", n->url); + id1 = apr_psprintf(pool, argfmt, "srch", i, j); + id2 = apr_psprintf(pool, argfmt, "cmpr", i, j); + id3 = apr_psprintf(pool, argfmt, "dncp", i, j); + + buf = apr_psprintf(pool, "%s\n\n" + "%s\n\n" + "%s\n\n" + "%s\n\n", + buf, + util_ald_cache_display_stats(r, n->search_cache, t1, id1), + util_ald_cache_display_stats(r, n->compare_cache, t2, id2), + util_ald_cache_display_stats(r, n->dn_compare_cache, t3, id3) + ); + } + } + ap_rputs(buf, r); + ap_rputs("</table>\n</p>\n", r); + } + + return buf; +} + +#endif /* APR_HAS_LDAP */ |