From 6900616faf528cf050ba982a79d111e875089d52 Mon Sep 17 00:00:00 2001 From: gregwroblewski Date: Fri, 12 Oct 2012 06:27:22 +0000 Subject: [PATCH] Standalone: added Include command IIS: added locking, response processing check, fixed file chunk reading bugs --- .../ModSecurityIIS/ModSecurityIIS.vdproj | 71 ++- .../ModSecurityConfigurator.cs | 4 + iis/mymodule.cpp | 121 +++-- iis/mymodule.h | 5 +- standalone/api.c | 10 + standalone/api.h | 2 + standalone/config.c | 440 ++++++++++++++++-- 7 files changed, 557 insertions(+), 96 deletions(-) diff --git a/iis/ModSecurityIIS/ModSecurityIIS/ModSecurityIIS.vdproj b/iis/ModSecurityIIS/ModSecurityIIS/ModSecurityIIS.vdproj index 5a092b10..1d834023 100644 --- a/iis/ModSecurityIIS/ModSecurityIIS/ModSecurityIIS.vdproj +++ b/iis/ModSecurityIIS/ModSecurityIIS/ModSecurityIIS.vdproj @@ -45,6 +45,12 @@ } "Entry" { + "MsmKey" = "8:_3CE93C3FC5AC3E954253889334FBCDA8" + "OwnerKey" = "8:_CB8446F7ADCD4E3DA3F2C6246FA844A0" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_51AF671FCA3544DEA3E5756B5D450275" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -141,6 +147,12 @@ } "Entry" { + "MsmKey" = "8:_CB8446F7ADCD4E3DA3F2C6246FA844A0" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_CEB23D021A2E4EEF9245EEDC143AFBA8" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -187,6 +199,12 @@ "OwnerKey" = "8:_764D5BE911464BEFBCC3BC3B25068987" "MsmSig" = "8:_UNDEFINED" } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_CB8446F7ADCD4E3DA3F2C6246FA844A0" + "MsmSig" = "8:_UNDEFINED" + } } "Configurations" { @@ -468,6 +486,26 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3CE93C3FC5AC3E954253889334FBCDA8" + { + "SourcePath" = "8:nativerd.dll" + "TargetName" = "8:nativerd.dll" + "Tag" = "8:" + "Folder" = "8:_565C3432A64049EAA7CA6E8C007B2188" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_51AF671FCA3544DEA3E5756B5D450275" { "SourcePath" = "8:x86\\ModSecurityIIS.dll" @@ -768,6 +806,37 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_CB8446F7ADCD4E3DA3F2C6246FA844A0" + { + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:Interop.AppHostAdminLibrary, Version=1.0.0.0, Culture=neutral, processorArchitecture=x86" + "ScatterAssemblies" + { + "_CB8446F7ADCD4E3DA3F2C6246FA844A0" + { + "Name" = "8:Interop.AppHostAdminLibrary.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:installer project\\bin\\Release\\Interop.AppHostAdminLibrary.dll" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_565C3432A64049EAA7CA6E8C007B2188" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CEB23D021A2E4EEF9245EEDC143AFBA8" { "SourcePath" = "8:amd64\\ModSecurityIIS.dll" @@ -987,7 +1056,7 @@ "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:ModSecurity IIS" "ProductCode" = "8:{81EE8A4A-5128-4CDB-97B2-06B147E8B4B8}" - "PackageCode" = "8:{0E266CA7-97F3-4DCE-AC7B-5ECCAE18A108}" + "PackageCode" = "8:{B5E59B35-BF44-4075-B9F5-C251002DF58E}" "UpgradeCode" = "8:{7B32CF94-443C-47BB-91C3-0E9D3D12DF8B}" "AspNetVersion" = "8:4.0.30319.0" "RestartWWWService" = "11:FALSE" diff --git a/iis/ModSecurityIIS/ModSecurityIIS/installer project/ModSecurityConfigurator.cs b/iis/ModSecurityIIS/ModSecurityIIS/installer project/ModSecurityConfigurator.cs index f40b9b33..3cb0d35b 100644 --- a/iis/ModSecurityIIS/ModSecurityIIS/installer project/ModSecurityConfigurator.cs +++ b/iis/ModSecurityIIS/ModSecurityIIS/installer project/ModSecurityConfigurator.cs @@ -16,6 +16,10 @@ namespace configure { installDir = installDir.Substring(0, installDir.Length - 1); } + if (installDir.StartsWith("\"")) + { + installDir = installDir.Substring(1); + } Console.WriteLine("Copying 32-bit binaries..."); string dstpath = Environment.ExpandEnvironmentVariables("%windir%\\SysWow64"); diff --git a/iis/mymodule.cpp b/iis/mymodule.cpp index d92ab7c4..33ce7173 100644 --- a/iis/mymodule.cpp +++ b/iis/mymodule.cpp @@ -76,8 +76,8 @@ class REQUEST_STORED_CONTEXT : public IHttpStoredContext IHttpContext *m_pHttpContext; IHttpEventProvider *m_pProvider; char *m_pResponseBuffer; - unsigned int m_pResponseLength; - unsigned int m_pResponsePosition; + ULONGLONG m_pResponseLength; + ULONGLONG m_pResponsePosition; }; //---------------------------------------------------------------------------- @@ -94,39 +94,39 @@ char *GetIpAddr(apr_pool_t *pool, PSOCKADDR pAddr) apr_sockaddr_t *CopySockAddr(apr_pool_t *pool, PSOCKADDR pAddr) { - apr_sockaddr_t *addr = (apr_sockaddr_t *)apr_palloc(pool, sizeof(apr_sockaddr_t)); - int adrlen = 16, iplen = 4; - - if(pAddr->sa_family == AF_INET6) - { - adrlen = 46; - iplen = 16; - } - - addr->addr_str_len = adrlen; - addr->family = pAddr->sa_family; - - addr->hostname = "unknown"; -#ifdef WIN32 - addr->ipaddr_len = sizeof(IN_ADDR); -#else - addr->ipaddr_len = sizeof(struct in_addr); -#endif - addr->ipaddr_ptr = &addr->sa.sin.sin_addr; - addr->pool = pool; - addr->port = 80; -#ifdef WIN32 - memcpy(&addr->sa.sin.sin_addr.S_un.S_addr, pAddr->sa_data, iplen); -#else - memcpy(&addr->sa.sin.sin_addr.s_addr, pAddr->sa_data, iplen); -#endif - addr->sa.sin.sin_family = pAddr->sa_family; - addr->sa.sin.sin_port = 80; - addr->salen = sizeof(addr->sa); - addr->servname = addr->hostname; - - return addr; -} + apr_sockaddr_t *addr = (apr_sockaddr_t *)apr_palloc(pool, sizeof(apr_sockaddr_t)); + int adrlen = 16, iplen = 4; + + if(pAddr->sa_family == AF_INET6) + { + adrlen = 46; + iplen = 16; + } + + addr->addr_str_len = adrlen; + addr->family = pAddr->sa_family; + + addr->hostname = "unknown"; +#ifdef WIN32 + addr->ipaddr_len = sizeof(IN_ADDR); +#else + addr->ipaddr_len = sizeof(struct in_addr); +#endif + addr->ipaddr_ptr = &addr->sa.sin.sin_addr; + addr->pool = pool; + addr->port = 80; +#ifdef WIN32 + memcpy(&addr->sa.sin.sin_addr.S_un.S_addr, pAddr->sa_data, iplen); +#else + memcpy(&addr->sa.sin.sin_addr.s_addr, pAddr->sa_data, iplen); +#endif + addr->sa.sin.sin_family = pAddr->sa_family; + addr->sa.sin.sin_port = 80; + addr->salen = sizeof(addr->sa); + addr->servname = addr->hostname; + + return addr; +} //---------------------------------------------------------------------------- @@ -269,7 +269,7 @@ HRESULT CMyHttpModule::ReadFileChunk(HTTP_DATA_CHUNK *chunk, char *buf) { OVERLAPPED ovl; DWORD dwDataStartOffset; - DWORD bytesTotal = 0; + ULONGLONG bytesTotal = 0; BYTE * pIoBuffer = NULL; HANDLE hIoEvent = INVALID_HANDLE_VALUE; HRESULT hr = S_OK; @@ -332,6 +332,7 @@ HRESULT CMyHttpModule::ReadFileChunk(HTTP_DATA_CHUNK *chunk, char *buf) TRUE)) { dwErr = GetLastError(); + switch(dwErr) { case ERROR_HANDLE_EOF: @@ -343,7 +344,6 @@ HRESULT CMyHttpModule::ReadFileChunk(HTTP_DATA_CHUNK *chunk, char *buf) goto Done; } } - break; case ERROR_HANDLE_EOF: @@ -396,7 +396,9 @@ CMyHttpModule::OnSendResponse( rsc = (REQUEST_STORED_CONTEXT *)pHttpContext->GetModuleContextContainer()->GetModuleContext(g_pModuleContext); - if(rsc == NULL || rsc->m_pRequestRec == NULL || rsc->m_pResponseBuffer != NULL) + EnterCriticalSection(&m_csLock); + + if(rsc == NULL || rsc->m_pRequestRec == NULL || rsc->m_pResponseBuffer != NULL || !modsecIsResponseBodyAccessEnabled(rsc->m_pRequestRec)) { goto Exit; } @@ -408,8 +410,8 @@ CMyHttpModule::OnSendResponse( HTTP_DATA_CHUNK *pSourceDataChunk = NULL; LARGE_INTEGER lFileSize; REQUEST_NOTIFICATION_STATUS ret = RQ_NOTIFICATION_CONTINUE; - ULONG ulTotalLength = 0; - DWORD c, bytesRead; + ULONGLONG ulTotalLength = 0; + DWORD c; request_rec *r = rsc->m_pRequestRec; pHttpResponse = pHttpContext->GetResponse(); @@ -430,7 +432,6 @@ CMyHttpModule::OnSendResponse( // assume HTML if content type not set // without this output filter would not buffer response and processing would hang - // this needs further investigation (it did not repro on debug build) // if(ctz[0] == 0) ctz = "text/html"; @@ -495,6 +496,9 @@ CMyHttpModule::OnSendResponse( *(const char **)apr_array_push(r->content_languages) = lng; } + // here we must check if response body processing is enabled + // + // Disable kernel caching for this response // Probably we don't have to do it for ModSecurity @@ -575,6 +579,7 @@ CMyHttpModule::OnSendResponse( DWORD dwErr = GetLastError(); hr = HRESULT_FROM_WIN32(dwErr); + goto Finished; } ulTotalLength += pFileByteRange->Length.QuadPart; @@ -639,6 +644,8 @@ Finished: pHttpContext->SetRequestHandled(); rsc->FinishRequest(); + + LeaveCriticalSection(&m_csLock); return RQ_NOTIFICATION_FINISH_REQUEST; } @@ -648,6 +655,8 @@ Exit: if(rsc != NULL) rsc->FinishRequest(); + LeaveCriticalSection(&m_csLock); + return RQ_NOTIFICATION_CONTINUE; } @@ -665,7 +674,11 @@ CMyHttpModule::OnPostEndRequest( // if(rsc != NULL && rsc->m_pResponseBuffer != NULL) { + EnterCriticalSection(&m_csLock); + rsc->FinishRequest(); + + LeaveCriticalSection(&m_csLock); } return RQ_NOTIFICATION_CONTINUE; @@ -683,6 +696,8 @@ CMyHttpModule::OnBeginRequest( UNREFERENCED_PARAMETER ( pProvider ); + EnterCriticalSection(&m_csLock); + if ( pHttpContext == NULL ) { hr = E_UNEXPECTED; @@ -996,14 +1011,14 @@ CMyHttpModule::OnBeginRequest( PSOCKADDR pAddr = pRequest->GetRemoteAddress(); -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 - c->remote_addr = CopySockAddr(r->pool, pAddr); - c->remote_ip = GetIpAddr(r->pool, pAddr); -#else - c->client_addr = CopySockAddr(r->pool, pAddr); - c->client_ip = GetIpAddr(r->pool, pAddr); -#endif - c->remote_host = NULL; +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 + c->remote_addr = CopySockAddr(r->pool, pAddr); + c->remote_ip = GetIpAddr(r->pool, pAddr); +#else + c->client_addr = CopySockAddr(r->pool, pAddr); + c->client_ip = GetIpAddr(r->pool, pAddr); +#endif + c->remote_host = NULL; int status = modsecProcessRequest(r); @@ -1012,10 +1027,12 @@ CMyHttpModule::OnBeginRequest( pHttpContext->GetResponse()->SetStatus(status, "ModSecurity Action"); pHttpContext->SetRequestHandled(); - return RQ_NOTIFICATION_FINISH_REQUEST; + hr = E_FAIL; + goto Finished; } Finished: + LeaveCriticalSection(&m_csLock); if ( FAILED( hr ) ) { @@ -1201,6 +1218,8 @@ CMyHttpModule::CMyHttpModule() GetSystemInfo(&sysInfo); m_dwPageSize = sysInfo.dwPageSize; + InitializeCriticalSection(&m_csLock); + modsecSetLogHook(this, Log); modsecSetReadBody(ReadBodyCallback); @@ -1238,6 +1257,8 @@ CMyHttpModule::~CMyHttpModule() // Close the handle to the Event Viewer. DeregisterEventSource( m_hEventLog ); m_hEventLog = NULL; + + DeleteCriticalSection(&m_csLock); } } diff --git a/iis/mymodule.h b/iis/mymodule.h index 3ddf617f..9a53f4e6 100644 --- a/iis/mymodule.h +++ b/iis/mymodule.h @@ -22,8 +22,9 @@ class CMyHttpModule : public CHttpModule { public: - HANDLE m_hEventLog; - DWORD m_dwPageSize; + HANDLE m_hEventLog; + DWORD m_dwPageSize; + CRITICAL_SECTION m_csLock; REQUEST_NOTIFICATION_STATUS OnBeginRequest( diff --git a/standalone/api.c b/standalone/api.c index ba350914..7eb65be2 100644 --- a/standalone/api.c +++ b/standalone/api.c @@ -422,6 +422,16 @@ int modsecProcessRequest(request_rec *r) { return status; } +int modsecIsResponseBodyAccessEnabled(request_rec *r) +{ + modsec_rec *msr = retrieve_msr(r); + + if(msr == NULL || msr->txcfg == NULL) + return 0; + + return msr->txcfg->resbody_access; +} + int modsecProcessResponse(request_rec *r) { int status = DECLINED; diff --git a/standalone/api.h b/standalone/api.h index b0f17101..49a5f163 100644 --- a/standalone/api.h +++ b/standalone/api.h @@ -71,6 +71,8 @@ void modsecSetReadResponse(apr_status_t (*func)(request_rec *r, char *buf, unsig void modsecSetWriteBody(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length)); void modsecSetWriteResponse(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length)); +int modsecIsResponseBodyAccessEnabled(request_rec *r); + #ifdef __cplusplus } #endif diff --git a/standalone/config.c b/standalone/config.c index 1a49eee6..4f836e06 100644 --- a/standalone/config.c +++ b/standalone/config.c @@ -37,7 +37,7 @@ #include "apr_lib.h" #include "ap_config.h" #include "http_config.h" - +#include "apr_fnmatch.h" AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp) { @@ -702,6 +702,281 @@ static cmd_parms default_parms = {NULL, 0, 0, NULL, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; #endif +typedef struct { + const char *fname; +} fnames; + +AP_DECLARE(int) ap_is_directory(apr_pool_t *p, const char *path) +{ + apr_finfo_t finfo; + + if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS) + return 0; /* in error condition, just return no */ + + return (finfo.filetype == APR_DIR); +} + +AP_DECLARE(char *) ap_make_full_path(apr_pool_t *a, const char *src1, + const char *src2) +{ + apr_size_t len1, len2; + char *path; + + len1 = strlen(src1); + len2 = strlen(src2); + /* allocate +3 for '/' delimiter, trailing NULL and overallocate + * one extra byte to allow the caller to add a trailing '/' + */ + path = (char *)apr_palloc(a, len1 + len2 + 3); + if (len1 == 0) { + *path = '/'; + memcpy(path + 1, src2, len2 + 1); + } + else { + char *next; + memcpy(path, src1, len1); + next = path + len1; + if (next[-1] != '/') { + *next++ = '/'; + } + memcpy(next, src2, len2 + 1); + } + return path; +} + +static int fname_alphasort(const void *fn1, const void *fn2) +{ + const fnames *f1 = fn1; + const fnames *f2 = fn2; + + return strcmp(f1->fname,f2->fname); +} + +AP_DECLARE(const char *) ap_process_resource_config(const char *fname, + apr_array_header_t *ari, + apr_pool_t *ptemp) +{ + *(char **)apr_array_push(ari) = (char *)fname; + + return NULL; +} + +static const char *process_resource_config_nofnmatch(const char *fname, + apr_array_header_t *ari, + apr_pool_t *p, + apr_pool_t *ptemp, + unsigned depth, + int optional) +{ + const char *error; + apr_status_t rv; + + if (ap_is_directory(ptemp, fname)) { + apr_dir_t *dirp; + apr_finfo_t dirent; + int current; + apr_array_header_t *candidates = NULL; + fnames *fnew; + char *path = apr_pstrdup(ptemp, fname); + + if (++depth > 100) { + return apr_psprintf(p, "Directory %s exceeds the maximum include " + "directory nesting level of %u. You have " + "probably a recursion somewhere.", path, + 100); + } + + /* + * first course of business is to grok all the directory + * entries here and store 'em away. Recall we need full pathnames + * for this. + */ + rv = apr_dir_open(&dirp, path, ptemp); + if (rv != APR_SUCCESS) { + char errmsg[120]; + return apr_psprintf(p, "Could not open config directory %s: %s", + path, apr_strerror(rv, errmsg, sizeof errmsg)); + } + + candidates = apr_array_make(ptemp, 1, sizeof(fnames)); + while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) { + /* strip out '.' and '..' */ + if (strcmp(dirent.name, ".") + && strcmp(dirent.name, "..")) { + fnew = (fnames *) apr_array_push(candidates); + fnew->fname = ap_make_full_path(ptemp, path, dirent.name); + } + } + + apr_dir_close(dirp); + if (candidates->nelts != 0) { + qsort((void *) candidates->elts, candidates->nelts, + sizeof(fnames), fname_alphasort); + + /* + * Now recurse these... we handle errors and subdirectories + * via the recursion, which is nice + */ + for (current = 0; current < candidates->nelts; ++current) { + fnew = &((fnames *) candidates->elts)[current]; + error = process_resource_config_nofnmatch(fnew->fname, + ari, p, ptemp, + depth, optional); + if (error) { + return error; + } + } + } + + return NULL; + } + + return ap_process_resource_config(fname, ari, ptemp); +} + +static const char *process_resource_config_fnmatch(const char *path, + const char *fname, + apr_array_header_t *ari, + apr_pool_t *p, + apr_pool_t *ptemp, + unsigned depth, + int optional) +{ + const char *rest; + apr_status_t rv; + apr_dir_t *dirp; + apr_finfo_t dirent; + apr_array_header_t *candidates = NULL; + fnames *fnew; + int current; + + /* find the first part of the filename */ + rest = ap_strchr_c(fname, '/'); + if (rest) { + fname = apr_pstrndup(ptemp, fname, rest - fname); + rest++; + } + + /* optimisation - if the filename isn't a wildcard, process it directly */ + if (!apr_fnmatch_test(fname)) { + path = ap_make_full_path(ptemp, path, fname); + if (!rest) { + return process_resource_config_nofnmatch(path, + ari, p, + ptemp, 0, optional); + } + else { + return process_resource_config_fnmatch(path, rest, + ari, p, + ptemp, 0, optional); + } + } + + /* + * first course of business is to grok all the directory + * entries here and store 'em away. Recall we need full pathnames + * for this. + */ + rv = apr_dir_open(&dirp, path, ptemp); + if (rv != APR_SUCCESS) { + char errmsg[120]; + return apr_psprintf(p, "Could not open config directory %s: %s", + path, apr_strerror(rv, errmsg, sizeof errmsg)); + } + + candidates = apr_array_make(ptemp, 1, sizeof(fnames)); + while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp) == APR_SUCCESS) { + /* strip out '.' and '..' */ + if (strcmp(dirent.name, ".") + && strcmp(dirent.name, "..") + && (apr_fnmatch(fname, dirent.name, + APR_FNM_PERIOD) == APR_SUCCESS)) { + const char *full_path = ap_make_full_path(ptemp, path, dirent.name); + /* If matching internal to path, and we happen to match something + * other than a directory, skip it + */ + if (rest && (rv == APR_SUCCESS) && (dirent.filetype != APR_DIR)) { + continue; + } + fnew = (fnames *) apr_array_push(candidates); + fnew->fname = full_path; + } + } + + apr_dir_close(dirp); + if (candidates->nelts != 0) { + const char *error; + + qsort((void *) candidates->elts, candidates->nelts, + sizeof(fnames), fname_alphasort); + + /* + * Now recurse these... we handle errors and subdirectories + * via the recursion, which is nice + */ + for (current = 0; current < candidates->nelts; ++current) { + fnew = &((fnames *) candidates->elts)[current]; + if (!rest) { + error = process_resource_config_nofnmatch(fnew->fname, + ari, p, + ptemp, 0, optional); + } + else { + error = process_resource_config_fnmatch(fnew->fname, rest, + ari, p, + ptemp, 0, optional); + } + if (error) { + return error; + } + } + } + else { + + if (!optional) { + return apr_psprintf(p, "No matches for the wildcard '%s' in '%s', failing " + "(use IncludeOptional if required)", fname, path); + } + } + + return NULL; +} + +AP_DECLARE(const char *) ap_process_fnmatch_configs(apr_array_header_t *ari, + const char *fname, + apr_pool_t *p, + apr_pool_t *ptemp, + int optional) +{ + if (!apr_fnmatch_test(fname)) { + return ap_process_resource_config(fname, ari, p); + } + else { + apr_status_t status; + const char *rootpath, *filepath = fname; + + /* locate the start of the directories proper */ + status = apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME, ptemp); + + /* we allow APR_SUCCESS and APR_EINCOMPLETE */ + if (APR_ERELATIVE == status) { + return apr_pstrcat(p, "Include must have an absolute path, ", fname, NULL); + } + else if (APR_EBADPATH == status) { + return apr_pstrcat(p, "Include has a bad path, ", fname, NULL); + } + + /* walk the filepath */ + return process_resource_config_fnmatch(rootpath, filepath, ari, p, ptemp, + 0, optional); + } +} + +const char *populate_include_files(apr_pool_t *p, apr_pool_t *ptemp, apr_array_header_t *ari, const char *fname, int optional) +{ + return ap_process_fnmatch_configs(ari, fname, p, ptemp, optional); +} + const char *process_command_config(server_rec *s, void *mconfig, apr_pool_t *p, @@ -709,74 +984,153 @@ const char *process_command_config(server_rec *s, const char *filename) { const char *errmsg; - cmd_parms parms; char *l = apr_palloc (ptemp, MAX_STRING_LEN); const char *args = l; - char *cmd_name; + char *cmd_name, *w; const command_rec *cmd; - apr_array_header_t *arr = apr_array_make(p, 1, sizeof(char *)); + apr_array_header_t *arr = apr_array_make(p, 1, sizeof(cmd_parms)); + apr_array_header_t *ari = apr_array_make(p, 1, sizeof(char *)); + cmd_parms *parms; apr_status_t status; ap_directive_t *newdir; + int optional; - parms = default_parms; - parms.pool = p; - parms.temp_pool = ptemp; - parms.server = s; - parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT); - parms.override_opts = OPT_ALL | OPT_SYM_OWNER | OPT_MULTI; + //*(char **)apr_array_push(ari) = (char *)filename; + errmsg = populate_include_files(p, ptemp, ari, filename, 0); - status = ap_pcfg_openfile(&parms.config_file, p, filename); + if(errmsg != NULL) + goto Exit; - if(status != APR_SUCCESS) + while(ari->nelts != 0 || arr->nelts != 0) { - // cannot open config file - // - } - - while (!(ap_cfg_getline(l, MAX_STRING_LEN, parms.config_file))) { - if (*l == '#' || *l == '\0') - continue; - - args = l; - - cmd_name = ap_getword_conf(p, &args); - if (*cmd_name == '\0') - continue; - - cmd = ap_find_command(cmd_name, security2_module.cmds); - - if(cmd == NULL) + if(ari->nelts > 0) { - // unknown command, should error - // - printf("Unknown command: %s\n", cmd_name); - continue; + char *fn = *(char **)apr_array_pop(ari); + + parms = (cmd_parms *)apr_array_push(arr); + *parms = default_parms; + parms->pool = p; + parms->temp_pool = ptemp; + parms->server = s; + parms->override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT); + parms->override_opts = OPT_ALL | OPT_SYM_OWNER | OPT_MULTI; + + status = ap_pcfg_openfile(&parms->config_file, p, fn); + + if(status != APR_SUCCESS) + { + apr_array_pop(arr); + errmsg = apr_pstrcat(p, "Cannot open config file: ", fn, NULL); + goto Exit; + } } - newdir = apr_pcalloc(p, sizeof(ap_directive_t)); - newdir->filename = parms.config_file->name; - newdir->line_num = parms.config_file->line_number; - newdir->directive = cmd_name; - newdir->args = apr_pstrdup(p, args); + if (arr->nelts > 1024) { + errmsg = "Exceeded the maximum include directory nesting level. You have " + "probably a recursion somewhere."; + goto Exit; + } - parms.directive = newdir; + parms = (cmd_parms *)apr_array_pop(arr); - errmsg = invoke_cmd(cmd, &parms, mconfig, args); + if(parms == NULL) + break; + + while (!(ap_cfg_getline(l, MAX_STRING_LEN, parms->config_file))) { + if (*l == '#' || *l == '\0') + continue; + + args = l; + + cmd_name = ap_getword_conf(p, &args); + + if (*cmd_name == '\0') + continue; + + if (!strcasecmp(cmd_name, "IncludeOptional")) + { + optional = 1; + goto ProcessInclude; + } + + if (!strcasecmp(cmd_name, "Include")) + { + optional = 0; +ProcessInclude: + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args != 0) + { + ap_cfg_closefile(parms->config_file); + errmsg = apr_pstrcat(parms->pool, "Include takes one argument", NULL); + goto Exit; + } + + errmsg = populate_include_files(p, ptemp, ari, w, optional); + + *(cmd_parms *)apr_array_push(arr) = *parms; + + if(errmsg != NULL) + goto Exit; + + // we don't want to close the current file yet + // + parms = NULL; + break; + } + + cmd = ap_find_command(cmd_name, security2_module.cmds); + + if(cmd == NULL) + { + // unknown command, should error + // + ap_cfg_closefile(parms->config_file); + errmsg = apr_pstrcat(p, "Unknown command in config: ", cmd_name, NULL); + goto Exit; + } + + newdir = apr_pcalloc(p, sizeof(ap_directive_t)); + newdir->filename = parms->config_file->name; + newdir->line_num = parms->config_file->line_number; + newdir->directive = cmd_name; + newdir->args = apr_pstrdup(p, args); + + parms->directive = newdir; + + errmsg = invoke_cmd(cmd, parms, mconfig, args); + + if(errmsg != NULL) + break; + } + + if(parms != NULL) + ap_cfg_closefile(parms->config_file); if(errmsg != NULL) break; } - ap_cfg_closefile(parms.config_file); + while((parms = (cmd_parms *)apr_array_pop(arr)) != NULL) + { + ap_cfg_closefile(parms->config_file); + } if (errmsg) { char *err = (char *)apr_palloc(p, 1024); - apr_snprintf(err, 1024, "Syntax error in config file %s, line %d: %s", parms.config_file->name, - parms.config_file->line_number, errmsg); + apr_snprintf(err, 1024, "Syntax error in config file %s, line %d: %s", parms->config_file->name, + parms->config_file->line_number, errmsg); return err; } return NULL; +Exit: + while((parms = (cmd_parms *)apr_array_pop(arr)) != NULL) + { + ap_cfg_closefile(parms->config_file); + } + + return errmsg; }