/* Copyright (C) 2007-2010 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 2 along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ /** * \file * * \author Victor Julien */ #include "suricata-common.h" #include "suricata.h" #include "debug.h" #include "detect.h" #include "flow.h" #include "flow-private.h" #include "flow-util.h" #include "conf.h" #include "conf-yaml-loader.h" #include "app-layer-htp.h" #include "detect-parse.h" #include "detect-engine-sigorder.h" #include "detect-engine-siggroup.h" #include "detect-engine-address.h" #include "detect-engine-port.h" #include "detect-engine-mpm.h" #include "detect-engine-hcbd.h" #include "detect-engine-iponly.h" #include "detect-engine-tag.h" #include "detect-engine-uri.h" #include "detect-engine-hcbd.h" #include "detect-engine-hsbd.h" #include "detect-engine-hhd.h" #include "detect-engine-hrhd.h" #include "detect-engine-hmd.h" #include "detect-engine-hcd.h" #include "detect-engine-hrud.h" #include "detect-engine-hrl.h" #include "detect-engine-hsmd.h" #include "detect-engine-hscd.h" #include "detect-engine-hua.h" #include "detect-engine-hhhd.h" #include "detect-engine-hrhhd.h" #include "detect-engine-file.h" #include "detect-engine-dns.h" #include "detect-engine-modbus.h" #include "detect-engine-filedata-smtp.h" #include "detect-engine-template.h" #include "detect-engine.h" #include "detect-engine-state.h" #include "detect-byte-extract.h" #include "detect-content.h" #include "detect-uricontent.h" #include "detect-engine-threshold.h" #include "detect-engine-loader.h" #include "util-classification-config.h" #include "util-reference-config.h" #include "util-threshold-config.h" #include "util-error.h" #include "util-hash.h" #include "util-byte.h" #include "util-debug.h" #include "util-unittest.h" #include "util-action.h" #include "util-magic.h" #include "util-signal.h" #include "util-var-name.h" #include "tm-threads.h" #include "runmodes.h" #ifdef PROFILING #include "util-profiling.h" #endif #include "reputation.h" #define DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT 3000 static uint32_t detect_engine_ctx_id = 1; static DetectEngineThreadCtx *DetectEngineThreadCtxInitForReload( ThreadVars *tv, DetectEngineCtx *new_de_ctx); static uint8_t DetectEngineCtxLoadConf(DetectEngineCtx *); static DetectEngineMasterCtx g_master_de_ctx = { SCMUTEX_INITIALIZER, 0, NULL, NULL, TENANT_SELECTOR_UNKNOWN, NULL,}; static DetectEngineThreadCtx *DetectEngineThreadCtxInitForMT(ThreadVars *tv); /* 2 - for each direction */ DetectEngineAppInspectionEngine *app_inspection_engine[FLOW_PROTO_DEFAULT][ALPROTO_MAX][2]; #if 0 static void DetectEnginePrintAppInspectionEngines(DetectEngineAppInspectionEngine *list[][ALPROTO_MAX][2]) { printf("\n"); AppProto alproto = ALPROTO_UNKNOWN + 1; for ( ; alproto < ALPROTO_MAX; alproto++) { printf("alproto - %d\n", alproto); int dir = 0; for ( ; dir < 2; dir++) { printf(" direction - %d\n", dir); DetectEngineAppInspectionEngine *engine = list[alproto][dir]; while (engine != NULL) { printf(" engine->alproto - %"PRIu16"\n", engine->alproto); printf(" engine->dir - %"PRIu16"\n", engine->dir); printf(" engine->sm_list - %d\n", engine->sm_list); printf(" engine->inspect_flags - %"PRIu32"\n", engine->inspect_flags); printf(" engine->match_flags - %"PRIu32"\n", engine->match_flags); printf("\n"); engine = engine->next; } } /* for ( ; dir < 2; dir++) */ } /* for ( ; alproto < ALPROTO_MAX; alproto++) */ return; } #endif void DetectEngineRegisterAppInspectionEngines(void) { struct tmp_t { uint8_t ipproto; AppProto alproto; int32_t sm_list; uint32_t inspect_flags; uint16_t dir; int (*Callback)(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Signature *sig, Flow *f, uint8_t flags, void *alstate, void *tx, uint64_t tx_id); }; struct tmp_t data_toserver[] = { { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_UMATCH, DE_STATE_FLAG_URI_INSPECT, 0, DetectEngineInspectPacketUris }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HRLMATCH, DE_STATE_FLAG_HRL_INSPECT, 0, DetectEngineInspectHttpRequestLine }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HCBDMATCH, DE_STATE_FLAG_HCBD_INSPECT, 0, DetectEngineInspectHttpClientBody }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HHDMATCH, DE_STATE_FLAG_HHD_INSPECT, 0, DetectEngineInspectHttpHeader }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HRHDMATCH, DE_STATE_FLAG_HRHD_INSPECT, 0, DetectEngineInspectHttpRawHeader }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HMDMATCH, DE_STATE_FLAG_HMD_INSPECT, 0, DetectEngineInspectHttpMethod }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HCDMATCH, DE_STATE_FLAG_HCD_INSPECT, 0, DetectEngineInspectHttpCookie }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HRUDMATCH, DE_STATE_FLAG_HRUD_INSPECT, 0, DetectEngineInspectHttpRawUri }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_FILEMATCH, DE_STATE_FLAG_FILE_TS_INSPECT, 0, DetectFileInspectHttp }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HUADMATCH, DE_STATE_FLAG_HUAD_INSPECT, 0, DetectEngineInspectHttpUA }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HHHDMATCH, DE_STATE_FLAG_HHHD_INSPECT, 0, DetectEngineInspectHttpHH }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HRHHDMATCH, DE_STATE_FLAG_HRHHD_INSPECT, 0, DetectEngineInspectHttpHRH }, /* DNS */ { IPPROTO_TCP, ALPROTO_DNS, DETECT_SM_LIST_DNSQUERYNAME_MATCH, DE_STATE_FLAG_DNSQUERYNAME_INSPECT, 0, DetectEngineInspectDnsQueryName }, /* specifically for UDP, register again * allows us to use the alproto w/o translation * in the detection engine */ { IPPROTO_UDP, ALPROTO_DNS, DETECT_SM_LIST_DNSQUERYNAME_MATCH, DE_STATE_FLAG_DNSQUERYNAME_INSPECT, 0, DetectEngineInspectDnsQueryName }, { IPPROTO_TCP, ALPROTO_DNS, DETECT_SM_LIST_DNSREQUEST_MATCH, DE_STATE_FLAG_DNSREQUEST_INSPECT, 0, DetectEngineInspectDnsRequest }, /* specifically for UDP, register again * allows us to use the alproto w/o translation * in the detection engine */ { IPPROTO_UDP, ALPROTO_DNS, DETECT_SM_LIST_DNSREQUEST_MATCH, DE_STATE_FLAG_DNSREQUEST_INSPECT, 0, DetectEngineInspectDnsRequest }, /* SMTP */ { IPPROTO_TCP, ALPROTO_SMTP, DETECT_SM_LIST_FILEMATCH, DE_STATE_FLAG_FILE_TS_INSPECT, 0, DetectFileInspectSmtp }, /* Modbus */ { IPPROTO_TCP, ALPROTO_MODBUS, DETECT_SM_LIST_MODBUS_MATCH, DE_STATE_FLAG_MODBUS_INSPECT, 0, DetectEngineInspectModbus }, /* file_data smtp */ { IPPROTO_TCP, ALPROTO_SMTP, DETECT_SM_LIST_FILEDATA, DE_STATE_FLAG_FD_SMTP_INSPECT, 0, DetectEngineInspectSMTPFiledata }, /* Template. */ { IPPROTO_TCP, ALPROTO_TEMPLATE, DETECT_SM_LIST_TEMPLATE_BUFFER_MATCH, DE_STATE_FLAG_TEMPLATE_BUFFER_INSPECT, 0, DetectEngineInspectTemplateBuffer }, }; struct tmp_t data_toclient[] = { { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_FILEDATA, DE_STATE_FLAG_HSBD_INSPECT, 1, DetectEngineInspectHttpServerBody }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HHDMATCH, DE_STATE_FLAG_HHD_INSPECT, 1, DetectEngineInspectHttpHeader }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HRHDMATCH, DE_STATE_FLAG_HRHD_INSPECT, 1, DetectEngineInspectHttpRawHeader }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HCDMATCH, DE_STATE_FLAG_HCD_INSPECT, 1, DetectEngineInspectHttpCookie }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_FILEMATCH, DE_STATE_FLAG_FILE_TC_INSPECT, 1, DetectFileInspectHttp }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HSMDMATCH, DE_STATE_FLAG_HSMD_INSPECT, 1, DetectEngineInspectHttpStatMsg }, { IPPROTO_TCP, ALPROTO_HTTP, DETECT_SM_LIST_HSCDMATCH, DE_STATE_FLAG_HSCD_INSPECT, 1, DetectEngineInspectHttpStatCode }, /* Modbus */ { IPPROTO_TCP, ALPROTO_MODBUS, DETECT_SM_LIST_MODBUS_MATCH, DE_STATE_FLAG_MODBUS_INSPECT, 0, DetectEngineInspectModbus }, { IPPROTO_TCP, ALPROTO_DNS, DETECT_SM_LIST_DNSRESPONSE_MATCH, DE_STATE_FLAG_DNSRESPONSE_INSPECT, 1, DetectEngineInspectDnsResponse }, /* specifically for UDP, register again * allows us to use the alproto w/o translation * in the detection engine */ { IPPROTO_UDP, ALPROTO_DNS, DETECT_SM_LIST_DNSRESPONSE_MATCH, DE_STATE_FLAG_DNSRESPONSE_INSPECT, 1, DetectEngineInspectDnsResponse }, /* Template. */ { IPPROTO_TCP, ALPROTO_TEMPLATE, DETECT_SM_LIST_TEMPLATE_BUFFER_MATCH, DE_STATE_FLAG_TEMPLATE_BUFFER_INSPECT, 1, DetectEngineInspectTemplateBuffer }, }; size_t i; for (i = 0 ; i < sizeof(data_toserver) / sizeof(struct tmp_t); i++) { DetectEngineRegisterAppInspectionEngine(data_toserver[i].ipproto, data_toserver[i].alproto, data_toserver[i].dir, data_toserver[i].sm_list, data_toserver[i].inspect_flags, data_toserver[i].Callback, app_inspection_engine); } for (i = 0 ; i < sizeof(data_toclient) / sizeof(struct tmp_t); i++) { DetectEngineRegisterAppInspectionEngine(data_toclient[i].ipproto, data_toclient[i].alproto, data_toclient[i].dir, data_toclient[i].sm_list, data_toclient[i].inspect_flags, data_toclient[i].Callback, app_inspection_engine); } #if 0 DetectEnginePrintAppInspectionEngines(app_inspection_engine); #endif return; } static void AppendAppInspectionEngine(DetectEngineAppInspectionEngine *engine, DetectEngineAppInspectionEngine *list[][ALPROTO_MAX][2]) { /* append to the list */ DetectEngineAppInspectionEngine *tmp = list[FlowGetProtoMapping(engine->ipproto)][engine->alproto][engine->dir]; DetectEngineAppInspectionEngine *insert = NULL; while (tmp != NULL) { if (tmp->dir == engine->dir && (tmp->sm_list == engine->sm_list || tmp->inspect_flags == engine->inspect_flags )) { SCLogError(SC_ERR_DETECT_PREPARE, "App Inspection Engine already " "registered for this direction(%"PRIu16") ||" "sm_list(%d) || " "[inspect(%"PRIu32")]_flags", tmp->dir, tmp->sm_list, tmp->inspect_flags); exit(EXIT_FAILURE); } insert = tmp; tmp = tmp->next; } if (insert == NULL) list[FlowGetProtoMapping(engine->ipproto)][engine->alproto][engine->dir] = engine; else insert->next = engine; return; } void DetectEngineRegisterAppInspectionEngine(uint8_t ipproto, AppProto alproto, uint16_t dir, int32_t sm_list, uint32_t inspect_flags, int (*Callback)(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Signature *sig, Flow *f, uint8_t flags, void *alstate, void *tx, uint64_t tx_id), DetectEngineAppInspectionEngine *list[][ALPROTO_MAX][2]) { if ((list == NULL) || (alproto <= ALPROTO_UNKNOWN || alproto >= ALPROTO_FAILED) || (dir > 1) || (sm_list < DETECT_SM_LIST_MATCH || sm_list >= DETECT_SM_LIST_MAX) || (Callback == NULL)) { SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid arguments"); exit(EXIT_FAILURE); } DetectEngineAppInspectionEngine *tmp = list[FlowGetProtoMapping(ipproto)][alproto][dir]; while (tmp != NULL) { if (tmp->sm_list == sm_list && tmp->Callback == Callback) { return; } tmp = tmp->next; } DetectEngineAppInspectionEngine *new_engine = SCMalloc(sizeof(DetectEngineAppInspectionEngine)); if (unlikely(new_engine == NULL)) { exit(EXIT_FAILURE); } memset(new_engine, 0, sizeof(*new_engine)); new_engine->ipproto = ipproto; new_engine->alproto = alproto; new_engine->dir = dir; new_engine->sm_list = sm_list; new_engine->inspect_flags = inspect_flags; new_engine->Callback = Callback; AppendAppInspectionEngine(new_engine, list); return; } /* code to control the main thread to do a reload */ enum DetectEngineSyncState { IDLE, /**< ready to start a reload */ RELOAD, /**< command main thread to do the reload */ DONE, /**< main thread telling us reload is done */ }; typedef struct DetectEngineSyncer_ { SCMutex m; enum DetectEngineSyncState state; } DetectEngineSyncer; static DetectEngineSyncer detect_sync = { SCMUTEX_INITIALIZER, IDLE }; /* tell main to start reloading */ int DetectEngineReloadStart(void) { int r = 0; SCMutexLock(&detect_sync.m); if (detect_sync.state == IDLE) { detect_sync.state = RELOAD; } else { r = -1; } SCMutexUnlock(&detect_sync.m); return r; } /* main thread checks this to see if it should start */ int DetectEngineReloadIsStart(void) { int r = 0; SCMutexLock(&detect_sync.m); if (detect_sync.state == RELOAD) { r = 1; } SCMutexUnlock(&detect_sync.m); return r; } /* main thread sets done when it's done */ void DetectEngineReloadSetDone(void) { SCMutexLock(&detect_sync.m); detect_sync.state = DONE; SCMutexUnlock(&detect_sync.m); } /* caller loops this until it returns 1 */ int DetectEngineReloadIsDone(void) { int r = 0; SCMutexLock(&detect_sync.m); if (detect_sync.state == DONE) { r = 1; detect_sync.state = IDLE; } SCMutexUnlock(&detect_sync.m); return r; } /** \internal * \brief Update detect threads with new detect engine * * Atomically update each detect thread with a new thread context * that is associated to the new detection engine(s). * * If called in unix socket mode, it's possible that we don't have * detect threads yet. * * \retval -1 error * \retval 0 no detection threads * \retval 1 successful reload */ static int DetectEngineReloadThreads(DetectEngineCtx *new_de_ctx) { SCEnter(); int i = 0; int no_of_detect_tvs = 0; ThreadVars *tv = NULL; /* count detect threads in use */ SCMutexLock(&tv_root_lock); tv = tv_root[TVT_PPT]; while (tv) { /* obtain the slots for this TV */ TmSlot *slots = tv->tm_slots; while (slots != NULL) { TmModule *tm = TmModuleGetById(slots->tm_id); if (suricata_ctl_flags != 0) { SCLogInfo("rule reload interupted by engine shutdown"); SCMutexUnlock(&tv_root_lock); return -1; } if (!(tm->flags & TM_FLAG_DETECT_TM)) { slots = slots->slot_next; continue; } no_of_detect_tvs++; break; } tv = tv->next; } SCMutexUnlock(&tv_root_lock); /* can be zero in unix socket mode */ if (no_of_detect_tvs == 0) { return 0; } SCLogNotice("rule reload starting"); /* prepare swap structures */ DetectEngineThreadCtx *old_det_ctx[no_of_detect_tvs]; DetectEngineThreadCtx *new_det_ctx[no_of_detect_tvs]; ThreadVars *detect_tvs[no_of_detect_tvs]; memset(old_det_ctx, 0x00, (no_of_detect_tvs * sizeof(DetectEngineThreadCtx *))); memset(new_det_ctx, 0x00, (no_of_detect_tvs * sizeof(DetectEngineThreadCtx *))); memset(detect_tvs, 0x00, (no_of_detect_tvs * sizeof(ThreadVars *))); /* start the process of swapping detect threads ctxs */ /* get reference to tv's and setup new_det_ctx array */ SCMutexLock(&tv_root_lock); tv = tv_root[TVT_PPT]; while (tv) { /* obtain the slots for this TV */ TmSlot *slots = tv->tm_slots; while (slots != NULL) { TmModule *tm = TmModuleGetById(slots->tm_id); if (suricata_ctl_flags != 0) { SCMutexUnlock(&tv_root_lock); goto error; } if (!(tm->flags & TM_FLAG_DETECT_TM)) { slots = slots->slot_next; continue; } old_det_ctx[i] = SC_ATOMIC_GET(slots->slot_data); detect_tvs[i] = tv; if (new_de_ctx != NULL) new_det_ctx[i] = DetectEngineThreadCtxInitForReload(tv, new_de_ctx); else new_det_ctx[i] = DetectEngineThreadCtxInitForMT(tv); if (new_det_ctx[i] == NULL) { SCLogError(SC_ERR_LIVE_RULE_SWAP, "Detect engine thread init " "failure in live rule swap. Let's get out of here"); SCMutexUnlock(&tv_root_lock); goto error; } SCLogDebug("live rule swap created new det_ctx - %p and de_ctx " "- %p\n", new_det_ctx[i], new_de_ctx); i++; break; } tv = tv->next; } BUG_ON(i != no_of_detect_tvs); /* atomicly replace the det_ctx data */ i = 0; tv = tv_root[TVT_PPT]; while (tv) { /* find the correct slot */ TmSlot *slots = tv->tm_slots; while (slots != NULL) { if (suricata_ctl_flags != 0) { return -1; } TmModule *tm = TmModuleGetById(slots->tm_id); if (!(tm->flags & TM_FLAG_DETECT_TM)) { slots = slots->slot_next; continue; } SCLogDebug("swapping new det_ctx - %p with older one - %p", new_det_ctx[i], SC_ATOMIC_GET(slots->slot_data)); (void)SC_ATOMIC_SET(slots->slot_data, new_det_ctx[i++]); break; } tv = tv->next; } SCMutexUnlock(&tv_root_lock); /* threads now all have new data, however they may not have started using * it and may still use the old data */ SCLogInfo("Live rule swap has swapped %d old det_ctx's with new ones, " "along with the new de_ctx", no_of_detect_tvs); /* inject a fake packet if the detect thread isn't using the new ctx yet, * this speeds up the process */ for (i = 0; i < no_of_detect_tvs; i++) { int break_out = 0; int pseudo_pkt_inserted = 0; usleep(1000); while (SC_ATOMIC_GET(new_det_ctx[i]->so_far_used_by_detect) != 1) { if (suricata_ctl_flags != 0) { break_out = 1; break; } if (pseudo_pkt_inserted == 0) { pseudo_pkt_inserted = 1; if (detect_tvs[i]->inq != NULL) { Packet *p = PacketGetFromAlloc(); if (p != NULL) { p->flags |= PKT_PSEUDO_STREAM_END; PacketQueue *q = &trans_q[detect_tvs[i]->inq->id]; SCMutexLock(&q->mutex_q); PacketEnqueue(q, p); SCCondSignal(&q->cond_q); SCMutexUnlock(&q->mutex_q); } } } usleep(1000); } if (break_out) break; SCLogDebug("new_det_ctx - %p used by detect engine", new_det_ctx[i]); } /* this is to make sure that if someone initiated shutdown during a live * rule swap, the live rule swap won't clean up the old det_ctx and * de_ctx, till all detect threads have stopped working and sitting * silently after setting RUNNING_DONE flag and while waiting for * THV_DEINIT flag */ if (i != no_of_detect_tvs) { // not all threads we swapped ThreadVars *tv = tv_root[TVT_PPT]; while (tv) { /* obtain the slots for this TV */ TmSlot *slots = tv->tm_slots; while (slots != NULL) { TmModule *tm = TmModuleGetById(slots->tm_id); if (!(tm->flags & TM_FLAG_DETECT_TM)) { slots = slots->slot_next; continue; } while (!TmThreadsCheckFlag(tv, THV_RUNNING_DONE)) { usleep(100); } slots = slots->slot_next; } tv = tv->next; } } /* free all the ctxs */ for (i = 0; i < no_of_detect_tvs; i++) { SCLogDebug("Freeing old_det_ctx - %p used by detect", old_det_ctx[i]); DetectEngineThreadCtxDeinit(NULL, old_det_ctx[i]); } SRepReloadComplete(); SCLogNotice("rule reload complete"); return 1; error: for (i = 0; i < no_of_detect_tvs; i++) { if (new_det_ctx[i] != NULL) DetectEngineThreadCtxDeinit(NULL, new_det_ctx[i]); } return -1; } static DetectEngineCtx *DetectEngineCtxInitReal(int minimal, const char *prefix) { DetectEngineCtx *de_ctx; ConfNode *seq_node = NULL; ConfNode *insp_recursion_limit_node = NULL; ConfNode *de_engine_node = NULL; char *insp_recursion_limit = NULL; de_ctx = SCMalloc(sizeof(DetectEngineCtx)); if (unlikely(de_ctx == NULL)) goto error; memset(de_ctx,0,sizeof(DetectEngineCtx)); if (minimal) { de_ctx->minimal = 1; de_ctx->id = detect_engine_ctx_id++; return de_ctx; } if (prefix != NULL) { strlcpy(de_ctx->config_prefix, prefix, sizeof(de_ctx->config_prefix)); } if (ConfGetBool("engine.init-failure-fatal", (int *)&(de_ctx->failure_fatal)) != 1) { SCLogDebug("ConfGetBool could not load the value."); } de_engine_node = ConfGetNode("detect-engine"); if (de_engine_node != NULL) { TAILQ_FOREACH(seq_node, &de_engine_node->head, next) { if (strcmp(seq_node->val, "inspection-recursion-limit") != 0) continue; insp_recursion_limit_node = ConfNodeLookupChild(seq_node, seq_node->val); if (insp_recursion_limit_node == NULL) { SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Error retrieving conf " "entry for detect-engine:inspection-recursion-limit"); break; } insp_recursion_limit = insp_recursion_limit_node->val; SCLogDebug("Found detect-engine:inspection-recursion-limit - %s:%s", insp_recursion_limit_node->name, insp_recursion_limit_node->val); break; } } if (insp_recursion_limit != NULL) { de_ctx->inspection_recursion_limit = atoi(insp_recursion_limit); } else { de_ctx->inspection_recursion_limit = DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT; } if (de_ctx->inspection_recursion_limit == 0) de_ctx->inspection_recursion_limit = -1; SCLogDebug("de_ctx->inspection_recursion_limit: %d", de_ctx->inspection_recursion_limit); de_ctx->mpm_matcher = PatternMatchDefaultMatcher(); DetectEngineCtxLoadConf(de_ctx); SigGroupHeadHashInit(de_ctx); SigGroupHeadMpmHashInit(de_ctx); SigGroupHeadMpmUriHashInit(de_ctx); SigGroupHeadSPortHashInit(de_ctx); SigGroupHeadDPortHashInit(de_ctx); DetectPortSpHashInit(de_ctx); DetectPortDpHashInit(de_ctx); ThresholdHashInit(de_ctx); VariableNameInitHash(de_ctx); DetectParseDupSigHashInit(de_ctx); de_ctx->mpm_pattern_id_store = MpmPatternIdTableInitHash(); if (de_ctx->mpm_pattern_id_store == NULL) { goto error; } /* init iprep... ignore errors for now */ (void)SRepInit(de_ctx); #ifdef PROFILING SCProfilingKeywordInitCounters(de_ctx); #endif SCClassConfLoadClassficationConfigFile(de_ctx, NULL); SCRConfLoadReferenceConfigFile(de_ctx, NULL); if (ActionInitConfig() < 0) { goto error; } de_ctx->id = detect_engine_ctx_id++; return de_ctx; error: return NULL; } DetectEngineCtx *DetectEngineCtxInitMinimal(void) { return DetectEngineCtxInitReal(1, NULL); } DetectEngineCtx *DetectEngineCtxInit(void) { return DetectEngineCtxInitReal(0, NULL); } DetectEngineCtx *DetectEngineCtxInitWithPrefix(const char *prefix) { if (prefix == NULL || strlen(prefix) == 0) return DetectEngineCtxInit(); else return DetectEngineCtxInitReal(0, prefix); } static void DetectEngineCtxFreeThreadKeywordData(DetectEngineCtx *de_ctx) { DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list; while (item) { DetectEngineThreadKeywordCtxItem *next = item->next; SCFree(item); item = next; } de_ctx->keyword_list = NULL; } /** * \brief Free a DetectEngineCtx:: * * \param de_ctx DetectEngineCtx:: to be freed */ void DetectEngineCtxFree(DetectEngineCtx *de_ctx) { if (de_ctx == NULL) return; #ifdef PROFILING if (de_ctx->profile_ctx != NULL) { SCProfilingRuleDestroyCtx(de_ctx->profile_ctx); de_ctx->profile_ctx = NULL; } if (de_ctx->profile_keyword_ctx != NULL) { SCProfilingKeywordDestroyCtx(de_ctx);//->profile_keyword_ctx); // de_ctx->profile_keyword_ctx = NULL; } #endif /* Normally the hashes are freed elsewhere, but * to be sure look at them again here. */ MpmPatternIdTableFreeHash(de_ctx->mpm_pattern_id_store); /* normally cleaned up in SigGroupBuild */ SigGroupHeadHashFree(de_ctx); SigGroupHeadMpmHashFree(de_ctx); SigGroupHeadMpmUriHashFree(de_ctx); SigGroupHeadSPortHashFree(de_ctx); SigGroupHeadDPortHashFree(de_ctx); DetectParseDupSigHashFree(de_ctx); SCSigSignatureOrderingModuleCleanup(de_ctx); DetectPortSpHashFree(de_ctx); DetectPortDpHashFree(de_ctx); ThresholdContextDestroy(de_ctx); SigCleanSignatures(de_ctx); VariableNameFreeHash(de_ctx); if (de_ctx->sig_array) SCFree(de_ctx->sig_array); SCClassConfDeInitContext(de_ctx); SCRConfDeInitContext(de_ctx); SigGroupCleanup(de_ctx); if (de_ctx->sgh_mpm_context == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE) { MpmFactoryDeRegisterAllMpmCtxProfiles(de_ctx); } DetectEngineCtxFreeThreadKeywordData(de_ctx); SRepDestroy(de_ctx); /* if we have a config prefix, remove the config from the tree */ if (strlen(de_ctx->config_prefix) > 0) { /* remove config */ ConfNode *node = ConfGetNode(de_ctx->config_prefix); if (node != NULL) { ConfNodeRemove(node); /* frees node */ } #if 0 ConfDump(); #endif } SCFree(de_ctx); //DetectAddressGroupPrintMemory(); //DetectSigGroupPrintMemory(); //DetectPortPrintMemory(); } /** \brief Function that load DetectEngineCtx config for grouping sigs * used by the engine * \retval 0 if no config provided, 1 if config was provided * and loaded successfuly */ static uint8_t DetectEngineCtxLoadConf(DetectEngineCtx *de_ctx) { uint8_t profile = ENGINE_PROFILE_UNKNOWN; char *de_ctx_profile = NULL; const char *max_uniq_toclient_src_groups_str = NULL; const char *max_uniq_toclient_dst_groups_str = NULL; const char *max_uniq_toclient_sp_groups_str = NULL; const char *max_uniq_toclient_dp_groups_str = NULL; const char *max_uniq_toserver_src_groups_str = NULL; const char *max_uniq_toserver_dst_groups_str = NULL; const char *max_uniq_toserver_sp_groups_str = NULL; const char *max_uniq_toserver_dp_groups_str = NULL; char *sgh_mpm_context = NULL; ConfNode *de_ctx_custom = ConfGetNode("detect-engine"); ConfNode *opt = NULL; if (de_ctx_custom != NULL) { TAILQ_FOREACH(opt, &de_ctx_custom->head, next) { if (strcmp(opt->val, "profile") == 0) { de_ctx_profile = opt->head.tqh_first->val; } else if (strcmp(opt->val, "sgh-mpm-context") == 0) { sgh_mpm_context = opt->head.tqh_first->val; } } } if (de_ctx_profile != NULL) { if (strcmp(de_ctx_profile, "low") == 0) { profile = ENGINE_PROFILE_LOW; } else if (strcmp(de_ctx_profile, "medium") == 0) { profile = ENGINE_PROFILE_MEDIUM; } else if (strcmp(de_ctx_profile, "high") == 0) { profile = ENGINE_PROFILE_HIGH; } else if (strcmp(de_ctx_profile, "custom") == 0) { profile = ENGINE_PROFILE_CUSTOM; } SCLogDebug("Profile for detection engine groups is \"%s\"", de_ctx_profile); } else { SCLogDebug("Profile for detection engine groups not provided " "at suricata.yaml. Using default (\"medium\")."); } /* detect-engine.sgh-mpm-context option parsing */ if (sgh_mpm_context == NULL || strcmp(sgh_mpm_context, "auto") == 0) { /* for now, since we still haven't implemented any intelligence into * understanding the patterns and distributing mpm_ctx across sgh */ if (de_ctx->mpm_matcher == DEFAULT_MPM || de_ctx->mpm_matcher == MPM_AC_GFBS || #ifdef __SC_CUDA_SUPPORT__ de_ctx->mpm_matcher == MPM_AC_BS || de_ctx->mpm_matcher == MPM_AC_CUDA) { #else de_ctx->mpm_matcher == MPM_AC_BS) { #endif de_ctx->sgh_mpm_context = ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE; } else { de_ctx->sgh_mpm_context = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL; } } else { if (strcmp(sgh_mpm_context, "single") == 0) { de_ctx->sgh_mpm_context = ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE; } else if (strcmp(sgh_mpm_context, "full") == 0) { #ifdef __SC_CUDA_SUPPORT__ if (de_ctx->mpm_matcher == MPM_AC_CUDA) { SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "You can't use " "the cuda version of our mpm ac, i.e. \"ac-cuda\" " "along with \"full\" \"sgh-mpm-context\". " "Allowed values are \"single\" and \"auto\"."); exit(EXIT_FAILURE); } #endif de_ctx->sgh_mpm_context = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL; } else { SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "You have supplied an " "invalid conf value for detect-engine.sgh-mpm-context-" "%s", sgh_mpm_context); exit(EXIT_FAILURE); } } if (run_mode == RUNMODE_UNITTEST) { de_ctx->sgh_mpm_context = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL; } opt = NULL; switch (profile) { case ENGINE_PROFILE_LOW: de_ctx->max_uniq_toclient_src_groups = 2; de_ctx->max_uniq_toclient_dst_groups = 2; de_ctx->max_uniq_toclient_sp_groups = 2; de_ctx->max_uniq_toclient_dp_groups = 3; de_ctx->max_uniq_toserver_src_groups = 2; de_ctx->max_uniq_toserver_dst_groups = 2; de_ctx->max_uniq_toserver_sp_groups = 2; de_ctx->max_uniq_toserver_dp_groups = 3; break; case ENGINE_PROFILE_HIGH: de_ctx->max_uniq_toclient_src_groups = 15; de_ctx->max_uniq_toclient_dst_groups = 15; de_ctx->max_uniq_toclient_sp_groups = 15; de_ctx->max_uniq_toclient_dp_groups = 20; de_ctx->max_uniq_toserver_src_groups = 15; de_ctx->max_uniq_toserver_dst_groups = 15; de_ctx->max_uniq_toserver_sp_groups = 15; de_ctx->max_uniq_toserver_dp_groups = 40; break; case ENGINE_PROFILE_CUSTOM: TAILQ_FOREACH(opt, &de_ctx_custom->head, next) { if (strcmp(opt->val, "custom-values") == 0) { max_uniq_toclient_src_groups_str = ConfNodeLookupChildValue (opt->head.tqh_first, "toclient-src-groups"); max_uniq_toclient_dst_groups_str = ConfNodeLookupChildValue (opt->head.tqh_first, "toclient-dst-groups"); max_uniq_toclient_sp_groups_str = ConfNodeLookupChildValue (opt->head.tqh_first, "toclient-sp-groups"); max_uniq_toclient_dp_groups_str = ConfNodeLookupChildValue (opt->head.tqh_first, "toclient-dp-groups"); max_uniq_toserver_src_groups_str = ConfNodeLookupChildValue (opt->head.tqh_first, "toserver-src-groups"); max_uniq_toserver_dst_groups_str = ConfNodeLookupChildValue (opt->head.tqh_first, "toserver-dst-groups"); max_uniq_toserver_sp_groups_str = ConfNodeLookupChildValue (opt->head.tqh_first, "toserver-sp-groups"); max_uniq_toserver_dp_groups_str = ConfNodeLookupChildValue (opt->head.tqh_first, "toserver-dp-groups"); } } if (max_uniq_toclient_src_groups_str != NULL) { if (ByteExtractStringUint16(&de_ctx->max_uniq_toclient_src_groups, 10, strlen(max_uniq_toclient_src_groups_str), (const char *)max_uniq_toclient_src_groups_str) <= 0) { de_ctx->max_uniq_toclient_src_groups = 4; SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for " "toclient-src-groups failed, using %u", max_uniq_toclient_src_groups_str, de_ctx->max_uniq_toclient_src_groups); } } else { de_ctx->max_uniq_toclient_src_groups = 4; } if (max_uniq_toclient_dst_groups_str != NULL) { if (ByteExtractStringUint16(&de_ctx->max_uniq_toclient_dst_groups, 10, strlen(max_uniq_toclient_dst_groups_str), (const char *)max_uniq_toclient_dst_groups_str) <= 0) { de_ctx->max_uniq_toclient_dst_groups = 4; SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for " "toclient-dst-groups failed, using %u", max_uniq_toclient_dst_groups_str, de_ctx->max_uniq_toclient_dst_groups); } } else { de_ctx->max_uniq_toclient_dst_groups = 4; } if (max_uniq_toclient_sp_groups_str != NULL) { if (ByteExtractStringUint16(&de_ctx->max_uniq_toclient_sp_groups, 10, strlen(max_uniq_toclient_sp_groups_str), (const char *)max_uniq_toclient_sp_groups_str) <= 0) { de_ctx->max_uniq_toclient_sp_groups = 4; SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for " "toclient-sp-groups failed, using %u", max_uniq_toclient_sp_groups_str, de_ctx->max_uniq_toclient_sp_groups); } } else { de_ctx->max_uniq_toclient_sp_groups = 4; } if (max_uniq_toclient_dp_groups_str != NULL) { if (ByteExtractStringUint16(&de_ctx->max_uniq_toclient_dp_groups, 10, strlen(max_uniq_toclient_dp_groups_str), (const char *)max_uniq_toclient_dp_groups_str) <= 0) { de_ctx->max_uniq_toclient_dp_groups = 6; SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for " "toclient-dp-groups failed, using %u", max_uniq_toclient_dp_groups_str, de_ctx->max_uniq_toclient_dp_groups); } } else { de_ctx->max_uniq_toclient_dp_groups = 6; } if (max_uniq_toserver_src_groups_str != NULL) { if (ByteExtractStringUint16(&de_ctx->max_uniq_toserver_src_groups, 10, strlen(max_uniq_toserver_src_groups_str), (const char *)max_uniq_toserver_src_groups_str) <= 0) { de_ctx->max_uniq_toserver_src_groups = 4; SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for " "toserver-src-groups failed, using %u", max_uniq_toserver_src_groups_str, de_ctx->max_uniq_toserver_src_groups); } } else { de_ctx->max_uniq_toserver_src_groups = 4; } if (max_uniq_toserver_dst_groups_str != NULL) { if (ByteExtractStringUint16(&de_ctx->max_uniq_toserver_dst_groups, 10, strlen(max_uniq_toserver_dst_groups_str), (const char *)max_uniq_toserver_dst_groups_str) <= 0) { de_ctx->max_uniq_toserver_dst_groups = 8; SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for " "toserver-dst-groups failed, using %u", max_uniq_toserver_dst_groups_str, de_ctx->max_uniq_toserver_dst_groups); } } else { de_ctx->max_uniq_toserver_dst_groups = 8; } if (max_uniq_toserver_sp_groups_str != NULL) { if (ByteExtractStringUint16(&de_ctx->max_uniq_toserver_sp_groups, 10, strlen(max_uniq_toserver_sp_groups_str), (const char *)max_uniq_toserver_sp_groups_str) <= 0) { de_ctx->max_uniq_toserver_sp_groups = 4; SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for " "toserver-sp-groups failed, using %u", max_uniq_toserver_sp_groups_str, de_ctx->max_uniq_toserver_sp_groups); } } else { de_ctx->max_uniq_toserver_sp_groups = 4; } if (max_uniq_toserver_dp_groups_str != NULL) { if (ByteExtractStringUint16(&de_ctx->max_uniq_toserver_dp_groups, 10, strlen(max_uniq_toserver_dp_groups_str), (const char *)max_uniq_toserver_dp_groups_str) <= 0) { de_ctx->max_uniq_toserver_dp_groups = 30; SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for " "toserver-dp-groups failed, using %u", max_uniq_toserver_dp_groups_str, de_ctx->max_uniq_toserver_dp_groups); } } else { de_ctx->max_uniq_toserver_dp_groups = 30; } break; /* Default (or no config provided) is profile medium */ case ENGINE_PROFILE_MEDIUM: case ENGINE_PROFILE_UNKNOWN: default: de_ctx->max_uniq_toclient_src_groups = 4; de_ctx->max_uniq_toclient_dst_groups = 4; de_ctx->max_uniq_toclient_sp_groups = 4; de_ctx->max_uniq_toclient_dp_groups = 6; de_ctx->max_uniq_toserver_src_groups = 4; de_ctx->max_uniq_toserver_dst_groups = 8; de_ctx->max_uniq_toserver_sp_groups = 4; de_ctx->max_uniq_toserver_dp_groups = 30; break; } if (profile == ENGINE_PROFILE_UNKNOWN) return 0; return 1; } /* * getting & (re)setting the internal sig i */ //inline uint32_t DetectEngineGetMaxSigId(DetectEngineCtx *de_ctx) //{ // return de_ctx->signum; //} void DetectEngineResetMaxSigId(DetectEngineCtx *de_ctx) { de_ctx->signum = 0; } static int DetectEngineThreadCtxInitKeywords(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx) { if (de_ctx->keyword_id > 0) { det_ctx->keyword_ctxs_array = SCMalloc(de_ctx->keyword_id * sizeof(void *)); if (det_ctx->keyword_ctxs_array == NULL) { SCLogError(SC_ERR_DETECT_PREPARE, "setting up thread local detect ctx"); return TM_ECODE_FAILED; } memset(det_ctx->keyword_ctxs_array, 0x00, de_ctx->keyword_id * sizeof(void *)); det_ctx->keyword_ctxs_size = de_ctx->keyword_id; DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list; while (item) { det_ctx->keyword_ctxs_array[item->id] = item->InitFunc(item->data); if (det_ctx->keyword_ctxs_array[item->id] == NULL) { SCLogError(SC_ERR_DETECT_PREPARE, "setting up thread local detect ctx " "for keyword \"%s\" failed", item->name); return TM_ECODE_FAILED; } item = item->next; } } return TM_ECODE_OK; } static void DetectEngineThreadCtxDeinitKeywords(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx) { if (de_ctx->keyword_id > 0) { DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list; while (item) { if (det_ctx->keyword_ctxs_array[item->id] != NULL) item->FreeFunc(det_ctx->keyword_ctxs_array[item->id]); item = item->next; } det_ctx->keyword_ctxs_size = 0; SCFree(det_ctx->keyword_ctxs_array); det_ctx->keyword_ctxs_array = NULL; } } /** \internal * \brief Helper for DetectThread setup functions */ static TmEcode ThreadCtxDoInit (DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx) { int i; /** \todo we still depend on the global mpm_ctx here * * Initialize the thread pattern match ctx with the max size * of the content and uricontent id's so our match lookup * table is always big enough */ PatternMatchThreadPrepare(&det_ctx->mtc, de_ctx->mpm_matcher, DetectContentMaxId(de_ctx)); PatternMatchThreadPrepare(&det_ctx->mtcs, de_ctx->mpm_matcher, DetectContentMaxId(de_ctx)); PatternMatchThreadPrepare(&det_ctx->mtcu, de_ctx->mpm_matcher, DetectUricontentMaxId(de_ctx)); PmqSetup(&det_ctx->pmq, de_ctx->max_fp_id); for (i = 0; i < DETECT_SMSG_PMQ_NUM; i++) { PmqSetup(&det_ctx->smsg_pmq[i], de_ctx->max_fp_id); } /* sized to the max of our sgh settings. A max setting of 0 implies that all * sgh's have: sgh->non_mpm_store_cnt == 0 */ if (de_ctx->non_mpm_store_cnt_max > 0) { det_ctx->non_mpm_id_array = SCCalloc(de_ctx->non_mpm_store_cnt_max, sizeof(SigIntId)); BUG_ON(det_ctx->non_mpm_id_array == NULL); } /* IP-ONLY */ DetectEngineIPOnlyThreadInit(de_ctx,&det_ctx->io_ctx); /* DeState */ if (de_ctx->sig_array_len > 0) { det_ctx->de_state_sig_array_len = de_ctx->sig_array_len; det_ctx->de_state_sig_array = SCMalloc(det_ctx->de_state_sig_array_len * sizeof(uint8_t)); if (det_ctx->de_state_sig_array == NULL) { return TM_ECODE_FAILED; } memset(det_ctx->de_state_sig_array, 0, det_ctx->de_state_sig_array_len * sizeof(uint8_t)); det_ctx->match_array_len = de_ctx->sig_array_len; det_ctx->match_array = SCMalloc(det_ctx->match_array_len * sizeof(Signature *)); if (det_ctx->match_array == NULL) { return TM_ECODE_FAILED; } memset(det_ctx->match_array, 0, det_ctx->match_array_len * sizeof(Signature *)); } /* byte_extract storage */ det_ctx->bj_values = SCMalloc(sizeof(*det_ctx->bj_values) * (de_ctx->byte_extract_max_local_id + 1)); if (det_ctx->bj_values == NULL) { return TM_ECODE_FAILED; } DetectEngineThreadCtxInitKeywords(de_ctx, det_ctx); #ifdef PROFILING SCProfilingRuleThreadSetup(de_ctx->profile_ctx, det_ctx); SCProfilingKeywordThreadSetup(de_ctx->profile_keyword_ctx, det_ctx); #endif SC_ATOMIC_INIT(det_ctx->so_far_used_by_detect); return TM_ECODE_OK; } /** \brief initialize thread specific detection engine context * * \note there is a special case when using delayed detect. In this case the * function is called twice per thread. The first time the rules are not * yet loaded. de_ctx->delayed_detect_initialized will be 0. The 2nd * time they will be loaded. de_ctx->delayed_detect_initialized will be 1. * This is needed to do the per thread counter registration before the * packet runtime starts. In delayed detect mode, the first call will * return a NULL ptr through the data ptr. * * \param tv ThreadVars for this thread * \param initdata pointer to de_ctx * \param data[out] pointer to store our thread detection ctx * * \retval TM_ECODE_OK if all went well * \retval TM_ECODE_FAILED on serious erro */ TmEcode DetectEngineThreadCtxInit(ThreadVars *tv, void *initdata, void **data) { if (DetectEngineMultiTenantEnabled()) { DetectEngineThreadCtx *mt_det_ctx = DetectEngineThreadCtxInitForMT(tv); *data = (void *)mt_det_ctx; return (mt_det_ctx == NULL) ? TM_ECODE_FAILED : TM_ECODE_OK; } /* first register the counter. In delayed detect mode we exit right after if the * rules haven't been loaded yet. */ uint16_t counter_alerts = StatsRegisterCounter("detect.alert", tv); #ifdef PROFILING uint16_t counter_mpm_list = StatsRegisterAvgCounter("detect.mpm_list", tv); uint16_t counter_nonmpm_list = StatsRegisterAvgCounter("detect.nonmpm_list", tv); uint16_t counter_fnonmpm_list = StatsRegisterAvgCounter("detect.fnonmpm_list", tv); uint16_t counter_match_list = StatsRegisterAvgCounter("detect.match_list", tv); #endif DetectEngineThreadCtx *det_ctx = SCMalloc(sizeof(DetectEngineThreadCtx)); if (unlikely(det_ctx == NULL)) return TM_ECODE_FAILED; memset(det_ctx, 0, sizeof(DetectEngineThreadCtx)); det_ctx->tv = tv; det_ctx->de_ctx = DetectEngineGetCurrent(); if (det_ctx->de_ctx == NULL) { #ifdef UNITTESTS if (RunmodeIsUnittests()) { det_ctx->de_ctx = (DetectEngineCtx *)initdata; } else { DetectEngineThreadCtxDeinit(tv, det_ctx); return TM_ECODE_FAILED; } #else DetectEngineThreadCtxDeinit(tv, det_ctx); return TM_ECODE_FAILED; #endif } if (det_ctx->de_ctx->minimal == 0) { if (ThreadCtxDoInit(det_ctx->de_ctx, det_ctx) != TM_ECODE_OK) { DetectEngineThreadCtxDeinit(tv, det_ctx); return TM_ECODE_FAILED; } } /** alert counter setup */ det_ctx->counter_alerts = counter_alerts; #ifdef PROFILING det_ctx->counter_mpm_list = counter_mpm_list; det_ctx->counter_nonmpm_list = counter_nonmpm_list; det_ctx->counter_fnonmpm_list = counter_fnonmpm_list; det_ctx->counter_match_list = counter_match_list; #endif /* pass thread data back to caller */ *data = (void *)det_ctx; return TM_ECODE_OK; } /** * \internal * \brief initialize a det_ctx for reload cases * \param new_de_ctx the new detection engine * \retval det_ctx detection engine thread ctx or NULL in case of error */ static DetectEngineThreadCtx *DetectEngineThreadCtxInitForReload( ThreadVars *tv, DetectEngineCtx *new_de_ctx) { DetectEngineThreadCtx *det_ctx = SCMalloc(sizeof(DetectEngineThreadCtx)); if (unlikely(det_ctx == NULL)) return NULL; memset(det_ctx, 0, sizeof(DetectEngineThreadCtx)); det_ctx->tenant_id = new_de_ctx->tenant_id; det_ctx->tv = tv; det_ctx->de_ctx = DetectEngineReference(new_de_ctx); if (det_ctx->de_ctx == NULL) { SCFree(det_ctx); return NULL; } /* most of the init happens here */ if (ThreadCtxDoInit(det_ctx->de_ctx, det_ctx) != TM_ECODE_OK) { DetectEngineDeReference(&det_ctx->de_ctx); SCFree(det_ctx); return NULL; } /** alert counter setup */ det_ctx->counter_alerts = StatsRegisterCounter("detect.alert", tv); #ifdef PROFILING uint16_t counter_mpm_list = StatsRegisterAvgCounter("detect.mpm_list", tv); uint16_t counter_nonmpm_list = StatsRegisterAvgCounter("detect.nonmpm_list", tv); uint16_t counter_fnonmpm_list = StatsRegisterAvgCounter("detect.fnonmpm_list", tv); uint16_t counter_match_list = StatsRegisterAvgCounter("detect.match_list", tv); det_ctx->counter_mpm_list = counter_mpm_list; det_ctx->counter_nonmpm_list = counter_nonmpm_list; det_ctx->counter_fnonmpm_list = counter_fnonmpm_list; det_ctx->counter_match_list = counter_match_list; #endif return det_ctx; } void DetectEngineThreadCtxFree(DetectEngineThreadCtx *det_ctx) { if (det_ctx->tenant_array != NULL) { SCFree(det_ctx->tenant_array); det_ctx->tenant_array = NULL; } #ifdef PROFILING SCProfilingRuleThreadCleanup(det_ctx); SCProfilingKeywordThreadCleanup(det_ctx); #endif DetectEngineIPOnlyThreadDeinit(&det_ctx->io_ctx); /** \todo get rid of this static */ if (det_ctx->de_ctx != NULL) { PatternMatchThreadDestroy(&det_ctx->mtc, det_ctx->de_ctx->mpm_matcher); PatternMatchThreadDestroy(&det_ctx->mtcs, det_ctx->de_ctx->mpm_matcher); PatternMatchThreadDestroy(&det_ctx->mtcu, det_ctx->de_ctx->mpm_matcher); } PmqFree(&det_ctx->pmq); int i; for (i = 0; i < DETECT_SMSG_PMQ_NUM; i++) { PmqFree(&det_ctx->smsg_pmq[i]); } if (det_ctx->non_mpm_id_array != NULL) SCFree(det_ctx->non_mpm_id_array); if (det_ctx->de_state_sig_array != NULL) SCFree(det_ctx->de_state_sig_array); if (det_ctx->match_array != NULL) SCFree(det_ctx->match_array); if (det_ctx->bj_values != NULL) SCFree(det_ctx->bj_values); /* HHD temp storage */ for (i = 0; i < det_ctx->hhd_buffers_size; i++) { if (det_ctx->hhd_buffers[i] != NULL) SCFree(det_ctx->hhd_buffers[i]); } if (det_ctx->hhd_buffers) SCFree(det_ctx->hhd_buffers); det_ctx->hhd_buffers = NULL; if (det_ctx->hhd_buffers_len) SCFree(det_ctx->hhd_buffers_len); det_ctx->hhd_buffers_len = NULL; /* HSBD */ if (det_ctx->hsbd != NULL) { SCLogDebug("det_ctx hsbd %u", det_ctx->hsbd_buffers_size); for (i = 0; i < det_ctx->hsbd_buffers_size; i++) { if (det_ctx->hsbd[i].buffer != NULL) { HTPFree(det_ctx->hsbd[i].buffer, det_ctx->hsbd[i].buffer_size); } } SCFree(det_ctx->hsbd); } /* HSCB */ if (det_ctx->hcbd != NULL) { SCLogDebug("det_ctx hcbd %u", det_ctx->hcbd_buffers_size); for (i = 0; i < det_ctx->hcbd_buffers_size; i++) { if (det_ctx->hcbd[i].buffer != NULL) SCFree(det_ctx->hcbd[i].buffer); SCLogDebug("det_ctx->hcbd[i].buffer_size %u", det_ctx->hcbd[i].buffer_size); } SCFree(det_ctx->hcbd); } if (det_ctx->de_ctx != NULL) { DetectEngineThreadCtxDeinitKeywords(det_ctx->de_ctx, det_ctx); #ifdef UNITTESTS if (!RunmodeIsUnittests() || det_ctx->de_ctx->ref_cnt > 0) DetectEngineDeReference(&det_ctx->de_ctx); #else DetectEngineDeReference(&det_ctx->de_ctx); #endif } SCFree(det_ctx); } TmEcode DetectEngineThreadCtxDeinit(ThreadVars *tv, void *data) { DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data; if (det_ctx == NULL) { SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "argument \"data\" NULL"); return TM_ECODE_OK; } if (det_ctx->mt_det_ctxs_hash != NULL) { HashTableFree(det_ctx->mt_det_ctxs_hash); det_ctx->mt_det_ctxs_hash = NULL; } DetectEngineThreadCtxFree(det_ctx); return TM_ECODE_OK; } void DetectEngineThreadCtxInfo(ThreadVars *t, DetectEngineThreadCtx *det_ctx) { /* XXX */ PatternMatchThreadPrint(&det_ctx->mtc, det_ctx->de_ctx->mpm_matcher); PatternMatchThreadPrint(&det_ctx->mtcu, det_ctx->de_ctx->mpm_matcher); } /** \brief Register Thread keyword context Funcs * * \param de_ctx detection engine to register in * \param name keyword name for error printing * \param InitFunc function ptr * \param data keyword init data to pass to Func * \param FreeFunc function ptr * \param mode 0 normal (ctx per keyword instance) 1 shared (one ctx per det_ct) * * \retval id for retrieval of ctx at runtime * \retval -1 on error * * \note make sure "data" remains valid and it free'd elsewhere. It's * recommended to store it in the keywords global ctx so that * it's freed when the de_ctx is freed. */ int DetectRegisterThreadCtxFuncs(DetectEngineCtx *de_ctx, const char *name, void *(*InitFunc)(void *), void *data, void (*FreeFunc)(void *), int mode) { BUG_ON(de_ctx == NULL || InitFunc == NULL || FreeFunc == NULL || data == NULL); if (mode) { DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list; while (item != NULL) { if (strcmp(name, item->name) == 0) { return item->id; } item = item->next; } } DetectEngineThreadKeywordCtxItem *item = SCMalloc(sizeof(DetectEngineThreadKeywordCtxItem)); if (unlikely(item == NULL)) return -1; memset(item, 0x00, sizeof(DetectEngineThreadKeywordCtxItem)); item->InitFunc = InitFunc; item->FreeFunc = FreeFunc; item->data = data; item->name = name; item->next = de_ctx->keyword_list; de_ctx->keyword_list = item; item->id = de_ctx->keyword_id++; return item->id; } /** \brief Retrieve thread local keyword ctx by id * * \param det_ctx detection engine thread ctx to retrieve the ctx from * \param id id of the ctx returned by DetectRegisterThreadCtxInitFunc at * keyword init. * * \retval ctx or NULL on error */ void *DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx *det_ctx, int id) { if (id < 0 || id > det_ctx->keyword_ctxs_size || det_ctx->keyword_ctxs_array == NULL) return NULL; return det_ctx->keyword_ctxs_array[id]; } /** \brief Check if detection is enabled * \retval bool true or false */ int DetectEngineEnabled(void) { DetectEngineMasterCtx *master = &g_master_de_ctx; SCMutexLock(&master->lock); if (master->list == NULL) { SCMutexUnlock(&master->lock); return 0; } SCMutexUnlock(&master->lock); return 1; } DetectEngineCtx *DetectEngineGetCurrent(void) { DetectEngineMasterCtx *master = &g_master_de_ctx; SCMutexLock(&master->lock); if (master->list == NULL) { SCMutexUnlock(&master->lock); return NULL; } master->list->ref_cnt++; SCLogDebug("master->list %p ref_cnt %u", master->list, master->list->ref_cnt); SCMutexUnlock(&master->lock); return master->list; } DetectEngineCtx *DetectEngineReference(DetectEngineCtx *de_ctx) { if (de_ctx == NULL) return NULL; de_ctx->ref_cnt++; return de_ctx; } /** TODO locking? Not needed if this is a one time setting at startup */ int DetectEngineMultiTenantEnabled(void) { DetectEngineMasterCtx *master = &g_master_de_ctx; return (master->multi_tenant_enabled); } /** \internal * \brief load a tenant from a yaml file * * \param tenant_id the tenant id by which the config is known * \param filename full path of a yaml file * \param loader_id id of loader thread or -1 * * \retval 0 ok * \retval -1 failed */ static int DetectEngineMultiTenantLoadTenant(uint32_t tenant_id, const char *filename, int loader_id) { DetectEngineCtx *de_ctx = NULL; char prefix[64]; snprintf(prefix, sizeof(prefix), "multi-detect.%d", tenant_id); #ifdef OS_WIN32 struct _stat st; if(_stat(filename, &st) != 0) { #else struct stat st; if(stat(filename, &st) != 0) { #endif /* OS_WIN32 */ SCLogError(SC_ERR_FOPEN, "failed to stat file %s", filename); goto error; } de_ctx = DetectEngineGetByTenantId(tenant_id); if (de_ctx != NULL) { SCLogError(SC_ERR_MT_DUPLICATE_TENANT, "tenant %u already registered", tenant_id); DetectEngineDeReference(&de_ctx); goto error; } ConfNode *node = ConfGetNode(prefix); if (node == NULL) { SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to properly setup yaml %s", filename); goto error; } de_ctx = DetectEngineCtxInitWithPrefix(prefix); if (de_ctx == NULL) { SCLogError(SC_ERR_INITIALIZATION, "initializing detection engine " "context failed."); goto error; } SCLogDebug("de_ctx %p with prefix %s", de_ctx, de_ctx->config_prefix); de_ctx->tenant_id = tenant_id; de_ctx->loader_id = loader_id; if (SigLoadSignatures(de_ctx, NULL, 0) < 0) { SCLogError(SC_ERR_NO_RULES_LOADED, "Loading signatures failed."); goto error; } DetectEngineAddToMaster(de_ctx); return 0; error: if (de_ctx != NULL) { DetectEngineCtxFree(de_ctx); } return -1; } static int DetectEngineMultiTenantReloadTenant(uint32_t tenant_id, const char *filename, int reload_cnt) { DetectEngineCtx *old_de_ctx = DetectEngineGetByTenantId(tenant_id); if (old_de_ctx == NULL) { SCLogError(SC_ERR_INITIALIZATION, "tenant detect engine not found"); return -1; } char prefix[64]; snprintf(prefix, sizeof(prefix), "multi-detect.%d.reload.%d", tenant_id, reload_cnt); reload_cnt++; SCLogInfo("prefix %s", prefix); if (ConfYamlLoadFileWithPrefix(filename, prefix) != 0) { SCLogError(SC_ERR_INITIALIZATION,"failed to load yaml"); goto error; } ConfNode *node = ConfGetNode(prefix); if (node == NULL) { SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to properly setup yaml %s", filename); goto error; } DetectEngineCtx *new_de_ctx = DetectEngineCtxInitWithPrefix(prefix); if (new_de_ctx == NULL) { SCLogError(SC_ERR_INITIALIZATION, "initializing detection engine " "context failed."); goto error; } SCLogDebug("de_ctx %p with prefix %s", new_de_ctx, new_de_ctx->config_prefix); new_de_ctx->tenant_id = tenant_id; new_de_ctx->loader_id = old_de_ctx->loader_id; if (SigLoadSignatures(new_de_ctx, NULL, 0) < 0) { SCLogError(SC_ERR_NO_RULES_LOADED, "Loading signatures failed."); goto error; } DetectEngineAddToMaster(new_de_ctx); /* move to free list */ DetectEngineMoveToFreeList(old_de_ctx); DetectEngineDeReference(&old_de_ctx); return 0; error: DetectEngineDeReference(&old_de_ctx); return -1; } typedef struct TenantLoaderCtx_ { uint32_t tenant_id; int reload_cnt; /**< used by reload */ const char *yaml; } TenantLoaderCtx; static int DetectLoaderFuncLoadTenant(void *vctx, int loader_id) { TenantLoaderCtx *ctx = (TenantLoaderCtx *)vctx; SCLogInfo("loader %d", loader_id); if (DetectEngineMultiTenantLoadTenant(ctx->tenant_id, ctx->yaml, loader_id) != 0) { return -1; } return 0; } int DetectLoaderSetupLoadTenant(uint32_t tenant_id, const char *yaml) { TenantLoaderCtx *t = SCCalloc(1, sizeof(*t)); if (t == NULL) return -ENOMEM; t->tenant_id = tenant_id; t->yaml = yaml; return DetectLoaderQueueTask(-1, DetectLoaderFuncLoadTenant, t); } static int DetectLoaderFuncReloadTenant(void *vctx, int loader_id) { TenantLoaderCtx *ctx = (TenantLoaderCtx *)vctx; SCLogDebug("loader_id %d", loader_id); if (DetectEngineMultiTenantReloadTenant(ctx->tenant_id, ctx->yaml, ctx->reload_cnt) != 0) { return -1; } return 0; } int DetectLoaderSetupReloadTenant(uint32_t tenant_id, const char *yaml, int reload_cnt) { DetectEngineCtx *old_de_ctx = DetectEngineGetByTenantId(tenant_id); if (old_de_ctx == NULL) return -ENOENT; int loader_id = old_de_ctx->loader_id; DetectEngineDeReference(&old_de_ctx); TenantLoaderCtx *t = SCCalloc(1, sizeof(*t)); if (t == NULL) return -ENOMEM; t->tenant_id = tenant_id; t->yaml = yaml; t->reload_cnt = reload_cnt; SCLogDebug("loader_id %d", loader_id); return DetectLoaderQueueTask(loader_id, DetectLoaderFuncReloadTenant, t); } /** \brief Load a tenant and wait for loading to complete */ int DetectEngineLoadTenantBlocking(uint32_t tenant_id, const char *yaml) { int r = DetectLoaderSetupLoadTenant(tenant_id, yaml); if (r < 0) return r; if (DetectLoadersSync() != 0) return -1; return 0; } /** \brief Reload a tenant and wait for loading to complete */ int DetectEngineReloadTenantBlocking(uint32_t tenant_id, const char *yaml, int reload_cnt) { int r = DetectLoaderSetupReloadTenant(tenant_id, yaml, reload_cnt); if (r < 0) return r; if (DetectLoadersSync() != 0) return -1; return 0; } /** * \brief setup multi-detect / multi-tenancy * * See if MT is enabled. If so, setup the selector, tenants and mappings. * Tenants and mappings are optional, and can also dynamically be added * and removed from the unix socket. */ void DetectEngineMultiTenantSetup(void) { DetectEngineMasterCtx *master = &g_master_de_ctx; int failure_fatal = 0; (void)ConfGetBool("engine.init-failure-fatal", &failure_fatal); int enabled = 0; (void)ConfGetBool("multi-detect.enabled", &enabled); if (enabled == 1) { DetectLoadersInit(); TmModuleDetectLoaderRegister(); DetectLoaderThreadSpawn(); TmThreadContinueDetectLoaderThreads(); SCMutexLock(&master->lock); master->multi_tenant_enabled = 1; char *handler = NULL; if (ConfGet("multi-detect.selector", &handler) == 1) { SCLogInfo("selector %s", handler); if (strcmp(handler, "vlan") == 0) { master->tenant_selector = TENANT_SELECTOR_VLAN; } else if (strcmp(handler, "direct") == 0) { master->tenant_selector = TENANT_SELECTOR_DIRECT; } else { SCLogError(SC_ERR_INVALID_VALUE, "unknown value %s " "multi-detect.selector", handler); SCMutexUnlock(&master->lock); goto error; } } SCMutexUnlock(&master->lock); SCLogInfo("multi-detect is enabled (multi tenancy). Selector: %s", handler); /* traffic -- tenant mappings */ ConfNode *mappings_root_node = ConfGetNode("multi-detect.mappings"); ConfNode *mapping_node = NULL; if (mappings_root_node != NULL) { TAILQ_FOREACH(mapping_node, &mappings_root_node->head, next) { if (strcmp(mapping_node->val, "vlan") == 0) { ConfNode *tenant_id_node = ConfNodeLookupChild(mapping_node, "tenant-id"); if (tenant_id_node == NULL) goto bad_mapping; ConfNode *vlan_id_node = ConfNodeLookupChild(mapping_node, "vlan-id"); if (vlan_id_node == NULL) goto bad_mapping; SCLogInfo("vlan %s %s", tenant_id_node->val, vlan_id_node->val); uint32_t tenant_id = 0; if (ByteExtractStringUint32(&tenant_id, 10, strlen(tenant_id_node->val), tenant_id_node->val) == -1) { SCLogError(SC_ERR_INVALID_ARGUMENT, "tenant-id " "of %s is invalid", tenant_id_node->val); goto bad_mapping; } uint16_t vlan_id = 0; if (ByteExtractStringUint16(&vlan_id, 10, strlen(vlan_id_node->val), vlan_id_node->val) == -1) { SCLogError(SC_ERR_INVALID_ARGUMENT, "vlan-id " "of %s is invalid", vlan_id_node->val); goto bad_mapping; } if (DetectEngineTentantRegisterVlanId(tenant_id, (uint32_t)vlan_id) != 0) { goto error; } } else { SCLogWarning(SC_ERR_INVALID_VALUE, "multi-detect.mappings expects a list of 'vlan's. Not %s", mapping_node->val); goto bad_mapping; } continue; bad_mapping: if (failure_fatal) goto error; } } /* tenants */ ConfNode *tenants_root_node = ConfGetNode("multi-detect.tenants"); ConfNode *tenant_node = NULL; if (tenants_root_node != NULL) { TAILQ_FOREACH(tenant_node, &tenants_root_node->head, next) { if (strcmp(tenant_node->val, "tenant") != 0) { SCLogWarning(SC_ERR_INVALID_VALUE, "multi-detect.tenants expects a list of 'tenant's. Not %s", tenant_node->val); goto bad_tenant; } ConfNode *id_node = ConfNodeLookupChild(tenant_node, "id"); if (id_node == NULL) goto bad_tenant; ConfNode *yaml_node = ConfNodeLookupChild(tenant_node, "yaml"); if (yaml_node == NULL) goto bad_tenant; uint32_t tenant_id = 0; if (ByteExtractStringUint32(&tenant_id, 10, strlen(id_node->val), id_node->val) == -1) { SCLogError(SC_ERR_INVALID_ARGUMENT, "tenant_id " "of %s is invalid", id_node->val); goto bad_tenant; } SCLogInfo("tenant id: %u, %s", tenant_id, yaml_node->val); /* setup the yaml in this loop so that it's not done by the loader * threads. ConfYamlLoadFileWithPrefix is not thread safe. */ char prefix[64]; snprintf(prefix, sizeof(prefix), "multi-detect.%d", tenant_id); if (ConfYamlLoadFileWithPrefix(yaml_node->val, prefix) != 0) { SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to load yaml %s", yaml_node->val); goto bad_tenant; } if (DetectLoaderSetupLoadTenant(tenant_id, yaml_node->val) != 0) { /* error logged already */ goto bad_tenant; } continue; bad_tenant: if (failure_fatal) goto error; } } /* wait for our loaders to complete their tasks */ if (DetectLoadersSync() != 0) goto error; if (DetectEngineMTApply() < 0) { SCLogError(SC_ERR_DETECT_PREPARE, "initializing the detection engine failed"); goto error; } } else { SCLogDebug("multi-detect not enabled (multi tenancy)"); } error: return; } uint32_t DetectEngineTentantGetIdFromVlanId(const void *ctx, const Packet *p) { const DetectEngineThreadCtx *det_ctx = ctx; uint32_t x = 0; uint32_t vlan_id = 0; if (p->vlan_idx == 0) return 0; vlan_id = p->vlan_id[0]; if (det_ctx == NULL || det_ctx->tenant_array == NULL || det_ctx->tenant_array_size == 0) return 0; /* not very efficient, but for now we're targeting only limited amounts. * Can use hash/tree approach later. */ for (x = 0; x < det_ctx->tenant_array_size; x++) { if (det_ctx->tenant_array[x].traffic_id == vlan_id) return det_ctx->tenant_array[x].tenant_id; } return 0; } static int DetectEngineTentantRegisterSelector(enum DetectEngineTenantSelectors selector, uint32_t tenant_id, uint32_t traffic_id) { DetectEngineMasterCtx *master = &g_master_de_ctx; SCMutexLock(&master->lock); if (!(master->tenant_selector == TENANT_SELECTOR_UNKNOWN || master->tenant_selector == selector)) { SCLogInfo("conflicting selector already set"); SCMutexUnlock(&master->lock); return -1; } DetectEngineTenantMapping *m = master->tenant_mapping_list; while (m) { if (m->traffic_id == traffic_id) { SCLogInfo("traffic id already registered"); SCMutexUnlock(&master->lock); return -1; } m = m->next; } DetectEngineTenantMapping *map = SCCalloc(1, sizeof(*map)); if (map == NULL) { SCLogInfo("memory fail"); SCMutexUnlock(&master->lock); return -1; } map->traffic_id = traffic_id; map->tenant_id = tenant_id; map->next = master->tenant_mapping_list; master->tenant_mapping_list = map; master->tenant_selector = selector; SCLogInfo("tenant handler %u %u %u registered", selector, tenant_id, traffic_id); SCMutexUnlock(&master->lock); return 0; } static int DetectEngineTentantUnregisterSelector(enum DetectEngineTenantSelectors selector, uint32_t tenant_id, uint32_t traffic_id) { DetectEngineMasterCtx *master = &g_master_de_ctx; SCMutexLock(&master->lock); if (master->tenant_mapping_list == NULL) { SCMutexUnlock(&master->lock); return -1; } DetectEngineTenantMapping *prev = NULL; DetectEngineTenantMapping *map = master->tenant_mapping_list; while (map) { if (map->traffic_id == traffic_id && map->tenant_id == tenant_id) { if (prev != NULL) prev->next = map->next; else master->tenant_mapping_list = map->next; map->next = NULL; SCFree(map); SCLogInfo("tenant handler %u %u %u unregistered", selector, tenant_id, traffic_id); SCMutexUnlock(&master->lock); return 0; } prev = map; map = map->next; } SCMutexUnlock(&master->lock); return -1; } int DetectEngineTentantRegisterVlanId(uint32_t tenant_id, uint16_t vlan_id) { return DetectEngineTentantRegisterSelector(TENANT_SELECTOR_VLAN, tenant_id, (uint32_t)vlan_id); } int DetectEngineTentantUnregisterVlanId(uint32_t tenant_id, uint16_t vlan_id) { return DetectEngineTentantUnregisterSelector(TENANT_SELECTOR_VLAN, tenant_id, (uint32_t)vlan_id); } int DetectEngineTentantRegisterPcapFile(uint32_t tenant_id) { SCLogInfo("registering %u %d 0", TENANT_SELECTOR_DIRECT, tenant_id); return DetectEngineTentantRegisterSelector(TENANT_SELECTOR_DIRECT, tenant_id, 0); } int DetectEngineTentantUnregisterPcapFile(uint32_t tenant_id) { SCLogInfo("unregistering %u %d 0", TENANT_SELECTOR_DIRECT, tenant_id); return DetectEngineTentantUnregisterSelector(TENANT_SELECTOR_DIRECT, tenant_id, 0); } uint32_t DetectEngineTentantGetIdFromPcap(const void *ctx, const Packet *p) { return p->pcap_v.tenant_id; } DetectEngineCtx *DetectEngineGetByTenantId(int tenant_id) { DetectEngineMasterCtx *master = &g_master_de_ctx; SCMutexLock(&master->lock); if (master->list == NULL) { SCMutexUnlock(&master->lock); return NULL; } DetectEngineCtx *de_ctx = master->list; while (de_ctx) { if (de_ctx->tenant_id == tenant_id) { de_ctx->ref_cnt++; break; } de_ctx = de_ctx->next; } SCMutexUnlock(&master->lock); return de_ctx; } void DetectEngineDeReference(DetectEngineCtx **de_ctx) { BUG_ON((*de_ctx)->ref_cnt == 0); (*de_ctx)->ref_cnt--; *de_ctx = NULL; } static int DetectEngineAddToList(DetectEngineCtx *instance) { DetectEngineMasterCtx *master = &g_master_de_ctx; if (instance == NULL) return -1; if (master->list == NULL) { master->list = instance; } else { instance->next = master->list; master->list = instance; } return 0; } int DetectEngineAddToMaster(DetectEngineCtx *de_ctx) { int r; if (de_ctx == NULL) return -1; SCLogDebug("adding de_ctx %p to master", de_ctx); DetectEngineMasterCtx *master = &g_master_de_ctx; SCMutexLock(&master->lock); r = DetectEngineAddToList(de_ctx); SCMutexUnlock(&master->lock); return r; } int DetectEngineMoveToFreeList(DetectEngineCtx *de_ctx) { DetectEngineMasterCtx *master = &g_master_de_ctx; SCMutexLock(&master->lock); DetectEngineCtx *instance = master->list; if (instance == NULL) { SCMutexUnlock(&master->lock); return -1; } /* remove from active list */ if (instance == de_ctx) { master->list = instance->next; } else { DetectEngineCtx *prev = instance; instance = instance->next; /* already checked first element */ while (instance) { DetectEngineCtx *next = instance->next; if (instance == de_ctx) { prev->next = instance->next; break; } prev = instance; instance = next; } if (instance == NULL) { SCMutexUnlock(&master->lock); return -1; } } /* instance is now detached from list */ instance->next = NULL; /* add to free list */ if (master->free_list == NULL) { master->free_list = instance; } else { instance->next = master->free_list; master->free_list = instance; } SCLogDebug("detect engine %p moved to free list (%u refs)", de_ctx, de_ctx->ref_cnt); SCMutexUnlock(&master->lock); return 0; } void DetectEnginePruneFreeList(void) { DetectEngineMasterCtx *master = &g_master_de_ctx; SCMutexLock(&master->lock); DetectEngineCtx *prev = NULL; DetectEngineCtx *instance = master->free_list; while (instance) { DetectEngineCtx *next = instance->next; SCLogDebug("detect engine %p has %u ref(s)", instance, instance->ref_cnt); if (instance->ref_cnt == 0) { if (prev == NULL) { master->free_list = next; } else { prev->next = next; } SCLogDebug("freeing detect engine %p", instance); DetectEngineCtxFree(instance); instance = NULL; } prev = instance; instance = next; } SCMutexUnlock(&master->lock); } static int reloads = 0; /** \brief Reload the detection engine * * \param filename YAML file to load for the detect config * * \retval -1 error * \retval 0 ok */ int DetectEngineReload(const char *filename, SCInstance *suri) { DetectEngineCtx *new_de_ctx = NULL; DetectEngineCtx *old_de_ctx = NULL; char prefix[128] = ""; if (filename != NULL) { snprintf(prefix, sizeof(prefix), "detect-engine-reloads.%d", reloads++); if (ConfYamlLoadFileWithPrefix(filename, prefix) != 0) { SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to load yaml %s", filename); return -1; } ConfNode *node = ConfGetNode(prefix); if (node == NULL) { SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to properly setup yaml %s", filename); return -1; } #if 0 ConfDump(); #endif } /* get a reference to the current de_ctx */ old_de_ctx = DetectEngineGetCurrent(); if (old_de_ctx == NULL) return -1; SCLogDebug("get ref to old_de_ctx %p", old_de_ctx); /* get new detection engine */ new_de_ctx = DetectEngineCtxInitWithPrefix(prefix); if (new_de_ctx == NULL) { SCLogError(SC_ERR_INITIALIZATION, "initializing detection engine " "context failed."); DetectEngineDeReference(&old_de_ctx); return -1; } if (SigLoadSignatures(new_de_ctx, suri->sig_file, suri->sig_file_exclusive) != 0) { DetectEngineCtxFree(new_de_ctx); DetectEngineDeReference(&old_de_ctx); return -1; } SCThresholdConfInitContext(new_de_ctx, NULL); SCLogDebug("set up new_de_ctx %p", new_de_ctx); /* add to master */ DetectEngineAddToMaster(new_de_ctx); /* move to old free list */ DetectEngineMoveToFreeList(old_de_ctx); DetectEngineDeReference(&old_de_ctx); SCLogDebug("going to reload the threads to use new_de_ctx %p", new_de_ctx); /* update the threads */ DetectEngineReloadThreads(new_de_ctx); SCLogDebug("threads now run new_de_ctx %p", new_de_ctx); /* walk free list, freeing the old_de_ctx */ DetectEnginePruneFreeList(); SCLogDebug("old_de_ctx should have been freed"); return 0; } static uint32_t TenantIdHash(HashTable *h, void *data, uint16_t data_len) { DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data; return det_ctx->tenant_id % h->array_size; } static char TenantIdCompare(void *d1, uint16_t d1_len, void *d2, uint16_t d2_len) { DetectEngineThreadCtx *det1 = (DetectEngineThreadCtx *)d1; DetectEngineThreadCtx *det2 = (DetectEngineThreadCtx *)d2; return (det1->tenant_id == det2->tenant_id); } static void TenantIdFree(void *d) { DetectEngineThreadCtxFree(d); } /** NOTE: master MUST be locked before calling this */ static DetectEngineThreadCtx *DetectEngineThreadCtxInitForMT(ThreadVars *tv) { DetectEngineMasterCtx *master = &g_master_de_ctx; DetectEngineTenantMapping *map_array = NULL; uint32_t map_array_size = 0; uint32_t map_cnt = 0; int max_tenant_id = 0; DetectEngineCtx *list = master->list; HashTable *mt_det_ctxs_hash = NULL; DetectEngineThreadCtx *det_ctx = NULL; if (master->tenant_selector == TENANT_SELECTOR_UNKNOWN) { SCLogError(SC_ERR_MT_NO_SELECTOR, "no tenant selector set: " "set using multi-detect.selector"); return NULL; } uint32_t tcnt = 0; while (list) { if (list->tenant_id > max_tenant_id) max_tenant_id = list->tenant_id; list = list->next; tcnt++; } mt_det_ctxs_hash = HashTableInit(tcnt * 2, TenantIdHash, TenantIdCompare, TenantIdFree); if (mt_det_ctxs_hash == NULL) { goto error; } if (max_tenant_id == 0) { SCLogInfo("no tenants left, or none registered yet"); } else { max_tenant_id++; DetectEngineTenantMapping *map = master->tenant_mapping_list; while (map) { map_cnt++; map = map->next; } if (map_cnt > 0) { map_array_size = map_cnt + 1; map_array = SCCalloc(map_array_size, sizeof(*map_array)); if (map_array == NULL) goto error; /* fill the array */ map_cnt = 0; map = master->tenant_mapping_list; while (map) { BUG_ON(map_cnt > map_array_size); map_array[map_cnt].traffic_id = map->traffic_id; map_array[map_cnt].tenant_id = map->tenant_id; map_cnt++; map = map->next; } } /* set up hash for tenant lookup */ list = master->list; while (list) { if (list->tenant_id != 0) { DetectEngineThreadCtx *mt_det_ctx = DetectEngineThreadCtxInitForReload(tv, list); if (mt_det_ctx == NULL) goto error; BUG_ON(HashTableAdd(mt_det_ctxs_hash, mt_det_ctx, 0) != 0); } list = list->next; } } det_ctx = SCCalloc(1, sizeof(DetectEngineThreadCtx)); if (det_ctx == NULL) { goto error; } det_ctx->mt_det_ctxs_hash = mt_det_ctxs_hash; mt_det_ctxs_hash = NULL; /* first register the counter. In delayed detect mode we exit right after if the * rules haven't been loaded yet. */ uint16_t counter_alerts = StatsRegisterCounter("detect.alert", tv); #ifdef PROFILING uint16_t counter_mpm_list = StatsRegisterAvgCounter("detect.mpm_list", tv); uint16_t counter_nonmpm_list = StatsRegisterAvgCounter("detect.nonmpm_list", tv); uint16_t counter_fnonmpm_list = StatsRegisterAvgCounter("detect.fnonmpm_list", tv); uint16_t counter_match_list = StatsRegisterAvgCounter("detect.match_list", tv); #endif /** alert counter setup */ det_ctx->counter_alerts = counter_alerts; #ifdef PROFILING det_ctx->counter_mpm_list = counter_mpm_list; det_ctx->counter_nonmpm_list = counter_nonmpm_list; det_ctx->counter_fnonmpm_list = counter_fnonmpm_list; det_ctx->counter_match_list = counter_match_list; #endif det_ctx->mt_det_ctxs_cnt = max_tenant_id; det_ctx->tenant_array = map_array; det_ctx->tenant_array_size = map_array_size; switch (master->tenant_selector) { case TENANT_SELECTOR_UNKNOWN: SCLogDebug("TENANT_SELECTOR_UNKNOWN"); break; case TENANT_SELECTOR_VLAN: det_ctx->TenantGetId = DetectEngineTentantGetIdFromVlanId; SCLogDebug("TENANT_SELECTOR_VLAN"); break; case TENANT_SELECTOR_DIRECT: det_ctx->TenantGetId = DetectEngineTentantGetIdFromPcap; SCLogDebug("TENANT_SELECTOR_DIRECT"); break; } return det_ctx; error: if (map_array != NULL) SCFree(map_array); if (mt_det_ctxs_hash != NULL) HashTableFree(mt_det_ctxs_hash); return NULL; } int DetectEngineMTApply(void) { DetectEngineMasterCtx *master = &g_master_de_ctx; SCMutexLock(&master->lock); if (master->tenant_selector == TENANT_SELECTOR_UNKNOWN) { SCLogInfo("error, no tenant selector"); SCMutexUnlock(&master->lock); return -1; } DetectEngineCtx *minimal_de_ctx = NULL; /* if we have no tenants, we need a minimal on */ if (master->list == NULL) { minimal_de_ctx = master->list = DetectEngineCtxInitMinimal(); SCLogDebug("no tenants, using minimal %p", minimal_de_ctx); } else if (master->list->next == NULL && master->list->tenant_id == 0) { minimal_de_ctx = master->list; SCLogDebug("no tenants, using original %p", minimal_de_ctx); } /* update the threads */ SCLogDebug("MT reload starting"); DetectEngineReloadThreads(minimal_de_ctx); SCLogDebug("MT reload done"); SCMutexUnlock(&master->lock); /* walk free list, freeing the old_de_ctx */ DetectEnginePruneFreeList(); SCLogDebug("old_de_ctx should have been freed"); return 0; } const char *DetectSigmatchListEnumToString(enum DetectSigmatchListEnum type) { switch (type) { case DETECT_SM_LIST_MATCH: return "packet"; case DETECT_SM_LIST_PMATCH: return "packet/stream payload"; case DETECT_SM_LIST_UMATCH: return "http uri"; case DETECT_SM_LIST_HRUDMATCH: return "http raw uri"; case DETECT_SM_LIST_HCBDMATCH: return "http client body"; case DETECT_SM_LIST_FILEDATA: return "http server body"; case DETECT_SM_LIST_HHDMATCH: return "http headers"; case DETECT_SM_LIST_HRHDMATCH: return "http raw headers"; case DETECT_SM_LIST_HSMDMATCH: return "http stat msg"; case DETECT_SM_LIST_HSCDMATCH: return "http stat code"; case DETECT_SM_LIST_HHHDMATCH: return "http host"; case DETECT_SM_LIST_HRHHDMATCH: return "http raw host header"; case DETECT_SM_LIST_HMDMATCH: return "http method"; case DETECT_SM_LIST_HCDMATCH: return "http cookie"; case DETECT_SM_LIST_HUADMATCH: return "http user-agent"; case DETECT_SM_LIST_HRLMATCH: return "http request line"; case DETECT_SM_LIST_APP_EVENT: return "app layer events"; case DETECT_SM_LIST_AMATCH: return "generic app layer"; case DETECT_SM_LIST_DMATCH: return "dcerpc"; case DETECT_SM_LIST_TMATCH: return "tag"; case DETECT_SM_LIST_FILEMATCH: return "file"; case DETECT_SM_LIST_DNSQUERYNAME_MATCH: return "dns query name"; case DETECT_SM_LIST_DNSREQUEST_MATCH: return "dns request"; case DETECT_SM_LIST_DNSRESPONSE_MATCH: return "dns response"; case DETECT_SM_LIST_MODBUS_MATCH: return "modbus"; case DETECT_SM_LIST_TEMPLATE_BUFFER_MATCH: return "template_buffer"; case DETECT_SM_LIST_POSTMATCH: return "post-match"; case DETECT_SM_LIST_SUPPRESS: return "suppress"; case DETECT_SM_LIST_THRESHOLD: return "threshold"; case DETECT_SM_LIST_MAX: return "max (internal)"; case DETECT_SM_LIST_NOTSET: return "not set (internal)"; } return "error"; } /*************************************Unittest*********************************/ #ifdef UNITTESTS static int DetectEngineInitYamlConf(char *conf) { ConfCreateContextBackup(); ConfInit(); return ConfYamlLoadString(conf, strlen(conf)); } static void DetectEngineDeInitYamlConf(void) { ConfDeInit(); ConfRestoreContextBackup(); return; } static int DetectEngineTest01(void) { char *conf = "%YAML 1.1\n" "---\n" "detect-engine:\n" " - profile: medium\n" " - custom-values:\n" " toclient_src_groups: 2\n" " toclient_dst_groups: 2\n" " toclient_sp_groups: 2\n" " toclient_dp_groups: 3\n" " toserver_src_groups: 2\n" " toserver_dst_groups: 4\n" " toserver_sp_groups: 2\n" " toserver_dp_groups: 25\n" " - inspection-recursion-limit: 0\n"; DetectEngineCtx *de_ctx = NULL; int result = 0; if (DetectEngineInitYamlConf(conf) == -1) return 0; de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) goto end; result = (de_ctx->inspection_recursion_limit == -1); end: if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); DetectEngineDeInitYamlConf(); return result; } static int DetectEngineTest02(void) { char *conf = "%YAML 1.1\n" "---\n" "detect-engine:\n" " - profile: medium\n" " - custom-values:\n" " toclient_src_groups: 2\n" " toclient_dst_groups: 2\n" " toclient_sp_groups: 2\n" " toclient_dp_groups: 3\n" " toserver_src_groups: 2\n" " toserver_dst_groups: 4\n" " toserver_sp_groups: 2\n" " toserver_dp_groups: 25\n" " - inspection-recursion-limit:\n"; DetectEngineCtx *de_ctx = NULL; int result = 0; if (DetectEngineInitYamlConf(conf) == -1) return 0; de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) goto end; result = (de_ctx->inspection_recursion_limit == -1); end: if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); DetectEngineDeInitYamlConf(); return result; } static int DetectEngineTest03(void) { char *conf = "%YAML 1.1\n" "---\n" "detect-engine:\n" " - profile: medium\n" " - custom-values:\n" " toclient_src_groups: 2\n" " toclient_dst_groups: 2\n" " toclient_sp_groups: 2\n" " toclient_dp_groups: 3\n" " toserver_src_groups: 2\n" " toserver_dst_groups: 4\n" " toserver_sp_groups: 2\n" " toserver_dp_groups: 25\n"; DetectEngineCtx *de_ctx = NULL; int result = 0; if (DetectEngineInitYamlConf(conf) == -1) return 0; de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) goto end; result = (de_ctx->inspection_recursion_limit == DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT); end: if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); DetectEngineDeInitYamlConf(); return result; } static int DetectEngineTest04(void) { char *conf = "%YAML 1.1\n" "---\n" "detect-engine:\n" " - profile: medium\n" " - custom-values:\n" " toclient_src_groups: 2\n" " toclient_dst_groups: 2\n" " toclient_sp_groups: 2\n" " toclient_dp_groups: 3\n" " toserver_src_groups: 2\n" " toserver_dst_groups: 4\n" " toserver_sp_groups: 2\n" " toserver_dp_groups: 25\n" " - inspection-recursion-limit: 10\n"; DetectEngineCtx *de_ctx = NULL; int result = 0; if (DetectEngineInitYamlConf(conf) == -1) return 0; de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) goto end; result = (de_ctx->inspection_recursion_limit == 10); end: if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); DetectEngineDeInitYamlConf(); return result; } int DummyTestAppInspectionEngine01(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Signature *sig, Flow *f, uint8_t flags, void *alstate, void *tx, uint64_t tx_id) { return 0; } int DummyTestAppInspectionEngine02(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Signature *sig, Flow *f, uint8_t flags, void *alstate, void *tx, uint64_t tx_id) { return 0; } int DetectEngineTest05(void) { int result = 0; int ip = 0; DetectEngineAppInspectionEngine *engine_list[FLOW_PROTO_DEFAULT][ALPROTO_MAX][2]; memset(engine_list, 0, sizeof(engine_list)); DetectEngineRegisterAppInspectionEngine(IPPROTO_TCP, ALPROTO_HTTP, 0 /* STREAM_TOSERVER */, DETECT_SM_LIST_UMATCH, DE_STATE_FLAG_URI_INSPECT, DummyTestAppInspectionEngine01, engine_list); int alproto = ALPROTO_UNKNOWN + 1; for (ip = 0; ip < FLOW_PROTO_DEFAULT; ip++) { for ( ; alproto < ALPROTO_FAILED; alproto++) { int dir = 0; for ( ; dir < 2; dir++) { if (alproto == ALPROTO_HTTP && dir == 0) { if (engine_list[ip][alproto][dir]->next != NULL) { printf("more than one entry found\n"); goto end; } DetectEngineAppInspectionEngine *engine = engine_list[ip][alproto][dir]; if (engine->alproto != alproto || engine->dir != dir || engine->sm_list != DETECT_SM_LIST_UMATCH || engine->inspect_flags != DE_STATE_FLAG_URI_INSPECT || engine->Callback != DummyTestAppInspectionEngine01) { printf("failed for http and dir(0-toserver)\n"); goto end; } } /* if (alproto == ALPROTO_HTTP && dir == 0) */ if (alproto == ALPROTO_HTTP && dir == 1) { if (engine_list[ip][alproto][dir] != NULL) { printf("failed for http and dir(1-toclient)\n"); goto end; } } if (alproto != ALPROTO_HTTP && engine_list[ip][alproto][0] != NULL && engine_list[ip][alproto][1] != NULL) { printf("failed for protocol %d\n", alproto); goto end; } } /* for ( ; dir < 2 ..)*/ } /* for ( ; alproto < ALPROTO_FAILED; ..) */ } result = 1; end: return result; } int DetectEngineTest06(void) { int result = 0; int ip = 0; DetectEngineAppInspectionEngine *engine_list[FLOW_PROTO_DEFAULT][ALPROTO_MAX][2]; memset(engine_list, 0, sizeof(engine_list)); DetectEngineRegisterAppInspectionEngine(IPPROTO_TCP, ALPROTO_HTTP, 0 /* STREAM_TOSERVER */, DETECT_SM_LIST_UMATCH, DE_STATE_FLAG_URI_INSPECT, DummyTestAppInspectionEngine01, engine_list); DetectEngineRegisterAppInspectionEngine(IPPROTO_TCP, ALPROTO_HTTP, 1 /* STREAM_TOCLIENT */, DETECT_SM_LIST_UMATCH, DE_STATE_FLAG_URI_INSPECT, DummyTestAppInspectionEngine02, engine_list); int alproto = ALPROTO_UNKNOWN + 1; for (ip = 0; ip < FLOW_PROTO_DEFAULT; ip++) { for ( ; alproto < ALPROTO_FAILED; alproto++) { int dir = 0; for ( ; dir < 2; dir++) { if (alproto == ALPROTO_HTTP && dir == 0) { if (engine_list[ip][alproto][dir]->next != NULL) { printf("more than one entry found\n"); goto end; } DetectEngineAppInspectionEngine *engine = engine_list[ip][alproto][dir]; if (engine->alproto != alproto || engine->dir != dir || engine->sm_list != DETECT_SM_LIST_UMATCH || engine->inspect_flags != DE_STATE_FLAG_URI_INSPECT || engine->Callback != DummyTestAppInspectionEngine01) { printf("failed for http and dir(0-toserver)\n"); goto end; } } /* if (alproto == ALPROTO_HTTP && dir == 0) */ if (alproto == ALPROTO_HTTP && dir == 1) { if (engine_list[ip][alproto][dir]->next != NULL) { printf("more than one entry found\n"); goto end; } DetectEngineAppInspectionEngine *engine = engine_list[ip][alproto][dir]; if (engine->alproto != alproto || engine->dir != dir || engine->sm_list != DETECT_SM_LIST_UMATCH || engine->inspect_flags != DE_STATE_FLAG_URI_INSPECT || engine->Callback != DummyTestAppInspectionEngine02) { printf("failed for http and dir(0-toclient)\n"); goto end; } } /* if (alproto == ALPROTO_HTTP && dir == 1) */ if (alproto != ALPROTO_HTTP && engine_list[ip][alproto][0] != NULL && engine_list[ip][alproto][1] != NULL) { printf("failed for protocol %d\n", alproto); goto end; } } /* for ( ; dir < 2 ..)*/ } /* for ( ; alproto < ALPROTO_FAILED; ..) */ } result = 1; end: return result; } int DetectEngineTest07(void) { int result = 0; int ip = 0; DetectEngineAppInspectionEngine *engine_list[FLOW_PROTO_DEFAULT][ALPROTO_MAX][2]; memset(engine_list, 0, sizeof(engine_list)); struct test_data_t { int32_t sm_list; uint32_t inspect_flags; uint16_t dir; int (*Callback)(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Signature *sig, Flow *f, uint8_t flags, void *alstate, void *tx, uint64_t tx_id); }; struct test_data_t data[] = { { DETECT_SM_LIST_UMATCH, DE_STATE_FLAG_URI_INSPECT, 0, DummyTestAppInspectionEngine01 }, { DETECT_SM_LIST_HCBDMATCH, DE_STATE_FLAG_HCBD_INSPECT, 0, DummyTestAppInspectionEngine02 }, { DETECT_SM_LIST_FILEDATA, DE_STATE_FLAG_HSBD_INSPECT, 1, DummyTestAppInspectionEngine02 }, { DETECT_SM_LIST_HHDMATCH, DE_STATE_FLAG_HHD_INSPECT, 0, DummyTestAppInspectionEngine01 }, { DETECT_SM_LIST_HRHDMATCH, DE_STATE_FLAG_HRHD_INSPECT, 0, DummyTestAppInspectionEngine01 }, { DETECT_SM_LIST_HMDMATCH, DE_STATE_FLAG_HMD_INSPECT, 0, DummyTestAppInspectionEngine02 }, { DETECT_SM_LIST_HCDMATCH, DE_STATE_FLAG_HCD_INSPECT, 0, DummyTestAppInspectionEngine01 }, { DETECT_SM_LIST_HRUDMATCH, DE_STATE_FLAG_HRUD_INSPECT, 0, DummyTestAppInspectionEngine01 }, { DETECT_SM_LIST_FILEMATCH, DE_STATE_FLAG_FILE_TS_INSPECT, 0, DummyTestAppInspectionEngine02 }, { DETECT_SM_LIST_FILEMATCH, DE_STATE_FLAG_FILE_TC_INSPECT, 1, DummyTestAppInspectionEngine02 }, { DETECT_SM_LIST_HSMDMATCH, DE_STATE_FLAG_HSMD_INSPECT, 0, DummyTestAppInspectionEngine01 }, { DETECT_SM_LIST_HSCDMATCH, DE_STATE_FLAG_HSCD_INSPECT, 0, DummyTestAppInspectionEngine01 }, { DETECT_SM_LIST_HUADMATCH, DE_STATE_FLAG_HUAD_INSPECT, 0, DummyTestAppInspectionEngine02 }, }; size_t i = 0; for ( ; i < sizeof(data) / sizeof(struct test_data_t); i++) { DetectEngineRegisterAppInspectionEngine(IPPROTO_TCP, ALPROTO_HTTP, data[i].dir /* STREAM_TOCLIENT */, data[i].sm_list, data[i].inspect_flags, data[i].Callback, engine_list); } #if 0 DetectEnginePrintAppInspectionEngines(engine_list); #endif int alproto = ALPROTO_UNKNOWN + 1; for (ip = 0; ip < FLOW_PROTO_DEFAULT; ip++) { for ( ; alproto < ALPROTO_FAILED; alproto++) { int dir = 0; for ( ; dir < 2; dir++) { if (alproto == ALPROTO_HTTP) { DetectEngineAppInspectionEngine *engine = engine_list[ip][alproto][dir]; size_t i = 0; for ( ; i < (sizeof(data) / sizeof(struct test_data_t)); i++) { if (data[i].dir != dir) continue; if (engine->alproto != ALPROTO_HTTP || engine->dir != data[i].dir || engine->sm_list != data[i].sm_list || engine->inspect_flags != data[i].inspect_flags || engine->Callback != data[i].Callback) { printf("failed for http\n"); goto end; } engine = engine->next; } } else { if (engine_list[ip][alproto][0] != NULL && engine_list[ip][alproto][1] != NULL) { printf("failed for protocol %d\n", alproto); goto end; } } /* else */ } /* for ( ; dir < 2; dir++) */ } /* for ( ; alproto < ALPROTO_FAILED; ..) */ } result = 1; end: return result; } static int DetectEngineTest08(void) { char *conf = "%YAML 1.1\n" "---\n" "detect-engine:\n" " - profile: custom\n" " - custom-values:\n" " toclient-src-groups: 20\n" " toclient-dst-groups: 21\n" " toclient-sp-groups: 22\n" " toclient-dp-groups: 23\n" " toserver-src-groups: 24\n" " toserver-dst-groups: 25\n" " toserver-sp-groups: 26\n" " toserver-dp-groups: 27\n"; DetectEngineCtx *de_ctx = NULL; int result = 0; if (DetectEngineInitYamlConf(conf) == -1) return 0; de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) goto end; if (de_ctx->max_uniq_toclient_src_groups == 20 && de_ctx->max_uniq_toclient_dst_groups == 21 && de_ctx->max_uniq_toclient_sp_groups == 22 && de_ctx->max_uniq_toclient_dp_groups == 23 && de_ctx->max_uniq_toserver_src_groups == 24 && de_ctx->max_uniq_toserver_dst_groups == 25 && de_ctx->max_uniq_toserver_sp_groups == 26 && de_ctx->max_uniq_toserver_dp_groups == 27) result = 1; end: if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); DetectEngineDeInitYamlConf(); return result; } /** \test bug 892 bad values */ static int DetectEngineTest09(void) { char *conf = "%YAML 1.1\n" "---\n" "detect-engine:\n" " - profile: custom\n" " - custom-values:\n" " toclient-src-groups: BA\n" " toclient-dst-groups: BA\n" " toclient-sp-groups: BA\n" " toclient-dp-groups: BA\n" " toserver-src-groups: BA\n" " toserver-dst-groups: BA\n" " toserver-sp-groups: BA\n" " toserver-dp-groups: BA\n" " - inspection-recursion-limit: 10\n"; DetectEngineCtx *de_ctx = NULL; int result = 0; if (DetectEngineInitYamlConf(conf) == -1) return 0; de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) goto end; if (de_ctx->max_uniq_toclient_src_groups == 4 && de_ctx->max_uniq_toclient_dst_groups == 4 && de_ctx->max_uniq_toclient_sp_groups == 4 && de_ctx->max_uniq_toclient_dp_groups == 6 && de_ctx->max_uniq_toserver_src_groups == 4 && de_ctx->max_uniq_toserver_dst_groups == 8 && de_ctx->max_uniq_toserver_sp_groups == 4 && de_ctx->max_uniq_toserver_dp_groups == 30) result = 1; end: if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); DetectEngineDeInitYamlConf(); return result; } #endif void DetectEngineRegisterTests() { #ifdef UNITTESTS UtRegisterTest("DetectEngineTest01", DetectEngineTest01, 1); UtRegisterTest("DetectEngineTest02", DetectEngineTest02, 1); UtRegisterTest("DetectEngineTest03", DetectEngineTest03, 1); UtRegisterTest("DetectEngineTest04", DetectEngineTest04, 1); UtRegisterTest("DetectEngineTest05", DetectEngineTest05, 1); UtRegisterTest("DetectEngineTest06", DetectEngineTest06, 1); UtRegisterTest("DetectEngineTest07", DetectEngineTest07, 1); UtRegisterTest("DetectEngineTest08", DetectEngineTest08, 1); UtRegisterTest("DetectEngineTest09", DetectEngineTest09, 1); #endif return; }