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