1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 2005-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 <cassert>
25 
26 #include "port_object.h"
27 
28 #include "log/messages.h"
29 #include "parser/parser.h"
30 #include "utils/util.h"
31 #include "utils/util_cstring.h"
32 
33 #include "port_group.h"
34 #include "port_item.h"
35 #include "port_utils.h"
36 
37 using namespace snort;
38 
39 //-------------------------------------------------------------------------
40 // PortObject - public
41 //-------------------------------------------------------------------------
42 
PortObjectNew()43 PortObject* PortObjectNew()
44 {
45     PortObject* po = (PortObject*)snort_calloc(sizeof(PortObject));
46     po->item_list =(SF_LIST*)sflist_new();
47     po->rule_list =(SF_LIST*)sflist_new();
48     return po;
49 }
50 
PortObjectFree(void * pv)51 void PortObjectFree(void* pv)
52 {
53     assert(pv);
54     PortObject* po = (PortObject*)pv;
55 
56     if ( po->name )
57         snort_free(po->name);
58 
59     if ( po->item_list)
60         sflist_free_all(po->item_list, snort_free);
61 
62     if ( po->rule_list)
63         sflist_free_all(po->rule_list, snort_free);
64 
65     if (po->group )
66         delete po->group;
67 
68     snort_free(po);
69 }
70 
PortObjectFinalize(PortObject * po)71 void PortObjectFinalize(PortObject* po)
72 {
73     sflist_free_all(po->item_list, snort_free);
74     sflist_free_all(po->rule_list, snort_free);
75 
76     po->item_list = nullptr;
77     po->rule_list = nullptr;
78 }
79 
80 /*
81  *  Set the name of the Port Object
82  */
PortObjectSetName(PortObject * po,const char * name)83 int PortObjectSetName(PortObject* po, const char* name)
84 {
85     if ( !po )
86         return -1;
87 
88     if ( !name )
89         return -1;
90 
91     /* free the old name */
92     if (po->name)
93         snort_free(po->name);
94 
95     /* alloc a new name */
96     po->name = snort_strdup(name);
97     return 0;
98 }
99 
100 /*
101  * Add a PortObjectItem to a PortObject
102  */
PortObjectAddItem(PortObject * po,PortObjectItem * poi,int * errflag)103 int PortObjectAddItem(PortObject* po, PortObjectItem* poi, int* errflag)
104 {
105     PortObjectItem* p;
106     SF_LNODE* pos = nullptr;
107 
108     if (!po || !poi)
109         return 0;
110 
111     if (errflag)
112         *errflag = 0;
113 
114     /* Make sure this is not a duplicate */
115     for (p = (PortObjectItem*)sflist_first(po->item_list,&pos);
116         p != nullptr;
117         p = (PortObjectItem*)sflist_next(&pos))
118     {
119         if ((p->lport == poi->lport) && (p->hport == poi->hport))
120             ParseWarning(WARN_RULES, "duplicate ports in list");
121     }
122 
123     sflist_add_tail(po->item_list, poi);
124     return 0;
125 }
126 
127 /*
128  * Add a PortObjectItem to a PortObject
129  */
PortObjectAddPortObject(PortObject * podst,PortObject * posrc,int * errflag)130 int PortObjectAddPortObject(PortObject* podst, PortObject* posrc, int* errflag)
131 {
132     PortObjectItem* po;
133     SF_LNODE* pos = nullptr;
134     int ret = 0;
135 
136     if (errflag)
137         *errflag = 0;
138 
139     for (po=(PortObjectItem*)sflist_first(posrc->item_list, &pos);
140         po != nullptr;
141         po=(PortObjectItem*)sflist_next(&pos) )
142     {
143         PortObjectItem* poi = PortObjectItemDup(po);
144         if ((ret = PortObjectAddItem(podst, poi, errflag)) != 0)
145         {
146             PortObjectItemFree(poi);
147             return ret;
148         }
149     }
150 
151     return ret;
152 }
153 
PortObjectAddPort(PortObject * po,int port)154 int PortObjectAddPort(PortObject* po, int port)
155 {
156     return PortObjectAddRange(po, port, port);
157 }
158 
PortObjectAddRange(PortObject * po,int lport,int hport)159 int PortObjectAddRange(PortObject* po, int lport, int hport)
160 {
161     PortObjectItem* poi = PortObjectItemNew();
162     poi->lport = (unsigned short)lport;
163     poi->hport = (unsigned short)hport;
164 
165     sflist_add_tail(po->item_list, poi);
166     return 0;
167 }
168 
PortObjectAddRule(PortObject * po,int rule)169 int PortObjectAddRule(PortObject* po, int rule)
170 {
171     int* pruleid;
172 
173     //LogMessage("Adding Rule %d to Port Object '%s'\n",rule,po->name);
174     if ( !po )
175         return -1;
176 
177     if ( !po->rule_list )
178         return -1;
179 
180     /* Add rule index to rule list */
181     pruleid = (int*)snort_calloc(sizeof(int));
182     *pruleid = rule;
183 
184     sflist_add_tail(po->rule_list, pruleid);
185     return 0;
186 }
187 
PortObjectAddPortAny(PortObject * po)188 int PortObjectAddPortAny(PortObject* po)
189 {
190     if (!po->name)
191         po->name = snort_strdup("any");
192 
193     return PortObjectAddRange(po, 0, SFPO_MAX_PORTS-1);
194 }
195 
196 /*
197  * Dup the PortObjects Item List, RuleList, and Name
198  */
PortObjectDup(PortObject * po)199 PortObject* PortObjectDup(PortObject* po)
200 {
201     SF_LNODE* lpos = nullptr;
202     PortObject* ponew = PortObjectNew();
203 
204     /* Dup the Name */
205     if ( po->name )
206         ponew->name = snort_strdup(po->name);
207     else
208         ponew->name = snort_strdup("dup");
209 
210     /* Dup the Item List */
211     if ( po->item_list )
212     {
213         for (PortObjectItem* poi = (PortObjectItem*)sflist_first(po->item_list, &lpos);
214             poi != nullptr;
215             poi = (PortObjectItem*)sflist_next(&lpos) )
216         {
217             PortObjectItem* poinew = PortObjectItemDup(poi);
218             PortObjectAddItem(ponew, poinew, nullptr);
219         }
220     }
221 
222     /* Dup the input rule list */
223     if ( po->rule_list )
224     {
225         for (int* prid  = (int*)sflist_first(po->rule_list, &lpos);
226             prid != nullptr;
227             prid  = (int*)sflist_next(&lpos) )
228         {
229             int* prule = (int*)snort_calloc(sizeof(int));
230             *prule = *prid;
231             sflist_add_tail(ponew->rule_list, prule);
232         }
233     }
234 
235     return ponew;
236 }
237 
238 /*
239  * Dup the PortObjects Item List, and Name
240  */
PortObjectDupPorts(PortObject * po)241 PortObject* PortObjectDupPorts(PortObject* po)
242 {
243     SF_LNODE* lpos = nullptr;
244     PortObject* ponew = PortObjectNew();
245 
246     /* Dup the Name */
247     if ( po->name )
248         ponew->name = snort_strdup(po->name);
249     else
250         ponew->name = snort_strdup("dup");
251 
252     /* Dup the Item List */
253     if ( po->item_list )
254     {
255         for (PortObjectItem* poi = (PortObjectItem*)sflist_first(po->item_list, &lpos);
256             poi != nullptr;
257             poi = (PortObjectItem*)sflist_next(&lpos))
258         {
259             PortObjectItem* poinew = PortObjectItemDup(poi);
260             PortObjectAddItem(ponew, poinew, nullptr);
261         }
262     }
263     return ponew;
264 }
265 
266 /*
267  *   Normalize a port object
268  *
269  *   The reduces multiple references to a given port to a single unique reference
270  *   This function should be used on each PortObject, once it's completed. After
271  *   the normalized PortObject is created, the input PortObject may be deleted.
272  */
PortObjectNormalize(PortObject * po)273 int PortObjectNormalize(PortObject* po)
274 {
275     if ( PortObjectHasAny (po) )
276         return 0;   /* ANY =64K */
277 
278     PortBitSet parray;
279     int nports = PortObjectBits(parray, po);
280 
281     sflist_free_all(po->item_list, snort_free);
282     po->item_list = PortObjectItemListFromBits(parray, SFPO_MAX_PORTS);
283 
284     return nports;
285 }
286 
287 /*
288    PortObjects should be normalized, prior to testing
289 */
PortObjectEqual(PortObject * a,PortObject * b)290 bool PortObjectEqual(PortObject* a, PortObject* b)
291 {
292     PortObjectItem* pa;
293     PortObjectItem* pb;
294     SF_LNODE* posa;
295     SF_LNODE* posb;
296 
297     if ( a->item_list->count != b->item_list->count )
298         return false;
299 
300     pa = (PortObjectItem*)sflist_first(a->item_list,&posa);
301     pb = (PortObjectItem*)sflist_first(b->item_list,&posb);
302 
303     while ( pa && pb )
304     {
305         if ( !PortObjectItemsEqual(pa, pb) )
306             return false;
307 
308         pa = (PortObjectItem*)sflist_next(&posa);
309         pb = (PortObjectItem*)sflist_next(&posb);
310     }
311 
312     if ( pa || pb ) /* both are not done - cannot match */
313         return false;
314 
315     return true; /* match */
316 }
317 
318 /*
319  * Calcs number of ports in this object,
320  * object do not have to  be normalized,
321  * but if the same ports are referenced
322  * twice, the count will be off.
323  *
324  * returns:
325  *  any = -1
326  *  0   = none/empty
327  *  >0  = number of ports
328 */
PortObjectPortCount(PortObject * po)329 int PortObjectPortCount(PortObject* po)
330 {
331     SF_LNODE* cursor;
332     int cnt=0;
333 
334     if ( !po )
335         return 0;
336 
337     for (PortObjectItem* poi=(PortObjectItem*)sflist_first(po->item_list, &cursor);
338         poi != nullptr;
339         poi=(PortObjectItem*)sflist_next(&cursor) )
340     {
341         if ( poi->any() )
342             return -1;
343 
344         int nports = poi->hport - poi->lport + 1;
345 
346         if ( poi->negate )
347             cnt -= nports;
348         else
349             cnt += nports;
350     }
351 
352     if ( cnt < 0 )
353     {
354         /* we have a pure not port or port range
355          *
356          * !80    = -1, add 64K (65535 -1 = 65534)
357          * !80:81 = -2, (65535 - 2 = 65533)
358          *
359          * [:1023,!80]  = 1024 - 1 = 1023 ports
360          *
361          */
362         cnt += SFPO_MAX_PORTS;  /* add back in the acceptable ports */
363     }
364 
365     return cnt;
366 }
367 
368 /*
369  * This does NOT return true if the object is an ANY port
370 */
PortObjectHasPort(PortObject * po,int port)371 int PortObjectHasPort(PortObject* po, int port)
372 {
373     PortObjectItem* poi;
374     SF_LNODE* cursor;
375 
376     if ( !po )
377         return 0;
378 
379     for (poi=(PortObjectItem*)sflist_first(po->item_list, &cursor);
380         poi != nullptr;
381         poi=(PortObjectItem*)sflist_next(&cursor) )
382     {
383         if ( poi->any() )
384             return 0;
385 
386         // FIXIT-L need to check range based on flag???
387         if ( (uint16_t)port >= poi->lport &&
388             (uint16_t)port <= poi->hport )
389             return 1;
390 
391         if ( poi->negate )
392             return 1;
393     }
394     return 0;
395 }
396 
PortObjectToggle(PortObject * po)397 void PortObjectToggle(PortObject* po)
398 {
399     PortObjectItem* poi;
400     SF_LNODE* pos;
401 
402     if (!po)
403         return;
404 
405     for (poi=(PortObjectItem*)sflist_first(po->item_list,&pos);
406         poi != nullptr;
407         poi=(PortObjectItem*)sflist_next(&pos) )
408     {
409         poi->negate = !poi->negate;
410     }
411 }
412 
PortObjectIsPureNot(PortObject * po)413 int PortObjectIsPureNot(PortObject* po)
414 {
415     PortObjectItem* poi;
416     SF_LNODE* cursor;
417     int cnt=0;
418 
419     if ( !po )
420         return 0;
421 
422     for (poi=(PortObjectItem*)sflist_first(po->item_list, &cursor);
423         poi != nullptr;
424         poi=(PortObjectItem*)sflist_next(&cursor) )
425     {
426         cnt++;
427         if ( !poi->negate )
428             return 0;
429     }
430 
431     if ( cnt == 0 )
432         return 0;
433 
434     return 1;
435 }
436 
PortObjectHasAny(PortObject * po)437 int PortObjectHasAny(PortObject* po)
438 {
439     PortObjectItem* poi;
440     SF_LNODE* cursor;
441 
442     if ( !po )
443         return 0;
444 
445     for (poi=(PortObjectItem*)sflist_first(po->item_list, &cursor);
446         poi != nullptr;
447         poi=(PortObjectItem*)sflist_next(&cursor) )
448     {
449         if ( poi->any() )
450             return 1;
451     }
452     return 0;
453 }
454 
455 /*
456  *  Removes Ports in B from A ... A = A - B
457  */
PortObjectRemovePorts(PortObject * a,PortObject * b)458 int PortObjectRemovePorts(PortObject* a,  PortObject* b)
459 {
460     PortBitSet pA, pB;
461 
462     PortObjectBits(pA, a);
463     PortObjectBits(pB, b);
464 
465     pA &= ~pB;
466 
467     /* Release the old port list */
468     sflist_free_all(a->item_list, snort_free);
469 
470     /* Replace the old PortObject list */
471     a->item_list = PortObjectItemListFromBits(pA, SFPO_MAX_PORTS);
472 
473     return 0;
474 }
475 
476 /*
477    Dup and Append PortObjectItems from pob to poa
478 */
PortObjectAppend(PortObject * poa,PortObject * pob)479 PortObject* PortObjectAppend(PortObject* poa, PortObject* pob)
480 {
481     SF_LNODE* cursor;
482 
483     for (PortObjectItem* poib = (PortObjectItem*)sflist_first(pob->item_list, &cursor);
484          poib!= nullptr;
485          poib = (PortObjectItem*)sflist_next(&cursor) )
486     {
487         PortObjectItem* poia = PortObjectItemNew();
488 
489         if (!poia)
490             return nullptr;
491 
492         memcpy(poia,poib,sizeof(PortObjectItem));
493 
494         sflist_add_tail(poa->item_list,poia);
495     }
496     return poa;
497 }
498 
PortObjectPrint(PortObject * po)499 void PortObjectPrint(PortObject* po)
500 {
501     PortObjectPrintEx(po, rule_index_map_print_index);
502 }
503 
PortObjectPrintPortsRaw(PortObject * po)504 void PortObjectPrintPortsRaw(PortObject* po)
505 {
506     PortObjectItem* poi = nullptr;
507     SF_LNODE* pos = nullptr;
508     char* buf;
509     int bufsize;
510 
511     /* Need to buffer the string so we only do one LogMessage,
512      * due to syslog output.  The largest string needed to represent
513      * each portobject is the length required to represent:
514      * " unknown port type @ 0x<8 max bytes>" (See PortObjectItemPrint), or:
515      * 30 bytes.  For the entire list, need room for spaces and brackets and
516      * potential negations. Or:
517      *      list_size * (30 + 1space_for_each_element, +
518      *       1potential_negation) + surrounding_whitespace + brackets + null */
519 
520     bufsize = po->item_list->count * (30 + 1 + 1) + 5;
521     buf = (char*)snort_calloc(bufsize);
522     SnortSnprintfAppend(buf, bufsize, " [");
523 
524     for (poi=(PortObjectItem*)sflist_first(po->item_list, &pos);
525         poi != nullptr;
526         poi=(PortObjectItem*)sflist_next(&pos) )
527     {
528         PortObjectItemPrint(poi, buf, bufsize);
529     }
530 
531     SnortSnprintfAppend(buf, bufsize, " ]");
532 
533     LogMessage("%s", buf);
534 
535     snort_free(buf);
536 }
537 
538 /*
539    Print Port Object - Prints input ports and rules (uncompiled)
540     ports
541     rules (input by user)
542 */
PortObjectPrintEx(PortObject * po,po_print_f print_index_map)543 void PortObjectPrintEx(PortObject* po, po_print_f print_index_map)
544 {
545     PortObjectItem* poi = nullptr;
546     SF_LNODE* pos = nullptr;
547     int k=0;
548     int* rlist = nullptr;
549     unsigned i;
550 
551     /* static for printing so we don't put so many bytes on the stack */
552     static char print_buf[MAX_PORTS];  // FIXIT-L delete this; replace with local stringstream
553 
554     int bufsize = sizeof(print_buf);
555     print_buf[0] = '\0';
556 
557     if ( !po )
558         return;
559 
560     if ( !po->rule_list )
561         return;
562 
563     if ( !po->rule_list->count )
564         return;
565 
566     SnortSnprintfAppend(print_buf, bufsize, " PortObject ");
567 
568     if ( po->name )
569         SnortSnprintfAppend(print_buf, bufsize, "%s ", po->name);
570 
571     SnortSnprintfAppend(print_buf, bufsize,
572         " Id:%d  Ports:%u Rules:%u\n {\n",
573         po->id, po->item_list->count, po->rule_list->count);
574 
575     SnortSnprintfAppend(print_buf, bufsize, "  Ports [\n  ");
576 
577     if ( PortObjectHasAny(po) )
578     {
579         SnortSnprintfAppend(print_buf, bufsize, "any");
580     }
581     else
582     {
583         for (poi = (PortObjectItem*)sflist_first(po->item_list,&pos);
584             poi != nullptr;
585             poi = (PortObjectItem*)sflist_next(&pos) )
586         {
587             PortObjectItemPrint(poi, print_buf, bufsize);
588         }
589     }
590     SnortSnprintfAppend(print_buf, bufsize, "  ]\n");
591 
592     rlist = RuleListToSortedArray(po->rule_list);
593     if (!rlist )
594     {
595         return;
596     }
597 
598     SnortSnprintfAppend(print_buf, bufsize, "  Rules [ \n ");
599     for (i = 0; i < po->rule_list->count; i++)
600     {
601         if ( print_index_map )
602             print_index_map(rlist[i], print_buf, bufsize);
603         else
604             SnortSnprintfAppend(print_buf, bufsize, " %d",rlist[i]);
605 
606         k++;
607         if ( k == 25 )
608         {
609             k=0;
610             SnortSnprintfAppend(print_buf, bufsize, " \n ");
611         }
612     }
613     SnortSnprintfAppend(print_buf, bufsize, "  ]\n }\n");
614 
615     LogMessage("%s", print_buf);
616     snort_free(rlist);
617 }
618 
619