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