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