]> git.ipfire.org Git - people/ms/suricata.git/blob - src/detect-xbits.c
core: Remove unneeded consts
[people/ms/suricata.git] / src / detect-xbits.c
1 /* Copyright (C) 2007-2020 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18 /**
19 * \file
20 *
21 * \author Victor Julien <victor@inliniac.net>
22 *
23 * Implements the xbits keyword
24 */
25
26 #include "suricata-common.h"
27 #include "decode.h"
28 #include "detect.h"
29 #include "threads.h"
30 #include "flow.h"
31 #include "flow-util.h"
32 #include "detect-xbits.h"
33 #include "detect-hostbits.h"
34 #include "util-spm.h"
35 #include "util-byte.h"
36
37 #include "detect-engine-sigorder.h"
38
39 #include "app-layer-parser.h"
40
41 #include "detect-parse.h"
42 #include "detect-engine.h"
43 #include "detect-engine-mpm.h"
44 #include "detect-engine-state.h"
45
46 #include "flow-bit.h"
47 #include "host-bit.h"
48 #include "ippair-bit.h"
49 #include "util-var-name.h"
50 #include "util-unittest.h"
51 #include "util-debug.h"
52
53 /*
54 xbits:set,bitname,track ip_pair,expire 60
55 */
56
57 #define PARSE_REGEX "^([a-z]+)" "(?:,\\s*([^,]+))?" "(?:,\\s*(?:track\\s+([^,]+)))" "(?:,\\s*(?:expire\\s+([^,]+)))?"
58 static DetectParseRegex parse_regex;
59
60 static int DetectXbitMatch (DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *);
61 static int DetectXbitSetup (DetectEngineCtx *, Signature *, const char *);
62 #ifdef UNITTESTS
63 static void XBitsRegisterTests(void);
64 #endif
65 static void DetectXbitFree (DetectEngineCtx *, void *);
66
67 void DetectXbitsRegister (void)
68 {
69 sigmatch_table[DETECT_XBITS].name = "xbits";
70 sigmatch_table[DETECT_XBITS].desc = "operate on bits";
71 sigmatch_table[DETECT_XBITS].url = "/rules/xbits.html";
72 sigmatch_table[DETECT_XBITS].Match = DetectXbitMatch;
73 sigmatch_table[DETECT_XBITS].Setup = DetectXbitSetup;
74 sigmatch_table[DETECT_XBITS].Free = DetectXbitFree;
75 #ifdef UNITTESTS
76 sigmatch_table[DETECT_XBITS].RegisterTests = XBitsRegisterTests;
77 #endif
78 /* this is compatible to ip-only signatures */
79 sigmatch_table[DETECT_XBITS].flags |= SIGMATCH_IPONLY_COMPAT;
80
81 DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
82 }
83
84 static int DetectIPPairbitMatchToggle (Packet *p, const DetectXbitsData *fd)
85 {
86 IPPair *pair = IPPairGetIPPairFromHash(&p->src, &p->dst);
87 if (pair == NULL)
88 return 0;
89
90 IPPairBitToggle(pair,fd->idx,p->ts.tv_sec + fd->expire);
91 IPPairRelease(pair);
92 return 1;
93 }
94
95 /* return true even if bit not found */
96 static int DetectIPPairbitMatchUnset (Packet *p, const DetectXbitsData *fd)
97 {
98 IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst);
99 if (pair == NULL)
100 return 1;
101
102 IPPairBitUnset(pair,fd->idx);
103 IPPairRelease(pair);
104 return 1;
105 }
106
107 static int DetectIPPairbitMatchSet (Packet *p, const DetectXbitsData *fd)
108 {
109 IPPair *pair = IPPairGetIPPairFromHash(&p->src, &p->dst);
110 if (pair == NULL)
111 return 0;
112
113 IPPairBitSet(pair, fd->idx, p->ts.tv_sec + fd->expire);
114 IPPairRelease(pair);
115 return 1;
116 }
117
118 static int DetectIPPairbitMatchIsset (Packet *p, const DetectXbitsData *fd)
119 {
120 int r = 0;
121 IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst);
122 if (pair == NULL)
123 return 0;
124
125 r = IPPairBitIsset(pair,fd->idx,p->ts.tv_sec);
126 IPPairRelease(pair);
127 return r;
128 }
129
130 static int DetectIPPairbitMatchIsnotset (Packet *p, const DetectXbitsData *fd)
131 {
132 int r = 0;
133 IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst);
134 if (pair == NULL)
135 return 1;
136
137 r = IPPairBitIsnotset(pair,fd->idx,p->ts.tv_sec);
138 IPPairRelease(pair);
139 return r;
140 }
141
142 static int DetectXbitMatchIPPair(Packet *p, const DetectXbitsData *xd)
143 {
144 switch (xd->cmd) {
145 case DETECT_XBITS_CMD_ISSET:
146 return DetectIPPairbitMatchIsset(p,xd);
147 case DETECT_XBITS_CMD_ISNOTSET:
148 return DetectIPPairbitMatchIsnotset(p,xd);
149 case DETECT_XBITS_CMD_SET:
150 return DetectIPPairbitMatchSet(p,xd);
151 case DETECT_XBITS_CMD_UNSET:
152 return DetectIPPairbitMatchUnset(p,xd);
153 case DETECT_XBITS_CMD_TOGGLE:
154 return DetectIPPairbitMatchToggle(p,xd);
155 default:
156 SCLogError(SC_ERR_UNKNOWN_VALUE, "unknown cmd %" PRIu32 "", xd->cmd);
157 return 0;
158 }
159 return 0;
160 }
161
162 /*
163 * returns 0: no match
164 * 1: match
165 * -1: error
166 */
167
168 static int DetectXbitMatch (DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx)
169 {
170 const DetectXbitsData *fd = (const DetectXbitsData *)ctx;
171 if (fd == NULL)
172 return 0;
173
174 switch (fd->type) {
175 case VAR_TYPE_HOST_BIT:
176 return DetectXbitMatchHost(p, (const DetectXbitsData *)fd);
177 break;
178 case VAR_TYPE_IPPAIR_BIT:
179 return DetectXbitMatchIPPair(p, (const DetectXbitsData *)fd);
180 break;
181 default:
182 break;
183 }
184 return 0;
185 }
186
187 /** \internal
188 * \brief parse xbits rule options
189 * \retval 0 ok
190 * \retval -1 bad
191 * \param[out] cdout return DetectXbitsData structure or NULL if noalert
192 */
193 static int DetectXbitParse(DetectEngineCtx *de_ctx,
194 const char *rawstr, DetectXbitsData **cdout)
195 {
196 DetectXbitsData *cd = NULL;
197 uint8_t fb_cmd = 0;
198 uint8_t hb_dir = 0;
199 int ret = 0, res = 0;
200 size_t pcre2len;
201 char fb_cmd_str[16] = "", fb_name[256] = "";
202 char hb_dir_str[16] = "";
203 enum VarTypes var_type = VAR_TYPE_NOT_SET;
204 uint32_t expire = DETECT_XBITS_EXPIRE_DEFAULT;
205
206 ret = DetectParsePcreExec(&parse_regex, rawstr, 0, 0);
207 if (ret != 2 && ret != 3 && ret != 4 && ret != 5) {
208 SCLogError(SC_ERR_PCRE_MATCH, "\"%s\" is not a valid setting for xbits.", rawstr);
209 return -1;
210 }
211 SCLogDebug("ret %d, %s", ret, rawstr);
212 pcre2len = sizeof(fb_cmd_str);
213 res = pcre2_substring_copy_bynumber(
214 parse_regex.match, 1, (PCRE2_UCHAR8 *)fb_cmd_str, &pcre2len);
215 if (res < 0) {
216 SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed");
217 return -1;
218 }
219
220 if (ret >= 3) {
221 pcre2len = sizeof(fb_name);
222 res = pcre2_substring_copy_bynumber(
223 parse_regex.match, 2, (PCRE2_UCHAR8 *)fb_name, &pcre2len);
224 if (res < 0) {
225 SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed");
226 return -1;
227 }
228 if (ret >= 4) {
229 pcre2len = sizeof(hb_dir_str);
230 res = pcre2_substring_copy_bynumber(
231 parse_regex.match, 3, (PCRE2_UCHAR8 *)hb_dir_str, &pcre2len);
232 if (res < 0) {
233 SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed");
234 return -1;
235 }
236 SCLogDebug("hb_dir_str %s", hb_dir_str);
237 if (strlen(hb_dir_str) > 0) {
238 if (strcmp(hb_dir_str, "ip_src") == 0) {
239 hb_dir = DETECT_XBITS_TRACK_IPSRC;
240 var_type = VAR_TYPE_HOST_BIT;
241 } else if (strcmp(hb_dir_str, "ip_dst") == 0) {
242 hb_dir = DETECT_XBITS_TRACK_IPDST;
243 var_type = VAR_TYPE_HOST_BIT;
244 } else if (strcmp(hb_dir_str, "ip_pair") == 0) {
245 hb_dir = DETECT_XBITS_TRACK_IPPAIR;
246 var_type = VAR_TYPE_IPPAIR_BIT;
247 } else {
248 // TODO
249 return -1;
250 }
251 }
252
253 if (ret >= 5) {
254 char expire_str[16] = "";
255 pcre2len = sizeof(expire_str);
256 res = pcre2_substring_copy_bynumber(
257 parse_regex.match, 4, (PCRE2_UCHAR8 *)expire_str, &pcre2len);
258 if (res < 0) {
259 SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed");
260 return -1;
261 }
262 SCLogDebug("expire_str %s", expire_str);
263 if (StringParseUint32(&expire, 10, 0, (const char *)expire_str) < 0) {
264 SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for "
265 "expire: \"%s\"", expire_str);
266 return -1;
267 }
268 if (expire == 0) {
269 SCLogError(SC_ERR_INVALID_VALUE, "expire must be bigger than 0");
270 return -1;
271 }
272 SCLogDebug("expire %d", expire);
273 }
274 }
275 }
276
277 if (strcmp(fb_cmd_str,"noalert") == 0) {
278 fb_cmd = DETECT_XBITS_CMD_NOALERT;
279 } else if (strcmp(fb_cmd_str,"isset") == 0) {
280 fb_cmd = DETECT_XBITS_CMD_ISSET;
281 } else if (strcmp(fb_cmd_str,"isnotset") == 0) {
282 fb_cmd = DETECT_XBITS_CMD_ISNOTSET;
283 } else if (strcmp(fb_cmd_str,"set") == 0) {
284 fb_cmd = DETECT_XBITS_CMD_SET;
285 } else if (strcmp(fb_cmd_str,"unset") == 0) {
286 fb_cmd = DETECT_XBITS_CMD_UNSET;
287 } else if (strcmp(fb_cmd_str,"toggle") == 0) {
288 fb_cmd = DETECT_XBITS_CMD_TOGGLE;
289 } else {
290 SCLogError(SC_ERR_UNKNOWN_VALUE, "xbits action \"%s\" is not supported.", fb_cmd_str);
291 return -1;
292 }
293
294 switch (fb_cmd) {
295 case DETECT_XBITS_CMD_NOALERT: {
296 if (strlen(fb_name) != 0)
297 return -1;
298 /* return ok, cd is NULL. Flag sig. */
299 *cdout = NULL;
300 return 0;
301 }
302 case DETECT_XBITS_CMD_ISNOTSET:
303 case DETECT_XBITS_CMD_ISSET:
304 case DETECT_XBITS_CMD_SET:
305 case DETECT_XBITS_CMD_UNSET:
306 case DETECT_XBITS_CMD_TOGGLE:
307 default:
308 if (strlen(fb_name) == 0)
309 return -1;
310 break;
311 }
312
313 cd = SCMalloc(sizeof(DetectXbitsData));
314 if (unlikely(cd == NULL))
315 return -1;
316
317 cd->idx = VarNameStoreSetupAdd(fb_name, var_type);
318 cd->cmd = fb_cmd;
319 cd->tracker = hb_dir;
320 cd->type = var_type;
321 cd->expire = expire;
322
323 SCLogDebug("idx %" PRIu32 ", cmd %s, name %s",
324 cd->idx, fb_cmd_str, strlen(fb_name) ? fb_name : "(none)");
325
326 *cdout = cd;
327 return 0;
328 }
329
330 int DetectXbitSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
331 {
332 SigMatch *sm = NULL;
333 DetectXbitsData *cd = NULL;
334
335 int result = DetectXbitParse(de_ctx, rawstr, &cd);
336 if (result < 0) {
337 return -1;
338 /* noalert doesn't use a cd/sm struct. It flags the sig. We're done. */
339 } else if (result == 0 && cd == NULL) {
340 s->flags |= SIG_FLAG_NOALERT;
341 return 0;
342 }
343
344 /* Okay so far so good, lets get this into a SigMatch
345 * and put it in the Signature. */
346 sm = SigMatchAlloc();
347 if (sm == NULL)
348 goto error;
349
350 sm->type = DETECT_XBITS;
351 sm->ctx = (void *)cd;
352
353 switch (cd->cmd) {
354 /* case DETECT_XBITS_CMD_NOALERT can't happen here */
355
356 case DETECT_XBITS_CMD_ISNOTSET:
357 case DETECT_XBITS_CMD_ISSET:
358 /* checks, so packet list */
359 SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
360 break;
361
362 case DETECT_XBITS_CMD_SET:
363 case DETECT_XBITS_CMD_UNSET:
364 case DETECT_XBITS_CMD_TOGGLE:
365 /* modifiers, only run when entire sig has matched */
366 SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_POSTMATCH);
367 break;
368 }
369
370 return 0;
371
372 error:
373 if (cd != NULL)
374 SCFree(cd);
375 return -1;
376 }
377
378 static void DetectXbitFree (DetectEngineCtx *de_ctx, void *ptr)
379 {
380 DetectXbitsData *fd = (DetectXbitsData *)ptr;
381
382 if (fd == NULL)
383 return;
384
385 SCFree(fd);
386 }
387
388 #ifdef UNITTESTS
389
390 static void XBitsTestSetup(void)
391 {
392 StorageInit();
393 HostBitInitCtx();
394 IPPairBitInitCtx();
395 StorageFinalize();
396 HostInitConfig(true);
397 IPPairInitConfig(true);
398 }
399
400 static void XBitsTestShutdown(void)
401 {
402 HostCleanup();
403 IPPairCleanup();
404 StorageCleanup();
405 }
406
407
408 static int XBitsTestParse01(void)
409 {
410 DetectEngineCtx *de_ctx = NULL;
411 de_ctx = DetectEngineCtxInit();
412 FAIL_IF_NULL(de_ctx);
413 de_ctx->flags |= DE_QUIET;
414 DetectXbitsData *cd = NULL;
415
416 #define BAD_INPUT(str) \
417 FAIL_IF_NOT(DetectXbitParse(de_ctx, (str), &cd) == -1);
418
419 BAD_INPUT("alert");
420 BAD_INPUT("n0alert");
421 BAD_INPUT("nOalert");
422 BAD_INPUT("set,abc,track nonsense, expire 3600");
423 BAD_INPUT("set,abc,track ip_source, expire 3600");
424 BAD_INPUT("set,abc,track ip_src, expire -1");
425 BAD_INPUT("set,abc,track ip_src, expire 0");
426
427 #undef BAD_INPUT
428
429 #define GOOD_INPUT(str, command, trk, typ, exp) \
430 FAIL_IF_NOT(DetectXbitParse(de_ctx, (str), &cd) == 0); \
431 FAIL_IF_NULL(cd); \
432 FAIL_IF_NOT(cd->cmd == (command)); \
433 FAIL_IF_NOT(cd->tracker == (trk)); \
434 FAIL_IF_NOT(cd->type == (typ)); \
435 FAIL_IF_NOT(cd->expire == (exp)); \
436 DetectXbitFree(NULL, cd); \
437 cd = NULL;
438
439 GOOD_INPUT("set,abc,track ip_pair",
440 DETECT_XBITS_CMD_SET,
441 DETECT_XBITS_TRACK_IPPAIR, VAR_TYPE_IPPAIR_BIT,
442 DETECT_XBITS_EXPIRE_DEFAULT);
443 GOOD_INPUT("set,abc,track ip_pair, expire 3600",
444 DETECT_XBITS_CMD_SET,
445 DETECT_XBITS_TRACK_IPPAIR, VAR_TYPE_IPPAIR_BIT,
446 3600);
447 GOOD_INPUT("set,abc,track ip_src, expire 1234",
448 DETECT_XBITS_CMD_SET,
449 DETECT_XBITS_TRACK_IPSRC, VAR_TYPE_HOST_BIT,
450 1234);
451
452 #undef GOOD_INPUT
453
454 DetectEngineCtxFree(de_ctx);
455 PASS;
456 }
457
458 /**
459 * \test
460 */
461
462 static int XBitsTestSig01(void)
463 {
464 uint8_t *buf = (uint8_t *)
465 "GET /one/ HTTP/1.1\r\n"
466 "Host: one.example.org\r\n"
467 "\r\n";
468 uint16_t buflen = strlen((char *)buf);
469 Packet *p = SCMalloc(SIZE_OF_PACKET);
470 FAIL_IF_NULL(p);
471 Signature *s = NULL;
472 ThreadVars th_v;
473 DetectEngineThreadCtx *det_ctx = NULL;
474 DetectEngineCtx *de_ctx = NULL;
475
476 memset(&th_v, 0, sizeof(th_v));
477 memset(p, 0, SIZE_OF_PACKET);
478 p->src.family = AF_INET;
479 p->dst.family = AF_INET;
480 p->payload = buf;
481 p->payload_len = buflen;
482 p->proto = IPPROTO_TCP;
483
484 XBitsTestSetup();
485
486 de_ctx = DetectEngineCtxInit();
487 FAIL_IF_NULL(de_ctx);
488 de_ctx->flags |= DE_QUIET;
489
490 s = DetectEngineAppendSig(de_ctx,
491 "alert ip any any -> any any (xbits:set,abc,track ip_pair; content:\"GET \"; sid:1;)");
492 FAIL_IF_NULL(s);
493
494 SigGroupBuild(de_ctx);
495 DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
496 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
497 DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
498 DetectEngineCtxFree(de_ctx);
499 XBitsTestShutdown();
500 SCFree(p);
501 StatsThreadCleanup(&th_v);
502 StatsReleaseResources();
503 PASS;
504 }
505
506 /**
507 * \test various options
508 *
509 * \retval 1 on succces
510 * \retval 0 on failure
511 */
512
513 static int XBitsTestSig02(void)
514 {
515 Signature *s = NULL;
516 DetectEngineCtx *de_ctx = NULL;
517 de_ctx = DetectEngineCtxInit();
518 FAIL_IF_NULL(de_ctx);
519 de_ctx->flags |= DE_QUIET;
520
521 s = DetectEngineAppendSig(de_ctx,
522 "alert ip any any -> any any (xbits:isset,abc,track ip_src; content:\"GET \"; sid:1;)");
523 FAIL_IF_NULL(s);
524
525 s = DetectEngineAppendSig(de_ctx,
526 "alert ip any any -> any any (xbits:isnotset,abc,track ip_dst; content:\"GET \"; sid:2;)");
527 FAIL_IF_NULL(s);
528
529 s = DetectEngineAppendSig(de_ctx,
530 "alert ip any any -> any any (xbits:set,abc,track ip_pair; content:\"GET \"; sid:3;)");
531 FAIL_IF_NULL(s);
532
533 s = DetectEngineAppendSig(de_ctx,
534 "alert ip any any -> any any (xbits:unset,abc,track ip_src; content:\"GET \"; sid:4;)");
535 FAIL_IF_NULL(s);
536
537 s = DetectEngineAppendSig(de_ctx,
538 "alert ip any any -> any any (xbits:toggle,abc,track ip_dst; content:\"GET \"; sid:5;)");
539 FAIL_IF_NULL(s);
540
541 s = DetectEngineAppendSig(de_ctx,
542 "alert ip any any -> any any (xbits:!set,abc,track ip_dst; content:\"GET \"; sid:6;)");
543 FAIL_IF_NOT_NULL(s);
544
545 DetectEngineCtxFree(de_ctx);
546 PASS;
547 }
548
549 /**
550 * \brief this function registers unit tests for XBits
551 */
552 static void XBitsRegisterTests(void)
553 {
554 UtRegisterTest("XBitsTestParse01", XBitsTestParse01);
555 UtRegisterTest("XBitsTestSig01", XBitsTestSig01);
556 UtRegisterTest("XBitsTestSig02", XBitsTestSig02);
557 }
558 #endif /* UNITTESTS */