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