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