1 //--------------------------------------------------------------------------
2 // Copyright (C) 2016-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
19 // dce_http_proxy_splitter.cc author Ed Borgoyn <eborgoyn@cisco.com>
20 // based on work by Todd Wease
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "dce_http_proxy_splitter.h"
27
28 #include "dce_http_proxy_module.h"
29
30 using namespace snort;
31
32 // NOTE: These strings must have a length of at least one character
33 #define HTTP_PROXY_REQUEST "RPC_CONNECT"
34 #define HTTP_PROXY_RESPONSE "HTTP/1."
35
reassemble(Flow * flow,unsigned total,unsigned offset,const uint8_t * data,unsigned len,uint32_t flags,unsigned & copied)36 const StreamBuffer DceHttpProxySplitter::reassemble(
37 Flow* flow, unsigned total, unsigned offset,
38 const uint8_t* data, unsigned len, uint32_t flags, unsigned& copied)
39 {
40 // FIXIT-M Framework should permit the null return on both PDU directions
41 if ( to_server() )
42 {
43 copied = len;
44 return { nullptr, 0 };
45 }
46 else
47 return StreamSplitter::reassemble(flow,total,offset,data,len,flags,copied);
48 }
49
scan(Packet *,const uint8_t * data,uint32_t len,uint32_t flags,uint32_t * fp)50 StreamSplitter::Status DceHttpProxySplitter::scan(
51 Packet*, const uint8_t* data, uint32_t len,
52 uint32_t flags, uint32_t* fp)
53 {
54 StreamSplitter::Status status;
55
56 if ( (flags & PKT_FROM_CLIENT) != 0 )
57 status = match_request_head( data, len );
58 else if ( (flags & PKT_FROM_SERVER) != 0 )
59 status = match_response( data, len );
60 else
61 return StreamSplitter::ABORT;
62
63 if ( status == StreamSplitter::FLUSH )
64 {
65 *fp = len;
66 }
67 return status;
68 }
69
70 /* match_request_head() is only used by the c2s splitter instance. */
71 StreamSplitter::Status
match_request_head(const uint8_t * data,uint32_t & len)72 DceHttpProxySplitter::match_request_head(const uint8_t* data, uint32_t& len)
73 {
74 if ( match_index == (unsigned int)strlen(HTTP_PROXY_REQUEST) )
75 {
76 cutover = true;
77 return StreamSplitter::FLUSH;
78 }
79
80 len = (len > strlen(HTTP_PROXY_REQUEST)) ? strlen(HTTP_PROXY_REQUEST) : len;
81
82 if ( ((len+match_index) > strlen(HTTP_PROXY_REQUEST)) ||
83 memcmp( (const void*)data, (const void*)(&HTTP_PROXY_REQUEST[match_index]), len ) != 0 )
84 return StreamSplitter::ABORT;
85 else
86 {
87 match_index += len;
88 if ( match_index == (unsigned int)strlen(HTTP_PROXY_REQUEST) )
89 {
90 return StreamSplitter::FLUSH;
91 }
92 else
93 return StreamSplitter::SEARCH;
94 }
95 }
96
97 /* match_response_head() is only used by the s2c splitter instance. */
match_response_head(const uint8_t * data,uint32_t & len)98 StreamSplitter::Status DceHttpProxySplitter::match_response_head(const uint8_t* data, uint32_t& len)
99 {
100 assert(strlen(HTTP_PROXY_RESPONSE) > 0); // make sure we have a string to match
101
102 len = (len > strlen(HTTP_PROXY_RESPONSE)) ? strlen(HTTP_PROXY_RESPONSE) : len;
103
104 if ( memcmp( (const void*)data, (const void*)(&HTTP_PROXY_RESPONSE[match_index]), len ) != 0 )
105 return StreamSplitter::ABORT;
106 else
107 {
108 match_index += len;
109 return match_index == (unsigned int)strlen(HTTP_PROXY_RESPONSE) ?
110 StreamSplitter::FLUSH : StreamSplitter::SEARCH;
111 }
112 }
113
114 /* match_request() is only used by the s2c splitter instance. */
115 StreamSplitter::Status
match_response(const uint8_t * data,const uint32_t & len)116 DceHttpProxySplitter::match_response(const uint8_t* data, const uint32_t& len)
117 {
118 uint32_t starting_index = 0;
119
120 if ( match_state == HTTP_PROXY_INIT )
121 {
122 uint32_t my_len = len;
123 StreamSplitter::Status status = match_response_head(data, my_len);
124 if ( status != StreamSplitter::FLUSH )
125 return status;
126 starting_index = my_len;
127 match_state = HTTP_PROXY_HEAD;
128 }
129
130 for ( unsigned int i=starting_index; i<len; i++ )
131 {
132 // Skip any optional '\r's while parsing
133 if ( data[i] == '\r' )
134 continue;
135 if ( data[i] == '\n' )
136 {
137 if ( match_state == HTTP_PROXY_HEAD )
138 match_state = HTTP_PROXY_FIRST_NL;
139 else
140 {
141 cutover = true;
142 return StreamSplitter::FLUSH;
143 }
144 }
145 else
146 match_state = HTTP_PROXY_HEAD;
147 }
148 return StreamSplitter::SEARCH;
149 }
150
DceHttpProxySplitter(bool c2s)151 DceHttpProxySplitter::DceHttpProxySplitter(bool c2s) : StreamSplitter(c2s)
152 {
153 cutover = false;
154 match_index = 0;
155 match_state = HTTP_PROXY_INIT;
156 }
157
158 #ifdef CATCH_TEST_BUILD
159
160 #include "catch/catch.hpp"
161
162 //--------------------------------------------------------------------------
163 // mocks
164 //--------------------------------------------------------------------------
165
max(Flow *)166 unsigned StreamSplitter::max(Flow*) { return 16384; }
167
reassemble(Flow *,unsigned,unsigned,const uint8_t *,unsigned,uint32_t,unsigned &)168 const StreamBuffer StreamSplitter::reassemble(
169 Flow*, unsigned, unsigned, const uint8_t*,
170 unsigned, uint32_t, unsigned&)
171 {
172 return { nullptr, 0 };
173 }
174
175 //--------------------------------------------------------------------------
176 // unit tests
177 //--------------------------------------------------------------------------
178
179 TEST_CASE("DceHttpProxySplitter-scan - first_proxy_request", "[http_proxy_splitter]")
180 {
181 DceHttpProxySplitter* splitter = new DceHttpProxySplitter(true);
182 uint32_t fp;
183
184 REQUIRE(splitter->cutover_inspector() == false);
185 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"RPC", 3, PKT_FROM_CLIENT, &fp) ==
186 StreamSplitter::SEARCH);
187 REQUIRE(splitter->cutover_inspector() == false);
188 delete splitter;
189 }
190
191 TEST_CASE("DceHttpProxySplitter-scan - first_proxy_request_no_direction", "[http_proxy_splitter]")
192 {
193 DceHttpProxySplitter* splitter = new DceHttpProxySplitter(true);
194 uint32_t fp;
195
196 REQUIRE(splitter->cutover_inspector() == false);
197 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"RPC", 3, 0, &fp) == StreamSplitter::ABORT);
198 REQUIRE(splitter->cutover_inspector() == false);
199 delete splitter;
200 }
201
202 TEST_CASE("DceHttpProxySplitter-scan - bad_first_proxy_request", "[http_proxy_splitter]")
203 {
204 DceHttpProxySplitter* splitter = new DceHttpProxySplitter(true);
205 uint32_t fp;
206
207 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"xxx", 1, PKT_FROM_CLIENT, &fp) ==
208 StreamSplitter::ABORT);
209 REQUIRE(splitter->cutover_inspector() == false);
210 delete splitter;
211 }
212
213 TEST_CASE("DceHttpProxySplitter-scan - first_bad_second_proxy_request", "[http_proxy_splitter]")
214 {
215 DceHttpProxySplitter* splitter = new DceHttpProxySplitter(true);
216 uint32_t fp;
217
218 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"RPC", 3, PKT_FROM_CLIENT, &fp) ==
219 StreamSplitter::SEARCH);
220 REQUIRE(splitter->cutover_inspector() == false);
221 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"R", 1, PKT_FROM_CLIENT, &fp) ==
222 StreamSplitter::ABORT);
223 REQUIRE(splitter->cutover_inspector() == false);
224 delete splitter;
225 }
226
227 TEST_CASE("DceHttpProxySplitter-scan - first_good_second_proxy_request", "[http_proxy_splitter]")
228 {
229 DceHttpProxySplitter* splitter = new DceHttpProxySplitter(true);
230 uint32_t fp;
231
232 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"RPC", 3, PKT_FROM_CLIENT, &fp) ==
233 StreamSplitter::SEARCH);
234 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"_CON", 4, PKT_FROM_CLIENT, &fp) ==
235 StreamSplitter::SEARCH);
236 REQUIRE(splitter->cutover_inspector() == false);
237 delete splitter;
238 }
239
240 TEST_CASE("DceHttpProxySplitter-scan - full_proxy_request", "[http_proxy_splitter]")
241 {
242 DceHttpProxySplitter* splitter = new DceHttpProxySplitter(true);
243 uint32_t fp = 0;
244
245 REQUIRE(splitter->scan(nullptr, (const uint8_t*)HTTP_PROXY_REQUEST,
246 strlen(HTTP_PROXY_REQUEST), PKT_FROM_CLIENT, &fp) == StreamSplitter::FLUSH);
247 REQUIRE(fp == strlen(HTTP_PROXY_REQUEST));
248 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"0", 1, PKT_FROM_CLIENT, &fp) ==
249 StreamSplitter::FLUSH);
250 REQUIRE(splitter->cutover_inspector() == true);
251 delete splitter;
252 }
253
254 TEST_CASE("DceHttpProxySplitter-scan - extra_proxy_request", "[http_proxy_splitter]")
255 {
256 DceHttpProxySplitter* splitter = new DceHttpProxySplitter(true);
257 const char* extra = "ignore";
258 char* string = new char[strlen(HTTP_PROXY_REQUEST)+strlen(extra)+1];
259 uint32_t fp = 0;
260 strncpy(string,(const char*)HTTP_PROXY_REQUEST,strlen(HTTP_PROXY_REQUEST)+1);
261 strncpy(string+strlen(HTTP_PROXY_REQUEST),extra,strlen(extra)+1);
262
263 REQUIRE(splitter->scan(nullptr, (const uint8_t*)string,
264 (strlen(HTTP_PROXY_REQUEST)+strlen(extra)), PKT_FROM_CLIENT, &fp) ==
265 StreamSplitter::FLUSH);
266 REQUIRE(fp == strlen(HTTP_PROXY_REQUEST));
267 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"0", 1, PKT_FROM_CLIENT, &fp) ==
268 StreamSplitter::FLUSH);
269 REQUIRE(splitter->cutover_inspector() == true);
270 delete splitter;
271 delete[] string;
272 }
273
274 TEST_CASE("DceHttpProxySplitter-scan - first_proxy_response", "[http_proxy_splitter]")
275 {
276 DceHttpProxySplitter* splitter = new DceHttpProxySplitter(false);
277 uint32_t fp;
278
279 REQUIRE(splitter->cutover_inspector() == false);
280 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"xxx", 3, PKT_FROM_SERVER, &fp) ==
281 StreamSplitter::ABORT);
282 REQUIRE(splitter->cutover_inspector() == false);
283 delete splitter;
284 }
285
286 TEST_CASE("DceHttpProxySplitter-scan - good_1_proxy_response", "[http_proxy_splitter]")
287 {
288 DceHttpProxySplitter* splitter = new DceHttpProxySplitter(false);
289 uint32_t fp = 0;
290
291 REQUIRE(splitter->cutover_inspector() == false);
292 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"HTTP/1.xxx\n\n", 12,
293 PKT_FROM_SERVER, &fp) == StreamSplitter::FLUSH);
294 REQUIRE((fp == 12));
295 REQUIRE(splitter->cutover_inspector() == true);
296 delete splitter;
297 }
298
299 TEST_CASE("DceHttpProxySplitter-scan - good_2_proxy_response", "[http_proxy_splitter]")
300 {
301 DceHttpProxySplitter* splitter = new DceHttpProxySplitter(false);
302 uint32_t fp = 0;
303
304 REQUIRE(splitter->cutover_inspector() == false);
305 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"HTTP/1.xxx\nxx\n\n", 15,
306 PKT_FROM_SERVER, &fp) == StreamSplitter::FLUSH);
307 REQUIRE((fp == 15));
308 REQUIRE(splitter->cutover_inspector() == true);
309 delete splitter;
310 }
311 TEST_CASE("DceHttpProxySplitter-scan - good_3_proxy_response", "[http_proxy_splitter]")
312 {
313 DceHttpProxySplitter* splitter = new DceHttpProxySplitter(false);
314 uint32_t fp = 0;
315
316 REQUIRE(splitter->cutover_inspector() == false);
317 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"HTTP/1.xxx\nxx\n\nyyy", 18,
318 PKT_FROM_SERVER, &fp) == StreamSplitter::FLUSH);
319 REQUIRE((fp == 18));
320 REQUIRE(splitter->cutover_inspector() == true);
321 delete splitter;
322 }
323
324 TEST_CASE("DceHttpProxySplitter-scan - bad_1_proxy_response", "[http_proxy_splitter]")
325 {
326 DceHttpProxySplitter* splitter = new DceHttpProxySplitter(false);
327 uint32_t fp;
328
329 REQUIRE(splitter->cutover_inspector() == false);
330 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"HTTP/1.xxx\nx\n", 13, PKT_FROM_SERVER, &fp) ==
331 StreamSplitter::SEARCH);
332 REQUIRE(splitter->cutover_inspector() == false);
333 delete splitter;
334 }
335
336 TEST_CASE("DceHttpProxySplitter-scan - bad_2_proxy_response", "[http_proxy_splitter]")
337 {
338 DceHttpProxySplitter* splitter = new DceHttpProxySplitter(false);
339 uint32_t fp;
340
341 REQUIRE(splitter->cutover_inspector() == false);
342 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"HTTP/1.xxx\nx", 12, PKT_FROM_SERVER, &fp) ==
343 StreamSplitter::SEARCH);
344 REQUIRE(splitter->cutover_inspector() == false);
345 delete splitter;
346 }
347
348 TEST_CASE("DceHttpProxySplitter-scan - bad_3_proxy_response", "[http_proxy_splitter]")
349 {
350 DceHttpProxySplitter* splitter = new DceHttpProxySplitter(false);
351 uint32_t fp;
352
353 REQUIRE(splitter->cutover_inspector() == false);
354 REQUIRE(splitter->scan(nullptr, (const uint8_t*)"HTTP/1.", 7, PKT_FROM_SERVER, &fp) ==
355 StreamSplitter::SEARCH);
356 REQUIRE(splitter->cutover_inspector() == false);
357 delete splitter;
358 }
359
360 #endif
361