You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/detect-tls-cert-serial.c

236 lines
8.0 KiB
C

/* Copyright (C) 2017-2022 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 Mats Klepsland <mats.klepsland@gmail.com>
*
* Implements support for tls.cert_serial 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-buffer.h"
#include "detect-engine-mpm.h"
#include "detect-engine-prefilter.h"
#include "detect-content.h"
#include "detect-pcre.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 "detect-tls-cert-serial.h"
#include "util-unittest.h"
#include "util-unittest-helper.h"
static int DetectTlsSerialSetup(DetectEngineCtx *, Signature *, const char *);
#ifdef UNITTESTS
static void DetectTlsSerialRegisterTests(void);
#endif
static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
const DetectEngineTransforms *transforms,
Flow *f, const uint8_t flow_flags,
void *txv, const int list_id);
static void DetectTlsSerialSetupCallback(
const DetectEngineCtx *de_ctx, Signature *s, const DetectBufferType *map);
static bool DetectTlsSerialValidateCallback(
const Signature *s, const char **sigerror, const DetectBufferType *dbt);
static int g_tls_cert_serial_buffer_id = 0;
/**
* \brief Registration function for keyword: tls.cert_serial
*/
void DetectTlsSerialRegister(void)
{
sigmatch_table[DETECT_TLS_CERT_SERIAL].name = "tls.cert_serial";
sigmatch_table[DETECT_TLS_CERT_SERIAL].alias = "tls_cert_serial";
sigmatch_table[DETECT_TLS_CERT_SERIAL].desc =
"sticky buffer to match the TLS cert serial buffer";
sigmatch_table[DETECT_TLS_CERT_SERIAL].url = "/rules/tls-keywords.html#tls-cert-serial";
sigmatch_table[DETECT_TLS_CERT_SERIAL].Setup = DetectTlsSerialSetup;
#ifdef UNITTESTS
sigmatch_table[DETECT_TLS_CERT_SERIAL].RegisterTests = DetectTlsSerialRegisterTests;
#endif
sigmatch_table[DETECT_TLS_CERT_SERIAL].flags |= SIGMATCH_NOOPT;
sigmatch_table[DETECT_TLS_CERT_SERIAL].flags |= SIGMATCH_INFO_STICKY_BUFFER;
DetectAppLayerInspectEngineRegister("tls.cert_serial", ALPROTO_TLS, SIG_FLAG_TOCLIENT,
tls: make the server states active phases The server states were completion milestones: server_hello landed when the server hello record was fully parsed and server_cert_done when the certificate message was done, so the states named moments at which the phase data was complete instead of the phase being processed. The flight-completion state (server_hello_done) named a moment at which no data is published and no decision can act. The states now denote the record phase being processed: server_hello is entered when the server hello message begins (its header is read once, the message may span several records) and server_cert when the certificate message begins, so the state names the message being parsed and a message that fails to decode leaves the track in the state that names it. The record that completes a hello hands the track over to the certificate phase before a single certificate byte arrives. The enter/leave is the shared handshake phase mechanism, so the client track follows: client_hello is entered at the start of the client hello as well, and the completing client hello is decided at client_cert. The flight-done state is removed and renumbers the tail (server_data, server_finished), leaving both tracks with the same five-phase shape and an absorbing finished state. Hello and certificate decode failures suppress the phase advance of their own message only: the decoders keep their pre-existing return contract (the message bytes are consumed and the error events are raised as before) and set a per-connp suppression flag that is reset at the start of the next handshake message and checked at the phase advance. A decode failure is not an app-layer parse error: the flow is neither dropped nor blinded, and the next message that decodes cleanly still advances. An undecodable certificate in a chain is skipped, not fatal: one decoder event is raised for the bad certificate and the remaining certificates are still decoded - the rest of the chain is kept and the certificate data is filled from the first fully decodable certificate. The certificate phase is suppressed for such a message (the phase data is incomplete). The incomplete-data condition is sticky per connp: it is set while the certificate message is being decoded, cleared by a fully decoded certificate message only, and consulted by the handshake-done heuristic that would otherwise establish the data phase from the certificate data - a later handshake message does not complete the data, so the tracks do not cross the cert phase on partial data. The certificate field extraction is commit-on-success: the fields are extracted into stashes and published to the connp only when the whole certificate yields every field, so a certificate that fails a late extraction leaves no partial data behind and the next certificate can fill the fields. An extraction failure raises its decoder event once per message: the message no longer aborts at the first failing certificate, and the event is not repeated for the later failing entries. The tls.cert_chain_len match is gated on the chain being final (the per-connp flag set by a fully decoded certificate message), so it does not match while the track sits in server_cert with a chain that is still arriving. TOCLIENT keyword registrations and the tc log threshold follow the renamed constants (values 1/2 unchanged; the threshold is triggered by the same writes as before, so the log fires on the same packets). The ja3s lua registration moves to server_data: lua keeps a numeric hook, so it must follow the renamed state the old hook value pointed at (server hello done was 3, server data is 3). The ja3s content is final once the ServerHello parses, which is why the C keywords (ja3s.string, ja3s.hash) stay registered at server_hello. Two string-based generic buffer lookups and the user guide follow the renamed states. Ticket: #9047.
1 month ago
TLS_STATE_SERVER_CERT, DetectEngineInspectBufferGeneric, GetData);
DetectAppLayerMpmRegister("tls.cert_serial", SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister,
tls: make the server states active phases The server states were completion milestones: server_hello landed when the server hello record was fully parsed and server_cert_done when the certificate message was done, so the states named moments at which the phase data was complete instead of the phase being processed. The flight-completion state (server_hello_done) named a moment at which no data is published and no decision can act. The states now denote the record phase being processed: server_hello is entered when the server hello message begins (its header is read once, the message may span several records) and server_cert when the certificate message begins, so the state names the message being parsed and a message that fails to decode leaves the track in the state that names it. The record that completes a hello hands the track over to the certificate phase before a single certificate byte arrives. The enter/leave is the shared handshake phase mechanism, so the client track follows: client_hello is entered at the start of the client hello as well, and the completing client hello is decided at client_cert. The flight-done state is removed and renumbers the tail (server_data, server_finished), leaving both tracks with the same five-phase shape and an absorbing finished state. Hello and certificate decode failures suppress the phase advance of their own message only: the decoders keep their pre-existing return contract (the message bytes are consumed and the error events are raised as before) and set a per-connp suppression flag that is reset at the start of the next handshake message and checked at the phase advance. A decode failure is not an app-layer parse error: the flow is neither dropped nor blinded, and the next message that decodes cleanly still advances. An undecodable certificate in a chain is skipped, not fatal: one decoder event is raised for the bad certificate and the remaining certificates are still decoded - the rest of the chain is kept and the certificate data is filled from the first fully decodable certificate. The certificate phase is suppressed for such a message (the phase data is incomplete). The incomplete-data condition is sticky per connp: it is set while the certificate message is being decoded, cleared by a fully decoded certificate message only, and consulted by the handshake-done heuristic that would otherwise establish the data phase from the certificate data - a later handshake message does not complete the data, so the tracks do not cross the cert phase on partial data. The certificate field extraction is commit-on-success: the fields are extracted into stashes and published to the connp only when the whole certificate yields every field, so a certificate that fails a late extraction leaves no partial data behind and the next certificate can fill the fields. An extraction failure raises its decoder event once per message: the message no longer aborts at the first failing certificate, and the event is not repeated for the later failing entries. The tls.cert_chain_len match is gated on the chain being final (the per-connp flag set by a fully decoded certificate message), so it does not match while the track sits in server_cert with a chain that is still arriving. TOCLIENT keyword registrations and the tc log threshold follow the renamed constants (values 1/2 unchanged; the threshold is triggered by the same writes as before, so the log fires on the same packets). The ja3s lua registration moves to server_data: lua keeps a numeric hook, so it must follow the renamed state the old hook value pointed at (server hello done was 3, server data is 3). The ja3s content is final once the ServerHello parses, which is why the C keywords (ja3s.string, ja3s.hash) stay registered at server_hello. Two string-based generic buffer lookups and the user guide follow the renamed states. Ticket: #9047.
1 month ago
GetData, ALPROTO_TLS, TLS_STATE_SERVER_CERT);
DetectAppLayerInspectEngineRegister("tls.cert_serial", ALPROTO_TLS, SIG_FLAG_TOSERVER,
TLS_STATE_CLIENT_CERT, DetectEngineInspectBufferGeneric, GetData);
DetectAppLayerMpmRegister("tls.cert_serial", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister,
GetData, ALPROTO_TLS, TLS_STATE_CLIENT_CERT);
DetectBufferTypeSetDescriptionByName("tls.cert_serial",
"TLS certificate serial number");
DetectBufferTypeRegisterSetupCallback("tls.cert_serial",
DetectTlsSerialSetupCallback);
DetectBufferTypeRegisterValidateCallback("tls.cert_serial",
DetectTlsSerialValidateCallback);
g_tls_cert_serial_buffer_id = DetectBufferTypeGetByName("tls.cert_serial");
}
/**
* \brief this function setup the tls_cert_serial modifier keyword used in the rule
*
* \param de_ctx Pointer to the Detection Engine Context
* \param s Pointer to the Signature to which the current keyword belongs
* \param str Should hold an empty string always
*
* \retval 0 On success
* \retval -1 On failure
*/
static int DetectTlsSerialSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
{
if (SCDetectBufferSetActiveList(de_ctx, s, g_tls_cert_serial_buffer_id) < 0)
return -1;
if (SCDetectSignatureSetAppProto(s, ALPROTO_TLS) < 0)
return -1;
return 0;
}
static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
const DetectEngineTransforms *transforms, Flow *f,
const uint8_t flow_flags, void *txv, const int list_id)
{
InspectionBuffer *buffer = SCInspectionBufferGet(det_ctx, list_id);
if (buffer->inspect == NULL) {
const SSLState *ssl_state = (SSLState *)f->alstate;
const SSLStateConnp *connp;
if (flow_flags & STREAM_TOSERVER) {
connp = &ssl_state->client_connp;
} else {
connp = &ssl_state->server_connp;
}
if (connp->cert0_serial == NULL) {
return NULL;
}
const uint32_t data_len = connp->cert0_serial_len;
const uint8_t *data = connp->cert0_serial;
SCInspectionBufferSetupAndApplyTransforms(
det_ctx, list_id, buffer, data, data_len, transforms);
}
return buffer;
}
static bool DetectTlsSerialValidateCallback(
const Signature *s, const char **sigerror, const DetectBufferType *dbt)
{
detect: support multi buffer matching Multi buffer matching is implemented as a way for a rule to match on multiple buffers within the same transaction. Before this patch a rule like: dns.query; content:"example"; dns.query; content:".com"; would be equivalent to: dns.query; content:"example"; content:".com"; If a DNS query would request more than one name, e.g.: DNS: [example.net][something.com] Eeach would be inspected to have both patterns present. Otherwise, it would not be a match. So the rule above would not match, as neither example.net and somthing.com satisfy both conditions at the same time. This patch changes this behavior. Instead of the above, each time the sticky buffer is specified, it creates a separate detection unit. Each buffer is a "multi buffer" sticky buffer will now be evaluated against each "instance" of the sticky buffer. To continue with the above example: DNS: [example.net] <- matches 'dns.query; content:"example";' DNS: [something.com] <- matches 'dns.query; content:".com"' So this would now be a match. To make sure both patterns match in a single query string, the expression 'dns.query; content:"example"; content:".com";' still works for this. This patch doesn't yet enable the behavior for the keywords. That is done in a follow up patch. To be able to implement this the internal storage of parsed rules is changed. Until this patch and array of lists was used, where the index was the buffer id (e.g. http_uri, dns_query). Therefore there was only one list of matches per buffer id. As a side effect this array was always very sparsely populated as many buffers could not be mixed. This patch changes the internal representation. The new array is densely packed: dns.query; content:"1"; dns.query; bsize:1; content:"2"; [type: dns_query][list: content:"1";] [type: dns_query][list: bsize:1; content:"2";] The new scheme allows for multiple instances of the same buffer. These lists are then translated into multiple inspection engines during the final setup of the rule. Ticket: #5784.
4 years ago
for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
if (s->init_data->buffers[x].id != (uint32_t)dbt->id)
continue;
detect: support multi buffer matching Multi buffer matching is implemented as a way for a rule to match on multiple buffers within the same transaction. Before this patch a rule like: dns.query; content:"example"; dns.query; content:".com"; would be equivalent to: dns.query; content:"example"; content:".com"; If a DNS query would request more than one name, e.g.: DNS: [example.net][something.com] Eeach would be inspected to have both patterns present. Otherwise, it would not be a match. So the rule above would not match, as neither example.net and somthing.com satisfy both conditions at the same time. This patch changes this behavior. Instead of the above, each time the sticky buffer is specified, it creates a separate detection unit. Each buffer is a "multi buffer" sticky buffer will now be evaluated against each "instance" of the sticky buffer. To continue with the above example: DNS: [example.net] <- matches 'dns.query; content:"example";' DNS: [something.com] <- matches 'dns.query; content:".com"' So this would now be a match. To make sure both patterns match in a single query string, the expression 'dns.query; content:"example"; content:".com";' still works for this. This patch doesn't yet enable the behavior for the keywords. That is done in a follow up patch. To be able to implement this the internal storage of parsed rules is changed. Until this patch and array of lists was used, where the index was the buffer id (e.g. http_uri, dns_query). Therefore there was only one list of matches per buffer id. As a side effect this array was always very sparsely populated as many buffers could not be mixed. This patch changes the internal representation. The new array is densely packed: dns.query; content:"1"; dns.query; bsize:1; content:"2"; [type: dns_query][list: content:"1";] [type: dns_query][list: bsize:1; content:"2";] The new scheme allows for multiple instances of the same buffer. These lists are then translated into multiple inspection engines during the final setup of the rule. Ticket: #5784.
4 years ago
const SigMatch *sm = s->init_data->buffers[x].head;
for (; sm != NULL; sm = sm->next) {
if (sm->type != DETECT_CONTENT)
continue;
const DetectContentData *cd = (DetectContentData *)sm->ctx;
if (cd->flags & DETECT_CONTENT_NOCASE) {
*sigerror = "tls.cert_serial should not be used together "
"with nocase, since the rule is automatically "
"uppercased anyway which makes nocase redundant.";
SCLogWarning("rule %u: %s", s->id, *sigerror);
}
detect: support multi buffer matching Multi buffer matching is implemented as a way for a rule to match on multiple buffers within the same transaction. Before this patch a rule like: dns.query; content:"example"; dns.query; content:".com"; would be equivalent to: dns.query; content:"example"; content:".com"; If a DNS query would request more than one name, e.g.: DNS: [example.net][something.com] Eeach would be inspected to have both patterns present. Otherwise, it would not be a match. So the rule above would not match, as neither example.net and somthing.com satisfy both conditions at the same time. This patch changes this behavior. Instead of the above, each time the sticky buffer is specified, it creates a separate detection unit. Each buffer is a "multi buffer" sticky buffer will now be evaluated against each "instance" of the sticky buffer. To continue with the above example: DNS: [example.net] <- matches 'dns.query; content:"example";' DNS: [something.com] <- matches 'dns.query; content:".com"' So this would now be a match. To make sure both patterns match in a single query string, the expression 'dns.query; content:"example"; content:".com";' still works for this. This patch doesn't yet enable the behavior for the keywords. That is done in a follow up patch. To be able to implement this the internal storage of parsed rules is changed. Until this patch and array of lists was used, where the index was the buffer id (e.g. http_uri, dns_query). Therefore there was only one list of matches per buffer id. As a side effect this array was always very sparsely populated as many buffers could not be mixed. This patch changes the internal representation. The new array is densely packed: dns.query; content:"1"; dns.query; bsize:1; content:"2"; [type: dns_query][list: content:"1";] [type: dns_query][list: bsize:1; content:"2";] The new scheme allows for multiple instances of the same buffer. These lists are then translated into multiple inspection engines during the final setup of the rule. Ticket: #5784.
4 years ago
/* no need to worry about this if the content is short enough */
if (cd->content_len <= 2)
return true;
detect: support multi buffer matching Multi buffer matching is implemented as a way for a rule to match on multiple buffers within the same transaction. Before this patch a rule like: dns.query; content:"example"; dns.query; content:".com"; would be equivalent to: dns.query; content:"example"; content:".com"; If a DNS query would request more than one name, e.g.: DNS: [example.net][something.com] Eeach would be inspected to have both patterns present. Otherwise, it would not be a match. So the rule above would not match, as neither example.net and somthing.com satisfy both conditions at the same time. This patch changes this behavior. Instead of the above, each time the sticky buffer is specified, it creates a separate detection unit. Each buffer is a "multi buffer" sticky buffer will now be evaluated against each "instance" of the sticky buffer. To continue with the above example: DNS: [example.net] <- matches 'dns.query; content:"example";' DNS: [something.com] <- matches 'dns.query; content:".com"' So this would now be a match. To make sure both patterns match in a single query string, the expression 'dns.query; content:"example"; content:".com";' still works for this. This patch doesn't yet enable the behavior for the keywords. That is done in a follow up patch. To be able to implement this the internal storage of parsed rules is changed. Until this patch and array of lists was used, where the index was the buffer id (e.g. http_uri, dns_query). Therefore there was only one list of matches per buffer id. As a side effect this array was always very sparsely populated as many buffers could not be mixed. This patch changes the internal representation. The new array is densely packed: dns.query; content:"1"; dns.query; bsize:1; content:"2"; [type: dns_query][list: content:"1";] [type: dns_query][list: bsize:1; content:"2";] The new scheme allows for multiple instances of the same buffer. These lists are then translated into multiple inspection engines during the final setup of the rule. Ticket: #5784.
4 years ago
uint32_t u;
for (u = 0; u < cd->content_len; u++)
if (cd->content[u] == ':')
return true;
detect: support multi buffer matching Multi buffer matching is implemented as a way for a rule to match on multiple buffers within the same transaction. Before this patch a rule like: dns.query; content:"example"; dns.query; content:".com"; would be equivalent to: dns.query; content:"example"; content:".com"; If a DNS query would request more than one name, e.g.: DNS: [example.net][something.com] Eeach would be inspected to have both patterns present. Otherwise, it would not be a match. So the rule above would not match, as neither example.net and somthing.com satisfy both conditions at the same time. This patch changes this behavior. Instead of the above, each time the sticky buffer is specified, it creates a separate detection unit. Each buffer is a "multi buffer" sticky buffer will now be evaluated against each "instance" of the sticky buffer. To continue with the above example: DNS: [example.net] <- matches 'dns.query; content:"example";' DNS: [something.com] <- matches 'dns.query; content:".com"' So this would now be a match. To make sure both patterns match in a single query string, the expression 'dns.query; content:"example"; content:".com";' still works for this. This patch doesn't yet enable the behavior for the keywords. That is done in a follow up patch. To be able to implement this the internal storage of parsed rules is changed. Until this patch and array of lists was used, where the index was the buffer id (e.g. http_uri, dns_query). Therefore there was only one list of matches per buffer id. As a side effect this array was always very sparsely populated as many buffers could not be mixed. This patch changes the internal representation. The new array is densely packed: dns.query; content:"1"; dns.query; bsize:1; content:"2"; [type: dns_query][list: content:"1";] [type: dns_query][list: bsize:1; content:"2";] The new scheme allows for multiple instances of the same buffer. These lists are then translated into multiple inspection engines during the final setup of the rule. Ticket: #5784.
4 years ago
*sigerror = "No colon delimiters ':' detected in content after "
"tls.cert_serial. This rule will therefore never "
"match.";
SCLogWarning("rule %u: %s", s->id, *sigerror);
detect: support multi buffer matching Multi buffer matching is implemented as a way for a rule to match on multiple buffers within the same transaction. Before this patch a rule like: dns.query; content:"example"; dns.query; content:".com"; would be equivalent to: dns.query; content:"example"; content:".com"; If a DNS query would request more than one name, e.g.: DNS: [example.net][something.com] Eeach would be inspected to have both patterns present. Otherwise, it would not be a match. So the rule above would not match, as neither example.net and somthing.com satisfy both conditions at the same time. This patch changes this behavior. Instead of the above, each time the sticky buffer is specified, it creates a separate detection unit. Each buffer is a "multi buffer" sticky buffer will now be evaluated against each "instance" of the sticky buffer. To continue with the above example: DNS: [example.net] <- matches 'dns.query; content:"example";' DNS: [something.com] <- matches 'dns.query; content:".com"' So this would now be a match. To make sure both patterns match in a single query string, the expression 'dns.query; content:"example"; content:".com";' still works for this. This patch doesn't yet enable the behavior for the keywords. That is done in a follow up patch. To be able to implement this the internal storage of parsed rules is changed. Until this patch and array of lists was used, where the index was the buffer id (e.g. http_uri, dns_query). Therefore there was only one list of matches per buffer id. As a side effect this array was always very sparsely populated as many buffers could not be mixed. This patch changes the internal representation. The new array is densely packed: dns.query; content:"1"; dns.query; bsize:1; content:"2"; [type: dns_query][list: content:"1";] [type: dns_query][list: bsize:1; content:"2";] The new scheme allows for multiple instances of the same buffer. These lists are then translated into multiple inspection engines during the final setup of the rule. Ticket: #5784.
4 years ago
return false;
}
}
return true;
}
static void DetectTlsSerialSetupCallback(
const DetectEngineCtx *de_ctx, Signature *s, const DetectBufferType *map)
{
detect: support multi buffer matching Multi buffer matching is implemented as a way for a rule to match on multiple buffers within the same transaction. Before this patch a rule like: dns.query; content:"example"; dns.query; content:".com"; would be equivalent to: dns.query; content:"example"; content:".com"; If a DNS query would request more than one name, e.g.: DNS: [example.net][something.com] Eeach would be inspected to have both patterns present. Otherwise, it would not be a match. So the rule above would not match, as neither example.net and somthing.com satisfy both conditions at the same time. This patch changes this behavior. Instead of the above, each time the sticky buffer is specified, it creates a separate detection unit. Each buffer is a "multi buffer" sticky buffer will now be evaluated against each "instance" of the sticky buffer. To continue with the above example: DNS: [example.net] <- matches 'dns.query; content:"example";' DNS: [something.com] <- matches 'dns.query; content:".com"' So this would now be a match. To make sure both patterns match in a single query string, the expression 'dns.query; content:"example"; content:".com";' still works for this. This patch doesn't yet enable the behavior for the keywords. That is done in a follow up patch. To be able to implement this the internal storage of parsed rules is changed. Until this patch and array of lists was used, where the index was the buffer id (e.g. http_uri, dns_query). Therefore there was only one list of matches per buffer id. As a side effect this array was always very sparsely populated as many buffers could not be mixed. This patch changes the internal representation. The new array is densely packed: dns.query; content:"1"; dns.query; bsize:1; content:"2"; [type: dns_query][list: content:"1";] [type: dns_query][list: bsize:1; content:"2";] The new scheme allows for multiple instances of the same buffer. These lists are then translated into multiple inspection engines during the final setup of the rule. Ticket: #5784.
4 years ago
for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
if (s->init_data->buffers[x].id != (uint32_t)g_tls_cert_serial_buffer_id)
continue;
detect: support multi buffer matching Multi buffer matching is implemented as a way for a rule to match on multiple buffers within the same transaction. Before this patch a rule like: dns.query; content:"example"; dns.query; content:".com"; would be equivalent to: dns.query; content:"example"; content:".com"; If a DNS query would request more than one name, e.g.: DNS: [example.net][something.com] Eeach would be inspected to have both patterns present. Otherwise, it would not be a match. So the rule above would not match, as neither example.net and somthing.com satisfy both conditions at the same time. This patch changes this behavior. Instead of the above, each time the sticky buffer is specified, it creates a separate detection unit. Each buffer is a "multi buffer" sticky buffer will now be evaluated against each "instance" of the sticky buffer. To continue with the above example: DNS: [example.net] <- matches 'dns.query; content:"example";' DNS: [something.com] <- matches 'dns.query; content:".com"' So this would now be a match. To make sure both patterns match in a single query string, the expression 'dns.query; content:"example"; content:".com";' still works for this. This patch doesn't yet enable the behavior for the keywords. That is done in a follow up patch. To be able to implement this the internal storage of parsed rules is changed. Until this patch and array of lists was used, where the index was the buffer id (e.g. http_uri, dns_query). Therefore there was only one list of matches per buffer id. As a side effect this array was always very sparsely populated as many buffers could not be mixed. This patch changes the internal representation. The new array is densely packed: dns.query; content:"1"; dns.query; bsize:1; content:"2"; [type: dns_query][list: content:"1";] [type: dns_query][list: bsize:1; content:"2";] The new scheme allows for multiple instances of the same buffer. These lists are then translated into multiple inspection engines during the final setup of the rule. Ticket: #5784.
4 years ago
SigMatch *sm = s->init_data->buffers[x].head;
for (; sm != NULL; sm = sm->next) {
if (sm->type != DETECT_CONTENT)
continue;
DetectContentData *cd = (DetectContentData *)sm->ctx;
bool changed = false;
uint32_t u;
for (u = 0; u < cd->content_len; u++) {
if (islower(cd->content[u])) {
cd->content[u] = u8_toupper(cd->content[u]);
changed = true;
}
}
detect: support multi buffer matching Multi buffer matching is implemented as a way for a rule to match on multiple buffers within the same transaction. Before this patch a rule like: dns.query; content:"example"; dns.query; content:".com"; would be equivalent to: dns.query; content:"example"; content:".com"; If a DNS query would request more than one name, e.g.: DNS: [example.net][something.com] Eeach would be inspected to have both patterns present. Otherwise, it would not be a match. So the rule above would not match, as neither example.net and somthing.com satisfy both conditions at the same time. This patch changes this behavior. Instead of the above, each time the sticky buffer is specified, it creates a separate detection unit. Each buffer is a "multi buffer" sticky buffer will now be evaluated against each "instance" of the sticky buffer. To continue with the above example: DNS: [example.net] <- matches 'dns.query; content:"example";' DNS: [something.com] <- matches 'dns.query; content:".com"' So this would now be a match. To make sure both patterns match in a single query string, the expression 'dns.query; content:"example"; content:".com";' still works for this. This patch doesn't yet enable the behavior for the keywords. That is done in a follow up patch. To be able to implement this the internal storage of parsed rules is changed. Until this patch and array of lists was used, where the index was the buffer id (e.g. http_uri, dns_query). Therefore there was only one list of matches per buffer id. As a side effect this array was always very sparsely populated as many buffers could not be mixed. This patch changes the internal representation. The new array is densely packed: dns.query; content:"1"; dns.query; bsize:1; content:"2"; [type: dns_query][list: content:"1";] [type: dns_query][list: bsize:1; content:"2";] The new scheme allows for multiple instances of the same buffer. These lists are then translated into multiple inspection engines during the final setup of the rule. Ticket: #5784.
4 years ago
/* recreate the context if changes were made */
if (changed) {
SpmDestroyCtx(cd->spm_ctx);
cd->spm_ctx =
SpmInitCtx(cd->content, cd->content_len, 1, de_ctx->spm_global_thread_ctx);
}
}
}
}
#ifdef UNITTESTS
#include "tests/detect-tls-cert-serial.c"
#endif