1 /****************************************************************************
2 *
3 * Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
4 * Copyright (C) 2003-2013 Sourcefire, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License Version 2 as
8 * published by the Free Software Foundation. You may not use, modify or
9 * distribute this program under any other version of the GNU General
10 * Public License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 ****************************************************************************/
22
23 /**
24 ** @file hi_client.c
25 **
26 ** @author Daniel Roelker <droelker@sourcefire.com>
27 **
28 ** @brief Main file for all the client functions and inspection
29 ** flow.
30 **
31 **
32 ** The job of the client module is to analyze and inspect the HTTP
33 ** protocol, finding where the various fields begin and end. This must
34 ** be accomplished in a stateful and stateless manner.
35 **
36 ** While the fields are being determined, we also do checks for
37 ** normalization, so we don't normalize fields that don't need it.
38 **
39 ** Currently, the only fields we check for this is the URI and the
40 ** parameter fields.
41 **
42 ** NOTES:
43 ** - 3.8.03: Initial development. DJR
44 ** - 2.4.05: Added tab_uri_delimiter config option. AJM.
45 */
46
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <ctype.h>
51 #include <sys/types.h>
52 #include <errno.h>
53
54 #ifdef HAVE_CONFIG_H
55 #include "config.h"
56 #endif
57
58 #include "hi_ui_config.h"
59 #include "hi_si.h"
60 #include "hi_mi.h"
61 #include "hi_client.h"
62 #include "hi_eo_log.h"
63 #include "hi_util.h"
64 #include "hi_util_hbm.h"
65 #include "hi_return_codes.h"
66 #include "util.h"
67 #include "mstring.h"
68 #include "sfutil/util_unfold.h"
69 #include "hi_cmd_lookup.h"
70 #include "detection_util.h"
71 #include "hi_paf.h"
72 #include "memory_stats.h"
73
74 #if defined(FEAT_OPEN_APPID)
75 #include "spp_stream6.h"
76 #endif /* defined(FEAT_OPEN_APPID) */
77
78 #define HEADER_NAME__COOKIE "Cookie"
79 #define HEADER_LENGTH__COOKIE 6
80 #define HEADER_NAME__CONTENT_LENGTH "Content-length"
81 #define HEADER_LENGTH__CONTENT_LENGTH 14
82 #define HEADER_NAME__XFF HTTP_XFF_FIELD_X_FORWARDED_FOR
83 #define HEADER_LENGTH__XFF (sizeof(HEADER_NAME__XFF)-1)
84 #define HEADER_NAME__TRUE_IP HTTP_XFF_FIELD_TRUE_CLIENT_IP
85 #define HEADER_LENGTH__TRUE_IP (sizeof(HEADER_NAME__TRUE_IP)-1)
86 #define HEADER_NAME__HOSTNAME "Host"
87 #define HEADER_LENGTH__HOSTNAME 4
88 #define HEADER_NAME__RANGE "Range"
89 #define HEADER_LENGTH__RANGE 5
90 #define HEADER_NAME__TRANSFER_ENCODING "Transfer-encoding"
91 #define HEADER_LENGTH__TRANSFER_ENCODING 17
92 #define HEADER_NAME__CONTENT_TYPE "Content-Type"
93 #define HEADER_LENGTH__CONTENT_TYPE 12
94 #define HEADER_NAME__CONTENT_DISP "Content-Disposition"
95 #define HEADER_LENGTH__CONTENT_DISP 19
96 #if defined(FEAT_OPEN_APPID)
97 #define HEADER_NAME__USER_AGENT "User-Agent"
98 #define HEADER_LENGTH__USER_AGENT sizeof(HEADER_NAME__USER_AGENT)-1
99 #define HEADER_NAME__REFERER "Referer"
100 #define HEADER_LENGTH__REFERER sizeof(HEADER_NAME__REFERER)-1
101 #define HEADER_NAME__VIA "Via"
102 #define HEADER_LENGTH__VIA sizeof(HEADER_NAME__VIA)-1
103 #endif /* defined(FEAT_OPEN_APPID) */
104
105 const u_char *proxy_start = NULL;
106 const u_char *proxy_end = NULL;
107
108 static const char *g_field_names[] =
109 {
110 HEADER_NAME__COOKIE,
111 HEADER_NAME__CONTENT_LENGTH,
112 HEADER_NAME__XFF,
113 HEADER_NAME__TRUE_IP,
114 HEADER_NAME__HOSTNAME,
115 HEADER_NAME__TRANSFER_ENCODING,
116 HEADER_NAME__CONTENT_TYPE,
117 NULL
118 };
119
120 /** This makes passing function arguments much more readable and easier
121 ** to follow.
122 */
123 typedef int (*LOOKUP_FCN)(HI_SESSION *, const u_char *, const u_char *, const u_char **,
124 URI_PTR *);
125
126 /*
127 ** The lookup table contains functions for different HTTP delimiters
128 ** (like whitespace and the HTTP delimiter \r and \n).
129 */
130 LOOKUP_FCN lookup_table[256];
131 int NextNonWhiteSpace(HI_SESSION *Session, const u_char *start,
132 const u_char *end, const u_char **ptr, URI_PTR *uri_ptr);
133 extern const u_char *extract_http_transfer_encoding(HI_SESSION *, HttpSessionData *,
134 const u_char *, const u_char *, const u_char *, HEADER_PTR *, int);
135
136 extern void CheckSkipAlertMultipleColon(HI_SESSION *Session, const u_char *start,
137 const u_char *end, const u_char **ptr, int iInspectMode);
138
hi_client_get_field_names()139 char **hi_client_get_field_names() { return( (char **)g_field_names ); }
140
141 /*
142 ** NAME
143 ** CheckChunkEncoding::
144 */
145 /**
146 ** This routine checks for chunk encoding anomalies in an HTTP client request
147 ** packet.
148 **
149 ** We convert potential chunk lengths and test them against the user-defined
150 ** max chunk length. We log events on any chunk lengths that are over this
151 ** defined chunk lengths.
152 **
153 ** Chunks are skipped to save time when the chunk is contained in the packet.
154 **
155 ** We assume coming into this function that we are pointed at the beginning
156 ** of what may be a chunk length. That's why the iCheckChunk var is set
157 ** to 1.
158 **
159 ** @param Session pointer to the Session construct
160 ** @param start pointer to where to beginning of buffer
161 ** @param end pointer to the end of buffer
162 **
163 ** @return integer
164 **
165 ** @retval HI_SUCCESS function successful
166 ** @retval HI_INVALID_ARG invalid argument
167 */
CheckChunkEncoding(HI_SESSION * Session,const u_char * start,const u_char * end,const u_char ** post_end,u_char * iChunkBuf,uint32_t max_size,uint32_t chunk_remainder,uint32_t * updated_chunk_remainder,uint32_t * chunkRead,HttpSessionData * hsd,int iInspectMode)168 int CheckChunkEncoding(HI_SESSION *Session, const u_char *start, const u_char *end,
169 const u_char **post_end, u_char *iChunkBuf, uint32_t max_size,
170 uint32_t chunk_remainder, uint32_t *updated_chunk_remainder, uint32_t *chunkRead, HttpSessionData *hsd,
171 int iInspectMode)
172 {
173 uint32_t iChunkLen = 0;
174 uint32_t iChunkChars = 0;
175 int chunkPresent = 0;
176 uint32_t iCheckChunk = 1;
177 const u_char *ptr;
178 const u_char *jump_ptr;
179 uint32_t iDataLen = 0;
180 uint32_t chunkBytesCopied = 0;
181 uint8_t stateless_chunk_count = 0;
182 uint8_t iChunkLenState = CHUNK_LEN_DEFAULT;
183 bool iChunkRemainder = false;
184
185 if(!start || !end)
186 return HI_INVALID_ARG;
187
188 ptr = start;
189
190 if (hsd)
191 iChunkLenState = hsd->resp_state.chunk_len_state;
192
193 if(chunk_remainder && iChunkLenState == CHUNK_LEN_DEFAULT)
194 {
195 iDataLen = end - ptr;
196
197 if( iDataLen < max_size)
198 {
199 if( chunk_remainder > iDataLen )
200 {
201 if(updated_chunk_remainder)
202 *updated_chunk_remainder = chunk_remainder - iDataLen ;
203 chunk_remainder = iDataLen;
204 iChunkRemainder = true;
205 }
206 }
207 else
208 {
209 if( chunk_remainder > max_size )
210 {
211 if(updated_chunk_remainder)
212 *updated_chunk_remainder = chunk_remainder - max_size ;
213 chunk_remainder = max_size;
214 iChunkRemainder = true;
215 }
216 }
217
218 jump_ptr = ptr + chunk_remainder - 1;
219
220 if(hi_util_in_bounds(start, end, jump_ptr))
221 {
222 chunkPresent = 1;
223 if(iChunkBuf)
224 {
225 memcpy(iChunkBuf, ptr, chunk_remainder);
226 chunkBytesCopied = chunk_remainder;
227 }
228 ptr = jump_ptr + 1;
229 }
230 }
231 else if(iChunkLenState == CHUNK_LEN_INCOMPLETE)
232 {
233 /* Chunk length incompletely read previously, continue to read the remaining bytes */
234 iChunkLen = chunk_remainder;
235 }
236
237 while(hi_util_in_bounds(start, end, ptr))
238 {
239 if(*ptr == '\n')
240 {
241 if(iCheckChunk && iChunkLen != 0)
242 {
243 if (((Session->server_conf->chunk_length != 0)
244 && (iInspectMode == HI_SI_CLIENT_MODE)
245 && (Session->server_conf->chunk_length < iChunkLen)
246 && hi_eo_generate_event(Session, HI_EO_CLIENT_LARGE_CHUNK)))
247 {
248 hi_eo_client_event_log(Session, HI_EO_CLIENT_LARGE_CHUNK,
249 NULL, NULL);
250 }
251
252 if (Session->server_conf->small_chunk_length.size != 0)
253 {
254 if (iChunkLen <= Session->server_conf->small_chunk_length.size)
255 {
256 uint8_t* chunk_count;
257 int (*log_func)(HI_SESSION *, int, void *, void (*)(void *));
258 int event;
259
260 if (iInspectMode == HI_SI_CLIENT_MODE)
261 {
262 if (hsd)
263 chunk_count = &hsd->cli_small_chunk_count;
264 else
265 chunk_count = &stateless_chunk_count;
266 log_func = hi_eo_client_event_log;
267 event = HI_EO_CLIENT_CONSECUTIVE_SMALL_CHUNKS;
268 }
269 else
270 {
271 if (hsd)
272 chunk_count = &hsd->srv_small_chunk_count;
273 else
274 chunk_count = &stateless_chunk_count;
275 log_func = hi_eo_server_event_log;
276 event = HI_EO_SERVER_CONSECUTIVE_SMALL_CHUNKS;
277 }
278
279 (*chunk_count)++;
280 if (hi_eo_generate_event(Session, event)
281 && (*chunk_count >= Session->server_conf->small_chunk_length.num))
282 {
283 log_func(Session, event, NULL, NULL);
284 *chunk_count = 0;
285 }
286 }
287 else
288 {
289 // Reset for non-consecutive small chunks
290 if (iInspectMode == HI_SI_CLIENT_MODE)
291 {
292 if (hsd)
293 hsd->cli_small_chunk_count = 0;
294 else
295 stateless_chunk_count = 0;
296 }
297 else
298 {
299 if (hsd)
300 hsd->srv_small_chunk_count = 0;
301 else
302 stateless_chunk_count = 0;
303 }
304 }
305 }
306
307 SkipBlankAndNewLine(start,end, &ptr);
308
309 if(*ptr == '\n')
310 ptr++;
311
312 iChunkLenState = CHUNK_LEN_DEFAULT;
313
314 if(!hi_util_in_bounds(start, end, ptr))
315 {
316 if(updated_chunk_remainder)
317 *updated_chunk_remainder = iChunkLen;
318 iChunkRemainder = true;
319 break;
320 }
321
322 iDataLen = end - ptr ;
323
324 if( iChunkLen > iDataLen)
325 {
326 if(updated_chunk_remainder)
327 *updated_chunk_remainder = iChunkLen - iDataLen;
328 iChunkRemainder = true;
329 iChunkLen = iDataLen;
330 }
331
332 jump_ptr = ptr + iChunkLen;
333
334 if(jump_ptr <= ptr)
335 {
336 break;
337 }
338
339 /* Since we're doing a memcpy end and jump_ptr can be the same
340 * but hi_util_in_bounds ensures last arg is less than so
341 * subtract 1 from jump_ptr */
342 if(hi_util_in_bounds(start, end, jump_ptr - 1))
343 {
344 chunkPresent = 1;
345 if(iChunkBuf && ((chunkBytesCopied + iChunkLen) <= max_size))
346 {
347 memcpy(iChunkBuf+chunkBytesCopied, ptr, iChunkLen);
348 chunkBytesCopied += iChunkLen;
349 }
350 ptr = jump_ptr;
351
352 if (!hi_util_in_bounds(start, end, ptr))
353 break;
354
355 /* Check to see if the chunks ends - LF or CRLF are valid */
356 if (hi_eo_generate_event(Session, HI_EO_CLIENT_CHUNK_SIZE_MISMATCH)
357 && (*ptr != '\n') && (*ptr != '\r')
358 && ((ptr + 1) < end) && (*(ptr + 1) != '\n'))
359 {
360 hi_eo_client_event_log(Session, HI_EO_CLIENT_CHUNK_SIZE_MISMATCH,
361 NULL, NULL);
362 SnortEventqAdd(
363 GENERATOR_SPP_HTTP_INSPECT_CLIENT,
364 HI_EO_CLIENT_CHUNK_SIZE_MISMATCH+1, 1, 0, 2,
365 HI_EO_CLIENT_CHUNK_SIZE_MISMATCH_STR, NULL);
366 }
367 }
368 else
369 {
370 /*
371 ** Chunk too large for packet, so we bail
372 */
373 break;
374 }
375 }
376
377 /*
378 ** If we've already evaluated the chunk, or we have a valid delimiter
379 ** for handling new chunks, we reset and starting evaluating possible
380 ** chunk lengths.
381 */
382 if(iCheckChunk || (hi_util_in_bounds(start, end, ptr) && *ptr == '\n'))
383 {
384 iCheckChunk = 1;
385 iChunkLen = 0;
386 iChunkChars = 0;
387 iChunkLenState = CHUNK_LEN_DEFAULT;
388 }
389
390 ptr++;
391 continue;
392 }
393
394 if(iCheckChunk)
395 {
396 if(valid_lookup[*ptr] != HEX_VAL)
397 {
398 if(*ptr == '\r')
399 {
400 while((hi_util_in_bounds(start, end, ptr)) && (*ptr == '\r'))
401 {
402 ptr++;
403 }
404
405 if(!hi_util_in_bounds(start, end, ptr))
406 break;
407
408 if(*ptr == '\n')
409 continue;
410 }
411 else if(*ptr != '\n')
412 {
413 /*
414 ** This is where we skip through the chunk name=value
415 ** field.
416 */
417 ptr = memchr(ptr, '\n', (end-ptr));
418 if(ptr == NULL)
419 {
420 ptr = end;
421 iChunkLenState = CHUNK_LEN_DEFAULT;
422 break;
423 }
424 else
425 continue;
426
427 }
428
429 iCheckChunk = 0;
430 iChunkLen = 0;
431 iChunkChars = 0;
432 iChunkLenState = CHUNK_LEN_DEFAULT;
433 }
434 else
435 {
436 if(iChunkChars >= 8)
437 {
438 if (((Session->server_conf->chunk_length != 0)
439 && (iInspectMode == HI_SI_CLIENT_MODE)
440 && (Session->server_conf->chunk_length < iChunkLen)
441 && hi_eo_generate_event(Session, HI_EO_CLIENT_LARGE_CHUNK)))
442 {
443 hi_eo_client_event_log(Session, HI_EO_CLIENT_LARGE_CHUNK,
444 NULL, NULL);
445 }
446
447 iCheckChunk = 0;
448 iChunkLen = 0;
449 iChunkChars = 0;
450 iChunkLenState = CHUNK_LEN_DEFAULT;
451 }
452 else
453 {
454 iChunkLen <<= 4;
455 iChunkLen |= (unsigned int)(hex_lookup[*ptr]);
456 iChunkChars++;
457 /*
458 ** Chunk length incompletely read i.e CRLF not found yet
459 ** Storing the bytes of chunk length, read till now
460 ** (to handle the split of chunk length itself across different packets)
461 */
462 iChunkLenState = CHUNK_LEN_INCOMPLETE;
463 if(updated_chunk_remainder)
464 *updated_chunk_remainder = iChunkLen;
465 }
466 }
467 }
468
469 ptr++;
470 }
471
472 /*
473 ** If we neither have Chunk data split across packets (or)
474 ** Chunk length itself split across packets then clear
475 */
476 if(!( iChunkRemainder || (iChunkLenState == CHUNK_LEN_INCOMPLETE) ))
477 {
478 if(updated_chunk_remainder)
479 *updated_chunk_remainder = 0;
480 }
481
482 if (hsd)
483 hsd->resp_state.chunk_len_state = iChunkLenState;
484
485 if (chunkPresent )
486 {
487 if(post_end)
488 {
489 *(post_end) = ptr;
490 }
491
492 if(chunkRead)
493 {
494 *chunkRead = chunkBytesCopied;
495 }
496 return 1;
497 }
498
499 return HI_SUCCESS;
500 }
501
502 /*
503 ** NAME
504 ** FindPipelineReq::
505 */
506 /**
507 ** Catch multiple requests per packet, by returning pointer to after the
508 ** end of the request header if there is another request.
509 **
510 ** There are 4 types of "valid" delimiters that we look for. They are:
511 ** "\r\n\r\n"
512 ** "\r\n\n"
513 ** "\n\r\n"
514 ** "\n\n"
515 ** The only patterns that we really only need to look for are:
516 ** "\n\r\n"
517 ** "\n\n"
518 ** The reason being that these two patterns are suffixes of the other
519 ** patterns. So once we find those, we are all good.
520 **
521 ** @param Session pointer to the session
522 ** @param start pointer to the start of text
523 ** @param end pointer to the end of text
524 **
525 ** @return pointer
526 **
527 ** @retval NULL Did not find pipeline request
528 ** @retval !NULL Found another possible request.
529 */
FindPipelineReq(HI_SESSION * Session,const u_char * start,const u_char * end)530 static inline const u_char *FindPipelineReq(HI_SESSION *Session,
531 const u_char *start, const u_char *end)
532 {
533 const u_char *p;
534 u_char *offset;
535
536 if(!start || !end)
537 return NULL;
538
539 p = start;
540
541 offset = (u_char*)p;
542
543 /*
544 ** We say end - 6 because we need at least six bytes to verify that
545 ** there is an end to the URI and still a request afterwards. To be
546 ** exact, we should only subtract 1, but we are not interested in a
547 ** 1 byte method, uri, etc.
548 **
549 ** a.k.a there needs to be data after the initial request to inspect
550 ** to make it worth our while.
551 */
552 while(p < (end - 6))
553 {
554 if(*p == '\n')
555 {
556 if(hi_eo_generate_event(Session, Session->server_conf->max_hdr_len)
557 && ((p - offset) >= Session->server_conf->max_hdr_len))
558 {
559 hi_eo_client_event_log(Session, HI_EO_CLIENT_LONG_HDR, NULL, NULL);
560 }
561
562 p++;
563
564 offset = (u_char*)p;
565
566 if(*p < 0x0E)
567 {
568 if(*p == '\r')
569 {
570 p++;
571
572 if(*p == '\n')
573 {
574 return ++p;
575 }
576 }
577 else if(*p == '\n')
578 {
579 return ++p;
580 }
581 }
582 }
583
584 p++;
585 }
586
587 /* Never observed an end-of-field. Maybe it's not there, but the header is long anyway: */
588 if(hi_eo_generate_event(Session, Session->server_conf->max_hdr_len)
589 && ((p - start) >= Session->server_conf->max_hdr_len))
590 {
591 hi_eo_client_event_log(Session, HI_EO_CLIENT_LONG_HDR, NULL, NULL);
592 }
593
594 return NULL;
595 }
596
597 /*
598 ** NAME
599 ** IsHttpVersion::
600 */
601 /**
602 ** This checks that there is a version following a space with in an HTTP
603 ** packet.
604 **
605 ** This function gets called when a whitespace area has ended, and we want
606 ** to know if a version identifier is followed directly after. So we look
607 ** for the rfc standard "HTTP/" and report appropriately. We also need
608 ** to make sure that the function succeeds given an end of buffer, so for
609 ** instance if the buffer ends like " HTT", we still assume that this is
610 ** a valid version identifier because of TCP segmentation.
611 **
612 ** We also check for the 0.9 standard of GET URI\r\n. When we see a \r or
613 ** a \n, then we just return with the pointer still pointing to that char.
614 ** The reason is because on the next loop, we'll do the evaluation that
615 ** we normally do and finish up processing there.
616 **
617 ** @param start pointer to the start of the version identifier
618 ** @param end pointer to the end of the buffer (could be the end of the
619 ** data section, or just to the beginning of the delimiter.
620 **
621 ** @return integer
622 **
623 ** @retval 1 this is an HTTP version identifier
624 ** @retval 0 this is not an HTTP identifier, or bad parameters
625 */
IsHttpVersion(const u_char ** ptr,const u_char * end)626 int IsHttpVersion(const u_char **ptr, const u_char *end)
627 {
628 static u_char s_acHttpDelimiter[] = "HTTP/";
629 static int s_iHttpDelimiterLen = 5;
630 int len;
631 int iCtr;
632
633 if(*ptr >= end)
634 {
635 return 0;
636 }
637
638 len = end - *ptr;
639 if(len > s_iHttpDelimiterLen)
640 {
641 len = s_iHttpDelimiterLen;
642 }
643
644 /*
645 ** This is where we check for the defunct method again. This method
646 ** allows a request of "GET /index.html \r[\n]". So we need to
647 ** check validate this as a legal identifier.
648 */
649 if(**ptr == '\n' || **ptr == '\r')
650 {
651 /*
652 ** We don't increment the pointer because we check for a legal
653 ** identifier in the delimiter checking. Read the comments for
654 ** setting the defunct variable in these functions.
655 */
656 return 1;
657 }
658
659 for(iCtr = 0; iCtr < len; iCtr++)
660 {
661 if(s_acHttpDelimiter[iCtr] != (u_char)toupper((int)**ptr))
662 {
663 return 0;
664 }
665
666 (*ptr)++;
667 }
668
669 /*
670 ** This means that we match all the chars that we could given the
671 ** remaining length so we should increment the pointer by that much
672 ** since we don't need to inspect this again.
673 */
674
675 /* This pointer is not used again. When 1 is returned it causes
676 * NextNonWhiteSpace to return also. */
677 #if 0
678 (*ptr)++;
679 #endif
680
681 return 1;
682 }
683
684 /*
685 ** NAME
686 ** find_rfc_delimiter::
687 */
688 /**
689 ** Check for standard RFC HTTP delimiter.
690 **
691 ** If we find the delimiter, we return that URI_PTR structures should
692 ** be checked, which bails us out of the loop. If there isn't a RFC
693 ** delimiter, then we bail with a no URI. Otherwise, we check for out
694 ** of bounds.
695 **
696 ** @param ServerConf pointer to the server configuration
697 ** @param start pointer to the start of payload
698 ** @param end pointer to the end of the payload
699 ** @param ptr pointer to the pointer of the current index
700 ** @param uri_ptr pointer to the URI_PTR construct
701 **
702 ** @return integer
703 **
704 ** @retval HI_OUT_OF_BOUNDS
705 ** @retval URI_END end of the URI is found, check URI_PTR.
706 ** @retval NO_URI malformed delimiter, no URI.
707 */
find_rfc_delimiter(HI_SESSION * Session,const u_char * start,const u_char * end,const u_char ** ptr,URI_PTR * uri_ptr)708 int find_rfc_delimiter(HI_SESSION *Session, const u_char *start,
709 const u_char *end, const u_char **ptr, URI_PTR *uri_ptr)
710 {
711 if(*ptr == start || !uri_ptr->uri)
712 return NO_URI;
713
714 /*
715 ** This is important to catch the defunct way of getting URIs without
716 ** specifying "HTTP/major.minor\r\n\r\n". This is a quick way for
717 ** us to tell if we are in that state.
718 **
719 ** We check for a legal identifier to deal with the case of
720 ** "some_of_the_uri_in segmented packet \r\n" in the defunct case.
721 ** Since we find a "valid" (still defunct) delimiter, we account for
722 ** it here, so that we don't set the uri_end to the delimiter.
723 **
724 ** NOTE:
725 ** We now assume that the defunct method is in effect and if there is
726 ** a valid identifier, then we don't update the uri_end because it's
727 ** already been set when the identifier was validated.
728 */
729
730 (*ptr)++;
731 if(!hi_util_in_bounds(start, end, *ptr))
732 {
733 return HI_OUT_OF_BOUNDS;
734 }
735
736 if(**ptr == '\n')
737 {
738 uri_ptr->delimiter = (*ptr)-1;
739
740 if(!uri_ptr->ident)
741 uri_ptr->uri_end = uri_ptr->delimiter;
742
743 return URI_END;
744 }
745
746 return NextNonWhiteSpace(Session, start, end, ptr, uri_ptr);
747 }
748
749 /*
750 ** NAME
751 ** find_non_rfc_delimiter::
752 */
753 /**
754 ** Check for non standard delimiter '\n'.
755 **
756 ** It now appears that apache and iis both take this non-standard
757 ** delimiter. So, we most likely will always look for it, but maybe
758 ** give off a special alert or something.
759 **
760 ** @param ServerConf pointer to the server configuration
761 ** @param start pointer to the start of payload
762 ** @param end pointer to the end of the payload
763 ** @param ptr pointer to the pointer of the current index
764 ** @param uri_ptr pointer to the URI_PTR construct
765 **
766 ** @return integer
767 **
768 ** @retval URI_END delimiter found, end of URI
769 ** @retval NO_URI
770 */
find_non_rfc_delimiter(HI_SESSION * Session,const u_char * start,const u_char * end,const u_char ** ptr,URI_PTR * uri_ptr)771 int find_non_rfc_delimiter(HI_SESSION *Session, const u_char *start,
772 const u_char *end, const u_char **ptr, URI_PTR *uri_ptr)
773 {
774 HTTPINSPECT_CONF *ServerConf = Session->server_conf;
775
776 if(*ptr == start || !uri_ptr->uri)
777 return NO_URI;
778
779 /*
780 ** This is important to catch the defunct way of getting URIs without
781 ** specifying "HTTP/major.minor\r\n\r\n". This is a quick way for
782 ** us to tell if we are in that state.
783 **
784 ** We check for a legal identifier to deal with the case of
785 ** "some_of_the_uri_in segmented packet \r\n" in the defunct case.
786 ** Since we find a "valid" (still defunct) delimiter, we account for
787 ** it here, so that we don't set the uri_end to the delimiter.
788 **
789 ** NOTE:
790 ** We now assume that the defunct method is in effect and if there is
791 ** a valid identifier, then we don't update the uri_end because it's
792 ** already been set when the identifier was validated.
793 */
794 if(ServerConf->iis_delimiter.on)
795 {
796 if(hi_eo_generate_event(Session, ServerConf->iis_delimiter.alert))
797 {
798 hi_eo_client_event_log(Session, HI_EO_CLIENT_IIS_DELIMITER,
799 NULL, NULL);
800 }
801
802 uri_ptr->delimiter = *ptr;
803
804 if(!uri_ptr->ident)
805 uri_ptr->uri_end = uri_ptr->delimiter;
806
807 return URI_END;
808 }
809
810 /*
811 ** This allows us to do something if the delimiter check is not turned
812 ** on. Most likely this is worthy of an alert, IF it's not normal to
813 ** see these requests.
814 **
815 ** But for now, we always return true.
816 */
817 uri_ptr->delimiter = *ptr;
818
819 if(!uri_ptr->ident)
820 uri_ptr->uri_end = uri_ptr->delimiter;
821
822 return URI_END;
823 }
824
825 /*
826 ** NAME
827 ** NextNonWhiteSpace::
828 */
829 /**
830 ** Update the URI_PTR fields spaces, find the next non-white space char,
831 ** and validate the HTTP version identifier after the spaces.
832 **
833 ** This is the main part of the URI algorithm. This verifies that there
834 ** isn't too many spaces in the data to be a URI, it checks that after the
835 ** second space that there is an HTTP identifier or otherwise it's no good.
836 ** Also, if we've found an identifier after the first whitespace, and
837 ** find another whitespace, there is no URI.
838 **
839 ** The uri and uri_end pointers are updated in this function depending
840 ** on what space we are at, and if the space was followed by the HTTP
841 ** identifier. (NOTE: the HTTP delimiter is no longer "HTTP/", but
842 ** can also be "\r\n", "\n", or "\r". This is the defunct method, and
843 ** we deal with it in the IsHttpVersion and delimiter functions.)
844 **
845 ** @param ServerConf pointer to the server configuration
846 ** @param start pointer to the start of payload
847 ** @param end pointer to the end of the payload
848 ** @param ptr pointer to the pointer of the current index
849 ** @param uri_ptr pointer to the URI_PTR construct
850 **
851 ** @return integer
852 **
853 ** @retval HI_SUCCESS found the next non-whitespace
854 ** @retval HI_OUT_OF_BOUNDS whitespace to the end of the buffer
855 ** @retval URI_END delimiter found, end of URI
856 ** @retval NO_URI
857 */
NextNonWhiteSpace(HI_SESSION * Session,const u_char * start,const u_char * end,const u_char ** ptr,URI_PTR * uri_ptr)858 int NextNonWhiteSpace(HI_SESSION *Session, const u_char *start,
859 const u_char *end, const u_char **ptr, URI_PTR *uri_ptr)
860 {
861 HTTPINSPECT_CONF *ServerConf = Session->server_conf;
862 const u_char **start_sp;
863 const u_char **end_sp;
864
865 /*
866 ** Horizontal tab is only accepted by apache web servers, not IIS.
867 ** Some IIS exploits contain a tab (0x09) in the URI, so we don't want
868 ** to treat it as a URI delimiter and cut off the URI.
869 */
870 if ( **ptr == '\t' && !ServerConf->tab_uri_delimiter )
871 {
872 (*ptr)++;
873 return HI_SUCCESS;
874 }
875
876 /*
877 ** Reset the identifier, because we've just seen another space. We
878 ** should only see the identifier immediately after a space followed
879 ** by a delimiter.
880 */
881 if(uri_ptr->ident)
882 {
883 if(ServerConf->non_strict)
884 {
885 /*
886 ** In non-strict mode it is ok to see spaces after the
887 ** "identifier", so we just increment the ptr and return.
888 */
889 (*ptr)++;
890 return HI_SUCCESS;
891 }
892 else
893 {
894 /*
895 ** This means that we've already seen a space and a version
896 ** identifier, and now that we've seen another space, we know
897 ** that this can't be the URI so we just bail out with no
898 ** URI.
899 */
900 return NO_URI;
901 }
902 }
903
904 uri_ptr->ident = NULL;
905
906 /*
907 ** We only check for one here, because both should be set if one
908 ** is.
909 */
910 if(uri_ptr->first_sp_end)
911 {
912 /*
913 ** If the second space has been set, then this means that we have
914 ** seen a third space, which we shouldn't see in the URI so we
915 ** are now done and know there is no URI in this packet.
916 */
917 if(uri_ptr->second_sp_end)
918 {
919 return NO_URI;
920 }
921
922 /*
923 ** Treat whitespace differently at the end of the URI than we did
924 ** at the beginning. Ignore and return if special characters are
925 ** not defined as whitespace after the URI.
926 */
927 if(ServerConf->whitespace[**ptr]
928 && !(ServerConf->whitespace[**ptr] & HI_UI_CONFIG_WS_AFTER_URI))
929 {
930 (*ptr)++;
931 return HI_SUCCESS;
932 }
933
934 /*
935 ** Since we've seen the second space, we need to update the uri ptr
936 ** to the end of the first space, since the URI cannot be before the
937 ** first space.
938 */
939 uri_ptr->uri = uri_ptr->first_sp_end;
940
941 uri_ptr->second_sp_start = *ptr;
942 uri_ptr->second_sp_end = NULL;
943
944 start_sp = &uri_ptr->second_sp_start;
945 end_sp = &uri_ptr->second_sp_end;
946 }
947 else
948 {
949 /*
950 ** This means that there is whitespace at the beginning of the line
951 ** and we unset the URI so we can set it later if need be.
952 **
953 ** This is mainly so we handle data that is all spaces correctly.
954 **
955 ** In the normal case where we've seen text and then the first space,
956 ** we leave the uri ptr pointing at the beginning of the data, and
957 ** set the uri end after we've determined where to put it.
958 */
959 if(start == *ptr)
960 uri_ptr->uri = NULL;
961
962
963 uri_ptr->first_sp_start = *ptr;
964 uri_ptr->first_sp_end = NULL;
965
966 start_sp = &uri_ptr->first_sp_start;
967 end_sp = &uri_ptr->first_sp_end;
968 }
969
970 while(hi_util_in_bounds(start, end, *ptr))
971 {
972 /*
973 ** Check for whitespace
974 */
975 if(**ptr == ' ')
976 {
977 (*ptr)++;
978 continue;
979 }
980 else if(ServerConf->whitespace[**ptr])
981 {
982 if(ServerConf->apache_whitespace.on)
983 {
984 if(hi_eo_generate_event(Session,
985 ServerConf->apache_whitespace.alert))
986 {
987 hi_eo_client_event_log(Session, HI_EO_CLIENT_APACHE_WS,
988 NULL, NULL);
989 }
990 }
991 (*ptr)++;
992 continue;
993 }
994 else
995 {
996 /*
997 ** This sets the sp_end for whatever space delimiter we are on,
998 ** whether that is the first space or the second space.
999 */
1000 *end_sp = *ptr;
1001
1002 if(!IsHttpVersion(ptr, end))
1003 {
1004 /*
1005 ** This is the default method and what we've been doing
1006 ** since the start of development.
1007 */
1008 if(uri_ptr->second_sp_start)
1009 {
1010 /*
1011 ** There is no HTTP version indentifier at the beginning
1012 ** of the second space, and this means that there is no
1013 ** URI.
1014 */
1015 if(ServerConf->non_strict)
1016 {
1017 /*
1018 ** In non-strict mode, we must assume the URI is
1019 ** between the first and second space, so now
1020 ** that we've seen the second space that's the
1021 ** identifier.
1022 */
1023 uri_ptr->ident = *end_sp;
1024 uri_ptr->uri_end = *start_sp;
1025
1026 return HI_SUCCESS;
1027 }
1028 else
1029 {
1030 /*
1031 ** Since we are in strict mode here, it means that
1032 ** we haven't seen a valid identifier, so there was
1033 ** no URI.
1034 */
1035
1036 return NO_URI;
1037 }
1038 }
1039
1040 /*
1041 ** RESET NECESSARY URI_PTRs HERE. This is the place where
1042 ** the uri is updated. It can only happen once, so do it
1043 ** right here.
1044 **
1045 ** When we get here it means that we have found the end of
1046 ** the FIRST whitespace, and that there was no delimiter,
1047 ** so we reset the uri pointers and other related
1048 ** pointers.
1049 */
1050 uri_ptr->uri = *end_sp;
1051 uri_ptr->uri_end = end;
1052 uri_ptr->norm = NULL;
1053 uri_ptr->last_dir = NULL;
1054 uri_ptr->param = NULL;
1055 uri_ptr->proxy = NULL;
1056 }
1057 else
1058 {
1059 /*
1060 ** Means we found the HTTP version identifier and we reset
1061 ** the uri_end pointer to point to the beginning of the
1062 ** whitespace detected.
1063 **
1064 ** This works for both "uri_is_here HTTP/1.0" and
1065 ** "METHOD uri_is_here HTTP/1.0", so it works when the
1066 ** identifier is after either the first or the second
1067 ** whitespace.
1068 */
1069 uri_ptr->ident = *end_sp;
1070 uri_ptr->uri_end = *start_sp;
1071 }
1072
1073 /*
1074 ** We found a non-whitespace char
1075 */
1076 return HI_SUCCESS;
1077 }
1078 }
1079
1080 /*
1081 ** This is the case where we've seen text and found a whitespace until
1082 ** the end of the buffer. In that case, we set the uri_end to the
1083 ** beginning of the whitespace.
1084 */
1085 uri_ptr->uri_end = *start_sp;
1086
1087 return HI_OUT_OF_BOUNDS;
1088 }
1089
1090 /*
1091 ** NAME
1092 ** SetPercentNorm::
1093 */
1094 /**
1095 ** Check for percent normalization in the URI buffer.
1096 **
1097 ** We don't do much here besides check the configuration, set the pointer,
1098 ** and continue processing.
1099 **
1100 ** @param ServerConf pointer to the server configuration
1101 ** @param start pointer to the start of payload
1102 ** @param end pointer to the end of the payload
1103 ** @param ptr pointer to the pointer of the current index
1104 ** @param uri_ptr pointer to the URI_PTR construct
1105 **
1106 ** @return integer
1107 **
1108 ** @retval HI_SUCCESS function successful
1109 */
SetPercentNorm(HI_SESSION * Session,const u_char * start,const u_char * end,const u_char ** ptr,URI_PTR * uri_ptr)1110 int SetPercentNorm(HI_SESSION *Session, const u_char *start,
1111 const u_char *end, const u_char **ptr, URI_PTR *uri_ptr)
1112 {
1113 HTTPINSPECT_CONF *ServerConf = Session->server_conf;
1114
1115 if(!uri_ptr->norm && !uri_ptr->ident)
1116 {
1117 if(ServerConf->ascii.on)
1118 {
1119 uri_ptr->norm = *ptr;
1120 }
1121 }
1122
1123 (*ptr)++;
1124
1125 return HI_SUCCESS;
1126 }
1127
1128 /*
1129 ** NAME
1130 ** CheckLongDir::
1131 */
1132 /**
1133 ** We check the directory length against the global config.
1134 **
1135 ** @param Session pointer to the current session
1136 ** @param uri_ptr pointer to the URI state
1137 ** @param ptr pointer to the current index in buffer
1138 **
1139 ** @return integer
1140 **
1141 ** @retval HI_SUCCESS
1142 */
CheckLongDir(HI_SESSION * Session,URI_PTR * uri_ptr,const u_char * ptr)1143 static inline int CheckLongDir(HI_SESSION *Session, URI_PTR *uri_ptr,
1144 const u_char *ptr)
1145 {
1146 int iDirLen;
1147
1148 /*
1149 ** Check for oversize directory
1150 */
1151 if(Session->server_conf->long_dir &&
1152 uri_ptr->last_dir && !uri_ptr->param)
1153 {
1154 iDirLen = ptr - uri_ptr->last_dir;
1155
1156 if(iDirLen > Session->server_conf->long_dir &&
1157 hi_eo_generate_event(Session, HI_EO_CLIENT_OVERSIZE_DIR))
1158 {
1159 hi_eo_client_event_log(Session, HI_EO_CLIENT_OVERSIZE_DIR,
1160 NULL, NULL);
1161 }
1162 }
1163
1164 return HI_SUCCESS;
1165
1166 }
1167
1168 /*
1169 ** NAME
1170 ** SetSlashNorm::
1171 */
1172 /**
1173 ** Check for any directory traversal or multi-slash normalization.
1174 **
1175 ** @param ServerConf pointer to the server configuration
1176 ** @param start pointer to the start of payload
1177 ** @param end pointer to the end of the payload
1178 ** @param ptr pointer to the pointer of the current index
1179 ** @param uri_ptr pointer to the URI_PTR construct
1180 **
1181 ** @return integer
1182 **
1183 ** @retval HI_SUCCESS function successful
1184 ** @retval HI_OUT_OF_BOUNDS reached the end of the buffer
1185 */
SetSlashNorm(HI_SESSION * Session,const u_char * start,const u_char * end,const u_char ** ptr,URI_PTR * uri_ptr)1186 int SetSlashNorm(HI_SESSION *Session, const u_char *start,
1187 const u_char *end, const u_char **ptr, URI_PTR *uri_ptr)
1188 {
1189 HTTPINSPECT_CONF *ServerConf = Session->server_conf;
1190
1191 CheckLongDir(Session, uri_ptr, *ptr);
1192 if( proxy_start)
1193 {
1194 // This is the first dir after http://
1195 if(!uri_ptr->ident && !uri_ptr->last_dir)
1196 proxy_end = *ptr;
1197 }
1198 uri_ptr->last_dir = *ptr;
1199
1200 if(!uri_ptr->norm && !uri_ptr->ident)
1201 {
1202
1203 uri_ptr->norm = *ptr;
1204
1205 (*ptr)++;
1206
1207 if(!hi_util_in_bounds(start,end, *ptr))
1208 {
1209 /*
1210 ** This is the case where there is a slash as the last char
1211 ** and we don't want to normalize that since there really
1212 ** is nothing to normalize.
1213 */
1214 uri_ptr->norm = NULL;
1215 return HI_OUT_OF_BOUNDS;
1216 }
1217
1218 /*
1219 ** Check for directory traversals
1220 */
1221 if(ServerConf->directory.on)
1222 {
1223 if(**ptr == '.')
1224 {
1225 (*ptr)++;
1226 if(!hi_util_in_bounds(start, end, *ptr))
1227 {
1228 uri_ptr->norm = NULL;
1229 return HI_OUT_OF_BOUNDS;
1230 }
1231
1232 if(**ptr == '.' || ** ptr == '/')
1233 {
1234 return HI_SUCCESS;
1235 }
1236 }
1237 }
1238
1239 /*
1240 ** Check for multiple slash normalization
1241 */
1242 if(ServerConf->multiple_slash.on)
1243 {
1244 if(**ptr == '/')
1245 {
1246 return HI_SUCCESS;
1247 }
1248 }
1249
1250 uri_ptr->norm = NULL;
1251 return HI_SUCCESS;
1252 }
1253
1254 (*ptr)++;
1255
1256 return HI_SUCCESS;
1257 }
1258
1259 /*
1260 ** NAME
1261 ** SetBackSlashNorm::
1262 */
1263 /**
1264 ** Check for backslashes and if we need to normalize.
1265 **
1266 ** This really just checks the configuration option, and sets the norm
1267 ** variable if applicable.
1268 **
1269 ** @param ServerConf pointer to the server configuration
1270 ** @param start pointer to the start of payload
1271 ** @param end pointer to the end of the payload
1272 ** @param ptr pointer to the pointer of the current index
1273 ** @param uri_ptr pointer to the URI_PTR construct
1274 **
1275 ** @return integer
1276 **
1277 ** @retval HI_SUCCESS function successful
1278 */
SetBackSlashNorm(HI_SESSION * Session,const u_char * start,const u_char * end,const u_char ** ptr,URI_PTR * uri_ptr)1279 int SetBackSlashNorm(HI_SESSION *Session, const u_char *start,
1280 const u_char *end, const u_char **ptr, URI_PTR *uri_ptr)
1281 {
1282 HTTPINSPECT_CONF *ServerConf = Session->server_conf;
1283
1284 if(!uri_ptr->norm && !uri_ptr->ident)
1285 {
1286 if(ServerConf->iis_backslash.on)
1287 {
1288 uri_ptr->norm = *ptr;
1289 }
1290 }
1291
1292 (*ptr)++;
1293
1294 return HI_SUCCESS;
1295 }
1296
1297 /*
1298 * ** NAME
1299 * ** SetPlusNorm::
1300 * */
1301 /**
1302 * ** Check for "+" and if we need to normalize.
1303 * **
1304 * **
1305 * ** @param ServerConf pointer to the server configuration
1306 * ** @param start pointer to the start of payload
1307 * ** @param end pointer to the end of the payload
1308 * ** @param ptr pointer to the pointer of the current index
1309 * ** @param uri_ptr pointer to the URI_PTR construct
1310 * **
1311 * ** @return integer
1312 * **
1313 * ** @retval HI_SUCCESS function successful
1314 * */
1315
1316
SetPlusNorm(HI_SESSION * Session,const u_char * start,const u_char * end,const u_char ** ptr,URI_PTR * uri_ptr)1317 int SetPlusNorm(HI_SESSION *Session, const u_char *start,
1318 const u_char *end, const u_char **ptr, URI_PTR *uri_ptr)
1319 {
1320 if(!uri_ptr->norm && !uri_ptr->ident)
1321 {
1322 uri_ptr->norm = *ptr;
1323 }
1324
1325 (*ptr)++;
1326
1327 return HI_SUCCESS;
1328 }
1329
1330 /*
1331 ** NAME
1332 ** SetBinaryNorm::
1333 */
1334 /**
1335 ** Look for non-ASCII chars in the URI.
1336 **
1337 ** We look for these chars in the URI and set the normalization variable
1338 ** if it's not already set. I think we really only need this for IIS
1339 ** servers, but we may want to know if it's in the URI too.
1340 **
1341 ** @param ServerConf pointer to the server configuration
1342 ** @param start pointer to the start of payload
1343 ** @param end pointer to the end of the payload
1344 ** @param ptr pointer to the pointer of the current index
1345 ** @param uri_ptr pointer to the URI_PTR construct
1346 **
1347 ** @return integer
1348 **
1349 ** @retval HI_SUCCESS function successful
1350 */
SetBinaryNorm(HI_SESSION * Session,const u_char * start,const u_char * end,const u_char ** ptr,URI_PTR * uri_ptr)1351 int SetBinaryNorm(HI_SESSION *Session, const u_char *start,
1352 const u_char *end, const u_char **ptr, URI_PTR *uri_ptr)
1353 {
1354 if(!uri_ptr->norm && !uri_ptr->ident)
1355 {
1356 uri_ptr->norm = *ptr;
1357 }
1358
1359 (*ptr)++;
1360
1361 return HI_SUCCESS;
1362 }
1363
1364 /*
1365 ** NAME
1366 ** SetParamField::
1367 */
1368 /**
1369 ** This function sets the parameter field as the first '?'. The big thing
1370 ** is that we set the param value, so we don't false positive long dir
1371 ** events when it's really just a long parameter field.
1372 **
1373 ** @param ServerConf pointer to the server configuration
1374 ** @param start pointer to the start of payload
1375 ** @param end pointer to the end of the payload
1376 ** @param ptr pointer to the pointer of the current index
1377 ** @param uri_ptr pointer to the URI_PTR construct
1378 **
1379 ** @return integer
1380 **
1381 ** @retval HI_SUCCESS function successful
1382 */
SetParamField(HI_SESSION * Session,const u_char * start,const u_char * end,const u_char ** ptr,URI_PTR * uri_ptr)1383 int SetParamField(HI_SESSION *Session, const u_char *start,
1384 const u_char *end, const u_char **ptr, URI_PTR *uri_ptr)
1385 {
1386 if(!uri_ptr->ident)
1387 {
1388 uri_ptr->param = *ptr;
1389 }
1390
1391 (*ptr)++;
1392
1393 return HI_SUCCESS;
1394 }
1395 /*
1396 ** NAME
1397 ** SetProxy::
1398 */
1399 /**
1400 ** This function checks for an absolute URI in the URI.
1401 **
1402 ** @param ServerConf pointer to the server configuration
1403 ** @param start pointer to the start of payload
1404 ** @param end pointer to the end of the payload
1405 ** @param ptr pointer to the pointer of the current index
1406 ** @param uri_ptr pointer to the URI_PTR construct
1407 **
1408 ** @return integer
1409 **
1410 ** @retval HI_SUCCESS function successful
1411 */
SetProxy(HI_SESSION * Session,const u_char * start,const u_char * end,const u_char ** ptr,URI_PTR * uri_ptr)1412 int SetProxy(HI_SESSION *Session, const u_char *start,
1413 const u_char *end, const u_char **ptr, URI_PTR *uri_ptr)
1414 {
1415 HTTPINSPECT_CONF *ServerConf = Session->server_conf;
1416
1417 if(!uri_ptr->ident && !uri_ptr->last_dir)
1418 {
1419 if(hi_util_in_bounds(start, end, ((*ptr)+2)))
1420 {
1421 if(*((*ptr)+1) == '/' && *((*ptr)+2) == '/')
1422 {
1423 if(Session->global_conf->proxy_alert && !ServerConf->allow_proxy)
1424 uri_ptr->proxy = *ptr;
1425 //If we found :// check to see if it is preceeded by http. If so, this is a proxy
1426 proxy_start = (u_char *)SnortStrcasestr((const char *)uri_ptr->uri, (*ptr - uri_ptr->uri), "http");
1427 proxy_end = end;
1428 (*ptr) = (*ptr) + 3;
1429 return HI_SUCCESS;
1430 }
1431 }
1432 }
1433
1434 (*ptr)++;
1435
1436 return HI_SUCCESS;
1437 }
1438
1439 /*
1440 ** NAME
1441 ** SetClientVars::
1442 */
1443 /**
1444 ** This is where we set the HI_CLIENT values that we found during URI
1445 ** discovery. This also covers checking these values for errors.
1446 **
1447 ** @param Client pointer to HI_CLIENT structure
1448 ** @param uri_ptr pointer to the uri data
1449 **
1450 ** @return integer
1451 **
1452 ** @retval HI_NONFATAL_ERR problem with the uri values.
1453 ** @retval HI_SUCCESS values set successfully
1454 */
SetClientVars(HI_CLIENT * Client,URI_PTR * uri_ptr,u_int dsize)1455 static int SetClientVars(HI_CLIENT *Client, URI_PTR *uri_ptr, u_int dsize)
1456 {
1457 /*
1458 ** We got here either because we found the delimiter or we are
1459 ** out of bounds.
1460 */
1461
1462 /*
1463 if(uri_ptr->first_sp_start)
1464 printf("** first_start = %c\n", *uri_ptr->first_sp_start);
1465 if(uri_ptr->first_sp_end)
1466 printf("** first_end = %c\n", *uri_ptr->first_sp_end);
1467 if(uri_ptr->second_sp_start)
1468 printf("** second_start = %c\n", *uri_ptr->second_sp_start);
1469 if(uri_ptr->second_sp_end)
1470 printf("** second_end = %c\n", *uri_ptr->second_sp_end);
1471 if(uri_ptr->delimiter)
1472 printf("** delimiter = %c\n", *uri_ptr->delimiter);
1473
1474 if(uri_ptr->uri)
1475 printf("** uri = %c\n", *uri_ptr->uri);
1476 if(uri_ptr->norm)
1477 printf("** norm = %.2x\n", *uri_ptr->norm);
1478 */
1479
1480 /*
1481 ** This means that there was only spaces or delimiters within the
1482 ** complete URI. In this case, there is no valid URI so we just
1483 ** return such.
1484 */
1485 if(uri_ptr->uri == NULL)
1486 {
1487 return HI_NONFATAL_ERR;
1488 }
1489
1490 /*
1491 ** This is where we set the Session variables before moving into more
1492 ** HttpInspect processing. If we don't get to this point, then we don't
1493 ** need to set these variables since we would have aborted with a
1494 ** NONFATAL_ERR.
1495 */
1496 Client->request.uri = uri_ptr->uri;
1497 Client->request.uri_size = uri_ptr->uri_end - uri_ptr->uri;
1498 Client->request.uri_norm = uri_ptr->norm;
1499
1500 /*
1501 ** LAST RESORT:
1502 **
1503 ** This is one of the last checks we do to make sure that we didn't
1504 ** mess up or anything.
1505 */
1506 if(Client->request.uri_size > dsize)
1507 {
1508 /*
1509 ** Bad stuff, let's just bail.
1510 */
1511 return HI_NONFATAL_ERR;
1512 }
1513
1514 /*
1515 printf("** Norm = %s\n", Client->request.uri_norm ? "YES" : "NO");
1516 printf("** URI: |%.*s| size = %u\n", Client->request.uri_size,
1517 Client->request.uri, Client->request.uri_size);
1518 */
1519
1520 return HI_SUCCESS;
1521 }
1522
hi_client_extract_post(HI_SESSION * Session,HTTPINSPECT_CONF * ServerConf,const u_char * ptr,const u_char * end,URI_PTR * result,int content_length,bool is_chunked,HttpSessionData * hsd)1523 static inline int hi_client_extract_post(
1524 HI_SESSION *Session, HTTPINSPECT_CONF *ServerConf,
1525 const u_char *ptr, const u_char *end, URI_PTR *result,
1526 int content_length, bool is_chunked, HttpSessionData *hsd)
1527 {
1528 const u_char *start = ptr;
1529 const u_char *post_end = end;
1530
1531 Session->norm_flags &= HI_BODY;
1532
1533 /* Limit search depth */
1534 if (is_chunked)
1535 {
1536 if ( (ServerConf->chunk_length || ServerConf->small_chunk_length.size)
1537 && (CheckChunkEncoding(Session, start, end, &post_end, NULL, 0,
1538 0, NULL, NULL, hsd, HI_SI_CLIENT_MODE) == 1) )
1539 {
1540 result->uri = start;
1541 result->uri_end = post_end;
1542 return POST_END;
1543 }
1544 else
1545 {
1546 return HI_NONFATAL_ERR;
1547 }
1548 }
1549 else if(content_length > 0)
1550 {
1551 if ((post_end - ptr ) > content_length)
1552 {
1553 post_end = ptr + content_length;
1554 }
1555 }
1556 else
1557 {
1558 return HI_NONFATAL_ERR;
1559 }
1560
1561 result->uri = start;
1562 result->uri_end = post_end;
1563
1564 return POST_END;
1565 }
1566
1567
HTTP_CopyExtraDataToSession(const uint8_t * start,int length,int command_type,HTTP_LOG_STATE * log_state)1568 static inline int HTTP_CopyExtraDataToSession(const uint8_t *start, int length, int command_type, HTTP_LOG_STATE *log_state)
1569 {
1570 uint8_t *alt_buf;
1571 uint32_t alt_size;
1572 uint32_t *alt_len;
1573 int ret;
1574
1575 if (length <= 0)
1576 return -1;
1577
1578
1579 switch (command_type)
1580 {
1581 case COPY_URI:
1582 alt_buf = log_state->uri_extracted;
1583 alt_size = MAX_URI_EXTRACTED;
1584 alt_len = &(log_state->uri_bytes);
1585 break;
1586
1587 case COPY_HOSTNAME:
1588 alt_buf = log_state->hostname_extracted;
1589 alt_size = MAX_HOSTNAME;
1590 alt_len = &(log_state->hostname_bytes);
1591 break;
1592
1593 default:
1594 return -1;
1595 }
1596
1597 if(length > (int) alt_size)
1598 length = alt_size;
1599
1600 *alt_len = 0;
1601
1602 ret = SafeMemcpy(alt_buf, start, length, alt_buf, alt_buf + alt_size);
1603
1604 if (ret != SAFEMEM_SUCCESS)
1605 {
1606 return -1;
1607 }
1608
1609 *alt_len += length;
1610
1611 return 0;
1612 }
1613
HTTP_CopyUri(HTTPINSPECT_CONF * ServerConf,const u_char * start,const u_char * end,HttpSessionData * hsd,int stream_ins,void * ssnptr)1614 static inline void HTTP_CopyUri(HTTPINSPECT_CONF *ServerConf, const u_char *start, const u_char *end, HttpSessionData *hsd, int stream_ins, void* ssnptr)
1615 {
1616 int iRet = 0;
1617 const u_char *cur_ptr;
1618
1619 cur_ptr = start;
1620
1621 #if defined(FEAT_OPEN_APPID)
1622 if((ServerConf->log_uri || ServerConf->appid_enabled) && !stream_ins && hsd)
1623 #else
1624 if(ServerConf->log_uri && !stream_ins && hsd)
1625 #endif /* defined(FEAT_OPEN_APPID) */
1626 {
1627 SkipBlankSpace(start,end,&cur_ptr);
1628
1629 start = cur_ptr;
1630 if(!SetLogBuffers(hsd, ssnptr))
1631 {
1632 iRet = HTTP_CopyExtraDataToSession((uint8_t *)start, (end - start), COPY_URI, hsd->log_state);
1633 if(!iRet)
1634 hsd->log_flags |= HTTP_LOG_URI;
1635 }
1636 }
1637 }
1638
1639
unfold_http_uri(HTTPINSPECT_CONF * ServerConf,const u_char * end,URI_PTR * uri_ptr,HttpSessionData * hsd,int stream_ins,void * ssnptr)1640 static inline int unfold_http_uri(HTTPINSPECT_CONF *ServerConf, const u_char *end, URI_PTR *uri_ptr, HttpSessionData *hsd, int stream_ins, void* ssnptr)
1641 {
1642 uint8_t unfold_buf[DECODE_BLEN];
1643 uint32_t unfold_size =0;
1644 const u_char *p;
1645 int folded = 0;
1646 const char *tmp = NULL;
1647 int iRet = -1;
1648
1649 p = uri_ptr->uri;
1650
1651
1652 sf_unfold_header(p, (end - p), unfold_buf, sizeof(unfold_buf), &unfold_size, 0, &folded);
1653
1654 if( !folded)
1655 {
1656 HTTP_CopyUri(ServerConf, uri_ptr->uri , uri_ptr->uri_end, hsd, stream_ins, ssnptr);
1657 return iRet;
1658 }
1659
1660 tmp = SnortStrnPbrk((const char *)unfold_buf, unfold_size, " \t");
1661
1662 if (tmp != NULL)
1663 {
1664 unfold_size = ((uint8_t *)tmp - unfold_buf);
1665 iRet = 0;
1666 }
1667
1668 p = p + unfold_size;
1669 uri_ptr->uri_end = p;
1670
1671 HTTP_CopyUri(ServerConf, unfold_buf, unfold_buf + unfold_size, hsd, stream_ins, ssnptr);
1672
1673 return iRet;
1674 }
1675
1676
hi_client_extract_uri(HI_SESSION * Session,HTTPINSPECT_CONF * ServerConf,HI_CLIENT * Client,const u_char * start,const u_char * end,const u_char * ptr,URI_PTR * uri_ptr,HttpSessionData * hsd,int stream_ins,void * ssnptr)1677 static inline int hi_client_extract_uri(
1678 HI_SESSION *Session, HTTPINSPECT_CONF *ServerConf,
1679 HI_CLIENT * Client, const u_char *start, const u_char *end,
1680 const u_char *ptr, URI_PTR *uri_ptr, HttpSessionData *hsd, int stream_ins, void* ssnptr)
1681 {
1682 int iRet = HI_SUCCESS;
1683 const u_char *tmp;
1684 int uri_copied = 0;
1685
1686 Session->norm_flags &= ~HI_BODY;
1687
1688
1689 /*
1690 ** This loop compares each char to an array of functions
1691 ** (one for each char) and calling that function if there is one.
1692 **
1693 ** If there is no function, then we just increment the char ptr and
1694 ** continue processing.
1695 **
1696 ** If there is a function, we call that function and process. It's
1697 ** important to note that the function that is called is responsible
1698 ** for incrementing the ptr to the next char to be inspected. The
1699 ** loop does not increment the pointer when a function is called to
1700 ** allow the maximum flexibility to the functions.
1701 */
1702
1703 while(hi_util_in_bounds(start, end, ptr))
1704 {
1705 if(!ServerConf->extended_ascii_uri)
1706 {
1707 /* isascii returns non-zero if it is ascii */
1708 if (isascii((int)*ptr) == 0)
1709 {
1710 /* Possible post data or something else strange... */
1711 iRet = URI_END;
1712 /* Find the end of the URI in this case*/
1713 tmp = (const u_char *)SnortStrnPbrk((const char *)ptr, (uri_ptr->uri_end - ptr), " \r\n\t");
1714 if(tmp != NULL)
1715 uri_ptr->uri_end = tmp;
1716
1717 if(!uri_copied)
1718 {
1719 HTTP_CopyUri(ServerConf, uri_ptr->uri , uri_ptr->uri_end, hsd, stream_ins, ssnptr);
1720 }
1721 break;
1722 }
1723 }
1724
1725 if(lookup_table[*ptr] || ServerConf->whitespace[*ptr])
1726 {
1727 if(lookup_table[*ptr])
1728 {
1729 iRet = (lookup_table[*ptr])(Session, start, end,
1730 &ptr, uri_ptr);
1731 }
1732 else
1733 {
1734 iRet = NextNonWhiteSpace(Session, start, end, &ptr, uri_ptr);
1735 }
1736
1737 if(iRet)
1738 {
1739 if(iRet == URI_END)
1740 {
1741 if((*(uri_ptr->uri_end) == '\n') || (*(uri_ptr->uri_end) == '\r') )
1742 {
1743 uri_copied = 1;
1744 if(!unfold_http_uri(ServerConf, end, uri_ptr, hsd, stream_ins, ssnptr ))
1745 {
1746 SkipCRLF(start,end, &ptr);
1747 continue;
1748 }
1749 }
1750 else if(!uri_copied)
1751 {
1752 HTTP_CopyUri(ServerConf, uri_ptr->uri , uri_ptr->uri_end, hsd, stream_ins, ssnptr);
1753 }
1754 /*
1755 ** You found a URI, let's break and check it out.
1756 */
1757 break;
1758 }
1759 else if(iRet == HI_OUT_OF_BOUNDS)
1760 {
1761 /*
1762 ** Means you've reached the end of the buffer. THIS
1763 ** DOESN'T MEAN YOU HAVEN'T FOUND A URI.
1764 */
1765 break;
1766 }
1767 else /* NO_URI */
1768 {
1769 /*
1770 ** Check for chunk encoding, because the delimiter can
1771 ** also be a space, which would look like a pipeline request
1772 ** to us if we don't do this first.
1773 */
1774 if(Session->server_conf->chunk_length || Session->server_conf->small_chunk_length.size)
1775 {
1776 (void)CheckChunkEncoding(Session, start, end, NULL, NULL, 0,
1777 0, NULL, NULL, hsd, HI_SI_CLIENT_MODE);
1778 }
1779
1780 /*
1781 ** We only inspect the packet for another pipeline
1782 ** request if there wasn't a previous pipeline request.
1783 ** The reason that we do this is because
1784 */
1785 if(!Client->request.pipeline_req)
1786 {
1787 /*
1788 ** Just because there was no URI in the first part
1789 ** the packet, doesn't mean that this isn't a
1790 ** pipelined request that has been segmented.
1791 */
1792 if(!ServerConf->no_pipeline)
1793 {
1794 Client->request.pipeline_req = FindPipelineReq(Session, ptr, end);
1795 if(Client->request.pipeline_req)
1796 {
1797 return HI_SUCCESS;
1798 }
1799 }
1800 }
1801
1802 return HI_NONFATAL_ERR;
1803 }
1804 }
1805 else
1806 {
1807 /*
1808 ** This means that we found the next non-whitespace char
1809 ** and since we are already pointed there, so we just
1810 ** continue.
1811 */
1812 continue;
1813 }
1814 }
1815
1816 ptr++;
1817 }
1818 /* No uri in this request. We shouldn't process this request */
1819 if(uri_ptr->uri == uri_ptr->uri_end)
1820 return HI_NONFATAL_ERR;
1821 return iRet;
1822 }
1823
extract_http_cookie(const u_char * p,const u_char * end,HEADER_PTR * header_ptr,HEADER_FIELD_PTR * header_field_ptr)1824 const u_char *extract_http_cookie(const u_char *p, const u_char *end, HEADER_PTR *header_ptr,
1825 HEADER_FIELD_PTR *header_field_ptr)
1826 {
1827 const u_char *crlf;
1828 const u_char *start;
1829 if (header_ptr->cookie.cookie)
1830 {
1831 /* unusal, multiple cookies... alloc new cookie pointer */
1832 COOKIE_PTR *extra_cookie = (COOKIE_PTR *)SnortPreprocAlloc(1, sizeof(COOKIE_PTR),
1833 PP_HTTPINSPECT, PP_MEM_CATEGORY_SESSION);
1834 hi_stats.mem_used += sizeof(COOKIE_PTR);
1835 if (!extra_cookie)
1836 {
1837 /* Failure to allocate, stop where we are... */
1838 header_ptr->header.uri_end = p;
1839 return p;
1840 }
1841 header_field_ptr->cookie->next = extra_cookie;
1842 header_field_ptr->cookie = extra_cookie;
1843 /* extra_cookie->next = NULL; */ /* removed, since calloc NULLs this. */
1844 }
1845 else
1846 {
1847 header_field_ptr->cookie = &header_ptr->cookie;
1848 }
1849
1850 start = p;
1851 /* skip spaces before : */
1852 SkipBlankSpace(start,end,&p);
1853 if(hi_util_in_bounds(start, end, p) && *p == ':')
1854 {
1855 p++;
1856 SkipBlankSpace(start,end,&p);
1857 }
1858
1859 header_field_ptr->cookie->cookie = p;
1860
1861 {
1862 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
1863
1864 /* find a \n */
1865 if (crlf) /* && hi_util_in_bounds(start, end, crlf+1)) bounds is checked in SnortStrnStr */
1866 {
1867 if(*(crlf -1) == '\r')
1868 header_field_ptr->cookie->cookie_end = crlf - 1;
1869 else
1870 header_field_ptr->cookie->cookie_end = crlf;
1871
1872 p = crlf;
1873 }
1874 else
1875 {
1876 header_ptr->header.uri_end = header_field_ptr->cookie->cookie_end = end;
1877 return end;
1878 }
1879 }
1880 return p;
1881 }
1882
createNode_tList(sfaddr_t * tmp,uint8_t req_id)1883 Transaction* createNode_tList(sfaddr_t *tmp, uint8_t req_id)
1884 {
1885 Transaction *tList_node = (Transaction*)SnortPreprocAlloc(1, sizeof(Transaction),
1886 PP_HTTPINSPECT, PP_MEM_CATEGORY_SESSION);
1887 hi_stats.mem_used += sizeof(Transaction);
1888 tList_node->true_ip = tmp;
1889 tList_node->tID = req_id;
1890 tList_node->next = NULL;
1891 return tList_node;
1892 }
1893
insertNode_tList(HttpSessionData * hsd,sfaddr_t * tmp)1894 void insertNode_tList(HttpSessionData* hsd, sfaddr_t *tmp)
1895 {
1896 Transaction *tList_node = createNode_tList(tmp,hsd->http_req_id);
1897 if( hsd->tList_start == NULL && hsd->tList_end == NULL )
1898 {
1899 hsd->tList_start = tList_node;
1900 hsd->tList_end = tList_node;
1901 }
1902 else if ( (hsd->tList_end != NULL) && ( hsd->tList_end->tID != hsd->http_req_id ) )
1903 {
1904 hsd->tList_end->next = tList_node;
1905 hsd->tList_end = tList_node;
1906 }
1907 else
1908 freeTransactionNode(tList_node);
1909 }
1910
extract_http_xff(HI_SESSION * Session,const u_char * p,const u_char * start,const u_char * end,HI_CLIENT_HDR_ARGS * hdrs_args)1911 const u_char *extract_http_xff(HI_SESSION *Session, const u_char *p, const u_char *start,
1912 const u_char *end, HI_CLIENT_HDR_ARGS *hdrs_args)
1913 {
1914 int num_spaces = 0;
1915 SFIP_RET status;
1916 sfaddr_t *tmp;
1917 char *ipAddr = NULL;
1918 uint8_t unfold_buf[DECODE_BLEN];
1919 uint32_t unfold_size =0;
1920 const u_char *start_ptr, *end_ptr, *cur_ptr;
1921 const u_char *port;
1922 HEADER_PTR *header_ptr;
1923
1924 header_ptr = hdrs_args->hdr_ptr;
1925
1926 if( (hdrs_args->true_clnt_xff & (HDRS_BOTH | XFF_HEADERS)) == HDRS_BOTH)
1927 {
1928 if(hi_eo_generate_event(Session, HI_EO_CLIENT_BOTH_TRUEIP_XFF_HDRS))
1929 {
1930 hi_eo_client_event_log(Session, HI_EO_CLIENT_BOTH_TRUEIP_XFF_HDRS, NULL, NULL);
1931 }
1932
1933 }
1934
1935 SkipBlankSpace(start,end,&p);
1936
1937 if(hi_util_in_bounds(start, end, p) && *p == ':')
1938 {
1939 p++;
1940 CheckSkipAlertMultipleColon(Session, start, end, &p, HI_SI_CLIENT_MODE);
1941
1942 if(hi_util_in_bounds(start, end, p))
1943 sf_unfold_header(p, end-p, unfold_buf, sizeof(unfold_buf), &unfold_size, 0 , &num_spaces);
1944
1945 if(!unfold_size)
1946 {
1947 header_ptr->header.uri_end = end;
1948 return end;
1949 }
1950
1951 if(num_spaces >= Session->server_conf->max_spaces)
1952 {
1953 if(hi_eo_generate_event(Session, Session->server_conf->max_spaces))
1954 {
1955 hi_eo_client_event_log(Session, HI_EO_CLIENT_EXCEEDS_SPACES, NULL, NULL);
1956 }
1957
1958 }
1959
1960 p = p + unfold_size;
1961
1962 start_ptr = unfold_buf;
1963 cur_ptr = unfold_buf;
1964 end_ptr = unfold_buf + unfold_size;
1965 SkipBlankSpace(start_ptr,end_ptr,&cur_ptr);
1966
1967 start_ptr = cur_ptr;
1968 while( cur_ptr < end_ptr )
1969 {
1970 if( *cur_ptr == ' ' || *cur_ptr == '\t' ||
1971 *cur_ptr == ',' )
1972 break;
1973 cur_ptr++;
1974 }
1975
1976 if(cur_ptr - start_ptr)
1977 {
1978 ipAddr = SnortStrndup((const char *)start_ptr, cur_ptr - start_ptr );
1979 }
1980 if(ipAddr)
1981 {
1982 if( (tmp = sfaddr_alloc(ipAddr, &status)) == NULL )
1983 {
1984 port = (u_char *)SnortStrnStr((const char *)start_ptr, (cur_ptr - start_ptr), ":");
1985 if(port)
1986 {
1987 free(ipAddr);
1988 ipAddr = SnortStrndup((const char *)start_ptr, port - start_ptr );
1989 if( !ipAddr)
1990 {
1991 return p;
1992 }
1993 if( (tmp = sfaddr_alloc(ipAddr, &status)) == NULL )
1994 {
1995 if((status != SFIP_ARG_ERR) && (status !=SFIP_ALLOC_ERR))
1996 {
1997 if(hi_eo_generate_event(Session, HI_EO_CLIENT_INVALID_TRUEIP))
1998 {
1999 hi_eo_client_event_log(Session, HI_EO_CLIENT_INVALID_TRUEIP, NULL, NULL);
2000 }
2001 free(ipAddr);
2002 return p;
2003 }
2004 }
2005 }
2006 else if((status != SFIP_ARG_ERR) && (status !=SFIP_ALLOC_ERR))
2007 {
2008 if(hi_eo_generate_event(Session, HI_EO_CLIENT_INVALID_TRUEIP))
2009 {
2010 hi_eo_client_event_log(Session, HI_EO_CLIENT_INVALID_TRUEIP, NULL, NULL);
2011 }
2012 free(ipAddr);
2013 return p;
2014 }
2015 }
2016 /* At this point we have a new/valid IP from the header being processed.
2017 If we are using custom xff headers, check the precedence ranking. */
2018 if( (hdrs_args->true_clnt_xff & XFF_HEADERS) != 0 )
2019 {
2020 /* Have we located any others? */
2021 if( (hdrs_args->top_precedence > 0) &&
2022 (hdrs_args->new_precedence >= hdrs_args->top_precedence) )
2023 {
2024 sfaddr_free( tmp );
2025 free( ipAddr );
2026 return( p );
2027 }
2028
2029 hdrs_args->top_precedence = hdrs_args->new_precedence;
2030
2031 /* if we find the top precedence, no need to continue
2032 looking so clear the XFF_HEADERS_ACTIVE flag. */
2033 if( hdrs_args->top_precedence == XFF_TOP_PRECEDENCE )
2034 hdrs_args->true_clnt_xff &= (~XFF_HEADERS_ACTIVE);
2035 }
2036
2037 /*** If Count reaches to Max limit, we are not store XFF data for further requests in the session.
2038 */
2039 if( ( hdrs_args->sd->tList_count != XFF_MAX_PIPELINE_REQ ) && (hdrs_args->sd->tList_count != 0 ) )
2040 {
2041 /* Check if true-IP for this request is added to the List or not. If not, add this new IP to the List.
2042 If already added true-IP , then check new Ip and current IP is same or not.*/
2043 if ( (hdrs_args->sd->tList_end != NULL) && ( hdrs_args->sd->tList_end->tID == hdrs_args->sd->http_req_id ) )
2044 {
2045 /* If we have already added a true_ip to the List for the currect request,
2046 see if the current IP differs from other XFF Headers IP.
2047 If so , replace it and post an alert saying multiple true IPs in same session.
2048 */
2049 if (!IP_EQUALITY(hdrs_args->sd->tList_end->true_ip, tmp))
2050 {
2051 if (hdrs_args->top_precedence)
2052 {
2053 /* If we've precedence configuration, then add new IP to the List */
2054 sfaddr_free(hdrs_args->sd->tList_end->true_ip);
2055 hdrs_args->sd->tList_end->true_ip = tmp;
2056
2057 }
2058 else if ((hdrs_args->prev_true_clnt_xff & TRUE_CLIENT_IP_HDR) && (hdrs_args->true_clnt_xff & XFF_HDR) )
2059 {
2060 /* when no precedence configured, First X-Forwarded-For IP should print though True-Client-IP present in GET request */
2061 sfaddr_free(hdrs_args->sd->tList_end->true_ip);
2062 hdrs_args->sd->tList_end->true_ip = tmp;
2063 }
2064 else
2065 {
2066 sfaddr_free(tmp);
2067 }
2068
2069 // alert
2070 if( ((hdrs_args->true_clnt_xff & XFF_HEADERS) == 0) &&
2071 hi_eo_generate_event(Session, HI_EO_CLIENT_MULTIPLE_TRUEIP_IN_SESSION))
2072 hi_eo_client_event_log(Session, HI_EO_CLIENT_MULTIPLE_TRUEIP_IN_SESSION, NULL, NULL);
2073 }
2074 else
2075 sfaddr_free(tmp);
2076 }
2077 else
2078 insertNode_tList(hdrs_args->sd, tmp);
2079 }
2080 else
2081 sfaddr_free(tmp);
2082
2083 free(ipAddr);
2084 }
2085
2086 }
2087 else
2088 {
2089 header_ptr->header.uri_end = end;
2090 return end;
2091 }
2092 hdrs_args->prev_true_clnt_xff = hdrs_args->true_clnt_xff;
2093 return p;
2094
2095 }
2096
2097 #if defined(FEAT_OPEN_APPID)
extract_http_client_header(HI_SESSION * Session,const u_char * p,const u_char * start,const u_char * end,HEADER_PTR * header_ptr,HEADER_LOCATION * headerLoc)2098 static const u_char *extract_http_client_header(HI_SESSION *Session, const u_char *p, const u_char *start,
2099 const u_char *end, HEADER_PTR *header_ptr, HEADER_LOCATION *headerLoc)
2100 {
2101 int num_spaces = 0;
2102 uint8_t unfold_buf[DECODE_BLEN];
2103 uint32_t unfold_size =0;
2104 const u_char *end_ptr, *cur_ptr;
2105
2106 SkipBlankSpace(start,end,&p);
2107
2108 if(hi_util_in_bounds(start, end, p) && *p == ':')
2109 {
2110 p++;
2111 CheckSkipAlertMultipleColon(Session, start, end, &p, HI_SI_CLIENT_MODE);
2112
2113 if(hi_util_in_bounds(start, end, p))
2114 sf_unfold_header(p, end-p, unfold_buf, sizeof(unfold_buf), &unfold_size, 0 , &num_spaces);
2115
2116 if(!unfold_size)
2117 {
2118 header_ptr->header.uri_end = end;
2119 return end;
2120 }
2121
2122 if(num_spaces >= Session->server_conf->max_spaces)
2123 {
2124 if(hi_eo_generate_event(Session, Session->server_conf->max_spaces))
2125 {
2126 hi_eo_client_event_log(Session, HI_EO_CLIENT_EXCEEDS_SPACES, NULL, NULL);
2127 }
2128 }
2129
2130 p = p + unfold_size;
2131
2132 cur_ptr = unfold_buf;
2133 end_ptr = unfold_buf + unfold_size;
2134 SkipBlankSpace(unfold_buf,end_ptr,&cur_ptr);
2135
2136 {
2137 unsigned field_strlen = (unsigned)(end_ptr - cur_ptr);
2138 if (field_strlen)
2139 {
2140 headerLoc->start = (u_char *)strndup((const char *)cur_ptr, field_strlen);
2141 if (NULL == headerLoc->start)
2142 {
2143 /* treat this out-of-memory error as a parse failure */
2144 header_ptr->header.uri_end = end;
2145 return end;
2146 }
2147 /* now that we have the memory, fill in len. */
2148 headerLoc->len = field_strlen;
2149 }
2150 }
2151 }
2152 else
2153 {
2154 header_ptr->header.uri_end = end;
2155 return end;
2156 }
2157
2158 return p;
2159
2160 }
2161
2162 #endif /* defined(FEAT_OPEN_APPID) */
2163
extract_http_hostname(HI_SESSION * Session,const u_char * p,const u_char * start,const u_char * end,HEADER_PTR * header_ptr,HttpSessionData * hsd)2164 const u_char *extract_http_hostname(HI_SESSION *Session, const u_char *p, const u_char *start,
2165 const u_char *end, HEADER_PTR *header_ptr, HttpSessionData *hsd)
2166 {
2167 int num_spaces = 0;
2168 uint8_t unfold_buf[DECODE_BLEN];
2169 uint32_t unfold_size =0;
2170 const u_char *start_ptr, *end_ptr, *cur_ptr;
2171 int iRet=0;
2172
2173
2174 SkipBlankSpace(start,end,&p);
2175
2176 if(hi_util_in_bounds(start, end, p) && *p == ':')
2177 {
2178 p++;
2179 CheckSkipAlertMultipleColon(Session, start, end, &p, HI_SI_CLIENT_MODE);
2180
2181 if(hi_util_in_bounds(start, end, p))
2182 sf_unfold_header(p, end-p, unfold_buf, sizeof(unfold_buf), &unfold_size, 0, &num_spaces);
2183
2184 if(!unfold_size)
2185 {
2186 header_ptr->header.uri_end = end;
2187 return end;
2188 }
2189
2190 if(num_spaces >= Session->server_conf->max_spaces)
2191 {
2192 //alert
2193 if(hi_eo_generate_event(Session, Session->server_conf->max_spaces))
2194 {
2195 hi_eo_client_event_log(Session, HI_EO_CLIENT_EXCEEDS_SPACES, NULL, NULL);
2196 }
2197 }
2198 p = p + unfold_size;
2199
2200 start_ptr = unfold_buf;
2201 cur_ptr = unfold_buf;
2202 end_ptr = unfold_buf + unfold_size;
2203 SkipBlankSpace(start_ptr,end_ptr,&cur_ptr);
2204
2205 start_ptr = cur_ptr;
2206
2207 if((end_ptr - start_ptr) >= MAX_HOSTNAME)
2208 {
2209 if(hi_eo_generate_event(Session, HI_EO_CLIENT_LONG_HOSTNAME))
2210 {
2211 hi_eo_client_event_log(Session, HI_EO_CLIENT_LONG_HOSTNAME, NULL, NULL);
2212 }
2213 }
2214
2215 iRet = HTTP_CopyExtraDataToSession((uint8_t *)start_ptr, (end_ptr - start_ptr), COPY_HOSTNAME, hsd->log_state);
2216 if(!iRet)
2217 {
2218 hsd->log_flags |= HTTP_LOG_HOSTNAME;
2219 }
2220 }
2221 else
2222 {
2223 header_ptr->header.uri_end = end;
2224 return end;
2225 }
2226
2227 return p;
2228 }
2229
2230 /* extract_http_range will extract "0-" and flag it as full
2231 * content, when the unit is bytes. Otherwise flag error or
2232 * partial content accordingly. Syntax as follows,
2233 * Range: <units>=<ranges separated with ,>
2234 */
extract_http_range(HI_SESSION * Session,const u_char * p,const u_char * start,const u_char * end,HEADER_PTR * header_ptr)2235 static const u_char *extract_http_range(HI_SESSION *Session,
2236 const u_char *p, const u_char *start, const u_char *end,
2237 HEADER_PTR *header_ptr)
2238 {
2239 u_char *crlf = NULL;
2240 const u_char *unit_start = NULL;
2241 const u_char *unit_end = NULL;
2242
2243 SkipBlankSpace(start,end,&p);
2244 if (hi_util_in_bounds(start, end, p) && *p == ':')
2245 {
2246 p++;
2247 CheckSkipAlertMultipleColon(Session, start, end, &p, HI_SI_CLIENT_MODE);
2248 while (hi_util_in_bounds(start, end, p) && ( *p == ' ' || *p == '\t' || *p == '\n'))
2249 {
2250 p++;
2251 }
2252
2253 if (hi_util_in_bounds(start, end, p))
2254 {
2255 /* extract units and look for '=' token */
2256 unit_start = p;
2257 while (hi_util_in_bounds(start, end, p) && ( *p != '='))
2258 {
2259 p++;
2260 }
2261
2262 if (*p != '=')
2263 {
2264 if (hi_eo_generate_event(Session, HI_EO_CLIENT_INVALID_RANGE_UNIT_FMT))
2265 {
2266 hi_eo_client_event_log(Session, HI_EO_CLIENT_INVALID_RANGE_UNIT_FMT, NULL, NULL);
2267 }
2268 header_ptr->range_flag = RANGE_WITH_REQ_ERROR;
2269 return end;
2270 }
2271
2272 unit_end = (p - 1);
2273 p++;
2274 SkipBlankSpace(start,end,&p);
2275
2276 while (hi_util_in_bounds(start, end, p) && ( *p == ','))
2277 {
2278 p++;
2279 }
2280 SkipBlankSpace(start,end,&p);
2281
2282 if (hi_util_in_bounds(start, end, p))
2283 {
2284 /* Look for "0-" and unit as bytes, then set it as full content */
2285 if (*p == '0')
2286 {
2287 p++;
2288 if (hi_util_in_bounds(start, end, p))
2289 {
2290 if (*p == '-')
2291 {
2292 p++;
2293 if (hi_util_in_bounds(start, end, p) && ( *p == '\r' || *p == '\n'))
2294 {
2295 if (((unit_end - unit_start) >= 5) &&
2296 (!strncasecmp((const char *)unit_start, RANGE_UNIT_BYTE, 5)))
2297 {
2298 header_ptr->range_flag = HTTP_RANGE_WITH_FULL_CONTENT_REQ;
2299 }
2300 else
2301 {
2302 header_ptr->range_flag = RANGE_WITH_PARTIAL_CONTENT_REQ;
2303 }
2304
2305 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
2306 if (crlf)
2307 {
2308 p = crlf;
2309 return p;
2310 }
2311 else
2312 {
2313 header_ptr->header.uri_end = end;
2314 return end;
2315 }
2316 }
2317 }
2318 }
2319 }
2320
2321 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
2322 if (crlf)
2323 {
2324 p = crlf;
2325 header_ptr->range_flag = RANGE_WITH_PARTIAL_CONTENT_REQ;
2326 return p;
2327 }
2328 else
2329 {
2330 header_ptr->header.uri_end = end;
2331 header_ptr->range_flag = RANGE_WITH_REQ_ERROR;
2332 return end;
2333 }
2334 }
2335 }
2336 }
2337
2338 header_ptr->range_flag = RANGE_WITH_REQ_ERROR;
2339 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
2340 if (crlf)
2341 {
2342 p = crlf;
2343 return p;
2344 }
2345 else
2346 {
2347 header_ptr->header.uri_end = end;
2348 return end;
2349 }
2350 }
2351
extract_http_content_length(HI_SESSION * Session,HTTPINSPECT_CONF * ServerConf,const u_char * p,const u_char * start,const u_char * end,HEADER_PTR * header_ptr,HEADER_FIELD_PTR * header_field_ptr,int iInspectMode)2352 const u_char *extract_http_content_length(HI_SESSION *Session,
2353 HTTPINSPECT_CONF *ServerConf, const u_char *p, const u_char *start,
2354 const u_char *end, HEADER_PTR *header_ptr, HEADER_FIELD_PTR *header_field_ptr, int iInspectMode)
2355 {
2356 int num_spaces = 0;
2357 const u_char *crlf;
2358 int space_present = 0;
2359 if (header_ptr->content_len.cont_len_start)
2360 {
2361 if(iInspectMode == HI_SI_SERVER_MODE)
2362 {
2363 if(hi_eo_generate_event(Session, HI_EO_SERVER_MULTIPLE_CONTLEN))
2364 {
2365 hi_eo_server_event_log(Session, HI_EO_SERVER_MULTIPLE_CONTLEN, NULL, NULL);
2366 }
2367 }
2368 else if(iInspectMode == HI_SI_CLIENT_MODE)
2369 {
2370 if(hi_eo_generate_event(Session, HI_EO_CLIENT_MULTIPLE_CONTLEN))
2371 {
2372 hi_eo_client_event_log(Session, HI_EO_CLIENT_MULTIPLE_CONTLEN, NULL, NULL);
2373 }
2374 }
2375 header_ptr->header.uri_end = p;
2376 return p;
2377 }
2378 else
2379 {
2380 header_field_ptr->content_len = &header_ptr->content_len;
2381 p = p + 14;
2382 }
2383 /* Move past all the blank spaces. Only tabs and spaces are allowed here */
2384 SkipBlankSpace(start,end,&p);
2385
2386 if(hi_util_in_bounds(start, end, p) && *p == ':')
2387 {
2388 p++;
2389 CheckSkipAlertMultipleColon(Session, start, end, &p, iInspectMode);
2390
2391 if ( hi_util_in_bounds(start, end, p) )
2392 {
2393 if ( ServerConf->profile == HI_APACHE || ServerConf->profile == HI_ALL)
2394 {
2395 SkipWhiteSpace(start,end,&p);
2396 }
2397 else
2398 {
2399 SkipBlankAndNewLine(start,end,&p);
2400 }
2401 if( hi_util_in_bounds(start, end, p))
2402 {
2403 if ( *p == '\n' )
2404 {
2405 while(hi_util_in_bounds(start, end, p))
2406 {
2407 if ( *p == '\n')
2408 {
2409 p++;
2410 while( hi_util_in_bounds(start, end, p) && ( *p == ' ' || *p == '\t'))
2411 {
2412 space_present = 1;
2413 p++;
2414 num_spaces++;
2415 }
2416 if ( space_present )
2417 {
2418 if(num_spaces >= Session->server_conf->max_spaces)
2419 {
2420 if(hi_eo_generate_event(Session, Session->server_conf->max_spaces))
2421 {
2422 hi_eo_client_event_log(Session, HI_EO_CLIENT_EXCEEDS_SPACES, NULL, NULL);
2423 }
2424 }
2425 if ( isdigit((int)*p))
2426 break;
2427 else if(isspace((int)*p) &&
2428 (ServerConf->profile == HI_APACHE || ServerConf->profile == HI_ALL) )
2429 {
2430 SkipWhiteSpace(start,end,&p);
2431 }
2432 else
2433 {
2434 header_field_ptr->content_len->cont_len_start =
2435 header_field_ptr->content_len->cont_len_end = NULL;
2436 header_field_ptr->content_len->len = 0;
2437 return p;
2438 }
2439 }
2440 else
2441 {
2442 header_field_ptr->content_len->cont_len_start =
2443 header_field_ptr->content_len->cont_len_end = NULL;
2444 header_field_ptr->content_len->len = 0;
2445 return p;
2446 }
2447 }
2448 else
2449 break;
2450 }
2451 }
2452 else if(!isdigit((int)*p))
2453 {
2454 header_field_ptr->content_len->cont_len_start =
2455 header_field_ptr->content_len->cont_len_end = NULL;
2456 header_field_ptr->content_len->len = 0;
2457 return p;
2458 }
2459 if(isdigit((int)*p))
2460 {
2461 header_field_ptr->content_len->cont_len_start = p;
2462 p++;
2463 while(hi_util_in_bounds(start, end, p))
2464 {
2465 if(isdigit((int)*p))
2466 {
2467 p++;
2468 continue;
2469 }
2470 else if((*p == '\n')) /* digit followed by \n */
2471 {
2472 header_field_ptr->content_len->cont_len_end = p;
2473 break;
2474 }
2475 else if( (!isdigit((int)*p)) && (!isspace((int)*p))) /* alphabet after digit*/
2476 {
2477 header_field_ptr->content_len->cont_len_start =
2478 header_field_ptr->content_len->cont_len_end = NULL;
2479 header_field_ptr->content_len->len = 0;
2480
2481 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
2482 if (crlf)
2483 {
2484 return p;
2485 }
2486 else
2487 {
2488 header_ptr->header.uri_end = end;
2489 return end;
2490 }
2491 }
2492 else
2493 {
2494 if (ServerConf->profile == HI_APACHE || ServerConf->profile == HI_ALL)
2495 {
2496 SkipWhiteSpace(start,end,&p);
2497 }
2498 else
2499 {
2500 SkipBlankAndNewLine(start,end,&p);
2501 }
2502 if ( *p == '\n' )
2503 {
2504 header_field_ptr->content_len->cont_len_end = p;
2505 break;
2506 }
2507 else /*either a "digit digit" or "digit other character" */
2508 {
2509 header_field_ptr->content_len->cont_len_start =
2510 header_field_ptr->content_len->cont_len_end = NULL;
2511 header_field_ptr->content_len->len = 0;
2512 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
2513 if (crlf)
2514 {
2515 p = crlf;
2516 return p;
2517 }
2518 else
2519 {
2520 header_ptr->header.uri_end = end;
2521 return end;
2522 }
2523 }
2524 }
2525 }
2526 }
2527 else
2528 {
2529 header_field_ptr->content_len->cont_len_start =
2530 header_field_ptr->content_len->cont_len_end = NULL;
2531 header_field_ptr->content_len->len = 0;
2532 return p;
2533 }
2534 }
2535 }
2536 }
2537 else
2538 {
2539 if(hi_util_in_bounds(start, end, p))
2540 {
2541 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
2542 if(crlf)
2543 {
2544 p = crlf;
2545 }
2546 else
2547 {
2548 header_ptr->header.uri_end = end ;
2549 return end;
2550 }
2551 }
2552 }
2553 if ( header_field_ptr->content_len->cont_len_start &&
2554 header_field_ptr->content_len->cont_len_end )
2555 {
2556 char *pcEnd;
2557 uint64_t len;
2558 len = (uint64_t)SnortStrtol((char *)header_field_ptr->content_len->cont_len_start, &pcEnd, 10);
2559
2560 if ( (errno == ERANGE)
2561 || ((char *)header_field_ptr->content_len->cont_len_start == pcEnd)
2562 || (len > 0xFFFFFFFF) )
2563 {
2564 header_field_ptr->content_len->len = 0;
2565 }
2566 else
2567 header_field_ptr->content_len->len = (uint32_t)len;
2568 }
2569 if(!p || !hi_util_in_bounds(start, end, p))
2570 p = end;
2571
2572 return p;
2573 }
2574
IsXFFFieldName(HI_CLIENT_HDR_ARGS * hdrs_args,u_char ** pp,const u_char * end,uint8_t ** Field_Names,uint8_t * Field_Length)2575 static inline bool IsXFFFieldName( HI_CLIENT_HDR_ARGS *hdrs_args,
2576 u_char **pp, const u_char *end,
2577 uint8_t **Field_Names, uint8_t *Field_Length )
2578 {
2579 int i;
2580 int len;
2581 uint8_t *header_ptr;
2582 uint8_t *field_ptr;
2583
2584 i = 0; // index into the list of XFF field names
2585 field_ptr = NULL; // pointer into the active Field_Name entry
2586 header_ptr = *pp; // pointer into the header, will not step past 'end'
2587 len = 0; // len of the matched name entry
2588
2589 while( true )
2590 {
2591 /* If we run off the end of the active table, or table is truncated then
2592 we can stop. We didn't locate a match. */
2593 if( (i >= (HTTP_MAX_XFF_FIELDS)) || (Field_Names[i] == NULL) )
2594 break;
2595
2596 if( field_ptr == NULL ) // didn't start to match any entry
2597 {
2598 /* If the length doesn't permit a match, move on. */
2599 if( (end - *pp) < Field_Length[i] )
2600 {
2601 i += 1;
2602 continue;
2603 }
2604
2605 if( toupper(*header_ptr) == *Field_Names[i] ) // does the first char match?
2606 {
2607 /* set our working pointer to the field name */
2608 field_ptr = (Field_Names[i] + 1);
2609 header_ptr += 1;
2610 len = 1; // We matched one character
2611 continue;
2612 }
2613 i += 1;
2614 }
2615 else
2616 {
2617 /* If we are still matching and we get to the end
2618 of the field name, then we've located a name match */
2619 if( *field_ptr == 0 ) // End of the field name
2620 {
2621 *pp += len; // Step input pointer over what we found
2622 hdrs_args->new_precedence = (i+1); // Precedence started with one
2623 return( true );
2624 }
2625 else
2626 {
2627 /* check for another matching character */
2628 if( toupper(*header_ptr) == *field_ptr )
2629 {
2630 header_ptr += 1;
2631 field_ptr += 1;
2632 len += 1;
2633 }
2634 else
2635 {
2636 header_ptr = *pp; // Back to the start for the name
2637 field_ptr = NULL; // No longer a match
2638 len = 0;
2639 i += 1;
2640 }
2641 }
2642 }
2643 }
2644
2645 return( false );
2646 }
2647
extractHeaderFieldValues(HI_SESSION * Session,HTTPINSPECT_CONF * ServerConf,const u_char * p,const u_char * offset,const u_char * start,const u_char * end,HI_CLIENT_HDR_ARGS * hdrs_args,void * ssnptr)2648 static inline const u_char *extractHeaderFieldValues(HI_SESSION *Session,
2649 HTTPINSPECT_CONF *ServerConf, const u_char *p, const u_char *offset,
2650 const u_char *start, const u_char *end, HI_CLIENT_HDR_ARGS *hdrs_args, void* ssnptr)
2651 {
2652 HttpSessionData *hsd;
2653
2654 hsd = hdrs_args->sd;
2655 if (((p - offset) == 0) && (ServerConf->enable_xff != 0) &&
2656 ((hdrs_args->true_clnt_xff & XFF_HEADERS_ACTIVE) != 0) && (hsd) &&
2657 IsXFFFieldName(hdrs_args, (u_char **)&p, (const u_char *)end,
2658 ServerConf->xff_headers, ServerConf->xff_header_lengths))
2659 {
2660 p = extract_http_xff(Session, p, start, end, hdrs_args);
2661 }
2662 else if (((p - offset) == 0) && ((*p == 'C') || (*p == 'c')))
2663 {
2664 /* Search for 'Cookie' at beginning, starting from current *p */
2665 if ( ServerConf->enable_cookie &&
2666 IsHeaderFieldName(p, end, HEADER_NAME__COOKIE, HEADER_LENGTH__COOKIE))
2667 {
2668 p = extract_http_cookie((p+ HEADER_LENGTH__COOKIE), end, hdrs_args->hdr_ptr, hdrs_args->hdr_field_ptr);
2669 }
2670 else if ( IsHeaderFieldName(p, end, HEADER_NAME__CONTENT_LENGTH, HEADER_LENGTH__CONTENT_LENGTH) )
2671 {
2672 p = extract_http_content_length(Session, ServerConf, p, start,
2673 end, hdrs_args->hdr_ptr, hdrs_args->hdr_field_ptr,HI_SI_CLIENT_MODE);
2674 }
2675 else if ( IsHeaderFieldName(p, end, HEADER_NAME__CONTENT_TYPE, HEADER_LENGTH__CONTENT_TYPE) )
2676 {
2677 Session->client.request.content_type = p;
2678 }
2679 else if ( IsHeaderFieldName(p, end, HEADER_NAME__CONTENT_DISP, HEADER_LENGTH__CONTENT_DISP) )
2680 {
2681 Session->client.request.content_disp = p;
2682 }
2683 }
2684 else if (((p - offset) == 0) && ((*p == 'x') || (*p == 'X') || (*p == 't') || (*p == 'T')))
2685 {
2686 //* The default/legacy behavior with two builtin XFF field names */
2687 if ( (ServerConf->enable_xff) && hsd && ((hdrs_args->true_clnt_xff & XFF_HEADERS) == 0) )
2688 {
2689 if(IsHeaderFieldName(p, end, HEADER_NAME__XFF, HEADER_LENGTH__XFF))
2690 {
2691 hdrs_args->true_clnt_xff |= XFF_HDR;
2692 p = p + HEADER_LENGTH__XFF;
2693 p = extract_http_xff(Session, p, start, end, hdrs_args);
2694 }
2695 else if(IsHeaderFieldName(p, end, HEADER_NAME__TRUE_IP, HEADER_LENGTH__TRUE_IP))
2696 {
2697 hdrs_args->true_clnt_xff |= TRUE_CLIENT_IP_HDR;
2698 p = p + HEADER_LENGTH__TRUE_IP;
2699 p = extract_http_xff(Session, p, start, end, hdrs_args);
2700 }
2701 }
2702 else if ( IsHeaderFieldName(p, end, HEADER_NAME__TRANSFER_ENCODING,
2703 HEADER_LENGTH__TRANSFER_ENCODING) && hsd)
2704 {
2705 if (!hi_paf_disable_te(ssnptr, true))
2706 {
2707 p = p + HEADER_LENGTH__TRANSFER_ENCODING;
2708 p = extract_http_transfer_encoding(Session, hsd, p, start, end, hdrs_args->hdr_ptr, HI_SI_CLIENT_MODE);
2709 }
2710 }
2711 }
2712 #if defined(FEAT_OPEN_APPID)
2713 else if(((p - offset) == 0) && ((*p == 'U') || (*p == 'u')))
2714 {
2715 if ((ServerConf->appid_enabled))
2716 {
2717 if(IsHeaderFieldName(p, end, HEADER_NAME__USER_AGENT, HEADER_LENGTH__USER_AGENT))
2718 {
2719 p = p + HEADER_LENGTH__USER_AGENT;
2720 p = extract_http_client_header(Session, p, start, end, hdrs_args->hdr_ptr, &hdrs_args->hdr_ptr->userAgent);
2721 }
2722 }
2723 }
2724 else if(((p - offset) == 0) && ((*p == 'R') || (*p == 'r')))
2725 {
2726 if ((ServerConf->appid_enabled))
2727 {
2728 if(IsHeaderFieldName(p, end, HEADER_NAME__REFERER, HEADER_LENGTH__REFERER))
2729 {
2730 p = p + HEADER_LENGTH__REFERER;
2731 p = extract_http_client_header(Session, p, start, end, hdrs_args->hdr_ptr, &hdrs_args->hdr_ptr->referer);
2732 }
2733 }
2734 if (IsHeaderFieldName(p, end, HEADER_NAME__RANGE, HEADER_LENGTH__RANGE))
2735 {
2736 p = p + HEADER_LENGTH__RANGE;
2737 p = extract_http_range(Session, p, start, end, hdrs_args->hdr_ptr);
2738 }
2739 }
2740 else if(((p - offset) == 0) && ((*p == 'V') || (*p == 'v')))
2741 {
2742 if ((ServerConf->appid_enabled))
2743 {
2744 if(IsHeaderFieldName(p, end, HEADER_NAME__VIA, HEADER_LENGTH__VIA))
2745 {
2746 p = p + HEADER_LENGTH__VIA;
2747 p = extract_http_client_header(Session, p, start, end, hdrs_args->hdr_ptr, &hdrs_args->hdr_ptr->via);
2748 }
2749 }
2750 }
2751 #else
2752 else if (((p - offset) == 0) && ((*p == 'R') || (*p == 'r')))
2753 {
2754 if (IsHeaderFieldName(p, end, HEADER_NAME__RANGE, HEADER_LENGTH__RANGE))
2755 {
2756 p = p + HEADER_LENGTH__RANGE;
2757 p = extract_http_range(Session, p, start, end, hdrs_args->hdr_ptr);
2758 }
2759 }
2760 #endif /* defined(FEAT_OPEN_APPID) */
2761 else if(((p - offset) == 0) && ((*p == 'H') || (*p == 'h')))
2762 {
2763 if(IsHeaderFieldName(p, end, HEADER_NAME__HOSTNAME, HEADER_LENGTH__HOSTNAME))
2764 {
2765 /* Alert when there are multiple host headers in one request */
2766 if(hdrs_args->hst_name_hdr)
2767 {
2768 if(hi_eo_generate_event(Session, HI_EO_CLIENT_MULTIPLE_HOST_HDRS))
2769 {
2770 hi_eo_client_event_log(Session, HI_EO_CLIENT_MULTIPLE_HOST_HDRS, NULL, NULL);
2771 }
2772 return p;
2773 }
2774 else
2775 {
2776 hdrs_args->hst_name_hdr = 1;
2777 #if defined(FEAT_OPEN_APPID)
2778 if ( hsd && (ServerConf->log_hostname || ServerConf->appid_enabled))
2779 #else
2780 if ( hsd && !(hdrs_args->strm_ins) && (ServerConf->log_hostname))
2781 #endif /* defined(FEAT_OPEN_APPID) */
2782 {
2783 if(!SetLogBuffers(hsd, ssnptr))
2784 {
2785 p = p + HEADER_LENGTH__HOSTNAME;
2786 p = extract_http_hostname(Session, p, start, end, hdrs_args->hdr_ptr, hsd);
2787 }
2788 }
2789 }
2790 }
2791 }
2792 return p;
2793 }
2794
2795
2796 /*
2797 ** NAME
2798 ** hi_client_extract_header::
2799 */
2800 /**
2801 ** Catch multiple requests per packet, by returning pointer to after the
2802 ** end of the request header if there is another request.
2803 **
2804 ** There are 4 types of "valid" delimiters that we look for. They are:
2805 ** "\r\n\r\n"
2806 ** "\r\n\n"
2807 ** "\n\r\n"
2808 ** "\n\n"
2809 ** The only patterns that we really only need to look for are:
2810 ** "\n\r\n"
2811 ** "\n\n"
2812 ** The reason being that these two patterns are suffixes of the other
2813 ** patterns. So once we find those, we are all good.
2814 **
2815 ** @param Session pointer to the session
2816 ** @param start pointer to the start of text
2817 ** @param end pointer to the end of text
2818 **
2819 ** @return pointer
2820 **
2821 ** @retval NULL Did not find pipeline request
2822 ** @retval !NULL Found another possible request.
2823 */
hi_client_extract_header(HI_SESSION * Session,HTTPINSPECT_CONF * ServerConf,HEADER_PTR * header_ptr,const u_char * start,const u_char * end,HttpSessionData * hsd,int stream_ins,void * ssnptr)2824 static inline const u_char *hi_client_extract_header(
2825 HI_SESSION *Session, HTTPINSPECT_CONF *ServerConf,
2826 HEADER_PTR *header_ptr, const u_char *start,
2827 const u_char *end, HttpSessionData *hsd, int stream_ins, void* ssnptr)
2828 {
2829 int iRet = HI_SUCCESS;
2830 const u_char *p;
2831 const u_char *offset;
2832 const u_char *crlf;
2833 URI_PTR version_string;
2834 HEADER_FIELD_PTR header_field_ptr ;
2835 HI_CLIENT_HDR_ARGS hdrs_args;
2836 int header_count = 0;
2837 int num_spaces = 0;
2838
2839 if(!start || !end)
2840 return NULL;
2841
2842 p = start;
2843
2844 /*
2845 ** We say end - 6 because we need at least six bytes to verify that
2846 ** there is an end to the URI and still a request afterwards. To be
2847 ** exact, we should only subtract 1, but we are not interested in a
2848 ** 1 byte method, uri, etc.
2849 **
2850 ** a.k.a there needs to be data after the initial request to inspect
2851 ** to make it worth our while.
2852 */
2853 if (p > (end - 6 ))
2854 {
2855 header_ptr->header.uri = NULL;
2856 return p;
2857 }
2858 header_ptr->content_len.len = 0;
2859 header_ptr->is_chunked = false;
2860
2861 header_ptr->header.uri = start;
2862 header_ptr->header.uri_end = end;
2863 hdrs_args.hdr_ptr = header_ptr;
2864 hdrs_args.hdr_field_ptr = &header_field_ptr;
2865 hdrs_args.sd = hsd;
2866 hdrs_args.strm_ins = stream_ins;
2867 hdrs_args.hst_name_hdr = 0;
2868 hdrs_args.true_clnt_xff = (ServerConf->xff_headers[0] != NULL) ? XFF_INIT : 0;
2869 hdrs_args.top_precedence = 0;
2870
2871 SkipBlankSpace(start,end,&p);
2872
2873 /* This is to skip past the HTTP/1.0 (or 1.1) version string */
2874 if (IsHttpVersion(&p, end))
2875 {
2876 memset(&version_string, 0, sizeof(URI_PTR));
2877 version_string.uri = p;
2878
2879 while (hi_util_in_bounds(start, end, p))
2880 {
2881 if(lookup_table[*p] || ServerConf->whitespace[*p])
2882 {
2883 if(lookup_table[*p])
2884 {
2885 iRet = (lookup_table[*p])(Session, start, end, &p, &version_string);
2886 }
2887 else
2888 {
2889 iRet = NextNonWhiteSpace(Session, start, end, &p, &version_string);
2890 }
2891
2892 if(iRet == URI_END)
2893 {
2894 if (*p == '\n')
2895 {
2896 p++;
2897 if (hi_util_in_bounds(start, end, p))
2898 {
2899 version_string.uri_end = p;
2900 }
2901 else
2902 {
2903 return p;
2904 }
2905
2906
2907 if(hi_eo_generate_event(Session, Session->server_conf->max_spaces))
2908 {
2909 num_spaces = SkipBlankSpace(start,end,&p);
2910 if(num_spaces >= Session->server_conf->max_spaces)
2911 {
2912 //alert
2913 hi_eo_client_event_log(Session, HI_EO_CLIENT_EXCEEDS_SPACES, NULL, NULL);
2914 }
2915 }
2916 }
2917 break;
2918 }
2919 else if(iRet == HI_OUT_OF_BOUNDS)
2920 {
2921 return p;
2922 }
2923 }
2924 p++;
2925 }
2926 if (iRet == URI_END)
2927 {
2928 header_ptr->header.uri = version_string.uri_end + 1;
2929 offset = (u_char *)p;
2930 }
2931 else
2932 {
2933 return p;
2934 }
2935 }
2936 else
2937 {
2938 if(hi_eo_generate_event(Session, HI_EO_CLIENT_UNESCAPED_SPACE_URI))
2939 {
2940 hi_eo_client_event_log(Session, HI_EO_CLIENT_UNESCAPED_SPACE_URI,
2941 NULL, NULL);
2942 }
2943 if(p < end)
2944 {
2945 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
2946 if(crlf)
2947 {
2948 p = crlf;
2949 }
2950 else
2951 return p;
2952 }
2953 else
2954 {
2955 return p;
2956 }
2957 }
2958
2959 /******* If xff is enabled , then only we are storing original client IP data */
2960 if( ServerConf->enable_xff )
2961 {
2962 if( ScPafEnabled() )
2963 {
2964 if (hsd->http_req_id == XFF_MAX_PIPELINE_REQ )
2965 hsd->http_req_id = 0;
2966
2967 hsd->http_req_id++;
2968 hsd->is_response = 0;
2969
2970 if( hsd->tList_count != XFF_MAX_PIPELINE_REQ )
2971 hsd->tList_count++;
2972 }
2973 }
2974
2975 offset = (u_char*)p;
2976
2977 header_ptr->header.uri = p;
2978
2979 while (hi_util_in_bounds(start, end, p))
2980 {
2981 if(*p == '\n')
2982 {
2983 header_count++;
2984
2985 if(hi_eo_generate_event(Session, Session->server_conf->max_hdr_len)
2986 && ((p - offset) >= Session->server_conf->max_hdr_len))
2987 {
2988 hi_eo_client_event_log(Session, HI_EO_CLIENT_LONG_HDR, NULL, NULL);
2989 }
2990
2991 if (Session->server_conf->max_headers &&
2992 (header_count > Session->server_conf->max_headers))
2993 {
2994 hi_eo_client_event_log(Session, HI_EO_CLIENT_MAX_HEADERS, NULL, NULL);
2995 }
2996
2997 p++;
2998
2999 if(hi_eo_generate_event(Session, Session->server_conf->max_spaces))
3000 {
3001 num_spaces = SkipBlankSpace(start,end,&p);
3002 if(num_spaces >= Session->server_conf->max_spaces)
3003 {
3004 //alert
3005 hi_eo_client_event_log(Session, HI_EO_CLIENT_EXCEEDS_SPACES, NULL, NULL);
3006 }
3007 }
3008
3009 offset = (u_char*)p;
3010
3011 if (!hi_util_in_bounds(start, end, p))
3012 {
3013 header_ptr->header.uri_end = p;
3014 return p;
3015 }
3016
3017 hdrs_args.hdr_ptr = header_ptr;
3018 hdrs_args.hdr_field_ptr = &header_field_ptr;
3019
3020 /* As performance ugly as this may be, need to bounds check p in each of the
3021 * if blocks below to prevent read beyond end of buffer */
3022 if (*p < 0x0E)
3023 {
3024 if(*p == '\r')
3025 {
3026 p++;
3027
3028 if (!hi_util_in_bounds(start, end, p))
3029 {
3030 header_ptr->header.uri_end = p;
3031 return p;
3032 }
3033 else if(*p == '\n')
3034 {
3035 p++;
3036 header_ptr->header.uri_end = p;
3037 return p;
3038 }
3039 }
3040 else if(*p == '\n')
3041 {
3042 p++;
3043 header_ptr->header.uri_end = p;
3044 return p;
3045 }
3046 }
3047 else if ( (p = extractHeaderFieldValues(Session, ServerConf, p, offset, start, end, &hdrs_args, ssnptr)) == end)
3048 {
3049 return end;
3050 }
3051
3052 }
3053 else if( (p == header_ptr->header.uri) &&
3054 (p = extractHeaderFieldValues(Session, ServerConf, p, offset, start, end, &hdrs_args, ssnptr)) == end)
3055 {
3056 return end;
3057 }
3058 if ( *p == '\n') continue;
3059 p++;
3060 }
3061
3062 /* Never observed an end-of-field. Maybe it's not there, but the header is long anyway: */
3063 if(hi_eo_generate_event(Session, Session->server_conf->max_hdr_len)
3064 && ((p - start) >= Session->server_conf->max_hdr_len))
3065 {
3066 hi_eo_client_event_log(Session, HI_EO_CLIENT_LONG_HDR, NULL, NULL);
3067 }
3068
3069 header_ptr->header.uri_end = p;
3070 return p;
3071 }
3072 #define CLR_POST(Client) \
3073 do { \
3074 Client->request.post_raw = NULL;\
3075 Client->request.post_raw_size = 0;\
3076 Client->request.post_norm = NULL; \
3077 } while(0);
3078
3079 #define CLR_HEADER(Client) \
3080 do { \
3081 Client->request.header_raw = NULL;\
3082 Client->request.header_raw_size = 0;\
3083 Client->request.header_norm = NULL; \
3084 Client->request.header_norm_size = 0 ;\
3085 Client->request.cookie.cookie = NULL;\
3086 Client->request.cookie.cookie_end = NULL;\
3087 if(Client->request.cookie.next) { \
3088 COOKIE_PTR *cookie = Client->request.cookie.next; \
3089 do { \
3090 Client->request.cookie.next = Client->request.cookie.next->next; \
3091 SnortPreprocFree(cookie, sizeof(COOKIE_PTR), PP_HTTPINSPECT, \
3092 PP_MEM_CATEGORY_SESSION); \
3093 cookie = Client->request.cookie.next; \
3094 } while(cookie); \
3095 }\
3096 Client->request.cookie.next = NULL;\
3097 Client->request.cookie_norm = NULL;\
3098 Client->request.cookie_norm_size = 0;\
3099 } while(0);
3100
3101 #define CLR_METHOD(Client) \
3102 do { \
3103 Client->request.method_raw = NULL;\
3104 Client->request.method_size = 0; \
3105 Client->request.method = 0 ;\
3106 } while(0);
3107
3108 /*
3109 ** NAME
3110 ** StatelessInspection::
3111 */
3112 /**
3113 ** Find the URI and determine whether the URI needs to be normalized.
3114 **
3115 ** This is a big step in stateless inspection, because we need to reliably
3116 ** find the URI and when possible filter out non-URIs. We do this using a
3117 ** simple state machine that is based on characters found in the data
3118 ** buffer.
3119 **
3120 ** Another important aspect of the stateless inspection is the ability to
3121 ** track and inspect pipelined requests. It is VERY IMPORTANT to reset the
3122 ** pipeline_req pointer, since we don't memset the whole structure. This
3123 ** pointer is reset in the hi_si_session_inspection() function. Check there
3124 ** for more details.
3125 **
3126 ** Normalization is detected when we are looking at the packet for the URI.
3127 ** We look for the following issues:
3128 ** - ////
3129 ** - /../
3130 ** - /./
3131 ** - non-ascii charss
3132 ** - %
3133 ** - \
3134 ** When these things are seen we point to the first occurence in the URI, or
3135 ** where we have to start normalizing. If the URI is updated to a new
3136 ** pointer, then the normalization pointer is reset and we start over.
3137 ** Using this method should cut down the memcpy()s per URI, since most
3138 ** URIs are not normalized.
3139 **
3140 ** If this function returns HI_NONFATAL_ERR, we return out of mode_inspection
3141 ** with an error and abort HttpInspect processing, and continue on with
3142 ** any other processing we do. The Session parameters that we use here are
3143 ** reset in the next time that we do session_inspection, so we don't do
3144 ** any initialization here.
3145 **
3146 ** @param Session pointer to the HTTP session
3147 ** @param data pointer to the start of the packet payload
3148 ** @param dsize size of the payload
3149 **
3150 ** @return integer
3151 **
3152 ** @retval HI_INVALID_ARG invalid argument
3153 ** @retval HI_NONFATAL_ERR no URI detected
3154 ** @retval HI_SUCCESS URI detected and Session pointers updated
3155 */
3156
StatelessInspection(Packet * p,HI_SESSION * Session,HttpSessionData * hsd,int stream_ins)3157 int StatelessInspection(Packet *p, HI_SESSION *Session, HttpSessionData *hsd, int stream_ins)
3158 {
3159 HTTPINSPECT_CONF *ServerConf;
3160 HTTPINSPECT_CONF *ClientConf;
3161 HI_CLIENT *Client;
3162 URI_PTR method_ptr;
3163 URI_PTR uri_ptr;
3164 URI_PTR post_ptr;
3165 HEADER_PTR header_ptr;
3166 HTTP_CMD_CONF *CmdConf = NULL;
3167 const u_char *start;
3168 const u_char *end;
3169 const u_char *ptr, *mthd;
3170 const u_char *method_end = NULL;
3171 int method_len;
3172 int iRet=0;
3173 char sans_uri = 0;
3174 const unsigned char *data = p->data;
3175 int dsize = p->dsize;
3176 bool http_post_hdr_flush = false;
3177
3178 if(stream_api->get_preproc_flags(p->ssnptr) & PP_HTTPINSPECT_PAF_FLUSH_POST_HDR)
3179 http_post_hdr_flush = true;
3180
3181 if ( ScPafEnabled() )
3182 {
3183 if ( stream_ins && (p->packet_flags & PKT_STREAM_INSERT) )
3184 return HI_INVALID_ARG;
3185 }
3186
3187 ServerConf = Session->server_conf;
3188 if(!ServerConf)
3189 {
3190 return HI_INVALID_ARG;
3191 }
3192
3193 ClientConf = Session->client_conf;
3194 if(!ClientConf)
3195 {
3196 return HI_INVALID_ARG;
3197 }
3198
3199 Client = &Session->client;
3200
3201 memset(&uri_ptr, 0x00, sizeof(URI_PTR));
3202 memset(&post_ptr, 0x00, sizeof(URI_PTR));
3203 memset(&header_ptr, 0x00, sizeof(HEADER_PTR));
3204 memset(&method_ptr, 0x00, sizeof(URI_PTR));
3205
3206 /*
3207 ** We set the starting boundary depending on whether this request is
3208 ** a normal request or a pipeline request. The end boundary is always
3209 ** the same whether it is a pipeline request or other.
3210 */
3211 if(Client->request.pipeline_req)
3212 {
3213 start = Client->request.pipeline_req;
3214 p->packet_flags |= PKT_ALLOW_MULTIPLE_DETECT;
3215 }
3216 else
3217 {
3218 start = data;
3219 }
3220
3221 Client->request.pipeline_req = NULL;
3222
3223 end = data + dsize;
3224 ptr = start;
3225
3226 /*
3227 ** Apache and IIS strike again . . . Thanks Kanatoko
3228 ** - Ignore CRLFs at the beginning of the request.
3229 */
3230 while(hi_util_in_bounds(start, end, ptr))
3231 {
3232 if(*ptr < 0x21)
3233 {
3234 if(*ptr < 0x0E && *ptr > 0x08)
3235 {
3236 ptr++;
3237 continue;
3238 }
3239 else
3240 {
3241 if(*ptr == 0x20)
3242 {
3243 ptr++;
3244 continue;
3245 }
3246 }
3247 }
3248
3249 break;
3250 }
3251
3252 mthd = method_ptr.uri = ptr;
3253
3254 while(hi_util_in_bounds(start, end, mthd))
3255 {
3256 if (ServerConf->whitespace[*mthd] || (lookup_table[*mthd] == NextNonWhiteSpace))
3257 {
3258 method_end = mthd++;
3259 break;
3260 }
3261 if ( !ScPafEnabled() )
3262 {
3263 /* isascii returns non-zero if it is ascii */
3264 if (isascii((int)*mthd) == 0)
3265 {
3266 /* Possible post data or something else strange... */
3267 method_end = mthd++;
3268 break;
3269 }
3270 }
3271 mthd++;
3272 }
3273 if (method_end)
3274 {
3275 method_ptr.uri_end = method_end;
3276 }
3277 else
3278 {
3279 method_ptr.uri_end = end;
3280 }
3281 method_len = method_ptr.uri_end - method_ptr.uri;
3282
3283 /* Need slightly special handling for POST requests
3284 * Since we don't normalize on the request method itself,
3285 * just do a strcmp here and skip the characters below. */
3286 if(method_len == 4 && !strncasecmp("POST", (const char *)method_ptr.uri, 4))
3287 {
3288 if(!http_post_hdr_flush)
3289 hi_stats.post++;
3290 Client->request.method = HI_POST_METHOD;
3291 }
3292 else if(method_len == 3 && !strncasecmp("GET", (const char *)method_ptr.uri, 3))
3293 {
3294 hi_stats.get++;
3295 Client->request.method = HI_GET_METHOD;
3296 }
3297 else if(method_len > 0 && method_len <= MAX_METHOD_LEN )
3298 {
3299 CmdConf = http_cmd_lookup_find(ServerConf->cmd_lookup, (const char *)method_ptr.uri,
3300 method_len, &iRet);
3301
3302 if(iRet == -1 || (CmdConf == NULL))
3303 {
3304 if(!stream_ins && hi_eo_generate_event(Session, HI_EO_CLIENT_UNKNOWN_METHOD))
3305 {
3306 hi_eo_client_event_log(Session, HI_EO_CLIENT_UNKNOWN_METHOD, NULL, NULL);
3307 }
3308
3309 Client->request.method = HI_UNKNOWN_METHOD;
3310 }
3311 }
3312 else
3313 {
3314 if( ScPafEnabled() )
3315 {
3316 /* Might have gotten non-ascii characters, hence no method, but if
3317 * PAF is in use, checking "!stream_ins" equates to PacketHasStartOfPDU()
3318 * so we know we're looking for a method and not guessing that we're in
3319 * the body or somewhere else because we found a non-ascii character */
3320 if (!stream_ins && hi_eo_generate_event(Session, HI_EO_CLIENT_UNKNOWN_METHOD))
3321 hi_eo_client_event_log(Session, HI_EO_CLIENT_UNKNOWN_METHOD, NULL, NULL);
3322 Client->request.method = HI_UNKNOWN_METHOD;
3323 }
3324 else
3325 {
3326 if (!stream_ins && hi_eo_generate_event(Session, HI_EO_CLIENT_UNKNOWN_METHOD))
3327 hi_eo_client_event_log(Session, HI_EO_CLIENT_UNKNOWN_METHOD, NULL, NULL);
3328 sans_uri = 1;
3329 Client->request.method = HI_UNKNOWN_METHOD;
3330 }
3331 }
3332 /* If the Http method is set to UNKNOWN and its not a valid http packet
3333 *(i.e. packet has junk characters ) then we return without populating
3334 * the URI buffers for inspection */
3335
3336 if ((Client->request.method == HI_UNKNOWN_METHOD ) && ScPafEnabled() && !(hi_paf_valid_http(p->ssnptr)))
3337 return HI_INVALID_ARG;
3338
3339 if (!sans_uri )
3340 {
3341 uri_ptr.uri = method_ptr.uri_end;
3342 uri_ptr.uri_end = end;
3343
3344 /* This will set up the URI pointers - effectively extracting
3345 * the URI. */
3346 iRet = hi_client_extract_uri(
3347 Session, ServerConf, Client, start, end, uri_ptr.uri, &uri_ptr, hsd, stream_ins, p->ssnptr);
3348 }
3349
3350 /* Check if the URI exceeds the max header field length */
3351 /* Only check if we succesfully observed a GET or POST method, otherwise,
3352 * this may very well be a POST body */
3353 if(iRet == URI_END &&
3354 hi_eo_generate_event(Session, ServerConf->max_hdr_len) &&
3355 ((uri_ptr.uri_end - uri_ptr.uri) >= ServerConf->max_hdr_len))
3356 {
3357 hi_eo_client_event_log(Session, HI_EO_CLIENT_LONG_HDR, NULL, NULL);
3358 }
3359
3360 #if defined(FEAT_OPEN_APPID)
3361 if(iRet == URI_END &&
3362 (!(ServerConf->uri_only) || ServerConf->appid_enabled))
3363 #else
3364 if(iRet == URI_END &&
3365 !(ServerConf->uri_only))
3366 #endif /* defined(FEAT_OPEN_APPID) */
3367 {
3368 Client->request.method_raw = method_ptr.uri;
3369 Client->request.method_size = method_ptr.uri_end - method_ptr.uri;
3370 ///XXX
3371 ///Copy out the header into its own buffer...,
3372 /// set ptr to end of header.
3373 //
3374 // uri_ptr.end points to end of URI & HTTP version identifier.
3375 if (hi_util_in_bounds(start, end, uri_ptr.uri_end + 1))
3376 {
3377 header_ptr.range_flag = HTTP_RANGE_NONE;
3378 ptr = hi_client_extract_header(Session, ServerConf, &header_ptr, uri_ptr.uri_end+1, end, hsd, stream_ins, p->ssnptr);
3379 if (header_ptr.range_flag != HTTP_RANGE_NONE)
3380 {
3381 Client->request.range_flag = header_ptr.range_flag;
3382 }
3383 else
3384 {
3385 Client->request.range_flag = HTTP_RANGE_NONE;
3386 }
3387 }
3388
3389 if (header_ptr.header.uri)
3390 {
3391 Client->request.header_raw = header_ptr.header.uri;
3392 Client->request.header_raw_size = header_ptr.header.uri_end - header_ptr.header.uri;
3393 if(!Client->request.header_raw_size)
3394 {
3395 CLR_HEADER(Client);
3396 }
3397 else
3398 {
3399 if(!http_post_hdr_flush)
3400 hi_stats.req_headers++;
3401 Client->request.header_norm = header_ptr.header.uri;
3402 if (header_ptr.cookie.cookie)
3403 {
3404 if(!http_post_hdr_flush)
3405 hi_stats.req_cookies++;
3406 Client->request.cookie.cookie = header_ptr.cookie.cookie;
3407 Client->request.cookie.cookie_end = header_ptr.cookie.cookie_end;
3408 Client->request.cookie.next = header_ptr.cookie.next;
3409 Client->request.cookie_norm = header_ptr.cookie.cookie;
3410 }
3411 else
3412 {
3413 Client->request.cookie.cookie = NULL;
3414 Client->request.cookie.cookie_end = NULL;
3415 Client->request.cookie.next = NULL;
3416 Client->request.cookie_norm = NULL;
3417 }
3418 }
3419 }
3420 else
3421 {
3422 CLR_HEADER(Client);
3423 }
3424
3425 /* Got a Content-Length or it's a POST request which may be chunked */
3426 if (header_ptr.content_len.cont_len_start || header_ptr.is_chunked)
3427 {
3428 /* Need to skip over header and get to the body.
3429 * The unaptly named FindPipelineReq will do that. */
3430 ptr = FindPipelineReq(Session, uri_ptr.delimiter, end);
3431 //ptr = FindPipelineReq(Session, ptr, end);
3432 if(ptr)
3433 {
3434 post_ptr.uri = ptr;
3435 post_ptr.uri_end = end;
3436 if((POST_END == hi_client_extract_post(
3437 Session, ServerConf, ptr, end, &post_ptr,
3438 header_ptr.content_len.len, header_ptr.is_chunked, hsd )))
3439 {
3440 if(!http_post_hdr_flush)
3441 hi_stats.post_params++;
3442 Client->request.post_raw = post_ptr.uri;
3443 Client->request.post_raw_size = post_ptr.uri_end - post_ptr.uri;
3444 Client->request.post_norm = post_ptr.norm;
3445 ptr = post_ptr.uri_end;
3446 }
3447 else
3448 {
3449 CLR_POST(Client);
3450 }
3451
3452 if ( ptr < end )
3453 Client->request.pipeline_req = ptr;
3454
3455 if(Client->request.post_raw && (ServerConf->post_extract_size > -1))
3456 {
3457 if(ServerConf->post_extract_size && ((int)Client->request.post_raw_size > ServerConf->post_extract_size))
3458 {
3459 Client->request.post_raw_size = (unsigned int)ServerConf->post_extract_size;
3460 }
3461 }
3462 else
3463 {
3464 CLR_POST(Client);
3465 }
3466 }
3467 else
3468 {
3469 CLR_POST(Client);
3470 ptr = uri_ptr.delimiter;
3471 }
3472 }
3473 else
3474 {
3475 ptr = uri_ptr.delimiter;
3476 }
3477 }
3478 else
3479 {
3480 CLR_HEADER(Client);
3481 CLR_POST(Client);
3482 if (!(Client->request.method & HI_UNKNOWN_METHOD) && method_ptr.uri)
3483 {
3484 Client->request.method_raw = method_ptr.uri;
3485 Client->request.method_size = method_ptr.uri_end - method_ptr.uri;
3486 }
3487 else
3488 {
3489 CLR_METHOD(Client);
3490 return HI_NONFATAL_ERR;
3491 }
3492 ptr = uri_ptr.delimiter;
3493 }
3494 #if defined(FEAT_OPEN_APPID)
3495 //copy over extracted headers for appId
3496 if ((ServerConf->appid_enabled))
3497 {
3498 HttpParsedHeaders headers;
3499 memset(&headers, 0, sizeof(headers));
3500 if (hsd->log_state)
3501 {
3502 if (hsd->log_state->hostname_extracted)
3503 {
3504 headers.host.start = hsd->log_state->hostname_extracted;
3505 headers.host.len = hsd->log_state->hostname_bytes;
3506 }
3507 if (hsd->log_state->uri_extracted)
3508 {
3509 headers.url.start = hsd->log_state->uri_extracted;
3510 headers.url.len = hsd->log_state->uri_bytes;
3511 }
3512 }
3513 if (Client->request.method_raw)
3514 {
3515 headers.method.start = Client->request.method_raw;
3516 headers.method.len = Client->request.method_size;
3517 }
3518
3519 headers.userAgent = header_ptr.userAgent;
3520 headers.referer = header_ptr.referer;
3521 headers.via = header_ptr.via;
3522
3523 /*callback into appId with header values extracted. */
3524 CallHttpHeaderProcessors(p, &headers);
3525
3526 SnortPreprocFree((void *)headers.userAgent.start,
3527 (headers.userAgent.len) * sizeof(char), PP_HTTPINSPECT, PP_MEM_CATEGORY_SESSION);
3528 SnortPreprocFree((void *)headers.referer.start,
3529 (headers.referer.len) * sizeof(char), PP_HTTPINSPECT, PP_MEM_CATEGORY_SESSION);
3530 SnortPreprocFree((void *)headers.via.start,
3531 (headers.via.len) * sizeof(char), PP_HTTPINSPECT, PP_MEM_CATEGORY_SESSION);
3532 }
3533
3534 #endif /* defined(FEAT_OPEN_APPID) */
3535
3536 /*
3537 ** Find the next pipeline request, if one is there. If we don't find
3538 ** a pipeline request, then we return NULL here, so this is always
3539 ** set to the correct value.
3540 */
3541 if(!ServerConf->no_pipeline)
3542 {
3543 if(post_ptr.uri)
3544 {
3545 Client->request.pipeline_req =
3546 FindPipelineReq(Session, post_ptr.delimiter, end);
3547 }
3548 else if(!Client->request.pipeline_req && uri_ptr.uri)
3549 {
3550 Client->request.pipeline_req =
3551 FindPipelineReq(Session, ptr, end);
3552 }
3553 }
3554 else
3555 {
3556 Client->request.pipeline_req = NULL;
3557 }
3558
3559 /*
3560 ** We set the HI_CLIENT variables from the URI_PTR structure. We also
3561 ** do error checking for the values in this routine as well.
3562 */
3563 iRet = SetClientVars(Client, &uri_ptr, dsize);
3564 if (iRet)
3565 {
3566 CLR_HEADER(Client);
3567 CLR_POST(Client);
3568 CLR_METHOD(Client);
3569 return iRet;
3570 }
3571 /*
3572 ** One last check for an oversize directory. This gets the long
3573 ** directory when there is a beginning slash and no other slashes
3574 ** until the end of the packet.
3575 **
3576 ** We do this check after we set the variables, just in case there
3577 ** was some errors while setting the variables. This could save some
3578 ** false positives on a bad URI setting.
3579 */
3580 if(uri_ptr.uri_end)
3581 CheckLongDir(Session, &uri_ptr, uri_ptr.uri_end);
3582
3583 /*
3584 ** Check for absolute URI and alert for proxy comm if necessary
3585 **
3586 ** NOTE:
3587 ** Also check ClientConf for proxy configuration so we don't
3588 ** alert on outbound requests from legitimate proxies.
3589 */
3590 if(uri_ptr.proxy && Session->global_conf->proxy_alert &&
3591 (!ServerConf->allow_proxy && !ClientConf->allow_proxy))
3592 {
3593 if(hi_eo_generate_event(Session, HI_EO_CLIENT_PROXY_USE))
3594 {
3595 hi_eo_client_event_log(Session, HI_EO_CLIENT_PROXY_USE,
3596 NULL, NULL);
3597 }
3598 }
3599
3600 return HI_SUCCESS;
3601 }
3602
hi_client_inspection(Packet * p,void * S,HttpSessionData * hsd,int stream_ins)3603 int hi_client_inspection(Packet *p, void *S, HttpSessionData *hsd, int stream_ins)
3604 {
3605 HI_SESSION *Session;
3606 int iRet;
3607
3608 if(!S || !(p->data) || (p->dsize < 1))
3609 return HI_INVALID_ARG;
3610
3611 Session = (HI_SESSION *)S;
3612
3613 if(!Session->global_conf)
3614 return HI_INVALID_ARG;
3615
3616 iRet = StatelessInspection(p, Session, hsd, stream_ins);
3617 if (iRet)
3618 return iRet;
3619
3620 return HI_SUCCESS;
3621 }
3622
3623 /*
3624 ** NAME
3625 ** hi_client_init::
3626 */
3627 /**
3628 ** Initializes arrays and search algorithms depending on the type of
3629 ** inspection that we are doing.
3630 **
3631 ** @param GlobalConf pointer to the global configuration
3632 **
3633 ** @return integer
3634 **
3635 ** @retval HI_SUCCESS function successful.
3636 */
hi_client_init(HTTPINSPECT_GLOBAL_CONF * GlobalConf)3637 int hi_client_init(HTTPINSPECT_GLOBAL_CONF *GlobalConf)
3638 {
3639 int iCtr;
3640
3641 memset(lookup_table, 0x00, sizeof(lookup_table));
3642
3643 /*
3644 ** Set up the non-ASCII register for processing.
3645 */
3646 for(iCtr = 0x80; iCtr <= 0xff; iCtr++)
3647 lookup_table[iCtr] = SetBinaryNorm;
3648 lookup_table[0x00] = SetBinaryNorm;
3649
3650 lookup_table[' '] = NextNonWhiteSpace;
3651 lookup_table['\r'] = find_rfc_delimiter;
3652 lookup_table['\n'] = find_non_rfc_delimiter;
3653
3654 /*
3655 ** ASCII encoding
3656 */
3657 lookup_table['%'] = SetPercentNorm;
3658
3659 /*
3660 ** Looking for multiple slashes
3661 */
3662 lookup_table['/'] = SetSlashNorm;
3663
3664 /*
3665 ** Looking for backslashs
3666 */
3667 lookup_table['\\'] = SetBackSlashNorm;
3668
3669 lookup_table['+'] = SetPlusNorm;
3670
3671 /*
3672 ** Look up parameter field, so we don't alert on long directory
3673 ** strings, when the next slash in the parameter field.
3674 */
3675 lookup_table['?'] = SetParamField;
3676
3677 /*
3678 ** Look for absolute URI and proxy communication.
3679 */
3680 lookup_table[':'] = SetProxy;
3681
3682 return HI_SUCCESS;
3683 }
3684
3685
3686
3687 /**
3688 ** This was just an initial testing program for these functions.
3689 */
3690 #ifdef TEST_ME
3691
3692 #include <sys/socket.h>
3693 #include <netinet/in.h>
3694 #include <arpa/inet.h>
3695
main(int argc,char ** argv)3696 int main(int argc, char **argv)
3697 {
3698 HTTPINSPECT_GLOBAL_CONF GlobalConf;
3699 HI_SESSION *Session;
3700 HI_SI_INPUT SiInput;
3701 int iInspectMode = 0;
3702 int iRet;
3703 char data[] = "Hdslkfjaslfkj HTTP/00000.111111";
3704
3705 if((iRet = hi_ui_config_init_global_conf(&GlobalConf)))
3706 {
3707 printf("** error during global init.\n");
3708 return iRet;
3709 }
3710
3711 if((iRet = hi_ui_config_default(&GlobalConf)))
3712 {
3713 printf("** error config default.\n");
3714 return iRet;
3715 }
3716
3717 hi_ui_config_print_config(&GlobalConf);
3718
3719 if((iRet = hi_client_init(&GlobalConf)))
3720 {
3721 printf("** error client init\n");
3722 return iRet;
3723 }
3724
3725 SiInput.sip = inet_addr("1.1.1.1");
3726 SiInput.sip = inet_addr("1.1.1.2");
3727 SiInput.dport = 80;
3728 SiInput.sport = 7880;
3729
3730 if((iRet = hi_si_session_inspection(&GlobalConf, &Session, &SiInput,
3731 &iInspectMode)))
3732 {
3733 printf("** error session inspection\n");
3734 return iRet;
3735 }
3736
3737 printf("** iInspectMode = %d\n", iInspectMode);
3738 if((iRet = hi_mi_mode_inspection(Session, iInspectMode, data,
3739 strlen(data))))
3740 {
3741 printf("** error mode_inspection\n");
3742 return iRet;
3743 }
3744
3745 return 0;
3746 }
3747 #endif
3748
3749
3750