new_engine->sm_list = t->sm_list;
new_engine->sm_list_base = t->sm_list_base;
new_engine->smd = smd;
- new_engine->match_on_null = DetectContentInspectionMatchOnAbsentBuffer(smd);
+ new_engine->match_on_null = smd ? DetectContentInspectionMatchOnAbsentBuffer(smd) : false;
new_engine->progress = t->progress;
new_engine->v2 = t->v2;
SCLogDebug("sm_list %d new_engine->v2 %p/%p/%p", new_engine->sm_list, new_engine->v2.Callback,
const int files_id = DetectBufferTypeGetByName("files");
bool head_is_mpm = false;
uint8_t last_id = DE_STATE_FLAG_BASE;
+ SCLogDebug("%u: setup app inspect engines. %u buffers", s->id, s->init_data->buffer_index);
for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
SigMatchData *smd = SigMatchList2DataArray(s->init_data->buffers[x].head);
}
}
+ /* handle rules that have an app-layer hook w/o bringing their own app inspect engine,
+ * e.g. `alert dns:request_complete ... (sid:1;)`
+ *
+ * Here we use a minimal stub inspect engine in which we set:
+ * - alproto
+ * - progress
+ * - sm_list/sm_list_base to get the mapping to the hook name
+ * - dir based on sig direction
+ *
+ * The inspect engine has no callback and is thus considered a straight match.
+ */
+ if (s->init_data->buffer_index == 0 && s->init_data->hook.type == SIGNATURE_HOOK_TYPE_APP) {
+ uint8_t dir = 0;
+ if ((s->flags & (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)) ==
+ (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT))
+ abort();
+ if ((s->flags & (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)) == 0)
+ abort();
+ if (s->flags & SIG_FLAG_TOSERVER)
+ dir = 0;
+ else if (s->flags & SIG_FLAG_TOCLIENT)
+ dir = 1;
+
+ DetectEngineAppInspectionEngine t = {
+ .alproto = s->init_data->hook.t.app.alproto,
+ .progress = (uint16_t)s->init_data->hook.t.app.app_progress,
+ .sm_list = (uint16_t)s->init_data->hook.sm_list,
+ .sm_list_base = (uint16_t)s->init_data->hook.sm_list,
+ .dir = dir,
+ };
+ AppendAppInspectEngine(de_ctx, &t, s, NULL, mpm_list, files_id, &last_id, &head_is_mpm);
+ }
+
if ((s->init_data->init_flags & SIG_FLAG_INIT_STATE_MATCH) &&
s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL)
{
return -1;
}
+static bool IsBuiltIn(const char *n)
+{
+ if (strcmp(n, "request_started") == 0 || strcmp(n, "response_started") == 0) {
+ return true;
+ }
+ if (strcmp(n, "request_complete") == 0 || strcmp(n, "response_complete") == 0) {
+ return true;
+ }
+ return false;
+}
+
+/** \brief register app hooks as generic lists
+ *
+ * Register each hook in each app protocol as:
+ * <alproto>:<hook name>:generic
+ * These lists can be used by lua scripts to hook into.
+ *
+ * \todo move elsewhere? maybe a detect-engine-hook.c?
+ */
+void DetectRegisterAppLayerHookLists(void)
+{
+ for (AppProto a = ALPROTO_FAILED + 1; a < g_alproto_max; a++) {
+ const char *alproto_name = AppProtoToString(a);
+ if (strcmp(alproto_name, "http") == 0)
+ alproto_name = "http1";
+ SCLogDebug("alproto %u/%s", a, alproto_name);
+
+ const int max_progress_ts =
+ AppLayerParserGetStateProgressCompletionStatus(a, STREAM_TOSERVER);
+ const int max_progress_tc =
+ AppLayerParserGetStateProgressCompletionStatus(a, STREAM_TOCLIENT);
+
+ char ts_tx_started[64];
+ snprintf(ts_tx_started, sizeof(ts_tx_started), "%s:request_started:generic", alproto_name);
+ DetectAppLayerInspectEngineRegister(
+ ts_tx_started, a, SIG_FLAG_TOSERVER, 0, DetectEngineInspectGenericList, NULL);
+ SCLogDebug("- hook %s:%s list %s (%u)", alproto_name, "request_name", ts_tx_started,
+ (uint32_t)strlen(ts_tx_started));
+
+ char tc_tx_started[64];
+ snprintf(tc_tx_started, sizeof(tc_tx_started), "%s:response_started:generic", alproto_name);
+ DetectAppLayerInspectEngineRegister(
+ tc_tx_started, a, SIG_FLAG_TOCLIENT, 0, DetectEngineInspectGenericList, NULL);
+ SCLogDebug("- hook %s:%s list %s (%u)", alproto_name, "response_name", tc_tx_started,
+ (uint32_t)strlen(tc_tx_started));
+
+ char ts_tx_complete[64];
+ snprintf(ts_tx_complete, sizeof(ts_tx_complete), "%s:request_complete:generic",
+ alproto_name);
+ DetectAppLayerInspectEngineRegister(ts_tx_complete, a, SIG_FLAG_TOSERVER, max_progress_ts,
+ DetectEngineInspectGenericList, NULL);
+ SCLogDebug("- hook %s:%s list %s (%u)", alproto_name, "request_name", ts_tx_complete,
+ (uint32_t)strlen(ts_tx_complete));
+
+ char tc_tx_complete[64];
+ snprintf(tc_tx_complete, sizeof(tc_tx_complete), "%s:response_complete:generic",
+ alproto_name);
+ DetectAppLayerInspectEngineRegister(tc_tx_complete, a, SIG_FLAG_TOCLIENT, max_progress_tc,
+ DetectEngineInspectGenericList, NULL);
+ SCLogDebug("- hook %s:%s list %s (%u)", alproto_name, "response_name", tc_tx_complete,
+ (uint32_t)strlen(tc_tx_complete));
+
+ for (int p = 0; p <= max_progress_ts; p++) {
+ const char *name = AppLayerParserGetStateNameById(
+ IPPROTO_TCP /* TODO no ipproto */, a, p, STREAM_TOSERVER);
+ if (name != NULL && !IsBuiltIn(name)) {
+ char list_name[64];
+ snprintf(list_name, sizeof(list_name), "%s:%s:generic", alproto_name, name);
+ SCLogDebug("- hook %s:%s list %s (%u)", alproto_name, name, list_name,
+ (uint32_t)strlen(list_name));
+
+ DetectAppLayerInspectEngineRegister(
+ list_name, a, SIG_FLAG_TOSERVER, p, DetectEngineInspectGenericList, NULL);
+ }
+ }
+ for (int p = 0; p <= max_progress_tc; p++) {
+ const char *name = AppLayerParserGetStateNameById(
+ IPPROTO_TCP /* TODO no ipproto */, a, p, STREAM_TOCLIENT);
+ if (name != NULL && !IsBuiltIn(name)) {
+ char list_name[64];
+ snprintf(list_name, sizeof(list_name), "%s:%s:generic", alproto_name, name);
+ SCLogDebug("- hook %s:%s list %s (%u)", alproto_name, name, list_name,
+ (uint32_t)strlen(list_name));
+
+ DetectAppLayerInspectEngineRegister(
+ list_name, a, SIG_FLAG_TOCLIENT, p, DetectEngineInspectGenericList, NULL);
+ }
+ }
+ }
+}
+
+static const char *SignatureHookTypeToString(enum SignatureHookType t)
+{
+ switch (t) {
+ case SIGNATURE_HOOK_TYPE_NOT_SET:
+ return "not_set";
+ case SIGNATURE_HOOK_TYPE_APP:
+ return "app";
+ // case SIGNATURE_HOOK_TYPE_PKT:
+ // return "pkt";
+ }
+ return "unknown";
+}
+
+static SignatureHook SetAppHook(const AppProto alproto, int progress)
+{
+ SignatureHook h = {
+ .type = SIGNATURE_HOOK_TYPE_APP,
+ .t.app.alproto = alproto,
+ .t.app.app_progress = progress,
+ };
+ return h;
+}
+
+/**
+ * \param proto_hook string of protocol and hook, e.g. dns:request_complete
+ */
+static int SigParseProtoHookApp(Signature *s, const char *proto_hook, const char *p, const char *h)
+{
+ if (strcmp(h, "request_started") == 0) {
+ s->flags |= SIG_FLAG_TOSERVER;
+ s->init_data->hook =
+ SetAppHook(s->alproto, 0); // state 0 should be the starting state in each protocol.
+ } else if (strcmp(h, "response_started") == 0) {
+ s->flags |= SIG_FLAG_TOCLIENT;
+ s->init_data->hook =
+ SetAppHook(s->alproto, 0); // state 0 should be the starting state in each protocol.
+ } else if (strcmp(h, "request_complete") == 0) {
+ s->flags |= SIG_FLAG_TOSERVER;
+ s->init_data->hook = SetAppHook(s->alproto,
+ AppLayerParserGetStateProgressCompletionStatus(s->alproto, STREAM_TOSERVER));
+ } else if (strcmp(h, "response_complete") == 0) {
+ s->flags |= SIG_FLAG_TOCLIENT;
+ s->init_data->hook = SetAppHook(s->alproto,
+ AppLayerParserGetStateProgressCompletionStatus(s->alproto, STREAM_TOCLIENT));
+ } else {
+ const int progress_ts = AppLayerParserGetStateIdByName(
+ IPPROTO_TCP /* TODO */, s->alproto, h, STREAM_TOSERVER);
+ if (progress_ts >= 0) {
+ s->flags |= SIG_FLAG_TOSERVER;
+ s->init_data->hook = SetAppHook(s->alproto, progress_ts);
+ } else {
+ const int progress_tc = AppLayerParserGetStateIdByName(
+ IPPROTO_TCP /* TODO */, s->alproto, h, STREAM_TOCLIENT);
+ if (progress_tc < 0) {
+ return -1;
+ }
+ s->flags |= SIG_FLAG_TOCLIENT;
+ s->init_data->hook = SetAppHook(s->alproto, progress_tc);
+ }
+ }
+
+ char generic_hook_name[64];
+ snprintf(generic_hook_name, sizeof(generic_hook_name), "%s:generic", proto_hook);
+ int list = DetectBufferTypeGetByName(generic_hook_name);
+ if (list < 0) {
+ SCLogError("no list registered as %s for hook %s", generic_hook_name, proto_hook);
+ return -1;
+ }
+ s->init_data->hook.sm_list = list;
+
+ SCLogNotice("protocol:%s hook:%s: type:%s alproto:%u hook:%d", p, h,
+ SignatureHookTypeToString(s->init_data->hook.type), s->init_data->hook.t.app.alproto,
+ s->init_data->hook.t.app.app_progress);
+ return 0;
+}
+
/**
* \brief Parses the protocol supplied by the Signature.
*
static int SigParseProto(Signature *s, const char *protostr)
{
SCEnter();
+ if (strlen(protostr) > 32)
+ return -1;
+
+ char proto[33];
+ strlcpy(proto, protostr, 33);
+ const char *p = proto;
+ const char *h = NULL;
- int r = DetectProtoParse(&s->proto, (char *)protostr);
+ bool has_hook = strchr(proto, ':') != NULL;
+ if (has_hook) {
+ char *xsaveptr = NULL;
+ p = strtok_r(proto, ":", &xsaveptr);
+ h = strtok_r(NULL, ":", &xsaveptr);
+ SCLogDebug("p: '%s' h: '%s'", p, h);
+ }
+ if (p == NULL) {
+ SCLogError("invalid protocol specification '%s'", proto);
+ return -1;
+ }
+
+ int r = DetectProtoParse(&s->proto, p);
if (r < 0) {
- s->alproto = AppLayerGetProtoByName((char *)protostr);
+ s->alproto = AppLayerGetProtoByName(p);
/* indicate that the signature is app-layer */
if (s->alproto != ALPROTO_UNKNOWN) {
s->flags |= SIG_FLAG_APPLAYER;
AppLayerProtoDetectSupportedIpprotos(s->alproto, s->proto.proto);
+
+ if (h) {
+ if (SigParseProtoHookApp(s, protostr, p, h) < 0) {
+ SCLogError("protocol \"%s\" does not support hook \"%s\"", p, h);
+ SCReturnInt(-1);
+ }
+ }
}
else {
SCLogError("protocol \"%s\" cannot be used "
"is not yet supported OR detection has been disabled for "
"protocol through the yaml option "
"app-layer.protocols.%s.detection-enabled",
- protostr, protostr);
+ p, p);
SCReturnInt(-1);
}
}
DetectEngineBufferTypeGetNameById(de_ctx, app->sm_list), app->dir,
app->alproto);
SCLogDebug("b->id %d nlists %d", b->id, nlists);
+
if (b->only_tc) {
if (app->dir == 1)
tc_excl++;
bufdir[b->id].ts += (app->dir == 0);
bufdir[b->id].tc += (app->dir == 1);
}
+
+ /* only allow rules to use the hook for engines at that
+ * exact progress for now. */
+ if (s->init_data->hook.type == SIGNATURE_HOOK_TYPE_APP) {
+ if ((s->flags & SIG_FLAG_TOSERVER) && (app->dir == 0) &&
+ app->progress != s->init_data->hook.t.app.app_progress) {
+ SCLogError("engine progress value %d doesn't match hook %u", app->progress,
+ s->init_data->hook.t.app.app_progress);
+ SCReturnInt(0);
+ }
+ if ((s->flags & SIG_FLAG_TOCLIENT) && (app->dir == 1) &&
+ app->progress != s->init_data->hook.t.app.app_progress) {
+ SCLogError("engine progress value doesn't match hook");
+ SCReturnInt(0);
+ }
+ }
}
}