You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Lubomir Rintel <lu...@gooddata.com> on 2013/07/12 00:37:14 UTC

[PATCH 55178] mod_authnz_ldap SASL authentication support

Hi,

I'm have submitted the following-up patches adding SASL authentication to LDAP 
modules. Some wise person on an IRC channel suggested that I'm breaking API and
it's a good idea to take this to the list as an extra work might be needed.

Would someone be kind enought to take a look at the patches and let me know what
would need to be chaged for them to be accepted into httpd tree?

Thank you!
Lubo

--
Lubomir Rintel, GoodData random furniture movement coordinator


Re: [PATCH 55178] mod_authnz_ldap SASL authentication support

Posted by Stefan Fritsch <sf...@sfritsch.de>.
Hi Lubomir,

On Friday 12 July 2013, Lubomir Rintel wrote:
> I'm have submitted the following-up patches adding SASL
> authentication to LDAP modules. Some wise person on an IRC channel
> suggested that I'm breaking API and it's a good idea to take this
> to the list as an extra work might be needed.
> 
> Would someone be kind enought to take a look at the patches and let
> me know what would need to be chaged for them to be accepted into
> httpd tree?

if the change should go into 2.4 (as opposed to 2.6 or 3.0), it must 
not modify the API/ABI in an incompatible way. This means that you may 
only add new members at the end of public structs (see 
util_ldap_connection_t), and that you may not remove functions. So you 
have to keep the old uldap_connection_find() interface available, and 
add a new uldap_connection_find_ex() interface that supports sasl.

Another problem is that you use additional ldap functions like 
ldap_sasl_interactive_bind_s() that may not be available in some LDAP 
libraries (I assume you use openldap). The correct way would be to add 
configure tests for those functions and use #ifdef in the code to 
ensure that it compiles with other LDAP libraries (or even old 
versions of openldap).

Another issue is the use of a synchronus function 
ldap_sasl_interactive_bind_s().  We have removed any use of 
ldap_simple_bind_s() because not all ldap libraries support setting a 
timeout for that operation, which caused significant problems in 
practice. Either the code needs to be changed to use 
ldap_sasl_interactive_bind() instead of the _s function, or it should 
only be compiled in if LDAP_OPT_TIMEOUT is available. In openldap this 
has been introduced in version 2.4.4. But given the compatibility 
problems between ldap libraries, I would lean towards only enabling 
the new code on openldap >= 2.4.4 anyway, and support other ldap 
libraries only when they have been actually tested to work.

Note that I haven't done an in-depth review of the code and don't have 
time for that at the moment. In general httpd developers are rather 
busy. So even if you do the suggested changes, it could take quite 
some time until the patches get reviewed and included.

Cheers,
Stefan

[PATCH 55178 1/2] mod_authn_ldap: Allow authentication with SASL

Posted by Lubomir Rintel <lu...@gooddata.com>.
---
 docs/manual/mod/mod_authnz_ldap.xml   |   34 +++++++++++-
 docs/manual/style/scripts/prettify.js |    2 +-
 include/util_ldap.h                   |    5 +-
 modules/aaa/mod_authnz_ldap.c         |   14 ++++-
 modules/ldap/util_ldap.c              |   92 ++++++++++++++++++++++-----------
 5 files changed, 110 insertions(+), 37 deletions(-)

diff --git a/docs/manual/mod/mod_authnz_ldap.xml b/docs/manual/mod/mod_authnz_ldap.xml
index 358115f..85e626c 100644
--- a/docs/manual/mod/mod_authnz_ldap.xml
+++ b/docs/manual/mod/mod_authnz_ldap.xml
@@ -183,6 +183,14 @@ for HTTP Basic authentication.</description>
         <td>An optional password to bind
         with during the search phase.</td>
       </tr>
+
+      <tr>
+        <td><directive
+        module="mod_authnz_ldap">AuthLDAPBindSASLMech</directive></td>
+
+        <td>An optional SASL mechanism to use for bind
+        with during the search phase.</td>
+      </tr>
     </table>
 </section>
 
@@ -890,8 +898,8 @@ to perform a DN lookup</description>
 
 <usage>
     <p>An optional DN used to bind to the server when searching for
-    entries. If not provided, <module>mod_authnz_ldap</module> will use
-    an anonymous bind.</p>
+    entries. If not provided, and simple bind (not SASL) is used,
+    <module>mod_authnz_ldap</module> will use an anonymous bind.</p>
 </usage>
 </directivesynopsis>
 
@@ -929,6 +937,28 @@ AuthLDAPBindPassword "exec:/path/to/otherProgram argument1"
 </directivesynopsis>
 
 <directivesynopsis>
+<name>AuthLDAPBindSASLMech</name>
+<description>Optional SASL mechanism to use in binding to the LDAP server</description>
+<syntax>AuthLDAPBindSASLMech <em>sasl-mech</em></syntax>
+<contextlist><context>directory</context><context>.htaccess</context>
+</contextlist>
+<override>AuthConfig</override>
+
+<usage>
+    <p>An optional SASL mechanism used to bind to the server when
+    searching for entries. Multiple mechanisms can be used,
+    separated with commas. If not provided,
+    <module>mod_authnz_ldap</module> will use simple bind.</p>
+
+<example><pre>
+#Authenticate with Kerberos GSSAPI
+AuthLDAPBindSASLMech "GSSAPI"
+</pre></example>
+
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
 <name>AuthLDAPCharsetConfig</name>
 <description>Language to charset conversion configuration file</description>
 <syntax>AuthLDAPCharsetConfig <em>file-path</em></syntax>
diff --git a/docs/manual/style/scripts/prettify.js b/docs/manual/style/scripts/prettify.js
index a27abe4..97c69c1 100644
--- a/docs/manual/style/scripts/prettify.js
+++ b/docs/manual/style/scripts/prettify.js
@@ -132,7 +132,7 @@ var prettyPrint;
   var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," +
       "function,in,local,set,then,until,echo"];
   var CONFIG_ENVS = ["User-Agent,HTTP_USER_AGENT,HTTP_REFERER,HTTP_COOKIE,HTTP_FORWARDED,HTTP_HOST,HTTP_PROXY_CONNECTION,HTTP_ACCEPT,REMOTE_ADDR,REMOTE_HOST,REMOTE_PORT,REMOTE_USER,REMOTE_IDENT,REQUEST_METHOD,SCRIPT_FILENAME,PATH_INFO,QUERY_STRING,AUTH_TYPE,DOCUMENT_ROOT,SERVER_ADMIN,SERVER_NAME,SERVER_ADDR,SERVER_PORT,SERVER_PROTOCOL,SERVER_SOFTWARE,TIME_YEAR,TIME_MON,TIME_DAY,TIME_HOUR,TIME_MIN,TIME_SEC,TIME_WDAY,TIME,API_VERSION,THE_REQUEST,REQUEST_URI,REQUEST_FILENAME,IS_SUBREQ,HTTPS,REQUEST_SCHEME"];
-  var CONFIG_KEYWORDS = ["Macro,UndefMacro,Use,AuthLDAPURL,AcceptFilter,AcceptPathInfo,AccessFileName,Action,AddAlt,AddAltByEncoding,AddAltByType,AddCharset,AddDefaultCharset,AddDescription,AddEncoding,AddHandler,AddIcon,AddIconByEncoding,AddIconByType,AddInputFilter,AddLanguage,AddModuleInfo,AddOutputFilter,AddOutputFilterByType,AddType,Alias,AliasMatch,Allow,AllowCONNECT,AllowEncodedSlashes,AllowMethods,AllowOverride,AllowOverrideList,Anonymous,Anonymous_LogEmail,Anonymous_MustGiveEmail,Anonymous_NoUserID,Anonymous_VerifyEmail,AsyncRequestWorkerFactor,AuthBasicAuthoritative,AuthBasicProvider,AuthDBDUserPWQuery,AuthDBDUserRealmQuery,AuthDBMGroupFile,AuthDBMType,AuthDBMUserFile,AuthDigestAlgorithm,AuthDigestDomain,AuthDigestNcCheck,AuthDigestNonceFormat,AuthDigestNonceLifetime,AuthDigestProvider,AuthDigestQop,AuthDigestShmemSize,AuthFormAuthoritative,AuthFormBody,AuthFormDisableNoStore,AuthFormFakeBasicAuth,AuthFormLocation,AuthFormLoginRequiredLocation,AuthFormLoginSuccessLocation,
 AuthFormLogoutLocation,AuthFormMethod,AuthFormMimetype,AuthFormPassword,AuthFormProvider,AuthFormSitePassphrase,AuthFormSize,AuthFormUsername,AuthGroupFile,AuthLDAPAuthorizePrefix,AuthLDAPBindAuthoritative,AuthLDAPBindDN,AuthLDAPBindPassword,AuthLDAPCharsetConfig,AuthLDAPCompareAsUser,AuthLDAPCompareDNOnServer,AuthLDAPDereferenceAliases,AuthLDAPGroupAttribute,AuthLDAPGroupAttributeIsDN,AuthLDAPInitialBindAsUser,AuthLDAPInitialBindPattern,AuthLDAPMaxSubGroupDepth,AuthLDAPRemoteUserAttribute,AuthLDAPRemoteUserIsDN,AuthLDAPSearchAsUser,AuthLDAPSubGroupAttribute,AuthLDAPSubGroupClass,AuthLDAPUrl,AuthMerging,AuthName,AuthnCacheContext,AuthnCacheEnable,AuthnCacheProvideFor,AuthnCacheSOCache,AuthnCacheTimeout,<AuthnProviderAlias>,AuthType,AuthUserFile,AuthzDBDLoginToReferer,AuthzDBDQuery,AuthzDBDRedirectQuery,AuthzDBMType,<AuthzProviderAlias>,AuthzSendForbiddenOnFailure,BalancerGrowth,BalancerMember,BrowserMatch,BrowserMatchNoCase,BufferedLogs,BufferSize,CacheDefaultExpire,CacheDetailHeade
 r,CacheDirLength,CacheDirLevels,CacheDisable,CacheEnable,CacheFile,CacheHeader,CacheIgnoreCacheControl,CacheIgnoreHeaders,CacheIgnoreNoLastMod,CacheIgnoreQueryString,CacheIgnoreURLSessionIdentifiers,CacheKeyBaseURL,CacheLastModifiedFactor,CacheLock,CacheLockMaxAge,CacheLockPath,CacheMaxExpire,CacheMaxFileSize,CacheMinExpire,CacheMinFileSize,CacheNegotiatedDocs,CacheQuickHandler,CacheReadSize,CacheReadTime,CacheRoot,CacheStaleOnError,CacheStoreExpired,CacheStoreNoStore,CacheStorePrivate,CGIMapExtension,CharsetDefault,CharsetOptions,CharsetSourceEnc,CheckCaseOnly,CheckSpelling,ChrootDir,ContentDigest,CookieDomain,CookieExpires,CookieName,CookieStyle,CookieTracking,CoreDumpDirectory,CustomLog,Dav,DavDepthInfinity,DavGenericLockDB,DavLockDB,DavMinTimeout,DBDExptime,DBDInitSQL,DBDKeep,DBDMax,DBDMin,DBDParams,DBDPersist,DBDPrepareSQL,DBDriver,DefaultIcon,DefaultLanguage,DefaultRuntimeDir,DefaultType,Define,DeflateBufferSize,DeflateCompressionLevel,DeflateFilterNote,DeflateMemLevel,Deflate
 WindowSize,Deny,<Directory>,DirectoryIndex,DirectoryIndexRedirect,<DirectoryMatch>,DirectorySlash,DocumentRoot,DTracePrivileges,DumpIOInput,DumpIOOutput,<Else>,<ElseIf>,EnableExceptionHook,EnableMMAP,EnableSendfile,Error,ErrorDocument,ErrorLog,ErrorLogFormat,Example,ExpiresActive,ExpiresByType,ExpiresDefault,ExtendedStatus,ExtFilterDefine,ExtFilterOptions,FallbackResource,FileETag,<Files>,<FilesMatch>,FilterChain,FilterDeclare,FilterProtocol,FilterProvider,FilterTrace,ForceLanguagePriority,ForceType,ForensicLog,GprofDir,GracefulShutdownTimeout,Group,Header,HeaderName,HeartbeatAddress,HeartbeatListen,HeartbeatMaxServers,HeartbeatStorage,HeartbeatStorage,HostnameLookups,IdentityCheck,IdentityCheckTimeout,<If>,<IfDefine>,<IfModule>,<IfVersion>,ImapBase,ImapDefault,ImapMenu,Include,IncludeOptional,IndexHeadInsert,IndexIgnore,IndexIgnoreReset,IndexOptions,IndexOrderDefault,IndexStyleSheet,InputSed,ISAPIAppendLogToErrors,ISAPIAppendLogToQuery,ISAPICacheFile,ISAPIFakeAsync,ISAPILogNotSuppo
 rted,ISAPIReadAheadBuffer,KeepAlive,KeepAliveTimeout,KeptBodySize,LanguagePriority,LDAPCacheEntries,LDAPCacheTTL,LDAPConnectionPoolTTL,LDAPConnectionTimeout,LDAPLibraryDebug,LDAPOpCacheEntries,LDAPOpCacheTTL,LDAPReferralHopLimit,LDAPReferrals,LDAPRetries,LDAPRetryDelay,LDAPSharedCacheFile,LDAPSharedCacheSize,LDAPTimeout,LDAPTrustedClientCert,LDAPTrustedGlobalCert,LDAPTrustedMode,LDAPVerifyServerCert,<Limit>,<LimitExcept>,LimitInternalRecursion,LimitRequestBody,LimitRequestFields,LimitRequestFieldSize,LimitRequestLine,LimitXMLRequestBody,Listen,ListenBackLog,LoadFile,LoadModule,<Location>,<LocationMatch>,LogFormat,LogLevel,LogMessage,LuaCodeCache,LuaHookAccessChecker,LuaHookAuthChecker,LuaAuthzProvider,LuaHookCheckUserID,LuaHookFixups,LuaHookInsertFilter,LuaHookMapToStorage,LuaHookTranslateName,LuaHookTypeChecker,LuaInherit,LuaInputFilter,LuaMapHandler,LuaOutputFilter,LuaPackageCPath,LuaPackagePath,LuaQuickHandler,LuaRoot,LuaScope,MaxConnectionsPerChild,MaxKeepAliveRequests,MaxMemFre
 e,MaxRangeOverlaps,MaxRangeReversals,MaxRanges,MaxRequestWorkers,MaxSpareServers,MaxSpareThreads,MaxThreads,MetaDir,MetaFiles,MetaSuffix,MimeMagicFile,MinSpareServers,MinSpareThreads,MMapFile,ModemStandard,ModMimeUsePathInfo,MultiviewsMatch,Mutex,NameVirtualHost,NoProxy,NWSSLTrustedCerts,NWSSLUpgradeable,Options,Order,OutputSed,PassEnv,PidFile,PrivilegesMode,Protocol,ProtocolEcho,<Proxy>,ProxyAddHeaders,ProxyBadHeader,ProxyBlock,ProxyDomain,ProxyErrorOverride,ProxyExpressDBMFile,ProxyExpressDBMType,ProxyExpressEnable,ProxyFtpDirCharset,ProxyFtpEscapeWildcards,ProxyFtpListOnWildcard,ProxyHTMLBufSize,ProxyHTMLCharsetOut,ProxyHTMLDocType,ProxyHTMLEnable,ProxyHTMLEvents,ProxyHTMLExtended,ProxyHTMLFixups,ProxyHTMLInterp,ProxyHTMLLinks,ProxyHTMLStripComments,ProxyHTMLURLMap,ProxyIOBufferSize,<ProxyMatch>,ProxyMaxForwards,ProxyPass,ProxyPassInterpolateEnv,ProxyPassMatch,ProxyPassReverse,ProxyPassReverseCookieDomain,ProxyPassReverseCookiePath,ProxyPreserveHost,ProxyReceiveBufferSize,ProxyRe
 mote,ProxyRemoteMatch,ProxyRequests,ProxySCGIInternalRedirect,ProxySCGISendfile,ProxySet,ProxySourceAddress,ProxyStatus,ProxyTimeout,ProxyVia,ReadmeName,ReceiveBufferSize,Redirect,RedirectMatch,RedirectPermanent,RedirectTemp,ReflectorHeader,RemoteIPHeader,RemoteIPInternalProxy,RemoteIPInternalProxyList,RemoteIPProxiesHeader,RemoteIPTrustedProxy,RemoteIPTrustedProxyList,RemoveCharset,RemoveEncoding,RemoveHandler,RemoveInputFilter,RemoveLanguage,RemoveOutputFilter,RemoveType,RequestHeader,RequestReadTimeout,Require,<RequireAll>,<RequireAny>,<RequireNone>,RewriteBase,RewriteCond,RewriteEngine,RewriteMap,RewriteOptions,RewriteRule,RLimitCPU,RLimitMEM,RLimitNPROC,Satisfy,ScoreBoardFile,Script,ScriptAlias,ScriptAliasMatch,ScriptInterpreterSource,ScriptLog,ScriptLogBuffer,ScriptLogLength,ScriptSock,SecureListen,SeeRequestTail,SendBufferSize,ServerAdmin,ServerAlias,ServerLimit,ServerName,ServerPath,ServerRoot,ServerSignature,ServerTokens,Session,SessionCookieName,SessionCookieName2,SessionC
 ookieRemove,SessionCryptoCipher,SessionCryptoDriver,SessionCryptoPassphrase,SessionCryptoPassphraseFile,SessionDBDCookieName,SessionDBDCookieName2,SessionDBDCookieRemove,SessionDBDDeleteLabel,SessionDBDInsertLabel,SessionDBDPerUser,SessionDBDSelectLabel,SessionDBDUpdateLabel,SessionEnv,SessionExclude,SessionHeader,SessionInclude,SessionMaxAge,SetEnv,SetEnvIf,SetEnvIfExpr,SetEnvIfNoCase,SetHandler,SetInputFilter,SetOutputFilter,SSIEndTag,SSIErrorMsg,SSIETag,SSILastModified,SSILegacyExprParser,SSIStartTag,SSITimeFormat,SSIUndefinedEcho,SSLCACertificateFile,SSLCACertificatePath,SSLCADNRequestFile,SSLCADNRequestPath,SSLCARevocationCheck,SSLCARevocationFile,SSLCARevocationPath,SSLCertificateChainFile,SSLCertificateFile,SSLCertificateKeyFile,SSLCipherSuite,SSLCryptoDevice,SSLEngine,SSLFIPS,SSLHonorCipherOrder,SSLInsecureRenegotiation,SSLOCSPDefaultResponder,SSLOCSPEnable,SSLOCSPOverrideResponder,SSLOCSPResponderTimeout,SSLOCSPResponseMaxAge,SSLOCSPResponseTimeSkew,SSLOptions,SSLPassPhrase
 Dialog,SSLProtocol,SSLProxyCACertificateFile,SSLProxyCACertificatePath,SSLProxyCARevocationCheck,SSLProxyCARevocationFile,SSLProxyCARevocationPath,SSLProxyCheckPeerCN,SSLProxyCheckPeerExpire,SSLProxyCipherSuite,SSLProxyEngine,SSLProxyMachineCertificateChainFile,SSLProxyMachineCertificateFile,SSLProxyMachineCertificatePath,SSLProxyProtocol,SSLProxyVerify,SSLProxyVerifyDepth,SSLRandomSeed,SSLRenegBufferSize,SSLRequire,SSLRequireSSL,SSLSessionCache,SSLSessionCacheTimeout,SSLSessionTicketKeyFile,SSLStaplingCache,SSLStaplingErrorCacheTimeout,SSLStaplingFakeTryLater,SSLStaplingForceURL,SSLStaplingResponderTimeout,SSLStaplingResponseMaxAge,SSLStaplingResponseTimeSkew,SSLStaplingReturnResponderErrors,SSLStaplingStandardCacheTimeout,SSLStrictSNIVHostCheck,SSLUserName,SSLUseStapling,SSLVerifyClient,SSLVerifyDepth,StartServers,StartThreads,Substitute,Suexec,SuexecUserGroup,ThreadLimit,ThreadsPerChild,ThreadStackSize,TimeOut,TraceEnable,TransferLog,TypesConfig,UnDefine,UnsetEnv,UseCanonicalName
 ,UseCanonicalPhysicalPort,User,UserDir,VHostCGIMode,VHostCGIPrivs,VHostGroup,VHostPrivs,VHostSecure,VHostUser,VirtualDocumentRoot,VirtualDocumentRootIP,<VirtualHost>,VirtualScriptAlias,VirtualScriptAliasIP,WatchdogInterval,XBitHack,xml2EncAlias,xml2EncDefault,xml2StartParse,RewriteLog,RewriteLogLevel"];
+  var CONFIG_KEYWORDS = ["Macro,UndefMacro,Use,AuthLDAPURL,AcceptFilter,AcceptPathInfo,AccessFileName,Action,AddAlt,AddAltByEncoding,AddAltByType,AddCharset,AddDefaultCharset,AddDescription,AddEncoding,AddHandler,AddIcon,AddIconByEncoding,AddIconByType,AddInputFilter,AddLanguage,AddModuleInfo,AddOutputFilter,AddOutputFilterByType,AddType,Alias,AliasMatch,Allow,AllowCONNECT,AllowEncodedSlashes,AllowMethods,AllowOverride,AllowOverrideList,Anonymous,Anonymous_LogEmail,Anonymous_MustGiveEmail,Anonymous_NoUserID,Anonymous_VerifyEmail,AsyncRequestWorkerFactor,AuthBasicAuthoritative,AuthBasicProvider,AuthDBDUserPWQuery,AuthDBDUserRealmQuery,AuthDBMGroupFile,AuthDBMType,AuthDBMUserFile,AuthDigestAlgorithm,AuthDigestDomain,AuthDigestNcCheck,AuthDigestNonceFormat,AuthDigestNonceLifetime,AuthDigestProvider,AuthDigestQop,AuthDigestShmemSize,AuthFormAuthoritative,AuthFormBody,AuthFormDisableNoStore,AuthFormFakeBasicAuth,AuthFormLocation,AuthFormLoginRequiredLocation,AuthFormLoginSuccessLocation,
 AuthFormLogoutLocation,AuthFormMethod,AuthFormMimetype,AuthFormPassword,AuthFormProvider,AuthFormSitePassphrase,AuthFormSize,AuthFormUsername,AuthGroupFile,AuthLDAPAuthorizePrefix,AuthLDAPBindAuthoritative,AuthLDAPBindDN,AuthLDAPBindPassword,AuthLDAPBindSASLMech,AuthLDAPCharsetConfig,AuthLDAPCompareAsUser,AuthLDAPCompareDNOnServer,AuthLDAPDereferenceAliases,AuthLDAPGroupAttribute,AuthLDAPGroupAttributeIsDN,AuthLDAPInitialBindAsUser,AuthLDAPInitialBindPattern,AuthLDAPMaxSubGroupDepth,AuthLDAPRemoteUserAttribute,AuthLDAPRemoteUserIsDN,AuthLDAPSearchAsUser,AuthLDAPSubGroupAttribute,AuthLDAPSubGroupClass,AuthLDAPUrl,AuthMerging,AuthName,AuthnCacheContext,AuthnCacheEnable,AuthnCacheProvideFor,AuthnCacheSOCache,AuthnCacheTimeout,<AuthnProviderAlias>,AuthType,AuthUserFile,AuthzDBDLoginToReferer,AuthzDBDQuery,AuthzDBDRedirectQuery,AuthzDBMType,<AuthzProviderAlias>,AuthzSendForbiddenOnFailure,BalancerGrowth,BalancerMember,BrowserMatch,BrowserMatchNoCase,BufferedLogs,BufferSize,CacheDefaultEx
 pire,CacheDetailHeader,CacheDirLength,CacheDirLevels,CacheDisable,CacheEnable,CacheFile,CacheHeader,CacheIgnoreCacheControl,CacheIgnoreHeaders,CacheIgnoreNoLastMod,CacheIgnoreQueryString,CacheIgnoreURLSessionIdentifiers,CacheKeyBaseURL,CacheLastModifiedFactor,CacheLock,CacheLockMaxAge,CacheLockPath,CacheMaxExpire,CacheMaxFileSize,CacheMinExpire,CacheMinFileSize,CacheNegotiatedDocs,CacheQuickHandler,CacheReadSize,CacheReadTime,CacheRoot,CacheStaleOnError,CacheStoreExpired,CacheStoreNoStore,CacheStorePrivate,CGIMapExtension,CharsetDefault,CharsetOptions,CharsetSourceEnc,CheckCaseOnly,CheckSpelling,ChrootDir,ContentDigest,CookieDomain,CookieExpires,CookieName,CookieStyle,CookieTracking,CoreDumpDirectory,CustomLog,Dav,DavDepthInfinity,DavGenericLockDB,DavLockDB,DavMinTimeout,DBDExptime,DBDInitSQL,DBDKeep,DBDMax,DBDMin,DBDParams,DBDPersist,DBDPrepareSQL,DBDriver,DefaultIcon,DefaultLanguage,DefaultRuntimeDir,DefaultType,Define,DeflateBufferSize,DeflateCompressionLevel,DeflateFilterNote,De
 flateMemLevel,DeflateWindowSize,Deny,<Directory>,DirectoryIndex,DirectoryIndexRedirect,<DirectoryMatch>,DirectorySlash,DocumentRoot,DTracePrivileges,DumpIOInput,DumpIOOutput,<Else>,<ElseIf>,EnableExceptionHook,EnableMMAP,EnableSendfile,Error,ErrorDocument,ErrorLog,ErrorLogFormat,Example,ExpiresActive,ExpiresByType,ExpiresDefault,ExtendedStatus,ExtFilterDefine,ExtFilterOptions,FallbackResource,FileETag,<Files>,<FilesMatch>,FilterChain,FilterDeclare,FilterProtocol,FilterProvider,FilterTrace,ForceLanguagePriority,ForceType,ForensicLog,GprofDir,GracefulShutdownTimeout,Group,Header,HeaderName,HeartbeatAddress,HeartbeatListen,HeartbeatMaxServers,HeartbeatStorage,HeartbeatStorage,HostnameLookups,IdentityCheck,IdentityCheckTimeout,<If>,<IfDefine>,<IfModule>,<IfVersion>,ImapBase,ImapDefault,ImapMenu,Include,IncludeOptional,IndexHeadInsert,IndexIgnore,IndexIgnoreReset,IndexOptions,IndexOrderDefault,IndexStyleSheet,InputSed,ISAPIAppendLogToErrors,ISAPIAppendLogToQuery,ISAPICacheFile,ISAPIFakeA
 sync,ISAPILogNotSupported,ISAPIReadAheadBuffer,KeepAlive,KeepAliveTimeout,KeptBodySize,LanguagePriority,LDAPCacheEntries,LDAPCacheTTL,LDAPConnectionPoolTTL,LDAPConnectionTimeout,LDAPLibraryDebug,LDAPOpCacheEntries,LDAPOpCacheTTL,LDAPReferralHopLimit,LDAPReferrals,LDAPRetries,LDAPRetryDelay,LDAPSharedCacheFile,LDAPSharedCacheSize,LDAPTimeout,LDAPTrustedClientCert,LDAPTrustedGlobalCert,LDAPTrustedMode,LDAPVerifyServerCert,<Limit>,<LimitExcept>,LimitInternalRecursion,LimitRequestBody,LimitRequestFields,LimitRequestFieldSize,LimitRequestLine,LimitXMLRequestBody,Listen,ListenBackLog,LoadFile,LoadModule,<Location>,<LocationMatch>,LogFormat,LogLevel,LogMessage,LuaCodeCache,LuaHookAccessChecker,LuaHookAuthChecker,LuaAuthzProvider,LuaHookCheckUserID,LuaHookFixups,LuaHookInsertFilter,LuaHookMapToStorage,LuaHookTranslateName,LuaHookTypeChecker,LuaInherit,LuaInputFilter,LuaMapHandler,LuaOutputFilter,LuaPackageCPath,LuaPackagePath,LuaQuickHandler,LuaRoot,LuaScope,MaxConnectionsPerChild,MaxKeepAl
 iveRequests,MaxMemFree,MaxRangeOverlaps,MaxRangeReversals,MaxRanges,MaxRequestWorkers,MaxSpareServers,MaxSpareThreads,MaxThreads,MetaDir,MetaFiles,MetaSuffix,MimeMagicFile,MinSpareServers,MinSpareThreads,MMapFile,ModemStandard,ModMimeUsePathInfo,MultiviewsMatch,Mutex,NameVirtualHost,NoProxy,NWSSLTrustedCerts,NWSSLUpgradeable,Options,Order,OutputSed,PassEnv,PidFile,PrivilegesMode,Protocol,ProtocolEcho,<Proxy>,ProxyAddHeaders,ProxyBadHeader,ProxyBlock,ProxyDomain,ProxyErrorOverride,ProxyExpressDBMFile,ProxyExpressDBMType,ProxyExpressEnable,ProxyFtpDirCharset,ProxyFtpEscapeWildcards,ProxyFtpListOnWildcard,ProxyHTMLBufSize,ProxyHTMLCharsetOut,ProxyHTMLDocType,ProxyHTMLEnable,ProxyHTMLEvents,ProxyHTMLExtended,ProxyHTMLFixups,ProxyHTMLInterp,ProxyHTMLLinks,ProxyHTMLStripComments,ProxyHTMLURLMap,ProxyIOBufferSize,<ProxyMatch>,ProxyMaxForwards,ProxyPass,ProxyPassInterpolateEnv,ProxyPassMatch,ProxyPassReverse,ProxyPassReverseCookieDomain,ProxyPassReverseCookiePath,ProxyPreserveHost,ProxyRece
 iveBufferSize,ProxyRemote,ProxyRemoteMatch,ProxyRequests,ProxySCGIInternalRedirect,ProxySCGISendfile,ProxySet,ProxySourceAddress,ProxyStatus,ProxyTimeout,ProxyVia,ReadmeName,ReceiveBufferSize,Redirect,RedirectMatch,RedirectPermanent,RedirectTemp,ReflectorHeader,RemoteIPHeader,RemoteIPInternalProxy,RemoteIPInternalProxyList,RemoteIPProxiesHeader,RemoteIPTrustedProxy,RemoteIPTrustedProxyList,RemoveCharset,RemoveEncoding,RemoveHandler,RemoveInputFilter,RemoveLanguage,RemoveOutputFilter,RemoveType,RequestHeader,RequestReadTimeout,Require,<RequireAll>,<RequireAny>,<RequireNone>,RewriteBase,RewriteCond,RewriteEngine,RewriteMap,RewriteOptions,RewriteRule,RLimitCPU,RLimitMEM,RLimitNPROC,Satisfy,ScoreBoardFile,Script,ScriptAlias,ScriptAliasMatch,ScriptInterpreterSource,ScriptLog,ScriptLogBuffer,ScriptLogLength,ScriptSock,SecureListen,SeeRequestTail,SendBufferSize,ServerAdmin,ServerAlias,ServerLimit,ServerName,ServerPath,ServerRoot,ServerSignature,ServerTokens,Session,SessionCookieName,Sessio
 nCookieName2,SessionCookieRemove,SessionCryptoCipher,SessionCryptoDriver,SessionCryptoPassphrase,SessionCryptoPassphraseFile,SessionDBDCookieName,SessionDBDCookieName2,SessionDBDCookieRemove,SessionDBDDeleteLabel,SessionDBDInsertLabel,SessionDBDPerUser,SessionDBDSelectLabel,SessionDBDUpdateLabel,SessionEnv,SessionExclude,SessionHeader,SessionInclude,SessionMaxAge,SetEnv,SetEnvIf,SetEnvIfExpr,SetEnvIfNoCase,SetHandler,SetInputFilter,SetOutputFilter,SSIEndTag,SSIErrorMsg,SSIETag,SSILastModified,SSILegacyExprParser,SSIStartTag,SSITimeFormat,SSIUndefinedEcho,SSLCACertificateFile,SSLCACertificatePath,SSLCADNRequestFile,SSLCADNRequestPath,SSLCARevocationCheck,SSLCARevocationFile,SSLCARevocationPath,SSLCertificateChainFile,SSLCertificateFile,SSLCertificateKeyFile,SSLCipherSuite,SSLCryptoDevice,SSLEngine,SSLFIPS,SSLHonorCipherOrder,SSLInsecureRenegotiation,SSLOCSPDefaultResponder,SSLOCSPEnable,SSLOCSPOverrideResponder,SSLOCSPResponderTimeout,SSLOCSPResponseMaxAge,SSLOCSPResponseTimeSkew,SSL
 Options,SSLPassPhraseDialog,SSLProtocol,SSLProxyCACertificateFile,SSLProxyCACertificatePath,SSLProxyCARevocationCheck,SSLProxyCARevocationFile,SSLProxyCARevocationPath,SSLProxyCheckPeerCN,SSLProxyCheckPeerExpire,SSLProxyCipherSuite,SSLProxyEngine,SSLProxyMachineCertificateChainFile,SSLProxyMachineCertificateFile,SSLProxyMachineCertificatePath,SSLProxyProtocol,SSLProxyVerify,SSLProxyVerifyDepth,SSLRandomSeed,SSLRenegBufferSize,SSLRequire,SSLRequireSSL,SSLSessionCache,SSLSessionCacheTimeout,SSLSessionTicketKeyFile,SSLStaplingCache,SSLStaplingErrorCacheTimeout,SSLStaplingFakeTryLater,SSLStaplingForceURL,SSLStaplingResponderTimeout,SSLStaplingResponseMaxAge,SSLStaplingResponseTimeSkew,SSLStaplingReturnResponderErrors,SSLStaplingStandardCacheTimeout,SSLStrictSNIVHostCheck,SSLUserName,SSLUseStapling,SSLVerifyClient,SSLVerifyDepth,StartServers,StartThreads,Substitute,Suexec,SuexecUserGroup,ThreadLimit,ThreadsPerChild,ThreadStackSize,TimeOut,TraceEnable,TransferLog,TypesConfig,UnDefine,Unse
 tEnv,UseCanonicalName,UseCanonicalPhysicalPort,User,UserDir,VHostCGIMode,VHostCGIPrivs,VHostGroup,VHostPrivs,VHostSecure,VHostUser,VirtualDocumentRoot,VirtualDocumentRootIP,<VirtualHost>,VirtualScriptAlias,VirtualScriptAliasIP,WatchdogInterval,XBitHack,xml2EncAlias,xml2EncDefault,xml2StartParse,RewriteLog,RewriteLogLevel"];
   var CONFIG_OPTIONS = /^[\\+\\-]?(AuthConfig|IncludesNOEXEC|ExecCGI|FollowSymLinks|MultiViews|Includes|Indexes|SymLinksIfOwnerMatch)\b/i;
   var ALL_KEYWORDS = [
       CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS +
diff --git a/include/util_ldap.h b/include/util_ldap.h
index 3d5faed..60123b3 100644
--- a/include/util_ldap.h
+++ b/include/util_ldap.h
@@ -113,6 +113,7 @@ typedef struct util_ldap_connection_t {
 
     const char *binddn;                 /* DN to bind to server (can be NULL) */
     const char *bindpw;                 /* Password to bind to server (can be NULL) */
+    const char *bindsaslmech;           /* SASL Mechanism to use for server bind (can be NULL) */
 
     int bound;                          /* Flag to indicate whether this connection is bound yet */
 
@@ -238,8 +239,8 @@ APR_DECLARE_OPTIONAL_FN(apr_status_t,uldap_connection_unbind,(void *param));
  *                                                           int netscapessl, int starttls)
  */
 APR_DECLARE_OPTIONAL_FN(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));
+                                                  const char *binddn, const char *bindpw, const char *bindsaslmech,
+                                                  deref_options deref, int secure));
 
 /**
  * Compare two DNs for sameness
diff --git a/modules/aaa/mod_authnz_ldap.c b/modules/aaa/mod_authnz_ldap.c
index b1c5740..cdf06f4 100644
--- a/modules/aaa/mod_authnz_ldap.c
+++ b/modules/aaa/mod_authnz_ldap.c
@@ -59,6 +59,7 @@ typedef struct {
     deref_options deref;            /* how to handle alias dereferening */
     char *binddn;                   /* DN to bind to server (can be NULL) */
     char *bindpw;                   /* Password to bind to server (can be NULL) */
+    char *bindsaslmech;             /* SASL Mechanism to use for server bind (can be NULL) */
     int bind_authoritative;         /* If true, will return errors when bind fails */
 
     int user_is_dn;                 /* If true, connection->user is DN instead of userid */
@@ -344,6 +345,7 @@ static void *create_authnz_ldap_dir_config(apr_pool_t *p, char *d)
     sec->host = NULL;
     sec->binddn = NULL;
     sec->bindpw = NULL;
+    sec->bindsaslmech = NULL;
     sec->bind_authoritative = 1;
     sec->deref = always;
     sec->group_attrib_is_dn = 1;
@@ -439,6 +441,7 @@ static util_ldap_connection_t *get_connection_for_authz(request_rec *r, enum aut
 
     char *binddn = sec->binddn;
     char *bindpw = sec->bindpw;
+    char *bindsaslmech = sec->bindsaslmech;
 
     /* If the per-request config isn't set, we didn't authenticate this user, and leave the default credentials */
     if (req && req->password &&
@@ -447,10 +450,11 @@ static util_ldap_connection_t *get_connection_for_authz(request_rec *r, enum aut
           (type == LDAP_COMPARE_AND_SEARCH && sec->compare_as_user && sec->search_as_user))){
             binddn = req->dn;
             bindpw = req->password;
+            bindsaslmech = NULL;
     }
 
     return util_ldap_connection_find(r, sec->host, sec->port,
-                                     binddn, bindpw,
+                                     binddn, bindpw, bindsaslmech,
                                      sec->deref, sec->secure);
 }
 /*
@@ -497,13 +501,15 @@ static authn_status authn_ldap_check_password(request_rec *r, const char *user,
     if (sec->host) {
         const char *binddn = sec->binddn;
         const char *bindpw = sec->bindpw;
+        const char *bindsaslmech = sec->bindsaslmech;
         if (sec->initial_bind_as_user) {
             bindpw = password;
             binddn = ldap_determine_binddn(r, user);
+            bindsaslmech = NULL;
         }
 
         ldc = util_ldap_connection_find(r, sec->host, sec->port,
-                                       binddn, bindpw,
+                                       binddn, bindpw, bindsaslmech,
                                        sec->deref, sec->secure);
     }
     else {
@@ -1621,6 +1627,10 @@ static const command_rec authnz_ldap_cmds[] =
     AP_INIT_TAKE1("AuthLDAPBindPassword", set_bind_password, NULL, OR_AUTHCFG,
                   "Password to use to bind to LDAP server. If not provided, will do an anonymous bind."),
 
+    AP_INIT_TAKE1("AuthLDAPBindSASLMech", ap_set_string_slot,
+                  (void *)APR_OFFSETOF(authn_ldap_config_t, bindsaslmech), OR_AUTHCFG,
+                  "SASL Mechanism to use to bind to LDAP server. If not provided, simple authentication will be done."),
+
     AP_INIT_FLAG("AuthLDAPBindAuthoritative", ap_set_flag_slot,
                   (void *)APR_OFFSETOF(authn_ldap_config_t, bind_authoritative), OR_AUTHCFG,
                   "Set to 'on' to return failures when user-specific bind fails - defaults to on."),
diff --git a/modules/ldap/util_ldap.c b/modules/ldap/util_ldap.c
index da0db6e..535c12a 100644
--- a/modules/ldap/util_ldap.c
+++ b/modules/ldap/util_ldap.c
@@ -490,39 +490,67 @@ static int uldap_ld_errno(util_ldap_connection_t *ldc)
 }
 
 /*
+ * SASL credentials conversation function. Does nothing really useful yet,
+ * is around just because it is required.
+ *
+ * Always returns LDAP_SUCCESS
+ */
+
+static int uldap_sasl_interact(LDAP *ld,
+                               unsigned flags,
+                               void *defaults,
+                               void *sasl_interact)
+{
+    return LDAP_SUCCESS;
+}
+
+/*
  * 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)
+static int uldap_bind(util_ldap_connection_t *ldc, char *binddn,
+                      char* bindpw, char *bindsaslmech,
+                      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 { 
-        ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, ldc->r, "LDC %pp bind", ldc);
-    }
+
+    if (bindsaslmech) {
+        rc = ldap_sasl_interactive_bind_s(ldc->ldap, binddn, bindsaslmech,
+                                          NULL, NULL, LDAP_SASL_QUIET,
+                                          uldap_sasl_interact, NULL);
+        if (rc == -1) {
+            ldc->reason = "LDAP: ldap_sasl_interactive_bind_s() failed";
+            /* -1 is LDAP_SERVER_DOWN in openldap, use something else */
+            return uldap_ld_errno(ldc);
+        }
+    } else {
+        LDAPMessage *result;
+        int msgid;
+
+        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);
+        }
+    }
+
+    ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, ldc->r, "LDC %pp bind", ldc);
     return rc;
 }
 
@@ -585,8 +613,8 @@ static int uldap_connection_open(request_rec *r,
         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);
+        rc = uldap_bind(ldc, (char *)ldc->binddn, (char *)ldc->bindpw,
+                        (char *)ldc->bindsaslmech, st->opTimeout);
 
         if (rc == LDAP_SUCCESS) break;
 
@@ -625,7 +653,7 @@ static int uldap_connection_open(request_rec *r,
     if (LDAP_SUCCESS != rc)
     {
         uldap_connection_unbind(ldc);
-        ldc->reason = "LDAP: ldap_simple_bind() failed";
+        ldc->reason = "LDAP: bind failed";
     }
     else {
         ldc->bound = 1;
@@ -690,6 +718,7 @@ static util_ldap_connection_t *
             uldap_connection_find(request_rec *r,
                                   const char *host, int port,
                                   const char *binddn, const char *bindpw,
+                                  const char *bindsaslmech,
                                   deref_options deref, int secure)
 {
     struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
@@ -723,6 +752,8 @@ static util_ldap_connection_t *
                                              && !strcmp(l->binddn, binddn)))
             && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
                                              && !strcmp(l->bindpw, bindpw)))
+            && ((!l->bindsaslmech && !bindsaslmech) || (l->bindsaslmech && bindsaslmech
+                                             && !strcmp(l->bindsaslmech, bindsaslmech)))
             && (l->deref == deref) && (l->secure == secureflag)
             && !compare_client_certs(dc->client_certs, l->client_certs))
         {
@@ -782,7 +813,7 @@ static util_ldap_connection_t *
                 l->must_rebind = 1;
                 util_ldap_strdup((char**)&(l->binddn), binddn);
                 util_ldap_strdup((char**)&(l->bindpw), bindpw);
-
+                util_ldap_strdup((char**)&(l->bindsaslmech), bindsaslmech);
                 break;
             }
 #if APR_HAS_THREADS
@@ -834,6 +865,7 @@ static util_ldap_connection_t *
         l->deref = deref;
         util_ldap_strdup((char**)&(l->binddn), binddn);
         util_ldap_strdup((char**)&(l->bindpw), bindpw);
+        util_ldap_strdup((char**)&(l->bindsaslmech), bindsaslmech);
         l->ChaseReferrals = dc->ChaseReferrals;
         l->ReferralHopLimit = dc->ReferralHopLimit;
 
@@ -1779,8 +1811,8 @@ start_over:
      * 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);
+    result = uldap_bind(ldc, (char *)*binddn, (char *)bindpw, NULL,
+                        st->opTimeout);
     if (AP_LDAP_IS_SERVER_DOWN(result) ||
         (result == LDAP_TIMEOUT && failures == 0)) {
         if (AP_LDAP_IS_SERVER_DOWN(result))
-- 
1.7.1


[PATCH 55178 2/2] mod_authn_ldap: Allow specifying SASL interaction

Posted by Lubomir Rintel <lu...@gooddata.com>.
---
 docs/manual/mod/mod_authnz_ldap.xml   |   28 ++++++++++++++
 docs/manual/style/scripts/prettify.js |    2 +-
 include/util_ldap.h                   |    4 +-
 modules/aaa/mod_authnz_ldap.c         |   16 +++++++-
 modules/ldap/util_ldap.c              |   67 +++++++++++++++++++++++++++-----
 5 files changed, 102 insertions(+), 15 deletions(-)

diff --git a/docs/manual/mod/mod_authnz_ldap.xml b/docs/manual/mod/mod_authnz_ldap.xml
index 85e626c..a76882c 100644
--- a/docs/manual/mod/mod_authnz_ldap.xml
+++ b/docs/manual/mod/mod_authnz_ldap.xml
@@ -191,6 +191,14 @@ for HTTP Basic authentication.</description>
         <td>An optional SASL mechanism to use for bind
         with during the search phase.</td>
       </tr>
+
+      <tr>
+        <td><directive
+        module="mod_authnz_ldap">AuthLDAPBindSASLInteract</directive></td>
+
+        <td>An optional command to run when SASL
+        requests interaction to obtain credentials.</td>
+      </tr>
     </table>
 </section>
 
@@ -959,6 +967,26 @@ AuthLDAPBindSASLMech "GSSAPI"
 </directivesynopsis>
 
 <directivesynopsis>
+<name>AuthLDAPBindSASLInteract</name>
+<description>Optional command to run when SASL requests interaction to obtain credentials</description>
+<syntax>AuthLDAPBindSASLInteract <em>command</em></syntax>
+<contextlist><context>directory</context><context>.htaccess</context>
+</contextlist>
+<override>AuthConfig</override>
+
+<usage>
+    <p>An optional command to run when SASL
+    requests interaction to obtain credentials.</p>
+
+<example><pre>
+#Initialize Kerberos Credentials Cache using a key from a keytab for given principal
+AuthLDAPBindSASLInteract "/usr/bin/kinit -k -t /etc/httpd/conf/krb5.keytab host/example.com"
+</pre></example>
+
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
 <name>AuthLDAPCharsetConfig</name>
 <description>Language to charset conversion configuration file</description>
 <syntax>AuthLDAPCharsetConfig <em>file-path</em></syntax>
diff --git a/docs/manual/style/scripts/prettify.js b/docs/manual/style/scripts/prettify.js
index 97c69c1..5b0c803 100644
--- a/docs/manual/style/scripts/prettify.js
+++ b/docs/manual/style/scripts/prettify.js
@@ -132,7 +132,7 @@ var prettyPrint;
   var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," +
       "function,in,local,set,then,until,echo"];
   var CONFIG_ENVS = ["User-Agent,HTTP_USER_AGENT,HTTP_REFERER,HTTP_COOKIE,HTTP_FORWARDED,HTTP_HOST,HTTP_PROXY_CONNECTION,HTTP_ACCEPT,REMOTE_ADDR,REMOTE_HOST,REMOTE_PORT,REMOTE_USER,REMOTE_IDENT,REQUEST_METHOD,SCRIPT_FILENAME,PATH_INFO,QUERY_STRING,AUTH_TYPE,DOCUMENT_ROOT,SERVER_ADMIN,SERVER_NAME,SERVER_ADDR,SERVER_PORT,SERVER_PROTOCOL,SERVER_SOFTWARE,TIME_YEAR,TIME_MON,TIME_DAY,TIME_HOUR,TIME_MIN,TIME_SEC,TIME_WDAY,TIME,API_VERSION,THE_REQUEST,REQUEST_URI,REQUEST_FILENAME,IS_SUBREQ,HTTPS,REQUEST_SCHEME"];
-  var CONFIG_KEYWORDS = ["Macro,UndefMacro,Use,AuthLDAPURL,AcceptFilter,AcceptPathInfo,AccessFileName,Action,AddAlt,AddAltByEncoding,AddAltByType,AddCharset,AddDefaultCharset,AddDescription,AddEncoding,AddHandler,AddIcon,AddIconByEncoding,AddIconByType,AddInputFilter,AddLanguage,AddModuleInfo,AddOutputFilter,AddOutputFilterByType,AddType,Alias,AliasMatch,Allow,AllowCONNECT,AllowEncodedSlashes,AllowMethods,AllowOverride,AllowOverrideList,Anonymous,Anonymous_LogEmail,Anonymous_MustGiveEmail,Anonymous_NoUserID,Anonymous_VerifyEmail,AsyncRequestWorkerFactor,AuthBasicAuthoritative,AuthBasicProvider,AuthDBDUserPWQuery,AuthDBDUserRealmQuery,AuthDBMGroupFile,AuthDBMType,AuthDBMUserFile,AuthDigestAlgorithm,AuthDigestDomain,AuthDigestNcCheck,AuthDigestNonceFormat,AuthDigestNonceLifetime,AuthDigestProvider,AuthDigestQop,AuthDigestShmemSize,AuthFormAuthoritative,AuthFormBody,AuthFormDisableNoStore,AuthFormFakeBasicAuth,AuthFormLocation,AuthFormLoginRequiredLocation,AuthFormLoginSuccessLocation,
 AuthFormLogoutLocation,AuthFormMethod,AuthFormMimetype,AuthFormPassword,AuthFormProvider,AuthFormSitePassphrase,AuthFormSize,AuthFormUsername,AuthGroupFile,AuthLDAPAuthorizePrefix,AuthLDAPBindAuthoritative,AuthLDAPBindDN,AuthLDAPBindPassword,AuthLDAPBindSASLMech,AuthLDAPCharsetConfig,AuthLDAPCompareAsUser,AuthLDAPCompareDNOnServer,AuthLDAPDereferenceAliases,AuthLDAPGroupAttribute,AuthLDAPGroupAttributeIsDN,AuthLDAPInitialBindAsUser,AuthLDAPInitialBindPattern,AuthLDAPMaxSubGroupDepth,AuthLDAPRemoteUserAttribute,AuthLDAPRemoteUserIsDN,AuthLDAPSearchAsUser,AuthLDAPSubGroupAttribute,AuthLDAPSubGroupClass,AuthLDAPUrl,AuthMerging,AuthName,AuthnCacheContext,AuthnCacheEnable,AuthnCacheProvideFor,AuthnCacheSOCache,AuthnCacheTimeout,<AuthnProviderAlias>,AuthType,AuthUserFile,AuthzDBDLoginToReferer,AuthzDBDQuery,AuthzDBDRedirectQuery,AuthzDBMType,<AuthzProviderAlias>,AuthzSendForbiddenOnFailure,BalancerGrowth,BalancerMember,BrowserMatch,BrowserMatchNoCase,BufferedLogs,BufferSize,CacheDefaultEx
 pire,CacheDetailHeader,CacheDirLength,CacheDirLevels,CacheDisable,CacheEnable,CacheFile,CacheHeader,CacheIgnoreCacheControl,CacheIgnoreHeaders,CacheIgnoreNoLastMod,CacheIgnoreQueryString,CacheIgnoreURLSessionIdentifiers,CacheKeyBaseURL,CacheLastModifiedFactor,CacheLock,CacheLockMaxAge,CacheLockPath,CacheMaxExpire,CacheMaxFileSize,CacheMinExpire,CacheMinFileSize,CacheNegotiatedDocs,CacheQuickHandler,CacheReadSize,CacheReadTime,CacheRoot,CacheStaleOnError,CacheStoreExpired,CacheStoreNoStore,CacheStorePrivate,CGIMapExtension,CharsetDefault,CharsetOptions,CharsetSourceEnc,CheckCaseOnly,CheckSpelling,ChrootDir,ContentDigest,CookieDomain,CookieExpires,CookieName,CookieStyle,CookieTracking,CoreDumpDirectory,CustomLog,Dav,DavDepthInfinity,DavGenericLockDB,DavLockDB,DavMinTimeout,DBDExptime,DBDInitSQL,DBDKeep,DBDMax,DBDMin,DBDParams,DBDPersist,DBDPrepareSQL,DBDriver,DefaultIcon,DefaultLanguage,DefaultRuntimeDir,DefaultType,Define,DeflateBufferSize,DeflateCompressionLevel,DeflateFilterNote,De
 flateMemLevel,DeflateWindowSize,Deny,<Directory>,DirectoryIndex,DirectoryIndexRedirect,<DirectoryMatch>,DirectorySlash,DocumentRoot,DTracePrivileges,DumpIOInput,DumpIOOutput,<Else>,<ElseIf>,EnableExceptionHook,EnableMMAP,EnableSendfile,Error,ErrorDocument,ErrorLog,ErrorLogFormat,Example,ExpiresActive,ExpiresByType,ExpiresDefault,ExtendedStatus,ExtFilterDefine,ExtFilterOptions,FallbackResource,FileETag,<Files>,<FilesMatch>,FilterChain,FilterDeclare,FilterProtocol,FilterProvider,FilterTrace,ForceLanguagePriority,ForceType,ForensicLog,GprofDir,GracefulShutdownTimeout,Group,Header,HeaderName,HeartbeatAddress,HeartbeatListen,HeartbeatMaxServers,HeartbeatStorage,HeartbeatStorage,HostnameLookups,IdentityCheck,IdentityCheckTimeout,<If>,<IfDefine>,<IfModule>,<IfVersion>,ImapBase,ImapDefault,ImapMenu,Include,IncludeOptional,IndexHeadInsert,IndexIgnore,IndexIgnoreReset,IndexOptions,IndexOrderDefault,IndexStyleSheet,InputSed,ISAPIAppendLogToErrors,ISAPIAppendLogToQuery,ISAPICacheFile,ISAPIFakeA
 sync,ISAPILogNotSupported,ISAPIReadAheadBuffer,KeepAlive,KeepAliveTimeout,KeptBodySize,LanguagePriority,LDAPCacheEntries,LDAPCacheTTL,LDAPConnectionPoolTTL,LDAPConnectionTimeout,LDAPLibraryDebug,LDAPOpCacheEntries,LDAPOpCacheTTL,LDAPReferralHopLimit,LDAPReferrals,LDAPRetries,LDAPRetryDelay,LDAPSharedCacheFile,LDAPSharedCacheSize,LDAPTimeout,LDAPTrustedClientCert,LDAPTrustedGlobalCert,LDAPTrustedMode,LDAPVerifyServerCert,<Limit>,<LimitExcept>,LimitInternalRecursion,LimitRequestBody,LimitRequestFields,LimitRequestFieldSize,LimitRequestLine,LimitXMLRequestBody,Listen,ListenBackLog,LoadFile,LoadModule,<Location>,<LocationMatch>,LogFormat,LogLevel,LogMessage,LuaCodeCache,LuaHookAccessChecker,LuaHookAuthChecker,LuaAuthzProvider,LuaHookCheckUserID,LuaHookFixups,LuaHookInsertFilter,LuaHookMapToStorage,LuaHookTranslateName,LuaHookTypeChecker,LuaInherit,LuaInputFilter,LuaMapHandler,LuaOutputFilter,LuaPackageCPath,LuaPackagePath,LuaQuickHandler,LuaRoot,LuaScope,MaxConnectionsPerChild,MaxKeepAl
 iveRequests,MaxMemFree,MaxRangeOverlaps,MaxRangeReversals,MaxRanges,MaxRequestWorkers,MaxSpareServers,MaxSpareThreads,MaxThreads,MetaDir,MetaFiles,MetaSuffix,MimeMagicFile,MinSpareServers,MinSpareThreads,MMapFile,ModemStandard,ModMimeUsePathInfo,MultiviewsMatch,Mutex,NameVirtualHost,NoProxy,NWSSLTrustedCerts,NWSSLUpgradeable,Options,Order,OutputSed,PassEnv,PidFile,PrivilegesMode,Protocol,ProtocolEcho,<Proxy>,ProxyAddHeaders,ProxyBadHeader,ProxyBlock,ProxyDomain,ProxyErrorOverride,ProxyExpressDBMFile,ProxyExpressDBMType,ProxyExpressEnable,ProxyFtpDirCharset,ProxyFtpEscapeWildcards,ProxyFtpListOnWildcard,ProxyHTMLBufSize,ProxyHTMLCharsetOut,ProxyHTMLDocType,ProxyHTMLEnable,ProxyHTMLEvents,ProxyHTMLExtended,ProxyHTMLFixups,ProxyHTMLInterp,ProxyHTMLLinks,ProxyHTMLStripComments,ProxyHTMLURLMap,ProxyIOBufferSize,<ProxyMatch>,ProxyMaxForwards,ProxyPass,ProxyPassInterpolateEnv,ProxyPassMatch,ProxyPassReverse,ProxyPassReverseCookieDomain,ProxyPassReverseCookiePath,ProxyPreserveHost,ProxyRece
 iveBufferSize,ProxyRemote,ProxyRemoteMatch,ProxyRequests,ProxySCGIInternalRedirect,ProxySCGISendfile,ProxySet,ProxySourceAddress,ProxyStatus,ProxyTimeout,ProxyVia,ReadmeName,ReceiveBufferSize,Redirect,RedirectMatch,RedirectPermanent,RedirectTemp,ReflectorHeader,RemoteIPHeader,RemoteIPInternalProxy,RemoteIPInternalProxyList,RemoteIPProxiesHeader,RemoteIPTrustedProxy,RemoteIPTrustedProxyList,RemoveCharset,RemoveEncoding,RemoveHandler,RemoveInputFilter,RemoveLanguage,RemoveOutputFilter,RemoveType,RequestHeader,RequestReadTimeout,Require,<RequireAll>,<RequireAny>,<RequireNone>,RewriteBase,RewriteCond,RewriteEngine,RewriteMap,RewriteOptions,RewriteRule,RLimitCPU,RLimitMEM,RLimitNPROC,Satisfy,ScoreBoardFile,Script,ScriptAlias,ScriptAliasMatch,ScriptInterpreterSource,ScriptLog,ScriptLogBuffer,ScriptLogLength,ScriptSock,SecureListen,SeeRequestTail,SendBufferSize,ServerAdmin,ServerAlias,ServerLimit,ServerName,ServerPath,ServerRoot,ServerSignature,ServerTokens,Session,SessionCookieName,Sessio
 nCookieName2,SessionCookieRemove,SessionCryptoCipher,SessionCryptoDriver,SessionCryptoPassphrase,SessionCryptoPassphraseFile,SessionDBDCookieName,SessionDBDCookieName2,SessionDBDCookieRemove,SessionDBDDeleteLabel,SessionDBDInsertLabel,SessionDBDPerUser,SessionDBDSelectLabel,SessionDBDUpdateLabel,SessionEnv,SessionExclude,SessionHeader,SessionInclude,SessionMaxAge,SetEnv,SetEnvIf,SetEnvIfExpr,SetEnvIfNoCase,SetHandler,SetInputFilter,SetOutputFilter,SSIEndTag,SSIErrorMsg,SSIETag,SSILastModified,SSILegacyExprParser,SSIStartTag,SSITimeFormat,SSIUndefinedEcho,SSLCACertificateFile,SSLCACertificatePath,SSLCADNRequestFile,SSLCADNRequestPath,SSLCARevocationCheck,SSLCARevocationFile,SSLCARevocationPath,SSLCertificateChainFile,SSLCertificateFile,SSLCertificateKeyFile,SSLCipherSuite,SSLCryptoDevice,SSLEngine,SSLFIPS,SSLHonorCipherOrder,SSLInsecureRenegotiation,SSLOCSPDefaultResponder,SSLOCSPEnable,SSLOCSPOverrideResponder,SSLOCSPResponderTimeout,SSLOCSPResponseMaxAge,SSLOCSPResponseTimeSkew,SSL
 Options,SSLPassPhraseDialog,SSLProtocol,SSLProxyCACertificateFile,SSLProxyCACertificatePath,SSLProxyCARevocationCheck,SSLProxyCARevocationFile,SSLProxyCARevocationPath,SSLProxyCheckPeerCN,SSLProxyCheckPeerExpire,SSLProxyCipherSuite,SSLProxyEngine,SSLProxyMachineCertificateChainFile,SSLProxyMachineCertificateFile,SSLProxyMachineCertificatePath,SSLProxyProtocol,SSLProxyVerify,SSLProxyVerifyDepth,SSLRandomSeed,SSLRenegBufferSize,SSLRequire,SSLRequireSSL,SSLSessionCache,SSLSessionCacheTimeout,SSLSessionTicketKeyFile,SSLStaplingCache,SSLStaplingErrorCacheTimeout,SSLStaplingFakeTryLater,SSLStaplingForceURL,SSLStaplingResponderTimeout,SSLStaplingResponseMaxAge,SSLStaplingResponseTimeSkew,SSLStaplingReturnResponderErrors,SSLStaplingStandardCacheTimeout,SSLStrictSNIVHostCheck,SSLUserName,SSLUseStapling,SSLVerifyClient,SSLVerifyDepth,StartServers,StartThreads,Substitute,Suexec,SuexecUserGroup,ThreadLimit,ThreadsPerChild,ThreadStackSize,TimeOut,TraceEnable,TransferLog,TypesConfig,UnDefine,Unse
 tEnv,UseCanonicalName,UseCanonicalPhysicalPort,User,UserDir,VHostCGIMode,VHostCGIPrivs,VHostGroup,VHostPrivs,VHostSecure,VHostUser,VirtualDocumentRoot,VirtualDocumentRootIP,<VirtualHost>,VirtualScriptAlias,VirtualScriptAliasIP,WatchdogInterval,XBitHack,xml2EncAlias,xml2EncDefault,xml2StartParse,RewriteLog,RewriteLogLevel"];
+  var CONFIG_KEYWORDS = ["Macro,UndefMacro,Use,AuthLDAPURL,AcceptFilter,AcceptPathInfo,AccessFileName,Action,AddAlt,AddAltByEncoding,AddAltByType,AddCharset,AddDefaultCharset,AddDescription,AddEncoding,AddHandler,AddIcon,AddIconByEncoding,AddIconByType,AddInputFilter,AddLanguage,AddModuleInfo,AddOutputFilter,AddOutputFilterByType,AddType,Alias,AliasMatch,Allow,AllowCONNECT,AllowEncodedSlashes,AllowMethods,AllowOverride,AllowOverrideList,Anonymous,Anonymous_LogEmail,Anonymous_MustGiveEmail,Anonymous_NoUserID,Anonymous_VerifyEmail,AsyncRequestWorkerFactor,AuthBasicAuthoritative,AuthBasicProvider,AuthDBDUserPWQuery,AuthDBDUserRealmQuery,AuthDBMGroupFile,AuthDBMType,AuthDBMUserFile,AuthDigestAlgorithm,AuthDigestDomain,AuthDigestNcCheck,AuthDigestNonceFormat,AuthDigestNonceLifetime,AuthDigestProvider,AuthDigestQop,AuthDigestShmemSize,AuthFormAuthoritative,AuthFormBody,AuthFormDisableNoStore,AuthFormFakeBasicAuth,AuthFormLocation,AuthFormLoginRequiredLocation,AuthFormLoginSuccessLocation,
 AuthFormLogoutLocation,AuthFormMethod,AuthFormMimetype,AuthFormPassword,AuthFormProvider,AuthFormSitePassphrase,AuthFormSize,AuthFormUsername,AuthGroupFile,AuthLDAPAuthorizePrefix,AuthLDAPBindAuthoritative,AuthLDAPBindDN,AuthLDAPBindPassword,AuthLDAPBindSASLMech,AuthLDAPBindSASLInteract,AuthLDAPCharsetConfig,AuthLDAPCompareAsUser,AuthLDAPCompareDNOnServer,AuthLDAPDereferenceAliases,AuthLDAPGroupAttribute,AuthLDAPGroupAttributeIsDN,AuthLDAPInitialBindAsUser,AuthLDAPInitialBindPattern,AuthLDAPMaxSubGroupDepth,AuthLDAPRemoteUserAttribute,AuthLDAPRemoteUserIsDN,AuthLDAPSearchAsUser,AuthLDAPSubGroupAttribute,AuthLDAPSubGroupClass,AuthLDAPUrl,AuthMerging,AuthName,AuthnCacheContext,AuthnCacheEnable,AuthnCacheProvideFor,AuthnCacheSOCache,AuthnCacheTimeout,<AuthnProviderAlias>,AuthType,AuthUserFile,AuthzDBDLoginToReferer,AuthzDBDQuery,AuthzDBDRedirectQuery,AuthzDBMType,<AuthzProviderAlias>,AuthzSendForbiddenOnFailure,BalancerGrowth,BalancerMember,BrowserMatch,BrowserMatchNoCase,BufferedLogs,
 BufferSize,CacheDefaultExpire,CacheDetailHeader,CacheDirLength,CacheDirLevels,CacheDisable,CacheEnable,CacheFile,CacheHeader,CacheIgnoreCacheControl,CacheIgnoreHeaders,CacheIgnoreNoLastMod,CacheIgnoreQueryString,CacheIgnoreURLSessionIdentifiers,CacheKeyBaseURL,CacheLastModifiedFactor,CacheLock,CacheLockMaxAge,CacheLockPath,CacheMaxExpire,CacheMaxFileSize,CacheMinExpire,CacheMinFileSize,CacheNegotiatedDocs,CacheQuickHandler,CacheReadSize,CacheReadTime,CacheRoot,CacheStaleOnError,CacheStoreExpired,CacheStoreNoStore,CacheStorePrivate,CGIMapExtension,CharsetDefault,CharsetOptions,CharsetSourceEnc,CheckCaseOnly,CheckSpelling,ChrootDir,ContentDigest,CookieDomain,CookieExpires,CookieName,CookieStyle,CookieTracking,CoreDumpDirectory,CustomLog,Dav,DavDepthInfinity,DavGenericLockDB,DavLockDB,DavMinTimeout,DBDExptime,DBDInitSQL,DBDKeep,DBDMax,DBDMin,DBDParams,DBDPersist,DBDPrepareSQL,DBDriver,DefaultIcon,DefaultLanguage,DefaultRuntimeDir,DefaultType,Define,DeflateBufferSize,DeflateCompressionL
 evel,DeflateFilterNote,DeflateMemLevel,DeflateWindowSize,Deny,<Directory>,DirectoryIndex,DirectoryIndexRedirect,<DirectoryMatch>,DirectorySlash,DocumentRoot,DTracePrivileges,DumpIOInput,DumpIOOutput,<Else>,<ElseIf>,EnableExceptionHook,EnableMMAP,EnableSendfile,Error,ErrorDocument,ErrorLog,ErrorLogFormat,Example,ExpiresActive,ExpiresByType,ExpiresDefault,ExtendedStatus,ExtFilterDefine,ExtFilterOptions,FallbackResource,FileETag,<Files>,<FilesMatch>,FilterChain,FilterDeclare,FilterProtocol,FilterProvider,FilterTrace,ForceLanguagePriority,ForceType,ForensicLog,GprofDir,GracefulShutdownTimeout,Group,Header,HeaderName,HeartbeatAddress,HeartbeatListen,HeartbeatMaxServers,HeartbeatStorage,HeartbeatStorage,HostnameLookups,IdentityCheck,IdentityCheckTimeout,<If>,<IfDefine>,<IfModule>,<IfVersion>,ImapBase,ImapDefault,ImapMenu,Include,IncludeOptional,IndexHeadInsert,IndexIgnore,IndexIgnoreReset,IndexOptions,IndexOrderDefault,IndexStyleSheet,InputSed,ISAPIAppendLogToErrors,ISAPIAppendLogToQuery,
 ISAPICacheFile,ISAPIFakeAsync,ISAPILogNotSupported,ISAPIReadAheadBuffer,KeepAlive,KeepAliveTimeout,KeptBodySize,LanguagePriority,LDAPCacheEntries,LDAPCacheTTL,LDAPConnectionPoolTTL,LDAPConnectionTimeout,LDAPLibraryDebug,LDAPOpCacheEntries,LDAPOpCacheTTL,LDAPReferralHopLimit,LDAPReferrals,LDAPRetries,LDAPRetryDelay,LDAPSharedCacheFile,LDAPSharedCacheSize,LDAPTimeout,LDAPTrustedClientCert,LDAPTrustedGlobalCert,LDAPTrustedMode,LDAPVerifyServerCert,<Limit>,<LimitExcept>,LimitInternalRecursion,LimitRequestBody,LimitRequestFields,LimitRequestFieldSize,LimitRequestLine,LimitXMLRequestBody,Listen,ListenBackLog,LoadFile,LoadModule,<Location>,<LocationMatch>,LogFormat,LogLevel,LogMessage,LuaCodeCache,LuaHookAccessChecker,LuaHookAuthChecker,LuaAuthzProvider,LuaHookCheckUserID,LuaHookFixups,LuaHookInsertFilter,LuaHookMapToStorage,LuaHookTranslateName,LuaHookTypeChecker,LuaInherit,LuaInputFilter,LuaMapHandler,LuaOutputFilter,LuaPackageCPath,LuaPackagePath,LuaQuickHandler,LuaRoot,LuaScope,MaxConn
 ectionsPerChild,MaxKeepAliveRequests,MaxMemFree,MaxRangeOverlaps,MaxRangeReversals,MaxRanges,MaxRequestWorkers,MaxSpareServers,MaxSpareThreads,MaxThreads,MetaDir,MetaFiles,MetaSuffix,MimeMagicFile,MinSpareServers,MinSpareThreads,MMapFile,ModemStandard,ModMimeUsePathInfo,MultiviewsMatch,Mutex,NameVirtualHost,NoProxy,NWSSLTrustedCerts,NWSSLUpgradeable,Options,Order,OutputSed,PassEnv,PidFile,PrivilegesMode,Protocol,ProtocolEcho,<Proxy>,ProxyAddHeaders,ProxyBadHeader,ProxyBlock,ProxyDomain,ProxyErrorOverride,ProxyExpressDBMFile,ProxyExpressDBMType,ProxyExpressEnable,ProxyFtpDirCharset,ProxyFtpEscapeWildcards,ProxyFtpListOnWildcard,ProxyHTMLBufSize,ProxyHTMLCharsetOut,ProxyHTMLDocType,ProxyHTMLEnable,ProxyHTMLEvents,ProxyHTMLExtended,ProxyHTMLFixups,ProxyHTMLInterp,ProxyHTMLLinks,ProxyHTMLStripComments,ProxyHTMLURLMap,ProxyIOBufferSize,<ProxyMatch>,ProxyMaxForwards,ProxyPass,ProxyPassInterpolateEnv,ProxyPassMatch,ProxyPassReverse,ProxyPassReverseCookieDomain,ProxyPassReverseCookiePath,Pr
 oxyPreserveHost,ProxyReceiveBufferSize,ProxyRemote,ProxyRemoteMatch,ProxyRequests,ProxySCGIInternalRedirect,ProxySCGISendfile,ProxySet,ProxySourceAddress,ProxyStatus,ProxyTimeout,ProxyVia,ReadmeName,ReceiveBufferSize,Redirect,RedirectMatch,RedirectPermanent,RedirectTemp,ReflectorHeader,RemoteIPHeader,RemoteIPInternalProxy,RemoteIPInternalProxyList,RemoteIPProxiesHeader,RemoteIPTrustedProxy,RemoteIPTrustedProxyList,RemoveCharset,RemoveEncoding,RemoveHandler,RemoveInputFilter,RemoveLanguage,RemoveOutputFilter,RemoveType,RequestHeader,RequestReadTimeout,Require,<RequireAll>,<RequireAny>,<RequireNone>,RewriteBase,RewriteCond,RewriteEngine,RewriteMap,RewriteOptions,RewriteRule,RLimitCPU,RLimitMEM,RLimitNPROC,Satisfy,ScoreBoardFile,Script,ScriptAlias,ScriptAliasMatch,ScriptInterpreterSource,ScriptLog,ScriptLogBuffer,ScriptLogLength,ScriptSock,SecureListen,SeeRequestTail,SendBufferSize,ServerAdmin,ServerAlias,ServerLimit,ServerName,ServerPath,ServerRoot,ServerSignature,ServerTokens,Session
 ,SessionCookieName,SessionCookieName2,SessionCookieRemove,SessionCryptoCipher,SessionCryptoDriver,SessionCryptoPassphrase,SessionCryptoPassphraseFile,SessionDBDCookieName,SessionDBDCookieName2,SessionDBDCookieRemove,SessionDBDDeleteLabel,SessionDBDInsertLabel,SessionDBDPerUser,SessionDBDSelectLabel,SessionDBDUpdateLabel,SessionEnv,SessionExclude,SessionHeader,SessionInclude,SessionMaxAge,SetEnv,SetEnvIf,SetEnvIfExpr,SetEnvIfNoCase,SetHandler,SetInputFilter,SetOutputFilter,SSIEndTag,SSIErrorMsg,SSIETag,SSILastModified,SSILegacyExprParser,SSIStartTag,SSITimeFormat,SSIUndefinedEcho,SSLCACertificateFile,SSLCACertificatePath,SSLCADNRequestFile,SSLCADNRequestPath,SSLCARevocationCheck,SSLCARevocationFile,SSLCARevocationPath,SSLCertificateChainFile,SSLCertificateFile,SSLCertificateKeyFile,SSLCipherSuite,SSLCryptoDevice,SSLEngine,SSLFIPS,SSLHonorCipherOrder,SSLInsecureRenegotiation,SSLOCSPDefaultResponder,SSLOCSPEnable,SSLOCSPOverrideResponder,SSLOCSPResponderTimeout,SSLOCSPResponseMaxAge,SS
 LOCSPResponseTimeSkew,SSLOptions,SSLPassPhraseDialog,SSLProtocol,SSLProxyCACertificateFile,SSLProxyCACertificatePath,SSLProxyCARevocationCheck,SSLProxyCARevocationFile,SSLProxyCARevocationPath,SSLProxyCheckPeerCN,SSLProxyCheckPeerExpire,SSLProxyCipherSuite,SSLProxyEngine,SSLProxyMachineCertificateChainFile,SSLProxyMachineCertificateFile,SSLProxyMachineCertificatePath,SSLProxyProtocol,SSLProxyVerify,SSLProxyVerifyDepth,SSLRandomSeed,SSLRenegBufferSize,SSLRequire,SSLRequireSSL,SSLSessionCache,SSLSessionCacheTimeout,SSLSessionTicketKeyFile,SSLStaplingCache,SSLStaplingErrorCacheTimeout,SSLStaplingFakeTryLater,SSLStaplingForceURL,SSLStaplingResponderTimeout,SSLStaplingResponseMaxAge,SSLStaplingResponseTimeSkew,SSLStaplingReturnResponderErrors,SSLStaplingStandardCacheTimeout,SSLStrictSNIVHostCheck,SSLUserName,SSLUseStapling,SSLVerifyClient,SSLVerifyDepth,StartServers,StartThreads,Substitute,Suexec,SuexecUserGroup,ThreadLimit,ThreadsPerChild,ThreadStackSize,TimeOut,TraceEnable,TransferLog,
 TypesConfig,UnDefine,UnsetEnv,UseCanonicalName,UseCanonicalPhysicalPort,User,UserDir,VHostCGIMode,VHostCGIPrivs,VHostGroup,VHostPrivs,VHostSecure,VHostUser,VirtualDocumentRoot,VirtualDocumentRootIP,<VirtualHost>,VirtualScriptAlias,VirtualScriptAliasIP,WatchdogInterval,XBitHack,xml2EncAlias,xml2EncDefault,xml2StartParse,RewriteLog,RewriteLogLevel"];
   var CONFIG_OPTIONS = /^[\\+\\-]?(AuthConfig|IncludesNOEXEC|ExecCGI|FollowSymLinks|MultiViews|Includes|Indexes|SymLinksIfOwnerMatch)\b/i;
   var ALL_KEYWORDS = [
       CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS +
diff --git a/include/util_ldap.h b/include/util_ldap.h
index 60123b3..efd4130 100644
--- a/include/util_ldap.h
+++ b/include/util_ldap.h
@@ -114,6 +114,7 @@ typedef struct util_ldap_connection_t {
     const char *binddn;                 /* DN to bind to server (can be NULL) */
     const char *bindpw;                 /* Password to bind to server (can be NULL) */
     const char *bindsaslmech;           /* SASL Mechanism to use for server bind (can be NULL) */
+    char *bindsaslinteract;             /* Command to run when SASL requests interaction to obtain credentials (can be NULL) */
 
     int bound;                          /* Flag to indicate whether this connection is bound yet */
 
@@ -239,7 +240,8 @@ APR_DECLARE_OPTIONAL_FN(apr_status_t,uldap_connection_unbind,(void *param));
  *                                                           int netscapessl, int starttls)
  */
 APR_DECLARE_OPTIONAL_FN(util_ldap_connection_t *,uldap_connection_find,(request_rec *r, const char *host, int port,
-                                                  const char *binddn, const char *bindpw, const char *bindsaslmech,
+                                                  const char *binddn, const char *bindpw,
+                                                  const char *bindsaslmech, const char *bindsaslinteract,
                                                   deref_options deref, int secure));
 
 /**
diff --git a/modules/aaa/mod_authnz_ldap.c b/modules/aaa/mod_authnz_ldap.c
index cdf06f4..af1f6ce 100644
--- a/modules/aaa/mod_authnz_ldap.c
+++ b/modules/aaa/mod_authnz_ldap.c
@@ -60,6 +60,7 @@ typedef struct {
     char *binddn;                   /* DN to bind to server (can be NULL) */
     char *bindpw;                   /* Password to bind to server (can be NULL) */
     char *bindsaslmech;             /* SASL Mechanism to use for server bind (can be NULL) */
+    char *bindsaslinteract;         /* Command to run when SASL requests interaction to obtain credentials. */
     int bind_authoritative;         /* If true, will return errors when bind fails */
 
     int user_is_dn;                 /* If true, connection->user is DN instead of userid */
@@ -346,6 +347,7 @@ static void *create_authnz_ldap_dir_config(apr_pool_t *p, char *d)
     sec->binddn = NULL;
     sec->bindpw = NULL;
     sec->bindsaslmech = NULL;
+    sec->bindsaslinteract = NULL;
     sec->bind_authoritative = 1;
     sec->deref = always;
     sec->group_attrib_is_dn = 1;
@@ -442,6 +444,7 @@ static util_ldap_connection_t *get_connection_for_authz(request_rec *r, enum aut
     char *binddn = sec->binddn;
     char *bindpw = sec->bindpw;
     char *bindsaslmech = sec->bindsaslmech;
+    char *bindsaslinteract = sec->bindsaslinteract;
 
     /* If the per-request config isn't set, we didn't authenticate this user, and leave the default credentials */
     if (req && req->password &&
@@ -451,10 +454,12 @@ static util_ldap_connection_t *get_connection_for_authz(request_rec *r, enum aut
             binddn = req->dn;
             bindpw = req->password;
             bindsaslmech = NULL;
+            bindsaslinteract = NULL;
     }
 
     return util_ldap_connection_find(r, sec->host, sec->port,
-                                     binddn, bindpw, bindsaslmech,
+                                     binddn, bindpw,
+                                     bindsaslmech, bindsaslinteract,
                                      sec->deref, sec->secure);
 }
 /*
@@ -502,14 +507,17 @@ static authn_status authn_ldap_check_password(request_rec *r, const char *user,
         const char *binddn = sec->binddn;
         const char *bindpw = sec->bindpw;
         const char *bindsaslmech = sec->bindsaslmech;
+        const char *bindsaslinteract = sec->bindsaslinteract;
         if (sec->initial_bind_as_user) {
             bindpw = password;
             binddn = ldap_determine_binddn(r, user);
             bindsaslmech = NULL;
+            bindsaslinteract = NULL;
         }
 
         ldc = util_ldap_connection_find(r, sec->host, sec->port,
-                                       binddn, bindpw, bindsaslmech,
+                                       binddn, bindpw,
+                                       bindsaslmech, bindsaslinteract,
                                        sec->deref, sec->secure);
     }
     else {
@@ -1631,6 +1639,10 @@ static const command_rec authnz_ldap_cmds[] =
                   (void *)APR_OFFSETOF(authn_ldap_config_t, bindsaslmech), OR_AUTHCFG,
                   "SASL Mechanism to use to bind to LDAP server. If not provided, simple authentication will be done."),
 
+    AP_INIT_TAKE1("AuthLDAPBindSASLInteract", ap_set_string_slot,
+                  (void *)APR_OFFSETOF(authn_ldap_config_t, bindsaslinteract), OR_AUTHCFG,
+                  "Command to run when SASL requests interaction to obtain credentials."),
+
     AP_INIT_FLAG("AuthLDAPBindAuthoritative", ap_set_flag_slot,
                   (void *)APR_OFFSETOF(authn_ldap_config_t, bind_authoritative), OR_AUTHCFG,
                   "Set to 'on' to return failures when user-specific bind fails - defaults to on."),
diff --git a/modules/ldap/util_ldap.c b/modules/ldap/util_ldap.c
index 535c12a..323983b 100644
--- a/modules/ldap/util_ldap.c
+++ b/modules/ldap/util_ldap.c
@@ -490,18 +490,57 @@ static int uldap_ld_errno(util_ldap_connection_t *ldc)
 }
 
 /*
- * SASL credentials conversation function. Does nothing really useful yet,
- * is around just because it is required.
+ * SASL credentials conversation function.
+ * Calls external helper if specified, otherwise just returns success.
  *
- * Always returns LDAP_SUCCESS
+ * Returns LDAP_SUCCESS on success; and an error code on failure
  */
-
 static int uldap_sasl_interact(LDAP *ld,
                                unsigned flags,
                                void *defaults,
                                void *sasl_interact)
 {
-    return LDAP_SUCCESS;
+    char *bindsaslinteract = (char *)defaults;
+    int ret = LDAP_SUCCESS;
+    apr_pool_t *pool;
+    apr_proc_t proc;
+    apr_procattr_t *procattr;
+    apr_exit_why_e why;
+    int code;
+    char **argv;
+
+    if (bindsaslinteract == NULL)
+        return ret;
+
+    apr_pool_create(&pool, NULL);
+
+    if (apr_tokenize_to_argv(bindsaslinteract, &argv, pool) != APR_SUCCESS) {
+        ret = LDAP_PARAM_ERROR;
+        goto out;
+    }
+
+    if (apr_procattr_create(&procattr, pool) != APR_SUCCESS) {
+        ret = LDAP_LOCAL_ERROR;
+        goto out;
+    }
+
+    if (apr_proc_create(&proc, argv[0],(const char * const*)argv,
+                        NULL, procattr, pool) != APR_SUCCESS) {
+        ret = LDAP_LOCAL_ERROR;
+        goto out;
+    }
+
+    if (apr_proc_wait(&proc, &code, &why, APR_WAIT) != APR_CHILD_DONE) {
+        ret = LDAP_LOCAL_ERROR;
+        goto out;
+    }
+
+    if (why != APR_PROC_EXIT)
+        ret = LDAP_LOCAL_ERROR;
+
+out:
+    apr_pool_destroy(pool);
+    return ret;
 }
 
 /*
@@ -511,8 +550,9 @@ static int uldap_sasl_interact(LDAP *ld,
  *
  * Returns LDAP_SUCCESS on success; and an error code on failure
  */
-static int uldap_bind(util_ldap_connection_t *ldc, char *binddn,
-                      char* bindpw, char *bindsaslmech,
+static int uldap_bind(util_ldap_connection_t *ldc,
+                      char *binddn, char* bindpw,
+                      char *bindsaslmech, char *bindsaslinteract,
                       struct timeval *timeout)
 {
     int rc;
@@ -520,7 +560,7 @@ static int uldap_bind(util_ldap_connection_t *ldc, char *binddn,
     if (bindsaslmech) {
         rc = ldap_sasl_interactive_bind_s(ldc->ldap, binddn, bindsaslmech,
                                           NULL, NULL, LDAP_SASL_QUIET,
-                                          uldap_sasl_interact, NULL);
+                                          uldap_sasl_interact, bindsaslinteract);
         if (rc == -1) {
             ldc->reason = "LDAP: ldap_sasl_interactive_bind_s() failed";
             /* -1 is LDAP_SERVER_DOWN in openldap, use something else */
@@ -614,7 +654,8 @@ static int uldap_connection_open(request_rec *r,
             apr_sleep(st->retry_delay);
         }
         rc = uldap_bind(ldc, (char *)ldc->binddn, (char *)ldc->bindpw,
-                        (char *)ldc->bindsaslmech, st->opTimeout);
+                        (char *)ldc->bindsaslmech, (char *)ldc->bindsaslinteract,
+                        st->opTimeout);
 
         if (rc == LDAP_SUCCESS) break;
 
@@ -718,7 +759,7 @@ static util_ldap_connection_t *
             uldap_connection_find(request_rec *r,
                                   const char *host, int port,
                                   const char *binddn, const char *bindpw,
-                                  const char *bindsaslmech,
+                                  const char *bindsaslmech, const char *bindsaslinteract,
                                   deref_options deref, int secure)
 {
     struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
@@ -754,6 +795,8 @@ static util_ldap_connection_t *
                                              && !strcmp(l->bindpw, bindpw)))
             && ((!l->bindsaslmech && !bindsaslmech) || (l->bindsaslmech && bindsaslmech
                                              && !strcmp(l->bindsaslmech, bindsaslmech)))
+            && ((!l->bindsaslinteract && !bindsaslinteract) || (l->bindsaslinteract && bindsaslinteract
+                                             && !strcmp(l->bindsaslinteract, bindsaslinteract)))
             && (l->deref == deref) && (l->secure == secureflag)
             && !compare_client_certs(dc->client_certs, l->client_certs))
         {
@@ -814,6 +857,7 @@ static util_ldap_connection_t *
                 util_ldap_strdup((char**)&(l->binddn), binddn);
                 util_ldap_strdup((char**)&(l->bindpw), bindpw);
                 util_ldap_strdup((char**)&(l->bindsaslmech), bindsaslmech);
+                util_ldap_strdup((char**)&(l->bindsaslinteract), bindsaslinteract);
                 break;
             }
 #if APR_HAS_THREADS
@@ -866,6 +910,7 @@ static util_ldap_connection_t *
         util_ldap_strdup((char**)&(l->binddn), binddn);
         util_ldap_strdup((char**)&(l->bindpw), bindpw);
         util_ldap_strdup((char**)&(l->bindsaslmech), bindsaslmech);
+        util_ldap_strdup((char**)&(l->bindsaslinteract), bindsaslinteract);
         l->ChaseReferrals = dc->ChaseReferrals;
         l->ReferralHopLimit = dc->ReferralHopLimit;
 
@@ -1811,7 +1856,7 @@ start_over:
      * fails, it means that the password is wrong (the dn obviously
      * exists, since we just retrieved it)
      */
-    result = uldap_bind(ldc, (char *)*binddn, (char *)bindpw, NULL,
+    result = uldap_bind(ldc, (char *)*binddn, (char *)bindpw, NULL, NULL,
                         st->opTimeout);
     if (AP_LDAP_IS_SERVER_DOWN(result) ||
         (result == LDAP_TIMEOUT && failures == 0)) {
-- 
1.7.1