1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 2013-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 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "parse_rule.h"
25 
26 #include "actions/actions.h"
27 #include "detection/detect.h"
28 #include "detection/fp_config.h"
29 #include "detection/fp_utils.h"
30 #include "detection/rtn_checks.h"
31 #include "detection/treenodes.h"
32 #include "framework/decode_data.h"
33 #include "hash/xhash.h"
34 #include "log/messages.h"
35 #include "main/snort_config.h"
36 #include "main/thread_config.h"
37 #include "managers/ips_manager.h"
38 #include "managers/module_manager.h"
39 #include "managers/so_manager.h"
40 #include "ports/rule_port_tables.h"
41 #include "sfip/sf_ipvar.h"
42 #include "sfip/sf_vartable.h"
43 #include "target_based/snort_protocols.h"
44 #include "utils/util.h"
45 #include "ips_options/extract.h"
46 
47 #include "parser.h"
48 #include "parse_conf.h"
49 #include "parse_ports.h"
50 
51 #include "parser.h"
52 #include "cmd_line.h"
53 #include "config_file.h"
54 #include "parse_conf.h"
55 #include "parse_ports.h"
56 
57 using namespace snort;
58 
59 #define SRC  0
60 #define DST  1
61 
62 /* rule counts for port lists */
63 struct rule_count_t
64 {
65     int src;
66     int dst;
67     int any;
68     int both;
69 };
70 
71 static int rule_count = 0;
72 static int prev_rule_count = 0;
73 static int skip_count = 0;
74 static int detect_rule_count = 0;
75 static int builtin_rule_count = 0;
76 static int so_rule_count = 0;
77 static int head_count = 0;          // rule headers
78 static int otn_count = 0;           // rule bodies
79 static int dup_count = 0;           // rule bodies
80 static int prev_dup_count = 0;
81 static int rule_proto = 0;
82 
83 static rule_count_t tcpCnt;
84 static rule_count_t udpCnt;
85 static rule_count_t icmpCnt;
86 static rule_count_t ipCnt;
87 static rule_count_t svcCnt;  // dummy for now
88 
89 static bool s_ignore = false;  // for skipping drop rules when not inline, etc.
90 static bool s_capture = false;
91 
92 static std::string s_type;
93 static std::string s_body;
94 
95 struct SoRule
96 {
SoRuleSoRule97     SoRule(RuleTreeNode* rtn, const OptTreeNode* otn) :
98         rtn(rtn), gid(otn->sigInfo.gid), sid(otn->sigInfo.sid), rev(otn->sigInfo.rev) { }
99 
100     RuleTreeNode* rtn;
101     uint32_t gid;
102     uint32_t sid;
103     uint32_t rev;
104 };
105 
106 static SoRule* s_so_rule = nullptr;
107 
ValidateIPList(sfip_var_t * addrset,const char * token)108 static int ValidateIPList(sfip_var_t* addrset, const char* token)
109 {
110     if (!addrset || !(addrset->head||addrset->neg_head))
111     {
112         ParseError("empty IP used either as source IP or as "
113             "destination IP in a rule. IP list: %s.", token);
114         return -1;
115     }
116     return 0;
117 }
118 
ProcessIP(SnortConfig * sc,const char * addr,RuleTreeNode * rtn,int mode,int)119 static int ProcessIP(SnortConfig* sc, const char* addr, RuleTreeNode* rtn, int mode, int)
120 {
121     vartable_t* ip_vartable = get_ips_policy()->ip_vartable;
122 
123     assert(rtn);
124     /* If a rule has a variable in it, we want to copy that variable's
125      * contents to the IP variable (IP list) stored with the rtn.
126      * This code tries to look up the variable, and if found, will copy it
127      * to the rtn->{sip,dip} */
128     if (mode == SRC)
129     {
130         int ret;
131 
132         if ( !rtn->sip )
133         {
134             sfip_var_t* tmp = sfvt_lookup_var(ip_vartable, addr);
135             if ( tmp )
136             {
137                 rtn->sip = sfvar_create_alias(tmp, tmp->name);
138                 ret = rtn->sip ?  SFIP_SUCCESS : SFIP_FAILURE;
139             }
140             else
141             {
142                 rtn->sip = (sfip_var_t*)snort_calloc(sizeof(sfip_var_t));
143                 ret = sfvt_add_to_var(ip_vartable, rtn->sip, addr);
144             }
145         }
146         else
147         {
148             ret = sfvt_add_to_var(ip_vartable, rtn->sip, addr);
149         }
150 
151         /* The function sfvt_add_to_var adds 'addr' to the variable 'rtn->sip' */
152         if ( ret == SFIP_LOOKUP_FAILURE and sc->dump_rule_info() )
153             ret = sfvt_add_to_var(ip_vartable, rtn->sip, "any");
154 
155         if (ret != SFIP_SUCCESS)
156         {
157             if (ret == SFIP_LOOKUP_FAILURE)
158             {
159                 ParseError("Undefined variable in the string: %s.", addr);
160                 return -1;
161             }
162             else if (ret == SFIP_CONFLICT)
163             {
164                 ParseError("Negated IP ranges that are more general than "
165                     "non-negated ranges are not allowed. Consider "
166                     "inverting the logic: %s.", addr);
167                 return -1;
168             }
169             else if (ret == SFIP_NOT_ANY)
170             {
171                 ParseError("!any is not allowed: %s.", addr);
172                 return -1;
173             }
174             else
175             {
176                 ParseError("Unable to process the IP address: %s.", addr);
177                 return -1;
178             }
179         }
180 
181         if (rtn->sip->head && rtn->sip->head->flags & SFIP_ANY)
182         {
183             rtn->flags |= RuleTreeNode::ANY_SRC_IP;
184         }
185     }
186     /* mode == DST */
187     else
188     {
189         int ret;
190 
191         if ( !rtn->dip )
192         {
193             sfip_var_t* tmp = sfvt_lookup_var(ip_vartable, addr);
194             if ( tmp )
195             {
196                 rtn->dip = sfvar_create_alias(tmp, tmp->name);
197                 ret = rtn->dip ?  SFIP_SUCCESS : SFIP_FAILURE;
198             }
199             else
200             {
201                 rtn->dip = (sfip_var_t*)snort_calloc(sizeof(sfip_var_t));
202                 ret = sfvt_add_to_var(ip_vartable, rtn->dip, addr);
203             }
204         }
205         else
206         {
207             ret = sfvt_add_to_var(ip_vartable, rtn->dip, addr);
208         }
209 
210         if ( ret == SFIP_LOOKUP_FAILURE and sc->dump_rule_info() )
211             ret = sfvt_add_to_var(ip_vartable, rtn->dip, "any");
212 
213         if ( ret != SFIP_SUCCESS )
214         {
215             if (ret == SFIP_LOOKUP_FAILURE)
216             {
217                 ParseError("undefined variable in the string: %s.", addr);
218                 return -1;
219             }
220             else if (ret == SFIP_CONFLICT)
221             {
222                 ParseError("negated IP ranges that are more general than "
223                     "non-negated ranges are not allowed. Consider "
224                     "inverting the logic: %s.", addr);
225                 return -1;
226             }
227             else if (ret == SFIP_NOT_ANY)
228             {
229                 ParseError("!any is not allowed: %s.", addr);
230                 return -1;
231             }
232             else
233             {
234                 ParseError("unable to process the IP address: %s.", addr);
235                 return -1;
236             }
237         }
238 
239         if (rtn->dip->head && rtn->dip->head->flags & SFIP_ANY)
240         {
241             rtn->flags |= RuleTreeNode::ANY_DST_IP;
242         }
243     }
244 
245     /* Make sure the IP lists provided by the user are valid */
246     if (mode == SRC)
247         ValidateIPList(rtn->sip, addr);
248     else
249         ValidateIPList(rtn->dip, addr);
250 
251     return 0;
252 }
253 
254 /*
255 *  Parse a port string as a port var, and create or find a port object for it,
256 *  and add it to the port var table. These are used by the rtn's
257 *  as src and dst port lists for final rtn/otn processing.
258 *
259 *  These should not be confused with the port objects used to merge ports and rules
260 *  to build port group objects. Those are generated after the otn processing.
261 */
ParsePortListTcpUdpPort(PortVarTable * pvt,PortTable * noname,const char * port_str)262 static PortObject* ParsePortListTcpUdpPort(
263     PortVarTable* pvt, PortTable* noname, const char* port_str)
264 {
265     PortObject* portobject;
266     POParser poparser;
267 
268     if ( !pvt or !noname or !port_str )
269         return nullptr;
270 
271     /* 1st - check if we have an any port */
272     if ( strcasecmp(port_str,"any")== 0 )
273     {
274         portobject = PortVarTableFind(pvt, "any");
275         if ( !portobject )
276             ParseAbort("PortVarTable missing an 'any' variable.");
277 
278         return portobject;
279     }
280     /* 2nd - check if we have a PortVar */
281     else if ( port_str[0]=='$' )
282     {
283         /*||isalpha(port_str[0])*/ /*TODO: interferes with protocol names for ports*/
284         const char* name = port_str + 1;
285 
286         /* look it up  in the port var table */
287         portobject = PortVarTableFind(pvt, name, true);
288         if ( !portobject )
289             ParseAbort("***PortVar Lookup failed on '%s'.", port_str);
290 
291     }
292     /* 3rd -  and finally process a raw port list */
293     else
294     {
295         /* port list = [p,p,p:p,p,...] or p or p:p , no embedded spaces due to tokenizer */
296         PortObject* pox;
297 
298         portobject = PortObjectParseString(pvt, &poparser, nullptr, port_str, 0);
299 
300         if ( !portobject )
301         {
302             const char* errstr = PortObjectParseError(&poparser);
303             ParseAbort("***Rule--PortVar Parse error: (pos=%d,error=%s)\n>>%s\n>>%*s",
304                 poparser.pos,errstr,port_str,poparser.pos,"^");
305         }
306 
307         /* check if we already have this port object in the un-named port var table  ... */
308         pox = PortTableFindInputPortObjectPorts(noname, portobject);
309         if ( pox )
310         {
311             PortObjectFree(portobject);
312             portobject = pox;
313         }
314         else
315         {
316             /* Add to the un-named port var table */
317             if (PortTableAddObject(noname, portobject))
318             {
319                 ParseAbort("Unable to add raw port object to unnamed "
320                     "port var table, out of memory.");
321             }
322         }
323     }
324 
325     return portobject;
326 }
327 
328 /*
329  *   Process the rule, add it to the appropriate PortObject
330  *   and add the PortObject to the rtn.
331  *
332  *   TCP/UDP rules use ports/portlists, icmp uses the icmp type field and ip uses the protocol
333  *   field as a dst port for the purposes of looking up a rule group as packets are being
334  *   processed.
335  *
336  *   TCP/UDP- use src/dst ports
337  *   ICMP   - use icmp type as dst port,src=-1
338  *   IP     - use protocol as dst port,src=-1
339  *
340  *   rtn - proto_node
341  *   port_str - port list string or port var name
342  *   dst_flag - dst or src port flag, true = dst, false = src
343  *
344  */
ParsePortList(RuleTreeNode * rtn,PortVarTable * pvt,PortTable * noname,const char * port_str,int dst_flag)345 static int ParsePortList(
346     RuleTreeNode* rtn, PortVarTable* pvt, PortTable* noname,
347     const char* port_str, int dst_flag)
348 {
349     PortObject* portobject;  /* src or dst */
350 
351     /* Get the protocol specific port object */
352     if ( rule_proto & (PROTO_BIT__TCP | PROTO_BIT__UDP | PROTO_BIT__PDU) )
353     {
354         portobject = ParsePortListTcpUdpPort(pvt, noname, port_str);
355     }
356     else /* ICMP, IP  - no real ports just Type and Protocol */
357     {
358         portobject = PortVarTableFind(pvt, "any");
359         if ( !portobject )
360         {
361             ParseError("PortVarTable missing an 'any' variable.");
362             return -1;
363         }
364     }
365 
366     /* !ports - port lists can be mixed 80:90,!82,
367     * so the old NOT flag is deprecated for port lists
368     */
369 
370     /* set up any any flags */
371     if ( PortObjectHasAny(portobject) )
372     {
373         if ( dst_flag )
374             rtn->flags |= RuleTreeNode::ANY_DST_PORT;
375         else
376             rtn->flags |= RuleTreeNode::ANY_SRC_PORT;
377     }
378 
379     /* check for a pure not rule - fatal if we find one */
380     if ( PortObjectIsPureNot(portobject) )
381     {
382         ParseError("Pure NOT ports are not allowed.");
383         return -1;
384     }
385 
386     /*
387     * set to the port object for this rules src/dst port,
388     * these are used during rtn/otn port verification of the rule.
389     */
390 
391     if (dst_flag)
392         rtn->dst_portobject = portobject;
393     else
394         rtn->src_portobject = portobject;
395 
396     return 0;
397 }
398 
same_headers(RuleTreeNode * rule,RuleTreeNode * rtn)399 bool same_headers(RuleTreeNode* rule, RuleTreeNode* rtn)
400 {
401     if ( !rule or !rtn )
402         return false;
403 
404     if (rule->action != rtn->action)
405         return false;
406 
407     if (rule->snort_protocol_id != rtn->snort_protocol_id)
408         return false;
409 
410     /* For custom rule type declarations */
411     if (rule->listhead != rtn->listhead)
412         return false;
413 
414     if (rule->flags != rtn->flags)
415         return false;
416 
417     if ( rule->sip and rtn->sip and sfvar_compare(rule->sip, rtn->sip) != SFIP_EQUAL )
418         return false;
419 
420     if ( rule->dip and rtn->dip and sfvar_compare(rule->dip, rtn->dip) != SFIP_EQUAL )
421         return false;
422 
423     /* compare the port group pointers - this prevents confusing src/dst port objects
424      * with the same port set, and it's quicker. It does assume that we only have
425      * one port object and pointer for each unique port set...this is handled by the
426      * parsing and initial port object storage and lookup.  This must be consistent during
427      * the rule parsing phase. - man */
428     if ( (rule->src_portobject != rtn->src_portobject)
429         or (rule->dst_portobject != rtn->dst_portobject))
430     {
431         return false;
432     }
433     return true;
434 }
435 
XferHeader(RuleTreeNode * from,RuleTreeNode * to)436 static void XferHeader(RuleTreeNode* from, RuleTreeNode* to)
437 {
438     to->flags = from->flags;
439     to->action = from->action;
440     to->sip = from->sip;
441     to->dip = from->dip;
442 
443     to->listhead = from->listhead;
444     to->snort_protocol_id = from->snort_protocol_id;
445 
446     to->src_portobject = from->src_portobject;
447     to->dst_portobject = from->dst_portobject;
448 
449     to->header = from->header;
450 }
451 
452 /****************************************************************************
453  * Purpose:  Adds RuleTreeNode associated detection functions to the
454  *          current rule's function list
455  *
456  * Arguments: *func => function pointer to the detection function
457  *            rtn   => pointer to the current rule
458  ***************************************************************************/
AddRuleFuncToList(int (* rfunc)(Packet *,RuleTreeNode *,struct RuleFpList *,int),RuleTreeNode * rtn)459 static void AddRuleFuncToList(
460     int (* rfunc)(Packet*, RuleTreeNode*, struct RuleFpList*, int),
461     RuleTreeNode* rtn)
462 {
463     RuleFpList* idx = rtn->rule_func;
464 
465     if ( !idx )
466     {
467         rtn->rule_func = new RuleFpList;
468         rtn->rule_func->RuleHeadFunc = rfunc;
469     }
470     else
471     {
472         while ( idx->next )
473             idx = idx->next;
474 
475         idx->next = new RuleFpList;
476         idx = idx->next;
477         idx->RuleHeadFunc = rfunc;
478     }
479 }
480 
481 /****************************************************************************
482  * Purpose: Links the proper IP address testing function to the current RTN
483  *          based on the address, netmask, and addr flags
484  *
485  * Arguments: rtn => the pointer to the current rules list entry to attach to
486  *            mode => indicates whether this is a rule for the source
487  *                    or destination IP for the rule
488  ***************************************************************************/
AddrToFunc(RuleTreeNode * rtn,int mode)489 static void AddrToFunc(RuleTreeNode* rtn, int mode)
490 {
491     /*
492      * if IP and mask are both 0, this is a "any" IP and we don't need to
493      * check it
494      */
495     switch (mode)
496     {
497     case SRC:
498         if ((rtn->flags & RuleTreeNode::ANY_SRC_IP) == 0)
499         {
500             AddRuleFuncToList(CheckSrcIP, rtn);
501         }
502         break;
503 
504     case DST:
505         if ((rtn->flags & RuleTreeNode::ANY_DST_IP) == 0)
506         {
507             AddRuleFuncToList(CheckDstIP, rtn);
508         }
509         break;
510     }
511 }
512 
513 /****************************************************************************
514  * Purpose: Links in the port analysis function for the current rule
515  *
516  * Arguments: rtn => the pointer to the current rules list entry to attach to
517  *            any_flag =>  accept any port if set
518  *            except_flag => indicates negation (logical NOT) of the test
519  *            mode => indicates whether this is a rule for the source
520  *                    or destination port for the rule
521  ***************************************************************************/
PortToFunc(RuleTreeNode * rtn,int any_flag,int except_flag,int mode)522 static void PortToFunc(RuleTreeNode* rtn, int any_flag, int except_flag, int mode)
523 {
524     /*
525      * if the any flag is set we don't need to perform any test to match on
526      * this port
527      */
528     if (any_flag)
529         return;
530 
531     /* if the except_flag is up, test with the "NotEq" funcs */
532     if (except_flag)
533     {
534         switch (mode)
535         {
536         case SRC:
537             AddRuleFuncToList(CheckSrcPortNotEq, rtn);
538             break;
539 
540         case DST:
541             AddRuleFuncToList(CheckDstPortNotEq, rtn);
542             break;
543         }
544 
545         return;
546     }
547     /* default to setting the straight test function */
548     switch (mode)
549     {
550     case SRC:
551         AddRuleFuncToList(CheckSrcPortEqual, rtn);
552         break;
553 
554     case DST:
555         AddRuleFuncToList(CheckDstPortEqual, rtn);
556         break;
557     }
558 }
559 
560 // Configures the function list for the rule header detection
561 // functions (addrs and ports)
SetupRTNFuncList(RuleTreeNode * rtn)562 static void SetupRTNFuncList(RuleTreeNode* rtn)
563 {
564     if (rtn->flags & RuleTreeNode::BIDIRECTIONAL)
565         AddRuleFuncToList(CheckBidirectional, rtn);
566 
567     else
568     {
569         PortToFunc(rtn, (rtn->any_dst_port() ? 1 : 0), 0, DST);
570         PortToFunc(rtn, (rtn->any_src_port() ? 1 : 0), 0, SRC);
571 
572         AddrToFunc(rtn, SRC);
573         AddrToFunc(rtn, DST);
574     }
575 
576     if ( rtn->snort_protocol_id < SNORT_PROTO_FILE )
577         AddRuleFuncToList(CheckProto, rtn);
578     else
579         rtn->flags |= RuleTreeNode::USER_MODE;
580 
581     AddRuleFuncToList(RuleListEnd, rtn);
582 }
583 
584 // make a new node and stick it at the end of the list
transfer_rtn(RuleTreeNode * tmpl)585 static RuleTreeNode* transfer_rtn(RuleTreeNode* tmpl)
586 {
587     head_count++;
588     auto rtn = new RuleTreeNode;
589     XferHeader(tmpl, rtn);
590     SetupRTNFuncList(rtn);
591     return rtn;
592 }
593 
594 // Conditionally removes duplicate OTN. Keeps duplicate with
595 // higher revision.  If revision is the same, keeps newest rule.
mergeDuplicateOtn(SnortConfig * sc,OptTreeNode * otn_cur,OptTreeNode * otn_new,RuleTreeNode * rtn_new)596 static int mergeDuplicateOtn(
597     SnortConfig* sc, OptTreeNode* otn_cur,
598     OptTreeNode* otn_new, RuleTreeNode* rtn_new)
599 {
600     if (otn_cur->snort_protocol_id != otn_new->snort_protocol_id)
601     {
602         ParseError("GID %u SID %u in rule duplicates previous rule, with different protocol.",
603             otn_new->sigInfo.gid, otn_new->sigInfo.sid);
604         return true;
605     }
606 
607     RuleTreeNode* rtn_cur = getRtnFromOtn(otn_cur);
608 
609     if ( rtn_cur and rtn_cur->action != rtn_new->action )
610     {
611         ParseError("GID %u SID %u in rule duplicates previous rule, with different type.",
612             otn_new->sigInfo.gid, otn_new->sigInfo.sid);
613         return true;
614     }
615 
616     if ( otn_new->sigInfo.rev < otn_cur->sigInfo.rev )
617     {
618         // keep orig, free new
619         ParseWarning(WARN_RULES, "%u:%u duplicates previous rule. Using revision %u.",
620             otn_cur->sigInfo.gid, otn_cur->sigInfo.sid, otn_cur->sigInfo.rev);
621 
622         // OTN is for new policy group, salvage RTN
623         deleteRtnFromOtn(otn_new, sc);
624 
625         // Now free the OTN itself -- this function is also used
626         // by the hash-table calls out of OtnRemove, so it cannot
627         // be modified to delete data for rule options
628         delete otn_new;
629 
630         // Add rtn to current otn for the first rule instance in a policy,
631         // otherwise ignore it
632         if ( !rtn_cur )
633             addRtnToOtn(sc, otn_cur, rtn_new);
634         else
635             DestroyRuleTreeNode(rtn_new);
636 
637         return true;
638     }
639     // keep new, free orig
640     ParseWarning(WARN_RULES, "%u:%u duplicates previous rule. Using revision %u.",
641         otn_new->sigInfo.gid, otn_new->sigInfo.sid, otn_new->sigInfo.rev);
642 
643     for ( unsigned i = 0; i < otn_cur->proto_node_num; ++i )
644     {
645         RuleTreeNode* rtnTmp2 = deleteRtnFromOtn(otn_cur, i, sc, (rtn_cur != rtn_new));
646 
647         if ( rtnTmp2 and (i != get_ips_policy()->policy_id) )
648             addRtnToOtn(sc, otn_new, rtnTmp2, i);
649     }
650 
651     if (rtn_cur)
652         DestroyRuleTreeNode(rtn_cur);
653 
654     OtnRemove(sc->otn_map, otn_cur);
655     return false;
656 }
657 
658 namespace snort
659 {
get_rule_count()660 int get_rule_count()
661 { return rule_count; }
662 }
663 
get_policy_loaded_rule_count()664 int get_policy_loaded_rule_count()
665 {
666     auto policy_rule_count = rule_count - prev_rule_count;
667     prev_rule_count = rule_count;
668     return policy_rule_count;
669 }
670 
get_policy_shared_rule_count()671 int get_policy_shared_rule_count()
672 {
673     auto policy_rule_count = dup_count - prev_dup_count;
674     prev_dup_count = dup_count;
675     return policy_rule_count;
676 }
677 
parse_rule_init()678 void parse_rule_init()
679 {
680     rule_count = 0;
681     prev_rule_count = 0;
682     skip_count = 0;
683     detect_rule_count = 0;
684     builtin_rule_count = 0;
685     so_rule_count = 0;
686     head_count = 0;
687     otn_count = 0;
688     dup_count = 0;
689     prev_dup_count = 0;
690     rule_proto = 0;
691 
692     memset(&ipCnt, 0, sizeof(ipCnt));
693     memset(&icmpCnt, 0, sizeof(icmpCnt));
694     memset(&tcpCnt, 0, sizeof(tcpCnt));
695     memset(&udpCnt, 0, sizeof(udpCnt));
696     memset(&svcCnt, 0, sizeof(svcCnt));
697 }
698 
parse_rule_term()699 void parse_rule_term()
700 { }
701 
parse_rule_print()702 void parse_rule_print()
703 {
704     if ( !rule_count and !skip_count )
705         return;
706 
707     LogLabel("rule counts");
708     LogCount("total rules loaded", rule_count);
709     LogCount("total rules not loaded", skip_count);
710     LogCount("duplicate rules", dup_count);
711     LogCount("text rules", detect_rule_count);
712     LogCount("builtin rules", builtin_rule_count);
713     LogCount("so rules", so_rule_count);
714     LogCount("option chains", otn_count);
715     LogCount("chain headers", head_count);
716 
717     unsigned ip = ipCnt.src + ipCnt.dst + ipCnt.any + ipCnt.both;
718     unsigned icmp = icmpCnt.src + icmpCnt.dst + icmpCnt.any + icmpCnt.both;
719     unsigned tcp = tcpCnt.src + tcpCnt.dst + tcpCnt.any + tcpCnt.both;
720     unsigned udp = udpCnt.src + udpCnt.dst + udpCnt.any + udpCnt.both;
721 
722     if ( !ip and !icmp and !tcp and !udp )
723         return;
724 
725     LogLabel("port rule counts");
726     LogMessage("%8s%8s%8s%8s%8s\n", " ", "tcp", "udp", "icmp", "ip");
727 
728     if ( tcpCnt.any || udpCnt.any || icmpCnt.any || ipCnt.any )
729         LogMessage("%8s%8u%8u%8u%8u\n", "any",
730             tcpCnt.any, udpCnt.any, icmpCnt.any, ipCnt.any);
731 
732     if ( tcpCnt.src || udpCnt.src || icmpCnt.src || ipCnt.src )
733         LogMessage("%8s%8u%8u%8u%8u\n", "src",
734             tcpCnt.src, udpCnt.src, icmpCnt.src, ipCnt.src);
735 
736     if ( tcpCnt.dst || udpCnt.dst || icmpCnt.dst || ipCnt.dst )
737         LogMessage("%8s%8u%8u%8u%8u\n", "dst",
738             tcpCnt.dst, udpCnt.dst, icmpCnt.dst, ipCnt.dst);
739 
740     if ( tcpCnt.both || udpCnt.both || icmpCnt.both || ipCnt.both )
741         LogMessage("%8s%8u%8u%8u%8u\n", "both",
742             tcpCnt.both, udpCnt.both, icmpCnt.both, ipCnt.both);
743 
744     LogMessage("%8s%8u%8u%8u%8u\n", "total", tcp, udp, icmp, ip);
745 }
746 
parse_rule_type(SnortConfig * sc,const char * s,RuleTreeNode & rtn)747 void parse_rule_type(SnortConfig* sc, const char* s, RuleTreeNode& rtn)
748 {
749     IpsPolicy* p = get_ips_policy();
750 
751     if ( !p->action_override.empty() )
752         s = p->action_override.c_str();
753 
754     auto it = p->action_map.find(s);
755 
756     if ( it != p->action_map.end() )
757         s = it->second.c_str();
758 
759     s_type = s;
760     rtn = RuleTreeNode();
761 
762     if ( s_so_rule )
763         return;
764 
765     assert(s);
766 
767     rtn.action = Actions::get_type(s);
768 
769     if ( !Actions::is_valid_action(rtn.action) )
770     {
771         s_ignore = true;
772         ParseError("unknown rule action '%s'", s);
773         return;
774     }
775 
776     if ( sc->dump_rule_meta() )
777         rtn.header = new RuleHeader(s);
778 
779     rtn.listhead = get_rule_list(sc, s);
780 
781     if ( !rtn.listhead )
782     {
783         CreateRuleType(sc, s, rtn.action);
784         rtn.listhead = get_rule_list(sc, s);
785     }
786 
787     if ( sc->get_default_rule_state() )
788         rtn.set_enabled();
789 }
790 
parse_rule_proto(SnortConfig * sc,const char * s,RuleTreeNode & rtn,bool elided)791 void parse_rule_proto(SnortConfig* sc, const char* s, RuleTreeNode& rtn, bool elided)
792 {
793     if ( s_ignore )
794         return;
795 
796     if ( !s_so_rule and !elided and rtn.header )
797         rtn.header->proto = s;
798 
799     if ( !strcmp(s, "tcp") )
800         rule_proto = PROTO_BIT__TCP;
801 
802     else if ( !strcmp(s, "udp") )
803         rule_proto = PROTO_BIT__UDP;
804 
805     else if ( !strcmp(s, "icmp") )
806         rule_proto = PROTO_BIT__ICMP;
807 
808     else if ( !strcmp(s, "ip") )
809         rule_proto = PROTO_BIT__IP;
810 
811     else
812         // this will allow other protocols like http to have ports
813         rule_proto = PROTO_BIT__PDU;
814 
815     rtn.snort_protocol_id = sc->proto_ref->add(s);
816 
817     if ( rtn.snort_protocol_id == UNKNOWN_PROTOCOL_ID )
818     {
819         ParseError("bad protocol: %s", s);
820         rule_proto = 0;
821     }
822     else if ( s_so_rule and s_so_rule->rtn->snort_protocol_id != rtn.snort_protocol_id )
823         ParseWarning(WARN_RULES, "so rule proto can not be changed");
824 }
825 
parse_rule_nets(SnortConfig * sc,const char * s,bool src,RuleTreeNode & rtn,bool elided)826 void parse_rule_nets(
827     SnortConfig* sc, const char* s, bool src, RuleTreeNode& rtn, bool elided)
828 {
829     if ( s_so_rule )
830         return;
831 
832     if ( s_ignore )
833         return;
834 
835     if ( !elided and rtn.header )
836     {
837         if ( src )
838             rtn.header->src_nets = s;
839         else
840             rtn.header->dst_nets = s;
841     }
842     ProcessIP(sc, s, &rtn, src ? SRC : DST, 0);
843 }
844 
parse_rule_ports(SnortConfig *,const char * s,bool src,RuleTreeNode & rtn,bool elided)845 void parse_rule_ports(
846     SnortConfig*, const char* s, bool src, RuleTreeNode& rtn, bool elided)
847 {
848     if ( s_so_rule )
849         return;
850 
851     if ( s_ignore )
852         return;
853 
854     if ( !elided and rtn.header )
855     {
856         if ( src )
857             rtn.header->src_ports = s;
858         else
859             rtn.header->dst_ports = s;
860     }
861 
862     IpsPolicy* p = get_ips_policy();
863 
864     if ( ParsePortList(&rtn, p->portVarTable, p->nonamePortVarTable, s, src ? SRC : DST) )
865         ParseError("bad ports: '%s'", s);
866 }
867 
parse_rule_dir(SnortConfig *,const char * s,RuleTreeNode & rtn,bool elided)868 void parse_rule_dir(SnortConfig*, const char* s, RuleTreeNode& rtn, bool elided)
869 {
870     if ( s_so_rule )
871         return;
872 
873     if ( s_ignore )
874         return;
875 
876     if ( !elided and rtn.header )
877         rtn.header->dir = s;
878 
879     if (strcmp(s, "<>") == 0)
880         rtn.flags |= RuleTreeNode::BIDIRECTIONAL;
881 
882     else if ( strcmp(s, "->") )
883         ParseError("illegal direction specifier: %s", s);
884 }
885 
886 // Values of the rule options "pcre", "regex" and "sd_pattern" are already escaped
887 // They are not unescaped during the rule parsing
is_already_escaped(const std::string & opt_key)888 static bool is_already_escaped(const std::string& opt_key)
889 { return opt_key == "pcre" or opt_key == "regex" or opt_key == "sd_pattern"; }
890 
escape(const std::string & s)891 static std::string escape(const std::string& s)
892 {
893     std::string res;
894     int quotes_first = 0;
895     int quotes_last = std::count(s.begin(), s.end(), '"') - 1;
896     int quotes_count = quotes_first;
897 
898     for ( auto it = s.begin(); it != s.end(); ++it )
899     {
900         switch ( *it )
901         {
902         case '"':
903         {
904             if ( ( quotes_count > quotes_first ) and ( quotes_count < quotes_last ) )
905                 res += "\\\"";
906             else
907                 res += "\"";
908 
909             ++quotes_count;
910             continue;
911         }
912         case '\\': res += "\\\\"; continue;
913         case '\a': res += "\\a"; continue;
914         case '\b': res += "\\b"; continue;
915         case '\f': res += "\\f"; continue;
916         case '\n': res += "\\n"; continue;
917         case '\r': res += "\\r"; continue;
918         case '\t': res += "\\t"; continue;
919         case '\v': res += "\\v"; continue;
920         }
921 
922         res += *it;
923     }
924 
925     return res;
926 }
927 
parse_rule_opt_begin(SnortConfig * sc,const char * key)928 void parse_rule_opt_begin(SnortConfig* sc, const char* key)
929 {
930     if ( s_ignore )
931         return;
932 
933     if ( s_capture )
934     {
935         s_body += " ";
936         s_body += key;
937         s_body += ":";
938     }
939     IpsManager::option_begin(sc, key, rule_proto);
940 }
941 
parse_rule_opt_set(SnortConfig * sc,const char * key,const char * opt,const char * val)942 void parse_rule_opt_set(
943     SnortConfig* sc, const char* key, const char* opt, const char* val)
944 {
945     if ( s_ignore )
946         return;
947 
948     assert(opt);
949     assert(val);
950     if ( s_capture )
951     {
952         s_body +=  is_already_escaped(key) ? opt : escape(opt);
953         if ( *val )
954         {
955             s_body += " ";
956             s_body += val;
957         }
958         s_body += ",";
959     }
960     IpsManager::option_set(sc, key, opt, val);
961 }
962 
parse_rule_opt_end(SnortConfig * sc,const char * key,OptTreeNode * otn)963 void parse_rule_opt_end(SnortConfig* sc, const char* key, OptTreeNode* otn)
964 {
965     if ( s_ignore )
966         return;
967 
968     if ( s_capture )
969     {
970         s_body.erase(s_body.length()-1, 1);
971         s_body += ";";
972     }
973     RuleOptType type = OPT_TYPE_MAX;
974     IpsOption* ips = IpsManager::option_end(sc, otn, otn->snort_protocol_id, key, type);
975     CursorActionType cat = ips ? ips->get_cursor_type() : CAT_NONE;
976 
977     if ( cat > CAT_ADJUST )
978         otn->sticky_buf = get_pm_type(cat);
979 
980     if ( type != OPT_TYPE_META )
981         otn->num_detection_opts++;
982 }
983 
parse_rule_open(SnortConfig * sc,RuleTreeNode & rtn,bool stub)984 OptTreeNode* parse_rule_open(SnortConfig* sc, RuleTreeNode& rtn, bool stub)
985 {
986     if ( s_ignore )
987         return nullptr;
988 
989     if ( stub )
990     {
991         parse_rule_proto(sc, "tcp", rtn, true);
992         parse_rule_nets(sc, "any", true, rtn, true);
993         parse_rule_ports(sc, "any", true, rtn, true);
994         parse_rule_dir(sc, "->", rtn, true);
995         parse_rule_nets(sc, "any", false, rtn, true);
996         parse_rule_ports(sc, "any", false, rtn, true);
997     }
998     OptTreeNode* otn = new OptTreeNode;
999     otn->state = new OtnState[ThreadConfig::get_instance_max()];
1000 
1001     if ( !stub )
1002         otn->sigInfo.gid = GID_DEFAULT;
1003 
1004     otn->snort_protocol_id = rtn.snort_protocol_id;
1005 
1006     if ( sc->get_default_rule_state() )
1007         rtn.set_enabled();
1008 
1009     IpsManager::reset_options();
1010 
1011     s_capture = sc->dump_rule_meta();
1012     s_body = "(";
1013 
1014     return otn;
1015 }
1016 
parse_rule_state(SnortConfig * sc,const RuleTreeNode & rtn,OptTreeNode * otn)1017 static void parse_rule_state(SnortConfig* sc, const RuleTreeNode& rtn, OptTreeNode* otn)
1018 {
1019     if ( !otn->sigInfo.gid )
1020         otn->sigInfo.gid = GID_DEFAULT;
1021 
1022     if ( otn->num_detection_opts )
1023     {
1024         ParseError("%u:%u rule state stubs do not support detection options",
1025             otn->sigInfo.gid, otn->sigInfo.sid);
1026     }
1027     RuleKey key =
1028     {
1029         snort::get_ips_policy()->policy_id,
1030         otn->sigInfo.gid,
1031         otn->sigInfo.sid
1032     };
1033     RuleState state =
1034     {
1035         s_type,
1036         rtn.action,
1037         otn->enable
1038     };
1039     sc->rule_states->add(key, state);
1040 
1041     if ( rtn.sip )
1042         sfvar_free(rtn.sip);
1043     if ( rtn.dip )
1044         sfvar_free(rtn.dip);
1045 
1046     delete otn;
1047 }
1048 
is_builtin(uint32_t gid)1049 static bool is_builtin(uint32_t gid)
1050 {
1051     if ( ModuleManager::gid_in_use(gid) )
1052         return true;
1053 
1054     // the builtin range prevents unloaded sids from firing on every packet
1055     if ( gid < GID_BUILTIN_MIN or gid > GID_BUILTIN_MAX )
1056         return false;
1057 
1058     // not builtin but may get used and abused by snort2lua
1059     // should be deleted at some point
1060     return gid != GID_EXCEPTION_SDF;
1061 }
1062 
parse_rule_close(SnortConfig * sc,RuleTreeNode & rtn,OptTreeNode * otn)1063 void parse_rule_close(SnortConfig* sc, RuleTreeNode& rtn, OptTreeNode* otn)
1064 {
1065     if ( s_ignore )
1066     {
1067         s_ignore = false;
1068         skip_count++;
1069         return;
1070     }
1071 
1072     if ( otn->is_rule_state_stub() )
1073     {
1074         parse_rule_state(sc, rtn, otn);
1075         delete rtn.header;
1076         rtn.header = nullptr;
1077         return;
1078     }
1079 
1080     if ( !s_so_rule and !sc->metadata_filter.empty() and !otn->metadata_matched() )
1081     {
1082         delete otn;
1083         FreeRuleTreeNode(&rtn);
1084         ClearIpsOptionsVars();
1085         skip_count++;
1086         return;
1087     }
1088 
1089     if ( s_so_rule )
1090     {
1091         otn->sigInfo.gid = s_so_rule->gid;
1092         otn->sigInfo.sid = s_so_rule->sid;
1093         otn->sigInfo.rev = s_so_rule->rev;
1094     }
1095     else if ( otn->soid )
1096     {
1097         // for so rules, delete the otn and parse the actual rule
1098         // keep the stub's rtn to allow user tuning of nets and ports
1099         // if already entered, don't recurse again
1100 
1101         const char* rule = SoManager::get_so_rule(otn->soid, sc);
1102         IpsManager::reset_options();
1103 
1104         if ( !rule )
1105         {
1106             if ( sc->allow_missing_so_rules )
1107                 ParseWarning(WARN_RULES, "SO rule %s not loaded.", otn->soid);
1108             else
1109                 ParseError("SO rule %s not loaded.", otn->soid);
1110 
1111             FreeRuleTreeNode(&rtn);
1112         }
1113         else
1114         {
1115             SoRule so_rule(&rtn, otn);
1116             s_so_rule = &so_rule;
1117             parse_rules_string(sc, rule);
1118             s_so_rule = nullptr;
1119         }
1120         delete otn;
1121         return;
1122     }
1123 
1124     RuleTreeNode* tmp = s_so_rule ? s_so_rule->rtn : &rtn;
1125     RuleTreeNode* new_rtn = transfer_rtn(tmp);
1126     addRtnToOtn(sc, otn, new_rtn);
1127 
1128     OptTreeNode* otn_dup =
1129         OtnLookup(sc->otn_map, otn->sigInfo.gid, otn->sigInfo.sid);
1130 
1131     if ( otn_dup )
1132     {
1133         dup_count++;
1134         otn->ruleIndex = otn_dup->ruleIndex;
1135 
1136         if ( mergeDuplicateOtn(sc, otn_dup, otn, new_rtn) )
1137         {
1138             /* We are keeping the old/dup OTN and trashing the new one
1139              * we just created - it's freed in the remove dup function */
1140             return;
1141         }
1142     }
1143     else
1144     {
1145         otn_count++;
1146         rule_count++;
1147     }
1148 
1149     if ( otn->soid )
1150     {
1151         otn->sigInfo.builtin = false;
1152         if ( !otn_dup )
1153             so_rule_count++;
1154     }
1155     else if ( is_builtin(otn->sigInfo.gid) )
1156     {
1157         if ( otn->num_detection_opts )
1158             ParseError("%u:%u builtin rules do not support detection options",
1159                 otn->sigInfo.gid, otn->sigInfo.sid);
1160 
1161         otn->sigInfo.builtin = true;
1162         if ( !otn_dup )
1163             builtin_rule_count++;
1164     }
1165     else
1166     {
1167         if ( !otn->num_detection_opts )
1168             ParseWarning(WARN_RULES, "%u:%u does not have any detection options",
1169                 otn->sigInfo.gid, otn->sigInfo.sid);
1170 
1171         otn->sigInfo.builtin = false;
1172         if ( !otn_dup )
1173             detect_rule_count++;
1174     }
1175 
1176     if ( !otn_dup )
1177         otn->ruleIndex = parser_get_rule_index(otn->sigInfo.gid, otn->sigInfo.sid);
1178 
1179     if ( otn->sigInfo.message.empty() )
1180         otn->sigInfo.message = "\"no msg in rule\"";
1181 
1182     OptFpList* fpl = AddOptFuncToList(OptListEnd, otn);
1183     fpl->type = RULE_OPTION_TYPE_LEAF_NODE;
1184 
1185     if ( is_service_protocol(otn->snort_protocol_id) )
1186     {
1187         // copy required because the call to add_service_to_otn can
1188         // invalidate the service name pointer
1189         std::string service = sc->proto_ref->get_name(otn->snort_protocol_id);
1190         add_service_to_otn(sc, otn, service.c_str());
1191     }
1192 
1193     validate_services(sc, otn);
1194     OtnLookupAdd(sc->otn_map, otn);
1195 
1196     if ( s_capture )
1197     {
1198         s_body += " )";
1199         otn->sigInfo.body = new std::string(s_body);
1200     }
1201 
1202     ClearIpsOptionsVars();
1203 }
1204 
parse_rule_process_rtn(RuleTreeNode * rtn)1205 void parse_rule_process_rtn(RuleTreeNode* rtn)
1206 {
1207     if (rtn->sip->head && rtn->sip->head->flags & SFIP_ANY)
1208         rtn->flags |= RuleTreeNode::ANY_SRC_IP;
1209     else
1210         rtn->flags &= ~RuleTreeNode::ANY_SRC_IP;
1211 
1212     if (rtn->dip->head && rtn->dip->head->flags & SFIP_ANY)
1213         rtn->flags |= RuleTreeNode::ANY_DST_IP;
1214     else
1215         rtn->flags &= ~RuleTreeNode::ANY_DST_IP;
1216 
1217     ValidateIPList(rtn->sip, rtn->sip->name);
1218     ValidateIPList(rtn->dip, rtn->dip->name);
1219 
1220     if ( PortObjectHasAny(rtn->src_portobject) )
1221         rtn->flags |= RuleTreeNode::ANY_SRC_PORT;
1222     else
1223         rtn->flags &= ~RuleTreeNode::ANY_SRC_PORT;
1224 
1225     if ( PortObjectHasAny(rtn->dst_portobject) )
1226         rtn->flags |= RuleTreeNode::ANY_DST_PORT;
1227     else
1228         rtn->flags &= ~RuleTreeNode::ANY_DST_PORT;
1229 
1230     head_count++;
1231     SetupRTNFuncList(rtn);
1232 }
1233 
1234 /*
1235  * Finish adding the rule to the port tables
1236  *
1237  * 1) find the table this rule should belong to (src/dst/any-any tcp,udp,icmp,ip or nocontent)
1238  * 2) find an index for the gid:sid pair
1239  * 3) add all no content rules to a single no content port object, the ports are irrelevant so
1240  *    make it a any-any port object.
1241  * 4) if it's an any-any rule with content, add to an any-any port object
1242  * 5) find if we have a port object with these ports defined, if so get it, otherwise create it.
1243  *    a)do this for src and dst port
1244  *    b)add the rule index/id to the portobject(s)
1245  *    c)if the rule is bidir add the rule and port-object to both src and dst tables
1246  */
parse_rule_finish_ports(SnortConfig * sc,RuleTreeNode * rtn,OptTreeNode * otn)1247 int parse_rule_finish_ports(SnortConfig* sc, RuleTreeNode* rtn, OptTreeNode* otn)
1248 {
1249     RulePortTables* port_tables = sc->port_tables;
1250     FastPatternConfig* fp = sc->fast_pattern_config;
1251 
1252     int large_port_group = 0;
1253     PortTable* dstTable;
1254     PortTable* srcTable;
1255     PortObject* aaObject;
1256     rule_count_t* prc;
1257     uint32_t orig_flags = rtn->flags;
1258 
1259     /* Select the Target PortTable for this rule, based on protocol, src/dst
1260      * dir, and if there is rule content */
1261     switch ( otn->snort_protocol_id )
1262     {
1263     case SNORT_PROTO_IP:
1264         dstTable = port_tables->ip.dst;
1265         srcTable = port_tables->ip.src;
1266         aaObject = port_tables->ip.any;
1267         prc = &ipCnt;
1268         break;
1269 
1270     case SNORT_PROTO_ICMP:
1271         dstTable = port_tables->icmp.dst;
1272         srcTable = port_tables->icmp.src;
1273         aaObject = port_tables->icmp.any;
1274         prc = &icmpCnt;
1275         break;
1276 
1277     case SNORT_PROTO_TCP:
1278         dstTable = port_tables->tcp.dst;
1279         srcTable = port_tables->tcp.src;
1280         aaObject = port_tables->tcp.any;
1281         prc = &tcpCnt;
1282         break;
1283 
1284     case SNORT_PROTO_UDP:
1285         dstTable = port_tables->udp.dst;
1286         srcTable = port_tables->udp.src;
1287         aaObject = port_tables->udp.any;
1288         prc = &udpCnt;
1289         break;
1290 
1291     default:
1292         rtn->flags |= RuleTreeNode::ANY_SRC_PORT|RuleTreeNode::ANY_DST_PORT;
1293         dstTable = srcTable = nullptr;
1294         aaObject = port_tables->svc_any;
1295         prc = &svcCnt;
1296     }
1297 
1298     if ( !rtn->any_src_port() and !rtn->any_dst_port() )
1299         prc->both++;
1300 
1301     int src_cnt = rtn->any_src_port() ? 65535 : PortObjectPortCount(rtn->src_portobject);
1302     int dst_cnt = rtn->any_dst_port() ? 65535 : PortObjectPortCount(rtn->dst_portobject);
1303 
1304     /* If not an any-any rule test for port bleedover, if we are using a
1305      * single rule group, don't bother */
1306     if ( !fp->get_single_rule_group() and !rtn->any_any_port() )
1307     {
1308         if (src_cnt >= fp->get_bleed_over_port_limit())
1309             ++large_port_group;
1310 
1311         if (dst_cnt >= fp->get_bleed_over_port_limit())
1312             ++large_port_group;
1313 
1314         if (large_port_group == 2 && fp->get_bleed_over_warnings())
1315         {
1316             LogMessage("***Bleedover Port Limit(%d) Exceeded for rule %u:%u "
1317                 "(%d)ports: ", fp->get_bleed_over_port_limit(),
1318                 otn->sigInfo.gid, otn->sigInfo.sid,
1319                 (src_cnt > dst_cnt) ? src_cnt : dst_cnt);
1320 
1321             /* If logging to syslog, this will be all multiline */
1322             fflush(stdout); fflush(stderr);
1323             PortObjectPrintPortsRaw(rtn->src_portobject);
1324             LogMessage(" -> ");
1325             PortObjectPrintPortsRaw(rtn->dst_portobject);
1326             LogMessage(" adding to any-any group\n");
1327             fflush(stdout); fflush(stderr);
1328         }
1329     }
1330 
1331     /* If an any-any rule add rule index to any-any port object
1332      * both content and no-content type rules go here if they are
1333      * any-any port rules...
1334      * If we have an any-any rule or a large port group or
1335      * were using a single rule group we make it an any-any rule. */
1336     if ( rtn->any_any_port() or large_port_group == 2 or fp->get_single_rule_group() )
1337     {
1338         if (otn->snort_protocol_id == SNORT_PROTO_IP)
1339         {
1340             PortObjectAddRule(port_tables->icmp.any, otn->ruleIndex);
1341             icmpCnt.any++;
1342 
1343             PortObjectAddRule(port_tables->tcp.any, otn->ruleIndex);
1344             tcpCnt.any++;
1345 
1346             PortObjectAddRule(port_tables->udp.any, otn->ruleIndex);
1347             udpCnt.any++;
1348         }
1349         /* For all protocols-add to the any any group */
1350         PortObjectAddRule(aaObject, otn->ruleIndex);
1351         prc->any++;
1352         rtn->flags = orig_flags;
1353         return 0; /* done */
1354     }
1355 
1356     bool both_dirs = false;
1357 
1358     /* add rule index to dst table if we have a specific dst port or port list */
1359     if ( dst_cnt < fp->get_bleed_over_port_limit() and dst_cnt <= src_cnt )
1360     {
1361         prc->dst++;
1362 
1363         /* find the proper port object */
1364         PortObject* pox = PortTableFindInputPortObjectPorts(dstTable, rtn->dst_portobject);
1365         if ( !pox )
1366         {
1367             /* Add the port object to the table, and add the rule to the port object */
1368             pox = PortObjectDupPorts(rtn->dst_portobject);
1369             PortTableAddObject(dstTable, pox);
1370         }
1371 
1372         PortObjectAddRule(pox, otn->ruleIndex);
1373 
1374         /* if bidir, add this rule and port group to the src table */
1375         if ( rtn->flags & RuleTreeNode::BIDIRECTIONAL )
1376         {
1377             pox = PortTableFindInputPortObjectPorts(srcTable, rtn->dst_portobject);
1378             if ( !pox )
1379             {
1380                 pox = PortObjectDupPorts(rtn->dst_portobject);
1381                 PortTableAddObject(srcTable, pox);
1382             }
1383 
1384             PortObjectAddRule(pox, otn->ruleIndex);
1385             both_dirs = true;
1386         }
1387     }
1388 
1389     /* add rule index to src table if we have a specific src port or port list */
1390     if ( src_cnt < fp->get_bleed_over_port_limit() and src_cnt < dst_cnt )
1391     {
1392         prc->src++;
1393         PortObject* pox = PortTableFindInputPortObjectPorts(srcTable, rtn->src_portobject);
1394         if ( !pox )
1395         {
1396             pox = PortObjectDupPorts(rtn->src_portobject);
1397             PortTableAddObject(srcTable, pox);
1398         }
1399 
1400         PortObjectAddRule(pox, otn->ruleIndex);
1401 
1402         /* if bidir, add this rule and port group to the dst table */
1403         if ( !both_dirs and rtn->flags & RuleTreeNode::BIDIRECTIONAL )
1404         {
1405             pox = PortTableFindInputPortObjectPorts(dstTable, rtn->src_portobject);
1406             if ( !pox )
1407             {
1408                 pox = PortObjectDupPorts(rtn->src_portobject);
1409                 PortTableAddObject(dstTable, pox);
1410             }
1411             PortObjectAddRule(pox, otn->ruleIndex);
1412         }
1413     }
1414     return 0;
1415 }
1416 
parse_rule_dec_head_count()1417 void parse_rule_dec_head_count()
1418 {
1419     head_count--;
1420 }
1421