/src/suricata7/src/detect-tls-certs.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2019-2022 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | /** |
19 | | * \file |
20 | | * |
21 | | * \author Mats Klepsland <mats.klepsland@gmail.com> |
22 | | * |
23 | | * Implements support for tls.certs keyword. |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | #include "threads.h" |
28 | | #include "decode.h" |
29 | | #include "detect.h" |
30 | | |
31 | | #include "detect-parse.h" |
32 | | #include "detect-engine.h" |
33 | | #include "detect-engine-mpm.h" |
34 | | #include "detect-engine-prefilter.h" |
35 | | #include "detect-engine-content-inspection.h" |
36 | | #include "detect-content.h" |
37 | | #include "detect-pcre.h" |
38 | | #include "detect-tls-certs.h" |
39 | | #include "detect-engine-uint.h" |
40 | | |
41 | | #include "flow.h" |
42 | | #include "flow-util.h" |
43 | | #include "flow-var.h" |
44 | | |
45 | | #include "util-debug.h" |
46 | | #include "util-spm.h" |
47 | | #include "util-print.h" |
48 | | |
49 | | #include "stream-tcp.h" |
50 | | |
51 | | #include "app-layer.h" |
52 | | #include "app-layer-ssl.h" |
53 | | |
54 | | #include "util-profiling.h" |
55 | | #include "util-unittest.h" |
56 | | #include "util-unittest-helper.h" |
57 | | |
58 | | static int DetectTlsCertsSetup(DetectEngineCtx *, Signature *, const char *); |
59 | | #ifdef UNITTESTS |
60 | | static void DetectTlsCertsRegisterTests(void); |
61 | | #endif |
62 | | static uint8_t DetectEngineInspectTlsCerts(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, |
63 | | const DetectEngineAppInspectionEngine *engine, const Signature *s, Flow *f, uint8_t flags, |
64 | | void *alstate, void *txv, uint64_t tx_id); |
65 | | static int PrefilterMpmTlsCertsRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, |
66 | | const DetectBufferMpmRegistry *mpm_reg, int list_id); |
67 | | |
68 | | static int g_tls_certs_buffer_id = 0; |
69 | | |
70 | | struct TlsCertsGetDataArgs { |
71 | | uint32_t local_id; /**< used as index into thread inspect array */ |
72 | | SSLCertsChain *cert; |
73 | | const uint8_t flags; |
74 | | }; |
75 | | |
76 | | typedef struct PrefilterMpmTlsCerts { |
77 | | int list_id; |
78 | | const MpmCtx *mpm_ctx; |
79 | | const DetectEngineTransforms *transforms; |
80 | | } PrefilterMpmTlsCerts; |
81 | | |
82 | | /** |
83 | | * \brief Registration function for keyword: tls.certs |
84 | | */ |
85 | | void DetectTlsCertsRegister(void) |
86 | 73 | { |
87 | 73 | sigmatch_table[DETECT_AL_TLS_CERTS].name = "tls.certs"; |
88 | 73 | sigmatch_table[DETECT_AL_TLS_CERTS].desc = "sticky buffer to match the TLS certificate buffer"; |
89 | 73 | sigmatch_table[DETECT_AL_TLS_CERTS].url = "/rules/tls-keywords.html#tls-certs"; |
90 | 73 | sigmatch_table[DETECT_AL_TLS_CERTS].Setup = DetectTlsCertsSetup; |
91 | | #ifdef UNITTESTS |
92 | | sigmatch_table[DETECT_AL_TLS_CERTS].RegisterTests = DetectTlsCertsRegisterTests; |
93 | | #endif |
94 | 73 | sigmatch_table[DETECT_AL_TLS_CERTS].flags |= SIGMATCH_NOOPT; |
95 | 73 | sigmatch_table[DETECT_AL_TLS_CERTS].flags |= SIGMATCH_INFO_STICKY_BUFFER; |
96 | | |
97 | 73 | DetectAppLayerInspectEngineRegister2("tls.certs", ALPROTO_TLS, |
98 | 73 | SIG_FLAG_TOCLIENT, TLS_STATE_CERT_READY, |
99 | 73 | DetectEngineInspectTlsCerts, NULL); |
100 | | |
101 | 73 | DetectAppLayerMpmRegister2("tls.certs", SIG_FLAG_TOCLIENT, 2, |
102 | 73 | PrefilterMpmTlsCertsRegister, NULL, ALPROTO_TLS, |
103 | 73 | TLS_STATE_CERT_READY); |
104 | | |
105 | 73 | DetectAppLayerInspectEngineRegister2("tls.certs", ALPROTO_TLS, SIG_FLAG_TOSERVER, |
106 | 73 | TLS_STATE_CERT_READY, DetectEngineInspectTlsCerts, NULL); |
107 | | |
108 | 73 | DetectAppLayerMpmRegister2("tls.certs", SIG_FLAG_TOSERVER, 2, PrefilterMpmTlsCertsRegister, |
109 | 73 | NULL, ALPROTO_TLS, TLS_STATE_CERT_READY); |
110 | | |
111 | 73 | DetectBufferTypeSetDescriptionByName("tls.certs", "TLS certificate"); |
112 | | |
113 | 73 | DetectBufferTypeSupportsMultiInstance("tls.certs"); |
114 | | |
115 | 73 | g_tls_certs_buffer_id = DetectBufferTypeGetByName("tls.certs"); |
116 | 73 | } |
117 | | |
118 | | /** |
119 | | * \brief This function setup the tls.certs modifier keyword |
120 | | * |
121 | | * \param de_ctx Pointer to the Detect Engine Context |
122 | | * \param s Pointer to the Signature to which the keyword belongs |
123 | | * \param str Should hold an empty string always |
124 | | * |
125 | | * \retval 0 On success |
126 | | * \retval -1 On failure |
127 | | */ |
128 | | static int DetectTlsCertsSetup(DetectEngineCtx *de_ctx, Signature *s, |
129 | | const char *str) |
130 | 1.78k | { |
131 | 1.78k | if (DetectBufferSetActiveList(de_ctx, s, g_tls_certs_buffer_id) < 0) |
132 | 10 | return -1; |
133 | | |
134 | 1.77k | if (DetectSignatureSetAppProto(s, ALPROTO_TLS) < 0) |
135 | 311 | return -1; |
136 | | |
137 | 1.45k | return 0; |
138 | 1.77k | } |
139 | | |
140 | | static InspectionBuffer *TlsCertsGetData(DetectEngineThreadCtx *det_ctx, |
141 | | const DetectEngineTransforms *transforms, Flow *f, |
142 | | struct TlsCertsGetDataArgs *cbdata, int list_id) |
143 | 0 | { |
144 | 0 | SCEnter(); |
145 | |
|
146 | 0 | InspectionBuffer *buffer = |
147 | 0 | InspectionBufferMultipleForListGet(det_ctx, list_id, cbdata->local_id); |
148 | 0 | if (buffer == NULL || buffer->initialized) |
149 | 0 | return buffer; |
150 | | |
151 | 0 | const SSLState *ssl_state = (SSLState *)f->alstate; |
152 | 0 | const SSLStateConnp *connp; |
153 | |
|
154 | 0 | if (cbdata->flags & STREAM_TOSERVER) { |
155 | 0 | connp = &ssl_state->client_connp; |
156 | 0 | } else { |
157 | 0 | connp = &ssl_state->server_connp; |
158 | 0 | } |
159 | |
|
160 | 0 | if (TAILQ_EMPTY(&connp->certs)) { |
161 | 0 | InspectionBufferSetupMultiEmpty(buffer); |
162 | 0 | return NULL; |
163 | 0 | } |
164 | | |
165 | 0 | if (cbdata->cert == NULL) { |
166 | 0 | cbdata->cert = TAILQ_FIRST(&connp->certs); |
167 | 0 | } else { |
168 | 0 | cbdata->cert = TAILQ_NEXT(cbdata->cert, next); |
169 | 0 | } |
170 | 0 | if (cbdata->cert == NULL) { |
171 | 0 | InspectionBufferSetupMultiEmpty(buffer); |
172 | 0 | return NULL; |
173 | 0 | } |
174 | | |
175 | 0 | InspectionBufferSetupMulti(buffer, transforms, cbdata->cert->cert_data, cbdata->cert->cert_len); |
176 | |
|
177 | 0 | SCReturnPtr(buffer, "InspectionBuffer"); |
178 | 0 | } |
179 | | |
180 | | static uint8_t DetectEngineInspectTlsCerts(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, |
181 | | const DetectEngineAppInspectionEngine *engine, const Signature *s, Flow *f, uint8_t flags, |
182 | | void *alstate, void *txv, uint64_t tx_id) |
183 | 0 | { |
184 | 0 | const DetectEngineTransforms *transforms = NULL; |
185 | 0 | if (!engine->mpm) { |
186 | 0 | transforms = engine->v2.transforms; |
187 | 0 | } |
188 | |
|
189 | 0 | struct TlsCertsGetDataArgs cbdata = { .local_id = 0, .cert = NULL, .flags = flags }; |
190 | |
|
191 | 0 | while (1) |
192 | 0 | { |
193 | 0 | InspectionBuffer *buffer = TlsCertsGetData(det_ctx, transforms, f, |
194 | 0 | &cbdata, engine->sm_list); |
195 | 0 | if (buffer == NULL || buffer->inspect == NULL) |
196 | 0 | break; |
197 | | |
198 | 0 | det_ctx->buffer_offset = 0; |
199 | 0 | det_ctx->discontinue_matching = 0; |
200 | 0 | det_ctx->inspection_recursion_counter = 0; |
201 | |
|
202 | 0 | const int match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, |
203 | 0 | NULL, f, (uint8_t *)buffer->inspect, |
204 | 0 | buffer->inspect_len, |
205 | 0 | buffer->inspect_offset, DETECT_CI_FLAGS_SINGLE, |
206 | 0 | DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE); |
207 | 0 | if (match == 1) { |
208 | 0 | return DETECT_ENGINE_INSPECT_SIG_MATCH; |
209 | 0 | } |
210 | | |
211 | 0 | cbdata.local_id++; |
212 | 0 | } |
213 | | |
214 | 0 | return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; |
215 | 0 | } |
216 | | |
217 | | static void PrefilterTxTlsCerts(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, |
218 | | Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags) |
219 | 0 | { |
220 | 0 | SCEnter(); |
221 | |
|
222 | 0 | const PrefilterMpmTlsCerts *ctx = (const PrefilterMpmTlsCerts *)pectx; |
223 | 0 | const MpmCtx *mpm_ctx = ctx->mpm_ctx; |
224 | 0 | const int list_id = ctx->list_id; |
225 | |
|
226 | 0 | struct TlsCertsGetDataArgs cbdata = { .local_id = 0, .cert = NULL, .flags = flags }; |
227 | |
|
228 | 0 | while (1) |
229 | 0 | { |
230 | 0 | InspectionBuffer *buffer = TlsCertsGetData(det_ctx, ctx->transforms, |
231 | 0 | f, &cbdata, list_id); |
232 | 0 | if (buffer == NULL) |
233 | 0 | break; |
234 | | |
235 | 0 | if (buffer->inspect_len >= mpm_ctx->minlen) { |
236 | 0 | (void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx, |
237 | 0 | &det_ctx->mtcu, &det_ctx->pmq, |
238 | 0 | buffer->inspect, buffer->inspect_len); |
239 | 0 | PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len); |
240 | 0 | } |
241 | |
|
242 | 0 | cbdata.local_id++; |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | | static void PrefilterMpmTlsCertsFree(void *ptr) |
247 | 2 | { |
248 | 2 | SCFree(ptr); |
249 | 2 | } |
250 | | |
251 | | static int PrefilterMpmTlsCertsRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, |
252 | | const DetectBufferMpmRegistry *mpm_reg, int list_id) |
253 | 2 | { |
254 | 2 | PrefilterMpmTlsCerts *pectx = SCCalloc(1, sizeof(*pectx)); |
255 | 2 | if (pectx == NULL) |
256 | 0 | return -1; |
257 | | |
258 | 2 | pectx->list_id = list_id; |
259 | 2 | pectx->mpm_ctx = mpm_ctx; |
260 | 2 | pectx->transforms = &mpm_reg->transforms; |
261 | | |
262 | 2 | return PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxTlsCerts, |
263 | 2 | mpm_reg->app_v2.alproto, mpm_reg->app_v2.tx_min_progress, |
264 | 2 | pectx, PrefilterMpmTlsCertsFree, mpm_reg->name); |
265 | 2 | } |
266 | | |
267 | | static int g_tls_cert_buffer_id = 0; |
268 | 146 | #define BUFFER_NAME "tls_validity" |
269 | 506 | #define KEYWORD_ID DETECT_AL_TLS_CHAIN_LEN |
270 | 73 | #define KEYWORD_NAME "tls.cert_chain_len" |
271 | 73 | #define KEYWORD_DESC "match TLS certificate chain length" |
272 | 73 | #define KEYWORD_URL "/rules/tls-keywords.html#tls-cert-chain-len" |
273 | | |
274 | | /** |
275 | | * \internal |
276 | | * \brief Function to match cert chain length in TLS |
277 | | * |
278 | | * \param t Pointer to thread vars. |
279 | | * \param det_ctx Pointer to the pattern matcher thread. |
280 | | * \param f Pointer to the current flow. |
281 | | * \param flags Flags. |
282 | | * \param state App layer state. |
283 | | * \param s Pointer to the Signature. |
284 | | * \param m Pointer to the sigmatch that we will cast into |
285 | | * DetectU64Data. |
286 | | * |
287 | | * \retval 0 no match. |
288 | | * \retval 1 match. |
289 | | */ |
290 | | static int DetectTLSCertChainLenMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, |
291 | | void *state, void *txv, const Signature *s, const SigMatchCtx *ctx) |
292 | 0 | { |
293 | 0 | SCEnter(); |
294 | |
|
295 | 0 | SSLState *ssl_state = state; |
296 | 0 | if (flags & STREAM_TOCLIENT) { |
297 | 0 | SSLStateConnp *connp = &ssl_state->server_connp; |
298 | 0 | uint32_t cnt = 0; |
299 | 0 | SSLCertsChain *cert; |
300 | 0 | TAILQ_FOREACH (cert, &connp->certs, next) { |
301 | 0 | cnt++; |
302 | 0 | } |
303 | 0 | SCLogDebug("%u certs in chain", cnt); |
304 | |
|
305 | 0 | const DetectU32Data *dd = (const DetectU32Data *)ctx; |
306 | 0 | if (DetectU32Match(cnt, dd)) { |
307 | 0 | SCReturnInt(1); |
308 | 0 | } |
309 | 0 | } |
310 | 0 | SCReturnInt(0); |
311 | 0 | } |
312 | | |
313 | | /** |
314 | | * \internal |
315 | | * \brief Function to free memory associated with DetectU64Data. |
316 | | * |
317 | | * \param de_ptr Pointer to DetectU64Data. |
318 | | */ |
319 | | static void DetectTLSCertChainLenFree(DetectEngineCtx *de_ctx, void *ptr) |
320 | 68 | { |
321 | 68 | rs_detect_u32_free(ptr); |
322 | 68 | } |
323 | | |
324 | | /** |
325 | | * \brief Function to add the parsed tls cert chain len field into the current signature. |
326 | | * |
327 | | * \param de_ctx Pointer to the Detection Engine Context. |
328 | | * \param s Pointer to the Current Signature. |
329 | | * \param rawstr Pointer to the user provided flags options. |
330 | | * \param type Defines if this is notBefore or notAfter. |
331 | | * |
332 | | * \retval 0 on Success. |
333 | | * \retval -1 on Failure. |
334 | | */ |
335 | | static int DetectTLSCertChainLenSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) |
336 | 75 | { |
337 | 75 | if (DetectSignatureSetAppProto(s, ALPROTO_TLS) != 0) |
338 | 4 | return -1; |
339 | | |
340 | 71 | DetectU32Data *dd = DetectU32Parse(rawstr); |
341 | 71 | if (dd == NULL) { |
342 | 3 | SCLogError("Parsing \'%s\' failed for %s", rawstr, sigmatch_table[KEYWORD_ID].name); |
343 | 3 | return -1; |
344 | 3 | } |
345 | | |
346 | 68 | SigMatch *sm = SigMatchAlloc(); |
347 | 68 | if (sm == NULL) { |
348 | 0 | rs_detect_u32_free(dd); |
349 | 0 | return -1; |
350 | 0 | } |
351 | 68 | sm->type = KEYWORD_ID; |
352 | 68 | sm->ctx = (void *)dd; |
353 | | |
354 | 68 | SigMatchAppendSMToList(s, sm, g_tls_cert_buffer_id); |
355 | 68 | return 0; |
356 | 68 | } |
357 | | |
358 | | void DetectTlsCertChainLenRegister(void) |
359 | 73 | { |
360 | 73 | sigmatch_table[KEYWORD_ID].name = KEYWORD_NAME; |
361 | 73 | sigmatch_table[KEYWORD_ID].desc = KEYWORD_DESC; |
362 | 73 | sigmatch_table[KEYWORD_ID].url = KEYWORD_URL; |
363 | 73 | sigmatch_table[KEYWORD_ID].AppLayerTxMatch = DetectTLSCertChainLenMatch; |
364 | 73 | sigmatch_table[KEYWORD_ID].Setup = DetectTLSCertChainLenSetup; |
365 | 73 | sigmatch_table[KEYWORD_ID].Free = DetectTLSCertChainLenFree; |
366 | | |
367 | 73 | DetectAppLayerInspectEngineRegister2(BUFFER_NAME, ALPROTO_TLS, SIG_FLAG_TOCLIENT, |
368 | 73 | TLS_STATE_CERT_READY, DetectEngineInspectGenericList, NULL); |
369 | | |
370 | 73 | g_tls_cert_buffer_id = DetectBufferTypeGetByName(BUFFER_NAME); |
371 | 73 | } |
372 | | |
373 | | #ifdef UNITTESTS |
374 | | #include "tests/detect-tls-certs.c" |
375 | | #endif |