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 
24 #include "suricata-common.h"
25 #include "suricata.h"
26 #include "debug.h"
27 #include "detect.h"
28 #include "flow.h"
29 #include "flow-private.h"
30 #include "flow-util.h"
31 #include "flow-worker.h"
32 #include "conf.h"
33 #include "conf-yaml-loader.h"
34 #include "datasets.h"
35 
36 #include "app-layer-parser.h"
37 #include "app-layer-htp.h"
38 
39 #include "detect-parse.h"
40 #include "detect-engine-sigorder.h"
41 
42 #include "detect-engine-siggroup.h"
43 #include "detect-engine-address.h"
44 #include "detect-engine-port.h"
45 #include "detect-engine-prefilter.h"
46 #include "detect-engine-mpm.h"
47 #include "detect-engine-iponly.h"
48 #include "detect-engine-tag.h"
49 
50 #include "detect-engine-file.h"
51 
52 #include "detect-engine.h"
53 #include "detect-engine-state.h"
54 #include "detect-engine-payload.h"
55 #include "detect-byte-extract.h"
56 #include "detect-content.h"
57 #include "detect-uricontent.h"
58 #include "detect-tcphdr.h"
59 #include "detect-engine-threshold.h"
60 #include "detect-engine-content-inspection.h"
61 
62 #include "detect-engine-loader.h"
63 
64 #include "util-classification-config.h"
65 #include "util-reference-config.h"
66 #include "util-threshold-config.h"
67 #include "util-error.h"
68 #include "util-hash.h"
69 #include "util-byte.h"
70 #include "util-debug.h"
71 #include "util-unittest.h"
72 #include "util-action.h"
73 #include "util-magic.h"
74 #include "util-signal.h"
75 #include "util-spm.h"
76 #include "util-device.h"
77 #include "util-var-name.h"
78 #include "util-profiling.h"
79 #include "util-validate.h"
80 
81 #include "tm-threads.h"
82 #include "runmodes.h"
83 
84 #include "reputation.h"
85 
86 #define DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT 3000
87 
88 static DetectEngineThreadCtx *DetectEngineThreadCtxInitForReload(
89         ThreadVars *tv, DetectEngineCtx *new_de_ctx, int mt);
90 
91 static int DetectEngineCtxLoadConf(DetectEngineCtx *);
92 
93 static DetectEngineMasterCtx g_master_de_ctx = { SCMUTEX_INITIALIZER,
94     0, 99, NULL, NULL, TENANT_SELECTOR_UNKNOWN, NULL, NULL, 0};
95 
96 static uint32_t TenantIdHash(HashTable *h, void *data, uint16_t data_len);
97 static char TenantIdCompare(void *d1, uint16_t d1_len, void *d2, uint16_t d2_len);
98 static void TenantIdFree(void *d);
99 static uint32_t DetectEngineTentantGetIdFromLivedev(const void *ctx, const Packet *p);
100 static uint32_t DetectEngineTentantGetIdFromVlanId(const void *ctx, const Packet *p);
101 static uint32_t DetectEngineTentantGetIdFromPcap(const void *ctx, const Packet *p);
102 
103 static DetectEngineAppInspectionEngine *g_app_inspect_engines = NULL;
104 static DetectEnginePktInspectionEngine *g_pkt_inspect_engines = NULL;
105 
106 SCEnumCharMap det_ctx_event_table[] = {
107 #ifdef UNITTESTS
108     { "TEST", DET_CTX_EVENT_TEST },
109 #endif
110     { "NO_MEMORY", FILE_DECODER_EVENT_NO_MEM },
111     { "INVALID_SWF_LENGTH", FILE_DECODER_EVENT_INVALID_SWF_LENGTH },
112     { "INVALID_SWF_VERSION", FILE_DECODER_EVENT_INVALID_SWF_VERSION },
113     { "Z_DATA_ERROR", FILE_DECODER_EVENT_Z_DATA_ERROR },
114     { "Z_STREAM_ERROR", FILE_DECODER_EVENT_Z_STREAM_ERROR },
115     { "Z_BUF_ERROR", FILE_DECODER_EVENT_Z_BUF_ERROR },
116     { "Z_UNKNOWN_ERROR", FILE_DECODER_EVENT_Z_UNKNOWN_ERROR },
117     { "LZMA_DECODER_ERROR", FILE_DECODER_EVENT_LZMA_DECODER_ERROR },
118     { "LZMA_MEMLIMIT_ERROR", FILE_DECODER_EVENT_LZMA_MEMLIMIT_ERROR },
119     { "LZMA_OPTIONS_ERROR", FILE_DECODER_EVENT_LZMA_OPTIONS_ERROR },
120     { "LZMA_FORMAT_ERROR", FILE_DECODER_EVENT_LZMA_FORMAT_ERROR },
121     { "LZMA_DATA_ERROR", FILE_DECODER_EVENT_LZMA_DATA_ERROR },
122     { "LZMA_BUF_ERROR", FILE_DECODER_EVENT_LZMA_BUF_ERROR },
123     { "LZMA_UNKNOWN_ERROR", FILE_DECODER_EVENT_LZMA_UNKNOWN_ERROR },
124     {
125             "TOO_MANY_BUFFERS",
126             DETECT_EVENT_TOO_MANY_BUFFERS,
127     },
128     { NULL, -1 },
129 };
130 
131 /** \brief register inspect engine at start up time
132  *
133  *  \note errors are fatal */
DetectPktInspectEngineRegister(const char * name,InspectionBufferGetPktDataPtr GetPktData,InspectionBufferPktInspectFunc Callback)134 void DetectPktInspectEngineRegister(const char *name,
135         InspectionBufferGetPktDataPtr GetPktData,
136         InspectionBufferPktInspectFunc Callback)
137 {
138     DetectBufferTypeRegister(name);
139     const int sm_list = DetectBufferTypeGetByName(name);
140     if (sm_list == -1) {
141         FatalError(SC_ERR_INITIALIZATION,
142             "failed to register inspect engine %s", name);
143     }
144 
145     if ((sm_list < DETECT_SM_LIST_MATCH) || (sm_list >= SHRT_MAX) ||
146         (Callback == NULL))
147     {
148         SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid arguments");
149         BUG_ON(1);
150     }
151 
152     DetectEnginePktInspectionEngine *new_engine = SCCalloc(1, sizeof(*new_engine));
153     if (unlikely(new_engine == NULL)) {
154         FatalError(SC_ERR_INITIALIZATION,
155             "failed to register inspect engine %s: %s", name, strerror(errno));
156     }
157     new_engine->sm_list = sm_list;
158     new_engine->sm_list_base = sm_list;
159     new_engine->v1.Callback = Callback;
160     new_engine->v1.GetData = GetPktData;
161 
162     if (g_pkt_inspect_engines == NULL) {
163         g_pkt_inspect_engines = new_engine;
164     } else {
165         DetectEnginePktInspectionEngine *t = g_pkt_inspect_engines;
166         while (t->next != NULL) {
167             t = t->next;
168         }
169 
170         t->next = new_engine;
171     }
172 }
173 
174 /** \brief register inspect engine at start up time
175  *
176  *  \note errors are fatal */
DetectAppLayerInspectEngineRegister(const char * name,AppProto alproto,uint32_t dir,int progress,InspectEngineFuncPtr Callback)177 void DetectAppLayerInspectEngineRegister(const char *name,
178         AppProto alproto, uint32_t dir,
179         int progress, InspectEngineFuncPtr Callback)
180 {
181     if (AppLayerParserIsEnabled(alproto)) {
182         if (!AppLayerParserSupportsTxDetectFlags(alproto)) {
183             FatalError(SC_ERR_INITIALIZATION,
184                 "Inspect engine registered for app-layer protocol without "
185                 "TX detect flag support: %s", AppProtoToString(alproto));
186         }
187     }
188     DetectBufferTypeRegister(name);
189     const int sm_list = DetectBufferTypeGetByName(name);
190     if (sm_list == -1) {
191         FatalError(SC_ERR_INITIALIZATION,
192             "failed to register inspect engine %s", name);
193     }
194 
195     if ((alproto >= ALPROTO_FAILED) ||
196         (!(dir == SIG_FLAG_TOSERVER || dir == SIG_FLAG_TOCLIENT)) ||
197         (sm_list < DETECT_SM_LIST_MATCH) || (sm_list >= SHRT_MAX) ||
198         (progress < 0 || progress >= SHRT_MAX) ||
199         (Callback == NULL))
200     {
201         SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid arguments");
202         BUG_ON(1);
203     }
204 
205     int direction;
206     if (dir == SIG_FLAG_TOSERVER) {
207         direction = 0;
208     } else {
209         direction = 1;
210     }
211 
212     DetectEngineAppInspectionEngine *new_engine = SCMalloc(sizeof(DetectEngineAppInspectionEngine));
213     if (unlikely(new_engine == NULL)) {
214         exit(EXIT_FAILURE);
215     }
216     memset(new_engine, 0, sizeof(*new_engine));
217     new_engine->alproto = alproto;
218     new_engine->dir = direction;
219     new_engine->sm_list = sm_list;
220     new_engine->progress = progress;
221     new_engine->Callback = Callback;
222 
223     if (g_app_inspect_engines == NULL) {
224         g_app_inspect_engines = new_engine;
225     } else {
226         DetectEngineAppInspectionEngine *t = g_app_inspect_engines;
227         while (t->next != NULL) {
228             t = t->next;
229         }
230 
231         t->next = new_engine;
232     }
233 }
234 
235 /** \brief register inspect engine at start up time
236  *
237  *  \note errors are fatal */
DetectAppLayerInspectEngineRegister2(const char * name,AppProto alproto,uint32_t dir,int progress,InspectEngineFuncPtr2 Callback2,InspectionBufferGetDataPtr GetData)238 void DetectAppLayerInspectEngineRegister2(const char *name,
239         AppProto alproto, uint32_t dir, int progress,
240         InspectEngineFuncPtr2 Callback2,
241         InspectionBufferGetDataPtr GetData)
242 {
243     DetectBufferTypeRegister(name);
244     const int sm_list = DetectBufferTypeGetByName(name);
245     if (sm_list == -1) {
246         FatalError(SC_ERR_INITIALIZATION,
247             "failed to register inspect engine %s", name);
248     }
249 
250     if ((alproto >= ALPROTO_FAILED) ||
251         (!(dir == SIG_FLAG_TOSERVER || dir == SIG_FLAG_TOCLIENT)) ||
252         (sm_list < DETECT_SM_LIST_MATCH) || (sm_list >= SHRT_MAX) ||
253         (progress < 0 || progress >= SHRT_MAX) ||
254         (Callback2 == NULL))
255     {
256         SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid arguments");
257         BUG_ON(1);
258     } else if (Callback2 == DetectEngineInspectBufferGeneric && GetData == NULL) {
259         SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid arguments: must register "
260                 "GetData with DetectEngineInspectBufferGeneric");
261         BUG_ON(1);
262     }
263 
264     int direction;
265     if (dir == SIG_FLAG_TOSERVER) {
266         direction = 0;
267     } else {
268         direction = 1;
269     }
270 
271     DetectEngineAppInspectionEngine *new_engine = SCMalloc(sizeof(DetectEngineAppInspectionEngine));
272     if (unlikely(new_engine == NULL)) {
273         exit(EXIT_FAILURE);
274     }
275     memset(new_engine, 0, sizeof(*new_engine));
276     new_engine->alproto = alproto;
277     new_engine->dir = direction;
278     new_engine->sm_list = sm_list;
279     new_engine->sm_list_base = sm_list;
280     new_engine->progress = progress;
281     new_engine->v2.Callback = Callback2;
282     new_engine->v2.GetData = GetData;
283 
284     if (g_app_inspect_engines == NULL) {
285         g_app_inspect_engines = new_engine;
286     } else {
287         DetectEngineAppInspectionEngine *t = g_app_inspect_engines;
288         while (t->next != NULL) {
289             t = t->next;
290         }
291 
292         t->next = new_engine;
293     }
294 }
295 
296 /* copy an inspect engine with transforms to a new list id. */
DetectAppLayerInspectEngineCopy(DetectEngineCtx * de_ctx,int sm_list,int new_list,const DetectEngineTransforms * transforms)297 static void DetectAppLayerInspectEngineCopy(
298         DetectEngineCtx *de_ctx,
299         int sm_list, int new_list,
300         const DetectEngineTransforms *transforms)
301 {
302     const DetectEngineAppInspectionEngine *t = g_app_inspect_engines;
303     while (t) {
304         if (t->sm_list == sm_list) {
305             DetectEngineAppInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
306             if (unlikely(new_engine == NULL)) {
307                 exit(EXIT_FAILURE);
308             }
309             new_engine->alproto = t->alproto;
310             new_engine->dir = t->dir;
311             new_engine->sm_list = new_list;         /* use new list id */
312             new_engine->sm_list_base = sm_list;
313             new_engine->progress = t->progress;
314             new_engine->Callback = t->Callback;
315             new_engine->v2 = t->v2;
316             new_engine->v2.transforms = transforms; /* assign transforms */
317 
318             if (de_ctx->app_inspect_engines == NULL) {
319                 de_ctx->app_inspect_engines = new_engine;
320             } else {
321                 DetectEngineAppInspectionEngine *list = de_ctx->app_inspect_engines;
322                 while (list->next != NULL) {
323                     list = list->next;
324                 }
325 
326                 list->next = new_engine;
327             }
328         }
329         t = t->next;
330     }
331 }
332 
333 /* copy inspect engines from global registrations to de_ctx list */
DetectAppLayerInspectEngineCopyListToDetectCtx(DetectEngineCtx * de_ctx)334 static void DetectAppLayerInspectEngineCopyListToDetectCtx(DetectEngineCtx *de_ctx)
335 {
336     const DetectEngineAppInspectionEngine *t = g_app_inspect_engines;
337     while (t) {
338         DetectEngineAppInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
339         if (unlikely(new_engine == NULL)) {
340             exit(EXIT_FAILURE);
341         }
342         new_engine->alproto = t->alproto;
343         new_engine->dir = t->dir;
344         new_engine->sm_list = t->sm_list;
345         new_engine->sm_list_base = t->sm_list;
346         new_engine->progress = t->progress;
347         new_engine->Callback = t->Callback;
348         new_engine->v2 = t->v2;
349 
350         if (de_ctx->app_inspect_engines == NULL) {
351             de_ctx->app_inspect_engines = new_engine;
352         } else {
353             DetectEngineAppInspectionEngine *list = de_ctx->app_inspect_engines;
354             while (list->next != NULL) {
355                 list = list->next;
356             }
357 
358             list->next = new_engine;
359         }
360 
361         t = t->next;
362     }
363 }
364 
365 /* copy an inspect engine with transforms to a new list id. */
DetectPktInspectEngineCopy(DetectEngineCtx * de_ctx,int sm_list,int new_list,const DetectEngineTransforms * transforms)366 static void DetectPktInspectEngineCopy(
367         DetectEngineCtx *de_ctx,
368         int sm_list, int new_list,
369         const DetectEngineTransforms *transforms)
370 {
371     const DetectEnginePktInspectionEngine *t = g_pkt_inspect_engines;
372     while (t) {
373         if (t->sm_list == sm_list) {
374             DetectEnginePktInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEnginePktInspectionEngine));
375             if (unlikely(new_engine == NULL)) {
376                 exit(EXIT_FAILURE);
377             }
378             new_engine->sm_list = new_list;         /* use new list id */
379             new_engine->sm_list_base = sm_list;
380             new_engine->v1 = t->v1;
381             new_engine->v1.transforms = transforms; /* assign transforms */
382 
383             if (de_ctx->pkt_inspect_engines == NULL) {
384                 de_ctx->pkt_inspect_engines = new_engine;
385             } else {
386                 DetectEnginePktInspectionEngine *list = de_ctx->pkt_inspect_engines;
387                 while (list->next != NULL) {
388                     list = list->next;
389                 }
390 
391                 list->next = new_engine;
392             }
393         }
394         t = t->next;
395     }
396 }
397 
398 /* copy inspect engines from global registrations to de_ctx list */
DetectPktInspectEngineCopyListToDetectCtx(DetectEngineCtx * de_ctx)399 static void DetectPktInspectEngineCopyListToDetectCtx(DetectEngineCtx *de_ctx)
400 {
401     const DetectEnginePktInspectionEngine *t = g_pkt_inspect_engines;
402     while (t) {
403         SCLogDebug("engine %p", t);
404         DetectEnginePktInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEnginePktInspectionEngine));
405         if (unlikely(new_engine == NULL)) {
406             exit(EXIT_FAILURE);
407         }
408         new_engine->sm_list = t->sm_list;
409         new_engine->sm_list_base = t->sm_list;
410         new_engine->v1 = t->v1;
411 
412         if (de_ctx->pkt_inspect_engines == NULL) {
413             de_ctx->pkt_inspect_engines = new_engine;
414         } else {
415             DetectEnginePktInspectionEngine *list = de_ctx->pkt_inspect_engines;
416             while (list->next != NULL) {
417                 list = list->next;
418             }
419 
420             list->next = new_engine;
421         }
422 
423         t = t->next;
424     }
425 }
426 
427 /** \internal
428  *  \brief append the stream inspection
429  *
430  *  If stream inspection is MPM, then prepend it.
431  */
AppendStreamInspectEngine(Signature * s,SigMatchData * stream,int direction,uint32_t id)432 static void AppendStreamInspectEngine(Signature *s, SigMatchData *stream, int direction, uint32_t id)
433 {
434     bool prepend = false;
435 
436     DetectEngineAppInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
437     if (unlikely(new_engine == NULL)) {
438         exit(EXIT_FAILURE);
439     }
440     if (SigMatchListSMBelongsTo(s, s->init_data->mpm_sm) == DETECT_SM_LIST_PMATCH) {
441         SCLogDebug("stream is mpm");
442         prepend = true;
443         new_engine->mpm = true;
444     }
445     new_engine->alproto = ALPROTO_UNKNOWN; /* all */
446     new_engine->dir = direction;
447     new_engine->stream = true;
448     new_engine->sm_list = DETECT_SM_LIST_PMATCH;
449     new_engine->sm_list_base = DETECT_SM_LIST_PMATCH;
450     new_engine->smd = stream;
451     new_engine->Callback = DetectEngineInspectStream;
452     new_engine->progress = 0;
453 
454     /* append */
455     if (s->app_inspect == NULL) {
456         s->app_inspect = new_engine;
457         new_engine->id = DE_STATE_FLAG_BASE; /* id is used as flag in stateful detect */
458     } else if (prepend) {
459         new_engine->next = s->app_inspect;
460         s->app_inspect = new_engine;
461         new_engine->id = id;
462 
463     } else {
464         DetectEngineAppInspectionEngine *a = s->app_inspect;
465         while (a->next != NULL) {
466             a = a->next;
467         }
468 
469         a->next = new_engine;
470         new_engine->id = id;
471     }
472     SCLogDebug("sid %u: engine %p/%u added", s->id, new_engine, new_engine->id);
473 }
474 
475 /**
476  *  \note for the file inspect engine, the id DE_STATE_ID_FILE_INSPECT
477  *        is assigned.
478  */
DetectEngineAppInspectionEngine2Signature(DetectEngineCtx * de_ctx,Signature * s)479 int DetectEngineAppInspectionEngine2Signature(DetectEngineCtx *de_ctx, Signature *s)
480 {
481     const int nlists = s->init_data->smlists_array_size;
482     SigMatchData *ptrs[nlists];
483     memset(&ptrs, 0, (nlists * sizeof(SigMatchData *)));
484 
485     const int mpm_list = s->init_data->mpm_sm ?
486         SigMatchListSMBelongsTo(s, s->init_data->mpm_sm) :
487         -1;
488 
489     const int files_id = DetectBufferTypeGetByName("files");
490 
491     /* convert lists to SigMatchData arrays */
492     int i = 0;
493     for (i = DETECT_SM_LIST_DYNAMIC_START; i < nlists; i++) {
494         if (s->init_data->smlists[i] == NULL)
495             continue;
496 
497         ptrs[i] = SigMatchList2DataArray(s->init_data->smlists[i]);
498         SCLogDebug("ptrs[%d] is set", i);
499     }
500 
501     /* set up pkt inspect engines */
502     const DetectEnginePktInspectionEngine *e = de_ctx->pkt_inspect_engines;
503     while (e != NULL) {
504         SCLogDebug("e %p sm_list %u nlists %u ptrs[] %p", e, e->sm_list, nlists, e->sm_list < nlists ? ptrs[e->sm_list] : NULL);
505         if (e->sm_list < nlists && ptrs[e->sm_list] != NULL) {
506             bool prepend = false;
507 
508             DetectEnginePktInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEnginePktInspectionEngine));
509             if (unlikely(new_engine == NULL)) {
510                 exit(EXIT_FAILURE);
511             }
512             if (mpm_list == e->sm_list) {
513                 SCLogDebug("%s is mpm", DetectBufferTypeGetNameById(de_ctx, e->sm_list));
514                 prepend = true;
515                 new_engine->mpm = true;
516             }
517 
518             new_engine->sm_list = e->sm_list;
519             new_engine->sm_list_base = e->sm_list_base;
520             new_engine->smd = ptrs[new_engine->sm_list];
521             new_engine->v1 = e->v1;
522             SCLogDebug("sm_list %d new_engine->v1 %p/%p/%p",
523                     new_engine->sm_list, new_engine->v1.Callback,
524                     new_engine->v1.GetData, new_engine->v1.transforms);
525 
526             if (s->pkt_inspect == NULL) {
527                 s->pkt_inspect = new_engine;
528             } else if (prepend) {
529                 new_engine->next = s->pkt_inspect;
530                 s->pkt_inspect = new_engine;
531             } else {
532                 DetectEnginePktInspectionEngine *a = s->pkt_inspect;
533                 while (a->next != NULL) {
534                     a = a->next;
535                 }
536                 new_engine->next = a->next;
537                 a->next = new_engine;
538             }
539         }
540         e = e->next;
541     }
542 
543     bool head_is_mpm = false;
544     uint32_t last_id = DE_STATE_FLAG_BASE;
545     const DetectEngineAppInspectionEngine *t = de_ctx->app_inspect_engines;
546     while (t != NULL) {
547         bool prepend = false;
548 
549         if (t->sm_list >= nlists)
550             goto next;
551 
552         if (ptrs[t->sm_list] == NULL)
553             goto next;
554 
555         SCLogDebug("ptrs[%d] is set", t->sm_list);
556 
557         if (t->alproto == ALPROTO_UNKNOWN) {
558             /* special case, inspect engine applies to all protocols */
559         } else if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, t->alproto))
560             goto next;
561 
562         if (s->flags & SIG_FLAG_TOSERVER && !(s->flags & SIG_FLAG_TOCLIENT)) {
563             if (t->dir == 1)
564                 goto next;
565         } else if (s->flags & SIG_FLAG_TOCLIENT && !(s->flags & SIG_FLAG_TOSERVER)) {
566             if (t->dir == 0)
567                 goto next;
568         }
569         DetectEngineAppInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
570         if (unlikely(new_engine == NULL)) {
571             exit(EXIT_FAILURE);
572         }
573         if (mpm_list == t->sm_list) {
574             SCLogDebug("%s is mpm", DetectBufferTypeGetNameById(de_ctx, t->sm_list));
575             prepend = true;
576             head_is_mpm = true;
577             new_engine->mpm = true;
578         }
579 
580         new_engine->alproto = t->alproto;
581         new_engine->dir = t->dir;
582         new_engine->sm_list = t->sm_list;
583         new_engine->sm_list_base = t->sm_list_base;
584         new_engine->smd = ptrs[new_engine->sm_list];
585         new_engine->Callback = t->Callback;
586         new_engine->progress = t->progress;
587         new_engine->v2 = t->v2;
588         SCLogDebug("sm_list %d new_engine->v2 %p/%p/%p",
589                 new_engine->sm_list, new_engine->v2.Callback,
590                 new_engine->v2.GetData, new_engine->v2.transforms);
591 
592         if (s->app_inspect == NULL) {
593             s->app_inspect = new_engine;
594             if (new_engine->sm_list == files_id) {
595                 SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
596                 new_engine->id = DE_STATE_ID_FILE_INSPECT;
597             } else {
598                 new_engine->id = DE_STATE_FLAG_BASE; /* id is used as flag in stateful detect */
599             }
600 
601         /* prepend engine if forced or if our engine has a lower progress. */
602         } else if (prepend || (!head_is_mpm && s->app_inspect->progress > new_engine->progress)) {
603             new_engine->next = s->app_inspect;
604             s->app_inspect = new_engine;
605             if (new_engine->sm_list == files_id) {
606                 SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
607                 new_engine->id = DE_STATE_ID_FILE_INSPECT;
608             } else {
609                 new_engine->id = ++last_id;
610             }
611 
612         } else {
613             DetectEngineAppInspectionEngine *a = s->app_inspect;
614             while (a->next != NULL) {
615                 if (a->next && a->next->progress > new_engine->progress) {
616                     break;
617                 }
618 
619                 a = a->next;
620             }
621 
622             new_engine->next = a->next;
623             a->next = new_engine;
624             if (new_engine->sm_list == files_id) {
625                 SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
626                 new_engine->id = DE_STATE_ID_FILE_INSPECT;
627             } else {
628                 new_engine->id = ++last_id;
629             }
630         }
631 
632         SCLogDebug("sid %u: engine %p/%u added", s->id, new_engine, new_engine->id);
633 
634         s->init_data->init_flags |= SIG_FLAG_INIT_STATE_MATCH;
635 next:
636         t = t->next;
637     }
638 
639     if ((s->init_data->init_flags & SIG_FLAG_INIT_STATE_MATCH) &&
640             s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL)
641     {
642         /* if engine is added multiple times, we pass it the same list */
643         SigMatchData *stream = SigMatchList2DataArray(s->init_data->smlists[DETECT_SM_LIST_PMATCH]);
644         BUG_ON(stream == NULL);
645         if (s->flags & SIG_FLAG_TOSERVER && !(s->flags & SIG_FLAG_TOCLIENT)) {
646             AppendStreamInspectEngine(s, stream, 0, last_id + 1);
647         } else if (s->flags & SIG_FLAG_TOCLIENT && !(s->flags & SIG_FLAG_TOSERVER)) {
648             AppendStreamInspectEngine(s, stream, 1, last_id + 1);
649         } else {
650             AppendStreamInspectEngine(s, stream, 0, last_id + 1);
651             AppendStreamInspectEngine(s, stream, 1, last_id + 1);
652         }
653 
654         if (s->init_data->init_flags & SIG_FLAG_INIT_NEED_FLUSH) {
655             SCLogDebug("set SIG_FLAG_FLUSH on %u", s->id);
656             s->flags |= SIG_FLAG_FLUSH;
657         }
658     }
659 
660 #ifdef DEBUG
661     const DetectEngineAppInspectionEngine *iter = s->app_inspect;
662     while (iter) {
663         SCLogDebug("%u: engine %s id %u progress %d %s", s->id,
664                 DetectBufferTypeGetNameById(de_ctx, iter->sm_list), iter->id,
665                 iter->progress,
666                 iter->sm_list == mpm_list ? "MPM":"");
667         iter = iter->next;
668     }
669 #endif
670     return 0;
671 }
672 
673 /** \brief free app inspect engines for a signature
674  *
675  *  For lists that are registered multiple times, like http_header and
676  *  http_cookie, making the engines owner of the lists is complicated.
677  *  Multiple engines in a sig may be pointing to the same list. To
678  *  address this the 'free' code needs to be extra careful about not
679  *  double freeing, so it takes an approach to first fill an array
680  *  of the to-free pointers before freeing them.
681  */
DetectEngineAppInspectionEngineSignatureFree(DetectEngineCtx * de_ctx,Signature * s)682 void DetectEngineAppInspectionEngineSignatureFree(DetectEngineCtx *de_ctx, Signature *s)
683 {
684     int nlists = 0;
685 
686     DetectEngineAppInspectionEngine *ie = s->app_inspect;
687     while (ie) {
688         nlists = MAX(ie->sm_list + 1, nlists);
689         ie = ie->next;
690     }
691     DetectEnginePktInspectionEngine *e = s->pkt_inspect;
692     while (e) {
693         nlists = MAX(e->sm_list + 1, nlists);
694         e = e->next;
695     }
696     if (nlists == 0) {
697         BUG_ON(s->pkt_inspect);
698         return;
699     }
700 
701     SigMatchData *ptrs[nlists];
702     memset(&ptrs, 0, (nlists * sizeof(SigMatchData *)));
703 
704     /* free engines and put smd in the array */
705     ie = s->app_inspect;
706     while (ie) {
707         DetectEngineAppInspectionEngine *next = ie->next;
708         BUG_ON(ptrs[ie->sm_list] != NULL && ptrs[ie->sm_list] != ie->smd);
709         ptrs[ie->sm_list] = ie->smd;
710         SCFree(ie);
711         ie = next;
712     }
713     e = s->pkt_inspect;
714     while (e) {
715         DetectEnginePktInspectionEngine *next = e->next;
716         ptrs[e->sm_list] = e->smd;
717         SCFree(e);
718         e = next;
719     }
720 
721     /* free the smds */
722     for (int i = 0; i < nlists; i++)
723     {
724         if (ptrs[i] == NULL)
725             continue;
726 
727         SigMatchData *smd = ptrs[i];
728         while(1) {
729             if (sigmatch_table[smd->type].Free != NULL) {
730                 sigmatch_table[smd->type].Free(de_ctx, smd->ctx);
731             }
732             if (smd->is_last)
733                 break;
734             smd++;
735         }
736         SCFree(ptrs[i]);
737     }
738 }
739 
740 /* code for registering buffers */
741 
742 #include "util-hash-lookup3.h"
743 
744 static HashListTable *g_buffer_type_hash = NULL;
745 static int g_buffer_type_id = DETECT_SM_LIST_DYNAMIC_START;
746 static int g_buffer_type_reg_closed = 0;
747 
748 static DetectEngineTransforms no_transforms = {
749     .transforms[0] = {0, NULL},
750     .cnt = 0,
751 };
752 
DetectBufferTypeMaxId(void)753 int DetectBufferTypeMaxId(void)
754 {
755     return g_buffer_type_id;
756 }
757 
DetectBufferTypeHashFunc(HashListTable * ht,void * data,uint16_t datalen)758 static uint32_t DetectBufferTypeHashFunc(HashListTable *ht, void *data, uint16_t datalen)
759 {
760     const DetectBufferType *map = (DetectBufferType *)data;
761     uint32_t hash = 0;
762 
763     hash = hashlittle_safe(map->string, strlen(map->string), 0);
764     hash += hashlittle_safe((uint8_t *)&map->transforms, sizeof(map->transforms), 0);
765     hash %= ht->array_size;
766 
767     return hash;
768 }
769 
DetectBufferTypeCompareFunc(void * data1,uint16_t len1,void * data2,uint16_t len2)770 static char DetectBufferTypeCompareFunc(void *data1, uint16_t len1, void *data2,
771                                         uint16_t len2)
772 {
773     DetectBufferType *map1 = (DetectBufferType *)data1;
774     DetectBufferType *map2 = (DetectBufferType *)data2;
775 
776     int r = (strcmp(map1->string, map2->string) == 0);
777     r &= (memcmp((uint8_t *)&map1->transforms, (uint8_t *)&map2->transforms, sizeof(map2->transforms)) == 0);
778     return r;
779 }
780 
DetectBufferTypeFreeFunc(void * data)781 static void DetectBufferTypeFreeFunc(void *data)
782 {
783     DetectBufferType *map = (DetectBufferType *)data;
784 
785     if (map == NULL) {
786         return;
787     }
788 
789     /* Release transformation option memory, if any */
790     for (int i = 0; i < map->transforms.cnt; i++) {
791         if (map->transforms.transforms[i].options == NULL)
792             continue;
793         if (sigmatch_table[map->transforms.transforms[i].transform].Free == NULL) {
794             SCLogError(SC_ERR_UNIMPLEMENTED,
795                        "%s allocates transform option memory but has no free routine",
796                        sigmatch_table[map->transforms.transforms[i].transform].name);
797             continue;
798         }
799         sigmatch_table[map->transforms.transforms[i].transform].Free(NULL, map->transforms.transforms[i].options);
800     }
801 
802     SCFree(map);
803 }
804 
DetectBufferTypeInit(void)805 static int DetectBufferTypeInit(void)
806 {
807     BUG_ON(g_buffer_type_hash);
808     g_buffer_type_hash = HashListTableInit(256,
809             DetectBufferTypeHashFunc,
810             DetectBufferTypeCompareFunc,
811             DetectBufferTypeFreeFunc);
812     if (g_buffer_type_hash == NULL)
813         return -1;
814 
815     return 0;
816 }
817 #if 0
818 static void DetectBufferTypeFree(void)
819 {
820     if (g_buffer_type_hash == NULL)
821         return;
822 
823     HashListTableFree(g_buffer_type_hash);
824     g_buffer_type_hash = NULL;
825     return;
826 }
827 #endif
DetectBufferTypeAdd(const char * string)828 static int DetectBufferTypeAdd(const char *string)
829 {
830     DetectBufferType *map = SCCalloc(1, sizeof(*map));
831     if (map == NULL)
832         return -1;
833 
834     map->string = string;
835     map->id = g_buffer_type_id++;
836 
837     BUG_ON(HashListTableAdd(g_buffer_type_hash, (void *)map, 0) != 0);
838     SCLogDebug("buffer %s registered with id %d", map->string, map->id);
839     return map->id;
840 }
841 
DetectBufferTypeLookupByName(const char * string)842 static DetectBufferType *DetectBufferTypeLookupByName(const char *string)
843 {
844     DetectBufferType map = { (char *)string, NULL, 0, 0, 0, 0, false, NULL, NULL, no_transforms };
845 
846     DetectBufferType *res = HashListTableLookup(g_buffer_type_hash, &map, 0);
847     return res;
848 }
849 
DetectBufferTypeRegister(const char * name)850 int DetectBufferTypeRegister(const char *name)
851 {
852     BUG_ON(g_buffer_type_reg_closed);
853     if (g_buffer_type_hash == NULL)
854         DetectBufferTypeInit();
855 
856     DetectBufferType *exists = DetectBufferTypeLookupByName(name);
857     if (!exists) {
858         return DetectBufferTypeAdd(name);
859     } else {
860         return exists->id;
861     }
862 }
863 
DetectBufferTypeSupportsPacket(const char * name)864 void DetectBufferTypeSupportsPacket(const char *name)
865 {
866     BUG_ON(g_buffer_type_reg_closed);
867     DetectBufferTypeRegister(name);
868     DetectBufferType *exists = DetectBufferTypeLookupByName(name);
869     BUG_ON(!exists);
870     exists->packet = TRUE;
871     SCLogDebug("%p %s -- %d supports packet inspection", exists, name, exists->id);
872 }
873 
DetectBufferTypeSupportsMpm(const char * name)874 void DetectBufferTypeSupportsMpm(const char *name)
875 {
876     BUG_ON(g_buffer_type_reg_closed);
877     DetectBufferTypeRegister(name);
878     DetectBufferType *exists = DetectBufferTypeLookupByName(name);
879     BUG_ON(!exists);
880     exists->mpm = TRUE;
881     SCLogDebug("%p %s -- %d supports mpm", exists, name, exists->id);
882 }
883 
DetectBufferTypeSupportsTransformations(const char * name)884 void DetectBufferTypeSupportsTransformations(const char *name)
885 {
886     BUG_ON(g_buffer_type_reg_closed);
887     DetectBufferTypeRegister(name);
888     DetectBufferType *exists = DetectBufferTypeLookupByName(name);
889     BUG_ON(!exists);
890     exists->supports_transforms = true;
891     SCLogDebug("%p %s -- %d supports transformations", exists, name, exists->id);
892 }
893 
DetectBufferTypeGetByName(const char * name)894 int DetectBufferTypeGetByName(const char *name)
895 {
896     DetectBufferType *exists = DetectBufferTypeLookupByName(name);
897     if (!exists) {
898         return -1;
899     }
900     return exists->id;
901 }
902 
DetectBufferTypeGetNameById(const DetectEngineCtx * de_ctx,const int id)903 const char *DetectBufferTypeGetNameById(const DetectEngineCtx *de_ctx, const int id)
904 {
905     BUG_ON(id < 0 || (uint32_t)id >= de_ctx->buffer_type_map_elements);
906     BUG_ON(de_ctx->buffer_type_map == NULL);
907 
908     if (de_ctx->buffer_type_map[id] == NULL)
909         return NULL;
910 
911     return de_ctx->buffer_type_map[id]->string;
912 }
913 
DetectBufferTypeGetById(const DetectEngineCtx * de_ctx,const int id)914 static const DetectBufferType *DetectBufferTypeGetById(const DetectEngineCtx *de_ctx, const int id)
915 {
916     BUG_ON(id < 0 || (uint32_t)id >= de_ctx->buffer_type_map_elements);
917     BUG_ON(de_ctx->buffer_type_map == NULL);
918 
919     return de_ctx->buffer_type_map[id];
920 }
921 
DetectBufferTypeSetDescriptionByName(const char * name,const char * desc)922 void DetectBufferTypeSetDescriptionByName(const char *name, const char *desc)
923 {
924     DetectBufferType *exists = DetectBufferTypeLookupByName(name);
925     if (!exists) {
926         return;
927     }
928     exists->description = desc;
929 }
930 
DetectBufferTypeGetDescriptionById(const DetectEngineCtx * de_ctx,const int id)931 const char *DetectBufferTypeGetDescriptionById(const DetectEngineCtx *de_ctx, const int id)
932 {
933     const DetectBufferType *exists = DetectBufferTypeGetById(de_ctx, id);
934     if (!exists) {
935         return NULL;
936     }
937     return exists->description;
938 }
939 
DetectBufferTypeGetDescriptionByName(const char * name)940 const char *DetectBufferTypeGetDescriptionByName(const char *name)
941 {
942     const DetectBufferType *exists = DetectBufferTypeLookupByName(name);
943     if (!exists) {
944         return NULL;
945     }
946     return exists->description;
947 }
948 
DetectBufferTypeSupportsPacketGetById(const DetectEngineCtx * de_ctx,const int id)949 bool DetectBufferTypeSupportsPacketGetById(const DetectEngineCtx *de_ctx, const int id)
950 {
951     const DetectBufferType *map = DetectBufferTypeGetById(de_ctx, id);
952     if (map == NULL)
953         return FALSE;
954     SCLogDebug("map %p id %d packet? %d", map, id, map->packet);
955     return map->packet;
956 }
957 
DetectBufferTypeSupportsMpmGetById(const DetectEngineCtx * de_ctx,const int id)958 bool DetectBufferTypeSupportsMpmGetById(const DetectEngineCtx *de_ctx, const int id)
959 {
960     const DetectBufferType *map = DetectBufferTypeGetById(de_ctx, id);
961     if (map == NULL)
962         return FALSE;
963     SCLogDebug("map %p id %d mpm? %d", map, id, map->mpm);
964     return map->mpm;
965 }
966 
DetectBufferTypeRegisterSetupCallback(const char * name,void (* SetupCallback)(const DetectEngineCtx *,Signature *))967 void DetectBufferTypeRegisterSetupCallback(const char *name,
968         void (*SetupCallback)(const DetectEngineCtx *, Signature *))
969 {
970     BUG_ON(g_buffer_type_reg_closed);
971     DetectBufferTypeRegister(name);
972     DetectBufferType *exists = DetectBufferTypeLookupByName(name);
973     BUG_ON(!exists);
974     exists->SetupCallback = SetupCallback;
975 }
976 
DetectBufferRunSetupCallback(const DetectEngineCtx * de_ctx,const int id,Signature * s)977 void DetectBufferRunSetupCallback(const DetectEngineCtx *de_ctx,
978         const int id, Signature *s)
979 {
980     const DetectBufferType *map = DetectBufferTypeGetById(de_ctx, id);
981     if (map && map->SetupCallback) {
982         map->SetupCallback(de_ctx, s);
983     }
984 }
985 
DetectBufferTypeRegisterValidateCallback(const char * name,bool (* ValidateCallback)(const Signature *,const char ** sigerror))986 void DetectBufferTypeRegisterValidateCallback(const char *name,
987         bool (*ValidateCallback)(const Signature *, const char **sigerror))
988 {
989     BUG_ON(g_buffer_type_reg_closed);
990     DetectBufferTypeRegister(name);
991     DetectBufferType *exists = DetectBufferTypeLookupByName(name);
992     BUG_ON(!exists);
993     exists->ValidateCallback = ValidateCallback;
994 }
995 
DetectBufferRunValidateCallback(const DetectEngineCtx * de_ctx,const int id,const Signature * s,const char ** sigerror)996 bool DetectBufferRunValidateCallback(const DetectEngineCtx *de_ctx,
997         const int id, const Signature *s, const char **sigerror)
998 {
999     const DetectBufferType *map = DetectBufferTypeGetById(de_ctx, id);
1000     if (map && map->ValidateCallback) {
1001         return map->ValidateCallback(s, sigerror);
1002     }
1003     return TRUE;
1004 }
1005 
DetectBufferSetActiveList(Signature * s,const int list)1006 int DetectBufferSetActiveList(Signature *s, const int list)
1007 {
1008     BUG_ON(s->init_data == NULL);
1009 
1010     if (s->init_data->list && s->init_data->transforms.cnt) {
1011         return -1;
1012     }
1013     s->init_data->list = list;
1014     s->init_data->list_set = true;
1015 
1016     return 0;
1017 }
1018 
DetectBufferGetActiveList(DetectEngineCtx * de_ctx,Signature * s)1019 int DetectBufferGetActiveList(DetectEngineCtx *de_ctx, Signature *s)
1020 {
1021     BUG_ON(s->init_data == NULL);
1022 
1023     if (s->init_data->list && s->init_data->transforms.cnt) {
1024         if (s->init_data->list == DETECT_SM_LIST_NOTSET ||
1025             s->init_data->list < DETECT_SM_LIST_DYNAMIC_START) {
1026             SCLogError(SC_ERR_INVALID_SIGNATURE, "previous transforms not consumed "
1027                     "(list: %u, transform_cnt %u)", s->init_data->list,
1028                     s->init_data->transforms.cnt);
1029             SCReturnInt(-1);
1030         }
1031 
1032         SCLogDebug("buffer %d has transform(s) registered: %d",
1033                 s->init_data->list, s->init_data->transforms.cnt);
1034         int new_list = DetectBufferTypeGetByIdTransforms(de_ctx, s->init_data->list,
1035                 s->init_data->transforms.transforms, s->init_data->transforms.cnt);
1036         if (new_list == -1) {
1037             SCReturnInt(-1);
1038         }
1039         SCLogDebug("new_list %d", new_list);
1040         s->init_data->list = new_list;
1041         s->init_data->list_set = false;
1042         // reset transforms now that we've set up the list
1043         s->init_data->transforms.cnt = 0;
1044     }
1045 
1046     SCReturnInt(0);
1047 }
1048 
InspectionBufferClean(DetectEngineThreadCtx * det_ctx)1049 void InspectionBufferClean(DetectEngineThreadCtx *det_ctx)
1050 {
1051     /* single buffers */
1052     for (uint32_t i = 0; i < det_ctx->inspect.to_clear_idx; i++)
1053     {
1054         const uint32_t idx = det_ctx->inspect.to_clear_queue[i];
1055         InspectionBuffer *buffer = &det_ctx->inspect.buffers[idx];
1056         buffer->inspect = NULL;
1057     }
1058     det_ctx->inspect.to_clear_idx = 0;
1059 
1060     /* multi buffers */
1061     for (uint32_t i = 0; i < det_ctx->multi_inspect.to_clear_idx; i++)
1062     {
1063         const uint32_t idx = det_ctx->multi_inspect.to_clear_queue[i];
1064         InspectionBufferMultipleForList *mbuffer = &det_ctx->multi_inspect.buffers[idx];
1065         for (uint32_t x = 0; x <= mbuffer->max; x++) {
1066             InspectionBuffer *buffer = &mbuffer->inspection_buffers[x];
1067             buffer->inspect = NULL;
1068         }
1069         mbuffer->init = 0;
1070         mbuffer->max = 0;
1071     }
1072     det_ctx->multi_inspect.to_clear_idx = 0;
1073 }
1074 
InspectionBufferGet(DetectEngineThreadCtx * det_ctx,const int list_id)1075 InspectionBuffer *InspectionBufferGet(DetectEngineThreadCtx *det_ctx, const int list_id)
1076 {
1077     return &det_ctx->inspect.buffers[list_id];
1078 }
1079 
InspectionBufferGetMulti(DetectEngineThreadCtx * det_ctx,const int list_id)1080 static InspectionBufferMultipleForList *InspectionBufferGetMulti(
1081         DetectEngineThreadCtx *det_ctx, const int list_id)
1082 {
1083     InspectionBufferMultipleForList *buffer = &det_ctx->multi_inspect.buffers[list_id];
1084     if (!buffer->init) {
1085         det_ctx->multi_inspect.to_clear_queue[det_ctx->multi_inspect.to_clear_idx++] = list_id;
1086         buffer->init = 1;
1087     }
1088     return buffer;
1089 }
1090 
1091 /** \brief for a InspectionBufferMultipleForList get a InspectionBuffer
1092  *  \param fb the multiple buffer array
1093  *  \param local_id the index to get a buffer
1094  *  \param buffer the inspect buffer or NULL in case of error */
InspectionBufferMultipleForListGet(DetectEngineThreadCtx * det_ctx,const int list_id,const uint32_t local_id)1095 InspectionBuffer *InspectionBufferMultipleForListGet(
1096         DetectEngineThreadCtx *det_ctx, const int list_id, const uint32_t local_id)
1097 {
1098     if (unlikely(local_id >= 1024)) {
1099         DetectEngineSetEvent(det_ctx, DETECT_EVENT_TOO_MANY_BUFFERS);
1100         return NULL;
1101     }
1102 
1103     InspectionBufferMultipleForList *fb = InspectionBufferGetMulti(det_ctx, list_id);
1104 
1105     if (local_id >= fb->size) {
1106         uint32_t old_size = fb->size;
1107         uint32_t new_size = local_id + 1;
1108         uint32_t grow_by = new_size - old_size;
1109         SCLogDebug("size is %u, need %u, so growing by %u", old_size, new_size, grow_by);
1110 
1111         SCLogDebug("fb->inspection_buffers %p", fb->inspection_buffers);
1112         void *ptr = SCRealloc(fb->inspection_buffers, (local_id + 1) * sizeof(InspectionBuffer));
1113         if (ptr == NULL)
1114             return NULL;
1115 
1116         InspectionBuffer *to_zero = (InspectionBuffer *)ptr + old_size;
1117         SCLogDebug("ptr %p to_zero %p", ptr, to_zero);
1118         memset((uint8_t *)to_zero, 0, (grow_by * sizeof(InspectionBuffer)));
1119         fb->inspection_buffers = ptr;
1120         fb->size = new_size;
1121     }
1122 
1123     fb->max = MAX(fb->max, local_id);
1124     InspectionBuffer *buffer = &fb->inspection_buffers[local_id];
1125     SCLogDebug("using file_data buffer %p", buffer);
1126 #ifdef DEBUG_VALIDATION
1127     buffer->multi = true;
1128 #endif
1129     return buffer;
1130 }
1131 
InspectionBufferInit(InspectionBuffer * buffer,uint32_t initial_size)1132 void InspectionBufferInit(InspectionBuffer *buffer, uint32_t initial_size)
1133 {
1134     memset(buffer, 0, sizeof(*buffer));
1135     buffer->buf = SCCalloc(initial_size, sizeof(uint8_t));
1136     if (buffer->buf != NULL) {
1137         buffer->size = initial_size;
1138     }
1139 }
1140 
1141 /** \brief setup the buffer with our initial data */
InspectionBufferSetupMulti(InspectionBuffer * buffer,const DetectEngineTransforms * transforms,const uint8_t * data,const uint32_t data_len)1142 void InspectionBufferSetupMulti(InspectionBuffer *buffer, const DetectEngineTransforms *transforms,
1143         const uint8_t *data, const uint32_t data_len)
1144 {
1145     DEBUG_VALIDATE_BUG_ON(!buffer->multi);
1146     buffer->inspect = buffer->orig = data;
1147     buffer->inspect_len = buffer->orig_len = data_len;
1148     buffer->len = 0;
1149 
1150     InspectionBufferApplyTransforms(buffer, transforms);
1151 }
1152 
1153 /** \brief setup the buffer with our initial data */
InspectionBufferSetup(DetectEngineThreadCtx * det_ctx,const int list_id,InspectionBuffer * buffer,const uint8_t * data,const uint32_t data_len)1154 void InspectionBufferSetup(DetectEngineThreadCtx *det_ctx, const int list_id,
1155         InspectionBuffer *buffer, const uint8_t *data, const uint32_t data_len)
1156 {
1157 #ifdef DEBUG_VALIDATION
1158     DEBUG_VALIDATE_BUG_ON(buffer->multi);
1159     DEBUG_VALIDATE_BUG_ON(buffer != InspectionBufferGet(det_ctx, list_id));
1160 #endif
1161     if (buffer->inspect == NULL) {
1162 #ifdef UNITTESTS
1163         if (det_ctx && list_id != -1)
1164 #endif
1165             det_ctx->inspect.to_clear_queue[det_ctx->inspect.to_clear_idx++] = list_id;
1166     }
1167     buffer->inspect = buffer->orig = data;
1168     buffer->inspect_len = buffer->orig_len = data_len;
1169     buffer->len = 0;
1170 }
1171 
InspectionBufferFree(InspectionBuffer * buffer)1172 void InspectionBufferFree(InspectionBuffer *buffer)
1173 {
1174     if (buffer->buf != NULL) {
1175         SCFree(buffer->buf);
1176     }
1177     memset(buffer, 0, sizeof(*buffer));
1178 }
1179 
1180 /**
1181  * \brief make sure that the buffer has at least 'min_size' bytes
1182  * Expand the buffer if necessary
1183  */
InspectionBufferCheckAndExpand(InspectionBuffer * buffer,uint32_t min_size)1184 void InspectionBufferCheckAndExpand(InspectionBuffer *buffer, uint32_t min_size)
1185 {
1186     if (likely(buffer->size >= min_size))
1187         return;
1188 
1189     uint32_t new_size = (buffer->size == 0) ? 4096 : buffer->size;
1190     while (new_size < min_size) {
1191         new_size *= 2;
1192     }
1193 
1194     void *ptr = SCRealloc(buffer->buf, new_size);
1195     if (ptr != NULL) {
1196         buffer->buf = ptr;
1197         buffer->size = new_size;
1198     }
1199 }
1200 
InspectionBufferCopy(InspectionBuffer * buffer,uint8_t * buf,uint32_t buf_len)1201 void InspectionBufferCopy(InspectionBuffer *buffer, uint8_t *buf, uint32_t buf_len)
1202 {
1203     InspectionBufferCheckAndExpand(buffer, buf_len);
1204 
1205     if (buffer->size) {
1206         uint32_t copy_size = MIN(buf_len, buffer->size);
1207         memcpy(buffer->buf, buf, copy_size);
1208         buffer->inspect = buffer->buf;
1209         buffer->inspect_len = copy_size;
1210     }
1211 }
1212 
1213 /** \brief Check content byte array compatibility with transforms
1214  *
1215  *  The "content" array is presented to the transforms so that each
1216  *  transform may validate that it's compatible with the transform.
1217  *
1218  *  When a transform indicates the byte array is incompatible, none of the
1219  *  subsequent transforms, if any, are invoked. This means the first positive
1220  *  validation result terminates the loop.
1221  *
1222  *  \param de_ctx Detection engine context.
1223  *  \param sm_list The SM list id.
1224  *  \param content The byte array being validated
1225  *  \param namestr returns the name of the transform that is incompatible with
1226  *  content.
1227  *
1228  *  \retval true (false) If any of the transforms indicate the byte array is
1229  *  (is not) compatible.
1230  **/
DetectBufferTypeValidateTransform(DetectEngineCtx * de_ctx,int sm_list,const uint8_t * content,uint16_t content_len,const char ** namestr)1231 bool DetectBufferTypeValidateTransform(DetectEngineCtx *de_ctx, int sm_list,
1232         const uint8_t *content, uint16_t content_len, const char **namestr)
1233 {
1234     const DetectBufferType *dbt = DetectBufferTypeGetById(de_ctx, sm_list);
1235     BUG_ON(dbt == NULL);
1236 
1237     for (int i = 0; i < dbt->transforms.cnt; i++) {
1238         const TransformData *t = &dbt->transforms.transforms[i];
1239         if (!sigmatch_table[t->transform].TransformValidate)
1240             continue;
1241 
1242         if (sigmatch_table[t->transform].TransformValidate(content, content_len, t->options)) {
1243             continue;
1244         }
1245 
1246         if (namestr) {
1247             *namestr = sigmatch_table[t->transform].name;
1248         }
1249 
1250         return false;
1251     }
1252 
1253     return true;
1254 }
1255 
InspectionBufferApplyTransforms(InspectionBuffer * buffer,const DetectEngineTransforms * transforms)1256 void InspectionBufferApplyTransforms(InspectionBuffer *buffer,
1257         const DetectEngineTransforms *transforms)
1258 {
1259     if (transforms) {
1260         for (int i = 0; i < DETECT_TRANSFORMS_MAX; i++) {
1261             const int id = transforms->transforms[i].transform;
1262             if (id == 0)
1263                 break;
1264             BUG_ON(sigmatch_table[id].Transform == NULL);
1265             sigmatch_table[id].Transform(buffer, transforms->transforms[i].options);
1266             SCLogDebug("applied transform %s", sigmatch_table[id].name);
1267         }
1268     }
1269 }
1270 
DetectBufferTypeSetupDetectEngine(DetectEngineCtx * de_ctx)1271 static void DetectBufferTypeSetupDetectEngine(DetectEngineCtx *de_ctx)
1272 {
1273     const int size = g_buffer_type_id;
1274     BUG_ON(!(size > 0));
1275 
1276     de_ctx->buffer_type_map = SCCalloc(size, sizeof(DetectBufferType *));
1277     BUG_ON(!de_ctx->buffer_type_map);
1278     de_ctx->buffer_type_map_elements = size;
1279     SCLogDebug("de_ctx->buffer_type_map %p with %u members", de_ctx->buffer_type_map, size);
1280 
1281     SCLogDebug("DETECT_SM_LIST_DYNAMIC_START %u", DETECT_SM_LIST_DYNAMIC_START);
1282     HashListTableBucket *b = HashListTableGetListHead(g_buffer_type_hash);
1283     while (b) {
1284         DetectBufferType *map = HashListTableGetListData(b);
1285         de_ctx->buffer_type_map[map->id] = map;
1286         SCLogDebug("name %s id %d mpm %s packet %s -- %s. "
1287                 "Callbacks: Setup %p Validate %p", map->string, map->id,
1288                 map->mpm ? "true" : "false", map->packet ? "true" : "false",
1289                 map->description, map->SetupCallback, map->ValidateCallback);
1290         b = HashListTableGetListNext(b);
1291     }
1292 
1293     de_ctx->buffer_type_hash = HashListTableInit(256,
1294             DetectBufferTypeHashFunc,
1295             DetectBufferTypeCompareFunc,
1296             DetectBufferTypeFreeFunc);
1297     if (de_ctx->buffer_type_hash == NULL) {
1298         BUG_ON(1);
1299     }
1300     de_ctx->buffer_type_id = g_buffer_type_id;
1301 
1302     PrefilterInit(de_ctx);
1303     DetectMpmInitializeAppMpms(de_ctx);
1304     DetectAppLayerInspectEngineCopyListToDetectCtx(de_ctx);
1305     DetectMpmInitializePktMpms(de_ctx);
1306     DetectPktInspectEngineCopyListToDetectCtx(de_ctx);
1307 }
1308 
DetectBufferTypeFreeDetectEngine(DetectEngineCtx * de_ctx)1309 static void DetectBufferTypeFreeDetectEngine(DetectEngineCtx *de_ctx)
1310 {
1311     if (de_ctx) {
1312         if (de_ctx->buffer_type_map)
1313             SCFree(de_ctx->buffer_type_map);
1314         if (de_ctx->buffer_type_hash)
1315             HashListTableFree(de_ctx->buffer_type_hash);
1316 
1317         DetectEngineAppInspectionEngine *ilist = de_ctx->app_inspect_engines;
1318         while (ilist) {
1319             DetectEngineAppInspectionEngine *next = ilist->next;
1320             SCFree(ilist);
1321             ilist = next;
1322         }
1323         DetectBufferMpmRegistery *mlist = de_ctx->app_mpms_list;
1324         while (mlist) {
1325             DetectBufferMpmRegistery *next = mlist->next;
1326             SCFree(mlist);
1327             mlist = next;
1328         }
1329         DetectEnginePktInspectionEngine *plist = de_ctx->pkt_inspect_engines;
1330         while (plist) {
1331             DetectEnginePktInspectionEngine *next = plist->next;
1332             SCFree(plist);
1333             plist = next;
1334         }
1335         DetectBufferMpmRegistery *pmlist = de_ctx->pkt_mpms_list;
1336         while (pmlist) {
1337             DetectBufferMpmRegistery *next = pmlist->next;
1338             SCFree(pmlist);
1339             pmlist = next;
1340         }
1341         PrefilterDeinit(de_ctx);
1342     }
1343 }
1344 
DetectBufferTypeCloseRegistration(void)1345 void DetectBufferTypeCloseRegistration(void)
1346 {
1347     BUG_ON(g_buffer_type_hash == NULL);
1348 
1349     g_buffer_type_reg_closed = 1;
1350 }
1351 
DetectBufferTypeGetByIdTransforms(DetectEngineCtx * de_ctx,const int id,TransformData * transforms,int transform_cnt)1352 int DetectBufferTypeGetByIdTransforms(DetectEngineCtx *de_ctx, const int id,
1353         TransformData *transforms, int transform_cnt)
1354 {
1355     const DetectBufferType *base_map = DetectBufferTypeGetById(de_ctx, id);
1356     if (!base_map) {
1357         return -1;
1358     }
1359     if (!base_map->supports_transforms) {
1360         SCLogError(SC_ERR_INVALID_SIGNATURE, "buffer '%s' does not support transformations",
1361                 base_map->string);
1362         return -1;
1363     }
1364 
1365     SCLogDebug("base_map %s", base_map->string);
1366 
1367     DetectEngineTransforms t;
1368     memset(&t, 0, sizeof(t));
1369     for (int i = 0; i < transform_cnt; i++) {
1370         t.transforms[i] = transforms[i];
1371     }
1372     t.cnt = transform_cnt;
1373 
1374     DetectBufferType lookup_map = { (char *)base_map->string, NULL, 0, 0, 0, 0, false, NULL, NULL, t };
1375     DetectBufferType *res = HashListTableLookup(de_ctx->buffer_type_hash, &lookup_map, 0);
1376 
1377     SCLogDebug("res %p", res);
1378     if (res != NULL) {
1379         return res->id;
1380     }
1381 
1382     DetectBufferType *map = SCCalloc(1, sizeof(*map));
1383     if (map == NULL)
1384         return -1;
1385 
1386     map->string = base_map->string;
1387     map->id = de_ctx->buffer_type_id++;
1388     map->parent_id = base_map->id;
1389     map->transforms = t;
1390     map->mpm = base_map->mpm;
1391     map->packet = base_map->packet;
1392     map->SetupCallback = base_map->SetupCallback;
1393     map->ValidateCallback = base_map->ValidateCallback;
1394     if (map->packet) {
1395         DetectPktMpmRegisterByParentId(de_ctx,
1396                 map->id, map->parent_id, &map->transforms);
1397     } else {
1398         DetectAppLayerMpmRegisterByParentId(de_ctx,
1399                 map->id, map->parent_id, &map->transforms);
1400     }
1401 
1402     BUG_ON(HashListTableAdd(de_ctx->buffer_type_hash, (void *)map, 0) != 0);
1403     SCLogDebug("buffer %s registered with id %d, parent %d", map->string, map->id, map->parent_id);
1404 
1405     if (map->id >= 0 && (uint32_t)map->id >= de_ctx->buffer_type_map_elements) {
1406         void *ptr = SCRealloc(de_ctx->buffer_type_map, (map->id + 1) * sizeof(DetectBufferType *));
1407         BUG_ON(ptr == NULL);
1408         SCLogDebug("de_ctx->buffer_type_map resized to %u (was %u)", (map->id + 1), de_ctx->buffer_type_map_elements);
1409         de_ctx->buffer_type_map = ptr;
1410         de_ctx->buffer_type_map[map->id] = map;
1411         de_ctx->buffer_type_map_elements = map->id + 1;
1412 
1413         if (map->packet) {
1414             DetectPktInspectEngineCopy(de_ctx, map->parent_id, map->id,
1415                     &map->transforms);
1416         } else {
1417             DetectAppLayerInspectEngineCopy(de_ctx, map->parent_id, map->id,
1418                     &map->transforms);
1419         }
1420     }
1421     return map->id;
1422 }
1423 
1424 /* returns false if no match, true if match */
DetectEngineInspectRulePacketMatches(DetectEngineThreadCtx * det_ctx,const DetectEnginePktInspectionEngine * engine,const Signature * s,Packet * p,uint8_t * _alert_flags)1425 static int DetectEngineInspectRulePacketMatches(
1426     DetectEngineThreadCtx *det_ctx,
1427     const DetectEnginePktInspectionEngine *engine,
1428     const Signature *s,
1429     Packet *p, uint8_t *_alert_flags)
1430 {
1431     SCEnter();
1432 
1433     /* run the packet match functions */
1434     KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_MATCH);
1435     const SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_MATCH];
1436 
1437     SCLogDebug("running match functions, sm %p", smd);
1438     while (1) {
1439         KEYWORD_PROFILING_START;
1440         if (sigmatch_table[smd->type].Match(det_ctx, p, s, smd->ctx) <= 0) {
1441             KEYWORD_PROFILING_END(det_ctx, smd->type, 0);
1442             SCLogDebug("no match");
1443             return false;
1444         }
1445         KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
1446         if (smd->is_last) {
1447             SCLogDebug("match and is_last");
1448             break;
1449         }
1450         smd++;
1451     }
1452     return true;
1453 }
1454 
DetectEngineInspectRulePayloadMatches(DetectEngineThreadCtx * det_ctx,const DetectEnginePktInspectionEngine * engine,const Signature * s,Packet * p,uint8_t * alert_flags)1455 static int DetectEngineInspectRulePayloadMatches(
1456      DetectEngineThreadCtx *det_ctx,
1457      const DetectEnginePktInspectionEngine *engine,
1458      const Signature *s, Packet *p, uint8_t *alert_flags)
1459 {
1460     SCEnter();
1461 
1462     DetectEngineCtx *de_ctx = det_ctx->de_ctx;
1463 
1464     KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_PMATCH);
1465     /* if we have stream msgs, inspect against those first,
1466      * but not for a "dsize" signature */
1467     if (s->flags & SIG_FLAG_REQUIRE_STREAM) {
1468         int pmatch = 0;
1469         if (p->flags & PKT_DETECT_HAS_STREAMDATA) {
1470             pmatch = DetectEngineInspectStreamPayload(de_ctx, det_ctx, s, p->flow, p);
1471             if (pmatch) {
1472                 det_ctx->flags |= DETECT_ENGINE_THREAD_CTX_STREAM_CONTENT_MATCH;
1473                 *alert_flags |= PACKET_ALERT_FLAG_STREAM_MATCH;
1474             }
1475         }
1476         /* no match? then inspect packet payload */
1477         if (pmatch == 0) {
1478             SCLogDebug("no match in stream, fall back to packet payload");
1479 
1480             /* skip if we don't have to inspect the packet and segment was
1481              * added to stream */
1482             if (!(s->flags & SIG_FLAG_REQUIRE_PACKET) && (p->flags & PKT_STREAM_ADD)) {
1483                 return false;
1484             }
1485             if (DetectEngineInspectPacketPayload(de_ctx, det_ctx, s, p->flow, p) != 1) {
1486                 return false;
1487             }
1488         }
1489     } else {
1490         if (DetectEngineInspectPacketPayload(de_ctx, det_ctx, s, p->flow, p) != 1) {
1491             return false;
1492         }
1493     }
1494     return true;
1495 }
1496 
DetectEnginePktInspectionRun(ThreadVars * tv,DetectEngineThreadCtx * det_ctx,const Signature * s,Flow * f,Packet * p,uint8_t * alert_flags)1497 bool DetectEnginePktInspectionRun(ThreadVars *tv,
1498         DetectEngineThreadCtx *det_ctx, const Signature *s,
1499         Flow *f, Packet *p,
1500         uint8_t *alert_flags)
1501 {
1502     SCEnter();
1503 
1504     for (DetectEnginePktInspectionEngine *e = s->pkt_inspect; e != NULL; e = e->next) {
1505         if (e->v1.Callback(det_ctx, e, s, p, alert_flags) == false) {
1506             SCLogDebug("sid %u: e %p Callback returned false", s->id, e);
1507             return false;
1508         }
1509         SCLogDebug("sid %u: e %p Callback returned true", s->id, e);
1510     }
1511 
1512     SCLogDebug("sid %u: returning true", s->id);
1513     return true;
1514 }
1515 
1516 /**
1517  * \param data pointer to SigMatchData. Allowed to be NULL.
1518  */
DetectEnginePktInspectionAppend(Signature * s,InspectionBufferPktInspectFunc Callback,SigMatchData * data,const int list_id)1519 static int DetectEnginePktInspectionAppend(Signature *s, InspectionBufferPktInspectFunc Callback,
1520         SigMatchData *data, const int list_id)
1521 {
1522     DetectEnginePktInspectionEngine *e = SCCalloc(1, sizeof(*e));
1523     if (e == NULL)
1524         return -1;
1525 
1526     e->sm_list = list_id;
1527     e->sm_list_base = list_id;
1528     e->v1.Callback = Callback;
1529     e->smd = data;
1530 
1531     if (s->pkt_inspect == NULL) {
1532         s->pkt_inspect = e;
1533     } else {
1534         DetectEnginePktInspectionEngine *a = s->pkt_inspect;
1535         while (a->next != NULL) {
1536             a = a->next;
1537         }
1538         a->next = e;
1539     }
1540     return 0;
1541 }
1542 
DetectEnginePktInspectionSetup(Signature * s)1543 int DetectEnginePktInspectionSetup(Signature *s)
1544 {
1545     /* only handle PMATCH here if we're not an app inspect rule */
1546     if (s->sm_arrays[DETECT_SM_LIST_PMATCH] && (s->init_data->init_flags & SIG_FLAG_INIT_STATE_MATCH) == 0) {
1547         if (DetectEnginePktInspectionAppend(
1548                     s, DetectEngineInspectRulePayloadMatches, NULL, DETECT_SM_LIST_PMATCH) < 0)
1549             return -1;
1550         SCLogDebug("sid %u: DetectEngineInspectRulePayloadMatches appended", s->id);
1551     }
1552 
1553     if (s->sm_arrays[DETECT_SM_LIST_MATCH]) {
1554         if (DetectEnginePktInspectionAppend(
1555                     s, DetectEngineInspectRulePacketMatches, NULL, DETECT_SM_LIST_MATCH) < 0)
1556             return -1;
1557         SCLogDebug("sid %u: DetectEngineInspectRulePacketMatches appended", s->id);
1558     }
1559 
1560     return 0;
1561 }
1562 
1563 /* code to control the main thread to do a reload */
1564 
1565 enum DetectEngineSyncState {
1566     IDLE,   /**< ready to start a reload */
1567     RELOAD, /**< command main thread to do the reload */
1568 };
1569 
1570 
1571 typedef struct DetectEngineSyncer_ {
1572     SCMutex m;
1573     enum DetectEngineSyncState state;
1574 } DetectEngineSyncer;
1575 
1576 static DetectEngineSyncer detect_sync = { SCMUTEX_INITIALIZER, IDLE };
1577 
1578 /* tell main to start reloading */
DetectEngineReloadStart(void)1579 int DetectEngineReloadStart(void)
1580 {
1581     int r = 0;
1582     SCMutexLock(&detect_sync.m);
1583     if (detect_sync.state == IDLE) {
1584         detect_sync.state = RELOAD;
1585     } else {
1586         r = -1;
1587     }
1588     SCMutexUnlock(&detect_sync.m);
1589     return r;
1590 }
1591 
1592 /* main thread checks this to see if it should start */
DetectEngineReloadIsStart(void)1593 int DetectEngineReloadIsStart(void)
1594 {
1595     int r = 0;
1596     SCMutexLock(&detect_sync.m);
1597     if (detect_sync.state == RELOAD) {
1598         r = 1;
1599     }
1600     SCMutexUnlock(&detect_sync.m);
1601     return r;
1602 }
1603 
1604 /* main thread sets done when it's done */
DetectEngineReloadSetIdle(void)1605 void DetectEngineReloadSetIdle(void)
1606 {
1607     SCMutexLock(&detect_sync.m);
1608     detect_sync.state = IDLE;
1609     SCMutexUnlock(&detect_sync.m);
1610 }
1611 
1612 /* caller loops this until it returns 1 */
DetectEngineReloadIsIdle(void)1613 int DetectEngineReloadIsIdle(void)
1614 {
1615     int r = 0;
1616     SCMutexLock(&detect_sync.m);
1617     if (detect_sync.state == IDLE) {
1618         r = 1;
1619     }
1620     SCMutexUnlock(&detect_sync.m);
1621     return r;
1622 }
1623 
1624 /** \brief Do the content inspection & validation for a signature
1625  *
1626  *  \param de_ctx Detection engine context
1627  *  \param det_ctx Detection engine thread context
1628  *  \param s Signature to inspect
1629  *  \param sm SigMatch to inspect
1630  *  \param f Flow
1631  *  \param flags app layer flags
1632  *  \param state App layer state
1633  *
1634  *  \retval 0 no match
1635  *  \retval 1 match
1636  */
DetectEngineInspectGenericList(ThreadVars * tv,const DetectEngineCtx * de_ctx,DetectEngineThreadCtx * det_ctx,const Signature * s,const SigMatchData * smd,Flow * f,const uint8_t flags,void * alstate,void * txv,uint64_t tx_id)1637 int DetectEngineInspectGenericList(ThreadVars *tv,
1638                                    const DetectEngineCtx *de_ctx,
1639                                    DetectEngineThreadCtx *det_ctx,
1640                                    const Signature *s, const SigMatchData *smd,
1641                                    Flow *f, const uint8_t flags,
1642                                    void *alstate, void *txv, uint64_t tx_id)
1643 {
1644     SCLogDebug("running match functions, sm %p", smd);
1645     if (smd != NULL) {
1646         while (1) {
1647             int match = 0;
1648             KEYWORD_PROFILING_START;
1649             match = sigmatch_table[smd->type].
1650                 AppLayerTxMatch(det_ctx, f, flags, alstate, txv, s, smd->ctx);
1651             KEYWORD_PROFILING_END(det_ctx, smd->type, (match == 1));
1652             if (match == 0)
1653                 return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
1654             if (match == 2) {
1655                 return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
1656             }
1657 
1658             if (smd->is_last)
1659                 break;
1660             smd++;
1661         }
1662     }
1663 
1664     return DETECT_ENGINE_INSPECT_SIG_MATCH;
1665 }
1666 
1667 
1668 /**
1669  * \brief Do the content inspection & validation for a signature
1670  *
1671  * \param de_ctx Detection engine context
1672  * \param det_ctx Detection engine thread context
1673  * \param s Signature to inspect
1674  * \param f Flow
1675  * \param flags app layer flags
1676  * \param state App layer state
1677  *
1678  * \retval 0 no match.
1679  * \retval 1 match.
1680  * \retval 2 Sig can't match.
1681  */
DetectEngineInspectBufferGeneric(DetectEngineCtx * de_ctx,DetectEngineThreadCtx * det_ctx,const DetectEngineAppInspectionEngine * engine,const Signature * s,Flow * f,uint8_t flags,void * alstate,void * txv,uint64_t tx_id)1682 int DetectEngineInspectBufferGeneric(
1683         DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
1684         const DetectEngineAppInspectionEngine *engine,
1685         const Signature *s,
1686         Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
1687 {
1688     const int list_id = engine->sm_list;
1689     SCLogDebug("running inspect on %d", list_id);
1690 
1691     const bool eof = (AppLayerParserGetStateProgress(f->proto, f->alproto, txv, flags) > engine->progress);
1692 
1693     SCLogDebug("list %d mpm? %s transforms %p",
1694             engine->sm_list, engine->mpm ? "true" : "false", engine->v2.transforms);
1695 
1696     /* if prefilter didn't already run, we need to consider transformations */
1697     const DetectEngineTransforms *transforms = NULL;
1698     if (!engine->mpm) {
1699         transforms = engine->v2.transforms;
1700     }
1701 
1702     const InspectionBuffer *buffer = engine->v2.GetData(det_ctx, transforms,
1703             f, flags, txv, list_id);
1704     if (unlikely(buffer == NULL)) {
1705         return eof ? DETECT_ENGINE_INSPECT_SIG_CANT_MATCH :
1706                      DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
1707     }
1708 
1709     const uint32_t data_len = buffer->inspect_len;
1710     const uint8_t *data = buffer->inspect;
1711     const uint64_t offset = buffer->inspect_offset;
1712 
1713     uint8_t ci_flags = eof ? DETECT_CI_FLAGS_END : 0;
1714     ci_flags |= (offset == 0 ? DETECT_CI_FLAGS_START : 0);
1715     ci_flags |= buffer->flags;
1716 
1717     det_ctx->discontinue_matching = 0;
1718     det_ctx->buffer_offset = 0;
1719     det_ctx->inspection_recursion_counter = 0;
1720 
1721     /* Inspect all the uricontents fetched on each
1722      * transaction at the app layer */
1723     int r = DetectEngineContentInspection(de_ctx, det_ctx,
1724                                           s, engine->smd,
1725                                           NULL, f,
1726                                           (uint8_t *)data, data_len, offset, ci_flags,
1727                                           DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
1728     if (r == 1) {
1729         return DETECT_ENGINE_INSPECT_SIG_MATCH;
1730     } else {
1731         return eof ? DETECT_ENGINE_INSPECT_SIG_CANT_MATCH :
1732                      DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
1733     }
1734 }
1735 
1736 /**
1737  * \brief Do the content inspection & validation for a signature
1738  *
1739  * \param de_ctx Detection engine context
1740  * \param det_ctx Detection engine thread context
1741  * \param s Signature to inspect
1742  * \param p Packet
1743  *
1744  * \retval 0 no match.
1745  * \retval 1 match.
1746  */
DetectEngineInspectPktBufferGeneric(DetectEngineThreadCtx * det_ctx,const DetectEnginePktInspectionEngine * engine,const Signature * s,Packet * p,uint8_t * _alert_flags)1747 int DetectEngineInspectPktBufferGeneric(
1748         DetectEngineThreadCtx *det_ctx,
1749         const DetectEnginePktInspectionEngine *engine,
1750         const Signature *s, Packet *p, uint8_t *_alert_flags)
1751 {
1752     const int list_id = engine->sm_list;
1753     SCLogDebug("running inspect on %d", list_id);
1754 
1755     SCLogDebug("list %d transforms %p",
1756             engine->sm_list, engine->v1.transforms);
1757 
1758     /* if prefilter didn't already run, we need to consider transformations */
1759     const DetectEngineTransforms *transforms = NULL;
1760     if (!engine->mpm) {
1761         transforms = engine->v1.transforms;
1762     }
1763 
1764     const InspectionBuffer *buffer = engine->v1.GetData(det_ctx, transforms, p,
1765             list_id);
1766     if (unlikely(buffer == NULL)) {
1767         return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
1768     }
1769 
1770     const uint32_t data_len = buffer->inspect_len;
1771     const uint8_t *data = buffer->inspect;
1772     const uint64_t offset = 0;
1773 
1774     uint8_t ci_flags = DETECT_CI_FLAGS_START|DETECT_CI_FLAGS_END;
1775     ci_flags |= buffer->flags;
1776 
1777     det_ctx->discontinue_matching = 0;
1778     det_ctx->buffer_offset = 0;
1779     det_ctx->inspection_recursion_counter = 0;
1780 
1781     /* Inspect all the uricontents fetched on each
1782      * transaction at the app layer */
1783     int r = DetectEngineContentInspection(det_ctx->de_ctx, det_ctx,
1784                                           s, engine->smd,
1785                                           p, p->flow,
1786                                           (uint8_t *)data, data_len, offset, ci_flags,
1787                                           DETECT_ENGINE_CONTENT_INSPECTION_MODE_HEADER);
1788     if (r == 1) {
1789         return DETECT_ENGINE_INSPECT_SIG_MATCH;
1790     } else {
1791         return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
1792     }
1793 }
1794 
1795 
1796 /* nudge capture loops to wake up */
BreakCapture(void)1797 static void BreakCapture(void)
1798 {
1799     SCMutexLock(&tv_root_lock);
1800     for (ThreadVars *tv = tv_root[TVT_PPT]; tv != NULL; tv = tv->next) {
1801         if ((tv->tmm_flags & TM_FLAG_RECEIVE_TM) == 0) {
1802             continue;
1803         }
1804         /* find the correct slot */
1805         for (TmSlot *s = tv->tm_slots; s != NULL; s = s->slot_next) {
1806             if (suricata_ctl_flags != 0) {
1807                 SCMutexUnlock(&tv_root_lock);
1808                 return;
1809             }
1810 
1811             TmModule *tm = TmModuleGetById(s->tm_id);
1812             if (!(tm->flags & TM_FLAG_RECEIVE_TM)) {
1813                 continue;
1814             }
1815 
1816             /* signal capture method that we need a packet. */
1817             TmThreadsSetFlag(tv, THV_CAPTURE_INJECT_PKT);
1818             /* if the method supports it, BreakLoop. Otherwise we rely on
1819              * the capture method's recv timeout */
1820             if (tm->PktAcqLoop && tm->PktAcqBreakLoop) {
1821                 tm->PktAcqBreakLoop(tv, SC_ATOMIC_GET(s->slot_data));
1822             }
1823             break;
1824         }
1825     }
1826     SCMutexUnlock(&tv_root_lock);
1827 }
1828 
1829 /** \internal
1830  *  \brief inject a pseudo packet into each detect thread that doesn't use the
1831  *         new det_ctx yet
1832  */
InjectPackets(ThreadVars ** detect_tvs,DetectEngineThreadCtx ** new_det_ctx,int no_of_detect_tvs)1833 static void InjectPackets(ThreadVars **detect_tvs,
1834                           DetectEngineThreadCtx **new_det_ctx,
1835                           int no_of_detect_tvs)
1836 {
1837     /* inject a fake packet if the detect thread isn't using the new ctx yet,
1838      * this speeds up the process */
1839     for (int i = 0; i < no_of_detect_tvs; i++) {
1840         if (SC_ATOMIC_GET(new_det_ctx[i]->so_far_used_by_detect) != 1) {
1841             if (detect_tvs[i]->inq != NULL) {
1842                 Packet *p = PacketGetFromAlloc();
1843                 if (p != NULL) {
1844                     p->flags |= PKT_PSEUDO_STREAM_END;
1845                     PKT_SET_SRC(p, PKT_SRC_DETECT_RELOAD_FLUSH);
1846                     PacketQueue *q = detect_tvs[i]->inq->pq;
1847                     SCMutexLock(&q->mutex_q);
1848                     PacketEnqueue(q, p);
1849                     SCCondSignal(&q->cond_q);
1850                     SCMutexUnlock(&q->mutex_q);
1851                 }
1852             }
1853         }
1854     }
1855 }
1856 
1857 /** \internal
1858  *  \brief Update detect threads with new detect engine
1859  *
1860  *  Atomically update each detect thread with a new thread context
1861  *  that is associated to the new detection engine(s).
1862  *
1863  *  If called in unix socket mode, it's possible that we don't have
1864  *  detect threads yet.
1865  *
1866  *  \retval -1 error
1867  *  \retval 0 no detection threads
1868  *  \retval 1 successful reload
1869  */
DetectEngineReloadThreads(DetectEngineCtx * new_de_ctx)1870 static int DetectEngineReloadThreads(DetectEngineCtx *new_de_ctx)
1871 {
1872     SCEnter();
1873     uint32_t i = 0;
1874 
1875     /* count detect threads in use */
1876     uint32_t no_of_detect_tvs = TmThreadCountThreadsByTmmFlags(TM_FLAG_DETECT_TM);
1877     /* can be zero in unix socket mode */
1878     if (no_of_detect_tvs == 0) {
1879         return 0;
1880     }
1881 
1882     /* prepare swap structures */
1883     DetectEngineThreadCtx *old_det_ctx[no_of_detect_tvs];
1884     DetectEngineThreadCtx *new_det_ctx[no_of_detect_tvs];
1885     ThreadVars *detect_tvs[no_of_detect_tvs];
1886     memset(old_det_ctx, 0x00, (no_of_detect_tvs * sizeof(DetectEngineThreadCtx *)));
1887     memset(new_det_ctx, 0x00, (no_of_detect_tvs * sizeof(DetectEngineThreadCtx *)));
1888     memset(detect_tvs, 0x00, (no_of_detect_tvs * sizeof(ThreadVars *)));
1889 
1890     /* start the process of swapping detect threads ctxs */
1891 
1892     /* get reference to tv's and setup new_det_ctx array */
1893     SCMutexLock(&tv_root_lock);
1894     for (ThreadVars *tv = tv_root[TVT_PPT]; tv != NULL; tv = tv->next) {
1895         if ((tv->tmm_flags & TM_FLAG_DETECT_TM) == 0) {
1896             continue;
1897         }
1898         for (TmSlot *s = tv->tm_slots; s != NULL; s = s->slot_next) {
1899             TmModule *tm = TmModuleGetById(s->tm_id);
1900             if (!(tm->flags & TM_FLAG_DETECT_TM)) {
1901                 continue;
1902             }
1903 
1904             if (suricata_ctl_flags != 0) {
1905                 SCMutexUnlock(&tv_root_lock);
1906                 goto error;
1907             }
1908 
1909             old_det_ctx[i] = FlowWorkerGetDetectCtxPtr(SC_ATOMIC_GET(s->slot_data));
1910             detect_tvs[i] = tv;
1911 
1912             new_det_ctx[i] = DetectEngineThreadCtxInitForReload(tv, new_de_ctx, 1);
1913             if (new_det_ctx[i] == NULL) {
1914                 SCLogError(SC_ERR_LIVE_RULE_SWAP, "Detect engine thread init "
1915                            "failure in live rule swap.  Let's get out of here");
1916                 SCMutexUnlock(&tv_root_lock);
1917                 goto error;
1918             }
1919             SCLogDebug("live rule swap created new det_ctx - %p and de_ctx "
1920                        "- %p\n", new_det_ctx[i], new_de_ctx);
1921             i++;
1922             break;
1923         }
1924     }
1925     BUG_ON(i != no_of_detect_tvs);
1926 
1927     /* atomically replace the det_ctx data */
1928     i = 0;
1929     for (ThreadVars *tv = tv_root[TVT_PPT]; tv != NULL; tv = tv->next) {
1930         if ((tv->tmm_flags & TM_FLAG_DETECT_TM) == 0) {
1931             continue;
1932         }
1933         for (TmSlot *s = tv->tm_slots; s != NULL; s = s->slot_next) {
1934             TmModule *tm = TmModuleGetById(s->tm_id);
1935             if (!(tm->flags & TM_FLAG_DETECT_TM)) {
1936                 continue;
1937             }
1938             SCLogDebug("swapping new det_ctx - %p with older one - %p",
1939                        new_det_ctx[i], SC_ATOMIC_GET(s->slot_data));
1940             FlowWorkerReplaceDetectCtx(SC_ATOMIC_GET(s->slot_data), new_det_ctx[i++]);
1941             break;
1942         }
1943     }
1944     SCMutexUnlock(&tv_root_lock);
1945 
1946     /* threads now all have new data, however they may not have started using
1947      * it and may still use the old data */
1948 
1949     SCLogDebug("Live rule swap has swapped %d old det_ctx's with new ones, "
1950                "along with the new de_ctx", no_of_detect_tvs);
1951 
1952     InjectPackets(detect_tvs, new_det_ctx, no_of_detect_tvs);
1953 
1954     for (i = 0; i < no_of_detect_tvs; i++) {
1955         int break_out = 0;
1956         usleep(1000);
1957         while (SC_ATOMIC_GET(new_det_ctx[i]->so_far_used_by_detect) != 1) {
1958             if (suricata_ctl_flags != 0) {
1959                 break_out = 1;
1960                 break;
1961             }
1962 
1963             BreakCapture();
1964             usleep(1000);
1965         }
1966         if (break_out)
1967             break;
1968         SCLogDebug("new_det_ctx - %p used by detect engine", new_det_ctx[i]);
1969     }
1970 
1971     /* this is to make sure that if someone initiated shutdown during a live
1972      * rule swap, the live rule swap won't clean up the old det_ctx and
1973      * de_ctx, till all detect threads have stopped working and sitting
1974      * silently after setting RUNNING_DONE flag and while waiting for
1975      * THV_DEINIT flag */
1976     if (i != no_of_detect_tvs) { // not all threads we swapped
1977         for (ThreadVars *tv = tv_root[TVT_PPT]; tv != NULL; tv = tv->next) {
1978             if ((tv->tmm_flags & TM_FLAG_DETECT_TM) == 0) {
1979                 continue;
1980             }
1981 
1982             while (!TmThreadsCheckFlag(tv, THV_RUNNING_DONE)) {
1983                 usleep(100);
1984             }
1985         }
1986     }
1987 
1988     /* free all the ctxs */
1989     for (i = 0; i < no_of_detect_tvs; i++) {
1990         SCLogDebug("Freeing old_det_ctx - %p used by detect",
1991                    old_det_ctx[i]);
1992         DetectEngineThreadCtxDeinit(NULL, old_det_ctx[i]);
1993     }
1994 
1995     SRepReloadComplete();
1996 
1997     return 1;
1998 
1999  error:
2000     for (i = 0; i < no_of_detect_tvs; i++) {
2001         if (new_det_ctx[i] != NULL)
2002             DetectEngineThreadCtxDeinit(NULL, new_det_ctx[i]);
2003     }
2004     return -1;
2005 }
2006 
DetectEngineCtxInitReal(enum DetectEngineType type,const char * prefix)2007 static DetectEngineCtx *DetectEngineCtxInitReal(enum DetectEngineType type, const char *prefix)
2008 {
2009     DetectEngineCtx *de_ctx = SCMalloc(sizeof(DetectEngineCtx));
2010     if (unlikely(de_ctx == NULL))
2011         goto error;
2012 
2013     memset(de_ctx,0,sizeof(DetectEngineCtx));
2014     memset(&de_ctx->sig_stat, 0, sizeof(SigFileLoaderStat));
2015     TAILQ_INIT(&de_ctx->sig_stat.failed_sigs);
2016     de_ctx->sigerror = NULL;
2017     de_ctx->type = type;
2018 
2019     if (type == DETECT_ENGINE_TYPE_DD_STUB || type == DETECT_ENGINE_TYPE_MT_STUB) {
2020         de_ctx->version = DetectEngineGetVersion();
2021         SCLogDebug("stub %u with version %u", type, de_ctx->version);
2022         return de_ctx;
2023     }
2024 
2025     if (prefix != NULL) {
2026         strlcpy(de_ctx->config_prefix, prefix, sizeof(de_ctx->config_prefix));
2027     }
2028 
2029     if (ConfGetBool("engine.init-failure-fatal", (int *)&(de_ctx->failure_fatal)) != 1) {
2030         SCLogDebug("ConfGetBool could not load the value.");
2031     }
2032 
2033     de_ctx->mpm_matcher = PatternMatchDefaultMatcher();
2034     de_ctx->spm_matcher = SinglePatternMatchDefaultMatcher();
2035     SCLogConfig("pattern matchers: MPM: %s, SPM: %s",
2036         mpm_table[de_ctx->mpm_matcher].name,
2037         spm_table[de_ctx->spm_matcher].name);
2038 
2039     de_ctx->spm_global_thread_ctx = SpmInitGlobalThreadCtx(de_ctx->spm_matcher);
2040     if (de_ctx->spm_global_thread_ctx == NULL) {
2041         SCLogDebug("Unable to alloc SpmGlobalThreadCtx.");
2042         goto error;
2043     }
2044 
2045     if (DetectEngineCtxLoadConf(de_ctx) == -1) {
2046         goto error;
2047     }
2048 
2049     SigGroupHeadHashInit(de_ctx);
2050     MpmStoreInit(de_ctx);
2051     ThresholdHashInit(de_ctx);
2052     DetectParseDupSigHashInit(de_ctx);
2053     DetectAddressMapInit(de_ctx);
2054     DetectMetadataHashInit(de_ctx);
2055     DetectBufferTypeSetupDetectEngine(de_ctx);
2056 
2057     /* init iprep... ignore errors for now */
2058     (void)SRepInit(de_ctx);
2059 
2060     SCClassConfLoadClassficationConfigFile(de_ctx, NULL);
2061     SCRConfLoadReferenceConfigFile(de_ctx, NULL);
2062 
2063     if (ActionInitConfig() < 0) {
2064         goto error;
2065     }
2066 
2067     de_ctx->version = DetectEngineGetVersion();
2068     VarNameStoreSetupStaging(de_ctx->version);
2069     SCLogDebug("dectx with version %u", de_ctx->version);
2070     return de_ctx;
2071 error:
2072     if (de_ctx != NULL) {
2073         DetectEngineCtxFree(de_ctx);
2074     }
2075     return NULL;
2076 
2077 }
2078 
DetectEngineCtxInitStubForMT(void)2079 DetectEngineCtx *DetectEngineCtxInitStubForMT(void)
2080 {
2081     return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_MT_STUB, NULL);
2082 }
2083 
DetectEngineCtxInitStubForDD(void)2084 DetectEngineCtx *DetectEngineCtxInitStubForDD(void)
2085 {
2086     return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_DD_STUB, NULL);
2087 }
2088 
DetectEngineCtxInit(void)2089 DetectEngineCtx *DetectEngineCtxInit(void)
2090 {
2091     return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_NORMAL, NULL);
2092 }
2093 
DetectEngineCtxInitWithPrefix(const char * prefix)2094 DetectEngineCtx *DetectEngineCtxInitWithPrefix(const char *prefix)
2095 {
2096     if (prefix == NULL || strlen(prefix) == 0)
2097         return DetectEngineCtxInit();
2098     else
2099         return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_NORMAL, prefix);
2100 }
2101 
DetectEngineCtxFreeThreadKeywordData(DetectEngineCtx * de_ctx)2102 static void DetectEngineCtxFreeThreadKeywordData(DetectEngineCtx *de_ctx)
2103 {
2104     DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list;
2105     while (item) {
2106         DetectEngineThreadKeywordCtxItem *next = item->next;
2107         SCFree(item);
2108         item = next;
2109     }
2110     de_ctx->keyword_list = NULL;
2111 }
2112 
DetectEngineCtxFreeFailedSigs(DetectEngineCtx * de_ctx)2113 static void DetectEngineCtxFreeFailedSigs(DetectEngineCtx *de_ctx)
2114 {
2115     SigString *item = NULL;
2116     SigString *sitem;
2117 
2118     TAILQ_FOREACH_SAFE(item, &de_ctx->sig_stat.failed_sigs, next, sitem) {
2119         SCFree(item->filename);
2120         SCFree(item->sig_str);
2121         if (item->sig_error) {
2122             SCFree(item->sig_error);
2123         }
2124         TAILQ_REMOVE(&de_ctx->sig_stat.failed_sigs, item, next);
2125         SCFree(item);
2126     }
2127 }
2128 
2129 /**
2130  * \brief Free a DetectEngineCtx::
2131  *
2132  * \param de_ctx DetectEngineCtx:: to be freed
2133  */
DetectEngineCtxFree(DetectEngineCtx * de_ctx)2134 void DetectEngineCtxFree(DetectEngineCtx *de_ctx)
2135 {
2136 
2137     if (de_ctx == NULL)
2138         return;
2139 
2140 #ifdef PROFILING
2141     if (de_ctx->profile_ctx != NULL) {
2142         SCProfilingRuleDestroyCtx(de_ctx->profile_ctx);
2143         de_ctx->profile_ctx = NULL;
2144     }
2145     if (de_ctx->profile_keyword_ctx != NULL) {
2146         SCProfilingKeywordDestroyCtx(de_ctx);//->profile_keyword_ctx);
2147 //        de_ctx->profile_keyword_ctx = NULL;
2148     }
2149     if (de_ctx->profile_sgh_ctx != NULL) {
2150         SCProfilingSghDestroyCtx(de_ctx);
2151     }
2152     SCProfilingPrefilterDestroyCtx(de_ctx);
2153 #endif
2154 
2155     /* Normally the hashes are freed elsewhere, but
2156      * to be sure look at them again here.
2157      */
2158     SigGroupHeadHashFree(de_ctx);
2159     MpmStoreFree(de_ctx);
2160     DetectParseDupSigHashFree(de_ctx);
2161     SCSigSignatureOrderingModuleCleanup(de_ctx);
2162     ThresholdContextDestroy(de_ctx);
2163     SigCleanSignatures(de_ctx);
2164     if (de_ctx->sig_array)
2165         SCFree(de_ctx->sig_array);
2166 
2167     SCClassConfDeInitContext(de_ctx);
2168     SCRConfDeInitContext(de_ctx);
2169 
2170     SigGroupCleanup(de_ctx);
2171 
2172     SpmDestroyGlobalThreadCtx(de_ctx->spm_global_thread_ctx);
2173 
2174     MpmFactoryDeRegisterAllMpmCtxProfiles(de_ctx);
2175 
2176     DetectEngineCtxFreeThreadKeywordData(de_ctx);
2177     SRepDestroy(de_ctx);
2178     DetectEngineCtxFreeFailedSigs(de_ctx);
2179 
2180     DetectAddressMapFree(de_ctx);
2181     DetectMetadataHashFree(de_ctx);
2182 
2183     /* if we have a config prefix, remove the config from the tree */
2184     if (strlen(de_ctx->config_prefix) > 0) {
2185         /* remove config */
2186         ConfNode *node = ConfGetNode(de_ctx->config_prefix);
2187         if (node != NULL) {
2188             ConfNodeRemove(node); /* frees node */
2189         }
2190 #if 0
2191         ConfDump();
2192 #endif
2193     }
2194 
2195     DetectPortCleanupList(de_ctx, de_ctx->tcp_whitelist);
2196     DetectPortCleanupList(de_ctx, de_ctx->udp_whitelist);
2197 
2198     DetectBufferTypeFreeDetectEngine(de_ctx);
2199     /* freed our var name hash */
2200     VarNameStoreFree(de_ctx->version);
2201 
2202     SCFree(de_ctx);
2203     //DetectAddressGroupPrintMemory();
2204     //DetectSigGroupPrintMemory();
2205     //DetectPortPrintMemory();
2206 }
2207 
2208 /** \brief  Function that load DetectEngineCtx config for grouping sigs
2209  *          used by the engine
2210  *  \retval 0 if no config provided, 1 if config was provided
2211  *          and loaded successfully
2212  */
DetectEngineCtxLoadConf(DetectEngineCtx * de_ctx)2213 static int DetectEngineCtxLoadConf(DetectEngineCtx *de_ctx)
2214 {
2215     uint8_t profile = ENGINE_PROFILE_MEDIUM;
2216     const char *max_uniq_toclient_groups_str = NULL;
2217     const char *max_uniq_toserver_groups_str = NULL;
2218     const char *sgh_mpm_context = NULL;
2219     const char *de_ctx_profile = NULL;
2220 
2221     (void)ConfGet("detect.profile", &de_ctx_profile);
2222     (void)ConfGet("detect.sgh-mpm-context", &sgh_mpm_context);
2223 
2224     ConfNode *de_ctx_custom = ConfGetNode("detect-engine");
2225     ConfNode *opt = NULL;
2226 
2227     if (de_ctx_custom != NULL) {
2228         TAILQ_FOREACH(opt, &de_ctx_custom->head, next) {
2229             if (de_ctx_profile == NULL) {
2230                 if (opt->val && strcmp(opt->val, "profile") == 0) {
2231                     de_ctx_profile = opt->head.tqh_first->val;
2232                 }
2233             }
2234 
2235             if (sgh_mpm_context == NULL) {
2236                 if (opt->val && strcmp(opt->val, "sgh-mpm-context") == 0) {
2237                     sgh_mpm_context = opt->head.tqh_first->val;
2238                 }
2239             }
2240         }
2241     }
2242 
2243     if (de_ctx_profile != NULL) {
2244         if (strcmp(de_ctx_profile, "low") == 0 ||
2245             strcmp(de_ctx_profile, "lowest") == 0) {        // legacy
2246             profile = ENGINE_PROFILE_LOW;
2247         } else if (strcmp(de_ctx_profile, "medium") == 0) {
2248             profile = ENGINE_PROFILE_MEDIUM;
2249         } else if (strcmp(de_ctx_profile, "high") == 0 ||
2250                    strcmp(de_ctx_profile, "highest") == 0) { // legacy
2251             profile = ENGINE_PROFILE_HIGH;
2252         } else if (strcmp(de_ctx_profile, "custom") == 0) {
2253             profile = ENGINE_PROFILE_CUSTOM;
2254         } else {
2255             SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
2256                     "invalid value for detect.profile: '%s'. "
2257                     "Valid options: low, medium, high and custom.",
2258                     de_ctx_profile);
2259             return -1;
2260         }
2261 
2262         SCLogDebug("Profile for detection engine groups is \"%s\"", de_ctx_profile);
2263     } else {
2264         SCLogDebug("Profile for detection engine groups not provided "
2265                    "at suricata.yaml. Using default (\"medium\").");
2266     }
2267 
2268     /* detect-engine.sgh-mpm-context option parsing */
2269     if (sgh_mpm_context == NULL || strcmp(sgh_mpm_context, "auto") == 0) {
2270         /* for now, since we still haven't implemented any intelligence into
2271          * understanding the patterns and distributing mpm_ctx across sgh */
2272         if (de_ctx->mpm_matcher == MPM_AC || de_ctx->mpm_matcher == MPM_AC_KS ||
2273 #ifdef BUILD_HYPERSCAN
2274             de_ctx->mpm_matcher == MPM_HS ||
2275 #endif
2276             de_ctx->mpm_matcher == MPM_AC_BS) {
2277             de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE;
2278         } else {
2279             de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL;
2280         }
2281     } else {
2282         if (strcmp(sgh_mpm_context, "single") == 0) {
2283             de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE;
2284         } else if (strcmp(sgh_mpm_context, "full") == 0) {
2285             de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL;
2286         } else {
2287            SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "You have supplied an "
2288                       "invalid conf value for detect-engine.sgh-mpm-context-"
2289                       "%s", sgh_mpm_context);
2290            exit(EXIT_FAILURE);
2291         }
2292     }
2293 
2294     if (run_mode == RUNMODE_UNITTEST) {
2295         de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL;
2296     }
2297 
2298     /* parse profile custom-values */
2299     opt = NULL;
2300     switch (profile) {
2301         case ENGINE_PROFILE_LOW:
2302             de_ctx->max_uniq_toclient_groups = 15;
2303             de_ctx->max_uniq_toserver_groups = 25;
2304             break;
2305 
2306         case ENGINE_PROFILE_HIGH:
2307             de_ctx->max_uniq_toclient_groups = 75;
2308             de_ctx->max_uniq_toserver_groups = 75;
2309             break;
2310 
2311         case ENGINE_PROFILE_CUSTOM:
2312             (void)ConfGet("detect.custom-values.toclient-groups",
2313                     &max_uniq_toclient_groups_str);
2314             (void)ConfGet("detect.custom-values.toserver-groups",
2315                     &max_uniq_toserver_groups_str);
2316 
2317             if (de_ctx_custom != NULL) {
2318                 TAILQ_FOREACH(opt, &de_ctx_custom->head, next) {
2319                     if (opt->val && strcmp(opt->val, "custom-values") == 0) {
2320                         if (max_uniq_toclient_groups_str == NULL) {
2321                             max_uniq_toclient_groups_str = (char *)ConfNodeLookupChildValue
2322                                 (opt->head.tqh_first, "toclient-sp-groups");
2323                         }
2324                         if (max_uniq_toclient_groups_str == NULL) {
2325                             max_uniq_toclient_groups_str = (char *)ConfNodeLookupChildValue
2326                                 (opt->head.tqh_first, "toclient-groups");
2327                         }
2328                         if (max_uniq_toserver_groups_str == NULL) {
2329                             max_uniq_toserver_groups_str = (char *)ConfNodeLookupChildValue
2330                                 (opt->head.tqh_first, "toserver-dp-groups");
2331                         }
2332                         if (max_uniq_toserver_groups_str == NULL) {
2333                             max_uniq_toserver_groups_str = (char *)ConfNodeLookupChildValue
2334                                 (opt->head.tqh_first, "toserver-groups");
2335                         }
2336                     }
2337                 }
2338             }
2339             if (max_uniq_toclient_groups_str != NULL) {
2340                 if (StringParseUint16(&de_ctx->max_uniq_toclient_groups, 10,
2341                     strlen(max_uniq_toclient_groups_str),
2342                     (const char *)max_uniq_toclient_groups_str) <= 0)
2343                 {
2344                     de_ctx->max_uniq_toclient_groups = 20;
2345 
2346                     SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for "
2347                             "toclient-groups failed, using %u",
2348                             max_uniq_toclient_groups_str,
2349                             de_ctx->max_uniq_toclient_groups);
2350                 }
2351             } else {
2352                 de_ctx->max_uniq_toclient_groups = 20;
2353             }
2354             SCLogConfig("toclient-groups %u", de_ctx->max_uniq_toclient_groups);
2355 
2356             if (max_uniq_toserver_groups_str != NULL) {
2357                 if (StringParseUint16(&de_ctx->max_uniq_toserver_groups, 10,
2358                     strlen(max_uniq_toserver_groups_str),
2359                     (const char *)max_uniq_toserver_groups_str) <= 0)
2360                 {
2361                     de_ctx->max_uniq_toserver_groups = 40;
2362 
2363                     SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for "
2364                             "toserver-groups failed, using %u",
2365                             max_uniq_toserver_groups_str,
2366                             de_ctx->max_uniq_toserver_groups);
2367                 }
2368             } else {
2369                 de_ctx->max_uniq_toserver_groups = 40;
2370             }
2371             SCLogConfig("toserver-groups %u", de_ctx->max_uniq_toserver_groups);
2372             break;
2373 
2374         /* Default (or no config provided) is profile medium */
2375         case ENGINE_PROFILE_MEDIUM:
2376         case ENGINE_PROFILE_UNKNOWN:
2377         default:
2378             de_ctx->max_uniq_toclient_groups = 20;
2379             de_ctx->max_uniq_toserver_groups = 40;
2380             break;
2381     }
2382 
2383     intmax_t value = 0;
2384     if (ConfGetInt("detect.inspection-recursion-limit", &value) == 1)
2385     {
2386         if (value >= 0 && value <= INT_MAX) {
2387             de_ctx->inspection_recursion_limit = (int)value;
2388         }
2389 
2390     /* fall back to old config parsing */
2391     } else {
2392         ConfNode *insp_recursion_limit_node = NULL;
2393         char *insp_recursion_limit = NULL;
2394 
2395         if (de_ctx_custom != NULL) {
2396             opt = NULL;
2397             TAILQ_FOREACH(opt, &de_ctx_custom->head, next) {
2398                 if (opt->val && strcmp(opt->val, "inspection-recursion-limit") != 0)
2399                     continue;
2400 
2401                 insp_recursion_limit_node = ConfNodeLookupChild(opt, opt->val);
2402                 if (insp_recursion_limit_node == NULL) {
2403                     SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Error retrieving conf "
2404                             "entry for detect-engine:inspection-recursion-limit");
2405                     break;
2406                 }
2407                 insp_recursion_limit = insp_recursion_limit_node->val;
2408                 SCLogDebug("Found detect-engine.inspection-recursion-limit - %s:%s",
2409                         insp_recursion_limit_node->name, insp_recursion_limit_node->val);
2410                 break;
2411             }
2412 
2413             if (insp_recursion_limit != NULL) {
2414                 if (StringParseInt32(&de_ctx->inspection_recursion_limit, 10,
2415                                      0, (const char *)insp_recursion_limit) < 0) {
2416                     SCLogWarning(SC_ERR_INVALID_VALUE, "Invalid value for "
2417                                  "detect-engine.inspection-recursion-limit: %s "
2418                                  "resetting to %d", insp_recursion_limit,
2419                                  DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT);
2420                     de_ctx->inspection_recursion_limit =
2421                         DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT;
2422                 }
2423             } else {
2424                 de_ctx->inspection_recursion_limit =
2425                     DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT;
2426             }
2427         }
2428     }
2429 
2430     if (de_ctx->inspection_recursion_limit == 0)
2431         de_ctx->inspection_recursion_limit = -1;
2432 
2433     SCLogDebug("de_ctx->inspection_recursion_limit: %d",
2434                de_ctx->inspection_recursion_limit);
2435 
2436     /* parse port grouping whitelisting settings */
2437 
2438     const char *ports = NULL;
2439     (void)ConfGet("detect.grouping.tcp-whitelist", &ports);
2440     if (ports) {
2441         SCLogConfig("grouping: tcp-whitelist %s", ports);
2442     } else {
2443         ports = "53, 80, 139, 443, 445, 1433, 3306, 3389, 6666, 6667, 8080";
2444         SCLogConfig("grouping: tcp-whitelist (default) %s", ports);
2445 
2446     }
2447     if (DetectPortParse(de_ctx, &de_ctx->tcp_whitelist, ports) != 0) {
2448         SCLogWarning(SC_ERR_INVALID_YAML_CONF_ENTRY, "'%s' is not a valid value "
2449                 "for detect.grouping.tcp-whitelist", ports);
2450     }
2451     DetectPort *x = de_ctx->tcp_whitelist;
2452     for ( ; x != NULL;  x = x->next) {
2453         if (x->port != x->port2) {
2454             SCLogWarning(SC_ERR_INVALID_YAML_CONF_ENTRY, "'%s' is not a valid value "
2455                 "for detect.grouping.tcp-whitelist: only single ports allowed", ports);
2456             DetectPortCleanupList(de_ctx, de_ctx->tcp_whitelist);
2457             de_ctx->tcp_whitelist = NULL;
2458             break;
2459         }
2460     }
2461 
2462     ports = NULL;
2463     (void)ConfGet("detect.grouping.udp-whitelist", &ports);
2464     if (ports) {
2465         SCLogConfig("grouping: udp-whitelist %s", ports);
2466     } else {
2467         ports = "53, 135, 5060";
2468         SCLogConfig("grouping: udp-whitelist (default) %s", ports);
2469 
2470     }
2471     if (DetectPortParse(de_ctx, &de_ctx->udp_whitelist, ports) != 0) {
2472         SCLogWarning(SC_ERR_INVALID_YAML_CONF_ENTRY, "'%s' is not a valid value "
2473                 "forr detect.grouping.udp-whitelist", ports);
2474     }
2475     for (x = de_ctx->udp_whitelist; x != NULL;  x = x->next) {
2476         if (x->port != x->port2) {
2477             SCLogWarning(SC_ERR_INVALID_YAML_CONF_ENTRY, "'%s' is not a valid value "
2478                 "for detect.grouping.udp-whitelist: only single ports allowed", ports);
2479             DetectPortCleanupList(de_ctx, de_ctx->udp_whitelist);
2480             de_ctx->udp_whitelist = NULL;
2481             break;
2482         }
2483     }
2484 
2485     de_ctx->prefilter_setting = DETECT_PREFILTER_MPM;
2486     const char *pf_setting = NULL;
2487     if (ConfGet("detect.prefilter.default", &pf_setting) == 1 && pf_setting) {
2488         if (strcasecmp(pf_setting, "mpm") == 0) {
2489             de_ctx->prefilter_setting = DETECT_PREFILTER_MPM;
2490         } else if (strcasecmp(pf_setting, "auto") == 0) {
2491             de_ctx->prefilter_setting = DETECT_PREFILTER_AUTO;
2492         }
2493     }
2494     switch (de_ctx->prefilter_setting) {
2495         case DETECT_PREFILTER_MPM:
2496             SCLogConfig("prefilter engines: MPM");
2497             break;
2498         case DETECT_PREFILTER_AUTO:
2499             SCLogConfig("prefilter engines: MPM and keywords");
2500             break;
2501     }
2502 
2503     return 0;
2504 }
2505 
2506 /*
2507  * getting & (re)setting the internal sig i
2508  */
2509 
2510 //inline uint32_t DetectEngineGetMaxSigId(DetectEngineCtx *de_ctx)
2511 //{
2512 //    return de_ctx->signum;
2513 //}
2514 
DetectEngineResetMaxSigId(DetectEngineCtx * de_ctx)2515 void DetectEngineResetMaxSigId(DetectEngineCtx *de_ctx)
2516 {
2517     de_ctx->signum = 0;
2518 }
2519 
DetectEngineThreadCtxInitGlobalKeywords(DetectEngineThreadCtx * det_ctx)2520 static int DetectEngineThreadCtxInitGlobalKeywords(DetectEngineThreadCtx *det_ctx)
2521 {
2522     const DetectEngineMasterCtx *master = &g_master_de_ctx;
2523 
2524     if (master->keyword_id > 0) {
2525         // coverity[suspicious_sizeof : FALSE]
2526         det_ctx->global_keyword_ctxs_array = (void **)SCCalloc(master->keyword_id, sizeof(void *));
2527         if (det_ctx->global_keyword_ctxs_array == NULL) {
2528             SCLogError(SC_ERR_DETECT_PREPARE, "setting up thread local detect ctx");
2529             return TM_ECODE_FAILED;
2530         }
2531         det_ctx->global_keyword_ctxs_size = master->keyword_id;
2532 
2533         const DetectEngineThreadKeywordCtxItem *item = master->keyword_list;
2534         while (item) {
2535             det_ctx->global_keyword_ctxs_array[item->id] = item->InitFunc(item->data);
2536             if (det_ctx->global_keyword_ctxs_array[item->id] == NULL) {
2537                 SCLogError(SC_ERR_DETECT_PREPARE, "setting up thread local detect ctx "
2538                         "for keyword \"%s\" failed", item->name);
2539                 return TM_ECODE_FAILED;
2540             }
2541             item = item->next;
2542         }
2543     }
2544     return TM_ECODE_OK;
2545 }
2546 
DetectEngineThreadCtxDeinitGlobalKeywords(DetectEngineThreadCtx * det_ctx)2547 static void DetectEngineThreadCtxDeinitGlobalKeywords(DetectEngineThreadCtx *det_ctx)
2548 {
2549     if (det_ctx->global_keyword_ctxs_array == NULL ||
2550         det_ctx->global_keyword_ctxs_size == 0) {
2551         return;
2552     }
2553 
2554     const DetectEngineMasterCtx *master = &g_master_de_ctx;
2555     if (master->keyword_id > 0) {
2556         const DetectEngineThreadKeywordCtxItem *item = master->keyword_list;
2557         while (item) {
2558             if (det_ctx->global_keyword_ctxs_array[item->id] != NULL)
2559                 item->FreeFunc(det_ctx->global_keyword_ctxs_array[item->id]);
2560 
2561             item = item->next;
2562         }
2563         det_ctx->global_keyword_ctxs_size = 0;
2564         SCFree(det_ctx->global_keyword_ctxs_array);
2565         det_ctx->global_keyword_ctxs_array = NULL;
2566     }
2567 }
2568 
DetectEngineThreadCtxInitKeywords(DetectEngineCtx * de_ctx,DetectEngineThreadCtx * det_ctx)2569 static int DetectEngineThreadCtxInitKeywords(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
2570 {
2571     if (de_ctx->keyword_id > 0) {
2572         // coverity[suspicious_sizeof : FALSE]
2573         det_ctx->keyword_ctxs_array = SCMalloc(de_ctx->keyword_id * sizeof(void *));
2574         if (det_ctx->keyword_ctxs_array == NULL) {
2575             SCLogError(SC_ERR_DETECT_PREPARE, "setting up thread local detect ctx");
2576             return TM_ECODE_FAILED;
2577         }
2578 
2579         memset(det_ctx->keyword_ctxs_array, 0x00, de_ctx->keyword_id * sizeof(void *));
2580 
2581         det_ctx->keyword_ctxs_size = de_ctx->keyword_id;
2582 
2583         DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list;
2584         while (item) {
2585             det_ctx->keyword_ctxs_array[item->id] = item->InitFunc(item->data);
2586             if (det_ctx->keyword_ctxs_array[item->id] == NULL) {
2587                 SCLogError(SC_ERR_DETECT_PREPARE, "setting up thread local detect ctx "
2588                         "for keyword \"%s\" failed", item->name);
2589                 return TM_ECODE_FAILED;
2590             }
2591             item = item->next;
2592         }
2593     }
2594     return TM_ECODE_OK;
2595 }
2596 
DetectEngineThreadCtxDeinitKeywords(DetectEngineCtx * de_ctx,DetectEngineThreadCtx * det_ctx)2597 static void DetectEngineThreadCtxDeinitKeywords(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
2598 {
2599     if (de_ctx->keyword_id > 0) {
2600         DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list;
2601         while (item) {
2602             if (det_ctx->keyword_ctxs_array[item->id] != NULL)
2603                 item->FreeFunc(det_ctx->keyword_ctxs_array[item->id]);
2604 
2605             item = item->next;
2606         }
2607         det_ctx->keyword_ctxs_size = 0;
2608         SCFree(det_ctx->keyword_ctxs_array);
2609         det_ctx->keyword_ctxs_array = NULL;
2610     }
2611 }
2612 
2613 /** NOTE: master MUST be locked before calling this */
DetectEngineThreadCtxInitForMT(ThreadVars * tv,DetectEngineThreadCtx * det_ctx)2614 static TmEcode DetectEngineThreadCtxInitForMT(ThreadVars *tv, DetectEngineThreadCtx *det_ctx)
2615 {
2616     DetectEngineMasterCtx *master = &g_master_de_ctx;
2617     DetectEngineTenantMapping *map_array = NULL;
2618     uint32_t map_array_size = 0;
2619     uint32_t map_cnt = 0;
2620     int max_tenant_id = 0;
2621     DetectEngineCtx *list = master->list;
2622     HashTable *mt_det_ctxs_hash = NULL;
2623 
2624     if (master->tenant_selector == TENANT_SELECTOR_UNKNOWN) {
2625         SCLogError(SC_ERR_MT_NO_SELECTOR, "no tenant selector set: "
2626                                           "set using multi-detect.selector");
2627         return TM_ECODE_FAILED;
2628     }
2629 
2630     uint32_t tcnt = 0;
2631     while (list) {
2632         if (list->tenant_id > max_tenant_id)
2633             max_tenant_id = list->tenant_id;
2634 
2635         list = list->next;
2636         tcnt++;
2637     }
2638 
2639     mt_det_ctxs_hash = HashTableInit(tcnt * 2, TenantIdHash, TenantIdCompare, TenantIdFree);
2640     if (mt_det_ctxs_hash == NULL) {
2641         goto error;
2642     }
2643 
2644     if (tcnt == 0) {
2645         SCLogInfo("no tenants left, or none registered yet");
2646     } else {
2647         max_tenant_id++;
2648 
2649         DetectEngineTenantMapping *map = master->tenant_mapping_list;
2650         while (map) {
2651             map_cnt++;
2652             map = map->next;
2653         }
2654 
2655         if (map_cnt > 0) {
2656             map_array_size = map_cnt + 1;
2657 
2658             map_array = SCCalloc(map_array_size, sizeof(*map_array));
2659             if (map_array == NULL)
2660                 goto error;
2661 
2662             /* fill the array */
2663             map_cnt = 0;
2664             map = master->tenant_mapping_list;
2665             while (map) {
2666                 if (map_cnt >= map_array_size) {
2667                     goto error;
2668                 }
2669                 map_array[map_cnt].traffic_id = map->traffic_id;
2670                 map_array[map_cnt].tenant_id = map->tenant_id;
2671                 map_cnt++;
2672                 map = map->next;
2673             }
2674 
2675         }
2676 
2677         /* set up hash for tenant lookup */
2678         list = master->list;
2679         while (list) {
2680             SCLogDebug("tenant-id %u", list->tenant_id);
2681             if (list->tenant_id != 0) {
2682                 DetectEngineThreadCtx *mt_det_ctx = DetectEngineThreadCtxInitForReload(tv, list, 0);
2683                 if (mt_det_ctx == NULL)
2684                     goto error;
2685                 if (HashTableAdd(mt_det_ctxs_hash, mt_det_ctx, 0) != 0) {
2686                     goto error;
2687                 }
2688             }
2689             list = list->next;
2690         }
2691     }
2692 
2693     det_ctx->mt_det_ctxs_hash = mt_det_ctxs_hash;
2694     mt_det_ctxs_hash = NULL;
2695 
2696     det_ctx->mt_det_ctxs_cnt = max_tenant_id;
2697 
2698     det_ctx->tenant_array = map_array;
2699     det_ctx->tenant_array_size = map_array_size;
2700 
2701     switch (master->tenant_selector) {
2702         case TENANT_SELECTOR_UNKNOWN:
2703             SCLogDebug("TENANT_SELECTOR_UNKNOWN");
2704             break;
2705         case TENANT_SELECTOR_VLAN:
2706             det_ctx->TenantGetId = DetectEngineTentantGetIdFromVlanId;
2707             SCLogDebug("TENANT_SELECTOR_VLAN");
2708             break;
2709         case TENANT_SELECTOR_LIVEDEV:
2710             det_ctx->TenantGetId = DetectEngineTentantGetIdFromLivedev;
2711             SCLogDebug("TENANT_SELECTOR_LIVEDEV");
2712             break;
2713         case TENANT_SELECTOR_DIRECT:
2714             det_ctx->TenantGetId = DetectEngineTentantGetIdFromPcap;
2715             SCLogDebug("TENANT_SELECTOR_DIRECT");
2716             break;
2717     }
2718 
2719     return TM_ECODE_OK;
2720 error:
2721     if (map_array != NULL)
2722         SCFree(map_array);
2723     if (mt_det_ctxs_hash != NULL)
2724         HashTableFree(mt_det_ctxs_hash);
2725 
2726     return TM_ECODE_FAILED;
2727 }
2728 
2729 /** \internal
2730  *  \brief Helper for DetectThread setup functions
2731  */
ThreadCtxDoInit(DetectEngineCtx * de_ctx,DetectEngineThreadCtx * det_ctx)2732 static TmEcode ThreadCtxDoInit (DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
2733 {
2734     PatternMatchThreadPrepare(&det_ctx->mtc, de_ctx->mpm_matcher);
2735     PatternMatchThreadPrepare(&det_ctx->mtcs, de_ctx->mpm_matcher);
2736     PatternMatchThreadPrepare(&det_ctx->mtcu, de_ctx->mpm_matcher);
2737 
2738     PmqSetup(&det_ctx->pmq);
2739 
2740     det_ctx->spm_thread_ctx = SpmMakeThreadCtx(de_ctx->spm_global_thread_ctx);
2741     if (det_ctx->spm_thread_ctx == NULL) {
2742         return TM_ECODE_FAILED;
2743     }
2744 
2745     /* sized to the max of our sgh settings. A max setting of 0 implies that all
2746      * sgh's have: sgh->non_pf_store_cnt == 0 */
2747     if (de_ctx->non_pf_store_cnt_max > 0) {
2748         det_ctx->non_pf_id_array =  SCCalloc(de_ctx->non_pf_store_cnt_max, sizeof(SigIntId));
2749         BUG_ON(det_ctx->non_pf_id_array == NULL);
2750     }
2751 
2752     /* IP-ONLY */
2753     DetectEngineIPOnlyThreadInit(de_ctx,&det_ctx->io_ctx);
2754 
2755     /* DeState */
2756     if (de_ctx->sig_array_len > 0) {
2757         det_ctx->match_array_len = de_ctx->sig_array_len;
2758         det_ctx->match_array = SCMalloc(det_ctx->match_array_len * sizeof(Signature *));
2759         if (det_ctx->match_array == NULL) {
2760             return TM_ECODE_FAILED;
2761         }
2762         memset(det_ctx->match_array, 0,
2763                det_ctx->match_array_len * sizeof(Signature *));
2764 
2765         RuleMatchCandidateTxArrayInit(det_ctx, de_ctx->sig_array_len);
2766     }
2767 
2768     /* byte_extract storage */
2769     det_ctx->byte_values = SCMalloc(sizeof(*det_ctx->byte_values) *
2770                                   (de_ctx->byte_extract_max_local_id + 1));
2771     if (det_ctx->byte_values == NULL) {
2772         return TM_ECODE_FAILED;
2773     }
2774 
2775     /* Allocate space for base64 decoded data. */
2776     if (de_ctx->base64_decode_max_len) {
2777         det_ctx->base64_decoded = SCMalloc(de_ctx->base64_decode_max_len);
2778         if (det_ctx->base64_decoded == NULL) {
2779             return TM_ECODE_FAILED;
2780         }
2781         det_ctx->base64_decoded_len_max = de_ctx->base64_decode_max_len;
2782         det_ctx->base64_decoded_len = 0;
2783     }
2784 
2785     det_ctx->inspect.buffers_size = de_ctx->buffer_type_id;
2786     det_ctx->inspect.buffers = SCCalloc(det_ctx->inspect.buffers_size, sizeof(InspectionBuffer));
2787     if (det_ctx->inspect.buffers == NULL) {
2788         return TM_ECODE_FAILED;
2789     }
2790     det_ctx->inspect.to_clear_queue = SCCalloc(det_ctx->inspect.buffers_size, sizeof(uint32_t));
2791     if (det_ctx->inspect.to_clear_queue == NULL) {
2792         return TM_ECODE_FAILED;
2793     }
2794     det_ctx->inspect.to_clear_idx = 0;
2795 
2796     det_ctx->multi_inspect.buffers_size = de_ctx->buffer_type_id;
2797     det_ctx->multi_inspect.buffers = SCCalloc(det_ctx->multi_inspect.buffers_size, sizeof(InspectionBufferMultipleForList));
2798     if (det_ctx->multi_inspect.buffers == NULL) {
2799         return TM_ECODE_FAILED;
2800     }
2801     det_ctx->multi_inspect.to_clear_queue = SCCalloc(det_ctx->multi_inspect.buffers_size, sizeof(uint32_t));
2802     if (det_ctx->multi_inspect.to_clear_queue == NULL) {
2803         return TM_ECODE_FAILED;
2804     }
2805     det_ctx->multi_inspect.to_clear_idx = 0;
2806 
2807 
2808     DetectEngineThreadCtxInitKeywords(de_ctx, det_ctx);
2809     DetectEngineThreadCtxInitGlobalKeywords(det_ctx);
2810 #ifdef PROFILING
2811     SCProfilingRuleThreadSetup(de_ctx->profile_ctx, det_ctx);
2812     SCProfilingKeywordThreadSetup(de_ctx->profile_keyword_ctx, det_ctx);
2813     SCProfilingPrefilterThreadSetup(de_ctx->profile_prefilter_ctx, det_ctx);
2814     SCProfilingSghThreadSetup(de_ctx->profile_sgh_ctx, det_ctx);
2815 #endif
2816     SC_ATOMIC_INIT(det_ctx->so_far_used_by_detect);
2817 
2818     return TM_ECODE_OK;
2819 }
2820 
2821 /** \brief initialize thread specific detection engine context
2822  *
2823  *  \note there is a special case when using delayed detect. In this case the
2824  *        function is called twice per thread. The first time the rules are not
2825  *        yet loaded. de_ctx->delayed_detect_initialized will be 0. The 2nd
2826  *        time they will be loaded. de_ctx->delayed_detect_initialized will be 1.
2827  *        This is needed to do the per thread counter registration before the
2828  *        packet runtime starts. In delayed detect mode, the first call will
2829  *        return a NULL ptr through the data ptr.
2830  *
2831  *  \param tv ThreadVars for this thread
2832  *  \param initdata pointer to de_ctx
2833  *  \param data[out] pointer to store our thread detection ctx
2834  *
2835  *  \retval TM_ECODE_OK if all went well
2836  *  \retval TM_ECODE_FAILED on serious errors
2837  */
DetectEngineThreadCtxInit(ThreadVars * tv,void * initdata,void ** data)2838 TmEcode DetectEngineThreadCtxInit(ThreadVars *tv, void *initdata, void **data)
2839 {
2840     DetectEngineThreadCtx *det_ctx = SCMalloc(sizeof(DetectEngineThreadCtx));
2841     if (unlikely(det_ctx == NULL))
2842         return TM_ECODE_FAILED;
2843     memset(det_ctx, 0, sizeof(DetectEngineThreadCtx));
2844 
2845     det_ctx->tv = tv;
2846     det_ctx->de_ctx = DetectEngineGetCurrent();
2847     if (det_ctx->de_ctx == NULL) {
2848 #ifdef UNITTESTS
2849         if (RunmodeIsUnittests()) {
2850             det_ctx->de_ctx = (DetectEngineCtx *)initdata;
2851         } else {
2852             DetectEngineThreadCtxDeinit(tv, det_ctx);
2853             return TM_ECODE_FAILED;
2854         }
2855 #else
2856         DetectEngineThreadCtxDeinit(tv, det_ctx);
2857         return TM_ECODE_FAILED;
2858 #endif
2859     }
2860 
2861     if (det_ctx->de_ctx->type == DETECT_ENGINE_TYPE_NORMAL ||
2862         det_ctx->de_ctx->type == DETECT_ENGINE_TYPE_TENANT)
2863     {
2864         if (ThreadCtxDoInit(det_ctx->de_ctx, det_ctx) != TM_ECODE_OK) {
2865             DetectEngineThreadCtxDeinit(tv, det_ctx);
2866             return TM_ECODE_FAILED;
2867         }
2868     }
2869 
2870     /** alert counter setup */
2871     det_ctx->counter_alerts = StatsRegisterCounter("detect.alert", tv);
2872 #ifdef PROFILING
2873     det_ctx->counter_mpm_list = StatsRegisterAvgCounter("detect.mpm_list", tv);
2874     det_ctx->counter_nonmpm_list = StatsRegisterAvgCounter("detect.nonmpm_list", tv);
2875     det_ctx->counter_fnonmpm_list = StatsRegisterAvgCounter("detect.fnonmpm_list", tv);
2876     det_ctx->counter_match_list = StatsRegisterAvgCounter("detect.match_list", tv);
2877 #endif
2878 
2879     if (DetectEngineMultiTenantEnabled()) {
2880         if (DetectEngineThreadCtxInitForMT(tv, det_ctx) != TM_ECODE_OK) {
2881             DetectEngineThreadCtxDeinit(tv, det_ctx);
2882             return TM_ECODE_FAILED;
2883         }
2884     }
2885 
2886     /* pass thread data back to caller */
2887     *data = (void *)det_ctx;
2888 
2889     return TM_ECODE_OK;
2890 }
2891 
2892 /**
2893  * \internal
2894  * \brief initialize a det_ctx for reload cases
2895  * \param new_de_ctx the new detection engine
2896  * \param mt flag to indicate if MT should be set up for this det_ctx
2897  *           this should only be done for the 'root' det_ctx
2898  *
2899  * \retval det_ctx detection engine thread ctx or NULL in case of error
2900  */
DetectEngineThreadCtxInitForReload(ThreadVars * tv,DetectEngineCtx * new_de_ctx,int mt)2901 static DetectEngineThreadCtx *DetectEngineThreadCtxInitForReload(
2902         ThreadVars *tv, DetectEngineCtx *new_de_ctx, int mt)
2903 {
2904     DetectEngineThreadCtx *det_ctx = SCMalloc(sizeof(DetectEngineThreadCtx));
2905     if (unlikely(det_ctx == NULL))
2906         return NULL;
2907     memset(det_ctx, 0, sizeof(DetectEngineThreadCtx));
2908 
2909     det_ctx->tenant_id = new_de_ctx->tenant_id;
2910     det_ctx->tv = tv;
2911     det_ctx->de_ctx = DetectEngineReference(new_de_ctx);
2912     if (det_ctx->de_ctx == NULL) {
2913         SCFree(det_ctx);
2914         return NULL;
2915     }
2916 
2917     /* most of the init happens here */
2918     if (det_ctx->de_ctx->type == DETECT_ENGINE_TYPE_NORMAL ||
2919         det_ctx->de_ctx->type == DETECT_ENGINE_TYPE_TENANT)
2920     {
2921         if (ThreadCtxDoInit(det_ctx->de_ctx, det_ctx) != TM_ECODE_OK) {
2922             DetectEngineDeReference(&det_ctx->de_ctx);
2923             SCFree(det_ctx);
2924             return NULL;
2925         }
2926     }
2927 
2928     /** alert counter setup */
2929     det_ctx->counter_alerts = StatsRegisterCounter("detect.alert", tv);
2930 #ifdef PROFILING
2931     uint16_t counter_mpm_list = StatsRegisterAvgCounter("detect.mpm_list", tv);
2932     uint16_t counter_nonmpm_list = StatsRegisterAvgCounter("detect.nonmpm_list", tv);
2933     uint16_t counter_fnonmpm_list = StatsRegisterAvgCounter("detect.fnonmpm_list", tv);
2934     uint16_t counter_match_list = StatsRegisterAvgCounter("detect.match_list", tv);
2935     det_ctx->counter_mpm_list = counter_mpm_list;
2936     det_ctx->counter_nonmpm_list = counter_nonmpm_list;
2937     det_ctx->counter_fnonmpm_list = counter_fnonmpm_list;
2938     det_ctx->counter_match_list = counter_match_list;
2939 #endif
2940 
2941     if (mt && DetectEngineMultiTenantEnabled()) {
2942         if (DetectEngineThreadCtxInitForMT(tv, det_ctx) != TM_ECODE_OK) {
2943             DetectEngineDeReference(&det_ctx->de_ctx);
2944             SCFree(det_ctx);
2945             return NULL;
2946         }
2947     }
2948 
2949     return det_ctx;
2950 }
2951 
DetectEngineThreadCtxFree(DetectEngineThreadCtx * det_ctx)2952 static void DetectEngineThreadCtxFree(DetectEngineThreadCtx *det_ctx)
2953 {
2954 #if  DEBUG
2955     SCLogDebug("PACKET PKT_STREAM_ADD: %"PRIu64, det_ctx->pkt_stream_add_cnt);
2956 
2957     SCLogDebug("PAYLOAD MPM %"PRIu64"/%"PRIu64, det_ctx->payload_mpm_cnt, det_ctx->payload_mpm_size);
2958     SCLogDebug("STREAM  MPM %"PRIu64"/%"PRIu64, det_ctx->stream_mpm_cnt, det_ctx->stream_mpm_size);
2959 
2960     SCLogDebug("PAYLOAD SIG %"PRIu64"/%"PRIu64, det_ctx->payload_persig_cnt, det_ctx->payload_persig_size);
2961     SCLogDebug("STREAM  SIG %"PRIu64"/%"PRIu64, det_ctx->stream_persig_cnt, det_ctx->stream_persig_size);
2962 #endif
2963 
2964     if (det_ctx->tenant_array != NULL) {
2965         SCFree(det_ctx->tenant_array);
2966         det_ctx->tenant_array = NULL;
2967     }
2968 
2969 #ifdef PROFILING
2970     SCProfilingRuleThreadCleanup(det_ctx);
2971     SCProfilingKeywordThreadCleanup(det_ctx);
2972     SCProfilingPrefilterThreadCleanup(det_ctx);
2973     SCProfilingSghThreadCleanup(det_ctx);
2974 #endif
2975 
2976     DetectEngineIPOnlyThreadDeinit(&det_ctx->io_ctx);
2977 
2978     /** \todo get rid of this static */
2979     if (det_ctx->de_ctx != NULL) {
2980         PatternMatchThreadDestroy(&det_ctx->mtc, det_ctx->de_ctx->mpm_matcher);
2981         PatternMatchThreadDestroy(&det_ctx->mtcs, det_ctx->de_ctx->mpm_matcher);
2982         PatternMatchThreadDestroy(&det_ctx->mtcu, det_ctx->de_ctx->mpm_matcher);
2983     }
2984 
2985     PmqFree(&det_ctx->pmq);
2986 
2987     if (det_ctx->spm_thread_ctx != NULL) {
2988         SpmDestroyThreadCtx(det_ctx->spm_thread_ctx);
2989     }
2990 
2991     if (det_ctx->non_pf_id_array != NULL)
2992         SCFree(det_ctx->non_pf_id_array);
2993 
2994     if (det_ctx->match_array != NULL)
2995         SCFree(det_ctx->match_array);
2996 
2997     RuleMatchCandidateTxArrayFree(det_ctx);
2998 
2999     if (det_ctx->byte_values != NULL)
3000         SCFree(det_ctx->byte_values);
3001 
3002     /* Decoded base64 data. */
3003     if (det_ctx->base64_decoded != NULL) {
3004         SCFree(det_ctx->base64_decoded);
3005     }
3006 
3007     if (det_ctx->inspect.buffers) {
3008         for (uint32_t i = 0; i < det_ctx->inspect.buffers_size; i++) {
3009             InspectionBufferFree(&det_ctx->inspect.buffers[i]);
3010         }
3011         SCFree(det_ctx->inspect.buffers);
3012     }
3013     if (det_ctx->inspect.to_clear_queue) {
3014         SCFree(det_ctx->inspect.to_clear_queue);
3015     }
3016     if (det_ctx->multi_inspect.buffers) {
3017         for (uint32_t i = 0; i < det_ctx->multi_inspect.buffers_size; i++) {
3018             InspectionBufferMultipleForList *fb = &det_ctx->multi_inspect.buffers[i];
3019             for (uint32_t x = 0; x < fb->size; x++) {
3020                 InspectionBufferFree(&fb->inspection_buffers[x]);
3021             }
3022             SCFree(fb->inspection_buffers);
3023         }
3024         SCFree(det_ctx->multi_inspect.buffers);
3025     }
3026     if (det_ctx->multi_inspect.to_clear_queue) {
3027         SCFree(det_ctx->multi_inspect.to_clear_queue);
3028     }
3029 
3030     DetectEngineThreadCtxDeinitGlobalKeywords(det_ctx);
3031     if (det_ctx->de_ctx != NULL) {
3032         DetectEngineThreadCtxDeinitKeywords(det_ctx->de_ctx, det_ctx);
3033 #ifdef UNITTESTS
3034         if (!RunmodeIsUnittests() || det_ctx->de_ctx->ref_cnt > 0)
3035             DetectEngineDeReference(&det_ctx->de_ctx);
3036 #else
3037         DetectEngineDeReference(&det_ctx->de_ctx);
3038 #endif
3039     }
3040 
3041     AppLayerDecoderEventsFreeEvents(&det_ctx->decoder_events);
3042 
3043     SCFree(det_ctx);
3044 }
3045 
DetectEngineThreadCtxDeinit(ThreadVars * tv,void * data)3046 TmEcode DetectEngineThreadCtxDeinit(ThreadVars *tv, void *data)
3047 {
3048     DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
3049 
3050     if (det_ctx == NULL) {
3051         SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "argument \"data\" NULL");
3052         return TM_ECODE_OK;
3053     }
3054 
3055     if (det_ctx->mt_det_ctxs_hash != NULL) {
3056         HashTableFree(det_ctx->mt_det_ctxs_hash);
3057         det_ctx->mt_det_ctxs_hash = NULL;
3058     }
3059     DetectEngineThreadCtxFree(det_ctx);
3060 
3061     return TM_ECODE_OK;
3062 }
3063 
DetectEngineThreadCtxInfo(ThreadVars * t,DetectEngineThreadCtx * det_ctx)3064 void DetectEngineThreadCtxInfo(ThreadVars *t, DetectEngineThreadCtx *det_ctx)
3065 {
3066     /* XXX */
3067     PatternMatchThreadPrint(&det_ctx->mtc, det_ctx->de_ctx->mpm_matcher);
3068     PatternMatchThreadPrint(&det_ctx->mtcu, det_ctx->de_ctx->mpm_matcher);
3069 }
3070 
3071 /** \brief Register Thread keyword context Funcs
3072  *
3073  *  \param de_ctx detection engine to register in
3074  *  \param name keyword name for error printing
3075  *  \param InitFunc function ptr
3076  *  \param data keyword init data to pass to Func. Can be NULL.
3077  *  \param FreeFunc function ptr
3078  *  \param mode 0 normal (ctx per keyword instance) 1 shared (one ctx per det_ct)
3079  *
3080  *  \retval id for retrieval of ctx at runtime
3081  *  \retval -1 on error
3082  *
3083  *  \note make sure "data" remains valid and it free'd elsewhere. It's
3084  *        recommended to store it in the keywords global ctx so that
3085  *        it's freed when the de_ctx is freed.
3086  */
DetectRegisterThreadCtxFuncs(DetectEngineCtx * de_ctx,const char * name,void * (* InitFunc)(void *),void * data,void (* FreeFunc)(void *),int mode)3087 int DetectRegisterThreadCtxFuncs(DetectEngineCtx *de_ctx, const char *name, void *(*InitFunc)(void *), void *data, void (*FreeFunc)(void *), int mode)
3088 {
3089     BUG_ON(de_ctx == NULL || InitFunc == NULL || FreeFunc == NULL);
3090 
3091     if (mode) {
3092         DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list;
3093         while (item != NULL) {
3094             if (strcmp(name, item->name) == 0) {
3095                 return item->id;
3096             }
3097 
3098             item = item->next;
3099         }
3100     }
3101 
3102     DetectEngineThreadKeywordCtxItem *item = SCMalloc(sizeof(DetectEngineThreadKeywordCtxItem));
3103     if (unlikely(item == NULL))
3104         return -1;
3105     memset(item, 0x00, sizeof(DetectEngineThreadKeywordCtxItem));
3106 
3107     item->InitFunc = InitFunc;
3108     item->FreeFunc = FreeFunc;
3109     item->data = data;
3110     item->name = name;
3111 
3112     item->next = de_ctx->keyword_list;
3113     de_ctx->keyword_list = item;
3114     item->id = de_ctx->keyword_id++;
3115 
3116     return item->id;
3117 }
3118 
3119 /** \brief Remove Thread keyword context registration
3120  *
3121  *  \param de_ctx detection engine to deregister from
3122  *  \param det_ctx detection engine thread context to deregister from
3123  *  \param data keyword init data to pass to Func. Can be NULL.
3124  *  \param name keyword name for error printing
3125  *
3126  *  \retval 1 Item unregistered
3127  *  \retval 0 otherwise
3128  *
3129  *  \note make sure "data" remains valid and it free'd elsewhere. It's
3130  *        recommended to store it in the keywords global ctx so that
3131  *        it's freed when the de_ctx is freed.
3132  */
DetectUnregisterThreadCtxFuncs(DetectEngineCtx * de_ctx,DetectEngineThreadCtx * det_ctx,void * data,const char * name)3133 int DetectUnregisterThreadCtxFuncs(DetectEngineCtx *de_ctx,
3134         DetectEngineThreadCtx *det_ctx, void *data, const char *name)
3135 {
3136     BUG_ON(de_ctx == NULL);
3137 
3138     DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list;
3139     DetectEngineThreadKeywordCtxItem *prev_item = NULL;
3140     while (item != NULL) {
3141         if (strcmp(name, item->name) == 0 && (data == item->data)) {
3142             if (prev_item == NULL)
3143                 de_ctx->keyword_list = item->next;
3144             else
3145                 prev_item->next = item->next;
3146             if (det_ctx)
3147                 item->FreeFunc(det_ctx->keyword_ctxs_array[item->id]);
3148             SCFree(item);
3149             return 1;
3150         }
3151         prev_item = item;
3152         item = item->next;
3153     }
3154     return 0;
3155 }
3156 /** \brief Retrieve thread local keyword ctx by id
3157  *
3158  *  \param det_ctx detection engine thread ctx to retrieve the ctx from
3159  *  \param id id of the ctx returned by DetectRegisterThreadCtxInitFunc at
3160  *            keyword init.
3161  *
3162  *  \retval ctx or NULL on error
3163  */
DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx * det_ctx,int id)3164 void *DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx *det_ctx, int id)
3165 {
3166     if (id < 0 || id > det_ctx->keyword_ctxs_size || det_ctx->keyword_ctxs_array == NULL)
3167         return NULL;
3168 
3169     return det_ctx->keyword_ctxs_array[id];
3170 }
3171 
3172 
3173 /** \brief Register Thread keyword context Funcs (Global)
3174  *
3175  *  IDs stay static over reloads and between tenants
3176  *
3177  *  \param name keyword name for error printing
3178  *  \param InitFunc function ptr
3179  *  \param FreeFunc function ptr
3180  *
3181  *  \retval id for retrieval of ctx at runtime
3182  *  \retval -1 on error
3183  */
DetectRegisterThreadCtxGlobalFuncs(const char * name,void * (* InitFunc)(void *),void * data,void (* FreeFunc)(void *))3184 int DetectRegisterThreadCtxGlobalFuncs(const char *name,
3185         void *(*InitFunc)(void *), void *data, void (*FreeFunc)(void *))
3186 {
3187     int id;
3188     BUG_ON(InitFunc == NULL || FreeFunc == NULL);
3189 
3190     DetectEngineMasterCtx *master = &g_master_de_ctx;
3191 
3192     /* if already registered, return existing id */
3193     DetectEngineThreadKeywordCtxItem *item = master->keyword_list;
3194     while (item != NULL) {
3195         if (strcmp(name, item->name) == 0) {
3196             id = item->id;
3197             return id;
3198         }
3199 
3200         item = item->next;
3201     }
3202 
3203     item = SCCalloc(1, sizeof(*item));
3204     if (unlikely(item == NULL)) {
3205         return -1;
3206     }
3207     item->InitFunc = InitFunc;
3208     item->FreeFunc = FreeFunc;
3209     item->name = name;
3210     item->data = data;
3211 
3212     item->next = master->keyword_list;
3213     master->keyword_list = item;
3214     item->id = master->keyword_id++;
3215 
3216     id = item->id;
3217     return id;
3218 }
3219 
3220 /** \brief Retrieve thread local keyword ctx by id
3221  *
3222  *  \param det_ctx detection engine thread ctx to retrieve the ctx from
3223  *  \param id id of the ctx returned by DetectRegisterThreadCtxInitFunc at
3224  *            keyword init.
3225  *
3226  *  \retval ctx or NULL on error
3227  */
DetectThreadCtxGetGlobalKeywordThreadCtx(DetectEngineThreadCtx * det_ctx,int id)3228 void *DetectThreadCtxGetGlobalKeywordThreadCtx(DetectEngineThreadCtx *det_ctx, int id)
3229 {
3230     if (id < 0 || id > det_ctx->global_keyword_ctxs_size ||
3231         det_ctx->global_keyword_ctxs_array == NULL) {
3232         return NULL;
3233     }
3234 
3235     return det_ctx->global_keyword_ctxs_array[id];
3236 }
3237 
3238 /** \brief Check if detection is enabled
3239  *  \retval bool true or false */
DetectEngineEnabled(void)3240 int DetectEngineEnabled(void)
3241 {
3242     DetectEngineMasterCtx *master = &g_master_de_ctx;
3243     SCMutexLock(&master->lock);
3244 
3245     if (master->list == NULL) {
3246         SCMutexUnlock(&master->lock);
3247         return 0;
3248     }
3249 
3250     SCMutexUnlock(&master->lock);
3251     return 1;
3252 }
3253 
DetectEngineGetVersion(void)3254 uint32_t DetectEngineGetVersion(void)
3255 {
3256     uint32_t version;
3257     DetectEngineMasterCtx *master = &g_master_de_ctx;
3258     SCMutexLock(&master->lock);
3259     version = master->version;
3260     SCMutexUnlock(&master->lock);
3261     return version;
3262 }
3263 
DetectEngineBumpVersion(void)3264 void DetectEngineBumpVersion(void)
3265 {
3266     DetectEngineMasterCtx *master = &g_master_de_ctx;
3267     SCMutexLock(&master->lock);
3268     master->version++;
3269     SCLogDebug("master version now %u", master->version);
3270     SCMutexUnlock(&master->lock);
3271 }
3272 
DetectEngineGetCurrent(void)3273 DetectEngineCtx *DetectEngineGetCurrent(void)
3274 {
3275     DetectEngineMasterCtx *master = &g_master_de_ctx;
3276     SCMutexLock(&master->lock);
3277 
3278     DetectEngineCtx *de_ctx = master->list;
3279     while (de_ctx) {
3280         if (de_ctx->type == DETECT_ENGINE_TYPE_NORMAL ||
3281             de_ctx->type == DETECT_ENGINE_TYPE_DD_STUB ||
3282             de_ctx->type == DETECT_ENGINE_TYPE_MT_STUB)
3283         {
3284             de_ctx->ref_cnt++;
3285             SCLogDebug("de_ctx %p ref_cnt %u", de_ctx, de_ctx->ref_cnt);
3286             SCMutexUnlock(&master->lock);
3287             return de_ctx;
3288         }
3289         de_ctx = de_ctx->next;
3290     }
3291 
3292     SCMutexUnlock(&master->lock);
3293     return NULL;
3294 }
3295 
DetectEngineReference(DetectEngineCtx * de_ctx)3296 DetectEngineCtx *DetectEngineReference(DetectEngineCtx *de_ctx)
3297 {
3298     if (de_ctx == NULL)
3299         return NULL;
3300     de_ctx->ref_cnt++;
3301     return de_ctx;
3302 }
3303 
3304 /** TODO locking? Not needed if this is a one time setting at startup */
DetectEngineMultiTenantEnabled(void)3305 int DetectEngineMultiTenantEnabled(void)
3306 {
3307     DetectEngineMasterCtx *master = &g_master_de_ctx;
3308     return (master->multi_tenant_enabled);
3309 }
3310 
3311 /** \internal
3312  *  \brief load a tenant from a yaml file
3313  *
3314  *  \param tenant_id the tenant id by which the config is known
3315  *  \param filename full path of a yaml file
3316  *  \param loader_id id of loader thread or -1
3317  *
3318  *  \retval 0 ok
3319  *  \retval -1 failed
3320  */
DetectEngineMultiTenantLoadTenant(uint32_t tenant_id,const char * filename,int loader_id)3321 static int DetectEngineMultiTenantLoadTenant(uint32_t tenant_id, const char *filename, int loader_id)
3322 {
3323     DetectEngineCtx *de_ctx = NULL;
3324     char prefix[64];
3325 
3326     snprintf(prefix, sizeof(prefix), "multi-detect.%d", tenant_id);
3327 
3328 #ifdef OS_WIN32
3329     struct _stat st;
3330     if(_stat(filename, &st) != 0) {
3331 #else
3332     struct stat st;
3333     if(stat(filename, &st) != 0) {
3334 #endif /* OS_WIN32 */
3335         SCLogError(SC_ERR_FOPEN, "failed to stat file %s", filename);
3336         goto error;
3337     }
3338 
3339     de_ctx = DetectEngineGetByTenantId(tenant_id);
3340     if (de_ctx != NULL) {
3341         SCLogError(SC_ERR_MT_DUPLICATE_TENANT, "tenant %u already registered",
3342                 tenant_id);
3343         DetectEngineDeReference(&de_ctx);
3344         goto error;
3345     }
3346 
3347     ConfNode *node = ConfGetNode(prefix);
3348     if (node == NULL) {
3349         SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to properly setup yaml %s", filename);
3350         goto error;
3351     }
3352 
3353     de_ctx = DetectEngineCtxInitWithPrefix(prefix);
3354     if (de_ctx == NULL) {
3355         SCLogError(SC_ERR_INITIALIZATION, "initializing detection engine "
3356                 "context failed.");
3357         goto error;
3358     }
3359     SCLogDebug("de_ctx %p with prefix %s", de_ctx, de_ctx->config_prefix);
3360 
3361     de_ctx->type = DETECT_ENGINE_TYPE_TENANT;
3362     de_ctx->tenant_id = tenant_id;
3363     de_ctx->loader_id = loader_id;
3364 
3365     if (SigLoadSignatures(de_ctx, NULL, 0) < 0) {
3366         SCLogError(SC_ERR_NO_RULES_LOADED, "Loading signatures failed.");
3367         goto error;
3368     }
3369 
3370     DetectEngineAddToMaster(de_ctx);
3371 
3372     return 0;
3373 
3374 error:
3375     if (de_ctx != NULL) {
3376         DetectEngineCtxFree(de_ctx);
3377     }
3378     return -1;
3379 }
3380 
3381 static int DetectEngineMultiTenantReloadTenant(uint32_t tenant_id, const char *filename, int reload_cnt)
3382 {
3383     DetectEngineCtx *old_de_ctx = DetectEngineGetByTenantId(tenant_id);
3384     if (old_de_ctx == NULL) {
3385         SCLogError(SC_ERR_INITIALIZATION, "tenant detect engine not found");
3386         return -1;
3387     }
3388 
3389     char prefix[64];
3390     snprintf(prefix, sizeof(prefix), "multi-detect.%d.reload.%d", tenant_id, reload_cnt);
3391     reload_cnt++;
3392     SCLogDebug("prefix %s", prefix);
3393 
3394     if (ConfYamlLoadFileWithPrefix(filename, prefix) != 0) {
3395         SCLogError(SC_ERR_INITIALIZATION,"failed to load yaml");
3396         goto error;
3397     }
3398 
3399     ConfNode *node = ConfGetNode(prefix);
3400     if (node == NULL) {
3401         SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to properly setup yaml %s", filename);
3402         goto error;
3403     }
3404 
3405     DetectEngineCtx *new_de_ctx = DetectEngineCtxInitWithPrefix(prefix);
3406     if (new_de_ctx == NULL) {
3407         SCLogError(SC_ERR_INITIALIZATION, "initializing detection engine "
3408                 "context failed.");
3409         goto error;
3410     }
3411     SCLogDebug("de_ctx %p with prefix %s", new_de_ctx, new_de_ctx->config_prefix);
3412 
3413     new_de_ctx->type = DETECT_ENGINE_TYPE_TENANT;
3414     new_de_ctx->tenant_id = tenant_id;
3415     new_de_ctx->loader_id = old_de_ctx->loader_id;
3416 
3417     if (SigLoadSignatures(new_de_ctx, NULL, 0) < 0) {
3418         SCLogError(SC_ERR_NO_RULES_LOADED, "Loading signatures failed.");
3419         goto error;
3420     }
3421 
3422     DetectEngineAddToMaster(new_de_ctx);
3423 
3424     /* move to free list */
3425     DetectEngineMoveToFreeList(old_de_ctx);
3426     DetectEngineDeReference(&old_de_ctx);
3427     return 0;
3428 
3429 error:
3430     DetectEngineDeReference(&old_de_ctx);
3431     return -1;
3432 }
3433 
3434 
3435 typedef struct TenantLoaderCtx_ {
3436     uint32_t tenant_id;
3437     int reload_cnt; /**< used by reload */
3438     const char *yaml;
3439 } TenantLoaderCtx;
3440 
3441 static int DetectLoaderFuncLoadTenant(void *vctx, int loader_id)
3442 {
3443     TenantLoaderCtx *ctx = (TenantLoaderCtx *)vctx;
3444 
3445     SCLogDebug("loader %d", loader_id);
3446     if (DetectEngineMultiTenantLoadTenant(ctx->tenant_id, ctx->yaml, loader_id) != 0) {
3447         return -1;
3448     }
3449     return 0;
3450 }
3451 
3452 static int DetectLoaderSetupLoadTenant(uint32_t tenant_id, const char *yaml)
3453 {
3454     TenantLoaderCtx *t = SCCalloc(1, sizeof(*t));
3455     if (t == NULL)
3456         return -ENOMEM;
3457 
3458     t->tenant_id = tenant_id;
3459     t->yaml = yaml;
3460 
3461     return DetectLoaderQueueTask(-1, DetectLoaderFuncLoadTenant, t);
3462 }
3463 
3464 static int DetectLoaderFuncReloadTenant(void *vctx, int loader_id)
3465 {
3466     TenantLoaderCtx *ctx = (TenantLoaderCtx *)vctx;
3467 
3468     SCLogDebug("loader_id %d", loader_id);
3469 
3470     if (DetectEngineMultiTenantReloadTenant(ctx->tenant_id, ctx->yaml, ctx->reload_cnt) != 0) {
3471         return -1;
3472     }
3473     return 0;
3474 }
3475 
3476 static int DetectLoaderSetupReloadTenant(uint32_t tenant_id, const char *yaml, int reload_cnt)
3477 {
3478     DetectEngineCtx *old_de_ctx = DetectEngineGetByTenantId(tenant_id);
3479     if (old_de_ctx == NULL)
3480         return -ENOENT;
3481     int loader_id = old_de_ctx->loader_id;
3482     DetectEngineDeReference(&old_de_ctx);
3483 
3484     TenantLoaderCtx *t = SCCalloc(1, sizeof(*t));
3485     if (t == NULL)
3486         return -ENOMEM;
3487 
3488     t->tenant_id = tenant_id;
3489     t->yaml = yaml;
3490     t->reload_cnt = reload_cnt;
3491 
3492     SCLogDebug("loader_id %d", loader_id);
3493 
3494     return DetectLoaderQueueTask(loader_id, DetectLoaderFuncReloadTenant, t);
3495 }
3496 
3497 /** \brief Load a tenant and wait for loading to complete
3498  */
3499 int DetectEngineLoadTenantBlocking(uint32_t tenant_id, const char *yaml)
3500 {
3501     int r = DetectLoaderSetupLoadTenant(tenant_id, yaml);
3502     if (r < 0)
3503         return r;
3504 
3505     if (DetectLoadersSync() != 0)
3506         return -1;
3507 
3508     return 0;
3509 }
3510 
3511 /** \brief Reload a tenant and wait for loading to complete
3512  */
3513 int DetectEngineReloadTenantBlocking(uint32_t tenant_id, const char *yaml, int reload_cnt)
3514 {
3515     int r = DetectLoaderSetupReloadTenant(tenant_id, yaml, reload_cnt);
3516     if (r < 0)
3517         return r;
3518 
3519     if (DetectLoadersSync() != 0)
3520         return -1;
3521 
3522     return 0;
3523 }
3524 
3525 static int DetectEngineMultiTenantSetupLoadLivedevMappings(const ConfNode *mappings_root_node,
3526         bool failure_fatal)
3527 {
3528     ConfNode *mapping_node = NULL;
3529 
3530     int mapping_cnt = 0;
3531     if (mappings_root_node != NULL) {
3532         TAILQ_FOREACH(mapping_node, &mappings_root_node->head, next) {
3533             ConfNode *tenant_id_node = ConfNodeLookupChild(mapping_node, "tenant-id");
3534             if (tenant_id_node == NULL)
3535                 goto bad_mapping;
3536             ConfNode *device_node = ConfNodeLookupChild(mapping_node, "device");
3537             if (device_node == NULL)
3538                 goto bad_mapping;
3539 
3540             uint32_t tenant_id = 0;
3541             if (StringParseUint32(&tenant_id, 10, strlen(tenant_id_node->val),
3542                         tenant_id_node->val) < 0)
3543             {
3544                 SCLogError(SC_ERR_INVALID_ARGUMENT, "tenant-id  "
3545                         "of %s is invalid", tenant_id_node->val);
3546                 goto bad_mapping;
3547             }
3548 
3549             const char *dev = device_node->val;
3550             LiveDevice *ld = LiveGetDevice(dev);
3551             if (ld == NULL) {
3552                 SCLogWarning(SC_ERR_MT_NO_MAPPING, "device %s not found", dev);
3553                 goto bad_mapping;
3554             }
3555 
3556             if (ld->tenant_id_set) {
3557                 SCLogWarning(SC_ERR_MT_NO_MAPPING, "device %s already mapped to tenant-id %u",
3558                         dev, ld->tenant_id);
3559                 goto bad_mapping;
3560             }
3561 
3562             ld->tenant_id = tenant_id;
3563             ld->tenant_id_set = true;
3564 
3565             if (DetectEngineTentantRegisterLivedev(tenant_id, ld->id) != 0) {
3566                 goto error;
3567             }
3568 
3569             SCLogConfig("device %s connected to tenant-id %u", dev, tenant_id);
3570             mapping_cnt++;
3571             continue;
3572 
3573         bad_mapping:
3574             if (failure_fatal)
3575                 goto error;
3576         }
3577     }
3578     SCLogConfig("%d device - tenant-id mappings defined", mapping_cnt);
3579     return mapping_cnt;
3580 
3581 error:
3582     return 0;
3583 }
3584 
3585 static int DetectEngineMultiTenantSetupLoadVlanMappings(const ConfNode *mappings_root_node,
3586         bool failure_fatal)
3587 {
3588     ConfNode *mapping_node = NULL;
3589 
3590     int mapping_cnt = 0;
3591     if (mappings_root_node != NULL) {
3592         TAILQ_FOREACH(mapping_node, &mappings_root_node->head, next) {
3593             ConfNode *tenant_id_node = ConfNodeLookupChild(mapping_node, "tenant-id");
3594             if (tenant_id_node == NULL)
3595                 goto bad_mapping;
3596             ConfNode *vlan_id_node = ConfNodeLookupChild(mapping_node, "vlan-id");
3597             if (vlan_id_node == NULL)
3598                 goto bad_mapping;
3599 
3600             uint32_t tenant_id = 0;
3601             if (StringParseUint32(&tenant_id, 10, strlen(tenant_id_node->val),
3602                         tenant_id_node->val) < 0)
3603             {
3604                 SCLogError(SC_ERR_INVALID_ARGUMENT, "tenant-id  "
3605                         "of %s is invalid", tenant_id_node->val);
3606                 goto bad_mapping;
3607             }
3608 
3609             uint16_t vlan_id = 0;
3610             if (StringParseUint16(&vlan_id, 10, strlen(vlan_id_node->val),
3611                         vlan_id_node->val) < 0)
3612             {
3613                 SCLogError(SC_ERR_INVALID_ARGUMENT, "vlan-id  "
3614                         "of %s is invalid", vlan_id_node->val);
3615                 goto bad_mapping;
3616             }
3617             if (vlan_id == 0 || vlan_id >= 4095) {
3618                 SCLogError(SC_ERR_INVALID_ARGUMENT, "vlan-id  "
3619                         "of %s is invalid. Valid range 1-4094.", vlan_id_node->val);
3620                 goto bad_mapping;
3621             }
3622 
3623             if (DetectEngineTentantRegisterVlanId(tenant_id, (uint32_t)vlan_id) != 0) {
3624                 goto error;
3625             }
3626             SCLogConfig("vlan %u connected to tenant-id %u", vlan_id, tenant_id);
3627             mapping_cnt++;
3628             continue;
3629 
3630         bad_mapping:
3631             if (failure_fatal)
3632                 goto error;
3633         }
3634     }
3635     return mapping_cnt;
3636 
3637 error:
3638     return 0;
3639 }
3640 
3641 /**
3642  *  \brief setup multi-detect / multi-tenancy
3643  *
3644  *  See if MT is enabled. If so, setup the selector, tenants and mappings.
3645  *  Tenants and mappings are optional, and can also dynamically be added
3646  *  and removed from the unix socket.
3647  */
3648 int DetectEngineMultiTenantSetup(void)
3649 {
3650     enum DetectEngineTenantSelectors tenant_selector = TENANT_SELECTOR_UNKNOWN;
3651     DetectEngineMasterCtx *master = &g_master_de_ctx;
3652 
3653     int unix_socket = ConfUnixSocketIsEnable();
3654 
3655     int failure_fatal = 0;
3656     (void)ConfGetBool("engine.init-failure-fatal", &failure_fatal);
3657 
3658     int enabled = 0;
3659     (void)ConfGetBool("multi-detect.enabled", &enabled);
3660     if (enabled == 1) {
3661         DetectLoadersInit();
3662         TmModuleDetectLoaderRegister();
3663         DetectLoaderThreadSpawn();
3664         TmThreadContinueDetectLoaderThreads();
3665 
3666         SCMutexLock(&master->lock);
3667         master->multi_tenant_enabled = 1;
3668 
3669         const char *handler = NULL;
3670         if (ConfGet("multi-detect.selector", &handler) == 1) {
3671             SCLogConfig("multi-tenant selector type %s", handler);
3672 
3673             if (strcmp(handler, "vlan") == 0) {
3674                 tenant_selector = master->tenant_selector = TENANT_SELECTOR_VLAN;
3675 
3676                 int vlanbool = 0;
3677                 if ((ConfGetBool("vlan.use-for-tracking", &vlanbool)) == 1 && vlanbool == 0) {
3678                     SCLogError(SC_ERR_INVALID_VALUE, "vlan tracking is disabled, "
3679                             "can't use multi-detect selector 'vlan'");
3680                     SCMutexUnlock(&master->lock);
3681                     goto error;
3682                 }
3683 
3684             } else if (strcmp(handler, "direct") == 0) {
3685                 tenant_selector = master->tenant_selector = TENANT_SELECTOR_DIRECT;
3686             } else if (strcmp(handler, "device") == 0) {
3687                 tenant_selector = master->tenant_selector = TENANT_SELECTOR_LIVEDEV;
3688                 if (EngineModeIsIPS()) {
3689                     SCLogWarning(SC_ERR_MT_NO_MAPPING,
3690                             "multi-tenant 'device' mode not supported for IPS");
3691                     SCMutexUnlock(&master->lock);
3692                     goto error;
3693                 }
3694 
3695             } else {
3696                 SCLogError(SC_ERR_INVALID_VALUE, "unknown value %s "
3697                                                  "multi-detect.selector", handler);
3698                 SCMutexUnlock(&master->lock);
3699                 goto error;
3700             }
3701         }
3702         SCMutexUnlock(&master->lock);
3703         SCLogConfig("multi-detect is enabled (multi tenancy). Selector: %s", handler);
3704 
3705         /* traffic -- tenant mappings */
3706         ConfNode *mappings_root_node = ConfGetNode("multi-detect.mappings");
3707 
3708         if (tenant_selector == TENANT_SELECTOR_VLAN) {
3709             int mapping_cnt = DetectEngineMultiTenantSetupLoadVlanMappings(mappings_root_node,
3710                     failure_fatal);
3711             if (mapping_cnt == 0) {
3712                 /* no mappings are valid when we're in unix socket mode,
3713                  * they can be added on the fly. Otherwise warn/error
3714                  * depending on failure_fatal */
3715 
3716                 if (unix_socket) {
3717                     SCLogNotice("no tenant traffic mappings defined, "
3718                             "tenants won't be used until mappings are added");
3719                 } else {
3720                     if (failure_fatal) {
3721                         SCLogError(SC_ERR_MT_NO_MAPPING, "no multi-detect mappings defined");
3722                         goto error;
3723                     } else {
3724                         SCLogWarning(SC_ERR_MT_NO_MAPPING, "no multi-detect mappings defined");
3725                     }
3726                 }
3727             }
3728         } else if (tenant_selector == TENANT_SELECTOR_LIVEDEV) {
3729             int mapping_cnt = DetectEngineMultiTenantSetupLoadLivedevMappings(mappings_root_node,
3730                     failure_fatal);
3731             if (mapping_cnt == 0) {
3732                 if (failure_fatal) {
3733                     SCLogError(SC_ERR_MT_NO_MAPPING, "no multi-detect mappings defined");
3734                     goto error;
3735                 } else {
3736                     SCLogWarning(SC_ERR_MT_NO_MAPPING, "no multi-detect mappings defined");
3737                 }
3738             }
3739         }
3740 
3741         /* tenants */
3742         ConfNode *tenants_root_node = ConfGetNode("multi-detect.tenants");
3743         ConfNode *tenant_node = NULL;
3744 
3745         if (tenants_root_node != NULL) {
3746             TAILQ_FOREACH(tenant_node, &tenants_root_node->head, next) {
3747                 ConfNode *id_node = ConfNodeLookupChild(tenant_node, "id");
3748                 if (id_node == NULL) {
3749                     goto bad_tenant;
3750                 }
3751                 ConfNode *yaml_node = ConfNodeLookupChild(tenant_node, "yaml");
3752                 if (yaml_node == NULL) {
3753                     goto bad_tenant;
3754                 }
3755 
3756                 uint32_t tenant_id = 0;
3757                 if (StringParseUint32(&tenant_id, 10, strlen(id_node->val),
3758                             id_node->val) < 0)
3759                 {
3760                     SCLogError(SC_ERR_INVALID_ARGUMENT, "tenant_id  "
3761                             "of %s is invalid", id_node->val);
3762                     goto bad_tenant;
3763                 }
3764                 SCLogDebug("tenant id: %u, %s", tenant_id, yaml_node->val);
3765 
3766                 /* setup the yaml in this loop so that it's not done by the loader
3767                  * threads. ConfYamlLoadFileWithPrefix is not thread safe. */
3768                 char prefix[64];
3769                 snprintf(prefix, sizeof(prefix), "multi-detect.%d", tenant_id);
3770                 if (ConfYamlLoadFileWithPrefix(yaml_node->val, prefix) != 0) {
3771                     SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to load yaml %s", yaml_node->val);
3772                     goto bad_tenant;
3773                 }
3774 
3775                 int r = DetectLoaderSetupLoadTenant(tenant_id, yaml_node->val);
3776                 if (r < 0) {
3777                     /* error logged already */
3778                     goto bad_tenant;
3779                 }
3780                 continue;
3781 
3782             bad_tenant:
3783                 if (failure_fatal)
3784                     goto error;
3785             }
3786         }
3787 
3788         /* wait for our loaders to complete their tasks */
3789         if (DetectLoadersSync() != 0) {
3790             goto error;
3791         }
3792 
3793         VarNameStoreActivateStaging();
3794 
3795     } else {
3796         SCLogDebug("multi-detect not enabled (multi tenancy)");
3797     }
3798     return 0;
3799 error:
3800     return -1;
3801 }
3802 
3803 static uint32_t DetectEngineTentantGetIdFromVlanId(const void *ctx, const Packet *p)
3804 {
3805     const DetectEngineThreadCtx *det_ctx = ctx;
3806     uint32_t x = 0;
3807     uint32_t vlan_id = 0;
3808 
3809     if (p->vlan_idx == 0)
3810         return 0;
3811 
3812     vlan_id = p->vlan_id[0];
3813 
3814     if (det_ctx == NULL || det_ctx->tenant_array == NULL || det_ctx->tenant_array_size == 0)
3815         return 0;
3816 
3817     /* not very efficient, but for now we're targeting only limited amounts.
3818      * Can use hash/tree approach later. */
3819     for (x = 0; x < det_ctx->tenant_array_size; x++) {
3820         if (det_ctx->tenant_array[x].traffic_id == vlan_id)
3821             return det_ctx->tenant_array[x].tenant_id;
3822     }
3823 
3824     return 0;
3825 }
3826 
3827 static uint32_t DetectEngineTentantGetIdFromLivedev(const void *ctx, const Packet *p)
3828 {
3829     const DetectEngineThreadCtx *det_ctx = ctx;
3830     const LiveDevice *ld = p->livedev;
3831 
3832     if (ld == NULL || det_ctx == NULL)
3833         return 0;
3834 
3835     SCLogDebug("using tenant-id %u for packet on device %s", ld->tenant_id, ld->dev);
3836     return ld->tenant_id;
3837 }
3838 
3839 static int DetectEngineTentantRegisterSelector(enum DetectEngineTenantSelectors selector,
3840                                            uint32_t tenant_id, uint32_t traffic_id)
3841 {
3842     DetectEngineMasterCtx *master = &g_master_de_ctx;
3843     SCMutexLock(&master->lock);
3844 
3845     if (!(master->tenant_selector == TENANT_SELECTOR_UNKNOWN || master->tenant_selector == selector)) {
3846         SCLogInfo("conflicting selector already set");
3847         SCMutexUnlock(&master->lock);
3848         return -1;
3849     }
3850 
3851     DetectEngineTenantMapping *m = master->tenant_mapping_list;
3852     while (m) {
3853         if (m->traffic_id == traffic_id) {
3854             SCLogInfo("traffic id already registered");
3855             SCMutexUnlock(&master->lock);
3856             return -1;
3857         }
3858         m = m->next;
3859     }
3860 
3861     DetectEngineTenantMapping *map = SCCalloc(1, sizeof(*map));
3862     if (map == NULL) {
3863         SCLogInfo("memory fail");
3864         SCMutexUnlock(&master->lock);
3865         return -1;
3866     }
3867     map->traffic_id = traffic_id;
3868     map->tenant_id = tenant_id;
3869 
3870     map->next = master->tenant_mapping_list;
3871     master->tenant_mapping_list = map;
3872 
3873     master->tenant_selector = selector;
3874 
3875     SCLogDebug("tenant handler %u %u %u registered", selector, tenant_id, traffic_id);
3876     SCMutexUnlock(&master->lock);
3877     return 0;
3878 }
3879 
3880 static int DetectEngineTentantUnregisterSelector(enum DetectEngineTenantSelectors selector,
3881                                            uint32_t tenant_id, uint32_t traffic_id)
3882 {
3883     DetectEngineMasterCtx *master = &g_master_de_ctx;
3884     SCMutexLock(&master->lock);
3885 
3886     if (master->tenant_mapping_list == NULL) {
3887         SCMutexUnlock(&master->lock);
3888         return -1;
3889     }
3890 
3891     DetectEngineTenantMapping *prev = NULL;
3892     DetectEngineTenantMapping *map = master->tenant_mapping_list;
3893     while (map) {
3894         if (map->traffic_id == traffic_id &&
3895             map->tenant_id == tenant_id)
3896         {
3897             if (prev != NULL)
3898                 prev->next = map->next;
3899             else
3900                 master->tenant_mapping_list = map->next;
3901 
3902             map->next = NULL;
3903             SCFree(map);
3904             SCLogInfo("tenant handler %u %u %u unregistered", selector, tenant_id, traffic_id);
3905             SCMutexUnlock(&master->lock);
3906             return 0;
3907         }
3908         prev = map;
3909         map = map->next;
3910     }
3911 
3912     SCMutexUnlock(&master->lock);
3913     return -1;
3914 }
3915 
3916 int DetectEngineTentantRegisterLivedev(uint32_t tenant_id, int device_id)
3917 {
3918     return DetectEngineTentantRegisterSelector(TENANT_SELECTOR_LIVEDEV, tenant_id, (uint32_t)device_id);
3919 }
3920 
3921 int DetectEngineTentantRegisterVlanId(uint32_t tenant_id, uint16_t vlan_id)
3922 {
3923     return DetectEngineTentantRegisterSelector(TENANT_SELECTOR_VLAN, tenant_id, (uint32_t)vlan_id);
3924 }
3925 
3926 int DetectEngineTentantUnregisterVlanId(uint32_t tenant_id, uint16_t vlan_id)
3927 {
3928     return DetectEngineTentantUnregisterSelector(TENANT_SELECTOR_VLAN, tenant_id, (uint32_t)vlan_id);
3929 }
3930 
3931 int DetectEngineTentantRegisterPcapFile(uint32_t tenant_id)
3932 {
3933     SCLogInfo("registering %u %d 0", TENANT_SELECTOR_DIRECT, tenant_id);
3934     return DetectEngineTentantRegisterSelector(TENANT_SELECTOR_DIRECT, tenant_id, 0);
3935 }
3936 
3937 int DetectEngineTentantUnregisterPcapFile(uint32_t tenant_id)
3938 {
3939     SCLogInfo("unregistering %u %d 0", TENANT_SELECTOR_DIRECT, tenant_id);
3940     return DetectEngineTentantUnregisterSelector(TENANT_SELECTOR_DIRECT, tenant_id, 0);
3941 }
3942 
3943 static uint32_t DetectEngineTentantGetIdFromPcap(const void *ctx, const Packet *p)
3944 {
3945     return p->pcap_v.tenant_id;
3946 }
3947 
3948 DetectEngineCtx *DetectEngineGetByTenantId(int tenant_id)
3949 {
3950     DetectEngineMasterCtx *master = &g_master_de_ctx;
3951     SCMutexLock(&master->lock);
3952 
3953     if (master->list == NULL) {
3954         SCMutexUnlock(&master->lock);
3955         return NULL;
3956     }
3957 
3958     DetectEngineCtx *de_ctx = master->list;
3959     while (de_ctx) {
3960         if (de_ctx->type == DETECT_ENGINE_TYPE_TENANT &&
3961                 de_ctx->tenant_id == tenant_id)
3962         {
3963             de_ctx->ref_cnt++;
3964             break;
3965         }
3966 
3967         de_ctx = de_ctx->next;
3968     }
3969 
3970     SCMutexUnlock(&master->lock);
3971     return de_ctx;
3972 }
3973 
3974 void DetectEngineDeReference(DetectEngineCtx **de_ctx)
3975 {
3976     BUG_ON((*de_ctx)->ref_cnt == 0);
3977     (*de_ctx)->ref_cnt--;
3978     *de_ctx = NULL;
3979 }
3980 
3981 static int DetectEngineAddToList(DetectEngineCtx *instance)
3982 {
3983     DetectEngineMasterCtx *master = &g_master_de_ctx;
3984 
3985     if (instance == NULL)
3986         return -1;
3987 
3988     if (master->list == NULL) {
3989         master->list = instance;
3990     } else {
3991         instance->next = master->list;
3992         master->list = instance;
3993     }
3994 
3995     return 0;
3996 }
3997 
3998 int DetectEngineAddToMaster(DetectEngineCtx *de_ctx)
3999 {
4000     int r;
4001 
4002     if (de_ctx == NULL)
4003         return -1;
4004 
4005     SCLogDebug("adding de_ctx %p to master", de_ctx);
4006 
4007     DetectEngineMasterCtx *master = &g_master_de_ctx;
4008     SCMutexLock(&master->lock);
4009     r = DetectEngineAddToList(de_ctx);
4010     SCMutexUnlock(&master->lock);
4011     return r;
4012 }
4013 
4014 int DetectEngineMoveToFreeList(DetectEngineCtx *de_ctx)
4015 {
4016     DetectEngineMasterCtx *master = &g_master_de_ctx;
4017 
4018     SCMutexLock(&master->lock);
4019     DetectEngineCtx *instance = master->list;
4020     if (instance == NULL) {
4021         SCMutexUnlock(&master->lock);
4022         return -1;
4023     }
4024 
4025     /* remove from active list */
4026     if (instance == de_ctx) {
4027         master->list = instance->next;
4028     } else {
4029         DetectEngineCtx *prev = instance;
4030         instance = instance->next; /* already checked first element */
4031 
4032         while (instance) {
4033             DetectEngineCtx *next = instance->next;
4034 
4035             if (instance == de_ctx) {
4036                 prev->next = instance->next;
4037                 break;
4038             }
4039 
4040             prev = instance;
4041             instance = next;
4042         }
4043         if (instance == NULL) {
4044             SCMutexUnlock(&master->lock);
4045             return -1;
4046         }
4047     }
4048 
4049     /* instance is now detached from list */
4050     instance->next = NULL;
4051 
4052     /* add to free list */
4053     if (master->free_list == NULL) {
4054         master->free_list = instance;
4055     } else {
4056         instance->next = master->free_list;
4057         master->free_list = instance;
4058     }
4059     SCLogDebug("detect engine %p moved to free list (%u refs)", de_ctx, de_ctx->ref_cnt);
4060 
4061     SCMutexUnlock(&master->lock);
4062     return 0;
4063 }
4064 
4065 void DetectEnginePruneFreeList(void)
4066 {
4067     DetectEngineMasterCtx *master = &g_master_de_ctx;
4068     SCMutexLock(&master->lock);
4069 
4070     DetectEngineCtx *prev = NULL;
4071     DetectEngineCtx *instance = master->free_list;
4072     while (instance) {
4073         DetectEngineCtx *next = instance->next;
4074 
4075         SCLogDebug("detect engine %p has %u ref(s)", instance, instance->ref_cnt);
4076 
4077         if (instance->ref_cnt == 0) {
4078             if (prev == NULL) {
4079                 master->free_list = next;
4080             } else {
4081                 prev->next = next;
4082             }
4083 
4084             SCLogDebug("freeing detect engine %p", instance);
4085             DetectEngineCtxFree(instance);
4086             instance = NULL;
4087         }
4088 
4089         prev = instance;
4090         instance = next;
4091     }
4092     SCMutexUnlock(&master->lock);
4093 }
4094 
4095 static int reloads = 0;
4096 
4097 /** \brief Reload the detection engine
4098  *
4099  *  \param filename YAML file to load for the detect config
4100  *
4101  *  \retval -1 error
4102  *  \retval 0 ok
4103  */
4104 int DetectEngineReload(const SCInstance *suri)
4105 {
4106     DetectEngineCtx *new_de_ctx = NULL;
4107     DetectEngineCtx *old_de_ctx = NULL;
4108 
4109     char prefix[128];
4110     memset(prefix, 0, sizeof(prefix));
4111 
4112     SCLogNotice("rule reload starting");
4113 
4114     if (suri->conf_filename != NULL) {
4115         snprintf(prefix, sizeof(prefix), "detect-engine-reloads.%d", reloads++);
4116         if (ConfYamlLoadFileWithPrefix(suri->conf_filename, prefix) != 0) {
4117             SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to load yaml %s",
4118                     suri->conf_filename);
4119             return -1;
4120         }
4121 
4122         ConfNode *node = ConfGetNode(prefix);
4123         if (node == NULL) {
4124             SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to properly setup yaml %s",
4125                     suri->conf_filename);
4126             return -1;
4127         }
4128 #if 0
4129         ConfDump();
4130 #endif
4131     }
4132 
4133     /* get a reference to the current de_ctx */
4134     old_de_ctx = DetectEngineGetCurrent();
4135     if (old_de_ctx == NULL)
4136         return -1;
4137     SCLogDebug("get ref to old_de_ctx %p", old_de_ctx);
4138     DatasetReload();
4139 
4140     /* only reload a regular 'normal' and 'delayed detect stub' detect engines */
4141     if (!(old_de_ctx->type == DETECT_ENGINE_TYPE_NORMAL ||
4142           old_de_ctx->type == DETECT_ENGINE_TYPE_DD_STUB))
4143     {
4144         DetectEngineDeReference(&old_de_ctx);
4145         SCLogNotice("rule reload complete");
4146         return -1;
4147     }
4148 
4149     /* get new detection engine */
4150     new_de_ctx = DetectEngineCtxInitWithPrefix(prefix);
4151     if (new_de_ctx == NULL) {
4152         SCLogError(SC_ERR_INITIALIZATION, "initializing detection engine "
4153                 "context failed.");
4154         DetectEngineDeReference(&old_de_ctx);
4155         return -1;
4156     }
4157     if (SigLoadSignatures(new_de_ctx,
4158                           suri->sig_file, suri->sig_file_exclusive) != 0) {
4159         DetectEngineCtxFree(new_de_ctx);
4160         DetectEngineDeReference(&old_de_ctx);
4161         return -1;
4162     }
4163     SCLogDebug("set up new_de_ctx %p", new_de_ctx);
4164 
4165     /* add to master */
4166     DetectEngineAddToMaster(new_de_ctx);
4167 
4168     /* move to old free list */
4169     DetectEngineMoveToFreeList(old_de_ctx);
4170     DetectEngineDeReference(&old_de_ctx);
4171 
4172     SCLogDebug("going to reload the threads to use new_de_ctx %p", new_de_ctx);
4173     /* update the threads */
4174     DetectEngineReloadThreads(new_de_ctx);
4175     SCLogDebug("threads now run new_de_ctx %p", new_de_ctx);
4176 
4177     /* walk free list, freeing the old_de_ctx */
4178     DetectEnginePruneFreeList();
4179 
4180     DatasetPostReloadCleanup();
4181 
4182     DetectEngineBumpVersion();
4183 
4184     SCLogDebug("old_de_ctx should have been freed");
4185 
4186     SCLogNotice("rule reload complete");
4187     return 0;
4188 }
4189 
4190 static uint32_t TenantIdHash(HashTable *h, void *data, uint16_t data_len)
4191 {
4192     DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
4193     return det_ctx->tenant_id % h->array_size;
4194 }
4195 
4196 static char TenantIdCompare(void *d1, uint16_t d1_len, void *d2, uint16_t d2_len)
4197 {
4198     DetectEngineThreadCtx *det1 = (DetectEngineThreadCtx *)d1;
4199     DetectEngineThreadCtx *det2 = (DetectEngineThreadCtx *)d2;
4200     return (det1->tenant_id == det2->tenant_id);
4201 }
4202 
4203 static void TenantIdFree(void *d)
4204 {
4205     DetectEngineThreadCtxFree(d);
4206 }
4207 
4208 int DetectEngineMTApply(void)
4209 {
4210     DetectEngineMasterCtx *master = &g_master_de_ctx;
4211     SCMutexLock(&master->lock);
4212 
4213     if (master->tenant_selector == TENANT_SELECTOR_UNKNOWN) {
4214         SCLogInfo("error, no tenant selector");
4215         SCMutexUnlock(&master->lock);
4216         return -1;
4217     }
4218 
4219     DetectEngineCtx *stub_de_ctx = NULL;
4220     DetectEngineCtx *list = master->list;
4221     for ( ; list != NULL; list = list->next) {
4222         SCLogDebug("list %p tenant %u", list, list->tenant_id);
4223 
4224         if (list->type == DETECT_ENGINE_TYPE_NORMAL ||
4225             list->type == DETECT_ENGINE_TYPE_MT_STUB ||
4226             list->type == DETECT_ENGINE_TYPE_DD_STUB)
4227         {
4228             stub_de_ctx = list;
4229             break;
4230         }
4231     }
4232     if (stub_de_ctx == NULL) {
4233         stub_de_ctx = DetectEngineCtxInitStubForMT();
4234         if (stub_de_ctx == NULL) {
4235             SCMutexUnlock(&master->lock);
4236             return -1;
4237         }
4238 
4239         if (master->list == NULL) {
4240             master->list = stub_de_ctx;
4241         } else {
4242             stub_de_ctx->next = master->list;
4243             master->list = stub_de_ctx;
4244         }
4245     }
4246 
4247     /* update the threads */
4248     SCLogDebug("MT reload starting");
4249     DetectEngineReloadThreads(stub_de_ctx);
4250     SCLogDebug("MT reload done");
4251 
4252     SCMutexUnlock(&master->lock);
4253 
4254     /* walk free list, freeing the old_de_ctx */
4255     DetectEnginePruneFreeList();
4256 
4257     SCLogDebug("old_de_ctx should have been freed");
4258     return 0;
4259 }
4260 
4261 static int g_parse_metadata = 0;
4262 
4263 void DetectEngineSetParseMetadata(void)
4264 {
4265     g_parse_metadata = 1;
4266 }
4267 
4268 void DetectEngineUnsetParseMetadata(void)
4269 {
4270     g_parse_metadata = 0;
4271 }
4272 
4273 int DetectEngineMustParseMetadata(void)
4274 {
4275     return g_parse_metadata;
4276 }
4277 
4278 const char *DetectSigmatchListEnumToString(enum DetectSigmatchListEnum type)
4279 {
4280     switch (type) {
4281         case DETECT_SM_LIST_MATCH:
4282             return "packet";
4283         case DETECT_SM_LIST_PMATCH:
4284             return "packet/stream payload";
4285 
4286         case DETECT_SM_LIST_TMATCH:
4287             return "tag";
4288 
4289         case DETECT_SM_LIST_BASE64_DATA:
4290             return "base64_data";
4291 
4292         case DETECT_SM_LIST_POSTMATCH:
4293             return "post-match";
4294 
4295         case DETECT_SM_LIST_SUPPRESS:
4296             return "suppress";
4297         case DETECT_SM_LIST_THRESHOLD:
4298             return "threshold";
4299 
4300         case DETECT_SM_LIST_MAX:
4301             return "max (internal)";
4302     }
4303     return "error";
4304 }
4305 
4306 /* events api */
4307 void DetectEngineSetEvent(DetectEngineThreadCtx *det_ctx, uint8_t e)
4308 {
4309     AppLayerDecoderEventsSetEventRaw(&det_ctx->decoder_events, e);
4310     det_ctx->events++;
4311 }
4312 
4313 AppLayerDecoderEvents *DetectEngineGetEvents(DetectEngineThreadCtx *det_ctx)
4314 {
4315     return det_ctx->decoder_events;
4316 }
4317 
4318 int DetectEngineGetEventInfo(const char *event_name, int *event_id,
4319                              AppLayerEventType *event_type)
4320 {
4321     *event_id = SCMapEnumNameToValue(event_name, det_ctx_event_table);
4322     if (*event_id == -1) {
4323         SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
4324                    "det_ctx's enum map table.",  event_name);
4325         /* this should be treated as fatal */
4326         return -1;
4327     }
4328     *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
4329 
4330     return 0;
4331 }
4332 
4333 /*************************************Unittest*********************************/
4334 
4335 #ifdef UNITTESTS
4336 
4337 static int DetectEngineInitYamlConf(const char *conf)
4338 {
4339     ConfCreateContextBackup();
4340     ConfInit();
4341     return ConfYamlLoadString(conf, strlen(conf));
4342 }
4343 
4344 static void DetectEngineDeInitYamlConf(void)
4345 {
4346     ConfDeInit();
4347     ConfRestoreContextBackup();
4348 
4349     return;
4350 }
4351 
4352 static int DetectEngineTest01(void)
4353 {
4354     const char *conf =
4355         "%YAML 1.1\n"
4356         "---\n"
4357         "detect-engine:\n"
4358         "  - profile: medium\n"
4359         "  - custom-values:\n"
4360         "      toclient_src_groups: 2\n"
4361         "      toclient_dst_groups: 2\n"
4362         "      toclient_sp_groups: 2\n"
4363         "      toclient_dp_groups: 3\n"
4364         "      toserver_src_groups: 2\n"
4365         "      toserver_dst_groups: 4\n"
4366         "      toserver_sp_groups: 2\n"
4367         "      toserver_dp_groups: 25\n"
4368         "  - inspection-recursion-limit: 0\n";
4369 
4370     DetectEngineCtx *de_ctx = NULL;
4371     int result = 0;
4372 
4373     if (DetectEngineInitYamlConf(conf) == -1)
4374         return 0;
4375     de_ctx = DetectEngineCtxInit();
4376     if (de_ctx == NULL)
4377         goto end;
4378 
4379     result = (de_ctx->inspection_recursion_limit == -1);
4380 
4381  end:
4382     if (de_ctx != NULL)
4383         DetectEngineCtxFree(de_ctx);
4384 
4385     DetectEngineDeInitYamlConf();
4386 
4387     return result;
4388 }
4389 
4390 static int DetectEngineTest02(void)
4391 {
4392     const char *conf =
4393         "%YAML 1.1\n"
4394         "---\n"
4395         "detect-engine:\n"
4396         "  - profile: medium\n"
4397         "  - custom-values:\n"
4398         "      toclient_src_groups: 2\n"
4399         "      toclient_dst_groups: 2\n"
4400         "      toclient_sp_groups: 2\n"
4401         "      toclient_dp_groups: 3\n"
4402         "      toserver_src_groups: 2\n"
4403         "      toserver_dst_groups: 4\n"
4404         "      toserver_sp_groups: 2\n"
4405         "      toserver_dp_groups: 25\n"
4406         "  - inspection-recursion-limit:\n";
4407 
4408     DetectEngineCtx *de_ctx = NULL;
4409     int result = 0;
4410 
4411     if (DetectEngineInitYamlConf(conf) == -1)
4412         return 0;
4413     de_ctx = DetectEngineCtxInit();
4414     if (de_ctx == NULL)
4415         goto end;
4416 
4417     result = (de_ctx->inspection_recursion_limit == DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT);
4418 
4419  end:
4420     if (de_ctx != NULL)
4421         DetectEngineCtxFree(de_ctx);
4422 
4423     DetectEngineDeInitYamlConf();
4424 
4425     return result;
4426 }
4427 
4428 static int DetectEngineTest03(void)
4429 {
4430     const char *conf =
4431         "%YAML 1.1\n"
4432         "---\n"
4433         "detect-engine:\n"
4434         "  - profile: medium\n"
4435         "  - custom-values:\n"
4436         "      toclient_src_groups: 2\n"
4437         "      toclient_dst_groups: 2\n"
4438         "      toclient_sp_groups: 2\n"
4439         "      toclient_dp_groups: 3\n"
4440         "      toserver_src_groups: 2\n"
4441         "      toserver_dst_groups: 4\n"
4442         "      toserver_sp_groups: 2\n"
4443         "      toserver_dp_groups: 25\n";
4444 
4445     DetectEngineCtx *de_ctx = NULL;
4446     int result = 0;
4447 
4448     if (DetectEngineInitYamlConf(conf) == -1)
4449         return 0;
4450     de_ctx = DetectEngineCtxInit();
4451     if (de_ctx == NULL)
4452         goto end;
4453 
4454     result = (de_ctx->inspection_recursion_limit ==
4455               DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT);
4456 
4457  end:
4458     if (de_ctx != NULL)
4459         DetectEngineCtxFree(de_ctx);
4460 
4461     DetectEngineDeInitYamlConf();
4462 
4463     return result;
4464 }
4465 
4466 static int DetectEngineTest04(void)
4467 {
4468     const char *conf =
4469         "%YAML 1.1\n"
4470         "---\n"
4471         "detect-engine:\n"
4472         "  - profile: medium\n"
4473         "  - custom-values:\n"
4474         "      toclient_src_groups: 2\n"
4475         "      toclient_dst_groups: 2\n"
4476         "      toclient_sp_groups: 2\n"
4477         "      toclient_dp_groups: 3\n"
4478         "      toserver_src_groups: 2\n"
4479         "      toserver_dst_groups: 4\n"
4480         "      toserver_sp_groups: 2\n"
4481         "      toserver_dp_groups: 25\n"
4482         "  - inspection-recursion-limit: 10\n";
4483 
4484     DetectEngineCtx *de_ctx = NULL;
4485     int result = 0;
4486 
4487     if (DetectEngineInitYamlConf(conf) == -1)
4488         return 0;
4489     de_ctx = DetectEngineCtxInit();
4490     if (de_ctx == NULL)
4491         goto end;
4492 
4493     result = (de_ctx->inspection_recursion_limit == 10);
4494 
4495  end:
4496     if (de_ctx != NULL)
4497         DetectEngineCtxFree(de_ctx);
4498 
4499     DetectEngineDeInitYamlConf();
4500 
4501     return result;
4502 }
4503 
4504 static int DetectEngineTest08(void)
4505 {
4506     const char *conf =
4507         "%YAML 1.1\n"
4508         "---\n"
4509         "detect-engine:\n"
4510         "  - profile: custom\n"
4511         "  - custom-values:\n"
4512         "      toclient-groups: 23\n"
4513         "      toserver-groups: 27\n";
4514 
4515     DetectEngineCtx *de_ctx = NULL;
4516     int result = 0;
4517 
4518     if (DetectEngineInitYamlConf(conf) == -1)
4519         return 0;
4520     de_ctx = DetectEngineCtxInit();
4521     if (de_ctx == NULL)
4522         goto end;
4523 
4524     if (de_ctx->max_uniq_toclient_groups == 23 &&
4525         de_ctx->max_uniq_toserver_groups == 27)
4526         result = 1;
4527 
4528  end:
4529     if (de_ctx != NULL)
4530         DetectEngineCtxFree(de_ctx);
4531 
4532     DetectEngineDeInitYamlConf();
4533 
4534     return result;
4535 }
4536 
4537 /** \test bug 892 bad values */
4538 static int DetectEngineTest09(void)
4539 {
4540     const char *conf =
4541         "%YAML 1.1\n"
4542         "---\n"
4543         "detect-engine:\n"
4544         "  - profile: custom\n"
4545         "  - custom-values:\n"
4546         "      toclient-groups: BA\n"
4547         "      toserver-groups: BA\n"
4548         "  - inspection-recursion-limit: 10\n";
4549 
4550     DetectEngineCtx *de_ctx = NULL;
4551     int result = 0;
4552 
4553     if (DetectEngineInitYamlConf(conf) == -1)
4554         return 0;
4555     de_ctx = DetectEngineCtxInit();
4556     if (de_ctx == NULL)
4557         goto end;
4558 
4559     if (de_ctx->max_uniq_toclient_groups == 20 &&
4560         de_ctx->max_uniq_toserver_groups == 40)
4561         result = 1;
4562 
4563  end:
4564     if (de_ctx != NULL)
4565         DetectEngineCtxFree(de_ctx);
4566 
4567     DetectEngineDeInitYamlConf();
4568 
4569     return result;
4570 }
4571 
4572 #endif
4573 
4574 void DetectEngineRegisterTests()
4575 {
4576 #ifdef UNITTESTS
4577     UtRegisterTest("DetectEngineTest01", DetectEngineTest01);
4578     UtRegisterTest("DetectEngineTest02", DetectEngineTest02);
4579     UtRegisterTest("DetectEngineTest03", DetectEngineTest03);
4580     UtRegisterTest("DetectEngineTest04", DetectEngineTest04);
4581     UtRegisterTest("DetectEngineTest08", DetectEngineTest08);
4582     UtRegisterTest("DetectEngineTest09", DetectEngineTest09);
4583 #endif
4584     return;
4585 }
4586