1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 2002-2013 Sourcefire, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License Version 2 as published
7 // by the Free Software Foundation. You may not use, modify or distribute
8 // this program under any other version of the GNU General Public License.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 //--------------------------------------------------------------------------
19 /*
20 ** Dan Roelker <droelker@sourcefire.com>
21 ** Marc Norton <mnorton@sourcefire.com>
22 **
23 ** NOTES
24 ** 5.7.02 - Initial Checkin. Norton/Roelker
25 **
26 ** 6/13/05 - marc norton
27 ** Added plugin support for fast pattern match data
28 **
29 */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include "fp_create.h"
36
37 #include "framework/mpse.h"
38 #include "framework/mpse_batch.h"
39 #include "hash/ghash.h"
40 #include "hash/hash_defs.h"
41 #include "hash/xhash.h"
42 #include "log/messages.h"
43 #include "main/snort.h"
44 #include "main/snort_config.h"
45 #include "main/thread_config.h"
46 #include "managers/mpse_manager.h"
47 #include "parser/parse_rule.h"
48 #include "parser/parser.h"
49 #include "ports/port_table.h"
50 #include "ports/rule_port_tables.h"
51 #include "utils/stats.h"
52 #include "utils/util.h"
53
54 #include "detection_options.h"
55 #include "detect_trace.h"
56 #include "fp_config.h"
57 #include "fp_utils.h"
58 #include "pattern_match_data.h"
59 #include "pcrm.h"
60 #include "service_map.h"
61 #include "treenodes.h"
62
63 using namespace snort;
64 using namespace std;
65
66 static unsigned mpse_count = 0;
67 static unsigned offload_mpse_count = 0;
68 static const char* s_group = "";
69
70 static void fpDeletePMX(void* data);
71
72 static int fpGetFinalPattern(
73 FastPatternConfig*, PatternMatchData*, const char*& ret_pattern, unsigned& ret_bytes);
74
75 static void print_nfp_info(const char*, OptTreeNode*);
76 static void print_fp_info(const char*, const OptTreeNode*, const PatternMatchData*,
77 const char* pattern, unsigned pattern_length);
78
finalize_detection_option_tree(SnortConfig * sc,detection_option_tree_root_t * root)79 static int finalize_detection_option_tree(SnortConfig* sc, detection_option_tree_root_t* root)
80 {
81 if ( !root )
82 return -1;
83
84 for ( int i=0; i<root->num_children; i++ )
85 {
86 detection_option_tree_node_t* node = root->children[i];
87
88 if ( void* dup_node = add_detection_option_tree(sc, node) )
89 {
90 // FIXIT-L delete dup_node and keep original?
91 free_detection_option_tree(node);
92 root->children[i] = (detection_option_tree_node_t*)dup_node;
93 }
94 print_option_tree(root->children[i], 0);
95 }
96
97 return 0;
98 }
99
fixup_tree(detection_option_tree_node_t * dot,bool branched,unsigned contents)100 static OptTreeNode* fixup_tree(
101 detection_option_tree_node_t* dot, bool branched, unsigned contents)
102 {
103 if ( dot->num_children == 0 )
104 {
105 if ( !branched and contents )
106 return (OptTreeNode*)dot->option_data;
107
108 dot->otn = (OptTreeNode*)dot->option_data;
109 return nullptr;
110 }
111 if ( dot->num_children == 1 )
112 {
113 if ( dot->option_type == RULE_OPTION_TYPE_CONTENT )
114 ++contents;
115
116 OptTreeNode* otn = fixup_tree(dot->children[0], false, contents);
117
118 if ( !branched and contents > 1 )
119 return otn;
120
121 dot->otn = otn;
122 return nullptr;
123 }
124 for ( int i = 0; i < dot->num_children; ++i )
125 fixup_tree(dot->children[i], true, 0);
126
127 return nullptr;
128 }
129
fixup_trees(SnortConfig * sc)130 static void fixup_trees(SnortConfig* sc)
131 {
132 if ( !sc->detection_option_tree_hash_table )
133 return;
134
135 HashNode* hn = sc->detection_option_tree_hash_table->find_first_node();
136
137 while ( hn )
138 {
139 detection_option_tree_node_t* node = (detection_option_tree_node_t*)hn->data;
140 fixup_tree(node, true, 0);
141 hn = sc->detection_option_tree_hash_table->find_next_node();
142 }
143 }
144
new_sig(int num_children,detection_option_tree_node_t ** nodes,OptTreeNode * otn)145 static bool new_sig(int num_children, detection_option_tree_node_t** nodes, OptTreeNode* otn)
146 {
147 for ( int i = 0; i < num_children; ++i )
148 {
149 detection_option_tree_node_t* child = nodes[i];
150
151 if ( child->option_type != RULE_OPTION_TYPE_LEAF_NODE )
152 continue;
153
154 OptTreeNode* cotn = (OptTreeNode*)child->option_data;
155 SigInfo& csi = cotn->sigInfo;
156 SigInfo& osi = otn->sigInfo;
157
158 if ( csi.gid == osi.gid and csi.sid == osi.sid and csi.rev == osi.rev )
159 return false;
160 }
161 return true;
162 }
163
otn_create_tree(OptTreeNode * otn,void ** existing_tree,Mpse::MpseType mpse_type)164 static int otn_create_tree(OptTreeNode* otn, void** existing_tree, Mpse::MpseType mpse_type)
165 {
166 detection_option_tree_node_t* node = nullptr, * child;
167 bool need_leaf = false;
168
169 if (!existing_tree)
170 return -1;
171
172 if (!*existing_tree)
173 *existing_tree = new_root(otn);
174
175 detection_option_tree_root_t* root = (detection_option_tree_root_t*)*existing_tree;
176
177 if (!root->children)
178 {
179 root->num_children++;
180 root->children = (detection_option_tree_node_t**)
181 snort_calloc(root->num_children, sizeof(detection_option_tree_node_t*));
182 need_leaf = true;
183 }
184
185 int i = 0;
186 child = root->children[i];
187 OptFpList* opt_fp = otn->opt_func;
188
189 /* Build out sub-nodes for each option in the OTN fp list */
190 while (opt_fp)
191 {
192 /* If child node does not match existing option_data,
193 * Create a child branch from a given sub-node. */
194 void* option_data = opt_fp->ips_opt;
195
196 if (opt_fp->type == RULE_OPTION_TYPE_LEAF_NODE)
197 {
198 opt_fp = opt_fp->next;
199 continue;
200 }
201
202 /* Don't add contents that are only for use in the
203 * fast pattern matcher */
204 if ( is_fast_pattern_only(otn, opt_fp, mpse_type) )
205 {
206 opt_fp = opt_fp->next;
207 continue;
208 }
209
210 if (!child)
211 {
212 /* No children at this node */
213 child = new_node(opt_fp->type, option_data);
214 child->evaluate = opt_fp->OptTestFunc;
215
216 if (!node)
217 root->children[i] = child;
218 else
219 node->children[i] = child;
220
221 child->num_children++;
222 child->children = (detection_option_tree_node_t**)
223 snort_calloc(child->num_children, sizeof(detection_option_tree_node_t*));
224 child->is_relative = opt_fp->isRelative;
225
226 if (node && child->is_relative)
227 node->relative_children++;
228
229 need_leaf = true;
230 }
231 else
232 {
233 bool found_child_match = false;
234
235 if (child->option_data != option_data)
236 {
237 if (!node)
238 {
239 for (i=1; i<root->num_children; i++)
240 {
241 child = root->children[i];
242 if (child->option_data == option_data)
243 {
244 found_child_match = true;
245 break;
246 }
247 }
248 }
249 else
250 {
251 for (i=1; i<node->num_children; i++)
252 {
253 child = node->children[i];
254 if (child->option_data == option_data)
255 {
256 found_child_match = true;
257 break;
258 }
259 }
260 }
261 }
262 else
263 {
264 found_child_match = true;
265 }
266
267 if ( !found_child_match )
268 {
269 /* No matching child node, create a new and add to array */
270 detection_option_tree_node_t** tmp_children;
271 child = new_node(opt_fp->type, option_data);
272 child->evaluate = opt_fp->OptTestFunc;
273 child->num_children++;
274 child->children = (detection_option_tree_node_t**)
275 snort_calloc(child->num_children, sizeof(child->children));
276 child->is_relative = opt_fp->isRelative;
277
278 if (!node)
279 {
280 root->num_children++;
281 tmp_children = (detection_option_tree_node_t**)
282 snort_calloc(root->num_children, sizeof(tmp_children));
283 memcpy(tmp_children, root->children,
284 sizeof(detection_option_tree_node_t*) * (root->num_children-1));
285
286 snort_free(root->children);
287 root->children = tmp_children;
288 root->children[root->num_children-1] = child;
289 }
290 else
291 {
292 node->num_children++;
293 tmp_children = (detection_option_tree_node_t**)
294 snort_calloc(node->num_children, sizeof(detection_option_tree_node_t*));
295 memcpy(tmp_children, node->children,
296 sizeof(detection_option_tree_node_t*) * (node->num_children-1));
297
298 snort_free(node->children);
299 node->children = tmp_children;
300 node->children[node->num_children-1] = child;
301 if (child->is_relative)
302 node->relative_children++;
303 }
304 need_leaf = true;
305 }
306 }
307 node = child;
308 i=0;
309 child = node->children[i];
310 opt_fp = opt_fp->next;
311 }
312
313 // don't add a new leaf node unless we branched higher in the tree or this
314 // is a different sig ( eg alert ip ( sid:1; ) vs alert tcp ( sid:2; ) )
315 // note: same sig different policy branches at rtn (this is for same policy)
316
317 if ( !need_leaf )
318 {
319 if ( node )
320 need_leaf = new_sig(node->num_children, node->children, otn);
321 else
322 need_leaf = new_sig(root->num_children, root->children, otn);
323 }
324
325 if ( !need_leaf )
326 return 0;
327
328 /* Append a leaf node that has option data of the SigInfo/otn pointer */
329 child = new_node(RULE_OPTION_TYPE_LEAF_NODE, otn);
330
331 if (!node)
332 {
333 if (root->children[0])
334 {
335 detection_option_tree_node_t** tmp_children;
336 root->num_children++;
337 tmp_children = (detection_option_tree_node_t**)
338 snort_calloc(root->num_children, sizeof(detection_option_tree_node_t*));
339 memcpy(tmp_children, root->children,
340 sizeof(detection_option_tree_node_t*) * (root->num_children-1));
341 snort_free(root->children);
342 root->children = tmp_children;
343 }
344 root->children[root->num_children-1] = child;
345 }
346 else
347 {
348 if (node->children[0])
349 {
350 detection_option_tree_node_t** tmp_children;
351 node->num_children++;
352 tmp_children = (detection_option_tree_node_t**)
353 snort_calloc(node->num_children, sizeof(detection_option_tree_node_t*));
354 memcpy(tmp_children, node->children,
355 sizeof(detection_option_tree_node_t*) * (node->num_children-1));
356 snort_free(node->children);
357 node->children = tmp_children;
358 }
359 node->children[node->num_children-1] = child;
360 }
361
362 return 0;
363 }
364
add_patrn_to_neg_list(void * id,void ** list)365 static int add_patrn_to_neg_list(void* id, void** list)
366 {
367 if ( !id or !list )
368 return -1;
369
370 NCListNode** ncl = (NCListNode**)list;
371 NCListNode* node = (NCListNode*)snort_alloc(sizeof(NCListNode));
372
373 node->pmx = (PMX*)id;
374 node->next = *ncl;
375 *ncl = node;
376
377 return 0;
378 }
379
neg_list_free(void ** list)380 static void neg_list_free(void** list)
381 {
382 NCListNode* ncln;
383
384 if (list == nullptr)
385 return;
386
387 ncln = (NCListNode*)*list;
388 while (ncln != nullptr)
389 {
390 NCListNode* tmp = ncln->next;
391 snort_free(ncln);
392 ncln = tmp;
393 }
394
395 *list = nullptr;
396 }
397
pmx_create_tree(SnortConfig * sc,void * id,void ** existing_tree,Mpse::MpseType mpse_type)398 static int pmx_create_tree(SnortConfig* sc, void* id, void** existing_tree, Mpse::MpseType mpse_type)
399 {
400 assert(existing_tree);
401
402 if (!id)
403 {
404 if ( !*existing_tree )
405 return -1;
406
407 /* null input id (PMX *), last call for this pattern state */
408 return finalize_detection_option_tree(sc, (detection_option_tree_root_t*)*existing_tree);
409 }
410
411 PMX* pmx = (PMX*)id;
412 OptTreeNode* otn = (OptTreeNode*)pmx->rule_node.rnRuleData;
413
414 if (!*existing_tree)
415 *existing_tree = new_root(otn);
416
417 return otn_create_tree(otn, existing_tree, mpse_type);
418 }
419
pmx_create_tree_normal(SnortConfig * sc,void * id,void ** existing_tree)420 static int pmx_create_tree_normal(SnortConfig* sc, void* id, void** existing_tree)
421 {
422 return pmx_create_tree(sc, id, existing_tree, Mpse::MPSE_TYPE_NORMAL);
423 }
424
pmx_create_tree_offload(SnortConfig * sc,void * id,void ** existing_tree)425 static int pmx_create_tree_offload(SnortConfig* sc, void* id, void** existing_tree)
426 {
427 return pmx_create_tree(sc, id, existing_tree, Mpse::MPSE_TYPE_OFFLOAD);
428 }
429
fpFinishRuleGroupRule(Mpse * mpse,OptTreeNode * otn,PatternMatchData * pmd,FastPatternConfig * fp,bool get_final_pat)430 static int fpFinishRuleGroupRule(
431 Mpse* mpse, OptTreeNode* otn, PatternMatchData* pmd, FastPatternConfig* fp, bool get_final_pat)
432 {
433 const char* pattern;
434 unsigned pattern_length;
435
436 if (get_final_pat)
437 {
438 if (fpGetFinalPattern(fp, pmd, pattern, pattern_length) == -1)
439 return -1;
440 }
441 else
442 {
443 pattern = pmd->pattern_buf;
444 pattern_length = pmd->pattern_size;
445 }
446
447 if ( fp->get_debug_print_fast_patterns() and !otn->soid )
448 print_fp_info(s_group, otn, pmd, pattern, pattern_length);
449
450 PMX* pmx = (PMX*)snort_calloc(sizeof(PMX));
451 pmx->rule_node.rnRuleData = otn;
452 pmx->pmd = pmd;
453
454 Mpse::PatternDescriptor desc(
455 pmd->is_no_case(), pmd->is_negated(), pmd->is_literal(), pmd->mpse_flags);
456
457 mpse->add_pattern((const uint8_t*)pattern, pattern_length, desc, pmx);
458
459 return 0;
460 }
461
fpFinishRuleGroup(SnortConfig * sc,RuleGroup * pg,FastPatternConfig * fp)462 static int fpFinishRuleGroup(SnortConfig* sc, RuleGroup* pg, FastPatternConfig* fp)
463 {
464 int rules = 0;
465
466 if (pg == nullptr)
467 return -1;
468
469 if (fp == nullptr)
470 {
471 delete pg;
472 return -1;
473 }
474
475 for (int i = PM_TYPE_PKT; i < PM_TYPE_MAX; i++)
476 {
477 if (pg->mpsegrp[i] != nullptr)
478 {
479 if (pg->mpsegrp[i]->normal_mpse != nullptr)
480 {
481 if (pg->mpsegrp[i]->normal_mpse->get_pattern_count() != 0)
482 {
483 queue_mpse(pg->mpsegrp[i]->normal_mpse);
484
485 if (fp->get_debug_mode())
486 pg->mpsegrp[i]->normal_mpse->print_info();
487
488 rules = 1;
489 }
490 else
491 {
492 MpseManager::delete_search_engine(pg->mpsegrp[i]->normal_mpse);
493 pg->mpsegrp[i]->normal_mpse = nullptr;
494 }
495 }
496 if (pg->mpsegrp[i]->offload_mpse != nullptr)
497 {
498 if (pg->mpsegrp[i]->offload_mpse->get_pattern_count() != 0)
499 {
500 queue_mpse(pg->mpsegrp[i]->offload_mpse);
501
502 if (fp->get_debug_mode())
503 pg->mpsegrp[i]->offload_mpse->print_info();
504
505 rules = 1;
506 }
507 else
508 {
509 MpseManager::delete_search_engine(pg->mpsegrp[i]->offload_mpse);
510 pg->mpsegrp[i]->offload_mpse = nullptr;
511 }
512 }
513
514 if ((pg->mpsegrp[i]->normal_mpse == nullptr) and
515 (pg->mpsegrp[i]->offload_mpse == nullptr))
516 {
517 delete pg->mpsegrp[i];
518 pg->mpsegrp[i] = nullptr;
519 }
520 }
521 }
522
523 if ( pg->nfp_head )
524 {
525 RULE_NODE* ruleNode;
526
527 for (ruleNode = pg->nfp_head; ruleNode; ruleNode = ruleNode->rnNext)
528 {
529 OptTreeNode* otn = (OptTreeNode*)ruleNode->rnRuleData;
530 otn_create_tree(otn, &pg->nfp_tree, Mpse::MPSE_TYPE_NORMAL);
531 }
532
533 finalize_detection_option_tree(sc, (detection_option_tree_root_t*)pg->nfp_tree);
534 rules = 1;
535
536 pg->delete_nfp_rules();
537 }
538
539 if (!rules)
540 {
541 /* Nothing in the port group so we can just free it */
542 delete pg;
543 return -1;
544 }
545
546 return 0;
547 }
548
fpAddAlternatePatterns(Mpse * mpse,OptTreeNode * otn,PatternMatchData * pmd,FastPatternConfig * fp)549 static void fpAddAlternatePatterns(
550 Mpse* mpse, OptTreeNode* otn, PatternMatchData* pmd, FastPatternConfig* fp)
551 {
552 fpFinishRuleGroupRule(mpse, otn, pmd, fp, false);
553 }
554
fpAddRuleGroupRule(SnortConfig * sc,RuleGroup * pg,OptTreeNode * otn,FastPatternConfig * fp,bool srvc)555 static int fpAddRuleGroupRule(
556 SnortConfig* sc, RuleGroup* pg, OptTreeNode* otn, FastPatternConfig* fp, bool srvc)
557 {
558 const MpseApi* search_api = nullptr;
559 const MpseApi* offload_search_api = nullptr;
560 OptFpList* ofp = nullptr;
561 bool exclude;
562
563 // skip builtin rules, continue for text and so rules
564 if ( otn->sigInfo.builtin )
565 return -1;
566
567 if ( !otn->enabled_somewhere() )
568 return -1;
569
570 search_api = fp->get_search_api();
571 assert(search_api);
572
573 bool only_literal = !MpseManager::is_regex_capable(search_api);
574 PatternMatchVector pmv = get_fp_content(otn, ofp, srvc, only_literal, exclude);
575
576 if ( !pmv.empty() )
577 {
578 PatternMatchVector pmv_ol;
579 OptFpList* ofp_ol = nullptr;
580 bool add_to_offload = false;
581 bool cont = true;
582 PatternMatchData* ol_pmd = nullptr;
583
584 offload_search_api = fp->get_offload_search_api();
585
586 // Only add rule to the offload search engine if the offload search engine
587 // is different to the normal search engine.
588 if (offload_search_api and (offload_search_api != search_api))
589 {
590 bool exclude_ol;
591 bool only_literal_ol = !MpseManager::is_regex_capable(offload_search_api);
592 pmv_ol = get_fp_content(otn, ofp_ol, srvc, only_literal_ol, exclude_ol);
593
594 // If we can get a fast_pattern for the normal search engine but not for the
595 // offload search engine then add rule to the non fast pattern list
596 if (!pmv_ol.empty())
597 add_to_offload = true;
598 else
599 cont = false;
600 }
601
602 // From here on we will create the mpses that are needed and add the patterns
603 if (cont)
604 {
605 PatternMatchData* main_pmd = pmv.back();
606 pmv.pop_back();
607
608 static MpseAgent agent =
609 {
610 pmx_create_tree_normal, add_patrn_to_neg_list,
611 fpDeletePMX, free_detection_option_root, neg_list_free
612 };
613
614 if ( !pg->mpsegrp[main_pmd->pm_type] )
615 pg->mpsegrp[main_pmd->pm_type] = new MpseGroup;
616
617 if ( !pg->mpsegrp[main_pmd->pm_type]->normal_mpse )
618 {
619 if (!pg->mpsegrp[main_pmd->pm_type]->create_normal_mpse(sc, &agent))
620 {
621 ParseError("Failed to create normal pattern matcher for %d", main_pmd->pm_type);
622 return -1;
623 }
624
625 mpse_count++;
626 if ( fp->get_search_opt() )
627 pg->mpsegrp[main_pmd->pm_type]->normal_mpse->set_opt(1);
628 }
629
630 if (add_to_offload)
631 {
632 ol_pmd = pmv_ol.back();
633 pmv_ol.pop_back();
634
635 static MpseAgent agent_offload =
636 {
637 pmx_create_tree_offload, add_patrn_to_neg_list,
638 fpDeletePMX, free_detection_option_root, neg_list_free
639 };
640
641 // Keep the created mpse alongside the same pm type as the main pmd
642 if ( !pg->mpsegrp[main_pmd->pm_type]->offload_mpse )
643 {
644 if (!pg->mpsegrp[main_pmd->pm_type]->create_offload_mpse(sc, &agent_offload))
645 {
646 ParseError("Failed to create offload pattern matcher for %d",
647 main_pmd->pm_type);
648 return -1;
649 }
650
651 offload_mpse_count++;
652 if ( fp->get_search_opt() )
653 pg->mpsegrp[main_pmd->pm_type]->offload_mpse->set_opt(1);
654 }
655 }
656
657 bool add_rule = false;
658 bool add_nfp_rule = false;
659
660 if (pg->mpsegrp[main_pmd->pm_type]->normal_mpse)
661 {
662 add_rule = true;
663 if (main_pmd->is_negated())
664 add_nfp_rule = true;
665
666 // Now add patterns
667 if (fpFinishRuleGroupRule(
668 pg->mpsegrp[main_pmd->pm_type]->normal_mpse, otn, main_pmd, fp, true) == 0)
669 {
670 if (main_pmd->pattern_size > otn->longestPatternLen)
671 otn->longestPatternLen = main_pmd->pattern_size;
672
673 if ( make_fast_pattern_only(ofp, main_pmd) )
674 otn->normal_fp_only = ofp;
675
676 // Add Alternative patterns
677 for (auto p : pmv)
678 fpAddAlternatePatterns(
679 pg->mpsegrp[main_pmd->pm_type]->normal_mpse, otn, p, fp);
680 }
681 }
682
683 if (ol_pmd and pg->mpsegrp[main_pmd->pm_type]->offload_mpse)
684 {
685 add_rule = true;
686 if (ol_pmd->is_negated())
687 add_nfp_rule = true;
688
689 // Now add patterns
690 if (fpFinishRuleGroupRule(
691 pg->mpsegrp[main_pmd->pm_type]->offload_mpse, otn, ol_pmd, fp, true) == 0)
692 {
693 if (ol_pmd->pattern_size > otn->longestPatternLen)
694 otn->longestPatternLen = ol_pmd->pattern_size;
695
696 if ( make_fast_pattern_only(ofp_ol, ol_pmd) )
697 otn->offload_fp_only = ofp_ol;
698
699 // Add Alternative patterns
700 for (auto p : pmv_ol)
701 fpAddAlternatePatterns(
702 pg->mpsegrp[main_pmd->pm_type]->offload_mpse, otn, p, fp);
703 }
704 }
705
706 if ( add_rule )
707 {
708 if ( !add_nfp_rule )
709 pg->add_rule();
710 else
711 {
712 pg->add_nfp_rule(otn);
713 print_nfp_info(s_group, otn);
714 }
715 }
716 return 0;
717 }
718 }
719
720 if ( exclude )
721 return 0;
722
723 // no fast pattern added
724 pg->add_nfp_rule(otn);
725 print_nfp_info(s_group, otn);
726
727 return 0;
728 }
729
730 /*
731 * Original PortRuleMaps for each protocol requires creating the following structures.
732 *
733 * PORT_RULE_MAP -> srcRuleGroup,dstRuleGroup,genericRuleGroup
734 * RuleGroup -> pgPatData, pgPatDataUri (acsm objects), (also rule_node lists 1/rule,
735 * not needed). each rule content added to an acsm object has a PMX data ptr
736 * associated with it.
737 * RULE_NODE -> iRuleNodeID (used for bitmap object index)
738 * PMX -> RULE_NODE(->otn), PatternMatchData
739 *
740 * PortList model supports the same structures except:
741 *
742 * RuleGroup -> no rule_node lists needed, PortObjects maintain a list of rules used
743 *
744 * Generation of PortRuleMaps and data is done differently.
745 *
746 * 1) Build tcp/udp/icmp/ip src and dst RuleGroup objects based on the PortList Objects rules.
747 *
748 * 2) For each protocols PortList objects walk it's ports and assign the PORT_RULE_MAP src and
749 * dst RuleGroup[port] array pointers to that PortList objects RuleGroup.
750 *
751 * Implementation:
752 *
753 * Each PortList Object will be translated into a RuleGroup, then pointed to by the
754 * RuleGroup array in the PORT_RULE_MAP for the protocol
755 *
756 * protocol = tcp, udp, ip, icmp - one port_rule_map for each of these protocols
757 * { create a port_rule_map
758 * dst port processing
759 * for each port-list object create a port_group object
760 * { create a pattern match object, store its pointer in port_group
761 * for each rule index in port-list object
762 * {
763 * get the gid+sid for the index
764 * lookup up the otn
765 * create pmx
766 * create RULE_NODE, set iRuleNodeID within this port-list object
767 * get longest content for the rule
768 * set up pmx,RULE_NODE
769 * add the content and pmx to the pattern match object
770 * }
771 * compile the pattern match object
772 *
773 * repeat for uri content
774 * }
775 * src port processing
776 * repeat as for dst port processing
777 * }
778 * ** bidirectional rules - these are added to both src and dst PortList objects, so they are
779 * automatically handled during conversion to port_group objects.
780 */
781 /*
782 ** Build a Pattern group for the Uri-Content rules in this group
783 **
784 ** The patterns added for each rule must be sufficient so if we find any of them
785 ** we proceed to fully analyze the OTN and RTN against the packet.
786 **
787 */
788 /*
789 * Init a port-list based rule map
790 */
791 struct PortIteratorData
792 {
PortIteratorDataPortIteratorData793 PortIteratorData(RuleGroup** a, RuleGroup* g)
794 {
795 array = a;
796 group = g;
797 }
798
setPortIteratorData799 static void set(int port, void* pv)
800 {
801 PortIteratorData* pid = (PortIteratorData*)pv;
802 pid->array[port] = pid->group;
803 }
804
805 RuleGroup** array;
806 RuleGroup* group;
807 };
808
fpCreateInitRuleMap(PORT_RULE_MAP * prm,PortTable * src,PortTable * dst,PortObject * any)809 static void fpCreateInitRuleMap(
810 PORT_RULE_MAP* prm, PortTable* src, PortTable* dst, PortObject* any)
811 {
812 /* setup the any-port content port group */
813 prm->prmGeneric = any->group;
814
815 /* all rules that are any any some may not be content ? */
816 prm->prmNumGenericRules = any->rule_list->count;
817
818 prm->prmNumSrcRules = 0;
819 prm->prmNumDstRules = 0;
820
821 prm->prmNumSrcGroups = 0;
822 prm->prmNumDstGroups = 0;
823
824 /* Process src PORT groups */
825 if ( src )
826 {
827 for (GHashNode* node = src->pt_mpxo_hash->find_first();
828 node;
829 node = src->pt_mpxo_hash->find_next())
830 {
831 PortObject2* po = (PortObject2*)node->data;
832
833 if ( !po or !po->group )
834 continue;
835
836 /* Add up the total src rules */
837 prm->prmNumSrcRules += po->rule_hash->get_count();
838
839 /* Increment the port group count */
840 prm->prmNumSrcGroups++;
841
842 /* Add this port group to the src table at each port that uses it */
843 PortIteratorData pit_data(prm->prmSrcPort, po->group);
844 PortObject2Iterate(po, PortIteratorData::set, &pit_data);
845 }
846 }
847
848 /* process destination port groups */
849 if ( dst )
850 {
851 for (GHashNode* node = dst->pt_mpxo_hash->find_first();
852 node;
853 node = dst->pt_mpxo_hash->find_next())
854 {
855 PortObject2* po = (PortObject2*)node->data;
856
857 if ( !po or !po->group )
858 continue;
859
860 /* Add up the total src rules */
861 prm->prmNumDstRules += po->rule_hash->get_count();
862
863 /* Increment the port group count */
864 prm->prmNumDstGroups++;
865
866 /* Add this port group to the src table at each port that uses it */
867 PortIteratorData pit_data(prm->prmDstPort, po->group);
868 PortObject2Iterate(po, PortIteratorData::set, &pit_data);
869 }
870 }
871 }
872
873 /*
874 * Create and initialize the rule maps
875 */
fpCreateRuleMaps(SnortConfig * sc,RulePortTables * p)876 static void fpCreateRuleMaps(SnortConfig* sc, RulePortTables* p)
877 {
878 sc->prmIpRTNX = prmNewMap();
879 sc->prmIcmpRTNX = prmNewMap();
880 sc->prmTcpRTNX = prmNewMap();
881 sc->prmUdpRTNX = prmNewMap();
882
883 fpCreateInitRuleMap(sc->prmIpRTNX, p->ip.src, p->ip.dst, p->ip.any);
884 fpCreateInitRuleMap(sc->prmIcmpRTNX, p->icmp.src, p->icmp.dst, p->icmp.any);
885 fpCreateInitRuleMap(sc->prmTcpRTNX, p->tcp.src, p->tcp.dst, p->tcp.any);
886 fpCreateInitRuleMap(sc->prmUdpRTNX, p->udp.src, p->udp.dst, p->udp.any);
887 }
888
fpFreeRuleMaps(SnortConfig * sc)889 static void fpFreeRuleMaps(SnortConfig* sc)
890 {
891 if (sc == nullptr)
892 return;
893
894 if (sc->prmIpRTNX != nullptr)
895 {
896 snort_free(sc->prmIpRTNX);
897 sc->prmIpRTNX = nullptr;
898 }
899
900 if (sc->prmIcmpRTNX != nullptr)
901 {
902 snort_free(sc->prmIcmpRTNX);
903 sc->prmIcmpRTNX = nullptr;
904 }
905
906 if (sc->prmTcpRTNX != nullptr)
907 {
908 snort_free(sc->prmTcpRTNX);
909 sc->prmTcpRTNX = nullptr;
910 }
911
912 if (sc->prmUdpRTNX != nullptr)
913 {
914 snort_free(sc->prmUdpRTNX);
915 sc->prmUdpRTNX = nullptr;
916 }
917 }
918
fpGetFinalPattern(FastPatternConfig * fp,PatternMatchData * pmd,const char * & ret_pattern,unsigned & ret_bytes)919 static int fpGetFinalPattern(
920 FastPatternConfig* fp, PatternMatchData* pmd, const char*& ret_pattern, unsigned& ret_bytes)
921 {
922 assert(fp and pmd);
923
924 const char* pattern = pmd->pattern_buf;
925 unsigned bytes = pmd->pattern_size;
926
927 // Don't mess with:
928 //
929 // 1. fast pattern only contents - they should be inserted into the
930 // pattern matcher as is since the content won't be evaluated as a rule
931 // option.
932 //
933 // 2. negated contents since truncating them could inadvertently
934 // disable evaluation of a rule - the shorter pattern may be found,
935 // while the unaltered pattern may not be found, disabling inspection
936 // of a rule we should inspect.
937 //
938 // 3. non-literals like regex - truncation could invalidate the
939 // expression.
940
941 if ( pmd->is_negated() or !pmd->is_literal() )
942 {
943 ret_pattern = pattern;
944 ret_bytes = bytes;
945 return 0;
946 }
947
948 if ( pmd->is_fast_pattern() && (pmd->fp_offset || pmd->fp_length) )
949 {
950 /* (offset + length) potentially being larger than the pattern itself
951 * is taken care of during parsing */
952 assert(pmd->fp_offset + pmd->fp_length <= pmd->pattern_size);
953 pattern = pmd->pattern_buf + pmd->fp_offset;
954 bytes = pmd->fp_length ? pmd->fp_length : pmd->pattern_size - pmd->fp_length;
955 }
956
957 ret_pattern = pattern;
958 ret_bytes = fp->set_max(bytes);
959
960 return 0;
961 }
962
fpRuleGroupPrintRuleCount(RuleGroup * pg,const char * what)963 static void fpRuleGroupPrintRuleCount(RuleGroup* pg, const char* what)
964 {
965 int type;
966
967 if (pg == nullptr)
968 return;
969
970 LogMessage("RuleGroup rule summary (%s):\n", what);
971
972 for (type = PM_TYPE_PKT; type < PM_TYPE_MAX; type++)
973 {
974 if (pg->mpsegrp[type])
975 {
976 int count = pg->mpsegrp[type]->normal_mpse ?
977 pg->mpsegrp[type]->normal_mpse->get_pattern_count() : 0;
978 int count_ol = pg->mpsegrp[type]->offload_mpse ?
979 pg->mpsegrp[type]->offload_mpse->get_pattern_count() : 0;
980
981 if ( count )
982 LogMessage("\tNormal Pattern Matcher %s: %d\n", pm_type_strings[type], count);
983
984 if ( count_ol )
985 LogMessage("\tOffload Pattern Matcher %s: %d\n", pm_type_strings[type], count_ol);
986 }
987 }
988
989 if ( pg->nfp_rule_count )
990 LogMessage("\tNormal Pattern Matcher No content: %u\n", pg->nfp_rule_count);
991 }
992
fpDeletePMX(void * pv)993 static void fpDeletePMX(void* pv)
994 {
995 if ( pv )
996 snort_free(pv);
997 }
998
999 /*
1000 * Create the RuleGroup for these PortObject2 entities
1001 *
1002 * This builds the 1st pass multi-pattern state machines for
1003 * content and uricontent based on the rules in the PortObjects
1004 * hash table.
1005 */
fpCreatePortObject2RuleGroup(SnortConfig * sc,PortObject2 * po,PortObject2 * poaa)1006 static void fpCreatePortObject2RuleGroup(SnortConfig* sc, PortObject2* po, PortObject2* poaa)
1007 {
1008 assert( po );
1009
1010 po->group = nullptr;
1011 FastPatternConfig* fp = sc->fast_pattern_config;
1012 if ( fp->get_debug_print_rule_group_build_details() )
1013 PortObject2PrintPorts(po);
1014
1015 /* Check if we have any rules */
1016 if ( !po->rule_hash )
1017 return;
1018
1019 /* create a port_group */
1020 RuleGroup* pg = new RuleGroup;
1021 s_group = "port";
1022
1023 /*
1024 * Walk the rules in the PortObject and add to
1025 * the RuleGroup pattern state machine
1026 * and to the port group RULE_NODE lists.
1027 * (The lists are still used in some cases
1028 * during detection to walk the rules in a group
1029 * so we have to load these as well...fpEvalHeader()... for now.)
1030 *
1031 * po src/dst ports : content/uri and nocontent
1032 * poaa any-any ports : content/uri and nocontent
1033 *
1034 * each PG has src or dst contents, generic-contents, and no-contents
1035 * (src/dst or any-any ports)
1036 *
1037 */
1038 PortObject2* pox = po;
1039 while ( pox )
1040 {
1041 for (GHashNode* node = pox->rule_hash->find_first();
1042 node;
1043 node = pox->rule_hash->find_next())
1044 {
1045 unsigned sid, gid;
1046 int* prindex = (int*)node->data;
1047
1048 /* be safe - no rule index, ignore it */
1049 if (prindex == nullptr)
1050 continue;
1051
1052 /* look up gid:sid */
1053 parser_get_rule_ids(*prindex, gid, sid);
1054
1055 /* look up otn */
1056 OptTreeNode* otn = OtnLookup(sc->otn_map, gid, sid);
1057 assert(otn);
1058
1059 if ( is_network_protocol(otn->snort_protocol_id) )
1060 fpAddRuleGroupRule(sc, pg, otn, fp, false);
1061 }
1062
1063 if (fp->get_debug_print_rule_group_build_details())
1064 fpRuleGroupPrintRuleCount(pg, pox == po ? "ports" : "any");
1065
1066 if (pox == poaa)
1067 break;
1068
1069 pox = poaa;
1070 }
1071
1072 // This might happen if there was ip proto only rules...Don't return failure
1073 if (fpFinishRuleGroup(sc, pg, fp) != 0)
1074 return;
1075
1076 po->group = pg;
1077 return;
1078 }
1079
1080 /*
1081 * Create the port groups for this port table
1082 */
fpCreatePortTableRuleGroups(SnortConfig * sc,PortTable * p,PortObject2 * poaa)1083 static void fpCreatePortTableRuleGroups(SnortConfig* sc, PortTable* p, PortObject2* poaa)
1084 {
1085 int cnt = 1;
1086 FastPatternConfig* fp = sc->fast_pattern_config;
1087 if ( fp->get_debug_print_rule_group_build_details() )
1088 LogMessage("%d Port Groups in Port Table\n",p->pt_mpo_hash->get_count());
1089
1090 for (GHashNode* node = p->pt_mpo_hash->find_first();
1091 node;
1092 node = p->pt_mpo_hash->find_next())
1093 {
1094 PortObject2* po = (PortObject2*)node->data;
1095 if ( !po )
1096 continue;
1097
1098 if (fp->get_debug_print_rule_group_build_details())
1099 LogMessage("Creating Port Group Object %d of %d\n", cnt++, p->pt_mpo_hash->get_count());
1100
1101 /* if the object is not referenced, don't add it to the RuleGroups
1102 * as it may overwrite other objects that are more inclusive. */
1103 if ( !po->port_cnt )
1104 continue;
1105
1106 fpCreatePortObject2RuleGroup(sc, po, poaa);
1107 }
1108 }
1109
1110 /*
1111 * Create port group objects for all port tables
1112 *
1113 * note: any ports are standard PortObjects not PortObject2s so we have to
1114 * upgrade them for the create port group function
1115 */
fpCreateRuleGroups(SnortConfig * sc,RulePortTables * p)1116 static int fpCreateRuleGroups(SnortConfig* sc, RulePortTables* p)
1117 {
1118 if (!get_rule_count())
1119 return 0;
1120
1121 FastPatternConfig* fp = sc->fast_pattern_config;
1122 bool log_rule_group_details = fp->get_debug_print_rule_group_build_details();
1123
1124 /* IP */
1125 PortObject2* po2 = PortObject2Dup(*p->ip.any);
1126 PortObject2* add_any_any = fp->get_split_any_any() ? nullptr : po2;
1127
1128 if ( log_rule_group_details )
1129 LogMessage("\nIP-SRC ");
1130
1131 fpCreatePortTableRuleGroups(sc, p->ip.src, add_any_any);
1132
1133 if ( log_rule_group_details )
1134 LogMessage("\nIP-DST ");
1135
1136 fpCreatePortTableRuleGroups(sc, p->ip.dst, add_any_any);
1137
1138 if ( log_rule_group_details )
1139 LogMessage("\nIP-ANY ");
1140
1141 fpCreatePortObject2RuleGroup(sc, po2, nullptr);
1142 p->ip.any->group = po2->group;
1143 po2->group = nullptr;
1144 PortObject2Free(po2);
1145
1146 /* ICMP */
1147 po2 = PortObject2Dup(*p->icmp.any);
1148 add_any_any = fp->get_split_any_any() ? nullptr : po2;
1149
1150 if ( log_rule_group_details )
1151 LogMessage("\nICMP-SRC ");
1152
1153 fpCreatePortTableRuleGroups(sc, p->icmp.src, add_any_any);
1154
1155 if ( log_rule_group_details )
1156 LogMessage("\nICMP-DST ");
1157
1158 fpCreatePortTableRuleGroups(sc, p->icmp.dst, add_any_any);
1159
1160 if ( log_rule_group_details )
1161 LogMessage("\nICMP-ANY ");
1162
1163 fpCreatePortObject2RuleGroup(sc, po2, nullptr);
1164 p->icmp.any->group = po2->group;
1165 po2->group = nullptr;
1166 PortObject2Free(po2);
1167
1168 po2 = PortObject2Dup(*p->tcp.any);
1169 add_any_any = fp->get_split_any_any() ? nullptr : po2;
1170
1171 if ( log_rule_group_details )
1172 LogMessage("\nTCP-SRC ");
1173
1174 fpCreatePortTableRuleGroups(sc, p->tcp.src, add_any_any);
1175
1176 if ( log_rule_group_details )
1177 LogMessage("\nTCP-DST ");
1178
1179 fpCreatePortTableRuleGroups(sc, p->tcp.dst, add_any_any);
1180
1181 if ( log_rule_group_details )
1182 LogMessage("\nTCP-ANY ");
1183
1184 fpCreatePortObject2RuleGroup(sc, po2, nullptr);
1185 p->tcp.any->group = po2->group;
1186 po2->group = nullptr;
1187 PortObject2Free(po2);
1188
1189 /* UDP */
1190 po2 = PortObject2Dup(*p->udp.any);
1191 add_any_any = fp->get_split_any_any() ? nullptr : po2;
1192
1193 if ( log_rule_group_details )
1194 LogMessage("\nUDP-SRC ");
1195
1196 fpCreatePortTableRuleGroups(sc, p->udp.src, add_any_any);
1197
1198 if ( log_rule_group_details )
1199 LogMessage("\nUDP-DST ");
1200
1201 fpCreatePortTableRuleGroups(sc, p->udp.dst, add_any_any);
1202
1203 if ( log_rule_group_details )
1204 LogMessage("\nUDP-ANY ");
1205
1206 fpCreatePortObject2RuleGroup(sc, po2, nullptr);
1207 p->udp.any->group = po2->group;
1208 po2->group = nullptr;
1209 PortObject2Free(po2);
1210
1211 /* SVC */
1212 po2 = PortObject2Dup(*p->svc_any);
1213
1214 if ( log_rule_group_details )
1215 LogMessage("\nSVC-ANY ");
1216
1217 fpCreatePortObject2RuleGroup(sc, po2, nullptr);
1218 p->svc_any->group = po2->group;
1219 po2->group = nullptr;
1220 PortObject2Free(po2);
1221
1222 return 0;
1223 }
1224
1225 /*
1226 * Build a Port Group for this service based on the list of otns. The final
1227 * port_group pointer is stored using the service name as the key.
1228 *
1229 * p - hash table mapping services to port_groups
1230 * srvc- service name, key used to store the port_group
1231 * ...could use a service id instead (bytes, fixed length,etc...)
1232 * list- list of otns for this service
1233 */
fpBuildServiceRuleGroupByServiceOtnList(SnortConfig * sc,GHash * p,const char * srvc,SF_LIST * list,FastPatternConfig * fp)1234 static void fpBuildServiceRuleGroupByServiceOtnList(
1235 SnortConfig* sc, GHash* p, const char* srvc, SF_LIST* list, FastPatternConfig* fp)
1236 {
1237 RuleGroup* pg = new RuleGroup;
1238 s_group = srvc;
1239
1240 /*
1241 * add each rule to the service group pattern matchers,
1242 * or to the no-content rule list
1243 */
1244 SF_LNODE* cursor;
1245
1246 for (OptTreeNode* otn = (OptTreeNode*)sflist_first(list, &cursor);
1247 otn;
1248 otn = (OptTreeNode*)sflist_next(&cursor) )
1249 {
1250 fpAddRuleGroupRule(sc, pg, otn, fp, true);
1251 }
1252
1253 if (fpFinishRuleGroup(sc, pg, fp) != 0)
1254 return;
1255
1256 /* Add the port_group using it's service name */
1257 p->insert(srvc, pg);
1258 }
1259
1260 /*
1261 * For each service we create a RuleGroup based on the otn's defined to
1262 * be applicable to that service by the metadata option.
1263 *
1264 * Then we lookup the protocol/srvc ordinal in the target-based area
1265 * and assign the RuleGroup for the srvc to it.
1266 *
1267 * spg - service port group (lookup should be by service id/tag)
1268 * - this table maintains a port_group ptr for each service
1269 * srm - service rule map table (lookup by ascii service name)
1270 * - this table maintains a SF_LIST ptr (list of rule otns) for each service
1271 *
1272 */
fpBuildServiceRuleGroups(SnortConfig * sc,GHash * spg,RuleGroupVector & sopg,GHash * srm,FastPatternConfig * fp)1273 static void fpBuildServiceRuleGroups(
1274 SnortConfig* sc, GHash* spg, RuleGroupVector& sopg, GHash* srm, FastPatternConfig* fp)
1275 {
1276 for (GHashNode* n = srm->find_first(); n; n = srm->find_next())
1277 {
1278 SF_LIST* list = (SF_LIST*)n->data;
1279 const char* srvc = (const char*)n->key;
1280
1281 assert(list and srvc);
1282
1283 fpBuildServiceRuleGroupByServiceOtnList(sc, spg, srvc, list, fp);
1284
1285 /* Add this RuleGroup to the protocol-ordinal -> port_group table */
1286 RuleGroup* pg = (RuleGroup*)spg->find(srvc);
1287 if ( !pg )
1288 {
1289 ParseError("*** failed to create and find a port group for '%s'",srvc);
1290 continue;
1291 }
1292 SnortProtocolId snort_protocol_id = sc->proto_ref->find(srvc);
1293 assert(snort_protocol_id != UNKNOWN_PROTOCOL_ID);
1294 assert((unsigned)snort_protocol_id < sopg.size());
1295
1296 sopg[snort_protocol_id] = pg;
1297 }
1298 }
1299
1300 /*
1301 * For each proto+dir+service build a RuleGroup
1302 */
fpCreateServiceMapRuleGroups(SnortConfig * sc)1303 static void fpCreateServiceMapRuleGroups(SnortConfig* sc)
1304 {
1305 FastPatternConfig* fp = sc->fast_pattern_config;
1306
1307 sc->spgmmTable = ServiceRuleGroupMapNew();
1308 sc->sopgTable = new sopg_table_t(sc->proto_ref->get_count());
1309
1310 fpBuildServiceRuleGroups(sc, sc->spgmmTable->to_srv,
1311 sc->sopgTable->to_srv, sc->srmmTable->to_srv, fp);
1312
1313 fpBuildServiceRuleGroups(sc, sc->spgmmTable->to_cli,
1314 sc->sopgTable->to_cli, sc->srmmTable->to_cli, fp);
1315 }
1316
1317 /*
1318 * Print the rule gid:sid based onm the otn list
1319 */
fpPrintRuleList(SF_LIST * list)1320 static void fpPrintRuleList(SF_LIST* list)
1321 {
1322 OptTreeNode* otn;
1323 SF_LNODE* cursor;
1324
1325 for ( otn=(OptTreeNode*)sflist_first(list, &cursor);
1326 otn;
1327 otn=(OptTreeNode*)sflist_next(&cursor) )
1328 {
1329 LogMessage("| %u:%u\n", otn->sigInfo.gid, otn->sigInfo.sid);
1330 }
1331 }
1332
fpPrintServiceRuleMapTable(GHash * p,const char * dir)1333 static void fpPrintServiceRuleMapTable(GHash* p, const char* dir)
1334 {
1335 GHashNode* n;
1336
1337 if ( !p || !p->get_count() )
1338 return;
1339
1340 std::string label = "service rule counts - ";
1341 label += dir;
1342 LogLabel(label.c_str());
1343
1344 for (n = p->find_first(); n; n = p->find_next())
1345 {
1346 SF_LIST* list;
1347
1348 list = (SF_LIST*)n->data;
1349 if ( !list )
1350 continue;
1351
1352 if ( !n->key )
1353 continue;
1354
1355 LogCount((const char*)n->key, list->count);
1356
1357 fpPrintRuleList(list);
1358 }
1359 }
1360
fpPrintServiceRuleMaps(SnortConfig * sc)1361 static void fpPrintServiceRuleMaps(SnortConfig* sc)
1362 {
1363 fpPrintServiceRuleMapTable(sc->srmmTable->to_srv, "to server");
1364 fpPrintServiceRuleMapTable(sc->srmmTable->to_cli, "to client");
1365 }
1366
fp_print_service_rules(SnortConfig * sc,GHash * cli,GHash * srv)1367 static void fp_print_service_rules(SnortConfig* sc, GHash* cli, GHash* srv)
1368 {
1369 if ( !cli->get_count() and !srv->get_count() )
1370 return;
1371
1372 LogLabel("service rule counts to-srv to-cli");
1373
1374 uint16_t idx = 0;
1375 unsigned ctot = 0, stot = 0;
1376
1377 while ( const char* svc = sc->proto_ref->get_name_sorted(idx++) )
1378 {
1379 SF_LIST* clist = (SF_LIST*)cli->find(svc);
1380 SF_LIST* slist = (SF_LIST*)srv->find(svc);
1381
1382 if ( !clist and !slist )
1383 continue;
1384
1385 unsigned nc = clist ? clist->count : 0;
1386 unsigned ns = slist ? slist->count : 0;
1387
1388 LogMessage("%25.25s: %8u%8u\n", svc, nc, ns);
1389
1390 ctot += nc;
1391 stot += ns;
1392 }
1393 if ( ctot or stot )
1394 LogMessage("%25.25s: %8u%8u\n", "total", ctot, stot);
1395 }
1396
fp_print_service_rules_by_proto(SnortConfig * sc)1397 static void fp_print_service_rules_by_proto(SnortConfig* sc)
1398 {
1399 fp_print_service_rules(sc, sc->srmmTable->to_srv, sc->srmmTable->to_cli);
1400 }
1401
fp_sum_port_groups(RuleGroup * pg,unsigned c[PM_TYPE_MAX])1402 static void fp_sum_port_groups(RuleGroup* pg, unsigned c[PM_TYPE_MAX])
1403 {
1404 if ( !pg )
1405 return;
1406
1407 for ( int i = PM_TYPE_PKT; i < PM_TYPE_MAX; ++i )
1408 if ( pg->mpsegrp[i] and pg->mpsegrp[i]->normal_mpse and
1409 pg->mpsegrp[i]->normal_mpse->get_pattern_count() )
1410 c[i]++;
1411 }
1412
fp_sum_service_groups(GHash * h,unsigned c[PM_TYPE_MAX])1413 static void fp_sum_service_groups(GHash* h, unsigned c[PM_TYPE_MAX])
1414 {
1415 for (GHashNode* node = h->find_first();
1416 node;
1417 node = h->find_next())
1418 {
1419 RuleGroup* pg = (RuleGroup*)node->data;
1420 fp_sum_port_groups(pg, c);
1421 }
1422 }
1423
fp_print_service_groups(srmm_table_t * srmm)1424 static void fp_print_service_groups(srmm_table_t* srmm)
1425 {
1426 unsigned to_srv[PM_TYPE_MAX] = { };
1427 unsigned to_cli[PM_TYPE_MAX] = { };
1428
1429 fp_sum_service_groups(srmm->to_srv, to_srv);
1430 fp_sum_service_groups(srmm->to_cli, to_cli);
1431
1432 bool label = true;
1433
1434 for ( int i = PM_TYPE_PKT; i < PM_TYPE_MAX; ++i )
1435 {
1436 if ( !to_srv[i] and !to_cli[i] )
1437 continue;
1438
1439 if ( label )
1440 {
1441 LogLabel("fast pattern service groups to-srv to-cli");
1442 label = false;
1443 }
1444 LogMessage("%25.25s: %8u%8u\n", pm_type_strings[i], to_srv[i], to_cli[i]);
1445 }
1446 }
1447
fp_sum_port_groups(PortTable * tab,unsigned c[PM_TYPE_MAX])1448 static void fp_sum_port_groups(PortTable* tab, unsigned c[PM_TYPE_MAX])
1449 {
1450 for (GHashNode* node = tab->pt_mpo_hash->find_first();
1451 node;
1452 node = tab->pt_mpo_hash->find_next())
1453 {
1454 PortObject2* po = (PortObject2*)node->data;
1455 fp_sum_port_groups(po->group, c);
1456 PortObject2Finalize(po);
1457 }
1458 PortTableFinalize(tab);
1459 }
1460
fp_print_port_groups(RulePortTables * port_tables)1461 static void fp_print_port_groups(RulePortTables* port_tables)
1462 {
1463 unsigned src[PM_TYPE_MAX] = { };
1464 unsigned dst[PM_TYPE_MAX] = { };
1465 unsigned any[PM_TYPE_MAX] = { };
1466
1467 fp_sum_port_groups(port_tables->ip.src, src);
1468 fp_sum_port_groups(port_tables->ip.dst, dst);
1469 fp_sum_port_groups((RuleGroup*)port_tables->ip.any->group, any);
1470
1471 PortObjectFinalize(port_tables->ip.any);
1472 PortObjectFinalize(port_tables->ip.nfp);
1473
1474 fp_sum_port_groups(port_tables->icmp.src, src);
1475 fp_sum_port_groups(port_tables->icmp.dst, dst);
1476 fp_sum_port_groups((RuleGroup*)port_tables->icmp.any->group, any);
1477
1478 PortObjectFinalize(port_tables->icmp.any);
1479 PortObjectFinalize(port_tables->icmp.nfp);
1480
1481 fp_sum_port_groups(port_tables->tcp.src, src);
1482 fp_sum_port_groups(port_tables->tcp.dst, dst);
1483 fp_sum_port_groups((RuleGroup*)port_tables->tcp.any->group, any);
1484
1485 PortObjectFinalize(port_tables->tcp.any);
1486 PortObjectFinalize(port_tables->tcp.nfp);
1487
1488 fp_sum_port_groups(port_tables->udp.src, src);
1489 fp_sum_port_groups(port_tables->udp.dst, dst);
1490 fp_sum_port_groups((RuleGroup*)port_tables->udp.any->group, any);
1491
1492 PortObjectFinalize(port_tables->udp.any);
1493 PortObjectFinalize(port_tables->udp.nfp);
1494
1495 bool label = true;
1496
1497 for ( int i = PM_TYPE_PKT; i < PM_TYPE_MAX; ++i )
1498 {
1499 if ( !src[i] and !dst[i] and !any[i] )
1500 continue;
1501
1502 if ( label )
1503 {
1504 LogLabel("fast pattern port groups src dst any");
1505 label = false;
1506 }
1507 LogMessage("%25.25s: %8u%8u%8u\n", pm_type_strings[i], src[i], dst[i], any[i]);
1508 }
1509 }
1510
1511 /*
1512 * Build Service based RuleGroups using the rules
1513 * metadata option service parameter.
1514 */
fpCreateServiceRuleGroups(SnortConfig * sc)1515 static void fpCreateServiceRuleGroups(SnortConfig* sc)
1516 {
1517 FastPatternConfig* fp = sc->fast_pattern_config;
1518
1519 sc->srmmTable = ServiceMapNew();
1520
1521 fpCreateServiceMaps(sc);
1522 fp_print_service_rules_by_proto(sc);
1523
1524 if ( fp->get_debug_print_rule_group_build_details() )
1525 fpPrintServiceRuleMaps(sc);
1526
1527 fpCreateServiceMapRuleGroups(sc);
1528
1529 if (fp->get_debug_print_rule_group_build_details())
1530 fpPrintServiceRuleGroupSummary(sc);
1531
1532 ServiceMapFree(sc->srmmTable);
1533 sc->srmmTable = nullptr;
1534 }
1535
can_build_mt(FastPatternConfig * fp)1536 static unsigned can_build_mt(FastPatternConfig* fp)
1537 {
1538 if ( Snort::is_reloading() )
1539 return false;
1540
1541 const MpseApi* search_api = fp->get_search_api();
1542 assert(search_api);
1543
1544 if ( !MpseManager::parallel_compiles(search_api) )
1545 return false;
1546
1547 const MpseApi* offload_search_api = fp->get_offload_search_api();
1548
1549 if ( offload_search_api and !MpseManager::parallel_compiles(offload_search_api) )
1550 return false;
1551
1552 return true;
1553 }
1554
1555 /*
1556 * 7/2007 - man
1557 * Build Pattern Groups for 1st pass of content searching using
1558 * multi-pattern search method.
1559 */
fpCreateFastPacketDetection(SnortConfig * sc)1560 int fpCreateFastPacketDetection(SnortConfig* sc)
1561 {
1562 assert(sc);
1563
1564 RulePortTables* port_tables = sc->port_tables;
1565 FastPatternConfig* fp = sc->fast_pattern_config;
1566 bool log_rule_group_details = fp->get_debug_print_rule_group_build_details();
1567
1568 assert(port_tables);
1569 assert(fp);
1570
1571 if ( !get_rule_count() )
1572 {
1573 sc->sopgTable = new sopg_table_t(sc->proto_ref->get_count());
1574 return 0;
1575 }
1576
1577 mpse_count = 0;
1578 offload_mpse_count = 0;
1579
1580 MpseManager::start_search_engine(fp->get_search_api());
1581
1582 if ( log_rule_group_details )
1583 LogMessage("Creating Port Groups....\n");
1584
1585 fpCreateRuleGroups(sc, port_tables);
1586
1587 if ( log_rule_group_details )
1588 {
1589 LogMessage("Port Groups Done....\n");
1590 LogMessage("Creating Rule Maps....\n");
1591 }
1592
1593 fpCreateRuleMaps(sc, port_tables);
1594
1595 if ( log_rule_group_details )
1596 {
1597 LogMessage("Rule Maps Done....\n");
1598 LogMessage("Creating Service Based Rule Maps....\n");
1599 }
1600
1601 fpCreateServiceRuleGroups(sc);
1602
1603 if ( log_rule_group_details )
1604 LogMessage("Service Based Rule Maps Done....\n");
1605
1606 unsigned mpse_loaded = 0;
1607 unsigned mpse_dumped = 0;
1608
1609 if ( !sc->test_mode() or sc->mem_check() )
1610 {
1611 if ( !fp->get_rule_db_dir().empty() )
1612 mpse_loaded = fp_deserialize(sc, fp->get_rule_db_dir());
1613
1614 unsigned c = compile_mpses(sc, can_build_mt(fp));
1615 unsigned expected = mpse_count + offload_mpse_count;
1616
1617 if ( c != expected )
1618 ParseError("Failed to compile %u search engines", expected - c);
1619
1620 fixup_trees(sc);
1621 }
1622
1623 fp_print_port_groups(port_tables);
1624 fp_print_service_groups(sc->spgmmTable);
1625
1626 if ( !sc->rule_db_dir.empty() )
1627 mpse_dumped = fp_serialize(sc, sc->rule_db_dir);
1628
1629 if ( mpse_count )
1630 {
1631 LogLabel("search engine");
1632 MpseManager::print_mpse_summary(fp->get_search_api());
1633 }
1634
1635 if ( offload_mpse_count and (fp->get_offload_search_api()))
1636 {
1637 LogLabel("offload search engine");
1638 MpseManager::print_mpse_summary(fp->get_offload_search_api());
1639 }
1640
1641 LogCount("truncated patterns", fp->get_num_patterns_truncated());
1642 LogCount("mpse_loaded", mpse_loaded);
1643 LogCount("mpse_dumped", mpse_dumped);
1644
1645 MpseManager::setup_search_engine(fp->get_search_api(), sc);
1646
1647 return 0;
1648 }
1649
fpDeleteFastPacketDetection(SnortConfig * sc)1650 void fpDeleteFastPacketDetection(SnortConfig* sc)
1651 {
1652 if (sc == nullptr)
1653 return;
1654
1655 /* Cleanup the detection option tree */
1656 delete sc->detection_option_hash_table;
1657 delete sc->detection_option_tree_hash_table;
1658
1659 fpFreeRuleMaps(sc);
1660 ServiceRuleGroupMapFree(sc->spgmmTable);
1661
1662 if ( sc->sopgTable )
1663 delete sc->sopgTable;
1664 }
1665
print_nfp_info(const char * group,OptTreeNode * otn)1666 static void print_nfp_info(const char* group, OptTreeNode* otn)
1667 {
1668 if ( otn->warned_fp() )
1669 return;
1670
1671 const char* type = otn->longestPatternLen ? "negated" : "no";
1672
1673 ParseWarning(WARN_RULES, "%s rule %u:%u:%u has %s fast pattern",
1674 group, otn->sigInfo.gid, otn->sigInfo.sid, otn->sigInfo.rev, type);
1675
1676 otn->set_warned_fp();
1677 }
1678
get_pattern_info(const PatternMatchData * pmd,const char * pattern,int pattern_length,string & hex,string & txt,string & opts)1679 void get_pattern_info(const PatternMatchData* pmd,
1680 const char* pattern, int pattern_length, string& hex, string& txt, string& opts)
1681 {
1682 char buf[8];
1683
1684 for ( int i = 0; i < pattern_length; ++i )
1685 {
1686 snprintf(buf, sizeof(buf), "%2.02X ", (uint8_t)pattern[i]);
1687 hex += buf;
1688 txt += isprint(pattern[i]) ? pattern[i] : '.';
1689 }
1690 opts = "(";
1691 if ( pmd->is_fast_pattern() )
1692 opts += " user";
1693 if ( pmd->is_negated() )
1694 opts += " negated";
1695 opts += " )";
1696 }
1697
print_fp_info(const char * group,const OptTreeNode * otn,const PatternMatchData * pmd,const char * pattern,unsigned pattern_length)1698 static void print_fp_info(
1699 const char* group, const OptTreeNode* otn, const PatternMatchData* pmd,
1700 const char* pattern, unsigned pattern_length)
1701 {
1702 std::string hex, txt, opts;
1703
1704 get_pattern_info(pmd, pattern, pattern_length, hex, txt, opts);
1705 LogMessage("FP %s %u:%u:%u %s[%d] = '%s' |%s| %s\n",
1706 group, otn->sigInfo.gid, otn->sigInfo.sid, otn->sigInfo.rev,
1707 pm_type_strings[pmd->pm_type], pattern_length,
1708 txt.c_str(), hex.c_str(), opts.c_str());
1709 }
1710