Skip to content

Commit e1e5ce5

Browse files
authored
Merge pull request #104
Make ModSecurity IIS work with SecStreamInBodyInspection option disabled to prevent memory leak
2 parents 99e0a5d + 87bd3ad commit e1e5ce5

File tree

1 file changed

+53
-64
lines changed

1 file changed

+53
-64
lines changed

iis/mymodule.cpp

Lines changed: 53 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -448,58 +448,62 @@ static HRESULT SaveRequestBodyToRequestRec(RequestStoredContext* rsc)
448448
APR_BRIGADE_INSERT_TAIL(brigade, bucket);
449449
}
450450

451-
apr_bucket* e = apr_bucket_eos_create(conn->bucket_alloc);
452-
if (e == nullptr)
453-
{
454-
return E_OUTOFMEMORY;
455-
}
456-
APR_BRIGADE_INSERT_TAIL(brigade, e);
451+
if (!APR_BRIGADE_EMPTY(brigade)) {
452+
apr_bucket* e = apr_bucket_eos_create(conn->bucket_alloc);
453+
if (e == nullptr)
454+
{
455+
return E_OUTOFMEMORY;
456+
}
457+
APR_BRIGADE_INSERT_TAIL(brigade, e);
457458

458-
modsecSetBodyBrigade(aprRequest, brigade);
459+
modsecSetBodyBrigade(aprRequest, brigade);
459460

460-
apr_off_t contentLength = 0;
461-
apr_status_t status = apr_brigade_length(brigade, FALSE, &contentLength);
462-
if (status != APR_SUCCESS)
463-
{
464-
return E_FAIL;
465-
}
461+
apr_off_t contentLength = 0;
462+
apr_status_t status = apr_brigade_length(brigade, FALSE, &contentLength);
463+
if (status != APR_SUCCESS)
464+
{
465+
return E_FAIL;
466+
}
466467

467-
// Remove/Modify Transfer-Encoding header if "chunked" Encoding is set in the request.
468-
// This is to avoid sending both Content-Length and Chunked Transfer-Encoding in the request header.
469-
static const std::string CHUNKED = "chunked";
470-
USHORT encodingLength = 0;
471-
const char* transferEncoding = iisRequest->GetHeader(HttpHeaderTransferEncoding, &encodingLength);
472-
if (transferEncoding)
473-
{
474-
if (CHUNKED.size() == encodingLength &&
475-
std::equal(CHUNKED.cbegin(), CHUNKED.cend(), transferEncoding,
476-
[](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }))
468+
// Remove/Modify Transfer-Encoding header if "chunked" Encoding is set in the request.
469+
// This is to avoid sending both Content-Length and Chunked Transfer-Encoding in the request header.
470+
static const std::string CHUNKED = "chunked";
471+
USHORT encodingLength = 0;
472+
const char* transferEncoding = iisRequest->GetHeader(HttpHeaderTransferEncoding, &encodingLength);
473+
if (transferEncoding)
477474
{
478-
iisRequest->DeleteHeader(HttpHeaderTransferEncoding);
475+
if (encodingLength == CHUNKED.size() &&
476+
std::equal(CHUNKED.cbegin(), CHUNKED.cend(), transferEncoding,
477+
[](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }))
478+
{
479+
iisRequest->DeleteHeader(HttpHeaderTransferEncoding);
480+
}
479481
}
480-
}
481482

482-
auto contentLengthStr = std::to_string(contentLength);
483-
HRESULT hr = iisRequest->SetHeader(
484-
HttpHeaderContentLength,
485-
contentLengthStr.c_str(),
486-
contentLengthStr.size(),
487-
TRUE);
483+
auto contentLengthStr = std::to_string(contentLength);
484+
HRESULT hr = iisRequest->SetHeader(
485+
HttpHeaderContentLength,
486+
contentLengthStr.c_str(),
487+
contentLengthStr.size(),
488+
TRUE);
488489

489-
if (FAILED(hr))
490-
{
491-
return hr;
492-
}
490+
if (FAILED(hr))
491+
{
492+
return hr;
493+
}
493494

494-
// since we clean the APR pool at the end of OnSendRequest, we must get IIS-managed memory chunk
495-
//
496-
char* requestBuffer = static_cast<char*>(rsc->httpContext->AllocateRequestMemory(contentLength));
497-
status = apr_brigade_flatten(brigade, requestBuffer, reinterpret_cast<apr_size_t*>(&contentLength));
498-
if (status != APR_SUCCESS)
499-
{
500-
return E_FAIL;
495+
// since we clean the APR pool at the end of OnSendRequest, we must get IIS-managed memory chunk
496+
//
497+
char* requestBuffer = static_cast<char*>(rsc->httpContext->AllocateRequestMemory(contentLength));
498+
status = apr_brigade_flatten(brigade, requestBuffer, reinterpret_cast<apr_size_t*>(&contentLength));
499+
if (status != APR_SUCCESS)
500+
{
501+
return E_FAIL;
502+
}
503+
return iisRequest->InsertEntityBody(requestBuffer, contentLength);
501504
}
502-
return iisRequest->InsertEntityBody(requestBuffer, contentLength);
505+
506+
return S_OK;
503507
}
504508

505509

@@ -953,17 +957,6 @@ CMyHttpModule::OnBeginRequest(IHttpContext* httpContext, IHttpEventProvider* pro
953957

954958
delete apppath;
955959
delete path;
956-
957-
if (config->config->is_enabled == MODSEC_DETECTION_ONLY)
958-
{
959-
modsecSetReadBody(nullptr);
960-
modsecSetWriteBody(nullptr);
961-
}
962-
else
963-
{
964-
modsecSetReadBody(ReadBodyCallback);
965-
modsecSetWriteBody(WriteBodyCallback);
966-
}
967960
}
968961

969962
ConnRecPtr connRec = MakeConnReq();
@@ -979,11 +972,6 @@ CMyHttpModule::OnBeginRequest(IHttpContext* httpContext, IHttpEventProvider* pro
979972
rsc->responseProcessingEnabled = (config->config->resbody_access == 1);
980973
RequestStoredContext* context = rsc.get();
981974

982-
// on IIS we force input stream inspection flag, because its absence does not add any performance gain
983-
// it's because on IIS request body must be restored each time it was read
984-
//
985-
modsecSetConfigForIISRequestBody(r);
986-
987975
StoreIISContext(r, rsc.get());
988976

989977
httpContext->GetModuleContextContainer()->SetModuleContext(rsc.release(), g_pModuleContext);
@@ -1214,13 +1202,14 @@ CMyHttpModule::OnBeginRequest(IHttpContext* httpContext, IHttpEventProvider* pro
12141202
#endif
12151203
c->remote_host = NULL;
12161204

1205+
hr = SaveRequestBodyToRequestRec(context);
1206+
if (FAILED(hr)) {
1207+
context->provider->SetErrorStatus(hr);
1208+
return RQ_NOTIFICATION_FINISH_REQUEST;
1209+
}
1210+
12171211
if (config->config->is_enabled == MODSEC_DETECTION_ONLY)
12181212
{
1219-
hr = SaveRequestBodyToRequestRec(context);
1220-
if (FAILED(hr)) {
1221-
context->provider->SetErrorStatus(hr);
1222-
return RQ_NOTIFICATION_FINISH_REQUEST;
1223-
}
12241213
// We post the processing task to the thread pool to happen in the background.
12251214
// We store the future to track and wait for this processing in case if we also
12261215
// need to process the response because we need request processing to finish

0 commit comments

Comments
 (0)