|
|
|
|
/* 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 <shivani@oisf.net>
|
|
|
|
|
*
|
|
|
|
|
* 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-buffer.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 int g_tls_subjectaltname_buffer_id = 0;
|
|
|
|
|
|
|
|
|
|
static bool TlsSubjectAltNameGetData(DetectEngineThreadCtx *det_ctx, const void *txv,
|
|
|
|
|
const uint8_t flags, uint32_t idx, const uint8_t **buf, uint32_t *buf_len)
|
|
|
|
|
{
|
|
|
|
|
const SSLState *ssl_state = (SSLState *)txv;
|
|
|
|
|
const SSLStateConnp *connp;
|
|
|
|
|
|
|
|
|
|
connp = &ssl_state->server_connp;
|
|
|
|
|
|
|
|
|
|
if (idx >= connp->cert0_sans_num) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*buf = (const uint8_t *)connp->cert0_sans[idx].san;
|
|
|
|
|
if (*buf) {
|
|
|
|
|
*buf_len = connp->cert0_sans[idx].san_len;
|
|
|
|
|
} else {
|
|
|
|
|
// happens if the altname had a zero character in it
|
|
|
|
|
*buf_len = 0;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Registration function for keyword: tls.subjectaltname
|
|
|
|
|
*/
|
|
|
|
|
void DetectTlsSubjectAltNameRegister(void)
|
|
|
|
|
{
|
|
|
|
|
sigmatch_table[DETECT_TLS_SUBJECTALTNAME].name = "tls.subjectaltname";
|
|
|
|
|
sigmatch_table[DETECT_TLS_SUBJECTALTNAME].desc =
|
|
|
|
|
"sticky buffer to match the TLS Subject Alternative Name buffer";
|
|
|
|
|
sigmatch_table[DETECT_TLS_SUBJECTALTNAME].url = "/rules/tls-keywords.html#tls-subjectaltname";
|
|
|
|
|
sigmatch_table[DETECT_TLS_SUBJECTALTNAME].Setup = DetectTlsSubjectAltNameSetup;
|
|
|
|
|
sigmatch_table[DETECT_TLS_SUBJECTALTNAME].flags |= SIGMATCH_NOOPT;
|
|
|
|
|
sigmatch_table[DETECT_TLS_SUBJECTALTNAME].flags |=
|
|
|
|
|
SIGMATCH_INFO_STICKY_BUFFER | SIGMATCH_INFO_MULTI_BUFFER;
|
|
|
|
|
|
|
|
|
|
DetectAppLayerMultiRegister("tls.subjectaltname", 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, TlsSubjectAltNameGetData, 2);
|
|
|
|
|
|
|
|
|
|
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 (SCDetectBufferSetActiveList(de_ctx, s, g_tls_subjectaltname_buffer_id) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (SCDetectSignatureSetAppProto(s, ALPROTO_TLS) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|