From 404395b9901c2e18a51bafaac9a18c72e7fdd237 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Thu, 16 Nov 2023 14:35:49 +0100 Subject: [PATCH] plugins: app-layer plugins Ticket: 5053 --- src/app-layer-parser.c | 24 +++++++++++ src/app-layer-parser.h | 1 + src/app-layer-protos.c | 6 +-- src/detect-engine-file.h | 3 ++ src/detect-engine-register.c | 25 +++++++++++ src/detect-engine-register.h | 1 + src/detect-parse.c | 81 +++++++++++++++++++++++------------- src/output.c | 33 +++++++++++++++ src/output.h | 9 ++++ src/suricata-plugin.h | 16 +++++++ src/util-plugin.c | 36 ++++++++++++++++ 11 files changed, 204 insertions(+), 31 deletions(-) diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index 010c40a04c1e..1abe2f9f2d45 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -1700,6 +1700,27 @@ static void ValidateParsers(void) } } +#define ARRAY_CAP_STEP 16 +static void (**preregistered_callbacks)(void) = NULL; +static size_t preregistered_callbacks_nb = 0; +static size_t preregistered_callbacks_cap = 0; + +int AppLayerParserPreRegister(void (*Register)(void)) +{ + if (preregistered_callbacks_nb == preregistered_callbacks_cap) { + void *tmp = SCRealloc(preregistered_callbacks, + sizeof(void *) * (preregistered_callbacks_cap + ARRAY_CAP_STEP)); + if (tmp == NULL) { + return 1; + } + preregistered_callbacks_cap += ARRAY_CAP_STEP; + preregistered_callbacks = tmp; + } + preregistered_callbacks[preregistered_callbacks_nb] = Register; + preregistered_callbacks_nb++; + return 0; +} + void AppLayerParserRegisterProtocolParsers(void) { SCEnter(); @@ -1752,6 +1773,9 @@ void AppLayerParserRegisterProtocolParsers(void) } else { SCLogInfo("Protocol detection and parser disabled for pop3 protocol."); } + for (size_t i = 0; i < preregistered_callbacks_nb; i++) { + preregistered_callbacks[i](); + } ValidateParsers(); } diff --git a/src/app-layer-parser.h b/src/app-layer-parser.h index 58ad4333563c..d233edf9eb1f 100644 --- a/src/app-layer-parser.h +++ b/src/app-layer-parser.h @@ -158,6 +158,7 @@ typedef AppLayerGetTxIterTuple (*AppLayerGetTxIteratorFunc) typedef int (*AppLayerParserGetFrameIdByNameFn)(const char *frame_name); typedef const char *(*AppLayerParserGetFrameNameByIdFn)(const uint8_t id); +int AppLayerParserPreRegister(void (*Register)(void)); /** * \brief Register app layer parser for the protocol. * diff --git a/src/app-layer-protos.c b/src/app-layer-protos.c index e97d94ce12af..710f698fc725 100644 --- a/src/app-layer-protos.c +++ b/src/app-layer-protos.c @@ -26,9 +26,9 @@ #include "app-layer-protos.h" #include "rust.h" -AppProto AlprotoMax = ALPROTO_MAX_STATIC + 1; +AppProto AlprotoMax = ALPROTO_MAX_STATIC; #define ARRAY_CAP_STEP 16 -AppProto AppProtoStringsCap = ALPROTO_MAX_STATIC + 1; +AppProto AppProtoStringsCap = ALPROTO_MAX_STATIC; typedef struct AppProtoStringTuple { AppProto alproto; @@ -80,7 +80,7 @@ void AppProtoRegisterProtoString(AppProto alproto, const char *proto_name) FatalError("Unable to allocate AppProtoStrings"); } } - } else if (alproto + 1 == AlprotoMax) { + } else if (alproto == AlprotoMax) { if (AlprotoMax == AppProtoStringsCap) { void *tmp = SCRealloc(AppProtoStrings, sizeof(AppProtoStringTuple) * (AppProtoStringsCap + ARRAY_CAP_STEP)); diff --git a/src/detect-engine-file.h b/src/detect-engine-file.h index 13aa7465f436..10bcf5ca7dbc 100644 --- a/src/detect-engine-file.h +++ b/src/detect-engine-file.h @@ -28,4 +28,7 @@ uint8_t DetectFileInspectGeneric(DetectEngineCtx *de_ctx, DetectEngineThreadCtx const struct DetectEngineAppInspectionEngine_ *engine, const Signature *s, Flow *f, uint8_t flags, void *_alstate, void *tx, uint64_t tx_id); +void DetectFileRegisterProto( + AppProto alproto, int direction, int to_client_progress, int to_server_progress); + #endif /* SURICATA_DETECT_ENGINE_FILE_H */ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 9bddf0fd8437..77defa2fbb89 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -461,6 +461,27 @@ void SigTableCleanup(void) } } +#define ARRAY_CAP_STEP 16 +static void (**preregistered_callbacks)(void) = NULL; +static size_t preregistered_callbacks_nb = 0; +static size_t preregistered_callbacks_cap = 0; + +int SigTablePreRegister(void (*KeywordsRegister)(void)) +{ + if (preregistered_callbacks_nb == preregistered_callbacks_cap) { + void *tmp = SCRealloc(preregistered_callbacks, + sizeof(void *) * (preregistered_callbacks_cap + ARRAY_CAP_STEP)); + if (tmp == NULL) { + return 1; + } + preregistered_callbacks_cap += ARRAY_CAP_STEP; + preregistered_callbacks = tmp; + } + preregistered_callbacks[preregistered_callbacks_nb] = KeywordsRegister; + preregistered_callbacks_nb++; + return 0; +} + void SigTableInit(void) { if (sigmatch_table == NULL) { @@ -708,6 +729,10 @@ void SigTableSetup(void) ScDetectSipRegister(); ScDetectTemplateRegister(); + for (size_t i = 0; i < preregistered_callbacks_nb; i++) { + preregistered_callbacks[i](); + } + /* close keyword registration */ DetectBufferTypeCloseRegistration(); } diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index b7a029998555..69ddcfa3a268 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -343,6 +343,7 @@ int SigTableList(const char *keyword); void SigTableCleanup(void); void SigTableInit(void); void SigTableSetup(void); +int SigTablePreRegister(void (*KeywordsRegister)(void)); void SigTableRegisterTests(void); bool SigTableHasKeyword(const char *keyword); diff --git a/src/detect-parse.c b/src/detect-parse.c index 90d3b5706d3d..f59d8e1c67f5 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -69,55 +69,80 @@ #include "string.h" #include "detect-parse.h" #include "detect-engine-iponly.h" +#include "detect-engine-file.h" #include "app-layer-detect-proto.h" #include "action-globals.h" #include "util-validate.h" +// file protocols with common file handling +typedef struct { + AppProto alproto; + int direction; + int to_client_progress; + int to_server_progress; +} DetectFileHandlerProtocol_t; + /* Table with all filehandler registrations */ DetectFileHandlerTableElmt filehandler_table[DETECT_TBLSIZE_STATIC]; +#define ALPROTO_WITHFILES_MAX 16 + +// file protocols with common file handling +DetectFileHandlerProtocol_t al_protocols[ALPROTO_WITHFILES_MAX] = { + { .alproto = ALPROTO_NFS, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, + { .alproto = ALPROTO_SMB, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, + { .alproto = ALPROTO_FTP, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, + { .alproto = ALPROTO_FTPDATA, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, + { .alproto = ALPROTO_HTTP1, + .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT, + .to_client_progress = HTP_RESPONSE_BODY, + .to_server_progress = HTP_REQUEST_BODY }, + { .alproto = ALPROTO_HTTP2, + .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT, + .to_client_progress = HTTP2StateDataServer, + .to_server_progress = HTTP2StateDataClient }, + { .alproto = ALPROTO_SMTP, .direction = SIG_FLAG_TOSERVER }, { .alproto = ALPROTO_UNKNOWN } +}; + +void DetectFileRegisterProto( + AppProto alproto, int direction, int to_client_progress, int to_server_progress) +{ + size_t i = 0; + while (i < ALPROTO_WITHFILES_MAX && al_protocols[i].alproto != ALPROTO_UNKNOWN) { + i++; + } + if (i == ALPROTO_WITHFILES_MAX) { + return; + } + al_protocols[i].alproto = alproto; + al_protocols[i].direction = direction; + al_protocols[i].to_client_progress = to_client_progress; + al_protocols[i].to_server_progress = to_server_progress; + al_protocols[i + 1].alproto = ALPROTO_UNKNOWN; +} + void DetectFileRegisterFileProtocols(DetectFileHandlerTableElmt *reg) { - // file protocols with common file handling - typedef struct { - AppProto al_proto; - int direction; - int to_client_progress; - int to_server_progress; - } DetectFileHandlerProtocol_t; - static DetectFileHandlerProtocol_t al_protocols[] = { - { .al_proto = ALPROTO_NFS, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, - { .al_proto = ALPROTO_SMB, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, - { .al_proto = ALPROTO_FTP, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, - { .al_proto = ALPROTO_FTPDATA, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, - { .al_proto = ALPROTO_HTTP1, - .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT, - .to_client_progress = HTP_RESPONSE_BODY, - .to_server_progress = HTP_REQUEST_BODY }, - { .al_proto = ALPROTO_HTTP2, - .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT, - .to_client_progress = HTTP2StateDataServer, - .to_server_progress = HTTP2StateDataClient }, - { .al_proto = ALPROTO_SMTP, .direction = SIG_FLAG_TOSERVER } - }; - - for (size_t i = 0; i < ARRAY_SIZE(al_protocols); i++) { + for (size_t i = 0; i < AlprotoMax; i++) { + if (al_protocols[i].alproto == ALPROTO_UNKNOWN) { + break; + } int direction = al_protocols[i].direction == 0 ? (int)(SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT) : al_protocols[i].direction; if (direction & SIG_FLAG_TOCLIENT) { DetectAppLayerMpmRegister(reg->name, SIG_FLAG_TOCLIENT, reg->priority, reg->PrefilterFn, - reg->GetData, al_protocols[i].al_proto, al_protocols[i].to_client_progress); - DetectAppLayerInspectEngineRegister(reg->name, al_protocols[i].al_proto, + reg->GetData, al_protocols[i].alproto, al_protocols[i].to_client_progress); + DetectAppLayerInspectEngineRegister(reg->name, al_protocols[i].alproto, SIG_FLAG_TOCLIENT, al_protocols[i].to_client_progress, reg->Callback, reg->GetData); } if (direction & SIG_FLAG_TOSERVER) { DetectAppLayerMpmRegister(reg->name, SIG_FLAG_TOSERVER, reg->priority, reg->PrefilterFn, - reg->GetData, al_protocols[i].al_proto, al_protocols[i].to_server_progress); - DetectAppLayerInspectEngineRegister(reg->name, al_protocols[i].al_proto, + reg->GetData, al_protocols[i].alproto, al_protocols[i].to_server_progress); + DetectAppLayerInspectEngineRegister(reg->name, al_protocols[i].alproto, SIG_FLAG_TOSERVER, al_protocols[i].to_server_progress, reg->Callback, reg->GetData); } diff --git a/src/output.c b/src/output.c index ef9e396e3c03..d5b10c04cb21 100644 --- a/src/output.c +++ b/src/output.c @@ -949,6 +949,28 @@ static int JsonGenericDirFlowLogger(ThreadVars *tv, void *thread_data, const Pac return JsonGenericLogger(tv, thread_data, p, f, state, tx, tx_id, LOG_DIR_FLOW); } +#define ARRAY_CAP_STEP 16 +static EveJsonLoggerRegistrationData *preregistered_loggers = NULL; +static size_t preregistered_loggers_nb = 0; +static size_t preregistered_loggers_cap = 0; + +int OutputPreRegisterLogger(EveJsonLoggerRegistrationData reg_data) +{ + if (preregistered_loggers_nb == preregistered_loggers_cap) { + void *tmp = SCRealloc( + preregistered_loggers, sizeof(EveJsonLoggerRegistrationData) * + (preregistered_loggers_cap + ARRAY_CAP_STEP)); + if (tmp == NULL) { + return 1; + } + preregistered_loggers_cap += ARRAY_CAP_STEP; + preregistered_loggers = tmp; + } + preregistered_loggers[preregistered_loggers_nb] = reg_data; + preregistered_loggers_nb++; + return 0; +} + /** * \brief Register all non-root logging modules. */ @@ -1105,4 +1127,15 @@ void OutputRegisterLoggers(void) } /* ARP JSON logger */ JsonArpLogRegister(); + + for (size_t i = 0; i < preregistered_loggers_nb; i++) { + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", preregistered_loggers[i].logname, + preregistered_loggers[i].confname, OutputJsonLogInitSub, + preregistered_loggers[i].alproto, JsonGenericDirFlowLogger, JsonLogThreadInit, + JsonLogThreadDeinit); + SCLogNotice( + "%s JSON logger registered.", AppProtoToString(preregistered_loggers[i].alproto)); + RegisterSimpleJsonApplayerLogger( + preregistered_loggers[i].alproto, preregistered_loggers[i].LogTx, NULL); + } } diff --git a/src/output.h b/src/output.h index 43bd9d8b8f74..3820ae1665d8 100644 --- a/src/output.h +++ b/src/output.h @@ -170,4 +170,13 @@ typedef struct EveJsonSimpleAppLayerLogger { EveJsonSimpleAppLayerLogger *SCEveJsonSimpleGetLogger(AppProto alproto); +typedef struct EveJsonLoggerRegistrationData { + const char *confname; + const char *logname; + AppProto alproto; + EveJsonSimpleTxLogFunc LogTx; +} EveJsonLoggerRegistrationData; + +int OutputPreRegisterLogger(EveJsonLoggerRegistrationData reg_data); + #endif /* ! SURICATA_OUTPUT_H */ diff --git a/src/suricata-plugin.h b/src/suricata-plugin.h index 639dd7c7313e..8bc2183d70fd 100644 --- a/src/suricata-plugin.h +++ b/src/suricata-plugin.h @@ -52,4 +52,20 @@ typedef struct SCCapturePlugin_ { int SCPluginRegisterCapture(SCCapturePlugin *); +// Every change in the API used by plugins should change this number +#define SC_PLUGIN_API_VERSION 8 + +typedef struct SCAppLayerPlugin_ { + // versioning to check suricata/plugin API compatibility + uint64_t version; + char *name; + void (*Register)(void); + void (*KeywordsRegister)(void); + char *logname; + char *confname; + bool (*Logger)(void *tx, void *jb); +} SCAppLayerPlugin; + +int SCPluginRegisterAppLayer(SCAppLayerPlugin *); + #endif /* __SURICATA_PLUGIN_H */ diff --git a/src/util-plugin.c b/src/util-plugin.c index 7a9b467daaac..ebcaeace9dd3 100644 --- a/src/util-plugin.c +++ b/src/util-plugin.c @@ -25,6 +25,11 @@ #ifdef HAVE_PLUGINS +#include "app-layer-protos.h" +#include "app-layer-parser.h" +#include "detect-engine-register.h" +#include "output.h" + #include typedef struct PluginListNode_ { @@ -148,4 +153,35 @@ SCCapturePlugin *SCPluginFindCaptureByName(const char *name) } return plugin; } + +int SCPluginRegisterAppLayer(SCAppLayerPlugin *plugin) +{ + if (plugin->version != SC_PLUGIN_API_VERSION) { + return 1; + } + AppProto alproto = AlprotoMax; + AppProtoRegisterProtoString(alproto, plugin->name); + if (plugin->Register) { + if (AppLayerParserPreRegister(plugin->Register) != 0) { + return 1; + } + } + if (plugin->KeywordsRegister) { + if (SigTablePreRegister(plugin->KeywordsRegister) != 0) { + return 1; + } + } + if (plugin->Logger) { + EveJsonLoggerRegistrationData reg_data = { + .confname = plugin->confname, + .logname = plugin->logname, + .alproto = alproto, + .LogTx = (EveJsonSimpleTxLogFunc)plugin->Logger, + }; + if (OutputPreRegisterLogger(reg_data) != 0) { + return 1; + } + } + return 0; +} #endif