diff --git a/src/Makefile.am b/src/Makefile.am index e28eecb0f9..ee4c320906 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -330,6 +330,7 @@ noinst_HEADERS = \ detect-tls-certs.h \ detect-tls-cert-subject.h \ detect-tls-cert-validity.h \ + detect-tls-subjectaltname.h \ detect-tls.h \ detect-tls-ja3-hash.h \ detect-tls-ja3s-hash.h \ @@ -941,6 +942,7 @@ libsuricata_c_a_SOURCES = \ detect-tls-cert-serial.c \ detect-tls-cert-subject.c \ detect-tls-cert-validity.c \ + detect-tls-subjectaltname.c \ detect-tls-ja3-hash.c \ detect-tls-ja3s-hash.c \ detect-tls-ja3s-string.c \ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 5608ae218f..4e39dfba98 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -59,6 +59,7 @@ #include "detect-tls-cert-issuer.h" #include "detect-tls-cert-subject.h" #include "detect-tls-cert-serial.h" +#include "detect-tls-subjectaltname.h" #include "detect-tls-random.h" #include "detect-tls-ja3-hash.h" #include "detect-tls-ja3-string.h" @@ -551,6 +552,7 @@ void SigTableSetup(void) DetectTlsFingerprintRegister(); DetectTlsCertsRegister(); DetectTlsCertChainLenRegister(); + DetectTlsSubjectAltNameRegister(); DetectTlsRandomRegister(); DetectTlsJa3HashRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index cd2edf5979..ee6f4cf9a8 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -240,6 +240,7 @@ enum DetectKeywordId { DETECT_AL_TLS_CERT_SUBJECT, DETECT_AL_TLS_CERT_SERIAL, DETECT_AL_TLS_CERT_FINGERPRINT, + DETECT_AL_TLS_SUBJECTALTNAME, DETECT_AL_TLS_RANDOM_TIME, DETECT_AL_TLS_RANDOM_BYTES, DETECT_AL_TLS_RANDOM, diff --git a/src/detect-tls-subjectaltname.c b/src/detect-tls-subjectaltname.c new file mode 100644 index 0000000000..cf61aa0b8c --- /dev/null +++ b/src/detect-tls-subjectaltname.c @@ -0,0 +1,208 @@ +/* Copyright (C) 2024 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 Shivani Bhardwaj + * + * Implements support for tls.subjectaltname keyword. + */ + +#include "suricata-common.h" +#include "threads.h" +#include "decode.h" +#include "detect.h" + +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-mpm.h" +#include "detect-engine-prefilter.h" +#include "detect-engine-content-inspection.h" +#include "detect-content.h" +#include "detect-tls-subjectaltname.h" +#include "detect-engine-uint.h" + +#include "flow.h" +#include "flow-util.h" +#include "flow-var.h" + +#include "util-debug.h" +#include "util-spm.h" +#include "util-print.h" + +#include "stream-tcp.h" + +#include "app-layer.h" +#include "app-layer-ssl.h" +#include "util-profiling.h" + +static int DetectTlsSubjectAltNameSetup(DetectEngineCtx *, Signature *, const char *); +static uint8_t DetectEngineInspectTlsSubjectAltName(DetectEngineCtx *de_ctx, + DetectEngineThreadCtx *det_ctx, const DetectEngineAppInspectionEngine *engine, + const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id); +static int PrefilterMpmTlsSubjectAltNameRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, + MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id); + +static int g_tls_subjectaltname_buffer_id = 0; + +typedef struct PrefilterMpmTlsSubjectAltName { + int list_id; + const MpmCtx *mpm_ctx; + const DetectEngineTransforms *transforms; +} PrefilterMpmTlsSubjectAltName; + +/** + * \brief Registration function for keyword: tls.subjectaltname + */ +void DetectTlsSubjectAltNameRegister(void) +{ + sigmatch_table[DETECT_AL_TLS_SUBJECTALTNAME].name = "tls.subjectaltname"; + sigmatch_table[DETECT_AL_TLS_SUBJECTALTNAME].desc = + "sticky buffer to match the TLS Subject Alternative Name buffer"; + sigmatch_table[DETECT_AL_TLS_SUBJECTALTNAME].url = + "/rules/tls-keywords.html#tls-subjectaltname"; + sigmatch_table[DETECT_AL_TLS_SUBJECTALTNAME].Setup = DetectTlsSubjectAltNameSetup; + sigmatch_table[DETECT_AL_TLS_SUBJECTALTNAME].flags |= SIGMATCH_NOOPT; + sigmatch_table[DETECT_AL_TLS_SUBJECTALTNAME].flags |= SIGMATCH_INFO_STICKY_BUFFER; + + DetectAppLayerInspectEngineRegister("tls.subjectaltname", ALPROTO_TLS, SIG_FLAG_TOCLIENT, + TLS_STATE_CERT_READY, DetectEngineInspectTlsSubjectAltName, NULL); + + DetectAppLayerMpmRegister("tls.subjectaltname", SIG_FLAG_TOCLIENT, 2, + PrefilterMpmTlsSubjectAltNameRegister, NULL, ALPROTO_TLS, TLS_STATE_CERT_READY); + + DetectBufferTypeSetDescriptionByName("tls.subjectaltname", "TLS Subject Alternative Name"); + + DetectBufferTypeSupportsMultiInstance("tls.subjectaltname"); + + g_tls_subjectaltname_buffer_id = DetectBufferTypeGetByName("tls.subjectaltname"); +} + +/** + * \brief This function setup the tls.subjectaltname sticky buffer keyword + * + * \param de_ctx Pointer to the Detect Engine Context + * \param s Pointer to the Signature to which the keyword belongs + * \param str Should hold an empty string always + * + * \retval 0 On success + * \retval -1 On failure + */ +static int DetectTlsSubjectAltNameSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str) +{ + if (DetectBufferSetActiveList(de_ctx, s, g_tls_subjectaltname_buffer_id) < 0) + return -1; + + if (DetectSignatureSetAppProto(s, ALPROTO_TLS) < 0) + return -1; + + return 0; +} + +static InspectionBuffer *TlsSubjectAltNameGetData(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *f, uint8_t flags, uint16_t idx, int list_id) +{ + SCEnter(); + InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, idx); + if (buffer == NULL || buffer->initialized) + return buffer; + + const SSLState *ssl_state = (SSLState *)f->alstate; + const SSLStateConnp *connp; + + connp = &ssl_state->server_connp; + + if (idx >= connp->cert0_sans_len) { + return NULL; + } + + InspectionBufferSetupMulti(buffer, transforms, (const uint8_t *)connp->cert0_sans[idx], + strlen(connp->cert0_sans[idx])); + + SCReturnPtr(buffer, "InspectionBuffer"); +} + +static uint8_t DetectEngineInspectTlsSubjectAltName(DetectEngineCtx *de_ctx, + DetectEngineThreadCtx *det_ctx, const DetectEngineAppInspectionEngine *engine, + const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id) +{ + const DetectEngineTransforms *transforms = NULL; + if (!engine->mpm) { + transforms = engine->v2.transforms; + } + + for (uint16_t i = 0;; i++) { + InspectionBuffer *buffer = + TlsSubjectAltNameGetData(det_ctx, transforms, f, flags, i, engine->sm_list); + if (buffer == NULL || buffer->inspect == NULL) + break; + const bool match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, NULL, f, + buffer->inspect, buffer->inspect_len, buffer->inspect_offset, + DETECT_CI_FLAGS_SINGLE, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE); + if (match) { + return DETECT_ENGINE_INSPECT_SIG_MATCH; + } + } + + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; +} + +static void PrefilterTxTlsSubjectAltName(DetectEngineThreadCtx *det_ctx, const void *pectx, + Packet *p, Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, + const uint8_t flags) +{ + SCEnter(); + + const PrefilterMpmTlsSubjectAltName *ctx = (const PrefilterMpmTlsSubjectAltName *)pectx; + const MpmCtx *mpm_ctx = ctx->mpm_ctx; + const int list_id = ctx->list_id; + + for (uint16_t i = 0;; i++) { + InspectionBuffer *buffer = + TlsSubjectAltNameGetData(det_ctx, ctx->transforms, f, flags, i, list_id); + if (buffer == NULL) + break; + + if (buffer->inspect_len >= mpm_ctx->minlen) { + (void)mpm_table[mpm_ctx->mpm_type].Search( + mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, buffer->inspect, buffer->inspect_len); + PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len); + } + } +} + +static void PrefilterMpmTlsSubjectAltNameFree(void *ptr) +{ + SCFree(ptr); +} + +static int PrefilterMpmTlsSubjectAltNameRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, + MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id) +{ + PrefilterMpmTlsSubjectAltName *pectx = SCCalloc(1, sizeof(*pectx)); + if (pectx == NULL) + return -1; + + pectx->list_id = list_id; + pectx->mpm_ctx = mpm_ctx; + pectx->transforms = &mpm_reg->transforms; + + return PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxTlsSubjectAltName, + mpm_reg->app_v2.alproto, mpm_reg->app_v2.tx_min_progress, pectx, + PrefilterMpmTlsSubjectAltNameFree, mpm_reg->name); +} diff --git a/src/detect-tls-subjectaltname.h b/src/detect-tls-subjectaltname.h new file mode 100644 index 0000000000..f97f149364 --- /dev/null +++ b/src/detect-tls-subjectaltname.h @@ -0,0 +1,29 @@ +/* Copyright (C) 2024 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 Shivani Bhardwaj + */ + +#ifndef SURICATA_DETECT_TLS_SUBJECTALTNAME_H +#define SURICATA_DETECT_TLS_SUBJECTALTNAME_H + +void DetectTlsSubjectAltNameRegister(void); + +#endif /* SURICATA_DETECT_TLS_SUBJECTALTNAME_H */