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