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 // so_manager.cc author Russ Combs <rucombs@cisco.com>
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "so_manager.h"
25
26 #include <zlib.h>
27
28 #include <cassert>
29 #include <cstdlib>
30 #include <cstring>
31 #include <iomanip>
32 #include <iostream>
33 #include <sstream>
34
35 #include "log/messages.h"
36 #include "framework/decode_data.h"
37 #include "framework/inspector.h"
38 #include "framework/module.h"
39 #include "main/snort_config.h"
40 #include "parser/parse_so_rule.h"
41
42 using namespace snort;
43 using namespace std;
44
45 //-------------------------------------------------------------------------
46 // plugins
47 //-------------------------------------------------------------------------
~SoRules()48 SoRules::~SoRules()
49 {
50 api.clear();
51 handles.clear();
52 }
53
add_plugin(const SoApi * api,SnortConfig * sc,SoHandlePtr handle)54 void SoManager::add_plugin(const SoApi* api, SnortConfig* sc, SoHandlePtr handle)
55 {
56 sc->so_rules->api.emplace_back(api);
57 sc->so_rules->handles.emplace_back(handle);
58 }
59
dump_plugins()60 void SoManager::dump_plugins()
61 {
62 Dumper d("SO Rules");
63
64 for ( auto* p : SnortConfig::get_conf()->so_rules->api )
65 d.dump(p->base.name, p->base.version);
66 }
67
68 //-------------------------------------------------------------------------
69 // so rules
70 //-------------------------------------------------------------------------
71
72 // FIXIT-L eliminate this arbitrary limit on rule text size
73 const int window_bits = -9;
74 const unsigned max_rule = 128000;
75 static uint8_t so_buf[max_rule];
76
compress(const string & text,unsigned & len)77 static const uint8_t* compress(const string& text, unsigned& len)
78 {
79 len = 0;
80 const char* s = text.c_str();
81 z_stream stream;
82
83 stream.zalloc = Z_NULL;
84 stream.zfree = Z_NULL;
85 stream.opaque = Z_NULL;
86 stream.next_in = Z_NULL;
87
88 // v2 avoids the header and trailer
89 int ret = deflateInit2(
90 &stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, window_bits, 1, Z_DEFAULT_STRATEGY);
91
92 if ( ret != Z_OK )
93 return nullptr;
94
95 stream.next_in = const_cast<Bytef*>(reinterpret_cast<const uint8_t*>(s));
96 stream.avail_in = text.size();
97
98 stream.next_out = so_buf;
99 stream.avail_out = max_rule;
100
101 ret = deflate(&stream, Z_FINISH);
102 (void)deflateEnd(&stream);
103
104 if ( ret != Z_STREAM_END )
105 return nullptr;
106
107 len= stream.total_out;
108 assert(stream.avail_out > 0);
109
110 return so_buf;
111 }
112
113 // FIXIT-L make this into a general utility for one shot decompress
114 // and add class for stream decompress
expand(const uint8_t * data,unsigned len)115 static const char* expand(const uint8_t* data, unsigned len)
116 {
117 z_stream stream;
118
119 stream.zalloc = Z_NULL;
120 stream.zfree = Z_NULL;
121 stream.opaque = Z_NULL;
122 stream.next_in = Z_NULL;
123 stream.avail_in = 0;
124
125 if ( inflateInit2(&stream, window_bits) != Z_OK )
126 return nullptr;
127
128 stream.next_in = const_cast<Bytef*>(data);
129 stream.avail_in = (uInt)len;
130
131 stream.next_out = (Bytef*)so_buf;
132 stream.avail_out = (uInt)(max_rule - 1);
133
134 int ret = inflate(&stream, Z_FINISH);
135 (void)inflateEnd(&stream);
136
137 if ( ret != Z_STREAM_END )
138 return nullptr;
139
140 // sanity check
141 if ( stream.avail_in or !stream.avail_out )
142 return nullptr;
143
144 assert(stream.total_out < max_rule);
145 so_buf[stream.total_out] = '\0';
146
147 return (char*)so_buf;
148 }
149
150 //-------------------------------------------------------------------------
151
strvrt(const string & text,string & data)152 static void strvrt(const string& text, string& data)
153 {
154 unsigned len = 0;
155 const uint8_t* d = compress(text, len);
156
157 data.assign((const char*)d, len);
158
159 // generate xor key. there is no hard core crypto requirement here but
160 // rand() is known to be weak, especially in the lower bits nonetheless
161 // this seems to work as good as the basic C++ 11 default generator and
162 // uniform distribution
163
164 uint8_t key = (uint8_t)(rand() >> 16);
165
166 if ( !key )
167 key = 0xA5;
168
169 for ( unsigned i = 0; i < len; i++ )
170 data[i] ^= key;
171
172 data.append(1, (char)key);
173 }
174
revert(const uint8_t * data,unsigned len)175 static const char* revert(const uint8_t* data, unsigned len)
176 {
177 if ( !len )
178 return (const char*)data;
179
180 uint8_t key = data[--len];
181 string s((const char*)data, len);
182
183 for ( unsigned i = 0; i < len; i++ )
184 s[i] ^= key;
185
186 return expand((const uint8_t*)s.c_str(), s.size());
187 }
188
189 //-------------------------------------------------------------------------
190
get_so_api(const char * soid,SoRules * so_rules)191 static const SoApi* get_so_api(const char* soid, SoRules* so_rules)
192 {
193 for ( auto* p : so_rules->api )
194 if ( !strcmp(p->base.name, soid) )
195 return p;
196
197 return nullptr;
198 }
199
get_so_rule(const char * soid,SnortConfig * sc)200 const char* SoManager::get_so_rule(const char* soid, SnortConfig* sc)
201 {
202 const SoApi* api = get_so_api(soid, sc->so_rules);
203
204 if ( !api )
205 return nullptr;
206
207 const char* rule = revert(api->rule, api->length);
208
209 return rule;
210 }
211
get_so_eval(const char * soid,const char * so,void ** data,SnortConfig * sc)212 SoEvalFunc SoManager::get_so_eval(const char* soid, const char* so, void** data, SnortConfig* sc)
213 {
214 const SoApi* api = get_so_api(soid, sc->so_rules);
215
216 if ( !api || !api->ctor )
217 return nullptr;
218
219 return api->ctor(so, data);
220 }
221
delete_so_data(const char * soid,void * pv,SoRules * so_rules)222 void SoManager::delete_so_data(const char* soid, void* pv, SoRules* so_rules)
223 {
224 if (!pv or !so_rules)
225 return;
226 const SoApi* api = get_so_api(soid, so_rules);
227
228 if ( api && api->dtor )
229 api->dtor(pv);
230 }
231
232 //-------------------------------------------------------------------------
233
dump_rule_stubs(const char *,SnortConfig * sc)234 void SoManager::dump_rule_stubs(const char*, SnortConfig* sc)
235 {
236 unsigned c = 0;
237
238 for ( auto* p : sc->so_rules->api )
239 {
240 const char* rule = revert(p->rule, p->length);
241
242 if ( !rule )
243 continue;
244
245 std::string stub;
246
247 if ( !get_so_stub(rule, stub) )
248 continue;
249
250 cout << stub << endl;
251
252 ++c;
253 }
254 if ( !c )
255 cerr << "no rules to dump" << endl;
256 }
257
strip_newline(string & s)258 static void strip_newline(string& s)
259 {
260 if ( s.find_last_of('\n') == s.length()-1 )
261 s.pop_back();
262 }
263
get_var(const string & s,string & v)264 static void get_var(const string& s, string& v)
265 {
266 v.clear();
267 size_t pos = s.find("soid:");
268
269 if ( pos == string::npos )
270 return;
271
272 pos += 5;
273
274 size_t end = s.find(';', pos);
275
276 if ( end == string::npos )
277 return;
278
279 v = s.substr(pos, end-pos);
280 }
281
rule_to_hex(const char *)282 void SoManager::rule_to_hex(const char*)
283 {
284 stringstream buffer;
285 buffer << cin.rdbuf();
286
287 string text = buffer.str();
288 strip_newline(text);
289
290 unsigned idx;
291 string data;
292 strvrt(text, data);
293
294 string var;
295 get_var(text, var);
296
297 const unsigned hex_per_row = 16;
298
299 std::ios_base::fmtflags f(cout.flags());
300 cout << "static const uint8_t rule_" << var;
301 cout << "[] =" << endl;
302 cout << "{" << endl << " ";
303 cout << hex << uppercase;
304
305 for ( idx = 0; idx < data.size(); idx++ )
306 {
307 if ( idx && !(idx % hex_per_row) )
308 cout << endl << " ";
309
310 uint8_t u = data[idx];
311 cout << " 0x" << setfill('0') << setw(2) << hex << (int)u << ",";
312 }
313 if ( idx % hex_per_row )
314 cout << endl;
315
316 cout << dec;
317 cout << "};" << endl;
318 cout << "static const unsigned rule_" << var << "_len = ";
319 cout << data.size() << ";" << endl;
320 cout.flags(f);
321 }
322
rule_to_text(const char * delim)323 void SoManager::rule_to_text(const char* delim)
324 {
325 stringstream buffer;
326 buffer << cin.rdbuf();
327
328 string text = buffer.str();
329 strip_newline(text);
330
331 string var;
332 get_var(text, var);
333
334 if ( !delim or !*delim )
335 delim = "[Snort_SO_Rule]";
336
337 cout << "static const char* rule_" << var << " = ";
338 cout << "R\"" << delim << "(" << endl;
339 cout << text << endl;
340 cout << ')' << delim << "\";" << endl;
341 cout << endl;
342 cout << "static const unsigned rule_" << var << "_len = 0;" << endl;
343 }
344
345 //-------------------------------------------------------------------------
346 // so_proxy inspector
347 //-------------------------------------------------------------------------
348 static const char* sp_name = "so_proxy";
349 static const char* sp_help = "a proxy inspector to track flow data from SO rules (internal use only)";
350 class SoProxy : public Inspector
351 {
352 public:
eval(Packet *)353 void eval(Packet*) override { }
configure(SnortConfig * sc)354 bool configure(SnortConfig* sc) override
355 {
356 for( auto i : sc->so_rules->handles )
357 handles.emplace_back(i);
358 sc->so_rules->proxy = this;
359 return true;
360 }
~SoProxy()361 ~SoProxy() override { handles.clear(); }
362
363 private:
364 std::list<SoHandlePtr> handles;
365 };
366
367 static const Parameter sp_params[] =
368 {
369 { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
370 };
371
372 class SoProxyModule : public Module
373 {
374 public:
SoProxyModule()375 SoProxyModule() : Module(sp_name, sp_help, sp_params) { }
get_usage() const376 Usage get_usage() const override
377 { return GLOBAL; }
378 };
379
mod_ctor()380 static Module* mod_ctor()
381 { return new SoProxyModule; }
382
mod_dtor(Module * m)383 static void mod_dtor(Module* m)
384 { delete m; }
385
sp_ctor(Module *)386 static Inspector* sp_ctor(Module*)
387 {
388 return new SoProxy;
389 }
390
sp_dtor(Inspector * p)391 static void sp_dtor(Inspector* p)
392 {
393 delete p;
394 }
395
396 static const InspectApi so_proxy_api
397 {
398 {
399 PT_INSPECTOR,
400 sizeof(InspectApi),
401 INSAPI_VERSION,
402 0,
403 API_RESERVED,
404 API_OPTIONS,
405 sp_name,
406 sp_help,
407 mod_ctor,
408 mod_dtor
409 },
410 IT_PASSIVE,
411 PROTO_BIT__NONE,
412 nullptr, // buffers
413 nullptr, // service
414 nullptr, // pinit
415 nullptr, // pterm
416 nullptr, // tinit,
417 nullptr, // tterm,
418 sp_ctor,
419 sp_dtor,
420 nullptr, // ssn
421 nullptr // reset
422 };
423
424 const BaseApi* so_proxy_plugins[] =
425 {
426 &so_proxy_api.base,
427 nullptr
428 };
429
load_so_proxy()430 void SoManager::load_so_proxy()
431 {
432 PluginManager::load_plugins(so_proxy_plugins);
433 }
434