/src/suricata8/src/detect-tls-certs.c
Line | Count | Source |
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-buffer.h" |
34 | | #include "detect-engine-mpm.h" |
35 | | #include "detect-engine-prefilter.h" |
36 | | #include "detect-engine-content-inspection.h" |
37 | | #include "detect-content.h" |
38 | | #include "detect-pcre.h" |
39 | | #include "detect-tls-certs.h" |
40 | | #include "detect-engine-uint.h" |
41 | | |
42 | | #include "flow.h" |
43 | | #include "flow-util.h" |
44 | | #include "flow-var.h" |
45 | | |
46 | | #include "util-debug.h" |
47 | | #include "util-spm.h" |
48 | | #include "util-print.h" |
49 | | |
50 | | #include "stream-tcp.h" |
51 | | |
52 | | #include "app-layer.h" |
53 | | #include "app-layer-ssl.h" |
54 | | |
55 | | #include "util-profiling.h" |
56 | | #include "util-unittest.h" |
57 | | #include "util-unittest-helper.h" |
58 | | |
59 | | static int DetectTlsCertsSetup(DetectEngineCtx *, Signature *, const char *); |
60 | | #ifdef UNITTESTS |
61 | | static void DetectTlsCertsRegisterTests(void); |
62 | | #endif |
63 | | |
64 | | static int g_tls_certs_buffer_id = 0; |
65 | | |
66 | | static bool TlsCertsGetData(DetectEngineThreadCtx *det_ctx, const void *txv, const uint8_t flags, |
67 | | uint32_t local_id, const uint8_t **buf, uint32_t *buf_len) |
68 | 263 | { |
69 | 263 | const SSLState *ssl_state = (SSLState *)txv; |
70 | 263 | const SSLStateConnp *connp; |
71 | | |
72 | 263 | if (flags & STREAM_TOSERVER) { |
73 | 0 | connp = &ssl_state->client_connp; |
74 | 263 | } else { |
75 | 263 | connp = &ssl_state->server_connp; |
76 | 263 | } |
77 | | |
78 | 263 | if (TAILQ_EMPTY(&connp->certs)) { |
79 | 32 | return false; |
80 | 32 | } |
81 | | |
82 | 231 | SSLCertsChain *cert; |
83 | 231 | if (local_id == 0) { |
84 | 79 | cert = TAILQ_FIRST(&connp->certs); |
85 | 152 | } else { |
86 | | // TODO optimize ? |
87 | 152 | cert = TAILQ_FIRST(&connp->certs); |
88 | 380 | for (uint32_t i = 0; i < local_id; i++) { |
89 | 228 | cert = TAILQ_NEXT(cert, next); |
90 | 228 | } |
91 | 152 | } |
92 | 231 | if (cert == NULL) { |
93 | 79 | return false; |
94 | 79 | } |
95 | | |
96 | 152 | *buf = cert->cert_data; |
97 | 152 | *buf_len = cert->cert_len; |
98 | 152 | return true; |
99 | 231 | } |
100 | | |
101 | | /** |
102 | | * \brief Registration function for keyword: tls.certs |
103 | | */ |
104 | | void DetectTlsCertsRegister(void) |
105 | 79 | { |
106 | 79 | sigmatch_table[DETECT_TLS_CERTS].name = "tls.certs"; |
107 | 79 | sigmatch_table[DETECT_TLS_CERTS].desc = "sticky buffer to match the TLS certificate buffer"; |
108 | 79 | sigmatch_table[DETECT_TLS_CERTS].url = "/rules/tls-keywords.html#tls-certs"; |
109 | 79 | sigmatch_table[DETECT_TLS_CERTS].Setup = DetectTlsCertsSetup; |
110 | | #ifdef UNITTESTS |
111 | | sigmatch_table[DETECT_TLS_CERTS].RegisterTests = DetectTlsCertsRegisterTests; |
112 | | #endif |
113 | 79 | sigmatch_table[DETECT_TLS_CERTS].flags |= SIGMATCH_NOOPT; |
114 | 79 | sigmatch_table[DETECT_TLS_CERTS].flags |= SIGMATCH_INFO_STICKY_BUFFER; |
115 | | |
116 | 79 | DetectAppLayerMultiRegister("tls.certs", ALPROTO_TLS, SIG_FLAG_TOCLIENT, |
117 | 79 | TLS_STATE_SERVER_CERT_DONE, TlsCertsGetData, 2); |
118 | 79 | DetectAppLayerMultiRegister("tls.certs", ALPROTO_TLS, SIG_FLAG_TOSERVER, |
119 | 79 | TLS_STATE_CLIENT_CERT_DONE, TlsCertsGetData, 2); |
120 | | |
121 | 79 | DetectBufferTypeSetDescriptionByName("tls.certs", "TLS certificate"); |
122 | | |
123 | 79 | DetectBufferTypeSupportsMultiInstance("tls.certs"); |
124 | | |
125 | 79 | g_tls_certs_buffer_id = DetectBufferTypeGetByName("tls.certs"); |
126 | 79 | } |
127 | | |
128 | | /** |
129 | | * \brief This function setup the tls.certs modifier keyword |
130 | | * |
131 | | * \param de_ctx Pointer to the Detect Engine Context |
132 | | * \param s Pointer to the Signature to which the keyword belongs |
133 | | * \param str Should hold an empty string always |
134 | | * |
135 | | * \retval 0 On success |
136 | | * \retval -1 On failure |
137 | | */ |
138 | | static int DetectTlsCertsSetup(DetectEngineCtx *de_ctx, Signature *s, |
139 | | const char *str) |
140 | 876 | { |
141 | 876 | if (SCDetectBufferSetActiveList(de_ctx, s, g_tls_certs_buffer_id) < 0) |
142 | 3 | return -1; |
143 | | |
144 | 873 | if (SCDetectSignatureSetAppProto(s, ALPROTO_TLS) < 0) |
145 | 189 | return -1; |
146 | | |
147 | 684 | return 0; |
148 | 873 | } |
149 | | |
150 | | static int g_tls_cert_buffer_id = 0; |
151 | 79 | #define BUFFER_NAME "tls:server_cert_done:generic" |
152 | 542 | #define KEYWORD_ID DETECT_TLS_CHAIN_LEN |
153 | 79 | #define KEYWORD_NAME "tls.cert_chain_len" |
154 | 79 | #define KEYWORD_DESC "match TLS certificate chain length" |
155 | 79 | #define KEYWORD_URL "/rules/tls-keywords.html#tls-cert-chain-len" |
156 | | |
157 | | /** |
158 | | * \internal |
159 | | * \brief Function to match cert chain length in TLS |
160 | | * |
161 | | * \param t Pointer to thread vars. |
162 | | * \param det_ctx Pointer to the pattern matcher thread. |
163 | | * \param f Pointer to the current flow. |
164 | | * \param flags Flags. |
165 | | * \param state App layer state. |
166 | | * \param s Pointer to the Signature. |
167 | | * \param m Pointer to the sigmatch that we will cast into |
168 | | * DetectU64Data. |
169 | | * |
170 | | * \retval 0 no match. |
171 | | * \retval 1 match. |
172 | | */ |
173 | | static int DetectTLSCertChainLenMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, |
174 | | void *state, void *txv, const Signature *s, const SigMatchCtx *ctx) |
175 | 0 | { |
176 | 0 | SCEnter(); |
177 | |
|
178 | 0 | SSLState *ssl_state = state; |
179 | 0 | if (flags & STREAM_TOCLIENT) { |
180 | 0 | SSLStateConnp *connp = &ssl_state->server_connp; |
181 | 0 | uint32_t cnt = 0; |
182 | 0 | SSLCertsChain *cert; |
183 | 0 | TAILQ_FOREACH (cert, &connp->certs, next) { |
184 | 0 | cnt++; |
185 | 0 | } |
186 | 0 | SCLogDebug("%u certs in chain", cnt); |
187 | |
|
188 | 0 | const DetectU32Data *dd = (const DetectU32Data *)ctx; |
189 | 0 | if (DetectU32Match(cnt, dd)) { |
190 | 0 | SCReturnInt(1); |
191 | 0 | } |
192 | 0 | } |
193 | 0 | SCReturnInt(0); |
194 | 0 | } |
195 | | |
196 | | /** |
197 | | * \internal |
198 | | * \brief Function to free memory associated with DetectU64Data. |
199 | | * |
200 | | * \param de_ptr Pointer to DetectU64Data. |
201 | | */ |
202 | | static void DetectTLSCertChainLenFree(DetectEngineCtx *de_ctx, void *ptr) |
203 | 68 | { |
204 | 68 | SCDetectU32Free(ptr); |
205 | 68 | } |
206 | | |
207 | | /** |
208 | | * \brief Function to add the parsed tls cert chain len field into the current signature. |
209 | | * |
210 | | * \param de_ctx Pointer to the Detection Engine Context. |
211 | | * \param s Pointer to the Current Signature. |
212 | | * \param rawstr Pointer to the user provided flags options. |
213 | | * \param type Defines if this is notBefore or notAfter. |
214 | | * |
215 | | * \retval 0 on Success. |
216 | | * \retval -1 on Failure. |
217 | | */ |
218 | | static int DetectTLSCertChainLenSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) |
219 | 72 | { |
220 | 72 | if (SCDetectSignatureSetAppProto(s, ALPROTO_TLS) != 0) |
221 | 2 | return -1; |
222 | | |
223 | 70 | DetectU32Data *dd = DetectU32Parse(rawstr); |
224 | 70 | if (dd == NULL) { |
225 | 2 | SCLogError("Parsing \'%s\' failed for %s", rawstr, sigmatch_table[KEYWORD_ID].name); |
226 | 2 | return -1; |
227 | 2 | } |
228 | | |
229 | 68 | if (SCSigMatchAppendSMToList(de_ctx, s, KEYWORD_ID, (SigMatchCtx *)dd, g_tls_cert_buffer_id) == |
230 | 68 | NULL) { |
231 | 0 | SCDetectU32Free(dd); |
232 | 0 | return -1; |
233 | 0 | } |
234 | 68 | return 0; |
235 | 68 | } |
236 | | |
237 | | void DetectTlsCertChainLenRegister(void) |
238 | 79 | { |
239 | 79 | sigmatch_table[KEYWORD_ID].name = KEYWORD_NAME; |
240 | 79 | sigmatch_table[KEYWORD_ID].desc = KEYWORD_DESC; |
241 | 79 | sigmatch_table[KEYWORD_ID].url = KEYWORD_URL; |
242 | 79 | sigmatch_table[KEYWORD_ID].AppLayerTxMatch = DetectTLSCertChainLenMatch; |
243 | 79 | sigmatch_table[KEYWORD_ID].Setup = DetectTLSCertChainLenSetup; |
244 | 79 | sigmatch_table[KEYWORD_ID].Free = DetectTLSCertChainLenFree; |
245 | | |
246 | 79 | g_tls_cert_buffer_id = DetectBufferTypeGetByName(BUFFER_NAME); |
247 | 79 | } |
248 | | |
249 | | #ifdef UNITTESTS |
250 | | #include "tests/detect-tls-certs.c" |
251 | | #endif |