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