1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation.  You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 //--------------------------------------------------------------------------
18 // http_cutter.h author Tom Peters <thopeter@cisco.com>
19 
20 #ifndef HTTP_CUTTER_H
21 #define HTTP_CUTTER_H
22 
23 #include <cassert>
24 #include <zlib.h>
25 
26 #include "http_enum.h"
27 #include "http_event.h"
28 #include "http_module.h"
29 
30 class HttpFlowData;
31 
32 //-------------------------------------------------------------------------
33 // HttpCutter class and subclasses
34 //-------------------------------------------------------------------------
35 
36 class HttpCutter
37 {
38 public:
39     virtual ~HttpCutter() = default;
40     virtual HttpEnums::ScanResult cut(const uint8_t* buffer, uint32_t length,
41         HttpInfractions* infractions, HttpEventGen* events, uint32_t flow_target, bool stretch,
42         HttpEnums::H2BodyState state) = 0;
get_num_flush()43     uint32_t get_num_flush() const { return num_flush; }
get_octets_seen()44     uint32_t get_octets_seen() const { return octets_seen; }
get_num_excess()45     uint32_t get_num_excess() const { return num_crlf; }
get_num_head_lines()46     virtual uint32_t get_num_head_lines() const { return 0; }
get_is_broken_chunk()47     virtual bool get_is_broken_chunk() const { return false; }
get_num_good_chunks()48     virtual uint32_t get_num_good_chunks() const { return 0; }
soft_reset()49     virtual void soft_reset() {}
50 
51 protected:
52     // number of octets processed by previous cut() calls that returned NOT_FOUND
53     uint32_t octets_seen = 0;
54     uint32_t num_crlf = 0;
55     uint32_t num_flush = 0;
56 };
57 
58 class HttpStartCutter : public HttpCutter
59 {
60 public:
61     HttpEnums::ScanResult cut(const uint8_t* buffer, uint32_t length,
62         HttpInfractions* infractions, HttpEventGen* events, uint32_t, bool, HttpEnums::H2BodyState)
63         override;
64 
65 protected:
66     enum ValidationResult { V_GOOD, V_BAD, V_TBD };
67 
68 private:
69     static const int MAX_LEADING_WHITESPACE = 20;
70     virtual ValidationResult validate(uint8_t octet, HttpInfractions*, HttpEventGen*) = 0;
71     bool validated = false;
72 };
73 
74 class HttpRequestCutter : public HttpStartCutter
75 {
76 private:
77     uint32_t octets_checked = 0;
78     bool check_h2 = true;
79     ValidationResult validate(uint8_t octet, HttpInfractions*, HttpEventGen*) override;
80 };
81 
82 class HttpStatusCutter : public HttpStartCutter
83 {
84 private:
85     uint32_t octets_checked = 0;
86     ValidationResult validate(uint8_t octet, HttpInfractions*, HttpEventGen*) override;
87 };
88 
89 class HttpHeaderCutter : public HttpCutter
90 {
91 public:
92     HttpEnums::ScanResult cut(const uint8_t* buffer, uint32_t length,
93         HttpInfractions* infractions, HttpEventGen* events, uint32_t, bool, HttpEnums::H2BodyState)
94         override;
get_num_head_lines()95     uint32_t get_num_head_lines() const override { return num_head_lines; }
96 
97 private:
98     enum LineEndState { ZERO, HALF, ONE, THREEHALF };
99     LineEndState state = ONE;
100     int32_t num_head_lines = 0;
101 };
102 
103 class HttpBodyCutter : public HttpCutter
104 {
105 public:
106     HttpBodyCutter(bool accelerated_blocking_, ScriptFinder* finder,
107         HttpEnums::CompressId compression_);
108     ~HttpBodyCutter() override;
soft_reset()109     void soft_reset() override { octets_seen = 0; }
110 
111 protected:
112     bool need_accelerated_blocking(const uint8_t* data, uint32_t length);
113 
114 private:
115     bool dangerous(const uint8_t* data, uint32_t length);
116     bool find_partial(const uint8_t*, uint32_t, bool);
117 
118     const bool accelerated_blocking;
119     uint8_t partial_match = 0;
120     HttpEnums::CompressId compression;
121     z_stream* compress_stream = nullptr;
122     bool decompress_failed = false;
123     ScriptFinder* const finder;
124     const uint8_t* match_string;
125     const uint8_t* match_string_upper;
126     uint8_t string_length;
127 };
128 
129 class HttpBodyClCutter : public HttpBodyCutter
130 {
131 public:
HttpBodyClCutter(int64_t expected_length,bool accelerated_blocking,ScriptFinder * finder,HttpEnums::CompressId compression)132     HttpBodyClCutter(int64_t expected_length,
133         bool accelerated_blocking,
134         ScriptFinder* finder,
135         HttpEnums::CompressId compression) :
136         HttpBodyCutter(accelerated_blocking, finder, compression),
137         remaining(expected_length)
138         { assert(remaining > 0); }
139     HttpEnums::ScanResult cut(const uint8_t*, uint32_t length, HttpInfractions*, HttpEventGen*,
140         uint32_t flow_target, bool stretch, HttpEnums::H2BodyState) override;
141 
142 private:
143     int64_t remaining;
144 };
145 
146 class HttpBodyOldCutter : public HttpBodyCutter
147 {
148 public:
HttpBodyOldCutter(bool accelerated_blocking,ScriptFinder * finder,HttpEnums::CompressId compression)149     HttpBodyOldCutter(bool accelerated_blocking, ScriptFinder* finder,
150         HttpEnums::CompressId compression) :
151         HttpBodyCutter(accelerated_blocking, finder, compression)
152         {}
153     HttpEnums::ScanResult cut(const uint8_t*, uint32_t, HttpInfractions*, HttpEventGen*,
154         uint32_t flow_target, bool stretch, HttpEnums::H2BodyState) override;
155 };
156 
157 class HttpBodyChunkCutter : public HttpBodyCutter
158 {
159 public:
HttpBodyChunkCutter(int64_t maximum_chunk_length_,bool accelerated_blocking,ScriptFinder * finder,HttpEnums::CompressId compression)160     HttpBodyChunkCutter(int64_t maximum_chunk_length_, bool accelerated_blocking,
161         ScriptFinder* finder, HttpEnums::CompressId compression) :
162         HttpBodyCutter(accelerated_blocking, finder, compression),
163         maximum_chunk_length(maximum_chunk_length_)
164         {}
165     HttpEnums::ScanResult cut(const uint8_t* buffer, uint32_t length,
166         HttpInfractions* infractions, HttpEventGen* events, uint32_t flow_target, bool stretch,
167         HttpEnums::H2BodyState) override;
get_is_broken_chunk()168     bool get_is_broken_chunk() const override { return curr_state == HttpEnums::CHUNK_BAD; }
get_num_good_chunks()169     uint32_t get_num_good_chunks() const override { return num_good_chunks; }
soft_reset()170     void soft_reset() override { num_good_chunks = 0; HttpBodyCutter::soft_reset(); }
171 
172 private:
173     void transition_to_chunk_bad(bool& accelerate_this_packet);
174 
175     const int64_t maximum_chunk_length;
176 
177     uint32_t data_seen = 0;
178     HttpEnums::ChunkState curr_state = HttpEnums::CHUNK_NEWLINES;
179     uint32_t expected = 0;
180     uint32_t num_leading_ws = 0;
181     uint32_t num_zeros = 0;
182     uint32_t digits_seen = 0;
183     uint32_t num_good_chunks = 0;  // that end in the current section
184     bool zero_chunk = true;
185 };
186 
187 class HttpBodyH2Cutter : public HttpBodyCutter
188 {
189 public:
HttpBodyH2Cutter(int64_t expected_length,bool accelerated_blocking,ScriptFinder * finder,HttpEnums::CompressId compression)190     HttpBodyH2Cutter(int64_t expected_length, bool accelerated_blocking, ScriptFinder* finder,
191         HttpEnums::CompressId compression) :
192         HttpBodyCutter(accelerated_blocking, finder, compression),
193             expected_body_length(expected_length)
194         {}
195     HttpEnums::ScanResult cut(const uint8_t* buffer, uint32_t length, HttpInfractions*,
196         HttpEventGen*, uint32_t flow_target, bool stretch, HttpEnums::H2BodyState state) override;
197 private:
198     int64_t expected_body_length;
199     uint32_t total_octets_scanned = 0;
200 };
201 
202 #endif
203 
204