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 // ips_manager.cc author Russ Combs <rucombs@cisco.com>
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "ips_manager.h"
25 
26 #include <cassert>
27 #include <map>
28 
29 #include "detection/fp_detect.h"
30 #include "detection/treenodes.h"
31 #include "log/messages.h"
32 #include "main/snort_config.h"
33 
34 #include "module_manager.h"
35 #include "plugin_manager.h"
36 
37 using namespace snort;
38 using namespace std;
39 
40 struct Option
41 {
42     const IpsApi* api;
43     bool init;
44     unsigned count;
45 
OptionOption46     Option(const IpsApi* p)
47     { api = p; init = false; count = 0; }
48 };
49 
50 typedef map<std::string, Option*> OptionMap;
51 static OptionMap s_options;
52 
53 static std::string current_keyword = std::string();
54 static Module* current_module = nullptr;
55 static const Parameter* current_params = nullptr;
56 
57 //-------------------------------------------------------------------------
58 // plugins
59 //-------------------------------------------------------------------------
60 
add_plugin(const IpsApi * api)61 void IpsManager::add_plugin(const IpsApi* api)
62 {
63     assert(s_options.find(api->base.name) == s_options.end());
64     s_options[api->base.name] = new Option(api);
65 }
66 
release_plugins()67 void IpsManager::release_plugins()
68 {
69     for ( auto& p : s_options )
70         delete p.second;
71 
72     s_options.clear();
73 }
74 
dump_plugins()75 void IpsManager::dump_plugins()
76 {
77     Dumper d("IPS Options");
78 
79     for ( auto& p : s_options )
80         d.dump(p.second->api->base.name, p.second->api->base.version);
81 }
82 
83 //-------------------------------------------------------------------------
84 // ips options
85 //-------------------------------------------------------------------------
86 
delete_option(IpsOption * ips)87 void IpsManager::delete_option(IpsOption* ips)
88 {
89     const IpsApi* api = (const IpsApi*)
90         PluginManager::get_api(PT_IPS_OPTION, ips->get_name());
91 
92     if ( api )
93         api->dtor(ips);
94 }
95 
96 //-------------------------------------------------------------------------
97 
set_arg(Module * m,const Parameter * p,const char * opt,const char * val,SnortConfig * sc)98 static bool set_arg(
99     Module* m, const Parameter* p,
100     const char* opt, const char* val, SnortConfig* sc)
101 {
102     if ( !p->is_positional() )
103     {
104         const Parameter* q = ModuleManager::get_parameter(m->get_name(), opt);
105         p = q ? q : Parameter::find(p, opt);
106     }
107     else if ( *opt )  // must contain spaces like ip_proto:! 6;
108         return false;
109 
110     if ( !p )
111         return false;
112 
113     Value v(opt);
114     bool ok = true;
115 
116     if ( p->type == Parameter::PT_IMPLIED )
117         v.set(true);
118 
119     else if ( p->type == Parameter::PT_BOOL )
120     {
121         if ( !val or !strcmp(val, "true") )
122             v.set(true);
123         else
124             v.set(false);
125     }
126     else if ( p->type == Parameter::PT_INT )
127     {
128         char* end = nullptr;
129 
130         if ( p->is_wild_card() )
131             val = opt;
132 
133         long n = (long)strtoll(val, &end, 0);
134 
135         if ( !*end )
136             v.set(n);
137         else
138             ok = false;
139     }
140     else if ( p->is_wild_card() )
141     {
142         string s = opt;
143         if ( val and *val )
144         {
145             s += " ";
146             s += val;
147         }
148         v.set(s.c_str());
149     }
150     else
151         v.set(val);
152 
153     if ( ok && p->validate(v) )
154     {
155         v.set(p);
156 
157         if ( m->set(p->name, v, sc) )
158             return true;
159     }
160     return false;
161 }
162 
163 //-------------------------------------------------------------------------
164 
get_opt(const char * keyword)165 static Option* get_opt(const char* keyword)
166 {
167     auto opt = s_options.find(keyword);
168     if ( opt != s_options.end() )
169         return opt->second;
170 
171     return nullptr;
172 }
173 
get_option_keyword()174 const char* IpsManager::get_option_keyword()
175 {
176     return current_keyword.c_str();
177 }
178 
get_option_api(const char * keyword)179 const IpsApi* IpsManager::get_option_api(const char* keyword)
180 {
181     Option* opt = get_opt(keyword);
182     if ( opt )
183         return opt->api;
184     else
185         return nullptr;
186 }
187 
option_begin(SnortConfig * sc,const char * key,SnortProtocolId)188 bool IpsManager::option_begin(
189     SnortConfig* sc, const char* key, SnortProtocolId)
190 {
191     Option* opt = get_opt(key);
192 
193     if ( !opt )
194     {
195         ParseError("unknown rule keyword: %s.", key);
196         return false;
197     }
198 
199     if ( !opt->init )
200     {
201         if ( opt->api->pinit )
202             opt->api->pinit(sc);
203         opt->init = true;
204     }
205 
206     if ( opt->api->max_per_rule && (++opt->count > opt->api->max_per_rule) )
207     {
208         ParseError("%s allowed only %u time(s) per rule",
209             opt->api->base.name, opt->api->max_per_rule);
210         return false;
211     }
212 
213     // FIXIT-M allow service too
214     //if ( opt->api->protos && !(proto & opt->api->protos) )
215     //{
216     //    ParseError("%s not allowed with given rule protocol", opt->api->base.name);
217     //    return false;
218     //}
219 
220     current_module = ModuleManager::get_module(key);
221 
222     if ( current_module && !current_module->begin(key, 0, sc) )
223     {
224         ParseError("can't initialize %s", key);
225         return false;
226     }
227     current_keyword = key;
228     current_params = current_module ? current_module->get_parameters() : nullptr;
229     return true;
230 }
231 
option_set(SnortConfig * sc,const char * key,const char * opt,const char * val)232 bool IpsManager::option_set(
233     SnortConfig* sc, const char* key, const char* opt, const char* val)
234 {
235     if ( !current_module || current_keyword.empty() )
236         return false;
237 
238     assert(!strcmp(current_keyword.c_str(), key));
239     std::string munge;
240 
241     if ( current_params->is_positional() )
242     {
243         if ( !*val )  // eg: gid:116; opt="116", val="" -> opt="", val="116"
244         {
245             val = opt;
246             opt = "";
247         }
248         else       // eg: dsize:> 80; opt=">", val="80" -> opt="", val="> 80"
249         {
250             munge = opt;
251             munge += " ";
252             munge += val;
253             val = munge.c_str();
254             opt = "";
255         }
256     }
257 
258     if ( !set_arg(current_module, current_params, opt, val, sc) )
259         ParseError("invalid argument %s:%s = %s", key, opt, val);
260 
261     if ( current_params->is_positional() )
262         ++current_params;
263 
264     return true;
265 }
266 
option_end(SnortConfig * sc,OptTreeNode * otn,SnortProtocolId snort_protocol_id,const char * key,RuleOptType & type)267 IpsOption* IpsManager::option_end(
268     SnortConfig* sc, OptTreeNode* otn, SnortProtocolId snort_protocol_id,
269     const char* key, RuleOptType& type)
270 {
271     if ( current_keyword.empty() )
272         return nullptr;
273 
274     assert(!strcmp(current_keyword.c_str(), key));
275 
276 #ifdef NDEBUG
277     UNUSED(snort_protocol_id);
278 #else
279     assert(snort_protocol_id == otn->snort_protocol_id);
280 #endif
281 
282     Module* mod = current_module;
283     current_module = nullptr;
284     current_params = nullptr;
285 
286     Option* opt = get_opt(key);
287     assert(opt);
288 
289     if ( !mod and opt->api->base.mod_ctor )
290     {
291         ParseError("unknown option %s", key);
292         current_keyword.clear();
293         return nullptr;
294     }
295 
296     if ( mod and !mod->end(key, 0, sc) )
297     {
298         ParseError("can't finalize %s", key);
299         current_keyword.clear();
300         return nullptr;
301     }
302 
303     IpsOption* ips = opt->api->ctor(mod, otn);
304     type = opt->api->type;
305     current_keyword.clear();
306 
307     if ( !ips )
308         return nullptr;
309 
310     if ( void* prev = add_detection_option(sc, ips->get_type(), ips) )
311     {
312         delete ips;
313         ips = (IpsOption*)prev;
314     }
315 
316     OptFpList* fpl = AddOptFuncToList(fp_eval_option, otn);
317     fpl->ips_opt = ips;
318     fpl->type = ips->get_type();
319 
320     if ( ips->is_relative() )
321         fpl->isRelative = 1;
322 
323     if ( ips->is_agent() and !otn_set_agent(otn, ips) )
324     {
325         // FIXIT-L support multiple actions (eg replaces) per rule
326         ParseWarning(WARN_RULES,
327             "at most one action per rule is allowed; other actions disabled");
328     }
329     return ips;
330 }
331 
332 //-------------------------------------------------------------------------
333 
global_init(const SnortConfig *)334 void IpsManager::global_init(const SnortConfig*)
335 {
336 }
337 
global_term(const SnortConfig * sc)338 void IpsManager::global_term(const SnortConfig* sc)
339 {
340     for ( auto& p : s_options )
341         if ( p.second->init && p.second->api->pterm )
342         {
343             p.second->api->pterm(sc);
344             p.second->init = false;
345         }
346 }
347 
reset_options()348 void IpsManager::reset_options()
349 {
350     for ( auto& p : s_options )
351         p.second->count = 0;
352 
353     // this is the default when we start parsing a rule body
354     IpsOption::set_buffer("pkt_data");
355 }
356 
setup_options(const SnortConfig * sc)357 void IpsManager::setup_options(const SnortConfig* sc)
358 {
359     for ( auto& p : s_options )
360         if ( p.second->init && p.second->api->tinit )
361             p.second->api->tinit(sc);
362 }
363 
clear_options(const SnortConfig * sc)364 void IpsManager::clear_options(const SnortConfig* sc)
365 {
366     for ( auto& p : s_options )
367         if ( p.second->init && p.second->api->tterm )
368             p.second->api->tterm(sc);
369 }
370 
verify(SnortConfig * sc)371 bool IpsManager::verify(SnortConfig* sc)
372 {
373     for ( auto& p : s_options )
374         if ( p.second->init && p.second->api->verify )
375             p.second->api->verify(sc);
376 
377     return true;
378 }
379 
380 #ifdef PIGLET
381 
find_api(const char * name)382 static const IpsApi* find_api(const char* name)
383 {
384     for ( auto& wrap : s_options )
385         if ( !strcmp(wrap.second->api->base.name, name) )
386             return wrap.second->api;
387 
388     return nullptr;
389 }
390 
instantiate(const char * name,Module * m,struct OptTreeNode * otn)391 IpsOptionWrapper* IpsManager::instantiate(const char* name, Module* m, struct OptTreeNode* otn)
392 {
393     auto api = find_api(name);
394     if ( !api || !api->ctor )
395         return nullptr;
396 
397     auto p = api->ctor(m, otn);
398     if ( !p )
399         return nullptr;
400 
401     return new IpsOptionWrapper(api, p);
402 }
403 
404 #endif
405 
406