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 "parse_ports.h"
25 
26 #include "protocols/packet.h"
27 #include "utils/util.h"
28 
29 using namespace snort;
30 
POParserInit(POParser * pop,const char * s,PortVarTable * pvTable)31 static int POParserInit(POParser* pop, const char* s, PortVarTable* pvTable)
32 {
33     memset(pop,0,sizeof(POParser));
34     pop->pos     = 0;
35     pop->s       = s;
36     pop->slen    = strlen(s);
37     pop->errflag = 0;
38     pop->pvTable = pvTable;
39 
40     return 0;
41 }
42 
43 /*
44     Get a Char
45 */
POPGetChar(POParser * pop)46 static int POPGetChar(POParser* pop)
47 {
48     if ( pop->slen > 0 )
49     {
50         int c = pop->s[0];
51         pop->slen--;
52         pop->s++;
53         pop->pos++;
54         return c;
55     }
56     return 0;
57 }
58 
59 /*
60    Skip whitespace till we find a non-whitespace char
61 */
POPGetChar2(POParser * pop)62 static int POPGetChar2(POParser* pop)
63 {
64     int c;
65     for (;; )
66     {
67         c=POPGetChar(pop);
68         if ( !c )
69             return 0;
70 
71         if ( isspace(c) || c==',' )
72             continue;
73 
74         break;
75     }
76     return c;
77 }
78 
79 /*
80    Restore last char
81 */
POPUnGetChar(POParser * pop)82 static void POPUnGetChar(POParser* pop)
83 {
84     if ( pop->pos > 0 )
85     {
86         pop->slen++;
87         pop->s--;
88         pop->pos--;
89     }
90 }
91 
92 /*
93   Peek at next char
94 */
POPPeekChar(POParser * pop)95 static int POPPeekChar(POParser* pop)
96 {
97     if ( pop->slen > 0)
98     {
99         return pop->s[0];
100     }
101     return 0;
102 }
103 
104 /*
105    Skip whitespace : ' ', '\t', '\n'
106 */
POPSkipSpace(POParser * p)107 static int POPSkipSpace(POParser* p)
108 {
109     int c;
110     for ( c  = POPPeekChar(p);
111         c != 0;
112         c  = POPPeekChar(p) )
113     {
114         if ( !isspace(c) && c != ',' )
115             return c;
116 
117         POPGetChar(p);
118     }
119     return 0;
120 }
121 
122 /*
123   Get the Port Object Name
124 */
POParserName(POParser * pop)125 static char* POParserName(POParser* pop)
126 {
127     int k = 0;
128     int c;
129 
130     /* check if were done  */
131     if ( !pop || !pop->s || !*(pop->s) )
132         return nullptr;
133 
134     /* Start the name - skip space */
135     c = POPGetChar2(pop);
136     if ( !c )
137         return nullptr;
138 
139     if ( c== '$' ) /* skip leading '$' - old Var indicator */
140     {
141         c = POPGetChar2(pop);
142         if ( !c )
143             return nullptr;
144     }
145 
146     if ( isalnum(c) )
147     {
148         pop->token[k++] = (char)c;
149         pop->token[k]   = (char)0;
150     }
151     else
152     {
153         POPUnGetChar(pop);
154         return nullptr; /* not a name */
155     }
156 
157     for ( c  = POPGetChar(pop);
158         c != 0 && k < POP_MAX_BUFFER_SIZE;
159         c  = POPGetChar(pop) )
160     {
161         if ( isalnum(c) || c== '_' || c=='-' || c=='.' )
162         {
163             pop->token[k++] = (char)c;
164             pop->token[k]   = (char)0;
165         }
166         else
167         {
168             POPUnGetChar(pop);
169             break;
170         }
171     }
172 
173     return snort_strdup(pop->token);
174 }
175 
176 /*
177 *   read an unsigned short (a port)
178 */
POParserGetShort(POParser * pop)179 static uint16_t POParserGetShort(POParser* pop)
180 {
181     int c;
182     int k = 0;
183     char buffer[32];
184     char* pend;
185 
186     POPSkipSpace(pop);
187 
188     buffer[0] = 0;
189 
190     while ( (c = POPGetChar(pop)) != 0 )
191     {
192         if ( isdigit(c) )
193         {
194             buffer[k++]=(char)c;
195             buffer[k]  =0;
196             if ( k == sizeof(buffer)-1 )
197                 break;                         /* thats all that fits */
198         }
199         else
200         {
201             if ( c != ':' && c != ' ' && c != ']' && c != ',' && c != '\t' && c != '\n' )
202             {
203                 pop->errflag = POPERR_NOT_A_NUMBER;
204                 return 0;
205             }
206             POPUnGetChar(pop);
207             break;
208         }
209     }
210 
211     c  = (int)strtoul(buffer,&pend,10);
212 
213     if (c > 65535 || c < 0)
214     {
215         pop->errflag = POPERR_BOUNDS;
216         return 0;
217     }
218 
219     return c;
220 }
221 
_POParseVar(POParser * pop)222 static PortObject* _POParseVar(POParser* pop)
223 {
224     char* name  = POParserName(pop);
225     if (!name)
226     {
227         pop->pos++;
228         pop->errflag = POPERR_NO_NAME;
229         return nullptr;
230     }
231 
232     PortObject* pox = PortVarTableFind(pop->pvTable, name);
233     snort_free(name);
234 
235     if (!pox)
236     {
237         pop->errflag = POPERR_BAD_VARIABLE;
238         return nullptr;
239     }
240 
241     pox = PortObjectDup(pox);
242     return pox;
243 }
244 
_POParsePort(POParser * pop)245 static PortObject* _POParsePort(POParser* pop)
246 {
247     PortObject* po = PortObjectNew();
248 
249     pop->token[0] = 0;
250 
251     /* The string in pop should only be of the form <port> or <port>:<port> */
252     uint16_t lport = POParserGetShort(pop);
253 
254     if (pop->errflag)
255     {
256         PortObjectFree(po);
257         return nullptr;
258     }
259 
260     char c = POPPeekChar(pop);
261 
262     if ( c == ':' ) /* half open range */
263     {
264         POPGetChar(pop);
265         c = POPPeekChar(pop);
266         uint16_t hport;
267 
268         if (((c == 0) && (pop->slen == 0)) ||
269             (c == ','))
270         {
271             /* Open ended range, highport is 65k */
272             hport = MAX_PORTS - 1;
273             PortObjectAddRange(po, lport, hport);
274             return po;
275         }
276 
277         if ( !isdigit((int)c) ) /* not a number */
278         {
279             pop->errflag = POPERR_NOT_A_NUMBER;
280             PortObjectFree(po);
281             return nullptr;
282         }
283 
284         hport = POParserGetShort(pop);
285 
286         if ( pop->errflag )
287         {
288             PortObjectFree(po);
289             return nullptr;
290         }
291 
292         if (lport > hport)
293         {
294             pop->errflag = POPERR_INVALID_RANGE;
295             PortObjectFree(po);
296             return nullptr;
297         }
298 
299         PortObjectAddRange(po, lport, hport);
300     }
301     else
302     {
303         PortObjectAddPort(po, lport);
304     }
305 
306     return po;
307 }
308 
_POFindMatchingBraces(const char * s)309 static const char* _POFindMatchingBraces(const char* s)
310 {
311     uint32_t depth = 0;
312 
313     do
314     {
315         if (*s == '[')
316         {
317             ++depth;
318         }
319         else if (*s == ']')
320         {
321             if (depth-- == 0)
322                 return s;
323         }
324     } while (*s++);
325     return nullptr;
326 }
327 
328 // FIXIT-L _POParseString creates 1 PortObject per port in the list and
329 // then consolidates into one PortObject; it should just create a single
330 // PortObject and put each port into appropriate PortItems
_POParseString(POParser * pop)331 static PortObject* _POParseString(POParser* pop)
332 {
333     PortObject* potmp = nullptr;
334     int local_neg = 0;
335     char c;
336     int list_count = 0;
337 
338     PortObject* po = PortObjectNew();
339     while ( (c = POPGetChar2(pop)) != 0 )
340     {
341         if (c == '!')
342         {
343             local_neg = 1;
344             continue;
345         }
346 
347         if (c == '$')
348         {
349             /* Don't dup this again - the returned PortObject has already
350              * been dup'ed */
351             potmp = _POParseVar(pop);
352         }
353         /* Start of a list. Tokenize list and recurse on it */
354         else if (c == '[')
355         {
356             POParser local_pop;
357             char* tok;
358             const char* end;
359 
360             list_count++;
361 
362             if ( (end = _POFindMatchingBraces(pop->s)) == nullptr )
363             {
364                 pop->errflag = POPERR_NO_ENDLIST_BRACKET;
365                 PortObjectFree(po);
366                 return nullptr;
367             }
368 
369             tok = snort_strndup(pop->s, end - pop->s);
370             POParserInit(&local_pop, tok, pop->pvTable);
371 
372             /* Recurse */
373             potmp = _POParseString(&local_pop);
374             snort_free(tok);
375 
376             if (!potmp)
377             {
378                 pop->errflag = local_pop.errflag;
379                 PortObjectFree(po);
380                 return nullptr;
381             }
382 
383             /* Advance "cursor" to end of this list */
384             while (c && pop->s != end)
385                 c = POPGetChar(pop);
386         }
387         else if (c == ']')
388         {
389             list_count--;
390 
391             if (list_count < 0)
392             {
393                 pop->errflag = POPERR_EXTRA_BRACKET;
394                 PortObjectFree(po);
395                 return nullptr;
396             }
397 
398             continue;
399         }
400         else
401         {
402             POPUnGetChar(pop);
403 
404             potmp = _POParsePort(pop);
405         }
406 
407         if (!potmp)
408         {
409             PortObjectFree(po);
410             return nullptr;
411         }
412 
413         if (local_neg)
414         {
415             /* Note: this intentionally only sets the negation flag!
416                The actual negation will take place when normalization is called */
417             PortObjectToggle(potmp);
418 
419             local_neg = 0;
420         }
421 
422         if (PortObjectAddPortObject(po, potmp, &pop->errflag))
423         {
424             PortObjectFree(po);
425             PortObjectFree(potmp);
426             return nullptr;
427         }
428 
429         PortObjectFree(potmp);
430         potmp = nullptr;
431     }
432 
433     /* Check for mis-matched brackets */
434     if (list_count)
435     {
436         if (list_count > 0)
437             pop->errflag = POPERR_NO_ENDLIST_BRACKET;
438         else
439             pop->errflag = POPERR_EXTRA_BRACKET;
440 
441         PortObjectFree(po);
442         return nullptr;
443     }
444 
445     return po;
446 }
447 
448 /*
449 *   PortObject : name value
450 *   PortObject : name [!][ value value value ... ]
451 *
452 *   value : [!]port
453 *           [!]low-port[:high-port]
454 *
455 *  inputs:
456 *  pvTable - PortVarTable to search for PortVar references in the current PortVar
457 *      pop - parsing structure
458 *        s - string with port object text
459 *
460 * nameflag - indicates a name must be present, this allows usage for
461 *            embedded rule or portvar declarations of portlists
462 * returns:
463 *      (PortObject *) - a normalized version
464 */
PortObjectParseString(PortVarTable * pvTable,POParser * pop,const char * name,const char * s,int nameflag)465 PortObject* PortObjectParseString(PortVarTable* pvTable, POParser* pop,
466     const char* name, const char* s, int nameflag)
467 {
468     POParserInit(pop, s, pvTable);
469     PortObject* po = PortObjectNew();
470 
471     if ( nameflag ) /* parse a name */
472     {
473         po->name = POParserName(pop);
474         if ( !po->name )
475         {
476             pop->errflag = POPERR_NO_NAME;
477             PortObjectFree(po);
478             return nullptr;
479         }
480     }
481     else
482     {
483         if ( name )
484             po->name = snort_strdup(name);
485         else
486             po->name = snort_strdup("noname");
487     }
488 
489     // LogMessage("PortObjectParseString: po->name=%s\n",po->name);
490 
491     PortObject* potmp = _POParseString(pop);
492     if ( !potmp )
493     {
494         PortObjectFree(po);
495         return nullptr;
496     }
497 
498     PortObjectNormalize(potmp);
499 
500     // Catches !:65535
501     if ( sflist_count(potmp->item_list) == 0 )
502     {
503         PortObjectFree(po);
504         PortObjectFree(potmp);
505         pop->errflag = POPERR_INVALID_RANGE;
506         return nullptr;
507     }
508 
509     if ( PortObjectAddPortObject(po, potmp, &pop->errflag) )
510     {
511         PortObjectFree(po);
512         PortObjectFree(potmp);
513         return nullptr;
514     }
515 
516     PortObjectFree(potmp);
517 
518     return po;
519 }
520 
PortObjectParseError(POParser * pop)521 const char* PortObjectParseError(POParser* pop)
522 {
523     switch ( pop->errflag )
524     {
525     case POPERR_NO_NAME:            return "no name";
526     case POPERR_NO_ENDLIST_BRACKET: return "no end of list bracket."
527         " Elements must be comma separated, and no spaces may appear between brackets.";
528     case POPERR_NOT_A_NUMBER:       return "not a number";
529     case POPERR_EXTRA_BRACKET:      return "extra list bracket";
530     case POPERR_NO_DATA:            return "no data";
531     case POPERR_ADDITEM_FAILED:     return "add item failed";
532     case POPERR_MALLOC_FAILED:      return "mem alloc failed";
533     case POPERR_INVALID_RANGE:      return "invalid port range";
534     case POPERR_DUPLICATE_ENTRY:    return "duplicate ports in list";
535     case POPERR_BOUNDS:             return "value out of bounds for a port";
536     case POPERR_BAD_VARIABLE:       return "unrecognized variable";
537     default:
538         break;
539     }
540     return "unknown POParse error";
541 }
542 
543