1 /*
2 * ModSecurity for Apache 2.x, http://www.modsecurity.org/
3 * Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/)
4 *
5 * You may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * If any of the files related to licensing are missing or if you have any
11 * other questions related to licensing please contact Trustwave Holdings, Inc.
12 * directly using the email address security@modsecurity.org.
13 */
14 
15 #define WIN32_LEAN_AND_MEAN
16 
17 #undef inline
18 #define inline inline
19 
20 //  IIS7 Server API header file
21 #include <Windows.h>
22 #include <sal.h>
23 #include <strsafe.h>
24 #include "httpserv.h"
25 
26 //  Project header files
27 #include "mymodule.h"
28 #include "mymodulefactory.h"
29 
30 #include "api.h"
31 #include "moduleconfig.h"
32 
33 #include "winsock2.h"
34 
35 
36 class REQUEST_STORED_CONTEXT : public IHttpStoredContext
37 {
38  public:
REQUEST_STORED_CONTEXT()39     REQUEST_STORED_CONTEXT()
40 	{
41 		m_pConnRec = NULL;
42 		m_pRequestRec = NULL;
43 		m_pHttpContext = NULL;
44 		m_pProvider = NULL;
45 		m_pResponseBuffer = NULL;
46 		m_pResponseLength = 0;
47 		m_pResponsePosition = 0;
48 	}
49 
~REQUEST_STORED_CONTEXT()50     ~REQUEST_STORED_CONTEXT()
51 	{
52 		FinishRequest();
53 	}
54 
55     // virtual
56     VOID
CleanupStoredContext(VOID)57     CleanupStoredContext(
58         VOID
59     )
60     {
61 		FinishRequest();
62         delete this;
63     }
64 
FinishRequest()65 	void FinishRequest()
66 	{
67 		if(m_pRequestRec != NULL)
68 		{
69 			modsecFinishRequest(m_pRequestRec);
70 			m_pRequestRec = NULL;
71 		}
72 		if(m_pConnRec != NULL)
73 		{
74 			modsecFinishConnection(m_pConnRec);
75 			m_pConnRec = NULL;
76 		}
77 	}
78 
79 	conn_rec			*m_pConnRec;
80 	request_rec			*m_pRequestRec;
81 	IHttpContext		*m_pHttpContext;
82 	IHttpEventProvider	*m_pProvider;
83 	char				*m_pResponseBuffer;
84 	ULONGLONG			m_pResponseLength;
85 	ULONGLONG			m_pResponsePosition;
86 };
87 
88 
89 //----------------------------------------------------------------------------
90 
GetIpAddr(apr_pool_t * pool,PSOCKADDR pAddr)91 char *GetIpAddr(apr_pool_t *pool, PSOCKADDR pAddr)
92 {
93 	const char *format = "%15[0-9.]:%5[0-9]";
94 	char ip[16] = { 0 };  // ip4 addresses have max len 15
95 	char port[6] = { 0 }; // port numbers are 16bit, ie 5 digits max
96 
97 	DWORD len = 50;
98 	char *buf = (char *)apr_palloc(pool, len);
99 
100 	if(buf == NULL)
101 		return "";
102 
103 	buf[0] = 0;
104 
105 	WSAAddressToString(pAddr, sizeof(SOCKADDR), NULL, buf, &len);
106 
107 	// test for IPV4 with port on the end
108 	if (sscanf(buf, format, ip, port) == 2) {
109 		// IPV4 but with port - remove the port
110 		char* input = ":";
111 		char* ipv4 = strtok(buf, input);
112 		return ipv4;
113 	}
114 
115 	return buf;
116 }
117 
CopySockAddr(apr_pool_t * pool,PSOCKADDR pAddr)118 apr_sockaddr_t *CopySockAddr(apr_pool_t *pool, PSOCKADDR pAddr)
119 {
120     apr_sockaddr_t *addr = (apr_sockaddr_t *)apr_palloc(pool, sizeof(apr_sockaddr_t));
121 	int adrlen = 16, iplen = 4;
122 
123 	if(pAddr->sa_family == AF_INET6)
124 	{
125 		adrlen = 46;
126 		iplen = 16;
127 	}
128 
129 	addr->addr_str_len = adrlen;
130 	addr->family = pAddr->sa_family;
131 
132 	addr->hostname = "unknown";
133 #ifdef WIN32
134     addr->ipaddr_len = sizeof(IN_ADDR);
135 #else
136     addr->ipaddr_len = sizeof(struct in_addr);
137 #endif
138     addr->ipaddr_ptr = &addr->sa.sin.sin_addr;
139     addr->pool = pool;
140     addr->port = 80;
141 #ifdef WIN32
142 	memcpy(&addr->sa.sin.sin_addr.S_un.S_addr, pAddr->sa_data, iplen);
143 #else
144     memcpy(&addr->sa.sin.sin_addr.s_addr, pAddr->sa_data, iplen);
145 #endif
146 	addr->sa.sin.sin_family = pAddr->sa_family;
147     addr->sa.sin.sin_port = 80;
148     addr->salen = sizeof(addr->sa);
149 	addr->servname = addr->hostname;
150 
151 	return addr;
152 }
153 
154 //----------------------------------------------------------------------------
155 
ZeroTerminate(const char * str,size_t len,apr_pool_t * pool)156 char *ZeroTerminate(const char *str, size_t len, apr_pool_t *pool)
157 {
158 	char *_n = (char *)apr_palloc(pool, len + 1);
159 
160 	memcpy(_n, str, len);
161 
162 	_n[len] = 0;
163 
164 	return _n;
165 }
166 
167 //----------------------------------------------------------------------------
168 // FUNCTION: ConvertUTF16ToUTF8
169 // DESC: Converts Unicode UTF-16 (Windows default) text to Unicode UTF-8.
170 //----------------------------------------------------------------------------
171 
ConvertUTF16ToUTF8(__in const WCHAR * pszTextUTF16,size_t cchUTF16,apr_pool_t * pool)172 char *ConvertUTF16ToUTF8( __in const WCHAR * pszTextUTF16, size_t cchUTF16, apr_pool_t *pool )
173 {
174     //
175     // Special case of NULL or empty input string
176     //
177     if ( (pszTextUTF16 == NULL) || (*pszTextUTF16 == L'\0') || cchUTF16 == 0 )
178     {
179         // Return empty string
180         return "";
181     }
182 
183     //
184     // Get size of destination UTF-8 buffer, in CHAR's (= bytes)
185     //
186     int cbUTF8 = ::WideCharToMultiByte(
187         CP_UTF8,                // convert to UTF-8
188         0,      // specify conversion behavior
189         pszTextUTF16,           // source UTF-16 string
190         static_cast<int>( cchUTF16 ),   // total source string length, in WCHAR's,
191         NULL,                   // unused - no conversion required in this step
192         0,                      // request buffer size
193         NULL, NULL              // unused
194         );
195 
196     if ( cbUTF8 == 0 )
197     {
198 		return "";
199     }
200 
201     //
202     // Allocate destination buffer for UTF-8 string
203     //
204 
205     int cchUTF8 = cbUTF8; // sizeof(CHAR) = 1 byte
206 
207     char *pszUTF8 = (char *)apr_palloc(pool, cchUTF8 + 1 );
208 
209     //
210     // Do the conversion from UTF-16 to UTF-8
211     //
212     int result = ::WideCharToMultiByte(
213         CP_UTF8,                // convert to UTF-8
214         0,      // specify conversion behavior
215         pszTextUTF16,           // source UTF-16 string
216         static_cast<int>( cchUTF16 ),   // total source string length, in WCHAR's,
217                                         // including end-of-string \0
218         pszUTF8,                // destination buffer
219         cbUTF8,                 // destination buffer size, in bytes
220         NULL, NULL              // unused
221         );
222 
223     if ( result == 0 )
224     {
225 		return "";
226     }
227 
228 	pszUTF8[cchUTF8] =  0;
229 
230     return pszUTF8;
231 }
232 
Log(void * obj,int level,char * str)233 void Log(void *obj, int level, char *str)
234 {
235 	CMyHttpModule *mod = (CMyHttpModule *)obj;
236 	WORD logcat = EVENTLOG_INFORMATION_TYPE;
237 
238 	level &= APLOG_LEVELMASK;
239 
240 	if(level <= APLOG_ERR)
241 		logcat = EVENTLOG_ERROR_TYPE;
242 
243 	if(level == APLOG_WARNING || strstr(str, "Warning.") != NULL)
244 		logcat = EVENTLOG_WARNING_TYPE;
245 
246 	mod->WriteEventViewerLog(str, logcat);
247 }
248 
249 #define NOTE_IIS "iis-tx-context"
250 
StoreIISContext(request_rec * r,REQUEST_STORED_CONTEXT * rsc)251 void StoreIISContext(request_rec *r, REQUEST_STORED_CONTEXT *rsc)
252 {
253     apr_table_setn(r->notes, NOTE_IIS, (const char *)rsc);
254 }
255 
RetrieveIISContext(request_rec * r)256 REQUEST_STORED_CONTEXT *RetrieveIISContext(request_rec *r)
257 {
258     REQUEST_STORED_CONTEXT *msr = NULL;
259     request_rec *rx = NULL;
260 
261     /* Look in the current request first. */
262     msr = (REQUEST_STORED_CONTEXT *)apr_table_get(r->notes, NOTE_IIS);
263     if (msr != NULL) {
264         //msr->r = r;
265         return msr;
266     }
267 
268     /* If this is a subrequest then look in the main request. */
269     if (r->main != NULL) {
270         msr = (REQUEST_STORED_CONTEXT *)apr_table_get(r->main->notes, NOTE_IIS);
271         if (msr != NULL) {
272             //msr->r = r;
273             return msr;
274         }
275     }
276 
277     /* If the request was redirected then look in the previous requests. */
278     rx = r->prev;
279     while(rx != NULL) {
280         msr = (REQUEST_STORED_CONTEXT *)apr_table_get(rx->notes, NOTE_IIS);
281         if (msr != NULL) {
282             //msr->r = r;
283             return msr;
284         }
285         rx = rx->prev;
286     }
287 
288     return NULL;
289 }
290 
291 
ReadFileChunk(HTTP_DATA_CHUNK * chunk,char * buf)292 HRESULT CMyHttpModule::ReadFileChunk(HTTP_DATA_CHUNK *chunk, char *buf)
293 {
294     OVERLAPPED ovl;
295     DWORD dwDataStartOffset;
296     ULONGLONG bytesTotal = 0;
297 	BYTE *	pIoBuffer = NULL;
298 	HANDLE	hIoEvent = INVALID_HANDLE_VALUE;
299 	HRESULT hr = S_OK;
300 
301     pIoBuffer = (BYTE *)VirtualAlloc(NULL,
302                                         1,
303                                         MEM_COMMIT | MEM_RESERVE,
304                                         PAGE_READWRITE);
305     if (pIoBuffer == NULL)
306     {
307         hr = HRESULT_FROM_WIN32(GetLastError());
308 		goto Done;
309     }
310 
311     hIoEvent = CreateEvent(NULL,  // security attr
312                                 FALSE, // manual reset
313                                 FALSE, // initial state
314                                 NULL); // name
315     if (hIoEvent == NULL)
316     {
317         hr = HRESULT_FROM_WIN32(GetLastError());
318 		goto Done;
319     }
320 
321 	while(bytesTotal < chunk->FromFileHandle.ByteRange.Length.QuadPart)
322 	{
323 		DWORD bytesRead = 0;
324 		int was_eof = 0;
325 		ULONGLONG offset = chunk->FromFileHandle.ByteRange.StartingOffset.QuadPart + bytesTotal;
326 
327 		ZeroMemory(&ovl, sizeof ovl);
328 		ovl.hEvent     = hIoEvent;
329 		ovl.Offset = (DWORD)offset;
330 		dwDataStartOffset = ovl.Offset & (m_dwPageSize - 1);
331 		ovl.Offset &= ~(m_dwPageSize - 1);
332 		ovl.OffsetHigh = offset >> 32;
333 
334 		if (!ReadFile(chunk->FromFileHandle.FileHandle,
335 					  pIoBuffer,
336 					  m_dwPageSize,
337 					  &bytesRead,
338 					  &ovl))
339 		{
340 			DWORD dwErr = GetLastError();
341 
342 			switch (dwErr)
343 			{
344 			case ERROR_IO_PENDING:
345 				//
346 				// GetOverlappedResult can return without waiting for the
347 				// event thus leaving it signalled and causing problems
348 				// with future use of that event handle, so just wait ourselves
349 				//
350 				WaitForSingleObject(ovl.hEvent, INFINITE); // == WAIT_OBJECT_0);
351 
352 				if (!GetOverlappedResult(
353 						 chunk->FromFileHandle.FileHandle,
354 						 &ovl,
355 						 &bytesRead,
356 						 TRUE))
357 				{
358 					dwErr = GetLastError();
359 
360 					switch(dwErr)
361 					{
362 					case ERROR_HANDLE_EOF:
363 						was_eof = 1;
364 						break;
365 
366 					default:
367 						hr = HRESULT_FROM_WIN32(dwErr);
368 						goto Done;
369 					}
370 				}
371 				break;
372 
373 			case ERROR_HANDLE_EOF:
374 				was_eof = 1;
375 				break;
376 
377 			default:
378 				hr = HRESULT_FROM_WIN32(dwErr);
379 				goto Done;
380 			}
381 		}
382 
383 		bytesRead -= dwDataStartOffset;
384 
385 		if (bytesRead > chunk->FromFileHandle.ByteRange.Length.QuadPart)
386 		{
387 			bytesRead = (DWORD)chunk->FromFileHandle.ByteRange.Length.QuadPart;
388 		}
389 		if ((bytesTotal + bytesRead) > chunk->FromFileHandle.ByteRange.Length.QuadPart)
390 		{
391 			bytesRead = chunk->FromFileHandle.ByteRange.Length.QuadPart - bytesTotal;
392 		}
393 
394 		memcpy(buf, pIoBuffer + dwDataStartOffset, bytesRead);
395 
396 		buf += bytesRead;
397 		bytesTotal += bytesRead;
398 
399 		if(was_eof != 0)
400 			chunk->FromFileHandle.ByteRange.Length.QuadPart = bytesTotal;
401 	}
402 
403 Done:
404 	if(NULL != pIoBuffer)
405 	{
406 		VirtualFree(pIoBuffer, 0, MEM_RELEASE);
407 	}
408 
409 	if(INVALID_HANDLE_VALUE != hIoEvent)
410 	{
411 		CloseHandle(hIoEvent);
412 	}
413 
414 	return hr;
415 }
416 
417 REQUEST_NOTIFICATION_STATUS
OnSendResponse(IN IHttpContext * pHttpContext,IN ISendResponseProvider * pResponseProvider)418 CMyHttpModule::OnSendResponse(
419     IN IHttpContext * pHttpContext,
420     IN ISendResponseProvider * pResponseProvider
421 )
422 {
423 	REQUEST_STORED_CONTEXT *rsc = NULL;
424 
425 	rsc = (REQUEST_STORED_CONTEXT *)pHttpContext->GetModuleContextContainer()->GetModuleContext(g_pModuleContext);
426 
427 	EnterCriticalSection(&m_csLock);
428 
429 	// here we must check if response body processing is enabled
430 	//
431 	if(rsc == NULL || rsc->m_pRequestRec == NULL || rsc->m_pResponseBuffer != NULL || !modsecIsResponseBodyAccessEnabled(rsc->m_pRequestRec))
432 	{
433 		goto Exit;
434 	}
435 
436     HRESULT hr = S_OK;
437 	IHttpResponse *pHttpResponse = NULL;
438     HTTP_RESPONSE *pRawHttpResponse = NULL;
439     HTTP_BYTE_RANGE *pFileByteRange = NULL;
440     HTTP_DATA_CHUNK *pSourceDataChunk = NULL;
441     LARGE_INTEGER  lFileSize;
442     REQUEST_NOTIFICATION_STATUS ret = RQ_NOTIFICATION_CONTINUE;
443 	ULONGLONG ulTotalLength = 0;
444 	DWORD c;
445 	request_rec *r = rsc->m_pRequestRec;
446 
447 	pHttpResponse = pHttpContext->GetResponse();
448 	pRawHttpResponse = pHttpResponse->GetRawHttpResponse();
449 
450 	// here we must add handling of chunked response
451 	// apparently IIS 7 calls this handler once per chunk
452 	// see: http://stackoverflow.com/questions/4385249/how-to-buffer-and-process-chunked-data-before-sending-headers-in-iis7-native-mod
453 
454 	if(pRawHttpResponse->EntityChunkCount == 0)
455 		goto Exit;
456 
457 	// here we must transfer response headers
458 	//
459 	USHORT ctcch = 0;
460 	char *ct = (char *)pHttpResponse->GetHeader(HttpHeaderContentType, &ctcch);
461 	char *ctz = ZeroTerminate(ct, ctcch, r->pool);
462 
463 	// assume HTML if content type not set
464 	// without this output filter would not buffer response and processing would hang
465 	//
466 	if(ctz[0] == 0)
467 		ctz = "text/html";
468 
469 	r->content_type = ctz;
470 
471 #define _TRANSHEADER(id,str) if(pRawHttpResponse->Headers.KnownHeaders[id].pRawValue != NULL) \
472 	{\
473 		apr_table_setn(r->headers_out, str, \
474 			ZeroTerminate(pRawHttpResponse->Headers.KnownHeaders[id].pRawValue, pRawHttpResponse->Headers.KnownHeaders[id].RawValueLength, r->pool)); \
475 	}
476 
477     _TRANSHEADER(HttpHeaderCacheControl, "Cache-Control");
478     _TRANSHEADER(HttpHeaderConnection, "Connection");
479     _TRANSHEADER(HttpHeaderDate, "Date");
480     _TRANSHEADER(HttpHeaderKeepAlive, "Keep-Alive");
481     _TRANSHEADER(HttpHeaderPragma, "Pragma");
482     _TRANSHEADER(HttpHeaderTrailer, "Trailer");
483     _TRANSHEADER(HttpHeaderTransferEncoding, "Transfer-Encoding");
484     _TRANSHEADER(HttpHeaderUpgrade, "Upgrade");
485     _TRANSHEADER(HttpHeaderVia, "Via");
486     _TRANSHEADER(HttpHeaderWarning, "Warning");
487     _TRANSHEADER(HttpHeaderAllow, "Allow");
488     _TRANSHEADER(HttpHeaderContentLength, "Content-Length");
489     _TRANSHEADER(HttpHeaderContentType, "Content-Type");
490     _TRANSHEADER(HttpHeaderContentEncoding, "Content-Encoding");
491     _TRANSHEADER(HttpHeaderContentLanguage, "Content-Language");
492     _TRANSHEADER(HttpHeaderContentLocation, "Content-Location");
493     _TRANSHEADER(HttpHeaderContentMd5, "Content-Md5");
494     _TRANSHEADER(HttpHeaderContentRange, "Content-Range");
495     _TRANSHEADER(HttpHeaderExpires, "Expires");
496     _TRANSHEADER(HttpHeaderLastModified, "Last-Modified");
497 	_TRANSHEADER(HttpHeaderAcceptRanges, "Accept-Ranges");
498     _TRANSHEADER(HttpHeaderAge, "Age");
499     _TRANSHEADER(HttpHeaderEtag, "Etag");
500     _TRANSHEADER(HttpHeaderLocation, "Location");
501     _TRANSHEADER(HttpHeaderProxyAuthenticate, "Proxy-Authenticate");
502     _TRANSHEADER(HttpHeaderRetryAfter, "Retry-After");
503     _TRANSHEADER(HttpHeaderServer, "Server");
504     _TRANSHEADER(HttpHeaderSetCookie, "Set-Cookie");
505     _TRANSHEADER(HttpHeaderVary, "Vary");
506     _TRANSHEADER(HttpHeaderWwwAuthenticate, "Www-Authenticate");
507 
508 #undef	_TRANSHEADER
509 
510 	for(int i = 0; i < pRawHttpResponse->Headers.UnknownHeaderCount; i++)
511 	{
512 		apr_table_setn(r->headers_out,
513 			ZeroTerminate(pRawHttpResponse->Headers.pUnknownHeaders[i].pName, pRawHttpResponse->Headers.pUnknownHeaders[i].NameLength, r->pool),
514 			ZeroTerminate(pRawHttpResponse->Headers.pUnknownHeaders[i].pRawValue, pRawHttpResponse->Headers.pUnknownHeaders[i].RawValueLength, r->pool));
515 	}
516 
517 	r->content_encoding = apr_table_get(r->headers_out, "Content-Encoding");
518 	//r->content_type = apr_table_get(r->headers_out, "Content-Type");		-- already set above
519 
520 	const char *lng = apr_table_get(r->headers_out, "Content-Languages");
521 
522 	if(lng != NULL)
523 	{
524 		r->content_languages = apr_array_make(r->pool, 1, sizeof(const char *));
525 
526 		*(const char **)apr_array_push(r->content_languages) = lng;
527 	}
528 
529 	// Disable kernel caching for this response
530 	// Probably we don't have to do it for ModSecurity
531 
532     //pHttpContext->GetResponse()->DisableKernelCache(
533     //        IISCacheEvents::HTTPSYS_CACHEABLE::HANDLER_HTTPSYS_UNFRIENDLY);
534 
535     for(c = 0; c < pRawHttpResponse->EntityChunkCount; c++ )
536     {
537         pSourceDataChunk = &pRawHttpResponse->pEntityChunks[ c ];
538 
539         switch( pSourceDataChunk->DataChunkType )
540         {
541             case HttpDataChunkFromMemory:
542                 ulTotalLength += pSourceDataChunk->FromMemory.BufferLength;
543                 break;
544             case HttpDataChunkFromFileHandle:
545                 pFileByteRange = &pSourceDataChunk->FromFileHandle.ByteRange;
546                 //
547                 // File chunks may contain by ranges with unspecified length
548                 // (HTTP_BYTE_RANGE_TO_EOF).  In order to send parts of such a chunk,
549                 // its necessary to know when the chunk is finished, and
550                 // we need to move to the next chunk.
551                 //
552                 if ( pFileByteRange->Length.QuadPart == HTTP_BYTE_RANGE_TO_EOF)
553                 {
554                     if ( GetFileType( pSourceDataChunk->FromFileHandle.FileHandle ) ==
555                                     FILE_TYPE_DISK )
556                     {
557                         if ( !GetFileSizeEx( pSourceDataChunk->FromFileHandle.FileHandle,
558                                              &lFileSize ) )
559                         {
560                             DWORD dwError = GetLastError();
561                             hr = HRESULT_FROM_WIN32(dwError);
562                             goto Finished;
563                         }
564 
565                         // put the resolved file length in the chunk, replacing
566                         // HTTP_BYTE_RANGE_TO_EOF
567                         pFileByteRange->Length.QuadPart =
568                               lFileSize.QuadPart - pFileByteRange->StartingOffset.QuadPart;
569                     }
570                     else
571                     {
572                         hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
573                         goto Finished;
574                     }
575                 }
576 
577                 ulTotalLength += pFileByteRange->Length.QuadPart;
578                 break;
579             default:
580                 // TBD: consider implementing HttpDataChunkFromFragmentCache,
581                 // and HttpDataChunkFromFragmentCacheEx
582                 hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
583                 goto Finished;
584         }
585     }
586 
587 	rsc->m_pResponseBuffer = (char *)apr_palloc(rsc->m_pRequestRec->pool, ulTotalLength);
588 
589 	ulTotalLength = 0;
590 
591     for(c = 0; c < pRawHttpResponse->EntityChunkCount; c++ )
592     {
593         pSourceDataChunk = &pRawHttpResponse->pEntityChunks[ c ];
594 
595         switch( pSourceDataChunk->DataChunkType )
596         {
597             case HttpDataChunkFromMemory:
598 				memcpy(rsc->m_pResponseBuffer + ulTotalLength, pSourceDataChunk->FromMemory.pBuffer, pSourceDataChunk->FromMemory.BufferLength);
599                 ulTotalLength += pSourceDataChunk->FromMemory.BufferLength;
600                 break;
601             case HttpDataChunkFromFileHandle:
602                 pFileByteRange = &pSourceDataChunk->FromFileHandle.ByteRange;
603 
604 				if(ReadFileChunk(pSourceDataChunk, rsc->m_pResponseBuffer + ulTotalLength) != S_OK)
605 				{
606 			        DWORD dwErr = GetLastError();
607 
608 					hr = HRESULT_FROM_WIN32(dwErr);
609 	                goto Finished;
610 				}
611 
612                 ulTotalLength += pFileByteRange->Length.QuadPart;
613                 break;
614             default:
615                 // TBD: consider implementing HttpDataChunkFromFragmentCache,
616                 // and HttpDataChunkFromFragmentCacheEx
617                 hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
618                 goto Finished;
619         }
620     }
621 
622 	rsc->m_pResponseLength = ulTotalLength;
623 
624 	//
625     // If there's no content-length set, we need to set it to avoid chunked transfer mode
626     // We can only do it if there is it's the only response to be sent.
627     //
628 
629     DWORD dwFlags = pResponseProvider->GetFlags();
630 
631 	if (pResponseProvider->GetHeadersBeingSent() &&
632          (dwFlags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0 &&
633          pHttpContext->GetResponse()->GetHeader(HttpHeaderContentLength) == NULL)
634     {
635         CHAR szLength[21]; //Max length for a 64 bit int is 20
636 
637          ZeroMemory(szLength, sizeof(szLength));
638 
639          hr = StringCchPrintfA(
640                     szLength,
641                     sizeof(szLength) / sizeof(CHAR) - 1, "%d",
642                     ulTotalLength);
643 
644         if(FAILED(hr))
645         {
646             goto Finished;
647         }
648 
649          hr = pHttpContext->GetResponse()->SetHeader(
650                     HttpHeaderContentLength,
651                     szLength,
652                     (USHORT)strlen(szLength),
653                     TRUE);
654 
655         if(FAILED(hr))
656         {
657             goto Finished;
658         }
659     }
660 
661 Finished:
662 
663 	int status = modsecProcessResponse(rsc->m_pRequestRec);
664 
665 	// the logic here is temporary, needs clarification
666 	//
667 	if(status != 0 && status != -1)
668 	{
669 		pHttpContext->GetResponse()->Clear();
670 		pHttpContext->GetResponse()->SetStatus(status, "ModSecurity Action");
671 		pHttpContext->SetRequestHandled();
672 
673 		rsc->FinishRequest();
674 
675 		LeaveCriticalSection(&m_csLock);
676 
677 		return RQ_NOTIFICATION_FINISH_REQUEST;
678 	}
679 Exit:
680 	// temporary hack, in reality OnSendRequest theoretically could possibly come before OnEndRequest
681 	//
682 	if(rsc != NULL)
683 		rsc->FinishRequest();
684 
685 	LeaveCriticalSection(&m_csLock);
686 
687 	return RQ_NOTIFICATION_CONTINUE;
688 }
689 
690 REQUEST_NOTIFICATION_STATUS
OnPostEndRequest(IN IHttpContext * pHttpContext,IN IHttpEventProvider * pProvider)691 CMyHttpModule::OnPostEndRequest(
692     IN IHttpContext * pHttpContext,
693     IN IHttpEventProvider * pProvider
694 )
695 {
696 	REQUEST_STORED_CONTEXT *rsc = NULL;
697 
698 	rsc = (REQUEST_STORED_CONTEXT *)pHttpContext->GetModuleContextContainer()->GetModuleContext(g_pModuleContext);
699 
700 	// only finish request if OnSendResponse have been called already
701 	//
702 	if(rsc != NULL && rsc->m_pResponseBuffer != NULL)
703 	{
704 		EnterCriticalSection(&m_csLock);
705 
706 		rsc->FinishRequest();
707 
708 		LeaveCriticalSection(&m_csLock);
709 	}
710 
711 	return RQ_NOTIFICATION_CONTINUE;
712 }
713 
714 REQUEST_NOTIFICATION_STATUS
OnBeginRequest(IN IHttpContext * pHttpContext,IN IHttpEventProvider * pProvider)715 CMyHttpModule::OnBeginRequest(
716     IN IHttpContext * pHttpContext,
717     IN IHttpEventProvider * pProvider
718 )
719 {
720     HRESULT                         hr                  = S_OK;
721     IHttpRequest*                   pRequest            = NULL;
722 	MODSECURITY_STORED_CONTEXT*		pConfig = NULL;
723 
724     UNREFERENCED_PARAMETER ( pProvider );
725 
726 	EnterCriticalSection(&m_csLock);
727 
728     if ( pHttpContext == NULL )
729     {
730         hr = E_UNEXPECTED;
731         goto Finished;
732     }
733 
734     pRequest = pHttpContext->GetRequest();
735 
736     if ( pRequest == NULL )
737     {
738         hr = E_UNEXPECTED;
739         goto Finished;
740     }
741 
742     hr = MODSECURITY_STORED_CONTEXT::GetConfig(pHttpContext, &pConfig );
743 
744     if ( FAILED( hr ) )
745     {
746         //hr = E_UNEXPECTED;
747 		hr = S_OK;
748         goto Finished;
749     }
750 
751 	// If module is disabled, dont go any further
752 	//
753 	if( pConfig->GetIsEnabled() == false )
754 	{
755         goto Finished;
756 	}
757 
758 	if(pConfig->m_Config == NULL)
759 	{
760 		char *path;
761 		USHORT pathlen;
762 
763 		hr = pConfig->GlobalWideCharToMultiByte(pConfig->GetPath(), wcslen(pConfig->GetPath()), &path, &pathlen);
764 
765 		if ( FAILED( hr ) )
766 		{
767 			hr = E_UNEXPECTED;
768 			goto Finished;
769 		}
770 
771 		pConfig->m_Config = modsecGetDefaultConfig();
772 
773 		PCWSTR servpath = pHttpContext->GetApplication()->GetApplicationPhysicalPath();
774 		char *apppath;
775 		USHORT apppathlen;
776 
777 		hr = pConfig->GlobalWideCharToMultiByte((WCHAR *)servpath, wcslen(servpath), &apppath, &apppathlen);
778 
779 		if ( FAILED( hr ) )
780 		{
781 			delete path;
782 			hr = E_UNEXPECTED;
783 			goto Finished;
784 		}
785 
786 		if(path[0] != 0)
787 		{
788 			const char * err = modsecProcessConfig((directory_config *)pConfig->m_Config, path, apppath);
789 
790 			if(err != NULL)
791 			{
792 				WriteEventViewerLog(err, EVENTLOG_ERROR_TYPE);
793 				delete apppath;
794 				delete path;
795 				goto Finished;
796 			}
797 
798 			modsecReportRemoteLoadedRules();
799 			if (this->status_call_already_sent == false)
800 			{
801 				this->status_call_already_sent = true;
802 				modsecStatusEngineCall();
803 			}
804 		}
805 
806 		delete apppath;
807 		delete path;
808 	}
809 
810 	conn_rec *c;
811 	request_rec *r;
812 
813 	c = modsecNewConnection();
814 
815 	modsecProcessConnection(c);
816 
817 	r = modsecNewRequest(c, (directory_config *)pConfig->m_Config);
818 
819 	// on IIS we force input stream inspection flag, because its absence does not add any performance gain
820 	// it's because on IIS request body must be restored each time it was read
821 	//
822 	modsecSetConfigForIISRequestBody(r);
823 
824 	REQUEST_STORED_CONTEXT *rsc = new REQUEST_STORED_CONTEXT();
825 
826 	rsc->m_pConnRec = c;
827 	rsc->m_pRequestRec = r;
828 	rsc->m_pHttpContext = pHttpContext;
829 	rsc->m_pProvider = pProvider;
830 
831 	pHttpContext->GetModuleContextContainer()->SetModuleContext(rsc, g_pModuleContext);
832 
833 	StoreIISContext(r, rsc);
834 
835 	HTTP_REQUEST *req = pRequest->GetRawHttpRequest();
836 
837 	r->hostname = ConvertUTF16ToUTF8(req->CookedUrl.pHost, req->CookedUrl.HostLength / sizeof(WCHAR), r->pool);
838 	r->path_info = ConvertUTF16ToUTF8(req->CookedUrl.pAbsPath, req->CookedUrl.AbsPathLength / sizeof(WCHAR), r->pool);
839 
840 	if(r->hostname == NULL)
841 	{
842 		if(req->Headers.KnownHeaders[HttpHeaderHost].pRawValue != NULL)
843 			r->hostname = ZeroTerminate(req->Headers.KnownHeaders[HttpHeaderHost].pRawValue,
844 										req->Headers.KnownHeaders[HttpHeaderHost].RawValueLength, r->pool);
845 	}
846 
847 	int port = 0;
848 	char *port_str = NULL;
849 
850 	if(r->hostname != NULL)
851 	{
852 		int k = 0;
853 		char *ptr = (char *)r->hostname;
854 
855 		while(*ptr != 0 && *ptr != ':')
856 			ptr++;
857 
858 		if(*ptr == ':')
859 		{
860 			*ptr = 0;
861 			port_str = ptr + 1;
862 			port = atoi(port_str);
863 		}
864 	}
865 
866 	if(req->CookedUrl.pQueryString != NULL && req->CookedUrl.QueryStringLength > 0)
867 		r->args = ConvertUTF16ToUTF8(req->CookedUrl.pQueryString + 1, (req->CookedUrl.QueryStringLength / sizeof(WCHAR)) - 1, r->pool);
868 
869 #define _TRANSHEADER(id,str) if(req->Headers.KnownHeaders[id].pRawValue != NULL) \
870 	{\
871 		apr_table_setn(r->headers_in, str, \
872 			ZeroTerminate(req->Headers.KnownHeaders[id].pRawValue, req->Headers.KnownHeaders[id].RawValueLength, r->pool)); \
873 	}
874 
875     _TRANSHEADER(HttpHeaderCacheControl, "Cache-Control");
876     _TRANSHEADER(HttpHeaderConnection, "Connection");
877     _TRANSHEADER(HttpHeaderDate, "Date");
878     _TRANSHEADER(HttpHeaderKeepAlive, "Keep-Alive");
879     _TRANSHEADER(HttpHeaderPragma, "Pragma");
880     _TRANSHEADER(HttpHeaderTrailer, "Trailer");
881     _TRANSHEADER(HttpHeaderTransferEncoding, "Transfer-Encoding");
882     _TRANSHEADER(HttpHeaderUpgrade, "Upgrade");
883     _TRANSHEADER(HttpHeaderVia, "Via");
884     _TRANSHEADER(HttpHeaderWarning, "Warning");
885     _TRANSHEADER(HttpHeaderAllow, "Allow");
886     _TRANSHEADER(HttpHeaderContentLength, "Content-Length");
887     _TRANSHEADER(HttpHeaderContentType, "Content-Type");
888     _TRANSHEADER(HttpHeaderContentEncoding, "Content-Encoding");
889     _TRANSHEADER(HttpHeaderContentLanguage, "Content-Language");
890     _TRANSHEADER(HttpHeaderContentLocation, "Content-Location");
891     _TRANSHEADER(HttpHeaderContentMd5, "Content-Md5");
892     _TRANSHEADER(HttpHeaderContentRange, "Content-Range");
893     _TRANSHEADER(HttpHeaderExpires, "Expires");
894     _TRANSHEADER(HttpHeaderLastModified, "Last-Modified");
895     _TRANSHEADER(HttpHeaderAccept, "Accept");
896     _TRANSHEADER(HttpHeaderAcceptCharset, "Accept-Charset");
897     _TRANSHEADER(HttpHeaderAcceptEncoding, "Accept-Encoding");
898     _TRANSHEADER(HttpHeaderAcceptLanguage, "Accept-Language");
899     _TRANSHEADER(HttpHeaderAuthorization, "Authorization");
900     _TRANSHEADER(HttpHeaderCookie, "Cookie");
901     _TRANSHEADER(HttpHeaderExpect, "Expect");
902     _TRANSHEADER(HttpHeaderFrom, "From");
903     _TRANSHEADER(HttpHeaderHost, "Host");
904     _TRANSHEADER(HttpHeaderIfMatch, "If-Match");
905     _TRANSHEADER(HttpHeaderIfModifiedSince, "If-Modified-Since");
906     _TRANSHEADER(HttpHeaderIfNoneMatch, "If-None-Match");
907     _TRANSHEADER(HttpHeaderIfRange, "If-Range");
908     _TRANSHEADER(HttpHeaderIfUnmodifiedSince, "If-Unmodified-Since");
909     _TRANSHEADER(HttpHeaderMaxForwards, "Max-Forwards");
910     _TRANSHEADER(HttpHeaderProxyAuthorization, "Proxy-Authorization");
911     _TRANSHEADER(HttpHeaderReferer, "Referer");
912     _TRANSHEADER(HttpHeaderRange, "Range");
913     _TRANSHEADER(HttpHeaderTe, "TE");
914     _TRANSHEADER(HttpHeaderTranslate, "Translate");
915     _TRANSHEADER(HttpHeaderUserAgent, "User-Agent");
916 
917 #undef _TRANSHEADER
918 
919 	for(int i = 0; i < req->Headers.UnknownHeaderCount; i++)
920 	{
921 		apr_table_setn(r->headers_in,
922 			ZeroTerminate(req->Headers.pUnknownHeaders[i].pName, req->Headers.pUnknownHeaders[i].NameLength, r->pool),
923 			ZeroTerminate(req->Headers.pUnknownHeaders[i].pRawValue, req->Headers.pUnknownHeaders[i].RawValueLength, r->pool));
924 	}
925 
926 	r->content_encoding = apr_table_get(r->headers_in, "Content-Encoding");
927 	r->content_type = apr_table_get(r->headers_in, "Content-Type");
928 
929 	const char *lng = apr_table_get(r->headers_in, "Content-Languages");
930 
931 	if(lng != NULL)
932 	{
933 		r->content_languages = apr_array_make(r->pool, 1, sizeof(const char *));
934 
935 		*(const char **)apr_array_push(r->content_languages) = lng;
936 	}
937 
938 	switch(req->Verb)
939 	{
940 	case HttpVerbUnparsed:
941 	case HttpVerbUnknown:
942 	case HttpVerbInvalid:
943     case HttpVerbTRACK:  // used by Microsoft Cluster Server for a non-logged trace
944     case HttpVerbSEARCH:
945 	default:
946 		r->method = "INVALID";
947 		r->method_number = M_INVALID;
948 		break;
949 	case HttpVerbOPTIONS:
950 		r->method = "OPTIONS";
951 		r->method_number = M_OPTIONS;
952 		break;
953 	case HttpVerbGET:
954 	case HttpVerbHEAD:
955 		r->method = "GET";
956 		r->method_number = M_GET;
957 		break;
958 	case HttpVerbPOST:
959 		r->method = "POST";
960 		r->method_number = M_POST;
961 		break;
962     case HttpVerbPUT:
963 		r->method = "PUT";
964 		r->method_number = M_PUT;
965 		break;
966     case HttpVerbDELETE:
967 		r->method = "DELETE";
968 		r->method_number = M_DELETE;
969 		break;
970     case HttpVerbTRACE:
971 		r->method = "TRACE";
972 		r->method_number = M_TRACE;
973 		break;
974     case HttpVerbCONNECT:
975 		r->method = "CONNECT";
976 		r->method_number = M_CONNECT;
977 		break;
978     case HttpVerbMOVE:
979 		r->method = "MOVE";
980 		r->method_number = M_MOVE;
981 		break;
982     case HttpVerbCOPY:
983 		r->method = "COPY";
984 		r->method_number = M_COPY;
985 		break;
986     case HttpVerbPROPFIND:
987 		r->method = "PROPFIND";
988 		r->method_number = M_PROPFIND;
989 		break;
990     case HttpVerbPROPPATCH:
991 		r->method = "PROPPATCH";
992 		r->method_number = M_PROPPATCH;
993 		break;
994     case HttpVerbMKCOL:
995 		r->method = "MKCOL";
996 		r->method_number = M_MKCOL;
997 		break;
998     case HttpVerbLOCK:
999 		r->method = "LOCK";
1000 		r->method_number = M_LOCK;
1001 		break;
1002     case HttpVerbUNLOCK:
1003 		r->method = "UNLOCK";
1004 		r->method_number = M_UNLOCK;
1005 		break;
1006 	}
1007 
1008 	if(HTTP_EQUAL_VERSION(req->Version, 0, 9))
1009 		r->protocol = "HTTP/0.9";
1010 	else if(HTTP_EQUAL_VERSION(req->Version, 1, 0))
1011 		r->protocol = "HTTP/1.0";
1012 	else
1013 		r->protocol = "HTTP/1.1";
1014 
1015 	r->request_time = apr_time_now();
1016 
1017 	r->parsed_uri.scheme = "http";
1018 	r->parsed_uri.path = r->path_info;
1019 	r->parsed_uri.hostname = (char *)r->hostname;
1020 	r->parsed_uri.is_initialized = 1;
1021 	r->parsed_uri.port = port;
1022 	r->parsed_uri.port_str = port_str;
1023 	r->parsed_uri.query = r->args;
1024 	r->parsed_uri.dns_looked_up = 0;
1025 	r->parsed_uri.dns_resolved = 0;
1026 	r->parsed_uri.password = NULL;
1027 	r->parsed_uri.user = NULL;
1028 	r->parsed_uri.fragment = NULL;
1029 
1030 	r->unparsed_uri = ZeroTerminate(req->pRawUrl, req->RawUrlLength, r->pool);
1031 	r->uri = r->unparsed_uri;
1032 
1033 	r->the_request = (char *)apr_palloc(r->pool, strlen(r->method) + 1 + req->RawUrlLength + 1 + strlen(r->protocol) + 1);
1034 
1035 	strcpy(r->the_request, r->method);
1036 	strcat(r->the_request, " ");
1037 	strcat(r->the_request, r->uri);
1038 	strcat(r->the_request, " ");
1039 	strcat(r->the_request, r->protocol);
1040 
1041 	HTTP_REQUEST_ID httpRequestID;
1042 	char *pszValue = (char *)apr_palloc(r->pool, 24);
1043 
1044 	httpRequestID = pRequest->GetRawHttpRequest()->RequestId;
1045 
1046 	_ui64toa(httpRequestID, pszValue, 10);
1047 
1048 	apr_table_setn(r->subprocess_env, "UNIQUE_ID", pszValue);
1049 
1050 	PSOCKADDR pAddr = pRequest->GetRemoteAddress();
1051 
1052 #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3
1053     c->remote_addr = CopySockAddr(r->pool, pAddr);
1054     c->remote_ip = GetIpAddr(r->pool, pAddr);
1055 #else
1056     c->client_addr = CopySockAddr(r->pool, pAddr);
1057 	c->client_ip = GetIpAddr(r->pool, pAddr);
1058 #endif
1059 	c->remote_host = NULL;
1060 
1061     LeaveCriticalSection(&m_csLock);
1062 	int status = modsecProcessRequest(r);
1063     EnterCriticalSection(&m_csLock);
1064 
1065 	if(status != DECLINED)
1066 	{
1067 		pHttpContext->GetResponse()->SetStatus(status, "ModSecurity Action");
1068 		pHttpContext->SetRequestHandled();
1069 
1070 		hr = E_FAIL;
1071 		goto Finished;
1072 	}
1073 
1074 Finished:
1075 	LeaveCriticalSection(&m_csLock);
1076 
1077     if ( FAILED( hr )  )
1078     {
1079         return RQ_NOTIFICATION_FINISH_REQUEST;
1080     }
1081 	return RQ_NOTIFICATION_CONTINUE;
1082 }
1083 
1084 
ReadBodyCallback(request_rec * r,char * buf,unsigned int length,unsigned int * readcnt,int * is_eos)1085 apr_status_t ReadBodyCallback(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)
1086 {
1087     REQUEST_STORED_CONTEXT *rsc = RetrieveIISContext(r);
1088 
1089     *readcnt = 0;
1090 
1091     if (rsc == NULL)
1092     {
1093         *is_eos = 1;
1094         return APR_SUCCESS;
1095     }
1096 
1097     IHttpContext *pHttpContext = rsc->m_pHttpContext;
1098     IHttpRequest *pRequest = pHttpContext->GetRequest();
1099 
1100     if (pRequest->GetRemainingEntityBytes() == 0)
1101     {
1102         *is_eos = 1;
1103         return APR_SUCCESS;
1104     }
1105 
1106     HRESULT hr = pRequest->ReadEntityBody(buf, length, false, (DWORD *)readcnt, NULL);
1107 
1108     if (FAILED(hr))
1109     {
1110         // End of data is okay.
1111         if (ERROR_HANDLE_EOF != (hr & 0x0000FFFF))
1112         {
1113             // Set the error status.
1114             rsc->m_pProvider->SetErrorStatus(hr);
1115         }
1116 
1117         *is_eos = 1;
1118     }
1119 
1120     return APR_SUCCESS;
1121 }
1122 
WriteBodyCallback(request_rec * r,char * buf,unsigned int length)1123 apr_status_t WriteBodyCallback(request_rec *r, char *buf, unsigned int length)
1124 {
1125 	REQUEST_STORED_CONTEXT *rsc = RetrieveIISContext(r);
1126 
1127 	if(rsc == NULL || rsc->m_pRequestRec == NULL)
1128 		return APR_SUCCESS;
1129 
1130 	IHttpContext *pHttpContext = rsc->m_pHttpContext;
1131 	IHttpRequest *pHttpRequest = pHttpContext->GetRequest();
1132 
1133     CHAR szLength[21]; //Max length for a 64 bit int is 20
1134 
1135     ZeroMemory(szLength, sizeof(szLength));
1136 
1137     HRESULT hr = StringCchPrintfA(
1138             szLength,
1139             sizeof(szLength) / sizeof(CHAR) - 1, "%d",
1140             length);
1141 
1142     if(FAILED(hr))
1143     {
1144 		// not possible
1145     }
1146 
1147     hr = pHttpRequest->SetHeader(
1148             HttpHeaderContentLength,
1149             szLength,
1150             (USHORT)strlen(szLength),
1151             TRUE);
1152 
1153     if(FAILED(hr))
1154     {
1155 		// possible, but there's nothing we can do
1156     }
1157 
1158 	// since we clean the APR pool at the end of OnSendRequest, we must get IIS-managed memory chunk
1159 	//
1160 	void *reqbuf = pHttpContext->AllocateRequestMemory(length);
1161 
1162 	memcpy(reqbuf, buf, length);
1163 
1164 	pHttpRequest->InsertEntityBody(reqbuf, length);
1165 
1166 	return APR_SUCCESS;
1167 }
1168 
ReadResponseCallback(request_rec * r,char * buf,unsigned int length,unsigned int * readcnt,int * is_eos)1169 apr_status_t ReadResponseCallback(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)
1170 {
1171 	REQUEST_STORED_CONTEXT *rsc = RetrieveIISContext(r);
1172 
1173 	*readcnt = 0;
1174 
1175 	if(rsc == NULL || rsc->m_pResponseBuffer == NULL)
1176 	{
1177 		*is_eos = 1;
1178 		return APR_SUCCESS;
1179 	}
1180 
1181 	unsigned int size = length;
1182 
1183 	if(size > rsc->m_pResponseLength - rsc->m_pResponsePosition)
1184 		size = rsc->m_pResponseLength - rsc->m_pResponsePosition;
1185 
1186 	memcpy(buf, rsc->m_pResponseBuffer + rsc->m_pResponsePosition, size);
1187 
1188 	*readcnt = size;
1189 	rsc->m_pResponsePosition += size;
1190 
1191 	if(rsc->m_pResponsePosition >= rsc->m_pResponseLength)
1192 		*is_eos = 1;
1193 
1194 	return APR_SUCCESS;
1195 }
1196 
WriteResponseCallback(request_rec * r,char * buf,unsigned int length)1197 apr_status_t WriteResponseCallback(request_rec *r, char *buf, unsigned int length)
1198 {
1199 	REQUEST_STORED_CONTEXT *rsc = RetrieveIISContext(r);
1200 
1201 	if(rsc == NULL || rsc->m_pRequestRec == NULL || rsc->m_pResponseBuffer == NULL)
1202 		return APR_SUCCESS;
1203 
1204 	IHttpContext *pHttpContext = rsc->m_pHttpContext;
1205 	IHttpResponse *pHttpResponse = pHttpContext->GetResponse();
1206 	HTTP_RESPONSE *pRawHttpResponse = pHttpResponse->GetRawHttpResponse();
1207 	HTTP_DATA_CHUNK *pDataChunk = (HTTP_DATA_CHUNK *)apr_palloc(rsc->m_pRequestRec->pool, sizeof(HTTP_DATA_CHUNK));
1208 
1209 	pRawHttpResponse->EntityChunkCount = 0;
1210 
1211 	// since we clean the APR pool at the end of OnSendRequest, we must get IIS-managed memory chunk
1212 	//
1213 	void *reqbuf = pHttpContext->AllocateRequestMemory(length);
1214 
1215 	memcpy(reqbuf, buf, length);
1216 
1217 	pDataChunk->DataChunkType = HttpDataChunkFromMemory;
1218 	pDataChunk->FromMemory.pBuffer = reqbuf;
1219 	pDataChunk->FromMemory.BufferLength = length;
1220 
1221     CHAR szLength[21]; //Max length for a 64 bit int is 20
1222 
1223     ZeroMemory(szLength, sizeof(szLength));
1224 
1225     HRESULT hr = StringCchPrintfA(
1226             szLength,
1227             sizeof(szLength) / sizeof(CHAR) - 1, "%d",
1228             length);
1229 
1230     if(FAILED(hr))
1231     {
1232 		// not possible
1233     }
1234 
1235     hr = pHttpResponse->SetHeader(
1236             HttpHeaderContentLength,
1237             szLength,
1238             (USHORT)strlen(szLength),
1239             TRUE);
1240 
1241     if(FAILED(hr))
1242     {
1243 		// possible, but there's nothing we can do
1244     }
1245 
1246 	pHttpResponse->WriteEntityChunkByReference(pDataChunk);
1247 
1248 	return APR_SUCCESS;
1249 }
1250 
1251 
CMyHttpModule()1252 CMyHttpModule::CMyHttpModule()
1253 {
1254     // Open a handle to the Event Viewer.
1255     m_hEventLog = RegisterEventSource( NULL, "ModSecurity" );
1256 
1257     SYSTEM_INFO         sysInfo;
1258 
1259     GetSystemInfo(&sysInfo);
1260     m_dwPageSize = sysInfo.dwPageSize;
1261 
1262     this->status_call_already_sent = false;
1263 
1264 	InitializeCriticalSection(&m_csLock);
1265 
1266 	modsecSetLogHook(this, Log);
1267 
1268 	modsecSetReadBody(ReadBodyCallback);
1269 	modsecSetReadResponse(ReadResponseCallback);
1270 	modsecSetWriteBody(WriteBodyCallback);
1271 	modsecSetWriteResponse(WriteResponseCallback);
1272 
1273 	server_rec *s = modsecInit();
1274 	char *compname = (char *)malloc(128);
1275 	DWORD size = 128;
1276 
1277 	GetComputerName(compname, &size);
1278 
1279 	s->server_hostname = compname;
1280 
1281 	modsecStartConfig();
1282 
1283 	modsecFinalizeConfig();
1284 
1285 	modsecInitProcess();
1286 }
1287 
~CMyHttpModule()1288 CMyHttpModule::~CMyHttpModule()
1289 {
1290 	// ModSecurity registers APR pool cleanups, which interfere with APR pool tear down process
1291 	// this causes crashes and since we are exiting the process here, so this is a temporary solution
1292 	//
1293 	//modsecTerminate();
1294 
1295 	//WriteEventViewerLog("Module deleted.");
1296 
1297 	// Test whether the handle for the Event Viewer is open.
1298     if (NULL != m_hEventLog)
1299     {
1300         // Close the handle to the Event Viewer.
1301         DeregisterEventSource( m_hEventLog );
1302         m_hEventLog = NULL;
1303 
1304 		DeleteCriticalSection(&m_csLock);
1305     }
1306 }
1307 
Dispose()1308 void CMyHttpModule::Dispose()
1309 {
1310 }
1311 
WriteEventViewerLog(LPCSTR szNotification,WORD category)1312 BOOL CMyHttpModule::WriteEventViewerLog(LPCSTR szNotification, WORD category)
1313 {
1314     // Test whether the handle for the Event Viewer is open.
1315     if (NULL != m_hEventLog)
1316     {
1317         // Write any strings to the Event Viewer and return.
1318         return ReportEvent(
1319             m_hEventLog,
1320             category, 0, 0x1,
1321             NULL, 1, 0, &szNotification, NULL );
1322     }
1323     return FALSE;
1324 }
1325