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