"Fossies" - the Fresh Open Source Software Archive 
Member "snort-2.9.17/src/preprocessors/HttpInspect/server/hi_server.c" (16 Oct 2020, 92555 Bytes) of package /linux/misc/snort-2.9.17.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "hi_server.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
2.9.16.1_vs_2.9.17.
1 /****************************************************************************
2 *
3 * Copyright (C) 2014-2020 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_server.c
25 **
26 ** @author Daniel Roelker <droelker@sourcefire.com>
27 **
28 ** @brief Handles inspection of HTTP server responses.
29 **
30 ** HttpInspect handles server responses in a stateless manner because we
31 ** are really only interested in the first response packet that contains
32 ** the HTTP response code, headers, and the payload.
33 **
34 ** The first big thing is to incorporate the HTTP protocol flow
35 ** analyzer.
36 **
37 ** NOTES:
38 ** - Initial development. DJR
39 */
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #include <stdio.h>
45 #include <string.h>
46 #include <zlib.h>
47 #include "mempool.h"
48 #include "hi_paf.h"
49 extern MemPool *hi_gzip_mempool;
50 extern uint8_t decompression_buffer[];
51
52 extern uint8_t dechunk_buffer[];
53 static bool simple_response = false;
54
55 #include "hi_server.h"
56 #include "hi_ui_config.h"
57 #include "hi_return_codes.h"
58 #include "hi_si.h"
59 #include "hi_eo_log.h"
60 #include "snort_bounds.h"
61 #include "detection_util.h"
62 #include "stream_api.h"
63 #include "sfutil/util_unfold.h"
64
65 #if defined(FEAT_OPEN_APPID)
66 #include "spp_stream6.h"
67 #endif /* defined(FEAT_OPEN_APPID) */
68
69 #define STAT_END 100
70 #define HTTPRESP_HEADER_NAME__COOKIE "Set-Cookie"
71 #define HTTPRESP_HEADER_LENGTH__COOKIE 10
72 #define HTTPRESP_HEADER_NAME__CONTENT_ENCODING "Content-Encoding"
73 #define HTTPRESP_HEADER_LENGTH__CONTENT_ENCODING 16
74 #define HTTPRESP_HEADER_NAME__GZIP "gzip"
75 #define HTTPRESP_HEADER_NAME__XGZIP "x-gzip"
76 #define HTTPRESP_HEADER_LENGTH__GZIP 4
77 #define HTTPRESP_HEADER_LENGTH__XGZIP 6
78 #define HTTPRESP_HEADER_NAME__DEFLATE "deflate"
79 #define HTTPRESP_HEADER_LENGTH__DEFLATE 7
80 #define HTTPRESP_HEADER_NAME__CONTENT_LENGTH "Content-length"
81 #define HTTPRESP_HEADER_LENGTH__CONTENT_LENGTH 14
82 #define HTTPRESP_HEADER_NAME__CONTENT_TYPE "Content-Type"
83 #define HTTPRESP_HEADER_LENGTH__CONTENT_TYPE 12
84 #define HTTPRESP_HEADER_NAME__TRANSFER_ENCODING "Transfer-Encoding"
85 #define HTTPRESP_HEADER_LENGTH__TRANSFER_ENCODING 17
86 #define HTTPRESP_HEADER_NAME__CONTENT_RANGE "Content-Range"
87 #define HTTPRESP_HEADER_LENGTH__CONTENT_RANGE 13
88 #define HTTPRESP_HEADER_NAME__ACCEPT_RANGES "Accept-Ranges"
89 #define HTTPRESP_HEADER_LENGTH__ACCEPT_RANGES 13
90 #if defined(FEAT_OPEN_APPID)
91 #define HEADER_NAME__VIA "Via"
92 #define HEADER_LENGTH__VIA sizeof(HEADER_NAME__VIA)-1
93 #define HEADER_NAME__SERVER "Server"
94 #define HEADER_LENGTH__SERVER sizeof(HEADER_NAME__SERVER)-1
95 #define HEADER_NAME__X_WORKING_WITH "X-Working-With"
96 #define HEADER_LENGTH__X_WORKING_WITH sizeof(HEADER_NAME__X_WORKING_WITH)-1
97 #endif /* defined(FEAT_OPEN_APPID) */
98
99 extern fd_config_t hi_fd_conf;
100
101 typedef int (*LOOKUP_FCN)(HI_SESSION *, const u_char *, const u_char *, const u_char **,
102 URI_PTR *);
103 extern LOOKUP_FCN lookup_table[256];
104 extern int NextNonWhiteSpace(HI_SESSION *, const u_char *, const u_char *, const u_char **, URI_PTR *);
105 extern int CheckChunkEncoding(HI_SESSION *, const u_char *, const u_char *, const u_char **, u_char *,
106 uint32_t , uint32_t, uint32_t *, uint32_t *, HttpSessionData *, int);
107 extern int IsHttpVersion(const u_char **, const u_char *);
108 extern int find_rfc_delimiter(HI_SESSION *, const u_char *, const u_char *, const u_char **, URI_PTR *);
109 extern int find_non_rfc_delimiter(HI_SESSION *, const u_char *, const u_char *, const u_char **, URI_PTR *);
110 extern int NextNonWhiteSpace(HI_SESSION *, const u_char *, const u_char *, const u_char **, URI_PTR *);
111 extern int SetPercentNorm(HI_SESSION *, const u_char *, const u_char *, const u_char **, URI_PTR *);
112 extern int SetSlashNorm(HI_SESSION *, const u_char *, const u_char *, const u_char **, URI_PTR *);
113 extern int SetBackSlashNorm(HI_SESSION *, const u_char *, const u_char *, const u_char **, URI_PTR *);
114 extern int SetPlusNorm(HI_SESSION *, const u_char *, const u_char *, const u_char **, URI_PTR *);
115 extern int SetBinaryNorm(HI_SESSION *, const u_char *, const u_char *, const u_char **, URI_PTR *);
116 extern int SetParamField(HI_SESSION *, const u_char *, const u_char *, const u_char **, URI_PTR *);
117 extern int SetProxy(HI_SESSION *, const u_char *, const u_char *, const u_char **, URI_PTR *);
118 extern const u_char *extract_http_cookie(const u_char *p, const u_char *end, HEADER_PTR *, HEADER_FIELD_PTR *);
119 extern const u_char *extract_http_content_length(HI_SESSION *, HTTPINSPECT_CONF *, const u_char *, const u_char *, const u_char *, HEADER_PTR *, HEADER_FIELD_PTR *, int) ;
120
121 #define CLR_SERVER_HEADER(Server) \
122 do { \
123 Server->response.header_raw = NULL;\
124 Server->response.header_raw_size = 0;\
125 Server->response.header_norm = NULL; \
126 Server->response.header_norm_size = 0 ;\
127 Server->response.cookie.cookie = NULL;\
128 Server->response.cookie.cookie_end = NULL;\
129 if(Server->response.cookie.next) {\
130 COOKIE_PTR *cookie = Server->response.cookie.next; \
131 do { \
132 Server->response.cookie.next = Server->response.cookie.next->next; \
133 free(cookie); \
134 cookie = Server->response.cookie.next; \
135 }while(cookie);\
136 }\
137 Server->response.cookie.next = NULL;\
138 Server->response.cookie_norm = NULL;\
139 Server->response.cookie_norm_size = 0;\
140 } while(0);
141
142 #define CLR_SERVER_STAT(Server) \
143 do { \
144 Server->response.status_msg = NULL;\
145 Server->response.status_code = NULL;\
146 Server->response.status_code_size = 0;\
147 Server->response.status_msg_size = 0;\
148 }while(0);
149
150 #define CLR_SERVER_STAT_MSG(Server) \
151 do { \
152 Server->response.status_msg = NULL;\
153 Server->response.status_msg_size = 0;\
154 }while(0);
155
156 #define CLR_SERVER_BODY(Server)\
157 do { \
158 Server->response.body = NULL;\
159 Server->response.body_size = 0;\
160 Server->response.body_raw = NULL;\
161 Server->response.body_raw_size = 0;\
162 }while(0);
163
164 static inline void clearHttpRespBuffer(HI_SERVER *Server)
165 {
166 CLR_SERVER_HEADER(Server);
167 CLR_SERVER_STAT(Server);
168 CLR_SERVER_BODY(Server);
169 }
170
171 static inline const u_char *MovePastDelims(const u_char *start, const u_char *end,const u_char *ptr)
172 {
173
174 while(hi_util_in_bounds(start, end, ptr))
175 {
176 if(*ptr < 0x21)
177 {
178 if(*ptr < 0x0E && *ptr > 0x08)
179 {
180 ptr++;
181 continue;
182 }
183 else
184 {
185 if(*ptr == 0x20)
186 {
187 ptr++;
188 continue;
189 }
190 }
191 }
192
193 break;
194 }
195
196 return ptr;
197 }
198
199 void CheckSkipAlertMultipleColon(HI_SESSION *Session, const u_char *start, const u_char *end, const u_char **ptr, int iInspectMode)
200 {
201 if (hi_util_in_bounds(start, end, *ptr) && **ptr == ':')
202 {
203 if (iInspectMode == HI_SI_SERVER_MODE && hi_eo_generate_event(Session, HI_EO_SERVER_MULTIPLE_COLON_BETN_KEY_VALUE))
204 {
205 hi_eo_server_event_log(Session, HI_EO_SERVER_MULTIPLE_COLON_BETN_KEY_VALUE, NULL, NULL);
206 }
207 else if (iInspectMode == HI_SI_CLIENT_MODE && hi_eo_generate_event(Session, HI_EO_CLIENT_MULTIPLE_COLON_BETN_KEY_VALUE))
208 {
209 hi_eo_client_event_log(Session, HI_EO_CLIENT_MULTIPLE_COLON_BETN_KEY_VALUE, NULL, NULL);
210 }
211 while (hi_util_in_bounds(start, end, *ptr) && **ptr == ':') (*ptr)++;
212 }
213 }
214
215 /**
216 ** CheckSkipAlertCharsBeforeColon:
217 **
218 ** This function skips any invalid characters between
219 ** http response header and ':'.
220 **/
221 static inline void CheckSkipAlertCharsBeforeColon(HI_SESSION *Session, const u_char *start,
222 const u_char *end, const u_char **ptr)
223 {
224 bool invalid_char = false;
225
226 while ( (hi_util_in_bounds(start, end, *ptr)) && (**ptr != ':')) {
227 switch(**ptr) {
228 case ' ':
229 case '\t':
230 (*ptr)++;
231 break;
232
233 case '\0':
234 case '\r':
235 case '\n':
236 case '\v':
237 case '\f':
238 invalid_char = true;
239 (*ptr)++;
240 break;
241
242 default:
243 return;
244 }
245
246 if (invalid_char &&
247 hi_eo_generate_event(Session, HI_EO_SERVER_INVALID_CHAR_BETN_KEY_VALUE)) {
248 hi_eo_server_event_log(Session, HI_EO_SERVER_INVALID_CHAR_BETN_KEY_VALUE, NULL, NULL);
249 }
250 }
251 }
252
253 /**
254 ** NAME
255 ** IsHttpServerData::
256 */
257 /**
258 ** Inspect an HTTP server response packet to determine the state.
259 **
260 ** We inspect this packet and determine whether we are in the beginning
261 ** of a response header or if we are looking at payload. We limit the
262 ** amount of inspection done on responses by only inspecting the HTTP header
263 ** and some payload. If the whole packet is a payload, then we just ignore
264 ** it, since we inspected the previous header and payload.
265 **
266 ** We limit the amount of the payload by adjusting the Server structure
267 ** members, header and header size.
268 **
269 ** @param Server the server structure
270 ** @param data pointer to the beginning of payload
271 ** @param dsize the size of the payload
272 ** @param flow_depth the amount of header and payload to inspect
273 **
274 ** @return integer
275 **
276 ** @retval HI_INVALID_ARG invalid argument
277 ** @retval HI_SUCCESS function success
278 */
279 static int IsHttpServerData(HI_SESSION *Session, Packet *p, HttpSessionData *sd)
280 {
281 const u_char *start;
282 const u_char *end;
283 const u_char *ptr;
284 int len;
285 uint32_t seq_num = 0;
286 HI_SERVER *Server;
287 HTTPINSPECT_CONF *ServerConf;
288
289 ServerConf = Session->server_conf;
290 if(!ServerConf)
291 return HI_INVALID_ARG;
292
293 Server = &(Session->server);
294
295 clearHttpRespBuffer(Server);
296 /*
297 ** HTTP:Server-Side-Session-Performance-Optimization
298 ** This drops Server->Client packets which are not part of the
299 ** HTTP Response header. It can miss part of the response header
300 ** if the header is sent as multiple packets.
301 */
302 if(!(p->data))
303 {
304 return HI_INVALID_ARG;
305 }
306
307 seq_num = GET_PKT_SEQ(p);
308
309 /*
310 ** Let's set up the data pointers.
311 */
312 Server->response.header_raw = p->data;
313 Server->response.header_raw_size = p->dsize;
314
315 start = p->data;
316 end = p->data + p->dsize;
317 ptr = start;
318
319 ptr = MovePastDelims(start,end,ptr);
320
321 len = end - ptr;
322 if ( len > 4 )
323 {
324 if(!IsHttpVersion(&ptr, end))
325 {
326 p->packet_flags |= PKT_HTTP_DECODE;
327 ApplyFlowDepth(ServerConf, p, sd, 0, 0, seq_num);
328 return HI_SUCCESS;
329 }
330 else
331 {
332 if(ServerConf->server_flow_depth > 0)
333 {
334 if(sd)
335 {
336 sd->resp_state.flow_depth_excd = false;
337 sd->resp_state.max_seq = seq_num + ServerConf->server_flow_depth;
338 }
339 }
340 p->packet_flags |= PKT_HTTP_DECODE;
341 ApplyFlowDepth(ServerConf, p, sd, 0, 0, seq_num);
342 return HI_SUCCESS;
343 }
344 }
345 else
346 {
347 return HI_SUCCESS;
348 }
349
350
351 return HI_SUCCESS;
352 }
353
354 static inline int hi_server_extract_status_msg( const u_char *start, const u_char *ptr,
355 const u_char *end, URI_PTR *result)
356 {
357 int iRet = HI_SUCCESS;
358 SkipBlankSpace(start,end,&ptr);
359
360 if ( hi_util_in_bounds(start, end, ptr) )
361 {
362 const u_char *crlf = (u_char *)SnortStrnStr((const char *)ptr, end - ptr, "\n");
363 result->uri = ptr;
364 if (crlf)
365 {
366 if(crlf[-1] == '\r')
367 result->uri_end = crlf - 1;
368 else
369 result->uri_end = crlf;
370 ptr = crlf;
371 }
372 else
373 {
374 result->uri_end =end;
375 }
376
377 if(result->uri < result->uri_end)
378 iRet = STAT_END;
379 else
380 iRet = HI_OUT_OF_BOUNDS;
381 }
382 else
383 iRet = HI_OUT_OF_BOUNDS;
384
385 return iRet;
386 }
387
388
389 static inline int hi_server_extract_status_code(HI_SESSION *Session, const u_char *start, const u_char *ptr,
390 const u_char *end, URI_PTR *result)
391 {
392 int iRet = HI_SUCCESS;
393 SkipBlankSpace(start,end,&ptr);
394
395 result->uri = ptr;
396 result->uri_end = ptr;
397
398 while( hi_util_in_bounds(start, end, ptr) )
399 {
400 if(isdigit((int)*ptr))
401 {
402 SkipDigits(start, end, &ptr);
403 if ( hi_util_in_bounds(start, end, ptr) )
404 {
405 if(isspace((int)*ptr))
406 {
407 result->uri_end = ptr;
408 iRet = STAT_END;
409 return iRet;
410 }
411 else
412 {
413 result->uri_end = ptr;
414 iRet = HI_NONFATAL_ERR;
415 return iRet;
416 }
417
418 }
419 else
420 {
421 iRet = HI_OUT_OF_BOUNDS;
422 return iRet;
423 }
424
425 }
426 else
427 {
428
429 if(hi_eo_generate_event(Session, HI_EO_SERVER_INVALID_STATCODE))
430 {
431 hi_eo_server_event_log(Session, HI_EO_SERVER_INVALID_STATCODE, NULL, NULL);
432 }
433 ptr++;
434 }
435 }
436
437 iRet = HI_OUT_OF_BOUNDS;
438
439 return iRet;
440 }
441
442 /* Given a string, removes header folding (\r\n followed by linear whitespace)
443 * and exits when the end of a header is found, defined as \n followed by a
444 * non-whitespace. This is especially helpful for HTML.
445 * Returns -1 if the header is too long and cant normalise completely */
446
447 int sf_unfold_http_header(HI_SESSION *Session, const uint8_t *inbuf,
448 uint32_t inbuf_size, uint8_t *outbuf,
449 uint32_t outbuf_size, uint32_t *output_bytes,
450 int *folded)
451 {
452 int num_spaces = 0;
453 bool line_folding = false;
454 const uint8_t *cursor = NULL, *endofinbuf = NULL;
455 uint8_t *outbuf_ptr = NULL;
456 uint32_t n = 0;
457 enum CursorState {CURSOR_STATE_NORMAL, CURSOR_STATE_NEWLINE};
458 uint8_t state = CURSOR_STATE_NORMAL;
459 cursor = inbuf;
460 endofinbuf = inbuf + inbuf_size;
461 outbuf_ptr = outbuf;
462
463 /* Keep adding chars until we get to the end of the line. If we get to the
464 * end of the line and the next line starts with a tab or space, add the space
465 * to the buffer and keep reading. If the next line does not start with a
466 * tab or space, stop reading because that's the end of the header. */
467 while((cursor < endofinbuf) && (n < outbuf_size))
468 {
469 switch (state)
470 {
471 case CURSOR_STATE_NORMAL:
472 if(*cursor == '\r')
473 {
474 if( (cursor + 1 < endofinbuf && *++cursor == '\n'))
475 {
476 state = CURSOR_STATE_NEWLINE;
477 }
478 else
479 {
480 *outbuf_ptr++ = *cursor;
481 n++;
482 }
483 }
484 else if( *cursor == '\n')
485 {
486 state = CURSOR_STATE_NEWLINE;
487 }
488 else
489 {
490 *outbuf_ptr++ = *cursor;
491 n++;
492 }
493 break;
494
495 case CURSOR_STATE_NEWLINE:
496 if((*cursor == ' ') || (*cursor == '\t'))
497 {
498 num_spaces++;
499 line_folding = true;
500 }
501 else if( line_folding )
502 {
503 if(*cursor != '\n')
504 state = CURSOR_STATE_NORMAL;
505 line_folding = false;
506 if(hi_server_is_known_header(cursor, endofinbuf))
507 {
508 if(hi_eo_generate_event(Session, HI_EO_SERVER_INVALID_HEADER_FOLDING))
509 {
510 hi_eo_server_event_log(Session,
511 HI_EO_SERVER_INVALID_HEADER_FOLDING, NULL, NULL);
512 }
513 }
514 *outbuf_ptr++ = *cursor;
515 n++;
516 }
517 else if((*cursor == 0x0b) || (*cursor == 0x0c))
518 {
519 if(hi_eo_generate_event(Session, HI_EO_SERVER_INVALID_HEADER_FOLDING))
520 {
521 hi_eo_server_event_log(Session,
522 HI_EO_SERVER_INVALID_HEADER_FOLDING, NULL, NULL);
523 }
524 goto exit_loop;
525 }
526 else
527 goto exit_loop;
528 break;
529
530 }
531 cursor++;
532 }
533 exit_loop:
534
535 if(n < outbuf_size)
536 *outbuf_ptr = '\0';
537
538 *output_bytes = outbuf_ptr - outbuf;
539 if(folded)
540 *folded = num_spaces;
541 if( n < outbuf_size)
542 return 0;
543 return -1;
544 }
545
546
547 /* Grab the argument of "charset=foo" from a Content-Type header */
548 static inline const u_char *extract_http_content_type_charset(HI_SESSION *Session,
549 HttpSessionData *hsd, const u_char *p, const u_char *start, const u_char *end )
550 {
551 size_t cmplen;
552 uint8_t unfold_buf[DECODE_BLEN];
553 uint32_t unfold_size =0;
554 const char *ptr, *ptr_end;
555
556 if (hsd == NULL)
557 return p;
558
559 /* Don't trim spaces so p is set to end of header */
560 sf_unfold_http_header(Session, p, end-p, unfold_buf,
561 sizeof(unfold_buf), &unfold_size, 0);
562 if (!unfold_size)
563 {
564 set_decode_utf_state_charset(&(hsd->utf_state), CHARSET_DEFAULT);
565 return p;
566 }
567 p += unfold_size;
568
569 ptr = (const char *)unfold_buf;
570 ptr_end = (const char *)(ptr + strlen((const char *)unfold_buf));
571
572 ptr = SnortStrcasestr(ptr, (int)(ptr_end - ptr), "text");
573 if (!ptr)
574 {
575 set_decode_utf_state_charset(&(hsd->utf_state), CHARSET_DEFAULT);
576 return p;
577 }
578
579 ptr = SnortStrcasestr(ptr, (int)(ptr_end - ptr), "utf-");
580 if (!ptr)
581 {
582 set_decode_utf_state_charset(&(hsd->utf_state), CHARSET_UNKNOWN);
583 return p;
584 }
585 ptr += 4; /* length of "utf-" */
586 cmplen = ptr_end - ptr;
587
588 if ((cmplen > 0) && (*ptr == '8'))
589 {
590 set_decode_utf_state_charset(&(hsd->utf_state), CHARSET_DEFAULT);
591 }
592 else if ((cmplen > 0) && (*ptr == '7'))
593 {
594 set_decode_utf_state_charset(&(hsd->utf_state), CHARSET_UTF7);
595 if(hi_eo_generate_event(Session, HI_EO_SERVER_UTF7))
596 hi_eo_server_event_log(Session, HI_EO_SERVER_UTF7, NULL, NULL);
597 }
598 else if (cmplen >= 4)
599 {
600 if ( !strncasecmp(ptr, "16le", 4) )
601 set_decode_utf_state_charset(&(hsd->utf_state), CHARSET_UTF16LE);
602 else if ( !strncasecmp(ptr, "16be", 4) )
603 set_decode_utf_state_charset(&(hsd->utf_state), CHARSET_UTF16BE);
604 else if ( !strncasecmp(ptr, "32le", 4) )
605 set_decode_utf_state_charset(&(hsd->utf_state), CHARSET_UTF32LE);
606 else if ( !strncasecmp(ptr, "32be", 4) )
607 set_decode_utf_state_charset(&(hsd->utf_state), CHARSET_UTF32BE);
608 else
609 set_decode_utf_state_charset(&(hsd->utf_state), CHARSET_UNKNOWN);
610 }
611 else
612 set_decode_utf_state_charset(&(hsd->utf_state), CHARSET_UNKNOWN);
613
614 return p;
615 }
616
617 static inline const u_char *extract_http_content_encoding(HTTPINSPECT_CONF *ServerConf,
618 const u_char *p, const u_char *start, const u_char *end, HEADER_PTR *header_ptr,
619 HEADER_FIELD_PTR *header_field_ptr,HI_SESSION *Session)
620 {
621 const u_char *crlf;
622 int space_present = 0;
623 if (header_ptr->content_encoding.cont_encoding_start)
624 {
625 if(hi_eo_generate_event(Session, HI_EO_SERVER_MULTIPLE_CONTENT_ENCODING))
626 {
627 hi_eo_server_event_log(Session, HI_EO_SERVER_MULTIPLE_CONTENT_ENCODING, NULL, NULL);
628 }
629
630 header_ptr->header.uri_end = p;
631 header_ptr->content_encoding.compress_fmt = 0;
632 return p;
633 }
634 else
635 {
636 header_field_ptr->content_encoding = &header_ptr->content_encoding;
637 header_field_ptr->content_encoding->cont_encoding_start =
638 header_field_ptr->content_encoding->cont_encoding_end = NULL;
639 header_field_ptr->content_encoding->compress_fmt = 0;
640 p = p + HTTPRESP_HEADER_LENGTH__CONTENT_ENCODING;
641 }
642 CheckSkipAlertCharsBeforeColon(Session, start, end, &p);
643 if(hi_util_in_bounds(start, end, p) && *p == ':')
644 {
645 p++;
646 CheckSkipAlertMultipleColon(Session, start, end, &p, HI_SI_SERVER_MODE);
647
648 if ( hi_util_in_bounds(start, end, p) )
649 {
650 if ( ServerConf->profile == HI_APACHE || ServerConf->profile == HI_ALL)
651 {
652 SkipWhiteSpace(start,end,&p);
653 }
654 else
655 {
656 SkipBlankAndNewLine(start,end,&p);
657 }
658 if( hi_util_in_bounds(start, end, p))
659 {
660 if ( *p == '\n' )
661 {
662 while(hi_util_in_bounds(start, end, p))
663 {
664 if ( *p == '\n')
665 {
666 p++;
667 while( hi_util_in_bounds(start, end, p) && ( *p == ' ' || *p == '\t'))
668 {
669 space_present = 1;
670 p++;
671 }
672 if ( space_present )
673 {
674 if ( isalpha((int)*p))
675 break;
676 else if(isspace((int)*p) && (ServerConf->profile == HI_APACHE || ServerConf->profile == HI_ALL) )
677 {
678 SkipWhiteSpace(start,end,&p);
679 }
680 else
681 return p;
682 }
683 else
684 return p;
685 }
686 else
687 break;
688 }
689 }
690 if(isalpha((int)*p))
691 {
692 header_field_ptr->content_encoding->cont_encoding_start = p;
693 while(hi_util_in_bounds(start, end, p) && *p!='\n' )
694 {
695 if(IsHeaderFieldName(p, end, HTTPRESP_HEADER_NAME__GZIP, HTTPRESP_HEADER_LENGTH__GZIP) ||
696 IsHeaderFieldName(p, end, HTTPRESP_HEADER_NAME__XGZIP, HTTPRESP_HEADER_LENGTH__XGZIP))
697 {
698 if (header_ptr->content_encoding.compress_fmt)
699 {
700 if(hi_eo_generate_event(Session, HI_EO_SERVER_MULTIPLE_CONTENT_ENCODING))
701 {
702 hi_eo_server_event_log(Session, HI_EO_SERVER_MULTIPLE_CONTENT_ENCODING, NULL, NULL);
703 }
704 }
705 header_field_ptr->content_encoding->compress_fmt |= HTTP_RESP_COMPRESS_TYPE__GZIP;
706 p = p + HTTPRESP_HEADER_LENGTH__GZIP;
707 continue;
708 }
709 else if(IsHeaderFieldName(p, end, HTTPRESP_HEADER_NAME__DEFLATE, HTTPRESP_HEADER_LENGTH__DEFLATE))
710 {
711 if (header_ptr->content_encoding.compress_fmt)
712 {
713 if(hi_eo_generate_event(Session, HI_EO_SERVER_MULTIPLE_CONTENT_ENCODING))
714 {
715 hi_eo_server_event_log(Session, HI_EO_SERVER_MULTIPLE_CONTENT_ENCODING, NULL, NULL);
716 }
717 }
718 header_field_ptr->content_encoding->compress_fmt |= HTTP_RESP_COMPRESS_TYPE__DEFLATE;
719 p = p + HTTPRESP_HEADER_LENGTH__DEFLATE;
720 continue;
721 }
722 else
723 p++;
724 }
725
726 /*crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
727 if(crlf)
728 {
729 p = crlf;
730 }
731 else
732 {
733 header_ptr->header.uri_end = end ;
734 return end;
735 }*/
736 }
737 else
738 return p;
739 }
740 }
741 }
742 else
743 {
744 if(hi_util_in_bounds(start, end, p))
745 {
746 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
747 if(crlf)
748 {
749 p = crlf;
750 }
751 else
752 {
753 header_ptr->header.uri_end = end ;
754 return end;
755 }
756 }
757 }
758 if(!p || !hi_util_in_bounds(start, end, p))
759 p = end;
760
761 return p;
762 }
763
764 /*
765 * extract_http_content_range() will extract required data from content-range
766 * field. Focus is for, when the units is "bytes".
767 * The possible syntax as follows,
768 * content-range: <units> <start_pos>-<end_pos>/<total_len>
769 * content-range: <units> * /<total_len>
770 * content-range: <units> <start_pos>-<end_pos>/ *
771 */
772 static const u_char *extract_http_content_range(HI_SESSION *Session,
773 const u_char *p, const u_char *start, const u_char *end,
774 HEADER_PTR *header_ptr)
775 {
776 u_char *crlf = NULL;
777 const u_char *unit_start = NULL;
778 int is_byte_range = false;
779
780 SkipBlankSpace(start,end,&p);
781 if (hi_util_in_bounds(start, end, p) && *p == ':')
782 {
783 p++;
784 CheckSkipAlertMultipleColon(Session, start, end, &p, HI_SI_SERVER_MODE);
785 while (hi_util_in_bounds(start, end, p))
786 {
787 if ((*p == ' ') || (*p == '\t'))
788 {
789 p++;
790 break;
791 }
792 p++;
793 }
794 if (hi_util_in_bounds(start, end, p))
795 {
796 /* extract unit */
797 unit_start = p;
798 while (hi_util_in_bounds(start, end, p) && ( *p != ' '))
799 {
800 p++;
801 }
802
803 if (*p != ' ')
804 {
805 if (hi_eo_generate_event(Session, HI_EO_SERVER_INVALID_CONTENT_RANGE_UNIT_FMT))
806 {
807 hi_eo_server_event_log(Session, HI_EO_SERVER_INVALID_CONTENT_RANGE_UNIT_FMT, NULL, NULL);
808 }
809 header_ptr->range_flag = RANGE_WITH_RESP_ERROR;
810 return end;
811 }
812
813 /* Set flag, when unit is bytes. Based on unit, flow will change */
814 if (!strncasecmp((const char *)unit_start, RANGE_UNIT_BYTE, 5))
815 {
816 is_byte_range = true;
817 }
818 else
819 {
820 is_byte_range = false;
821 }
822 p++;
823
824 if (hi_util_in_bounds(start, end, p))
825 {
826 if (is_byte_range == true)
827 {
828 if (isdigit((int)*p))
829 {
830 /*if start with digit, then expectation is
831 "<start_pos>-<end_pos>/<total_len>" or "<start_pos>-<end_pos>/ *"
832 There is no whitespace between / and * To make it comment added the space
833 pattern other than above mentioned format will flag as error */
834 uint64_t start_range = 0;
835 uint64_t end_range = 0;
836 uint64_t len = 0;
837 const u_char *start_ptr = NULL;
838 const u_char *end_ptr = NULL;
839 const u_char *len_ptr = NULL;
840 const u_char *ret_ptr = NULL;
841 int is_valid_range = false;
842 int delim_1 = false;
843 int delim_2 = false;
844 int assign = false;
845
846 start_ptr = p;
847 p++;
848 while (hi_util_in_bounds(start, end, p))
849 {
850 if (!isdigit((int)*p))
851 {
852 if (*p == '-')
853 {
854 if ((delim_2 == true) || (delim_1 == true))
855 {
856 break;
857 }
858 delim_1 = true;
859 assign = true;
860 p++;
861 continue;
862 }
863 else if (*p == '/')
864 {
865 if ((delim_1 == false) || (delim_2 == true))
866 {
867 break;
868 }
869 delim_2 = true;
870 assign = true;
871 p++;
872 continue;
873 }
874 else if ((*p == '*') || (*p == '\r') || (*p == '\n'))
875 {
876 if ((delim_1 == false) || (delim_2 == false))
877 {
878 break;
879 }
880 is_valid_range = true;
881 break;
882 }
883 break;
884 }
885
886 /* Here digit only come */
887 if (assign == true)
888 {
889 if ((delim_2 == false) && (delim_1 == true))
890 {
891 end_ptr = p;
892 }
893 else if ((delim_1 == true) && (delim_2 == true))
894 {
895 len_ptr = p;
896 }
897 else
898 {
899 assign = false;
900 break;
901 }
902 assign = false;
903 }
904 p++;
905 }
906
907 if (is_valid_range == true)
908 {
909 /* Now check total_len, * means set the flag accordingly
910 or convert str to value and flag as full content
911 when differnce between start_pos and end_pos and sum with 1
912 equals to total_len, otherwise partial flag set */
913 if (*p == '*')
914 {
915 header_ptr->range_flag = RANGE_WITH_RESP_UNKNOWN_CONTENT_SIZE;
916 }
917 else if ((start_ptr) && (end_ptr) && (len_ptr))
918 {
919 int error = false;
920
921 start_range = (uint64_t)SnortStrtol((char *)start_ptr, (char**)&ret_ptr, 10);
922 if ((errno == ERANGE) || (start_ptr == ret_ptr) || (start_range > 0xFFFFFFFF))
923 {
924 error = true;
925 }
926
927 ret_ptr = NULL;
928 end_range = (uint64_t)SnortStrtol((char *)end_ptr, (char**)&ret_ptr, 10);
929 if ((errno == ERANGE) || (end_ptr == ret_ptr) || (end_range > 0xFFFFFFFF))
930 {
931 error = true;
932 }
933
934 ret_ptr = NULL;
935 len = (uint64_t)SnortStrtol((char *)len_ptr, (char**)&ret_ptr, 10);
936 if ((errno == ERANGE) || (len_ptr == ret_ptr) || (len > 0xFFFFFFFF))
937 {
938 error = true;
939 }
940
941 if (error == false)
942 {
943 if (((end_range - start_range) + 1) == len)
944 {
945 header_ptr->range_flag = RANGE_WITH_RESP_FULL_CONTENT;
946 }
947 else
948 {
949 header_ptr->range_flag = RANGE_WITH_RESP_PARTIAL_CONTENT;
950 }
951 }
952 else
953 {
954 header_ptr->range_flag = RANGE_WITH_RESP_ERROR;
955 }
956 }
957 else
958 {
959 header_ptr->range_flag = RANGE_WITH_RESP_ERROR;
960 }
961
962 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
963 if (crlf)
964 {
965 return p;
966 }
967 else
968 {
969 header_ptr->header.uri_end = end;
970 return end;
971 }
972 }
973 }
974 else if (*p == '*')
975 {
976 /* To check pattern, "* /<total_len>" and set flag accordingly */
977 header_ptr->range_flag = RANGE_WITH_UNKNOWN_CONTENT_RANGE;
978 p++;
979 if (hi_util_in_bounds(start, end, p))
980 {
981 if (*p == '/')
982 {
983 p++;
984 if (hi_util_in_bounds(start, end, p))
985 {
986 if (isdigit((int)*p))
987 {
988 p++;
989 while (hi_util_in_bounds(start, end, p))
990 {
991 if (isdigit((int)*p))
992 {
993 p++;
994 continue;
995 }
996 else if ((*p == '\r') || (*p == '\n')) /* digit followed by \r or \n */
997 {
998 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
999 if (crlf)
1000 {
1001 return p;
1002 }
1003 else
1004 {
1005 header_ptr->header.uri_end = end;
1006 return end;
1007 }
1008 }
1009 }
1010 }
1011 }
1012 }
1013 }
1014 }
1015 header_ptr->range_flag = RANGE_WITH_RESP_ERROR;
1016 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
1017 if (crlf)
1018 {
1019 p = crlf;
1020 return p;
1021 }
1022 else
1023 {
1024 header_ptr->header.uri_end = end;
1025 return end;
1026 }
1027 }
1028 else
1029 {
1030 /* other than byte range is out of scope for snort */
1031 while (hi_util_in_bounds(start, end, p))
1032 {
1033 if ((*p == '\r') || (*p == '\n'))
1034 {
1035 header_ptr->range_flag = RANGE_WITH_RESP_NON_BYTE;
1036 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
1037 if (crlf)
1038 {
1039 p = crlf;
1040 return p;
1041 }
1042 else
1043 {
1044 header_ptr->header.uri_end = end;
1045 return end;
1046 }
1047 }
1048 p++;
1049 }
1050 }
1051 }
1052 }
1053 }
1054 header_ptr->range_flag = RANGE_WITH_RESP_ERROR;
1055 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
1056 if (crlf)
1057 {
1058 p = crlf;
1059 return p;
1060 }
1061 else
1062 {
1063 header_ptr->header.uri_end = end;
1064 return end;
1065 }
1066 }
1067
1068 /* extract_http_accept_ranges will extract and set flag based on bytes unit */
1069 static const u_char *extract_http_accept_ranges(HI_SESSION *Session,
1070 const u_char *p, const u_char *start, const u_char *end,
1071 HEADER_PTR *header_ptr, uint16_t *accept_range_flag)
1072 {
1073 const u_char *start_ptr = NULL;
1074 const u_char *end_ptr = NULL;
1075 u_char *crlf = NULL;
1076 int len = 0;
1077
1078 *accept_range_flag = ACCEPT_RANGE_UNKNOWN;
1079 SkipBlankSpace(start,end,&p);
1080
1081 if (hi_util_in_bounds(start, end, p) && (*p == ':'))
1082 {
1083 p++;
1084 CheckSkipAlertMultipleColon(Session, start, end, &p, HI_SI_SERVER_MODE);
1085 while (hi_util_in_bounds(start, end, p))
1086 {
1087 if ((*p == ' ') || (*p == '\t') || (*p == ','))
1088 {
1089 p++;
1090 break;
1091 }
1092 p++;
1093 }
1094
1095 if (hi_util_in_bounds(start, end, p))
1096 {
1097 start_ptr = p;
1098 while (hi_util_in_bounds(start, end, p))
1099 {
1100 if ((*p == '\r') || (*p == '\n'))
1101 {
1102 end_ptr = (p - 1);
1103 break;
1104 }
1105 p++;
1106 }
1107
1108 if (start_ptr && end_ptr)
1109 {
1110 len = end_ptr - start_ptr;
1111 if ((len >= 5) && (SnortStrcasestr((const char *)start, len, "bytes")))
1112 {
1113 *accept_range_flag = ACCEPT_RANGE_BYTES;
1114 }
1115 else if ((len >= 4) && (!strncasecmp((const char *)start, "none", 4)))
1116 {
1117 *accept_range_flag = ACCEPT_RANGE_NONE;
1118 }
1119 else
1120 {
1121 *accept_range_flag = ACCEPT_RANGE_OTHER;
1122 }
1123 }
1124 }
1125 }
1126
1127 crlf = (u_char *)SnortStrnStr((const char *)p, end - p, "\n");
1128 if (crlf)
1129 {
1130 p = crlf;
1131 return p;
1132 }
1133 else
1134 {
1135 header_ptr->header.uri_end = end;
1136 return end;
1137 }
1138 }
1139
1140 const u_char *extract_http_transfer_encoding(HI_SESSION *Session, HttpSessionData *hsd,
1141 const u_char *p, const u_char *start, const u_char *end,
1142 HEADER_PTR *header_ptr, int iInspectMode)
1143 {
1144 uint8_t unfold_buf[DECODE_BLEN];
1145 uint32_t unfold_size =0;
1146 const u_char *start_ptr, *end_ptr, *cur_ptr;
1147
1148
1149 SkipBlankSpace(start,end,&p);
1150
1151 if(hi_util_in_bounds(start, end, p) && *p == ':')
1152 {
1153 p++;
1154 CheckSkipAlertMultipleColon(Session, start, end, &p, iInspectMode);
1155
1156 if(hi_util_in_bounds(start, end, p))
1157 sf_unfold_http_header(Session, p, end-p, unfold_buf,
1158 sizeof(unfold_buf), &unfold_size, 0);
1159
1160 if(!unfold_size)
1161 {
1162 header_ptr->header.uri_end = end;
1163 return end;
1164 }
1165
1166 p = p + unfold_size;
1167
1168 start_ptr = unfold_buf;
1169 cur_ptr = unfold_buf;
1170 end_ptr = unfold_buf + unfold_size;
1171 SkipBlankSpace(start_ptr,end_ptr,&cur_ptr);
1172
1173 start_ptr = cur_ptr;
1174
1175 start_ptr = (u_char *)SnortStrcasestr((const char *)start_ptr, (end_ptr - start_ptr), "chunked");
1176 if (start_ptr)
1177 {
1178 if ((iInspectMode == HI_SI_SERVER_MODE) && hsd)
1179 {
1180 hsd->resp_state.last_pkt_chunked = 1;
1181 hsd->resp_state.last_pkt_contlen = 0;
1182 }
1183 header_ptr->content_len.len = 0 ;
1184 header_ptr->content_len.cont_len_start = NULL;
1185 header_ptr->is_chunked = true;
1186 }
1187 }
1188 else
1189 {
1190 header_ptr->header.uri_end = end;
1191 return end;
1192 }
1193
1194 return p;
1195 }
1196
1197 #if defined(FEAT_OPEN_APPID)
1198 static const u_char *extract_http_server_header(HI_SESSION *Session, const u_char *p, const u_char *start,
1199 const u_char *end, HEADER_PTR *header_ptr, HEADER_LOCATION *headerLoc)
1200 {
1201 int num_spaces = 0;
1202 uint8_t unfold_buf[DECODE_BLEN];
1203 uint32_t unfold_size =0;
1204 const u_char *end_ptr, *cur_ptr;
1205
1206 SkipBlankSpace(start,end,&p);
1207
1208 if(hi_util_in_bounds(start, end, p) && *p == ':')
1209 {
1210 p++;
1211 CheckSkipAlertMultipleColon(Session, start, end, &p, HI_SI_SERVER_MODE);
1212
1213 if(hi_util_in_bounds(start, end, p))
1214 sf_unfold_http_header(Session, p, end-p, unfold_buf,
1215 sizeof(unfold_buf), &unfold_size, &num_spaces);
1216
1217 if(!unfold_size)
1218 {
1219 header_ptr->header.uri_end = end;
1220 return end;
1221 }
1222
1223 p = p + unfold_size;
1224
1225 cur_ptr = unfold_buf;
1226 end_ptr = unfold_buf + unfold_size;
1227 SkipBlankSpace(unfold_buf,end_ptr,&cur_ptr);
1228
1229 {
1230 unsigned field_strlen = (unsigned)(end_ptr - cur_ptr);
1231 if (field_strlen)
1232 {
1233 headerLoc->start = (u_char *)strndup((const char *)cur_ptr, field_strlen);
1234 if (NULL == headerLoc->start)
1235 {
1236 /* treat this out-of-memory error as a parse failure */
1237 header_ptr->header.uri_end = end;
1238 return end;
1239 }
1240 /* now that we have the memory, fill in len. */
1241 headerLoc->len = field_strlen;
1242 }
1243 }
1244 }
1245 else
1246 {
1247 header_ptr->header.uri_end = end;
1248 return end;
1249 }
1250
1251 return p;
1252
1253 }
1254 #endif /* defined(FEAT_OPEN_APPID) */
1255
1256 static inline const u_char *extractHttpRespHeaderFieldValues(HTTPINSPECT_CONF *ServerConf,
1257 const u_char *p, const u_char *offset, const u_char *start,
1258 const u_char *end, HEADER_PTR *header_ptr,
1259 HEADER_FIELD_PTR *header_field_ptr, int parse_cont_encoding, bool parse_trans_encoding,
1260 HttpSessionData *hsd, HI_SESSION *Session)
1261 {
1262 if (((p - offset) == 0) && ((*p == 'S') || (*p == 's')))
1263 {
1264 /* Search for 'Cookie' at beginning, starting from current *p */
1265 if ( ServerConf->enable_cookie &&
1266 IsHeaderFieldName(p, end, HTTPRESP_HEADER_NAME__COOKIE,
1267 HTTPRESP_HEADER_LENGTH__COOKIE))
1268 {
1269 p = extract_http_cookie((p + HTTPRESP_HEADER_LENGTH__COOKIE), end, header_ptr, header_field_ptr);
1270 }
1271 #if defined(FEAT_OPEN_APPID)
1272 else if ((ServerConf->appid_enabled) && (IsHeaderFieldName(p, end, HEADER_NAME__SERVER, HEADER_LENGTH__SERVER)))
1273 {
1274 p = p + HEADER_LENGTH__SERVER;
1275 p = extract_http_server_header(Session, p, start, end, header_ptr, &header_ptr->server);
1276 }
1277 #endif /* defined(FEAT_OPEN_APPID) */
1278 }
1279 else if (((p - offset) == 0) && ((*p == 'C') || (*p == 'c')))
1280 {
1281 if ( IsHeaderFieldName(p, end, HTTPRESP_HEADER_NAME__CONTENT_TYPE,
1282 HTTPRESP_HEADER_LENGTH__CONTENT_TYPE) && ServerConf->normalize_utf)
1283 {
1284 #if defined(FEAT_OPEN_APPID)
1285 const u_char *ptr;
1286 ptr = p + HTTPRESP_HEADER_LENGTH__CONTENT_TYPE;
1287 #endif /* defined(FEAT_OPEN_APPID) */
1288 p = extract_http_content_type_charset(Session, hsd, p, start, end);
1289 #if defined(FEAT_OPEN_APPID)
1290 header_ptr->contentType.start = ptr;
1291 SkipBlankColon(ptr, p, &header_ptr->contentType.start);
1292 header_ptr->contentType.len = p - header_ptr->contentType.start;
1293 #endif /* defined(FEAT_OPEN_APPID) */
1294 }
1295
1296 else if ( IsHeaderFieldName(p, end, HTTPRESP_HEADER_NAME__CONTENT_ENCODING,
1297 HTTPRESP_HEADER_LENGTH__CONTENT_ENCODING) && ServerConf->extract_gzip &&
1298 parse_cont_encoding)
1299 {
1300 p = extract_http_content_encoding(ServerConf, p, start, end, header_ptr, header_field_ptr,Session);
1301 }
1302 else if ( IsHeaderFieldName(p, end, HTTPRESP_HEADER_NAME__CONTENT_LENGTH,
1303 HTTPRESP_HEADER_LENGTH__CONTENT_LENGTH) )
1304 {
1305 if(hsd && !hsd->resp_state.last_pkt_chunked)
1306 p = extract_http_content_length(Session, ServerConf, p, start, end, header_ptr, header_field_ptr, HI_SI_SERVER_MODE );
1307 }
1308 else if (IsHeaderFieldName(p, end, HTTPRESP_HEADER_NAME__CONTENT_RANGE, HTTPRESP_HEADER_LENGTH__CONTENT_RANGE))
1309 {
1310 p = p + HTTPRESP_HEADER_LENGTH__CONTENT_RANGE;
1311 p = extract_http_content_range(Session, p, start, end, header_ptr);
1312 }
1313 }
1314 else if (((p - offset) == 0) && ((*p == 'T') || (*p == 't')))
1315 {
1316 if ( IsHeaderFieldName(p, end, HTTPRESP_HEADER_NAME__TRANSFER_ENCODING,
1317 HTTPRESP_HEADER_LENGTH__TRANSFER_ENCODING) && parse_trans_encoding )
1318 {
1319 p = p + HTTPRESP_HEADER_LENGTH__TRANSFER_ENCODING;
1320 p = extract_http_transfer_encoding(Session, hsd, p, start, end, header_ptr, HI_SI_SERVER_MODE);
1321 }
1322 }
1323 else if (((p - offset) == 0) && ((*p == 'A') || (*p == 'a')))
1324 {
1325 if (IsHeaderFieldName(p, end, HTTPRESP_HEADER_NAME__ACCEPT_RANGES,
1326 HTTPRESP_HEADER_LENGTH__ACCEPT_RANGES))
1327 {
1328 uint16_t accept_range_flag = 0;
1329 p = p + HTTPRESP_HEADER_LENGTH__ACCEPT_RANGES;
1330 p = extract_http_accept_ranges(Session, p, start, end, header_ptr, &accept_range_flag);
1331 Session->server.response.accept_range_flag = accept_range_flag;
1332 }
1333 }
1334 #if defined(FEAT_OPEN_APPID)
1335 else if(((p - offset) == 0) && ((*p == 'V') || (*p == 'v')))
1336 {
1337 if((ServerConf->appid_enabled) && (IsHeaderFieldName(p, end, HEADER_NAME__VIA, HEADER_LENGTH__VIA)))
1338 {
1339 p = p + HEADER_LENGTH__VIA;
1340 p = extract_http_server_header(Session, p, start, end, header_ptr, &header_ptr->via);
1341 }
1342 }
1343 else if(((p - offset) == 0) && ((*p == 'X') || (*p == 'x')))
1344 {
1345 if((ServerConf->appid_enabled) && (IsHeaderFieldName(p, end, HEADER_NAME__X_WORKING_WITH, HEADER_LENGTH__X_WORKING_WITH)))
1346 {
1347 p = p + HEADER_LENGTH__X_WORKING_WITH;
1348 p = extract_http_server_header(Session, p, start, end, header_ptr, &header_ptr->xWorkingWith);
1349 }
1350 }
1351 #endif /* defined(FEAT_OPEN_APPID) */
1352 return p;
1353 }
1354
1355 int hi_server_is_known_header(
1356 const u_char *p, const u_char *end)
1357 {
1358
1359 if ((*p == 'C') || (*p == 'c'))
1360 {
1361 if (IsHeaderFieldName(p, end, HTTPRESP_HEADER_NAME__CONTENT_TYPE,
1362 HTTPRESP_HEADER_LENGTH__CONTENT_TYPE) ||
1363 IsHeaderFieldName(p, end, HTTPRESP_HEADER_NAME__CONTENT_ENCODING,
1364 HTTPRESP_HEADER_LENGTH__CONTENT_ENCODING) ||
1365 IsHeaderFieldName(p, end, HTTPRESP_HEADER_NAME__CONTENT_LENGTH,
1366 HTTPRESP_HEADER_LENGTH__CONTENT_LENGTH))
1367 {
1368 return 1;
1369 }
1370 }
1371
1372 if ((*p == 'T') || (*p == 't'))
1373 {
1374 if (IsHeaderFieldName(p, end, HTTPRESP_HEADER_NAME__TRANSFER_ENCODING,
1375 HTTPRESP_HEADER_LENGTH__TRANSFER_ENCODING))
1376 {
1377 return 1;
1378 }
1379 }
1380
1381 return 0;
1382 }
1383
1384 static inline const u_char *hi_server_extract_header(
1385 HI_SESSION *Session, HTTPINSPECT_CONF *ServerConf,
1386 HEADER_PTR *header_ptr, const u_char *start,
1387 const u_char *end, int parse_cont_encoding,
1388 bool parse_trans_encoding, HttpSessionData *hsd)
1389 {
1390 const u_char *p;
1391 const u_char *offset;
1392 HEADER_FIELD_PTR header_field_ptr ;
1393 bool newline = false;
1394
1395 if(!start || !end)
1396 return NULL;
1397
1398 p = start;
1399
1400 offset = (u_char*)p;
1401
1402 header_ptr->header.uri = p;
1403 header_ptr->header.uri_end = end;
1404 header_ptr->content_encoding.compress_fmt = 0;
1405 header_ptr->content_len.len = 0;
1406 header_ptr->is_chunked = false;
1407
1408 while (hi_util_in_bounds(start, end, p))
1409 {
1410 if(*p == '\n')
1411 {
1412 newline = true;
1413 p++;
1414
1415 offset = (u_char*)p;
1416
1417 if (!hi_util_in_bounds(start, end, p))
1418 {
1419 header_ptr->header.uri_end = p;
1420 return p;
1421 }
1422
1423 if (*p < 0x0E)
1424 {
1425 if(*p == '\r')
1426 {
1427 p++;
1428
1429 if(hi_util_in_bounds(start, end, p) && (*p == '\n'))
1430 {
1431 p++;
1432 header_ptr->header.uri_end = p;
1433 hsd->resp_state.eoh_found = true;
1434 return p;
1435 }
1436 }
1437 else if(*p == '\n')
1438 {
1439 p++;
1440 header_ptr->header.uri_end = p;
1441 hsd->resp_state.eoh_found = true;
1442 return p;
1443 }
1444 }
1445 else if ( (p = extractHttpRespHeaderFieldValues(ServerConf, p, offset,
1446 start, end, header_ptr, &header_field_ptr,
1447 parse_cont_encoding, parse_trans_encoding, hsd, Session)) == end)
1448 {
1449 return end;
1450 }
1451
1452 }
1453 else if( (p == header_ptr->header.uri) &&
1454 (p = extractHttpRespHeaderFieldValues(ServerConf, p, offset,
1455 start, end, header_ptr, &header_field_ptr,
1456 parse_cont_encoding, parse_trans_encoding, hsd, Session)) == end)
1457 {
1458 return end;
1459 }
1460 if ( *p == '\n')
1461 {
1462 newline = true;
1463 continue;
1464 }
1465 if (newline && (*p == '\t' || *p == ' '))
1466 {
1467 while(*p == '\t' || *p == ' ')
1468 p++;
1469
1470 if (hi_util_in_bounds(start, end, p) &&
1471 hi_server_is_known_header(p, end))
1472 {
1473 if(hi_eo_generate_event(Session, HI_EO_SERVER_INVALID_HEADER_FOLDING))
1474 {
1475 hi_eo_server_event_log(Session, HI_EO_SERVER_INVALID_HEADER_FOLDING,
1476 NULL, NULL);
1477 }
1478 newline = false;
1479 }
1480 }
1481 else
1482 {
1483 newline = false;
1484 p++;
1485 }
1486 }
1487
1488 header_ptr->header.uri_end = p;
1489 return p;
1490 }
1491
1492 static inline int hi_server_extract_body(
1493 HI_SESSION *Session, HttpSessionData *sd,
1494 const u_char *ptr, const u_char *end, URI_PTR *result)
1495 {
1496 HTTPINSPECT_CONF *ServerConf;
1497 const u_char *start = ptr;
1498 int iRet = HI_SUCCESS;
1499 const u_char *post_end = end;
1500 uint32_t updated_chunk_remainder = 0;
1501 uint32_t chunk_read = 0;
1502 int64_t bytes_to_read = 0;
1503 ServerConf = Session->server_conf;
1504
1505 switch(ServerConf->server_extract_size)
1506 {
1507 case -1:
1508 result->uri = result->uri_end = NULL;
1509 return iRet;
1510 case 0:
1511 break;
1512 default:
1513 if(sd->resp_state.data_extracted < ServerConf->server_extract_size)
1514 {
1515 bytes_to_read = ServerConf->server_extract_size - sd->resp_state.data_extracted;
1516 if((end-ptr) > bytes_to_read )
1517 {
1518 end = ptr + bytes_to_read;
1519 }
1520 else
1521 bytes_to_read = (end-ptr);
1522 sd->resp_state.data_extracted += (int)bytes_to_read;
1523 }
1524 else
1525 {
1526 result->uri = result->uri_end = NULL;
1527 return iRet;
1528 }
1529 }
1530
1531 /* if( ServerConf->server_flow_depth && ((end - ptr) > ServerConf->server_flow_depth) )
1532 {
1533 end = ptr + ServerConf->server_flow_depth;
1534 }*/
1535
1536 if (!(sd->resp_state.last_pkt_contlen))
1537 {
1538 if( ServerConf->chunk_length || ServerConf->small_chunk_length.size )
1539 {
1540 if (sd->resp_state.last_pkt_chunked
1541 && CheckChunkEncoding(Session, start, end, &post_end,
1542 (u_char *)HttpDecodeBuf.data, sizeof(HttpDecodeBuf.data),
1543 sd->resp_state.chunk_remainder, &updated_chunk_remainder, &chunk_read,
1544 sd, HI_SI_SERVER_MODE) == 1)
1545 {
1546 sd->resp_state.chunk_remainder = updated_chunk_remainder;
1547 sd->resp_state.last_pkt_chunked = 1;
1548 result->uri = (u_char *)HttpDecodeBuf.data;
1549 result->uri_end = result->uri + chunk_read;
1550 return iRet;
1551 }
1552 else
1553 {
1554 if(!(sd->resp_state.last_pkt_chunked) && !simple_response)
1555 {
1556 if(hi_eo_generate_event(Session, HI_EO_SERVER_NO_CONTLEN))
1557 {
1558 hi_eo_server_event_log(Session, HI_EO_SERVER_NO_CONTLEN, NULL, NULL);
1559 }
1560 }
1561 else
1562 sd->resp_state.last_pkt_chunked = 0;
1563 result->uri = start;
1564 result->uri_end = end;
1565 }
1566 }
1567 else
1568 {
1569 result->uri = start;
1570 result->uri_end = end;
1571 return iRet;
1572 }
1573 }
1574
1575 result->uri = start;
1576 result->uri_end = end;
1577
1578 return STAT_END;
1579 }
1580
1581 static void LogFileDecomp( void *Context, int Event )
1582 {
1583 if( Context != NULL )
1584 if(hi_eo_generate_event((HI_SESSION *)Context, Event))
1585 hi_eo_server_event_log((HI_SESSION *)Context, Event, NULL, NULL);
1586 }
1587
1588 static void InitFileDecomp(HttpSessionData *hsd, HI_SESSION *session, void* ssnptr)
1589 {
1590 fd_session_p_t fd_session;
1591
1592 if((hsd == NULL) || (session == NULL) || (session->server_conf == NULL) ||
1593 (session->global_conf == NULL) || (hi_fd_conf.fd_MemPool == NULL) )
1594 return;
1595
1596 if( (fd_session = File_Decomp_New(ssnptr)) == (fd_session_p_t)NULL )
1597 return;
1598
1599 hsd->fd_state = fd_session;
1600 fd_session->Modes = session->server_conf->file_decomp_modes;
1601
1602 fd_session->Alert_Callback = LogFileDecomp;
1603 fd_session->Alert_Context = session;
1604
1605 if( (session->server_conf->unlimited_decompress) != 0 )
1606 {
1607 fd_session->Compr_Depth = 0;
1608 fd_session->Decompr_Depth = 0;
1609 }
1610 else
1611 {
1612 fd_session->Compr_Depth = session->global_conf->compr_depth;
1613 fd_session->Decompr_Depth = session->global_conf->decompr_depth;
1614 }
1615
1616 (void)File_Decomp_Init( fd_session );
1617 }
1618
1619 static void SetGzipBuffers(HttpSessionData *hsd, HI_SESSION *session, void* scbPtr)
1620 {
1621 if ((hsd != NULL) && (hsd->decomp_state == NULL)
1622 && (session != NULL) && (session->server_conf != NULL)
1623 && (session->global_conf != NULL) && session->server_conf->extract_gzip)
1624 {
1625 MemBucket *bkt = mempool_alloc(hi_gzip_mempool);
1626
1627 if (bkt != NULL)
1628 {
1629 bkt->scbPtr = scbPtr;
1630 hsd->decomp_state = bkt->data;
1631 hsd->decomp_state->bkt = bkt;
1632 if (session->server_conf->unlimited_decompress)
1633 {
1634 hsd->decomp_state->compr_depth = MAX_GZIP_DEPTH;
1635 hsd->decomp_state->decompr_depth = MAX_GZIP_DEPTH;
1636 }
1637 else
1638 {
1639 hsd->decomp_state->compr_depth = session->global_conf->compr_depth;
1640 hsd->decomp_state->decompr_depth = session->global_conf->decompr_depth;
1641 }
1642 hsd->decomp_state->inflate_init = 0;
1643 hsd->decomp_state->stage = HTTP_DECOMP_START;
1644 }
1645 else
1646 {
1647 mempool_free(hi_gzip_mempool, bkt);
1648 }
1649 }
1650 }
1651
1652 int uncompress_gzip ( HI_SESSION *Session, u_char *dest, int destLen, const u_char *source,
1653 int sourceLen, HttpSessionData *sd, int *total_bytes_read, int compr_fmt)
1654 {
1655 z_streamp streamp;
1656 int err;
1657 int iRet = HI_SUCCESS;
1658 int bytes_read_so_far;
1659
1660 streamp = &sd->decomp_state->d_stream;
1661
1662 /* Are we starting a new packet or continuing on the current one? */
1663 if (sd->decomp_state->stage == HTTP_DECOMP_START)
1664 {
1665 streamp->next_in = (Bytef*)source;
1666 streamp->avail_in = (uInt)sourceLen;
1667 if ((uLong)streamp->avail_in != (uLong)sourceLen)
1668 {
1669 sd->decomp_state->stage = HTTP_DECOMP_FIN;
1670 return HI_FATAL_ERR;
1671 }
1672 bytes_read_so_far = 0;
1673 }
1674 else
1675 {
1676 bytes_read_so_far = streamp->total_out;
1677 }
1678
1679 streamp->next_out = dest;
1680 streamp->avail_out = (uInt)destLen;
1681 if ((uLong)streamp->avail_out != (uLong)destLen)
1682 {
1683 return HI_FATAL_ERR;
1684 }
1685
1686
1687 if(!sd->decomp_state->inflate_init)
1688 {
1689 sd->decomp_state->inflate_init = 1;
1690 streamp->zalloc = (alloc_func)0;
1691 streamp->zfree = (free_func)0;
1692 if(compr_fmt & HTTP_RESP_COMPRESS_TYPE__DEFLATE)
1693 err = inflateInit(streamp);
1694 else
1695 err = inflateInit2(streamp, GZIP_WBITS);
1696 if (err != Z_OK)
1697 {
1698 return HI_FATAL_ERR;
1699 }
1700 }
1701 else if (sd->decomp_state->stage != HTTP_DECOMP_MID)
1702 {
1703 streamp->total_in = 0;
1704 streamp->total_out =0;
1705 }
1706
1707 err = inflate(streamp, Z_SYNC_FLUSH);
1708 if ((!sd->decomp_state->deflate_initialized)
1709 && (err == Z_DATA_ERROR)
1710 && (compr_fmt & HTTP_RESP_COMPRESS_TYPE__DEFLATE))
1711 {
1712 inflateEnd(streamp);
1713 err = inflateInit2(streamp,DEFLATE_RAW_WBITS);
1714 if (err != Z_OK)
1715 {
1716 return HI_FATAL_ERR;
1717 }
1718
1719 sd->decomp_state->deflate_initialized = true;
1720
1721 streamp->next_in = (Bytef*)source;
1722 streamp->avail_in = (uInt)sourceLen;
1723
1724 err = inflate(streamp, Z_SYNC_FLUSH);
1725 }
1726
1727 if ((err != Z_STREAM_END) && (err !=Z_OK))
1728 {
1729
1730 /* If some of the compressed data is decompressed we need to provide that for detection */
1731 if (( streamp->total_out > 0) && (err != Z_DATA_ERROR))
1732 {
1733 *total_bytes_read = streamp->total_out;
1734 iRet = HI_NONFATAL_ERR;
1735 }
1736 else
1737 iRet = HI_FATAL_ERR;
1738 inflateEnd(streamp);
1739 sd->decomp_state->stage = HTTP_DECOMP_FIN;
1740 return iRet;
1741 }
1742 *total_bytes_read = streamp->total_out - bytes_read_so_far;
1743
1744 /* Check if we need to decompress more */
1745 if(err == Z_STREAM_END)
1746 {
1747 if(streamp->avail_in != 0 )
1748 {
1749 /* We have a case here where uncompression returned END, yet data is available for compression */
1750 if(streamp->total_out > 0)
1751 {
1752 //Partial decompression case, log an alert
1753 if(hi_eo_generate_event(Session, HI_EO_SERVER_PARTIAL_DECOMPRESSION_FAIL))
1754 {
1755 hi_eo_server_event_log(Session, HI_EO_SERVER_PARTIAL_DECOMPRESSION_FAIL, NULL, NULL);
1756 }
1757 }
1758 else
1759 {
1760 //Proceed with non-fatal error and generate decompression failed event
1761 iRet = HI_NONFATAL_ERR;
1762 }
1763 }
1764 sd->decomp_state->stage = HTTP_DECOMP_FIN;
1765 return iRet;
1766 }
1767 else if (sd->decomp_state->d_stream.total_in < sourceLen && *total_bytes_read != 0)
1768 sd->decomp_state->stage = HTTP_DECOMP_MID;
1769 else
1770 sd->decomp_state->stage = HTTP_DECOMP_FIN;
1771 return HI_SUCCESS;
1772 }
1773
1774 static inline int hi_server_decompress(HI_SESSION *Session, HttpSessionData *sd, const u_char *ptr,
1775 const u_char *end, URI_PTR *result, bool start_of_body)
1776 {
1777 const u_char *start = ptr;
1778 int rawbuf_size = end - ptr;
1779 int iRet = HI_SUCCESS;
1780 int zRet = HI_SUCCESS;
1781 int compr_depth, decompr_depth;
1782 int compr_bytes_read, decompr_bytes_read;
1783 int compr_avail, decompr_avail;
1784 int total_bytes_read = 0;
1785 static uint32_t updated_chunk_remainder = 0;
1786 static uint32_t chunk_read = 0;
1787 uint32_t saved_chunk_size = 0;
1788
1789 compr_depth = sd->decomp_state->compr_depth;
1790 decompr_depth = sd->decomp_state->decompr_depth;
1791 compr_bytes_read = sd->decomp_state->compr_bytes_read;
1792 decompr_bytes_read = sd->decomp_state->decompr_bytes_read;
1793 saved_chunk_size = sd->resp_state.chunk_remainder;
1794
1795 if(Session->server_conf->unlimited_decompress)
1796 {
1797 compr_avail = compr_depth;
1798 decompr_avail = decompr_depth;
1799 }
1800 else
1801 {
1802 compr_avail = compr_depth-compr_bytes_read;
1803 decompr_avail = decompr_depth - decompr_bytes_read;
1804 }
1805
1806 /* Apply the server extract size
1807 * If the server extract size is set then we need to decompress only upto the
1808 * server flow depth
1809 */
1810 switch ( Session->server_conf->server_extract_size)
1811 {
1812 case -1:
1813 decompr_avail=0;
1814 break;
1815 case 0:
1816 break;
1817 default:
1818 if(sd->resp_state.data_extracted < Session->server_conf->server_extract_size)
1819 {
1820 if(decompr_avail > (Session->server_conf->server_extract_size - sd->resp_state.data_extracted))
1821 decompr_avail = (int)(Session->server_conf->server_extract_size - sd->resp_state.data_extracted);
1822 }
1823 else
1824 {
1825 decompr_avail = 0;
1826 }
1827 break;
1828 }
1829
1830 if ((compr_avail <= 0) || (decompr_avail <= 0))
1831 {
1832 (void)File_Decomp_Reset(sd->fd_state);
1833 ResetGzipState(sd->decomp_state);
1834 ResetRespState(&(sd->resp_state));
1835 return iRet;
1836 }
1837
1838
1839 if(rawbuf_size < compr_avail)
1840 {
1841 compr_avail = rawbuf_size;
1842 }
1843
1844 if(!(sd->resp_state.last_pkt_contlen))
1845 {
1846 if(sd->resp_state.last_pkt_chunked)
1847 {
1848 int cRet = 1;
1849 if(!(sd->decomp_state && sd->decomp_state->stage == HTTP_DECOMP_MID))
1850 {
1851 chunk_read = 0;
1852 updated_chunk_remainder = 0;
1853 cRet = CheckChunkEncoding(Session, start, end, NULL, dechunk_buffer, compr_avail,
1854 sd->resp_state.chunk_remainder, &updated_chunk_remainder, &chunk_read,
1855 sd, HI_SI_SERVER_MODE);
1856 }
1857 if(cRet == 1)
1858 {
1859 sd->resp_state.chunk_remainder = updated_chunk_remainder;
1860 compr_avail = chunk_read;
1861 zRet = uncompress_gzip(Session, decompression_buffer, decompr_avail, dechunk_buffer,
1862 compr_avail, sd, &total_bytes_read, sd->decomp_state->compress_fmt);
1863 }
1864 }
1865 else
1866 {
1867 /* No Content-Length or Transfer-Encoding : chunked */
1868 if(hi_eo_generate_event(Session, HI_EO_SERVER_NO_CONTLEN))
1869 {
1870 hi_eo_server_event_log(Session, HI_EO_SERVER_NO_CONTLEN, NULL, NULL);
1871 }
1872
1873 zRet = uncompress_gzip(Session, decompression_buffer, decompr_avail, ptr, compr_avail,
1874 sd, &total_bytes_read, sd->decomp_state->compress_fmt);
1875 }
1876 }
1877 else
1878 {
1879 zRet = uncompress_gzip(Session, decompression_buffer, decompr_avail, ptr, compr_avail,
1880 sd, &total_bytes_read, sd->decomp_state->compress_fmt);
1881 }
1882 if(!Session->server_conf->unlimited_decompress)
1883 sd->decomp_state->stage = HTTP_DECOMP_FIN;
1884
1885 if((zRet == HI_SUCCESS) || (zRet == HI_NONFATAL_ERR))
1886 {
1887 sd->decomp_state->compr_bytes_read += compr_avail;
1888 hi_stats.compr_bytes_read += compr_avail;
1889
1890 result->uri = decompression_buffer;
1891 if ( total_bytes_read < decompr_avail )
1892 {
1893 result->uri_end = decompression_buffer + total_bytes_read;
1894 sd->decomp_state->decompr_bytes_read += total_bytes_read;
1895 sd->resp_state.data_extracted += total_bytes_read;
1896 hi_stats.decompr_bytes_read += total_bytes_read;
1897 }
1898 else
1899 {
1900 result->uri_end = decompression_buffer + decompr_avail;
1901 sd->decomp_state->decompr_bytes_read += decompr_avail;
1902 sd->resp_state.data_extracted += decompr_avail;
1903 hi_stats.decompr_bytes_read += decompr_avail;
1904 }
1905 }
1906 else
1907 {
1908 if(!sd->decomp_state->decompr_bytes_read)
1909 {
1910 sd->resp_state.chunk_remainder = saved_chunk_size;
1911 iRet = HI_NONFATAL_ERR;
1912 }
1913 else
1914 ResetRespState(&(sd->resp_state));
1915 (void)File_Decomp_Reset(sd->fd_state);
1916 ResetGzipState(sd->decomp_state);
1917 sd->decomp_state->stage = HTTP_DECOMP_FIN;
1918 }
1919
1920 if(zRet!=HI_SUCCESS && start_of_body)
1921 {
1922 if(hi_eo_generate_event(Session, HI_EO_SERVER_DECOMPR_FAILED))
1923 {
1924 hi_eo_server_event_log(Session, HI_EO_SERVER_DECOMPR_FAILED, NULL, NULL);
1925 }
1926 }
1927
1928 return iRet;
1929
1930
1931 }
1932
1933 static inline int hi_server_inspect_body(HI_SESSION *Session, HttpSessionData *sd, const u_char *ptr,
1934 const u_char *end, URI_PTR *result, bool start_of_body)
1935 {
1936 int iRet = HI_SUCCESS;
1937
1938 result->uri =ptr;
1939 result->uri_end = end;
1940 if(!Session || !sd )
1941 {
1942 if ((sd != NULL))
1943 {
1944 (void)File_Decomp_Reset(sd->fd_state);
1945 ResetGzipState(sd->decomp_state);
1946 ResetRespState(&(sd->resp_state));
1947 }
1948 return HI_INVALID_ARG;
1949 }
1950
1951 if((sd->decomp_state != NULL) && sd->decomp_state->decompress_data)
1952 {
1953 iRet = hi_server_decompress(Session, sd, ptr, end, result, start_of_body);
1954 if(iRet == HI_NONFATAL_ERR)
1955 {
1956 sd->resp_state.inspect_body = 1;
1957 result->uri = ptr;
1958 result->uri_end = end;
1959 iRet = hi_server_extract_body(Session, sd, ptr, end, result);
1960 }
1961 }
1962 else
1963 {
1964 result->uri = ptr;
1965 result->uri_end = end;
1966 iRet = hi_server_extract_body(Session, sd, ptr, end, result);
1967 }
1968
1969 return iRet;
1970
1971 }
1972 void ApplyFlowDepth(HTTPINSPECT_CONF *ServerConf, Packet *p,
1973 HttpSessionData *sd, int resp_header_size, int read_only, uint32_t seq_num)
1974 {
1975 if(!ServerConf->server_flow_depth)
1976 {
1977 SetDetectLimit(p, p->dsize);
1978 }
1979 else if(ServerConf->server_flow_depth == -1)
1980 {
1981 SetDetectLimit(p, resp_header_size);
1982 }
1983 else
1984 {
1985 if(sd != NULL)
1986 {
1987 if(!(sd->resp_state.flow_depth_excd ))
1988 {
1989 if(sd->resp_state.max_seq)
1990 {
1991 if(SEQ_GEQ((sd->resp_state.max_seq), seq_num))
1992 {
1993 if(((uint32_t)p->dsize) > (sd->resp_state.max_seq- seq_num))
1994 {
1995 SetDetectLimit(p, (uint16_t)(sd->resp_state.max_seq-seq_num));
1996 if( !read_only )
1997 sd->resp_state.flow_depth_excd = true;
1998 return;
1999 }
2000 else
2001 {
2002 SetDetectLimit(p, p->dsize);
2003 return;
2004 }
2005 }
2006 else
2007 {
2008 if( !read_only )
2009 sd->resp_state.flow_depth_excd = true;
2010 SetDetectLimit(p, resp_header_size);
2011 return;
2012 }
2013 }
2014 else
2015 {
2016 if( !read_only )
2017 sd->resp_state.flow_depth_excd = false;
2018 SetDetectLimit(p, (((ServerConf->server_flow_depth) < p->dsize)? ServerConf->server_flow_depth: p->dsize));
2019 }
2020 }
2021 else
2022 {
2023 SetDetectLimit(p, 0);
2024 return;
2025 }
2026
2027 }
2028 else
2029 {
2030
2031 SetDetectLimit(p, (((ServerConf->server_flow_depth) < p->dsize)? (ServerConf->server_flow_depth): (p->dsize)));
2032 }
2033 }
2034 }
2035
2036 static inline void ResetState (HttpSessionData* sd)
2037 {
2038 (void)File_Decomp_Reset(sd->fd_state);
2039 ResetGzipState(sd->decomp_state);
2040 ResetRespState(&(sd->resp_state));
2041 }
2042
2043 int HttpResponseInspection(HI_SESSION *Session, Packet *p, const unsigned char *data,
2044 int dsize, HttpSessionData *sd)
2045 {
2046 HTTPINSPECT_CONF *ServerConf;
2047 URI_PTR stat_code_ptr;
2048 URI_PTR stat_msg_ptr;
2049 HEADER_PTR header_ptr;
2050 URI_PTR body_ptr;
2051 HI_SERVER *Server;
2052
2053 const u_char *start;
2054 const u_char *end;
2055 const u_char *ptr;
2056 int len;
2057 int iRet = 0;
2058 int resp_header_size = 0;
2059 /* Refers to the stream reassembled packets when reassembly is turned on.
2060 * Refers to all packets when reassembly is turned off.
2061 */
2062 int not_stream_insert = 1;
2063 int parse_cont_encoding = 1;
2064 int status;
2065 int expected_pkt = 0;
2066 int alt_dsize;
2067 uint32_t seq_num = 0;
2068
2069 static uint32_t paf_bytes_total = 0;
2070 static int paf_bytes_curr = 0;
2071 uint32_t paf_bytes_processed = 0;
2072 bool parse_trans_encoding = true;
2073
2074 if (ScPafEnabled())
2075 {
2076 paf_bytes_processed = hi_paf_resp_bytes_processed(p->ssnptr);
2077 paf_bytes_curr = paf_bytes_processed - paf_bytes_total;
2078 paf_bytes_total = paf_bytes_processed;
2079 if (paf_bytes_curr < 0)
2080 paf_bytes_curr = paf_bytes_processed;
2081 }
2082
2083 if (!Session || !p || !data || (dsize == 0))
2084 return HI_INVALID_ARG;
2085
2086 ServerConf = Session->server_conf;
2087 if(!ServerConf)
2088 return HI_INVALID_ARG;
2089
2090
2091 Server = &(Session->server);
2092 clearHttpRespBuffer(Server);
2093
2094 seq_num = GET_PKT_SEQ(p);
2095
2096 if ( ScPafEnabled() )
2097 {
2098 expected_pkt = !PacketHasStartOfPDU(p);
2099 parse_cont_encoding = !expected_pkt;
2100 not_stream_insert = PacketHasPAFPayload(p);
2101 parse_trans_encoding = !hi_paf_disable_te(p->ssnptr, false);
2102
2103 if ( !expected_pkt )
2104 {
2105 simple_response = false;
2106 if ( sd )
2107 {
2108 if (!sd->decomp_state || (sd->decomp_state->stage != HTTP_DECOMP_MID))
2109 ResetState(sd);
2110 }
2111 }
2112 else if ( sd )
2113 {
2114 if(hi_paf_simple_request(p->ssnptr))
2115 {
2116 simple_response = true;
2117 if(!(sd->resp_state.next_seq))
2118 {
2119 /*first simple response packet */
2120 sd->resp_state.next_seq = seq_num + p->dsize;
2121 if(ServerConf->server_flow_depth == -1)
2122 sd->resp_state.flow_depth_excd = true;
2123 else
2124 {
2125 sd->resp_state.flow_depth_excd = false;
2126 sd->resp_state.max_seq = seq_num + ServerConf->server_flow_depth;
2127 }
2128
2129 }
2130 }
2131 else
2132 simple_response = false;
2133
2134 if(ServerConf->server_extract_size)
2135 {
2136 /*Packet is beyond the extract limit*/
2137 if ( sd && (sd->resp_state.data_extracted > ServerConf->server_extract_size ))
2138 {
2139 expected_pkt = 0;
2140 ResetState(sd);
2141 if (sd->decomp_state)
2142 sd->decomp_state->stage = HTTP_DECOMP_FIN;
2143 }
2144 }
2145 }
2146 }
2147 // when PAF is hardened, the following can be removed
2148 else if ( (sd != NULL) )
2149 {
2150 /* If the previously inspected packet in this session identified as a body
2151 * and if the packets are stream inserted wait for reassembled */
2152 if (sd->resp_state.inspect_reassembled)
2153 {
2154 if(p->packet_flags & PKT_STREAM_INSERT)
2155 {
2156 parse_cont_encoding = 0;
2157 not_stream_insert = 0;
2158 }
2159 }
2160 /* If this packet is the next expected packet to be inspected and is out of sequence
2161 * clear out the resp state*/
2162 if(( sd->decomp_state && sd->decomp_state->decompress_data) && parse_cont_encoding)
2163 {
2164 if( sd->resp_state.next_seq &&
2165 (seq_num == sd->resp_state.next_seq) )
2166 {
2167 sd->resp_state.next_seq = seq_num + p->dsize;
2168 expected_pkt = 1;
2169 }
2170 else
2171 {
2172 (void)File_Decomp_Reset(sd->fd_state);
2173 ResetRespState(&(sd->resp_state));
2174 if(sd->decomp_state && sd->decomp_state->stage != HTTP_DECOMP_START)
2175 {
2176 ResetGzipState(sd->decomp_state);
2177 sd->decomp_state->stage = HTTP_DECOMP_FIN;
2178 }
2179 else
2180 ResetGzipState(sd->decomp_state);
2181 }
2182 }
2183 else
2184 if(sd->resp_state.inspect_body && not_stream_insert)
2185 {
2186 /* If the server extrtact size is 0 then we need to check if the packet
2187 * is in sequence
2188 */
2189 if(!ServerConf->server_extract_size)
2190 {
2191 if( sd->resp_state.next_seq &&
2192 (seq_num == sd->resp_state.next_seq) )
2193 {
2194 sd->resp_state.next_seq = seq_num + p->dsize;
2195 expected_pkt = 1;
2196 }
2197 else
2198 {
2199 (void)File_Decomp_Reset(sd->fd_state);
2200 ResetRespState(&(sd->resp_state));
2201 if(sd->decomp_state && sd->decomp_state->stage != HTTP_DECOMP_START)
2202 {
2203 ResetGzipState(sd->decomp_state);
2204 sd->decomp_state->stage = HTTP_DECOMP_FIN;
2205 }
2206 else
2207 ResetGzipState(sd->decomp_state);
2208 }
2209 }
2210 else
2211 {
2212
2213 if( (ServerConf->server_extract_size > 0) &&(sd->resp_state.data_extracted > ServerConf->server_extract_size))
2214 {
2215 expected_pkt = 1;
2216 }
2217 else
2218 {
2219 (void)File_Decomp_Reset(sd->fd_state);
2220 ResetRespState(&(sd->resp_state));
2221 if(sd->decomp_state && sd->decomp_state->stage != HTTP_DECOMP_START)
2222 {
2223 ResetGzipState(sd->decomp_state);
2224 sd->decomp_state->stage = HTTP_DECOMP_FIN;
2225 }
2226 else
2227 ResetGzipState(sd->decomp_state);
2228 }
2229
2230 }
2231
2232 }
2233 }
2234
2235 memset(&stat_code_ptr, 0x00, sizeof(URI_PTR));
2236 memset(&stat_msg_ptr, 0x00, sizeof(URI_PTR));
2237 memset(&header_ptr, 0x00, sizeof(HEADER_PTR));
2238 memset(&body_ptr, 0x00, sizeof(URI_PTR));
2239
2240 start = data;
2241 end = data + dsize;
2242 ptr = start;
2243
2244 /* moving past the CRLF */
2245
2246 while(hi_util_in_bounds(start, end, ptr))
2247 {
2248 if(*ptr < 0x21)
2249 {
2250 if(*ptr < 0x0E && *ptr > 0x08)
2251 {
2252 ptr++;
2253 continue;
2254 }
2255 else
2256 {
2257 if(*ptr == 0x20)
2258 {
2259 ptr++;
2260 continue;
2261 }
2262 }
2263 }
2264
2265 break;
2266 }
2267
2268 /*after doing this we need to basically check for version, status code and status message*/
2269 if(( PacketHasStartOfPDU(p)) &&
2270 ((*ptr != 'H') || *(ptr+1) != 'T' || *(ptr+2) != 'T'
2271 || *(ptr+3) != 'P' || *(ptr+4) != '/'))
2272 {
2273 if( !(ptr = (u_char *)SnortStrcasestr((const char *)ptr, end-ptr, "HTTP/")))
2274 {
2275 CLR_SERVER_HEADER(Server);
2276 return HI_SUCCESS;
2277 }
2278 }
2279
2280 len = end - ptr;
2281
2282 if ( len > 4 )
2283 {
2284 bool header = false;
2285
2286 p->packet_flags |= PKT_HTTP_DECODE;
2287 if(!sd->resp_state.inspect_reassembled ||
2288 !PacketHasPAFPayload(p) ||
2289 PacketHasStartOfPDU(p) ||
2290 !ScPafEnabled())
2291 header = IsHttpVersion(&ptr, end);
2292
2293 if(!header)
2294 {
2295 if(expected_pkt)
2296 ptr = start;
2297 else
2298 {
2299 ApplyFlowDepth(ServerConf, p, sd, resp_header_size, 0, seq_num);
2300 if ( not_stream_insert && (sd != NULL))
2301 {
2302 (void)File_Decomp_Reset(sd->fd_state);
2303 ResetGzipState(sd->decomp_state);
2304 ResetRespState(&(sd->resp_state));
2305 }
2306 CLR_SERVER_HEADER(Server);
2307 return HI_SUCCESS;
2308 }
2309 }
2310 else
2311 {
2312 simple_response = false;
2313 /* This is a next expected packet to be decompressed but the packet is a
2314 * valid HTTP response. So the gzip decompression ends here */
2315 if(expected_pkt)
2316 {
2317 expected_pkt = 0;
2318 (void)File_Decomp_Reset(sd->fd_state);
2319 ResetGzipState(sd->decomp_state);
2320 ResetRespState(&(sd->resp_state));
2321 sd->resp_state.flow_depth_excd = false;
2322 }
2323 while(hi_util_in_bounds(start, end, ptr))
2324 {
2325 if (isspace((int)*ptr))
2326 break;
2327 ptr++;
2328 }
2329
2330 }
2331 }
2332 else if (expected_pkt)
2333 {
2334 ptr = start;
2335 }
2336 else
2337 {
2338 return HI_SUCCESS;
2339 }
2340
2341 /*If this is the next expected packet to be decompressed, send this packet
2342 * decompression */
2343
2344 if (expected_pkt)
2345 {
2346 bool resp_eoh = false;
2347 if (ScPafEnabled() && !sd->resp_state.eoh_found)
2348 {
2349 // check for EOH in this packet
2350 if ((resp_eoh = hi_paf_resp_eoh(p->ssnptr)))
2351 {
2352 sd->resp_state.eoh_found = true;
2353 ptr += paf_bytes_curr; // jump to body offset
2354 if (ptr == end)
2355 return HI_SUCCESS;
2356 }
2357 }
2358
2359 if (hi_util_in_bounds(start, end, ptr))
2360 {
2361 iRet = hi_server_inspect_body(Session, sd, ptr, end, &body_ptr, resp_eoh | !sd->resp_state.data_extracted);
2362 }
2363 }
2364 else
2365 {
2366 iRet = hi_server_extract_status_code(Session, start,ptr,end , &stat_code_ptr);
2367
2368 if ( iRet != HI_OUT_OF_BOUNDS )
2369 {
2370 Server->response.status_code = stat_code_ptr.uri;
2371 Server->response.status_code_size = stat_code_ptr.uri_end - stat_code_ptr.uri;
2372 if ( (int)Server->response.status_code_size <= 0)
2373 {
2374 CLR_SERVER_STAT(Server);
2375 }
2376 else
2377 {
2378 iRet = hi_server_extract_status_msg(start, stat_code_ptr.uri_end ,
2379 end, &stat_msg_ptr);
2380
2381 if ( stat_msg_ptr.uri )
2382 {
2383 Server->response.status_msg = stat_msg_ptr.uri;
2384 Server->response.status_msg_size = stat_msg_ptr.uri_end - stat_msg_ptr.uri;
2385 if ((int)Server->response.status_msg_size <= 0)
2386 {
2387 CLR_SERVER_STAT_MSG(Server);
2388 }
2389 {
2390 header_ptr.range_flag = HTTP_RANGE_NONE;
2391 ptr = hi_server_extract_header(Session, ServerConf, &header_ptr,
2392 stat_msg_ptr.uri_end , end, parse_cont_encoding, parse_trans_encoding, sd );
2393
2394 if (header_ptr.range_flag != HTTP_RANGE_NONE)
2395 {
2396 Server->response.range_flag = header_ptr.range_flag;
2397 }
2398 else
2399 {
2400 Server->response.range_flag = HTTP_RESP_RANGE_NONE;
2401 }
2402 }
2403 }
2404 else
2405 {
2406 CLR_SERVER_STAT(Server);
2407 }
2408 }
2409
2410 if (header_ptr.header.uri)
2411 {
2412 Server->response.header_raw = header_ptr.header.uri;
2413 Server->response.header_raw_size =
2414 header_ptr.header.uri_end - header_ptr.header.uri;
2415 if(!Server->response.header_raw_size)
2416 {
2417 CLR_SERVER_HEADER(Server);
2418 }
2419 else
2420 {
2421 resp_header_size = (header_ptr.header.uri_end - p->data);
2422 hi_stats.resp_headers++;
2423 Server->response.header_norm = header_ptr.header.uri;
2424 if (header_ptr.cookie.cookie)
2425 {
2426 hi_stats.resp_cookies++;
2427 Server->response.cookie.cookie = header_ptr.cookie.cookie;
2428 Server->response.cookie.cookie_end = header_ptr.cookie.cookie_end;
2429 Server->response.cookie.next = header_ptr.cookie.next;
2430 }
2431 else
2432 {
2433 Server->response.cookie.cookie = NULL;
2434 Server->response.cookie.cookie_end = NULL;
2435 Server->response.cookie.next = NULL;
2436 }
2437 if (sd != NULL)
2438 {
2439 if( header_ptr.content_encoding.compress_fmt )
2440 {
2441 hi_stats.gzip_pkts++;
2442
2443 /* We've got gzip data - grab buffer from mempool and attach
2444 * to session data if server is configured to do so */
2445 if (sd->decomp_state == NULL)
2446 SetGzipBuffers(sd, Session, p->ssnptr);
2447
2448 if (sd->decomp_state != NULL)
2449 {
2450 sd->decomp_state->decompress_data = 1;
2451 sd->decomp_state->compress_fmt =
2452 header_ptr.content_encoding.compress_fmt;
2453 }
2454
2455 }
2456 else
2457 {
2458 sd->resp_state.inspect_body = 1;
2459 }
2460
2461 if( ServerConf->file_decomp_modes != 0 )
2462 {
2463 InitFileDecomp(sd, Session,p->ssnptr);
2464 }
2465
2466 sd->resp_state.last_pkt_contlen = (header_ptr.content_len.len != 0);
2467 if(ServerConf->server_flow_depth == -1)
2468 sd->resp_state.flow_depth_excd = true;
2469 else
2470 {
2471 sd->resp_state.flow_depth_excd = false;
2472 sd->resp_state.max_seq = seq_num +
2473 (header_ptr.header.uri_end - start)+ ServerConf->server_flow_depth;
2474 }
2475
2476 if (p->packet_flags & PKT_STREAM_INSERT)
2477 {
2478 if ( ScPafEnabled() )
2479 {
2480 if ( p->packet_flags & PKT_PDU_TAIL )
2481 expected_pkt = 1;
2482 else
2483 sd->resp_state.inspect_reassembled = 1;
2484 }
2485 else if (
2486 header_ptr.content_len.cont_len_start &&
2487 ((uint32_t)(end - (header_ptr.header.uri_end)) >= header_ptr.content_len.len))
2488 {
2489 /* change this when the api is fixed to flush correctly */
2490 //stream_api->response_flush_stream(p);
2491 expected_pkt = 1;
2492 }
2493 else
2494 sd->resp_state.inspect_reassembled = 1;
2495 }
2496 else
2497 {
2498 if(p->packet_flags & PKT_REBUILT_STREAM)
2499 sd->resp_state.inspect_reassembled = 1;
2500
2501 expected_pkt = 1;
2502 }
2503 if(expected_pkt)
2504 {
2505 sd->resp_state.next_seq = seq_num + p->dsize;
2506
2507 if(hi_util_in_bounds(start, end, header_ptr.header.uri_end))
2508 {
2509 iRet = hi_server_inspect_body(Session, sd, header_ptr.header.uri_end,
2510 end, &body_ptr, PacketHasStartOfPDU(p) || !ScPafEnabled());
2511 }
2512 }
2513 }
2514 }
2515 }
2516 else
2517 {
2518 CLR_SERVER_HEADER(Server);
2519
2520 }
2521 }
2522 else
2523 {
2524 CLR_SERVER_STAT(Server);
2525 }
2526 }
2527
2528 if( body_ptr.uri )
2529 {
2530 Server->response.body = body_ptr.uri;
2531 Server->response.body_size = body_ptr.uri_end - body_ptr.uri;
2532 Server->response.body_raw = body_ptr.uri;
2533 Server->response.body_raw_size = body_ptr.uri_end - body_ptr.uri;
2534 if( Server->response.body_size > 0)
2535 {
2536 if ( Server->response.body_size < sizeof(HttpDecodeBuf.data) )
2537 {
2538 alt_dsize = Server->response.body_size;
2539 }
2540 else
2541 {
2542 alt_dsize = sizeof(HttpDecodeBuf.data);
2543 }
2544 /* not checking if sd== NULL as the body_ptr.uri = NULL when sd === NULL in hi_server_inspect_body */
2545 if(sd->decomp_state && sd->decomp_state->decompress_data)
2546 {
2547 status = SafeMemcpy(HttpDecodeBuf.data, Server->response.body,
2548 alt_dsize, HttpDecodeBuf.data, HttpDecodeBuf.data + sizeof(HttpDecodeBuf.data));
2549 if( status != SAFEMEM_SUCCESS )
2550 {
2551 CLR_SERVER_HEADER(Server);
2552 CLR_SERVER_STAT_MSG(Server);
2553 CLR_SERVER_STAT(Server);
2554 return HI_MEM_ALLOC_FAIL;
2555 }
2556
2557 SetHttpDecode((uint16_t)alt_dsize);
2558 Server->response.body = HttpDecodeBuf.data;
2559 Server->response.body_size = HttpDecodeBuf.len;
2560 sd->log_flags |= HTTP_LOG_GZIP_DATA;
2561 }
2562 else
2563 {
2564 if(sd->resp_state.last_pkt_chunked)
2565 {
2566 SetHttpDecode((uint16_t)alt_dsize);
2567 Server->response.body = HttpDecodeBuf.data;
2568 Server->response.body_size = HttpDecodeBuf.len;
2569 }
2570 else
2571 {
2572 Server->response.body_size = alt_dsize;
2573 }
2574 }
2575
2576 if ((get_decode_utf_state_charset(&(sd->utf_state)) != CHARSET_DEFAULT)
2577 || (ServerConf->normalize_javascript && Server->response.body_size))
2578 {
2579 if ( Server->response.body_size < sizeof(HttpDecodeBuf.data) )
2580 {
2581 alt_dsize = Server->response.body_size;
2582 }
2583 else
2584 {
2585 alt_dsize = sizeof(HttpDecodeBuf.data);
2586 }
2587 Server->response.body_size = alt_dsize;
2588 SetHttpDecode((uint16_t)alt_dsize);
2589 }
2590 }
2591
2592 }
2593
2594 #if defined(FEAT_OPEN_APPID)
2595 //copy over extracted headers for appId
2596 if ((ServerConf->appid_enabled))
2597 {
2598 HttpParsedHeaders headers;
2599 memset(&headers, 0, sizeof(headers));
2600 headers.via = header_ptr.via;
2601 headers.server = header_ptr.server;
2602 headers.xWorkingWith = header_ptr.xWorkingWith;
2603 headers.contentType = header_ptr.contentType;
2604 if (Server->response.status_code)
2605 {
2606 headers.responseCode.start = Server->response.status_code;
2607 headers.responseCode.len = Server->response.status_code_size;
2608 }
2609
2610 /*callback into appId with header values extracted. */
2611 CallHttpHeaderProcessors(p, &headers);
2612 free((void*)headers.server.start);
2613 free((void*)headers.via.start);
2614 free((void*)headers.xWorkingWith.start);
2615 }
2616
2617 #endif /* defined(FEAT_OPEN_APPID) */
2618
2619 ApplyFlowDepth(ServerConf, p, sd, resp_header_size, 0, seq_num);
2620
2621 return HI_SUCCESS;
2622 }
2623
2624 int ServerInspection(HI_SESSION *Session, Packet *p, HttpSessionData *hsd)
2625 {
2626 int iRet;
2627
2628 if ((p->data == NULL) || (p->dsize == 0))
2629 {
2630 return HI_INVALID_ARG;
2631 }
2632
2633 #if defined(FEAT_OPEN_APPID)
2634 if ( Session->server_conf->inspect_response || Session->server_conf->appid_enabled)
2635 #else
2636 if ( Session->server_conf->inspect_response )
2637 #endif /* defined(FEAT_OPEN_APPID) */
2638 {
2639 iRet = HttpResponseInspection(Session, p, p->data, p->dsize, hsd);
2640 }
2641 else
2642 {
2643 iRet = IsHttpServerData(Session, p, hsd);
2644 }
2645
2646 if (iRet)
2647 {
2648 return iRet;
2649 }
2650
2651 return HI_SUCCESS;
2652 }
2653
2654 int hi_server_inspection(void *S, Packet *p, HttpSessionData *hsd)
2655 {
2656 HI_SESSION *Session;
2657
2658 int iRet;
2659
2660 if(!S )
2661 {
2662 return HI_INVALID_ARG;
2663 }
2664
2665 Session = (HI_SESSION *)S;
2666
2667 /* IF Server->enable_xff is not enabled, we are not going to do anything related to
2668 Extradata list update. if XFF is not enabled req_id and resp_id will be zero always .
2669 */
2670 if (Session->server_conf->enable_xff)
2671 {
2672 if( ScPafEnabled() )
2673 {
2674 uint8_t find_id;
2675
2676 if(hsd->http_resp_id == XFF_MAX_PIPELINE_REQ)
2677 hsd->http_resp_id = 0;
2678 if( PacketHasStartOfPDU(p) )
2679 {
2680 hsd->http_resp_id++;
2681 if( hsd->tList_count != 0 )
2682 hsd->tList_count--;
2683 }
2684 hsd->is_response = 1;
2685
2686 find_id = (hsd->http_resp_id - 1 );
2687 if( !find_id )
2688 find_id = XFF_MAX_PIPELINE_REQ;
2689
2690 if( (hsd->tList_start != NULL) && ( hsd->tList_start->tID == find_id ) )
2691 deleteNode_tList(hsd);
2692 }
2693 }
2694
2695 /*
2696 ** Let's inspect the server response.
2697 */
2698 iRet = ServerInspection(Session, p, hsd);
2699 if (iRet)
2700 {
2701 return iRet;
2702 }
2703
2704 return HI_SUCCESS;
2705 }