1 /* $Id$ */
2 /*
3  ** Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
4  ** Copyright (C) 2002-2013 Sourcefire, Inc.
5  ** Author: Martin Roesch
6  **
7  ** This program is free software; you can redistribute it and/or modify
8  ** it under the terms of the GNU General Public License Version 2 as
9  ** published by the Free Software Foundation.  You may not use, modify or
10  ** distribute this program under any other version of the GNU General
11  ** Public License.
12  **
13  ** This program is distributed in the hope that it will be useful,
14  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  ** GNU General Public License for more details.
17  **
18  ** You should have received a copy of the GNU General Public License
19  ** along with this program; if not, write to the Free Software
20  ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  */
22 
23 /* sp_clientserver
24  *
25  * Purpose:
26  *
27  * Wouldn't be nice if we could tell a TCP rule to only apply if it's going
28  * to or from the client or server side of a connection?  Think of all the
29  * false alarms we could elminate!  That's what we're doing with this one,
30  * it allows you to write rules that only apply to client or server packets.
31  * One thing though, you *must* have stream4 enabled for it to work!
32  *
33  * Arguments:
34  *
35  *   None.
36  *
37  * Effect:
38  *
39  * Test the packet to see if it's coming from the client or the server side
40  * of a connection.
41  *
42  * Comments:
43  *
44  * None.
45  *
46  */
47 
48 /* put the name of your pluging header file here */
49 
50 #ifdef HAVE_CONFIG_H
51 #include "config.h"
52 #endif
53 
54 #include <sys/types.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <ctype.h>
58 
59 #include "sf_types.h"
60 #include "rules.h"
61 #include "treenodes.h"
62 #include "decode.h"
63 #include "plugbase.h"
64 #include "parser.h"
65 #include "snort_debug.h"
66 #include "util.h"
67 #include "plugin_enum.h"
68 #include "snort.h"
69 //#include "signature.h"
70 #include "sfhashfcn.h"
71 #include "sp_clientserver.h"
72 
73 #include "stream_api.h"
74 
75 #include "snort.h"
76 #include "profiler.h"
77 #ifdef PERF_PROFILING
78 PreprocStats flowCheckPerfStats;
79 extern PreprocStats ruleOTNEvalPerfStats;
80 #endif
81 
82 #include "sfhashfcn.h"
83 #include "detection_options.h"
84 
85 void FlowInit(struct _SnortConfig *, char *, OptTreeNode *, int);
86 void ParseFlowArgs(struct _SnortConfig *, char *, OptTreeNode *);
87 void InitFlowData(OptTreeNode *);
88 int CheckFlow(void *option_data, Packet *p);
89 
FlowHash(void * d)90 uint32_t FlowHash(void *d)
91 {
92     uint32_t a,b,c;
93     ClientServerData *data = (ClientServerData *)d;
94 
95     a = data->from_server || data->from_client << 16;
96     b = data->ignore_reassembled || data->only_reassembled << 16;
97     c = data->stateless || data->established << 16;
98 
99     mix(a,b,c);
100 
101     a += data->unestablished;
102     b += RULE_OPTION_TYPE_FLOW;
103 
104     final(a,b,c);
105 
106     return c;
107 }
108 
FlowCompare(void * l,void * r)109 int FlowCompare(void *l, void *r)
110 {
111     ClientServerData *left = (ClientServerData *)l;
112     ClientServerData *right = (ClientServerData *)r;
113 
114     if (!left || !right)
115         return DETECTION_OPTION_NOT_EQUAL;
116 
117     if (( left->from_server == right->from_server) &&
118         ( left->from_client == right->from_client) &&
119         ( left->ignore_reassembled == right->ignore_reassembled) &&
120         ( left->only_reassembled == right->only_reassembled) &&
121         ( left->stateless == right->stateless) &&
122         ( left->established == right->established) &&
123         ( left->unestablished == right->unestablished))
124     {
125         return DETECTION_OPTION_EQUAL;
126     }
127 
128     return DETECTION_OPTION_NOT_EQUAL;
129 }
130 
OtnFlowFromServer(OptTreeNode * otn)131 int OtnFlowFromServer( OptTreeNode * otn )
132 {
133     ClientServerData *csd;
134 
135     csd = (ClientServerData *)otn->ds_list[PLUGIN_CLIENTSERVER];
136     if(csd )
137     {
138         if( csd->from_server ) return 1;
139     }
140     return 0;
141 }
OtnFlowFromClient(OptTreeNode * otn)142 int OtnFlowFromClient( OptTreeNode * otn )
143 {
144     ClientServerData *csd;
145 
146     csd = (ClientServerData *)otn->ds_list[PLUGIN_CLIENTSERVER];
147     if(csd )
148     {
149         if( csd->from_client ) return 1;
150     }
151     return 0;
152 }
OtnFlowIgnoreReassembled(OptTreeNode * otn)153 int OtnFlowIgnoreReassembled( OptTreeNode * otn )
154 {
155     ClientServerData *csd;
156 
157     csd = (ClientServerData *)otn->ds_list[PLUGIN_CLIENTSERVER];
158     if( csd )
159     {
160         if( csd->ignore_reassembled ) return 1;
161     }
162     return 0;
163 }
OtnFlowOnlyReassembled(OptTreeNode * otn)164 int OtnFlowOnlyReassembled( OptTreeNode * otn )
165 {
166     ClientServerData *csd;
167 
168     csd = (ClientServerData *)otn->ds_list[PLUGIN_CLIENTSERVER];
169     if( csd )
170     {
171         if( csd->only_reassembled ) return 1;
172     }
173     return 0;
174 }
175 
176 /****************************************************************************
177  *
178  * Function: SetupClientServer()
179  *
180  * Purpose: Generic detection engine plugin template.  Registers the
181  *          configuration function and links it to a rule keyword.  This is
182  *          the function that gets called from InitPlugins in plugbase.c.
183  *
184  * Arguments: None.
185  *
186  * Returns: void function
187  *
188  ****************************************************************************/
SetupClientServer(void)189 void SetupClientServer(void)
190 {
191     /* map the keyword to an initialization/processing function */
192     RegisterRuleOption("flow", FlowInit, NULL, OPT_TYPE_DETECTION, NULL);
193 
194 #ifdef PERF_PROFILING
195     RegisterPreprocessorProfile("flow", &flowCheckPerfStats, 3, &ruleOTNEvalPerfStats, NULL);
196 #endif
197 
198     DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,
199                             "Plugin: ClientServerName(Flow) Setup\n"););
200 }
201 
202 
203 /****************************************************************************
204  *
205  * Function: FlowInit(struct _SnortConfig *, char *, OptTreeNode *)
206  *
207  * Purpose: Configure the flow init option to register the appropriate checks
208  *
209  * Arguments: data => rule arguments/data
210  *            otn => pointer to the current rule option list node
211  *
212  * Returns: void function
213  *
214  ****************************************************************************/
FlowInit(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)215 void FlowInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
216 {
217     ClientServerData *csd;
218     /* multiple declaration check */
219     if(otn->ds_list[PLUGIN_CLIENTSERVER])
220     {
221         FatalError("%s(%d): Multiple flow options in rule\n", file_name,
222                 file_line);
223     }
224 
225 
226     InitFlowData(otn);
227     ParseFlowArgs(sc, data, otn);
228     csd = (ClientServerData *)otn->ds_list[PLUGIN_CLIENTSERVER];
229 
230     if(protocol == IPPROTO_UDP)
231     {
232         if (!stream_api || (stream_api->version != STREAM_API_VERSION5))
233         {
234             FatalError("%s(%d): Cannot check flow connection "
235                    "for UDP traffic\n", file_name, file_line);
236         }
237     }
238 
239     if (protocol == IPPROTO_ICMP)
240     {
241         if ((csd->only_reassembled != ONLY_FRAG) && (csd->ignore_reassembled != IGNORE_FRAG))
242         {
243             FatalError("%s(%d): Cannot check flow connection "
244                    "for ICMP traffic\n", file_name, file_line);
245         }
246     }
247 }
248 
249 
CheckStream(char * token)250 static inline void CheckStream(char *token)
251 {
252     if (!stream_api)
253     {
254         FatalError("%s(%d): Stream must be enabled to use the '%s' option.\n",
255             file_name, file_line, token);
256     }
257 }
258 
259 /****************************************************************************
260  *
261  * Function: ParseFlowArgs(struct _SnortConfig *, char *, OptTreeNode *)
262  *
263  * Purpose: parse the arguments to the flow plugin and alter the otn
264  *          accordingly
265  *
266  * Arguments: otn => pointer to the current rule option list node
267  *
268  * Returns: void function
269  *
270  ****************************************************************************/
ParseFlowArgs(struct _SnortConfig * sc,char * data,OptTreeNode * otn)271 void ParseFlowArgs(struct _SnortConfig *sc, char *data, OptTreeNode *otn)
272 {
273     char *token, *str, *p;
274     ClientServerData *csd;
275     void *idx_dup;
276     OptFpList *fpl = NULL;
277 
278     csd = (ClientServerData *)otn->ds_list[PLUGIN_CLIENTSERVER];
279 
280     str = SnortStrdup(data);
281 
282     p = str;
283 
284     /* nuke leading whitespace */
285     while(isspace((int)*p)) p++;
286 
287     token = strtok(p, ",");
288 
289     while(token)
290     {
291         DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,
292                     "parsed %s,(%d)\n", token,strlen(token)););
293 
294         while(isspace((int)*token))
295             token++;
296 
297         if(!strcasecmp(token, "to_server"))
298         {
299             CheckStream(token);
300             csd->from_client = 1;
301         }
302         else if(!strcasecmp(token, "to_client"))
303         {
304             CheckStream(token);
305             csd->from_server = 1;
306         }
307         else if(!strcasecmp(token, "from_server"))
308         {
309             CheckStream(token);
310             csd->from_server = 1;
311         }
312         else if(!strcasecmp(token, "from_client"))
313         {
314             CheckStream(token);
315             csd->from_client = 1;
316         }
317         else if(!strcasecmp(token, "stateless"))
318         {
319             csd->stateless = 1;
320             otn->stateless = 1;
321         }
322         else if(!strcasecmp(token, "established"))
323         {
324             CheckStream(token);
325             csd->established = 1;
326             otn->established = 1;
327         }
328         else if(!strcasecmp(token, "not_established"))
329         {
330             CheckStream(token);
331             csd->unestablished = 1;
332             otn->unestablished = 1;
333         }
334         else if(!strcasecmp(token, "no_stream"))
335         {
336             CheckStream(token);
337             csd->ignore_reassembled |= IGNORE_STREAM;
338         }
339         else if(!strcasecmp(token, "only_stream"))
340         {
341             CheckStream(token);
342             csd->only_reassembled |= ONLY_STREAM;
343         }
344         else if(!strcasecmp(token, "no_frag"))
345         {
346             csd->ignore_reassembled |= IGNORE_FRAG;
347         }
348         else if(!strcasecmp(token, "only_frag"))
349         {
350             csd->only_reassembled |= ONLY_FRAG;
351         }
352         else
353         {
354             FatalError("%s:%d: Unknown Flow Option: '%s'\n",
355                        file_name,file_line,token);
356 
357         }
358 
359 
360         token = strtok(NULL, ",");
361     }
362 
363     if(csd->from_client && csd->from_server)
364     {
365         FatalError("%s:%d: Can't use both from_client"
366                    "and flow_from server", file_name, file_line);
367     }
368 
369     if((csd->ignore_reassembled & IGNORE_STREAM) && (csd->only_reassembled & ONLY_STREAM))
370     {
371         FatalError("%s:%d: Can't use no_stream and"
372                    " only_stream", file_name,file_line);
373     }
374 
375     if((csd->ignore_reassembled & IGNORE_FRAG) && (csd->only_reassembled & ONLY_FRAG))
376     {
377         FatalError("%s:%d: Can't use no_frag and"
378                    " only_frag", file_name,file_line);
379     }
380 
381     if(otn->stateless && (csd->from_client || csd->from_server))
382     {
383         FatalError("%s:%d: Can't use flow: stateless option with"
384                    " other options", file_name, file_line);
385     }
386 
387     if(otn->stateless && otn->established)
388     {
389         FatalError("%s:%d: Can't specify established and stateless "
390                    "options in same rule\n", file_name, file_line);
391     }
392 
393     if(otn->stateless && otn->unestablished)
394     {
395         FatalError("%s:%d: Can't specify unestablished and stateless "
396                    "options in same rule\n", file_name, file_line);
397     }
398 
399     if(otn->established && otn->unestablished)
400     {
401         FatalError("%s:%d: Can't specify unestablished and established "
402                    "options in same rule\n", file_name, file_line);
403     }
404 
405     if (add_detection_option(sc, RULE_OPTION_TYPE_FLOW, (void *)csd, &idx_dup) == DETECTION_OPTION_EQUAL)
406     {
407 #ifdef DEBUG_RULE_OPTION_TREE
408         LogMessage("Duplicate Flow:\n%c %c %c %c\n%c %c %c %c\n\n",
409             csd->from_client,
410             csd->from_server,
411             csd->ignore_reassembled,
412             csd->only_reassembled,
413             ((ClientServerData *)idx_dup)->from_client,
414             ((ClientServerData *)idx_dup)->from_server,
415             ((ClientServerData *)idx_dup)->ignore_reassembled,
416             ((ClientServerData *)idx_dup)->only_reassembled);
417 #endif
418         free(csd);
419         csd = otn->ds_list[PLUGIN_CLIENTSERVER] = (ClientServerData *)idx_dup;
420     }
421 
422     fpl = AddOptFuncToList(CheckFlow, otn);
423     if (fpl)
424     {
425         fpl->type = RULE_OPTION_TYPE_FLOW;
426         fpl->context = (void *)csd;
427     }
428 
429     free(str);
430 }
431 
432 /****************************************************************************
433  *
434  * Function: InitFlowData(OptTreeNode *)
435  *
436  * Purpose: calloc the clientserver data node
437  *
438  * Arguments: otn => pointer to the current rule option list node
439  *
440  * Returns: void function
441  *
442  ****************************************************************************/
InitFlowData(OptTreeNode * otn)443 void InitFlowData(OptTreeNode * otn)
444 {
445 
446     /* allocate the data structure and attach it to the
447        rule's data struct list */
448     otn->ds_list[PLUGIN_CLIENTSERVER] = (ClientServerData *)
449         calloc(sizeof(ClientServerData), sizeof(char));
450 
451     if(otn->ds_list[PLUGIN_CLIENTSERVER] == NULL)
452     {
453         FatalError("FlowData calloc Failed!\n");
454     }
455 }
456 
CheckFlow(void * option_data,Packet * p)457 int CheckFlow(void *option_data, Packet *p)
458 {
459     ClientServerData *csd = (ClientServerData *)option_data;
460     PROFILE_VARS;
461 
462     PREPROC_PROFILE_START(flowCheckPerfStats);
463 
464     /* Check established/unestablished first */
465     if (ScStateful())
466     {
467         if ((csd->established == 1) && !(p->packet_flags & PKT_STREAM_EST))
468         {
469             /*
470             ** This option requires an established connection and it isn't
471             ** in that state yet, so no match.
472             */
473             PREPROC_PROFILE_END(flowCheckPerfStats);
474             return DETECTION_OPTION_NO_MATCH;
475         }
476         else if ((csd->unestablished == 1) && (p->packet_flags & PKT_STREAM_EST))
477         {
478             /*
479             **  We're looking for an unestablished stream, and this is
480             **  established, so don't continue processing.
481             */
482             PREPROC_PROFILE_END(flowCheckPerfStats);
483             return DETECTION_OPTION_NO_MATCH;
484         }
485     }
486 
487     /* Now check from client */
488     if (csd->from_client)
489     {
490         if (ScStateful())
491         {
492             if (!(p->packet_flags & PKT_FROM_CLIENT) &&
493                 (p->packet_flags & PKT_FROM_SERVER))
494             {
495                 /* No match on from_client */
496                 PREPROC_PROFILE_END(flowCheckPerfStats);
497                 return DETECTION_OPTION_NO_MATCH;
498             }
499         }
500     }
501 
502     /* And from server */
503     if (csd->from_server)
504     {
505         if (ScStateful())
506         {
507             if (!(p->packet_flags & PKT_FROM_SERVER) &&
508                 (p->packet_flags & PKT_FROM_CLIENT))
509             {
510                 /* No match on from_server */
511                 PREPROC_PROFILE_END(flowCheckPerfStats);
512                 return DETECTION_OPTION_NO_MATCH;
513             }
514         }
515     }
516 
517     /* ...ignore_reassembled */
518     if (csd->ignore_reassembled & IGNORE_STREAM)
519     {
520         if (p->packet_flags & PKT_REBUILT_STREAM)
521         {
522             PREPROC_PROFILE_END(flowCheckPerfStats);
523             return DETECTION_OPTION_NO_MATCH;
524         }
525     }
526 
527     if (csd->ignore_reassembled & IGNORE_FRAG)
528     {
529         if (p->packet_flags & PKT_REBUILT_FRAG)
530         {
531             PREPROC_PROFILE_END(flowCheckPerfStats);
532             return DETECTION_OPTION_NO_MATCH;
533         }
534     }
535 
536     /* ...only_reassembled */
537     if (csd->only_reassembled & ONLY_STREAM)
538     {
539         if ( !PacketHasPAFPayload(p))
540         {
541             PREPROC_PROFILE_END(flowCheckPerfStats);
542             return DETECTION_OPTION_NO_MATCH;
543         }
544     }
545 
546     if (csd->only_reassembled & ONLY_FRAG)
547     {
548         if (!(p->packet_flags & PKT_REBUILT_FRAG))
549         {
550             PREPROC_PROFILE_END(flowCheckPerfStats);
551             return DETECTION_OPTION_NO_MATCH;
552         }
553     }
554 
555     PREPROC_PROFILE_END(flowCheckPerfStats);
556     return DETECTION_OPTION_MATCH;
557 }
558