1 /* $Id$ */
2 /****************************************************************************
3  *
4  * Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
5  * Copyright (C) 2011-2013 Sourcefire, Inc.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License Version 2 as
9  * published by the Free Software Foundation.  You may not use, modify or
10  * distribute this program under any other version of the GNU General
11  * Public License.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  *
22  ****************************************************************************/
23 
24 //--------------------------------------------------------------------
25 // hi stuff
26 //
27 // @file    hi_paf.c
28 // @author  Russ Combs <rcombs@sourcefire.com>
29 
30 // the goal is to perform the minimal http paf parsing required for
31 // correctness while maintaining loose coupling with hi proper:
32 
33 // * distinguish request from response by presence of http version
34 //   as first token in first header of response
35 // * identify head request so response is understood to not have body
36 // * determine length of body from content-length header
37 // * determine chunking from transfer-endoding header
38 // * extract chunk lengths for body chunks
39 // * determine end of chunks from chunk length of zero
40 
41 // 1XX, 204, or 304 status responses must not have a body per RFC but
42 // if other headers indicate a body is present we will process that.
43 // This is different for head responses because content-length or
44 // transfer-encoding are expected.
45 //
46 // TBD:
47 // * Expect: 100-continue
48 // * Range, Content-Range, and multipart
49 //--------------------------------------------------------------------
50 
51 #ifdef HAVE_CONFIG_H
52 #include "config.h"
53 #endif
54 
55 #include <assert.h>
56 #include <ctype.h>
57 #include <stdio.h>
58 #include <stdint.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <stdbool.h>
62 
63 
64 #include "generators.h"
65 #include "hi_paf.h"
66 #include "hi_eo_events.h"
67 #include "decode.h"
68 #include "snort.h"
69 #include "stream_api.h"
70 #include "snort_debug.h"
71 #include "snort_httpinspect.h"
72 #include "memory_stats.h"
73 
74 #ifdef DEBUG_MSGS
75 #define HI_TRACE     // define for state trace
76 #endif
77 
78 #define HIF_REQ 0x0001  // message is request
79 #define HIF_RSP 0x0002  // message is response
80 #define HIF_LEN 0x0004  // content-length
81 #define HIF_CHK 0x0008  // transfer-encoding: chunked
82 #define HIF_NOB 0x0010  // head (no body in response)
83 #define HIF_NOF 0x0020  // no flush (chunked body follows)
84 #define HIF_V09 0x0040  // simple request version 0.9
85 #define HIF_V10 0x0080  // server response version 1.0
86 #define HIF_V11 0x0100  // server response version 1.1
87 #define HIF_ERR 0x0200  // flag error for deferred abort (at eoh)
88 #define HIF_GET 0x0400  // post (requires content-length or chunks)
89 #define HIF_PST 0x0800  // post (requires content-length or chunks)
90 #define HIF_EOL 0x1000  // already saw at least one eol (for v09)
91 #define HIF_ECD 0x2000  // Content Encoding
92 #define HIF_UPG 0x4000  // Http/2 Upgrade:h2c
93 #define HIF_TST 0x8000  // HTTPv1.0 with chunked header, peek first few bytes of body
94 #define HIF_END 0x10000  //seen end of header
95 #define HIF_ALT 0x20000  // alet for no header
96 #define HIF_CHE 0x40000 //HTTP chunk extension
97 #define HIF_V1X 0x80000  // invalid HTTP 1.XX version
98 
99 enum FlowDepthState
100 {
101     HI_FLOW_DEPTH_STATE_NONE = 0,       //start
102     HI_FLOW_DEPTH_STATE_CONT_LEN,       //content length start
103     HI_FLOW_DEPTH_STATE_CHUNK_START,    //chunked encoding start
104     HI_FLOW_DEPTH_STATE_CHUNK_DISC_SKIP,//chunked encoding discard skip after flow depth
105     HI_FLOW_DEPTH_STATE_CHUNK_DISC_CONT,//chunked encoding discard continue till end of PDU
106 };
107 
108 
109 typedef struct {
110     int flow_depth;
111     uint32_t len;
112     uint32_t flags;
113     uint32_t pipe;
114     uint32_t paf_bytes;
115     uint8_t msg;
116     uint8_t fsm;
117     uint8_t flow_depth_state;
118     uint8_t char_end_of_header;
119     uint8_t junk_chars;
120     bool eoh;
121     bool disable_chunked;
122     bool valid_http;
123     bool fast_blocking;
124 } Hi5State;
125 
126 // config stuff
127 static uint32_t hi_cap = 0;
128 
129 // stats
130 static uint32_t hi_paf_calls = 0;
131 static uint32_t hi_paf_bytes = 0;
132 
133 //Reset flow depth if required ( for chunked transfer encoding )
134 static bool flow_depth_reset = false;
135 
136 static uint8_t hi_paf_id = 0;
137 
138 static bool hi_eoh_found = false;
139 static bool hi_is_chunked = false;
140 
141 #ifdef TARGET_BASED
142 extern int16_t hi_app_protocol_id;
143 #endif
144 
145 #define MAX_JUNK_CHAR_BEFORE_RESP_STATUS 127
146 #define  MAX_CHAR_BEFORE_RESP_STATUS 255
147 #define MAX_CHAR_AFTER_CHUNK_SIZE 255
148 
149 
150 
151 //--------------------------------------------------------------------
152 // char classification stuff per RFC 2616
153 //--------------------------------------------------------------------
154 
155 #define CLASS_ERR 0x00
156 #define CLASS_ANY 0x01
157 #define CLASS_CHR 0x02
158 #define CLASS_TOK 0x04
159 #define CLASS_CRT 0X08
160 #define CLASS_LWS 0x10
161 #define CLASS_SEP 0x20
162 #define CLASS_EOL 0x40
163 #define CLASS_DIG 0x80
164 
165 static const uint8_t class_map[256] =
166 {
167 //                                                         tab    lf                cr
168     0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x33, 0x43, 0x03, 0x03, 0x0B, 0x03, 0x03,
169     0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
170 //
171 //   ' ',  '!',  '"',  '#',  '$',  '%',  '&',  ''',  '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/'
172     0x33, 0x07, 0x23, 0x07, 0x07, 0x07, 0x07, 0x07, 0x23, 0x23, 0x07, 0x07, 0x23, 0x07, 0x07, 0x23,
173 //
174 //   '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',  '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?'
175     0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
176 //
177 //   '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',  'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O'
178     0x23, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
179 //
180 //   'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',  'X',  'Y',  'Z',  '[',  '\',  ']',  '^',  '_'
181     0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x23, 0x23, 0x23, 0x07, 0x07,
182 //
183 //   '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',  'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o'
184     0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
185 //
186 //   'p',  'q',  'r',  's',  't',  'u',  'v',  'w',  'x',  'y',  'z',  '{',  '|',  '}',  '~',  '.'
187     0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x23, 0x07, 0x23, 0x07, 0x03,
188 //
189 //  non-ascii
190     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
191     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
192     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
193     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
194     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
195     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
196     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
197     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
198 };
199 
200 // define class strings with ctl bytes to avoid collision
201 #define LWSS "\1"  // matches LWS (excludes \n)
202 #define EOLS "\2"  // matches LF (excludes \r)
203 #define ANYS "\3"  // matches OCTET; other not used so
204                    // set other = match for consistency
205 #define TOKS "\4"  // matches token
206 #define SEPS "\5"  // matches SEP
207 #define CHRS "\6"  // matches CHAR
208 #define DIGS "\7"  // matches DIGIT
209 #define CRET "\10" // matches CR
210 
is_lwspace(const char c)211 static inline bool is_lwspace(const char c)
212 {
213     return (c == ' ' || c == '\t');
214 }
215 
Classify(const char * s)216 static uint8_t Classify (const char* s)
217 {
218     if ( !strcmp(s, ANYS) )
219         return CLASS_ANY;
220 
221     if ( !strcmp(s, CHRS) )
222         return CLASS_CHR;
223 
224     if ( !strcmp(s, TOKS) )
225         return CLASS_TOK;
226 
227     if ( !strcmp(s, CRET) )
228         return CLASS_CRT;
229 
230     if ( !strcmp(s, LWSS) )
231         return CLASS_LWS;
232 
233     if ( !strcmp(s, SEPS) )
234         return CLASS_SEP;
235 
236     if ( !strcmp(s, EOLS) )
237         return CLASS_EOL;
238 
239     if ( !strcmp(s, DIGS) )
240         return CLASS_DIG;
241 
242     return CLASS_ERR;
243 }
244 
245 //--------------------------------------------------------------------
246 // fsm stuff
247 //--------------------------------------------------------------------
248 
249 typedef enum {
250     ACT_NOP, ACT_NOB,
251     ACT_GET, ACT_PST,
252     ACT_V09, ACT_V10, ACT_V11, ACT_V1X,
253     ACT_SRL, ACT_REQ, ACT_RSP,
254     ACT_SHI, ACT_SHX,
255     ACT_LNB, ACT_LNC, ACT_LN0, ACT_ECD,
256     ACT_CHK, ACT_CK0, ACT_HDR,
257     ACT_H2, ACT_UPG,
258     ACT_JNK
259 } Action;
260 
261 typedef struct {
262     uint8_t action;
263     uint8_t next;
264 } Cell;
265 
266 typedef struct {
267     Cell cell[256];
268 } State;
269 
270 static State* hi_fsm = NULL;
271 static unsigned hi_fsm_size = 0;
272 
273 #define EOL '\n'  // \r is ignored
274 #define LWS ' '   // space or tab
275 
276 typedef struct {
277     uint8_t state;
278     uint8_t match;
279     uint8_t other;
280     uint8_t action;
281     const char* event;
282 } HiRule;
283 
284 // these are just convenient jump points to the start
285 // of blocks; the states MUST match array index
286 // more of these make it easier to insert states
287 #define P0 (0)
288 #define P1 (P0+8)
289 #define P2 (P1+11)
290 #define P3 (P2+2)
291 #define P4 (P3+2)
292 
293 #define Q0 (P4+3)
294 #define Q1 (Q0+15)
295 #define Q2 (Q1+6)
296 #define Q3 (Q2+9)
297 
298 #define R2 (Q3+3)
299 #define R3 (R2+7)
300 #define R4 (R3+5)
301 #define R5 (R4+5)
302 #define R6 (R5+1)
303 #define R7 (R6+4)
304 #define R8 (R7+2)
305 #define R9 (R8+2)
306 #define R10 (R9+1)
307 
308 #define MAX_STATE        (R10+4)
309 #define RSP_START_STATE  (P0)
310 #define REQ_START_STATE  (Q0)
311 #define REQ_V09_STATE_1  (Q1+3)
312 #define REQ_V09_STATE_2  (Q2)
313 #define MSG_CHUNK_STATE  (R7)
314 #define MSG_EOL_STATE    (R10)
315 #define RSP_ABORT_STATE  (P4)
316 #define REQ_ABORT_STATE  (Q3)
317 
318 // -------------------------------------------------------------------
319 // byte-by-byte FSM
320 // -- eliminates need to accumulate a copy of the current token
321 // -- no need to compare prefixes when branching (just current byte)
322 //
323 // however, we must split tokens at branch points to distinguish
324 // between non-match events at the start or later in the string.
325 // also must split whenever the action differs from prior.
326 //
327 // it is possible to eliminate this split but it requires a lot
328 // of additional complexity in the compiler.
329 //
330 // see below for other possible enhancements.
331 // -------------------------------------------------------------------
332 
333 static HiRule hi_rule[] =
334 {
335     // P* are resPonse specific
336     // http version starts response (8 chars)
337     { P0+ 0, P0+ 1, P3   , ACT_SRL, "H"  },
338     { P0+ 1, P0+ 2, P3   , ACT_NOP, "TTP/1." },
339     { P0+ 2, P0+ 5, P0+ 3, ACT_V10, "0"  },
340     { P0+ 3, P0+ 5, P0+ 4, ACT_V11, "1"  },
341     { P0+ 4, P0+ 5, P0+ 6, ACT_V11, DIGS },
342     { P0+ 5, P0+ 5, P0+ 6, ACT_V1X, DIGS },
343     { P0+ 6, P0+ 7, P4   , ACT_NOP, LWSS },
344     { P0+ 7, P0+ 7, P1   , ACT_NOP, LWSS },
345 
346     // now get status code (3 chars)
347     { P1+ 0, P1+ 6, P1+ 1, ACT_NOB, "1"  },
348     { P1+ 1, P1+ 3, P1+ 2, ACT_NOP, "2"  },
349     { P1+ 2, P1+ 3, P1+ 5, ACT_NOP, "3"  },
350     { P1+ 3, P1+ 4, P1+ 6, ACT_NOP, "0"  },
351     { P1+ 4, P1+ 8, P1+ 7, ACT_NOB, "4"  },
352     { P1+ 5, P1+ 6, P1+ 6, ACT_NOP, DIGS },
353     { P1+ 6, P1+ 7, P1+ 7, ACT_NOP, DIGS },
354     { P1+ 7, P1+ 8, P4+ 2, ACT_NOP, DIGS },
355     { P1+ 8, P1+ 10, P1+ 9, ACT_NOP, LWSS },
356     // no status message (http response status code followed by \r\n)
357     { P1+ 9, R2+ 0, P4+ 2, ACT_RSP, EOLS },
358     { P1+ 10, P1+ 10, P2, ACT_NOP, LWSS },
359 
360     // now get status message (variable)
361     { P2+ 0, R2+ 0, P2+ 1, ACT_RSP, EOLS },
362     { P2+ 1, P2+ 0, P2+ 0, ACT_NOP, ANYS },
363 
364     { P3+ 0, P0   , P3+ 1,   ACT_JNK, EOLS },
365     { P3+ 1, P3+ 0, P3+ 0,   ACT_JNK, ANYS },
366 
367     // resync state
368     { P4+ 0, P0   , P4+ 1, ACT_NOP, LWSS },
369     { P4+ 1, P0   , P4+ 2, ACT_NOP, EOLS },
370     { P4+ 2, P4   , P4+ 0, ACT_NOP, ANYS },
371 
372     // Q* are reQuest specific
373     // get method required for 0.9 request
374     { Q0+ 0, Q0+ 1, Q0+ 3, ACT_SRL, "G"  },
375     { Q0+ 1, Q0+ 2, Q0+10, ACT_NOP, "ET" },
376     { Q0+ 2, Q1+ 0, Q0+10, ACT_GET, LWSS },
377     // head method signals no body in response
378     { Q0+ 3, Q0+ 4, Q0+ 6, ACT_SRL, "H"  },
379     { Q0+ 4, Q0+ 5, Q0+10, ACT_NOP, "EAD" },
380     { Q0+ 5, Q1+ 0, Q0+10, ACT_NOB, LWSS },
381     // post method must have content-length or chunks
382     { Q0+ 6, Q0+ 7, Q0+12, ACT_SRL, "P"  },
383     { Q0+ 7, Q0+ 8, Q0+10, ACT_NOP, "O" },
384     { Q0+ 8, Q0+ 9, Q0+13, ACT_NOP, "ST" },
385     { Q0+ 9, Q1+ 0, Q0+13, ACT_PST, LWSS },
386 
387     { Q0+ 10, Q0+ 11, Q0+ 13, ACT_NOP, "R"  },
388     { Q0+ 11, R8+ 1, Q0+ 13, ACT_H2, "I * HTTP/2.0"  }, // R8+0 or R8+1
389 
390     // other method
391     { Q0+ 12, Q0+13, Q3+ 0, ACT_SRL, TOKS },
392     { Q0+ 13, Q0+13, Q0+14, ACT_NOP, TOKS },
393     { Q0+ 14, Q1+ 0, Q3+ 2, ACT_NOP, LWSS },
394 
395     // this gets required URI / next token
396     { Q1+ 0, R10+ 0, Q1+ 1, ACT_NOP, EOLS },
397     { Q1+ 1, Q1+ 0, Q1+ 2, ACT_NOP, LWSS },
398     { Q1+ 2, Q1+ 3, Q1+ 3, ACT_NOP, ANYS },
399     { Q1+ 3, R10+ 0, Q1+ 4, ACT_V09, EOLS },
400     { Q1+ 4, Q2+ 0, Q1+ 5, ACT_NOP, LWSS },
401     { Q1+ 5, Q1+ 3, Q1+ 3, ACT_NOP, ANYS },
402 
403     // this gets version, allowing extra tokens
404     // if start line doesn't end with version,
405     // assume 0.9 SimpleRequest (1 line header)
406     { Q2+ 0, R10+ 0, Q2+ 1, ACT_V09, EOLS },
407     { Q2+ 1, Q2+ 0, Q2+ 2, ACT_NOP, LWSS },
408     { Q2+ 2, Q2+ 3, Q2+ 8, ACT_NOP, "H"  },
409     { Q2+ 3, Q2+ 4, Q2+ 8, ACT_NOP, "TTP/1." },
410     { Q2+ 4, Q2+ 6, Q2+ 5, ACT_V10, "0"  },
411     { Q2+ 5, Q2+ 6, Q2+ 8, ACT_V11, "1"  },
412     { Q2+ 6, R2+ 0, Q2+ 7, ACT_REQ, EOLS },
413     { Q2+ 7, Q2+ 6, Q2+ 8, ACT_NOP, LWSS },
414     { Q2+ 8, Q2+ 0, Q2+ 0, ACT_NOP, ANYS },
415 
416     // resync state
417     { Q3+ 0, Q0   , Q3+ 1, ACT_NOP, LWSS },
418     { Q3+ 1, Q0   , Q3+ 2, ACT_NOP, EOLS },
419     { Q3+ 2, Q3   , Q3+ 0, ACT_NOP, ANYS },
420 
421     // R* are request or response
422     // for simplicity, we don't restrict headers to allowed
423     // direction of transfer (eg cookie only from client).
424     // content-length is optional
425     { R2+ 0, R2+ 1, R3   , ACT_HDR, "C"  },
426     { R2+ 1, R2+ 2, R10  , ACT_NOP, "ONTENT-"   },
427     { R2+ 2, R2+ 4, R2+3 , ACT_NOP, "LENGTH"    },
428     { R2+ 3, R2+ 4, R10  , ACT_ECD, "ENCODING"  },
429     { R2+ 4, R2+ 4, R2+5 , ACT_NOP, LWSS },
430     { R2+ 5, R2+ 6, R10   , ACT_NOP, ":"  },
431     { R2+ 6, R2+ 6, R6   , ACT_LN0, LWSS },
432     // Upgrade parameter
433     { R3+ 0, R3+ 1, R4   , ACT_HDR, "U"  },
434     { R3+ 1, R3+ 2, R10   , ACT_NOP, "PGRADE"  },
435     { R3+ 2, R3+ 2, R3+ 3, ACT_NOP, LWSS },
436     { R3+ 3, R3+ 4, R10   , ACT_NOP, ":"  },
437     { R3+ 4, R3+ 4, R9   , ACT_NOP, LWSS },
438 
439     // transfer-encoding required for chunks
440     { R4+ 0, R4+ 1, R10   , ACT_HDR, "T"  },
441     { R4+ 1, R4+ 2, R10   , ACT_NOP, "RANSFER-ENCODING"  },
442     { R4+ 2, R4+ 2, R4+ 3, ACT_NOP, LWSS },
443     { R4+ 3, R4+ 4, R10   , ACT_NOP, ":"  },
444     { R4+ 4, R4+ 4, R5   , ACT_NOP, LWSS },
445 
446     // only recognized encoding
447     { R5+ 0, R10   , R10   , ACT_CHK, "CHUNKED" },
448 
449     // extract decimal content length
450     { R6+ 0, R2   , R6+ 1, ACT_LNB, EOLS },
451     { R6+ 1, R6   , R6+ 2, ACT_SHI, DIGS },
452     { R6+ 2, R6   , R6+ 3, ACT_SHI, LWSS },
453     { R6+ 3, R10   , R10   , ACT_SHI, ANYS },
454 
455     // extract hex chunk length
456     { R7+ 0, R8   , R7+ 1, ACT_LNC, EOLS },
457     { R7+ 1, R7   , R7   , ACT_SHX, ANYS },
458 
459     // skip to end of line after chunk data
460     { R8+ 0, R7   , R8+ 1, ACT_LN0, EOLS },
461     { R8+ 1, R8   , R8   , ACT_NOP, ANYS },
462 
463     { R9+ 0, R10   , R10   , ACT_UPG, "h2c" },
464 
465     // skip to end of line
466     { R10+ 0, R2    , R10+1  , ACT_NOP, EOLS },
467     { R10+ 1, R10+3 , R10+2  , ACT_NOP, CRET },
468     { R10+ 2, R10   , R10    , ACT_NOP, ANYS },
469     { R10+ 3, R2    , R10+2  , ACT_NOP, EOLS },
470 };
471 static void hi_update_flow_depth_state(Hi5State *hip, uint32_t* fp, PAF_Status *paf );
472 static void get_flow_depth(void *ssn, uint64_t flags, Hi5State *hip);
473 static void get_fast_blocking_status(Hi5State *hip);
474 
475 
476 //--------------------------------------------------------------------
477 // trace stuff
478 //--------------------------------------------------------------------
479 
480 #ifdef HI_TRACE
get_state(int s,char * buf,int max)481 static void get_state (int s, char* buf, int max)
482 {
483     int nbase;
484     const char* sbase;
485 
486     if ( s >= MAX_STATE ) { sbase = "X"; nbase = MAX_STATE; }
487 
488     else if ( s >= R8 ) { sbase = "R8"; nbase = R8; }
489     else if ( s >= R7 ) { sbase = "R7"; nbase = R7; }
490     else if ( s >= R6 ) { sbase = "R6"; nbase = R6; }
491     else if ( s >= R5 ) { sbase = "R5"; nbase = R5; }
492     else if ( s >= R4 ) { sbase = "R4"; nbase = R4; }
493     else if ( s >= R3 ) { sbase = "R3"; nbase = R3; }
494     else if ( s >= R2 ) { sbase = "R2"; nbase = R2; }
495     //else if ( s >= R1 ) { sbase = "R1"; nbase = R1; }
496     //else if ( s >= R0 ) { sbase = "R0"; nbase = R0; }
497 
498     else if ( s >= Q3 ) { sbase = "Q3"; nbase = Q3; }
499     else if ( s >= Q2 ) { sbase = "Q2"; nbase = Q2; }
500     else if ( s >= Q1 ) { sbase = "Q1"; nbase = Q1; }
501     else if ( s >= Q0 ) { sbase = "Q0"; nbase = Q0; }
502 
503     else if ( s >= P4 ) { sbase = "P4"; nbase = P4; }
504     else if ( s >= P3 ) { sbase = "P3"; nbase = P3; }
505     else if ( s >= P2 ) { sbase = "P2"; nbase = P2; }
506     else if ( s >= P1 ) { sbase = "P1"; nbase = P1; }
507     else                { sbase = "P0"; nbase = P0; }
508 
509     snprintf(buf, max, "%s+%d", sbase, (s-nbase));
510 }
511 
512 #if 1
dump_it(int c)513 static inline bool dump_it (int c)
514 {
515     if ( !c ) return false;
516     return ( isupper(c) || isdigit(c) || strchr(":-/. \t\n", c) );
517 }
518 #endif
519 
hi_fsm_dump()520 static void hi_fsm_dump ()
521 {
522 #if 0
523     unsigned i;
524     printf("%s", "s\\e");
525 
526     for ( i = 0; i < 256; i++ )
527     {
528         if ( dump_it(i) )
529         {
530             if ( strchr(" \t\n", i) )
531                 printf(",%02X", i);
532             else
533                 printf(",%c", i);
534         }
535     }
536     printf("\n");
537 
538     for ( i = 0; i < hi_fsm_size; i++ )
539     {
540         unsigned c;
541         char buf[16];
542         get_state(i, buf, sizeof(buf));
543         printf("%s", buf);
544 
545         for ( c = 0; c < 256; c++ )
546         {
547             if ( dump_it(c) )
548             {
549                 get_state(hi_fsm[i].cell[c].next, buf, sizeof(buf));
550                 printf(",%s", buf);
551             }
552         }
553         printf("\n");
554     }
555 #endif
556 }
557 #endif
558 
559 //--------------------------------------------------------------------
560 // fsm build
561 //--------------------------------------------------------------------
562 
563 #define TBD 0xFF
564 
hi_load(State * state,int event,HiRule * rule)565 static void hi_load (State* state, int event, HiRule* rule)
566 {
567     //assert(state->cell[event].next == TBD);
568     state->cell[event].action = rule->action;
569     state->cell[event].next = rule->match;
570 }
571 
hi_reload(State * state,int event,int next)572 static void hi_reload (State* state, int event, int next)
573 {
574     state->cell[event].action = ACT_NOP;
575     state->cell[event].next = next;
576 }
577 
hi_link(State * state,const char * event,HiRule * rule)578 static void hi_link (State* state, const char* event, HiRule* rule)
579 {
580     bool not_done;
581 
582     do
583     {
584         int i;
585         uint8_t class = Classify(event);
586         not_done = strcmp(event, ANYS) != 0;
587 
588         if ( !class )
589         {
590             hi_load(state, toupper(*event), rule);
591             hi_load(state, tolower(*event), rule);
592         }
593         else for ( i = 0; i < 256; i++ )
594         {
595             if ( (state->cell[i].next == TBD) && (class_map[i] & class) )
596             {
597                 hi_load(state, toupper(i), rule);
598                 hi_load(state, tolower(i), rule);
599             }
600         }
601         rule = hi_rule + rule->other;
602         event = rule->event;
603     }
604     while ( not_done );
605 }
606 
hi_relink(State * state,const char * event,int next)607 static void hi_relink (State* state, const char* event, int next)
608 {
609     hi_reload(state, toupper(*event), next);
610     hi_reload(state, tolower(*event), next);
611 }
612 
hi_link_check(void)613 static void hi_link_check (void)
614 {
615     unsigned i, j;
616 
617     for ( i = 0; i < hi_fsm_size; i++ )
618     {
619         for ( j = 0; j < 256; j++ )
620             assert(hi_fsm[i].cell[j].next != TBD);
621 
622         for ( j = 'A'; j <= 'Z'; j++ )
623             assert(hi_fsm[i].cell[j].next == hi_fsm[i].cell[tolower(j)].next);
624     }
625 }
626 
hi_fsm_compile(void)627 static bool hi_fsm_compile (void)
628 {
629     unsigned i = 0, j;
630     unsigned max = sizeof(hi_rule) / sizeof(hi_rule[0]);
631     unsigned next, extra = 0;
632 
633     while ( i < max )
634     {
635         // verify that HiRule.state corresponds to array index
636         // HiRule.state is used solely for this sanity check.
637         assert(hi_rule[i].state == i);
638 
639         if ( strlen(hi_rule[i].event) > 1 )
640             extra += strlen(hi_rule[i].event) - 1;
641 
642         i++;
643     }
644     hi_fsm_size = max + extra;
645     assert(hi_fsm_size < TBD);  // using uint8_t for Cell.next and Hi5State.fsm
646 
647     hi_fsm = SnortPreprocAlloc(1, hi_fsm_size*sizeof(*hi_fsm), PP_HTTPINSPECT,
648                   PP_MEM_CATEGORY_SESSION);
649     if ( hi_fsm == NULL )
650     {
651         DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF, "Unable to allocate memory for hi_fsm."););
652         return false;
653     }
654     next = max;
655 
656     for ( i = 0; i < hi_fsm_size; i++ )
657         for ( j = 0; j < 256; j++ )
658             hi_fsm[i].cell[j].next = TBD;
659 
660     for ( i = 0; i < max; i++ )
661     {
662         int prev = i, j, n = strlen(hi_rule[i].event);
663         const char* event = hi_rule[i].event;
664 
665         hi_link(hi_fsm+i, event, hi_rule+i);
666 
667 #if 0
668         if ( n > 1 )
669             printf("Expanding %s at %u\n", hi_rule[i].event, next);
670 #endif
671 
672         for ( j = 1; j < n; j++ )
673         {
674             event = hi_rule[i].event + j;
675             hi_link(hi_fsm+next, event, hi_rule+i);
676 
677             event = hi_rule[i].event + j - 1;
678             hi_relink(hi_fsm+prev, event, next);
679 
680             prev = next++;
681         }
682     }
683     hi_link_check();
684     assert(max + extra == next);
685     return true;
686 }
687 
688 //--------------------------------------------------------------------
689 // actions
690 //--------------------------------------------------------------------
691 
dton(int c)692 static inline int dton (int c)
693 {
694     return c - '0';
695 }
696 
xton(int c)697 static inline int xton (int c)
698 {
699     if ( isdigit(c) )
700         return c - '0';
701 
702     if ( isupper(c) )
703         return c - 'A' + 10;
704 
705     return c - 'a' + 10;
706 }
707 
hi_paf_event_post()708 static inline void hi_paf_event_post ()
709 {
710     SnortEventqAdd(
711         GENERATOR_SPP_HTTP_INSPECT_CLIENT,
712         HI_EO_CLIENT_UNBOUNDED_POST+1, 1, 0, 3,
713         HI_EO_CLIENT_UNBOUNDED_POST_STR, NULL);
714 }
715 
hi_paf_event_simple()716 static inline void hi_paf_event_simple ()
717 {
718     SnortEventqAdd(
719         GENERATOR_SPP_HTTP_INSPECT_CLIENT,
720         HI_EO_CLIENT_SIMPLE_REQUEST+1, 1, 0, 3,
721         HI_EO_CLIENT_SIMPLE_REQUEST_STR, NULL);
722 }
723 
hi_paf_event_msg_size()724 static inline void hi_paf_event_msg_size ()
725 {
726     SnortEventqAdd(
727         GENERATOR_SPP_HTTP_INSPECT,
728         HI_EO_CLISRV_MSG_SIZE_EXCEPTION+1, 1, 0, 3,
729         HI_EO_CLISRV_MSG_SIZE_EXCEPTION_STR, NULL);
730 }
731 
hi_paf_invalid_chunked()732 static inline void hi_paf_invalid_chunked ()
733 {
734     SnortEventqAdd(
735         GENERATOR_SPP_HTTP_INSPECT,
736         HI_EO_CLISRV_INVALID_CHUNKED_ENCODING+1, 1, 0, 3,
737         HI_EO_CLISRV_INVALID_CHUNKED_EXCEPTION_STR, NULL);
738 }
739 
hi_paf_event_pipe()740 static inline void hi_paf_event_pipe ()
741 {
742     SnortEventqAdd(
743         GENERATOR_SPP_HTTP_INSPECT_CLIENT,
744         HI_EO_CLIENT_PIPELINE_MAX+1, 1, 0, 3,
745         HI_EO_CLIENT_PIPELINE_MAX_STR, NULL);
746 }
747 
hi_paf_response_junk_line()748 static inline void hi_paf_response_junk_line ()
749 {
750     SnortEventqAdd(
751         GENERATOR_SPP_HTTP_INSPECT,
752         HI_EO_SERVER_JUNK_LINE_BEFORE_RESP_HEADER+1, 1, 0, 2,
753         HI_EO_SERVER_JUNK_LINE_BEFORE_RESP_HEADER_STR, NULL);
754 }
755 
hi_paf_response_invalid_chunksize()756 static inline void hi_paf_response_invalid_chunksize ()
757 {
758     SnortEventqAdd(
759         GENERATOR_SPP_HTTP_INSPECT,
760         HI_EO_SERVER_INVALID_CHUNK_SIZE+1, 1, 0, 2,
761         HI_EO_SERVER_INVALID_CHUNK_SIZE_STR, NULL);
762 }
763 
hi_paf_response_invalid_version()764 static inline void hi_paf_response_invalid_version ()
765 {
766     SnortEventqAdd(
767         GENERATOR_SPP_HTTP_INSPECT,
768         HI_EO_SERVER_INVALID_VERSION_RESP_HEADER+1, 1, 0, 2,
769         HI_EO_SERVER_INVALID_VERSION_RESP_HEADER_STR, NULL);
770 }
771 
hi_exec(Hi5State * s,Action a,int c,void * ssn)772 static inline PAF_Status hi_exec (Hi5State* s, Action a, int c, void* ssn)
773 {
774     switch ( a )
775     {
776         case ACT_HDR:
777             break;
778         case ACT_SRL:
779             break;
780         case ACT_NOP:
781             break;
782         case ACT_V09:
783             s->flags |= HIF_REQ|HIF_V09|HIF_ERR;
784             break;
785         case ACT_V10:
786             s->flags |= HIF_V10;
787             break;
788         case ACT_V11:
789             s->flags |= HIF_V11;
790             break;
791         case ACT_V1X:
792             if ( !(s->flags & HIF_V1X) )
793             {
794                 s->flags |= HIF_V1X;
795                 hi_paf_response_invalid_version();
796             }
797             break;
798         case ACT_NOB:
799             s->flags |= HIF_NOB;
800             break;
801         case ACT_GET:
802             s->flags |= HIF_GET;
803             break;
804         case ACT_PST:
805             s->flags |= HIF_PST;
806             break;
807         case ACT_REQ:
808             s->flags |= HIF_REQ;
809             break;
810         case ACT_RSP:
811             s->flags |= HIF_RSP;
812             break;
813         case ACT_SHI:
814             if ( s->flags & HIF_ERR )
815                 break;
816             if ( isdigit(c) && (s->len < 429496728) )
817             {
818                 s->len = (10 * s->len) + dton(c);
819             }
820             else if (!is_lwspace(c))
821             {
822                 hi_paf_event_msg_size();
823                 s->flags |= HIF_ERR;
824             }
825             break;
826         case ACT_SHX:
827             if ( s->flags & HIF_ERR )
828                 break;
829             if ( isxdigit(c) && !(s->len & 0xF8000000) && !(s->flags & HIF_CHE) )
830             {
831                 s->len = (s->len << 4) + xton(c);
832             }
833             else if((s->len & 0xF8000000))
834             {
835                 hi_paf_event_msg_size();
836                 s->flags |= HIF_ERR;
837                 return PAF_FLUSH;
838             }
839             else
840             {
841                 if( !(s->flags & HIF_CHE) )
842                 {
843                     /*A correct chunk extension starts with semi-colon*/
844                     if( c != ';' )
845                     {
846                         hi_paf_response_invalid_chunksize();
847                     }
848                     else
849                     {
850                         s->flags |= HIF_CHE;
851                         s->junk_chars = 1;
852                     }
853                 }
854                 else if( s->flags & HIF_CHE )
855                 {
856                     s->junk_chars++;
857                     if(s->junk_chars >= MAX_CHAR_AFTER_CHUNK_SIZE )
858                     {
859                         s->flags |= HIF_ERR;
860                         s->junk_chars = 0;
861                         return PAF_FLUSH;
862                     }
863                 }
864             }
865             break;
866         case ACT_LNB:
867             s->flags |= HIF_LEN;
868             DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
869                 "%s: lnb=%u\n", __FUNCTION__, s->len);)
870             break;
871         case ACT_ECD:
872            s->flags |=HIF_ECD;
873            break;
874         case ACT_LNC:
875             s->flags |= HIF_LEN;
876             s->flags &= ~HIF_CHE;
877             s->junk_chars = 0;
878             DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
879                 "%s: lnc=%u\n", __FUNCTION__, s->len);)
880             if ( s->len )
881                 return PAF_SKIP;
882             if( s->flags & HIF_NOF )
883                 flow_depth_reset = true;
884             s->flags &= ~HIF_NOF;
885             s->msg = 3;
886             break;
887         case ACT_LN0:
888             s->len = 0;
889             break;
890         case ACT_CHK:
891             s->flags |= HIF_CHK;
892             hi_is_chunked = true;
893             break;
894         case ACT_UPG:
895             DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
896             "%s: Http/1 Connection upgrade possible to Http/2\n", __FUNCTION__);)
897             s->flags |= HIF_UPG;
898             break;
899         case ACT_H2:
900             DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
901             "%s: Http/2 Connection preface received\n", __FUNCTION__);)
902             stream_api->set_session_http2(ssn);
903             return PAF_ABORT;
904         case ACT_CK0:
905             s->flags |= HIF_NOF;
906             s->flags &= ~HIF_CHK;
907             s->fsm = MSG_CHUNK_STATE;
908             s->len = 0;
909             break;
910         case ACT_JNK:
911             if(++(s->junk_chars) >= MAX_JUNK_CHAR_BEFORE_RESP_STATUS)
912             {
913                 s->valid_http = false;
914                 return PAF_ABORT;
915             }
916             break;
917     }
918     return PAF_SEARCH;
919 }
920 
921 //--------------------------------------------------------------------
922 // pipeline
923 //--------------------------------------------------------------------
924 
925 #define MAX_PIPELINE       24
926 #define PIPELINE_RUPTURED 255
927 
hi_pipe_push(Hi5State * s_req,void * ssn)928 static void hi_pipe_push (Hi5State* s_req, void* ssn)
929 {
930     uint32_t nreq = s_req->pipe & 0xFF;
931     uint32_t pipe = s_req->pipe >> 8;
932 
933     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
934         "%s: nreq=%d, pipe=0x%X\n", __FUNCTION__, nreq, pipe);)
935 
936     if ( nreq == MAX_PIPELINE )
937     {
938         if ( stream_api->is_paf_active(ssn, 0) )
939             hi_paf_event_pipe();
940     }
941     else if ( nreq < MAX_PIPELINE )
942     {
943         if ( s_req->flags & HIF_NOB )
944             pipe |= (0x1 << nreq);
945     }
946     if ( nreq == PIPELINE_RUPTURED )
947         return;
948 
949     s_req->pipe = (pipe << 8) | ++nreq;
950 }
951 
hi_pipe_pop(Hi5State * s_rsp,void * ssn)952 static void hi_pipe_pop (Hi5State* s_rsp, void* ssn)
953 {
954     Hi5State* s_req;
955     uint32_t nreq, pipe;
956 
957     void** pv = stream_api->get_paf_user_data(ssn, 1, hi_paf_id);
958 
959     if ( !*pv )
960         return;
961 
962     s_req = *pv;
963 
964     nreq = s_req->pipe & 0xFF;
965     pipe = s_req->pipe >> 8;
966 
967     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
968         "%s: nreq=%d, pipe=0x%X\n", __FUNCTION__, nreq, pipe);)
969 
970     if ( nreq == 0 || nreq == PIPELINE_RUPTURED )
971         return;
972 
973     if ( --nreq < MAX_PIPELINE )
974     {
975         if ( pipe & 0x1 )
976             s_rsp->flags |= HIF_NOB;
977     }
978     pipe >>= 1;
979     s_req->pipe = (pipe << 8) | nreq;
980 }
981 
982 //--------------------------------------------------------------------
983 // control
984 //--------------------------------------------------------------------
985 
simple_allowed(Hi5State * s)986 static inline bool simple_allowed (Hi5State* s)
987 {
988     return ( (s->fsm == REQ_V09_STATE_1 || s->fsm == REQ_V09_STATE_2)
989         && s->flags & HIF_GET );
990 }
991 
have_pdu(Hi5State * s)992 static inline bool have_pdu (Hi5State* s)
993 {
994     return ( s->fsm != REQ_START_STATE && s->fsm != RSP_START_STATE );
995 }
996 
paf_abort(Hi5State * s)997 static inline bool paf_abort (Hi5State* s)
998 {
999     return ( s->fsm == REQ_ABORT_STATE || s->fsm == RSP_ABORT_STATE );
1000 }
1001 
1002 // this is the 2nd step of stateful scanning, which executes
1003 // the fsm.
hi_scan_fsm(Hi5State * s,int c,void * ssn)1004 static PAF_Status hi_scan_fsm (Hi5State* s, int c, void* ssn)
1005 {
1006     PAF_Status status;
1007     State* m = hi_fsm + s->fsm;
1008     Cell* cell = &m->cell[c];
1009 
1010 #ifdef HI_TRACE
1011     char before[16], after[16];
1012     uint8_t prev = s->fsm;
1013 #endif
1014 
1015     s->fsm = cell->next;
1016 
1017 #ifdef HI_TRACE
1018     get_state(prev, before, sizeof(before));
1019     get_state(s->fsm, after, sizeof(after));
1020     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
1021         "%s: %s(%u)[0x%2X, '%c'] -> %d,%s(%u)\n",
1022         __FUNCTION__, before, prev, c, isgraph(c) ? c : '.',
1023         cell->action, after, s->fsm);)
1024 #endif
1025 
1026     status = hi_exec(s, cell->action, c, ssn);
1027 
1028     return status;
1029 }
1030 
hi_eoh(Hi5State * s,void * ssn)1031 static PAF_Status hi_eoh (Hi5State* s, void* ssn)
1032 {
1033     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
1034         "%s: flags=0x%X, len=%u\n", __FUNCTION__, s->flags, s->len);)
1035 
1036     s->eoh = true;
1037     s->flags &= ~HIF_ALT;
1038 
1039     if ( (s->flags & HIF_REQ) )
1040         hi_pipe_push(s, ssn);
1041     else
1042         hi_pipe_pop(s, ssn);
1043 
1044     if( (s->flags & HIF_V10 &&
1045         s->flags & HIF_CHK))
1046     {
1047         hi_paf_invalid_chunked();
1048         s->flags |= HIF_TST;
1049     }
1050     if ( (s->flags & HIF_PST) &&
1051         !(s->flags & (HIF_CHK|HIF_LEN)) )
1052     {
1053         hi_paf_event_post();
1054         s->flags |= HIF_ERR;
1055     }
1056 
1057     if( s->junk_chars && (s->flags & HIF_RSP))
1058     {
1059         hi_paf_response_junk_line();
1060         s->junk_chars = 0;
1061     }
1062 
1063     if ( (s->flags & HIF_ERR) ||
1064         ((s->flags & HIF_NOB) && (s->flags & HIF_RSP))
1065     ) {
1066         if ( s->flags & HIF_V09 )
1067             hi_paf_event_simple();
1068 
1069         hi_exec(s, ACT_LN0, 0, ssn);
1070         return PAF_FLUSH;
1071     }
1072     if ( s->flags & HIF_CHK )
1073     {
1074         if( !(s->flags & HIF_TST) )
1075         {
1076             uint32_t fp;
1077             PAF_Status paf = PAF_SEARCH;
1078             hi_exec(s, ACT_CK0, 0, ssn);
1079             hi_update_flow_depth_state(s, &fp, &paf );
1080             return paf;
1081         }
1082          return PAF_SEARCH;
1083     }
1084     if ( (s->flags & (HIF_REQ|HIF_LEN)) )
1085         return PAF_FLUSH;
1086 
1087     if ( (s->flags & HIF_V11) && (s->flags & HIF_RSP) )
1088     {
1089         hi_exec(s, ACT_LN0, 0, ssn);
1090         hi_paf_event_msg_size();
1091         return PAF_FLUSH;
1092     }
1093     if ( (s->flags & HIF_V10) && (s->flags & HIF_ECD) && !(s->flags & HIF_LEN))
1094         return PAF_FLUSH;
1095     return PAF_ABORT;
1096 }
1097 
1098 // http messages are scanned statefully, char-by-char, in
1099 // two steps.  this is the 1st step, which figures out
1100 // end-of-line (eol) and end-of-headers (eoh) from the byte
1101 // stream.  also unfolds headers before fsm scanning.  this
1102 // simplified version ignores \r (in the spirit of send strict,
1103 // recv tolerant, but it would only take 2 more states to check
1104 // for \r).  the 2nd step is hi_scan_fsm().
hi_scan_msg(Hi5State * s,int c,uint32_t * fp,void * ssn)1105 static inline PAF_Status hi_scan_msg (
1106     Hi5State* s, int c, uint32_t* fp, void* ssn)
1107 {
1108     PAF_Status paf = PAF_SEARCH;
1109     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
1110         "%s[%d]: 0x%2X, '%c'\n", __FUNCTION__, s->msg, c, isgraph(c) ? c : '.');)
1111 
1112     if ( c == '\r' )
1113     {
1114         if ( hi_is_chunked  && hi_eoh_found  )
1115         {
1116             hi_paf_response_invalid_chunksize();
1117             hi_is_chunked = false;
1118         }
1119         hi_eoh_found = false;
1120 
1121         if ( s->msg != 1 && s->msg != 5 )
1122         {
1123             *fp = 0;
1124             return paf;
1125         }
1126     }
1127     hi_eoh_found = false;
1128 
1129     switch ( s->msg )
1130     {
1131     case 0:
1132         if ( c == '\n' )
1133         {
1134             if ( !(s->flags & HIF_EOL) )
1135             {
1136                 s->flags |= HIF_EOL;
1137 
1138                 if ( simple_allowed(s) )
1139                 {
1140                     hi_scan_fsm(s, EOL, ssn);
1141                     paf = hi_eoh(s, ssn);
1142                 }
1143                 else
1144                     s->msg = 1;
1145             }
1146             else if ( s->flags & HIF_NOF )
1147                 paf = hi_scan_fsm(s, EOL, ssn);
1148             else
1149                 s->msg = 1;
1150         }
1151         else
1152             paf = hi_scan_fsm(s, c, ssn);
1153         break;
1154 
1155     case 1:
1156         if ( c == '\n' )
1157         {
1158             hi_scan_fsm(s, EOL, ssn);
1159 
1160             if ( have_pdu(s) )
1161                 paf = hi_eoh(s, ssn);
1162             s->msg = 0;
1163         }
1164         else if (c == ' ' || c == '\t' || c == 0xb || c == 0xc)
1165         {
1166             if(s->fsm == MSG_EOL_STATE)
1167             {
1168              /* This s->char_end_of_header + 3 to avoid \n\r\n and \n\r\r\n */
1169                s->char_end_of_header = s->char_end_of_header + 3;
1170                hi_scan_fsm(s, c, ssn);
1171                s->msg = 5;
1172             }
1173             else if(!(s->flags & HIF_RSP))
1174         {
1175             paf = hi_scan_fsm(s, LWS, ssn);
1176             s->msg = 0;
1177         }
1178             else
1179             {
1180                *fp = 0;
1181                return paf;
1182             }
1183         }
1184         else if ( c == '\r')
1185         {
1186             if(s->fsm == MSG_EOL_STATE)
1187             {
1188                s->char_end_of_header++;
1189                hi_scan_fsm(s, c, ssn);
1190                s->msg = 5;
1191             }
1192             else
1193             {
1194                *fp = 0;
1195                return paf;
1196             }
1197         }
1198         else
1199         {
1200             paf = hi_scan_fsm(s, EOL, ssn);
1201 
1202             if ( paf == PAF_SEARCH )
1203                 paf = hi_scan_fsm(s, c, ssn);
1204             s->msg = 0;
1205         }
1206         break;
1207 
1208     case 3:
1209         if ( c == '\n' )
1210             paf = hi_eoh(s, ssn);
1211         else
1212         {
1213             s->msg = 4;
1214         }
1215         break;
1216 
1217     case 4:
1218         if ( c == '\n' )
1219         {
1220             s->msg = 3;
1221         }
1222         break;
1223     case 5:
1224         if ( c == '\n' )
1225         {
1226             hi_eoh_found = true;
1227             if ( s->char_end_of_header >= 2)
1228                s->flags |= HIF_END;
1229             s->char_end_of_header =0;
1230             hi_scan_fsm(s, EOL, ssn);
1231 
1232             if ( have_pdu(s) )
1233                 paf = hi_eoh(s, ssn);
1234             s->msg = 0;
1235         }
1236         else if (c == '\r' ||c == '\t' || c == ' ' ||  c == 0xb || c == 0xc)
1237         {
1238           s->char_end_of_header++;
1239           hi_scan_fsm(s, c, ssn);
1240         }
1241         else
1242         {
1243             s->msg = 0;
1244             hi_scan_fsm(s, c, ssn);
1245             s->char_end_of_header =0;
1246         }
1247         break;
1248 
1249     }
1250 
1251     if ( paf_abort(s) )
1252         paf = PAF_ABORT;
1253 
1254     else if ( paf != PAF_SEARCH ) {
1255         /*
1256          * If the flush point is set based on content-length,
1257          * then the payload is eligible for pseudo flush for
1258          * early detection and file processing.
1259          */
1260         *fp = s->len;
1261         if (paf == PAF_FLUSH && s->fast_blocking &&
1262             !(stream_api->is_session_decrypted(ssn)) )
1263         {
1264             paf = PAF_PSEUDO_FLUSH_SEARCH;
1265         }
1266     }
1267 
1268     if( paf != PAF_SEARCH && paf != PAF_ABORT )
1269     {
1270         hi_update_flow_depth_state(s, fp, &paf );
1271     }
1272 
1273     return paf;
1274 }
1275 
1276 //--------------------------------------------------------------------
1277 // utility
1278 //--------------------------------------------------------------------
1279 
hi_reset(Hi5State * s,uint64_t flags,void * ssn)1280 static void hi_reset (Hi5State* s, uint64_t flags, void *ssn)
1281 {
1282     s->len = s->msg = 0;
1283 
1284     if ( flags & PKT_FROM_CLIENT )
1285     {
1286         s->fsm = REQ_START_STATE;
1287     }
1288     else
1289     {
1290         s->fsm = RSP_START_STATE;
1291     }
1292     s->flags = 0;
1293     s->junk_chars = 0;
1294     s->flow_depth_state = HI_FLOW_DEPTH_STATE_NONE;
1295 
1296     if( flow_depth_reset )
1297     {
1298         get_flow_depth(ssn, flags, s);
1299         flow_depth_reset = false;
1300     }
1301 
1302     get_fast_blocking_status(s);
1303 
1304     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
1305         "%s: fsm=%u, flags=0x%X\n", __FUNCTION__, s->fsm, s->flags);)
1306 }
1307 
1308 /*peek into first few bytes of response body to guess if this is chunked.*/
peek_chunk_size(const uint8_t * data,uint32_t len)1309 bool  peek_chunk_size(const uint8_t* data, uint32_t len)
1310 {
1311     bool chunk_len = false;
1312     int n = 0;
1313     while ( n < len )
1314     {
1315         char c = data[n];
1316         if(isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
1317         {
1318             chunk_len = true;
1319             //Too big a chunk, probably not chunk size
1320             if( n > 8 )
1321                 return false;
1322         }
1323         else if( c == '\r' || c == '\n' || c == ';')
1324         {
1325             if( chunk_len )
1326                 return true;
1327             else
1328                 return false;
1329         }
1330         else
1331         {
1332             //other character,cant be chunk size
1333             return false;
1334         }
1335         n++;
1336     }
1337     return true;
1338 }
1339 
1340 //--------------------------------------------------------------------
1341 // callback for stateful scanning of in-order raw payload
1342 //--------------------------------------------------------------------
1343 
hi_paf(void * ssn,void ** pv,const uint8_t * data,uint32_t len,uint64_t * flags,uint32_t * fp,uint32_t * fp_eoh)1344 static PAF_Status hi_paf (
1345     void* ssn, void** pv, const uint8_t* data, uint32_t len,
1346     uint64_t *flags, uint32_t* fp, uint32_t *fp_eoh)
1347 {
1348     Hi5State* hip = *pv;
1349     PAF_Status paf = PAF_SEARCH;
1350 
1351     uint32_t n = 0;
1352     *fp = 0;
1353 
1354 #ifdef TARGET_BASED
1355     if ( session_api )
1356     {
1357         int16_t proto_id = session_api->get_application_protocol_id(ssn);
1358 
1359         if ( (proto_id > 0) && (proto_id != hi_app_protocol_id) )
1360         {
1361              DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
1362                         "%s: its not HTTP, no need to detect\n",
1363                          __FUNCTION__);)
1364              return PAF_ABORT;
1365         }
1366     }
1367 #endif
1368 
1369     if ( !hip )
1370     {
1371         // beware - we allocate here but s5 calls free() directly
1372         // so no pointers allowed
1373         hip = SnortPreprocAlloc(1, sizeof(Hi5State), PP_HTTPINSPECT,
1374                    PP_MEM_CATEGORY_SESSION);
1375 
1376         if ( !hip )
1377         {
1378             *flags |= PKT_H1_ABORT;
1379             return PAF_ABORT;
1380         }
1381         *pv = hip;
1382 
1383         flow_depth_reset = true;
1384 
1385         hi_reset(hip, *flags, ssn );
1386     }
1387 
1388     hip->eoh = false;
1389 hip->valid_http = true;
1390     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
1391         "%s: len=%u\n", __FUNCTION__, len);)
1392 
1393     if ( hip->flags & HIF_ERR )
1394     {
1395         *flags |= PKT_H1_ABORT;
1396         return PAF_ABORT;
1397     }
1398 
1399     if ( hi_cap && (hi_paf_bytes > hi_cap) )
1400     {
1401         *flags |= PKT_H1_ABORT;
1402         return PAF_ABORT;
1403     }
1404 
1405     hi_update_flow_depth_state(hip, fp, &paf);
1406 
1407     if( paf != PAF_SEARCH )
1408     {
1409         hi_paf_calls++;
1410 
1411         if ( paf == PAF_DISCARD_END )
1412             hi_reset(hip, *flags, ssn);
1413 
1414         hi_paf_bytes += *fp;
1415 
1416         if (paf == PAF_ABORT)
1417             *flags |= PKT_H1_ABORT;
1418         return paf;
1419     }
1420 
1421 
1422     while ( n < len )
1423     {
1424         if( !(hip->flags & HIF_ERR) && hip->flags & HIF_TST )
1425         {
1426             if( n != len )
1427             {
1428                 if(peek_chunk_size(data + n, len - n))
1429                 {
1430                     hi_exec(hip, ACT_CK0, 0, ssn);
1431                     hip->flags &= ~HIF_LEN;
1432                     hi_update_flow_depth_state(hip, fp, &paf );
1433                 }
1434                 else
1435                 {
1436                      if( hip->flags & HIF_LEN )
1437                      {
1438                           paf = PAF_FLUSH;
1439                           *fp = hip->len;
1440                           hip->flags &= ~HIF_CHK;
1441                           hi_update_flow_depth_state(hip, fp, &paf );
1442                      }
1443                      else
1444                      {
1445                          if ( (hip->flags & HIF_PST))
1446                              hi_paf_event_post();
1447                          paf = PAF_ABORT;
1448                      }
1449                      hip->disable_chunked = true;
1450                 }
1451                 hip->flags &= ~HIF_TST;
1452             }
1453         }
1454 
1455         // jump ahead to next linefeed when possible
1456         if ( (hip->msg == 0) && (hip->fsm == MSG_EOL_STATE) )
1457         {
1458             uint8_t* lf = memchr(data+n, '\n', len-n);
1459             if ( !lf )
1460             {
1461                 n = len;
1462                 break;
1463             }
1464             n += (lf - (data + n));
1465         }
1466         if( paf == PAF_SEARCH)
1467         {
1468             paf = hi_scan_msg(hip, data[n++], fp, ssn);
1469             if( (hip->flags & HIF_RSP) && hip->flags &  HIF_END)
1470             {
1471                 SnortEventqAdd(
1472                 GENERATOR_SPP_HTTP_INSPECT,
1473                 HI_EO_SERVER_NO_RESP_HEADER_END+1, 1, 0, 2,
1474                 HI_EO_SERVER_NO_RESP_HEADER_END_STR, NULL);
1475                 hip->flags |= HIF_ALT;
1476                 hip->flags &= ~HIF_END;
1477                 hip->char_end_of_header = 0;
1478             }
1479             if (hip->flags & HIF_ALT)
1480             {
1481                 hip->char_end_of_header++;
1482                 if (hip->char_end_of_header  >= MAX_CHAR_BEFORE_RESP_STATUS )
1483                 {
1484                       paf = PAF_ABORT;
1485                       hip->flags &= ~HIF_ALT;
1486                       hip->char_end_of_header = 0;
1487                  }
1488              }
1489         }
1490 
1491         if ( hip->flags & HIF_UPG)
1492         {
1493             *flags |= PKT_UPGRADE_PROTO;
1494             //Before a client preface is received server can start
1495             //sending messages. From next packet h2_paf will take over.
1496             if (*flags & PKT_FROM_SERVER)
1497             {
1498                 stream_api->set_session_http2(ssn);
1499                 stream_api->set_session_http2_upg(ssn);
1500             }
1501         }
1502 
1503         if ( paf != PAF_SEARCH )
1504         {
1505             if ( hip->flags & HIF_ERR )
1506             {
1507                 *fp = len;
1508                 break;
1509             }
1510 
1511             *fp += n;
1512             if( hip->eoh && (( hip->flags & HIF_PST ))){
1513                 *fp_eoh = n;
1514                 if(*fp <= len )
1515                     *fp_eoh = 0;
1516             }
1517 
1518             if ( paf != PAF_SKIP && paf != PAF_DISCARD_START )
1519                 hi_reset(hip, *flags, ssn);
1520 
1521             if ( hip->fast_blocking && (paf == PAF_SKIP) &&
1522                  !(stream_api->is_session_decrypted(ssn)) )
1523                 paf = PAF_PSEUDO_FLUSH_SKIP;
1524 
1525             break;
1526         }
1527     }
1528 
1529     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
1530         "%s: paf=%d, rfp=%u\n", __FUNCTION__, paf, *fp);)
1531 
1532     hi_paf_calls++;
1533     hi_paf_bytes += n;
1534 
1535     hip->paf_bytes += n;
1536     if ( paf == PAF_ABORT)
1537         *flags |= PKT_H1_ABORT;
1538 
1539     return paf;
1540 }
1541 
1542 //--------------------------------------------------------------------
1543 // public stuff
1544 //--------------------------------------------------------------------
1545 
hi_paf_register_port(struct _SnortConfig * sc,uint16_t port,bool client,bool server,tSfPolicyId pid,bool auto_on)1546 int hi_paf_register_port (
1547     struct _SnortConfig *sc, uint16_t port, bool client, bool server, tSfPolicyId pid, bool auto_on)
1548 {
1549     if ( !ScPafEnabled() )
1550         return 0;
1551 
1552     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
1553         "%s: policy %u, port %u\n", __FUNCTION__, pid, port);)
1554 
1555     if ( !stream_api )
1556         return -1;
1557 
1558     if ( client )
1559         hi_paf_id = stream_api->register_paf_port(sc, pid, port, true, hi_paf, auto_on);
1560 
1561     if ( server )
1562         hi_paf_id = stream_api->register_paf_port(sc, pid, port, false, hi_paf, auto_on);
1563 
1564     return 0;
1565 }
1566 
hi_paf_cleanup(void * pafData)1567 static void hi_paf_cleanup(void *pafData)
1568 {
1569     if (pafData)
1570         SnortPreprocFree(pafData, sizeof(Hi5State), PP_HTTPINSPECT, PP_MEM_CATEGORY_SESSION);
1571 }
1572 
hi_paf_register_service(struct _SnortConfig * sc,uint16_t service,bool client,bool server,tSfPolicyId pid,bool auto_on)1573 int hi_paf_register_service (
1574     struct _SnortConfig *sc, uint16_t service, bool client, bool server, tSfPolicyId pid, bool auto_on)
1575 {
1576     if ( !ScPafEnabled() )
1577         return 0;
1578 
1579     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
1580         "%s: policy %u, service %u\n", __FUNCTION__, pid, service);)
1581 
1582     if ( !stream_api )
1583         return -1;
1584 
1585     if ( client )
1586     {
1587         hi_paf_id = stream_api->register_paf_service(sc, pid, service, true, hi_paf, auto_on);
1588         stream_api->register_paf_free(hi_paf_id, hi_paf_cleanup);
1589     }
1590     if ( server )
1591     {
1592         hi_paf_id = stream_api->register_paf_service(sc, pid, service, false, hi_paf, auto_on);
1593         stream_api->register_paf_free(hi_paf_id, hi_paf_cleanup);
1594     }
1595     return 0;
1596 }
1597 
1598 //--------------------------------------------------------------------
1599 
hi_paf_init(uint32_t cap)1600 bool hi_paf_init (uint32_t cap)
1601 {
1602     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
1603         "%s: cap=%u\n",  __FUNCTION__, cap);)
1604 
1605     hi_cap = cap;
1606 
1607     if ( !hi_fsm_compile() )
1608         return false;
1609 
1610 #ifdef HI_TRACE
1611     hi_fsm_dump();
1612 #endif
1613 
1614     return true;
1615 }
1616 
hi_paf_term(void)1617 void hi_paf_term (void)
1618 {
1619     SnortPreprocFree(hi_fsm, hi_fsm_size*sizeof(*hi_fsm), PP_HTTPINSPECT,
1620          PP_MEM_CATEGORY_SESSION);
1621     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
1622         "%s: calls=%u, bytes=%u\n",  __FUNCTION__,
1623         hi_paf_calls, hi_paf_bytes);)
1624 
1625     hi_fsm = NULL;
1626     hi_fsm_size = 0;
1627 }
1628 
1629 //--------------------------------------------------------------------
1630 
hi_paf_simple_request(void * ssn)1631 bool hi_paf_simple_request (void* ssn)
1632 {
1633     if ( ssn )
1634     {
1635         Hi5State** s = (Hi5State **)stream_api->get_paf_user_data(ssn, 1, hi_paf_id);
1636 
1637         if ( s && *s )
1638             return ( (*s)->flags & HIF_V09 );
1639     }
1640     return false;
1641 }
1642 
get_fast_blocking_status(Hi5State * hip)1643 static void get_fast_blocking_status(Hi5State *hip)
1644 {
1645     hip->fast_blocking = GetHttpFastBlockingStatus();
1646 }
1647 
get_flow_depth(void * ssn,uint64_t flags,Hi5State * hip)1648 static void  get_flow_depth(void *ssn, uint64_t flags, Hi5State *hip)
1649 {
1650     hip->flow_depth = GetHttpFlowDepth(ssn, flags);
1651 }
1652 
hi_update_flow_depth_state(Hi5State * hip,uint32_t * fp,PAF_Status * paf)1653 static void hi_update_flow_depth_state(Hi5State *hip, uint32_t* fp, PAF_Status *paf )
1654 {
1655     //sanity check
1656     assert( hip );
1657 
1658     if( !hip->flow_depth )
1659         return;
1660 
1661     if ( (hip->flags & HIF_ERR) ||
1662             ((hip->flags & HIF_NOB) ))
1663         return;
1664 
1665     if( hip->flags & HIF_V09 )
1666         return;
1667 
1668     //Apply flow depth for POST in case of request
1669     if( (hip->flags & HIF_REQ ) &&
1670             !(hip->flags & HIF_PST) )
1671         return;
1672 
1673     switch( hip->flow_depth_state )
1674     {
1675         case HI_FLOW_DEPTH_STATE_NONE:
1676             if( hip->flags & HIF_LEN )
1677             {
1678                 if( hip->len > 0 )
1679                 {
1680                     if( hip->flow_depth == -1 )
1681                     {
1682                         *paf = PAF_DISCARD_START;
1683                         *fp = 0;
1684                         hip->flow_depth_state = HI_FLOW_DEPTH_STATE_CONT_LEN;
1685                     }
1686                     else if( hip->flow_depth > 0 )
1687                     {
1688                         if( hip->len > hip->flow_depth )
1689                         {
1690                             *paf = PAF_DISCARD_START;
1691                             *fp = hip->flow_depth;
1692                             hip->flow_depth_state = HI_FLOW_DEPTH_STATE_CONT_LEN;
1693                         }
1694                     }
1695                 }
1696             }
1697             else if( hip->flags & HIF_NOF )
1698             {
1699                 if( hip->flow_depth == -1 )
1700                 {
1701                     *paf = PAF_DISCARD_START;
1702                     *fp = 0;
1703                     hip->flow_depth_state = HI_FLOW_DEPTH_STATE_CHUNK_DISC_CONT;
1704                 }
1705                 else if( hip->flow_depth > 0 )
1706                 {
1707                     hip->flow_depth_state = HI_FLOW_DEPTH_STATE_CHUNK_START;
1708                 }
1709 
1710             }
1711             break;
1712 
1713         case HI_FLOW_DEPTH_STATE_CONT_LEN:
1714             *paf = PAF_DISCARD_END;
1715             if( hip->flow_depth > 0 )
1716                 *fp = hip->len - hip->flow_depth;
1717             else
1718                 *fp = hip->len;
1719             break;
1720 
1721         case HI_FLOW_DEPTH_STATE_CHUNK_START:
1722             if( ( *paf == PAF_SKIP ) && ( hip->flow_depth <= hip->len ) )
1723             {
1724                 *paf = PAF_DISCARD_START;
1725                 *fp = hip->flow_depth;
1726                 if( hip->flow_depth == hip->len )
1727                     hip->flow_depth_state = HI_FLOW_DEPTH_STATE_CHUNK_DISC_CONT;
1728                 else
1729                     hip->flow_depth_state = HI_FLOW_DEPTH_STATE_CHUNK_DISC_SKIP;
1730                 hip->len -= hip->flow_depth;
1731             }
1732             else if( *paf == PAF_SKIP )
1733             {
1734                 hip->flow_depth -= hip->len;
1735             }
1736             break;
1737 
1738         case HI_FLOW_DEPTH_STATE_CHUNK_DISC_SKIP:
1739             if( hip->len )
1740             {
1741                 *paf = PAF_SKIP;
1742                 *fp = hip->len;
1743                 hip->len = 0;
1744             }
1745             hip->flow_depth_state = HI_FLOW_DEPTH_STATE_CHUNK_DISC_CONT;
1746             break;
1747 
1748         case HI_FLOW_DEPTH_STATE_CHUNK_DISC_CONT:
1749             if( !( hip->flags & HIF_NOF) )
1750             {
1751                 *paf = PAF_DISCARD_END;
1752             }
1753             break;
1754 
1755         default:
1756             break;
1757     }
1758 }
1759 
hi_paf_resp_eoh(void * ssn)1760 bool hi_paf_resp_eoh(void* ssn)
1761 {
1762     if ( ssn )
1763     {
1764         Hi5State** s = (Hi5State **)stream_api->get_paf_user_data(ssn, 0, hi_paf_id);
1765 
1766         if ( s && *s )
1767             return ( (*s)->eoh );
1768     }
1769     return false;
1770 }
1771 
hi_paf_resp_bytes_processed(void * ssn)1772 uint32_t hi_paf_resp_bytes_processed(void* ssn)
1773 {
1774     if ( ssn )
1775     {
1776         Hi5State** s = (Hi5State **)stream_api->get_paf_user_data(ssn, 0, hi_paf_id);
1777 
1778         if ( s && *s )
1779             return ( (*s)->paf_bytes );
1780     }
1781     return 0;
1782 }
1783 
hi_paf_valid_http(void * ssn)1784 bool hi_paf_valid_http(void* ssn)
1785 {
1786     if ( ssn )
1787     {
1788         Hi5State** s = (Hi5State **)stream_api->get_paf_user_data(ssn, 0, hi_paf_id);
1789 
1790         if ( s && *s )
1791             return ((*s)->valid_http);
1792     }
1793     return 1;
1794 }
hi_paf_disable_te(void * ssn,bool to_server)1795 bool hi_paf_disable_te(void* ssn, bool to_server)
1796 {
1797     if ( ssn )
1798     {
1799         Hi5State** s = (Hi5State **)stream_api->get_paf_user_data(ssn, to_server?1:0, hi_paf_id);
1800 
1801         if ( s && *s )
1802             return ( (*s)->disable_chunked);
1803     }
1804     return false;
1805 }
1806 
hi_paf_get_size()1807 uint32_t hi_paf_get_size()
1808 {
1809     return sizeof(Hi5State);
1810 }
1811 
1812