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