/src/suricata8/src/detect-ssl-state.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2020 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 Anoop Saldanha <anoopsaldanha@gmail.com> |
22 | | * |
23 | | * Implements support for ssl_state keyword. |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | #include "threads.h" |
28 | | #include "decode.h" |
29 | | |
30 | | #include "detect.h" |
31 | | #include "detect-parse.h" |
32 | | |
33 | | #include "detect-engine.h" |
34 | | #include "detect-engine-mpm.h" |
35 | | #include "detect-engine-state.h" |
36 | | |
37 | | #include "flow.h" |
38 | | #include "flow-var.h" |
39 | | #include "flow-util.h" |
40 | | |
41 | | #include "util-debug.h" |
42 | | #include "util-unittest.h" |
43 | | #include "util-unittest-helper.h" |
44 | | |
45 | | #include "app-layer.h" |
46 | | #include "app-layer-parser.h" |
47 | | |
48 | | #include "detect-ssl-state.h" |
49 | | |
50 | | #include "stream-tcp.h" |
51 | | #include "app-layer-ssl.h" |
52 | | |
53 | 12 | #define PARSE_REGEX1 "^(!?)([_a-zA-Z0-9]+)(.*)$" |
54 | | static DetectParseRegex parse_regex1; |
55 | | |
56 | 12 | #define PARSE_REGEX2 "^(?:\\s*[|,]\\s*(!?)([_a-zA-Z0-9]+))(.*)$" |
57 | | static DetectParseRegex parse_regex2; |
58 | | |
59 | | static int DetectSslStateMatch(DetectEngineThreadCtx *, |
60 | | Flow *, uint8_t, void *, void *, |
61 | | const Signature *, const SigMatchCtx *); |
62 | | static int DetectSslStateSetup(DetectEngineCtx *, Signature *, const char *); |
63 | | #ifdef UNITTESTS |
64 | | static void DetectSslStateRegisterTests(void); |
65 | | #endif |
66 | | static void DetectSslStateFree(DetectEngineCtx *, void *); |
67 | | |
68 | | static int g_tls_generic_list_id = 0; |
69 | | |
70 | | /** |
71 | | * \brief Registers the keyword handlers for the "ssl_state" keyword. |
72 | | */ |
73 | | void DetectSslStateRegister(void) |
74 | 12 | { |
75 | 12 | sigmatch_table[DETECT_SSL_STATE].name = "ssl_state"; |
76 | 12 | sigmatch_table[DETECT_SSL_STATE].desc = "match the state of the SSL connection"; |
77 | 12 | sigmatch_table[DETECT_SSL_STATE].url = "/rules/tls-keywords.html#ssl-state"; |
78 | 12 | sigmatch_table[DETECT_SSL_STATE].AppLayerTxMatch = DetectSslStateMatch; |
79 | 12 | sigmatch_table[DETECT_SSL_STATE].Setup = DetectSslStateSetup; |
80 | 12 | sigmatch_table[DETECT_SSL_STATE].Free = DetectSslStateFree; |
81 | | #ifdef UNITTESTS |
82 | | sigmatch_table[DETECT_SSL_STATE].RegisterTests = DetectSslStateRegisterTests; |
83 | | #endif |
84 | 12 | DetectSetupParseRegexes(PARSE_REGEX1, &parse_regex1); |
85 | 12 | DetectSetupParseRegexes(PARSE_REGEX2, &parse_regex2); |
86 | | |
87 | 12 | g_tls_generic_list_id = DetectBufferTypeRegister("tls_generic"); |
88 | | |
89 | 12 | DetectBufferTypeSetDescriptionByName("tls_generic", |
90 | 12 | "generic ssl/tls inspection"); |
91 | | |
92 | 12 | DetectAppLayerInspectEngineRegister( |
93 | 12 | "tls_generic", ALPROTO_TLS, SIG_FLAG_TOSERVER, 0, DetectEngineInspectGenericList, NULL); |
94 | 12 | DetectAppLayerInspectEngineRegister( |
95 | 12 | "tls_generic", ALPROTO_TLS, SIG_FLAG_TOCLIENT, 0, DetectEngineInspectGenericList, NULL); |
96 | 12 | } |
97 | | |
98 | | /** |
99 | | * \brief App layer match function ssl_state keyword. |
100 | | * |
101 | | * \param tv Pointer to threadvars. |
102 | | * \param det_ctx Pointer to the thread's detection context. |
103 | | * \param f Pointer to the flow. |
104 | | * \param flags Flags. |
105 | | * \param state App layer state. |
106 | | * \param s Sig we are currently inspecting. |
107 | | * \param m SigMatch we are currently inspecting. |
108 | | * |
109 | | * \retval 1 Match. |
110 | | * \retval 0 No match. |
111 | | */ |
112 | | static int DetectSslStateMatch(DetectEngineThreadCtx *det_ctx, |
113 | | Flow *f, uint8_t flags, void *alstate, void *txv, |
114 | | const Signature *s, const SigMatchCtx *m) |
115 | 0 | { |
116 | 0 | const DetectSslStateData *ssd = (const DetectSslStateData *)m; |
117 | 0 | SSLState *ssl_state = (SSLState *)alstate; |
118 | 0 | if (ssl_state == NULL) { |
119 | 0 | SCLogDebug("no app state, no match"); |
120 | 0 | return 0; |
121 | 0 | } |
122 | | |
123 | 0 | uint32_t ssl_flags = ssl_state->current_flags; |
124 | |
|
125 | 0 | if ((ssd->flags & ssl_flags) ^ ssd->mask) { |
126 | 0 | return 1; |
127 | 0 | } |
128 | | |
129 | 0 | return 0; |
130 | 0 | } |
131 | | |
132 | | /** |
133 | | * \brief Parse the arg supplied with ssl_state and return it in a |
134 | | * DetectSslStateData instance. |
135 | | * |
136 | | * \param arg Pointer to the string to be parsed. |
137 | | * |
138 | | * \retval ssd Pointer to DetectSslStateData on success. |
139 | | * \retval NULL On failure. |
140 | | */ |
141 | | static DetectSslStateData *DetectSslStateParse(const char *arg) |
142 | 5.05k | { |
143 | 5.05k | size_t pcre2len; |
144 | 5.05k | char str1[64]; |
145 | 5.05k | char str2[64]; |
146 | 5.05k | int negate = 0; |
147 | 5.05k | uint32_t flags = 0, mask = 0; |
148 | 5.05k | DetectSslStateData *ssd = NULL; |
149 | | |
150 | 5.05k | pcre2_match_data *match = NULL; |
151 | 5.05k | int ret = DetectParsePcreExec(&parse_regex1, &match, arg, 0, 0); |
152 | 5.05k | if (ret < 1) { |
153 | 887 | SCLogError("Invalid arg \"%s\" supplied to " |
154 | 887 | "ssl_state keyword.", |
155 | 887 | arg); |
156 | 887 | goto error; |
157 | 887 | } |
158 | | |
159 | 4.17k | pcre2len = sizeof(str1); |
160 | 4.17k | int res = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)str1, &pcre2len); |
161 | 4.17k | if (res < 0) { |
162 | 0 | SCLogError("pcre2_substring_copy_bynumber failed"); |
163 | 0 | goto error; |
164 | 0 | } |
165 | 4.17k | negate = !strcmp("!", str1); |
166 | | |
167 | 4.17k | pcre2len = sizeof(str1); |
168 | 4.17k | res = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)str1, &pcre2len); |
169 | 4.17k | if (res < 0) { |
170 | 52 | SCLogError("pcre2_substring_copy_bynumber failed"); |
171 | 52 | goto error; |
172 | 52 | } |
173 | | |
174 | 4.11k | if (strcmp("client_hello", str1) == 0) { |
175 | 160 | flags |= DETECT_SSL_STATE_CLIENT_HELLO; |
176 | 160 | if (negate) |
177 | 160 | mask |= DETECT_SSL_STATE_CLIENT_HELLO; |
178 | 3.95k | } else if (strcmp("server_hello", str1) == 0) { |
179 | 4 | flags |= DETECT_SSL_STATE_SERVER_HELLO; |
180 | 4 | if (negate) |
181 | 1 | mask |= DETECT_SSL_STATE_SERVER_HELLO; |
182 | 3.95k | } else if (strcmp("client_keyx", str1) == 0) { |
183 | 7 | flags |= DETECT_SSL_STATE_CLIENT_KEYX; |
184 | 7 | if (negate) |
185 | 3 | mask |= DETECT_SSL_STATE_CLIENT_KEYX; |
186 | 3.94k | } else if (strcmp("server_keyx", str1) == 0) { |
187 | 2 | flags |= DETECT_SSL_STATE_SERVER_KEYX; |
188 | 2 | if (negate) |
189 | 0 | mask |= DETECT_SSL_STATE_SERVER_KEYX; |
190 | 3.94k | } else if (strcmp("unknown", str1) == 0) { |
191 | 536 | flags |= DETECT_SSL_STATE_UNKNOWN; |
192 | 536 | if (negate) |
193 | 1 | mask |= DETECT_SSL_STATE_UNKNOWN; |
194 | 3.40k | } else { |
195 | 3.40k | SCLogError("Found invalid option \"%s\" " |
196 | 3.40k | "in ssl_state keyword.", |
197 | 3.40k | str1); |
198 | 3.40k | goto error; |
199 | 3.40k | } |
200 | | |
201 | 709 | pcre2len = sizeof(str1); |
202 | 709 | res = pcre2_substring_copy_bynumber(match, 3, (PCRE2_UCHAR8 *)str1, &pcre2len); |
203 | 709 | if (res < 0) { |
204 | 2 | SCLogError("pcre2_substring_copy_bynumber failed"); |
205 | 2 | goto error; |
206 | 2 | } |
207 | 1.25k | while (res >= 0 && strlen(str1) > 0) { |
208 | 915 | pcre2_match_data *match2 = NULL; |
209 | 915 | ret = DetectParsePcreExec(&parse_regex2, &match2, str1, 0, 0); |
210 | 915 | if (ret < 1) { |
211 | 207 | SCLogError("Invalid arg \"%s\" supplied to " |
212 | 207 | "ssl_state keyword.", |
213 | 207 | arg); |
214 | 207 | if (match2) { |
215 | 207 | pcre2_match_data_free(match2); |
216 | 207 | } |
217 | 207 | goto error; |
218 | 207 | } |
219 | | |
220 | 708 | pcre2len = sizeof(str2); |
221 | 708 | res = pcre2_substring_copy_bynumber(match2, 1, (PCRE2_UCHAR8 *)str2, &pcre2len); |
222 | 708 | if (res < 0) { |
223 | 0 | SCLogError("pcre2_substring_copy_bynumber failed"); |
224 | 0 | pcre2_match_data_free(match2); |
225 | 0 | goto error; |
226 | 0 | } |
227 | 708 | negate = !strcmp("!", str2); |
228 | | |
229 | 708 | pcre2len = sizeof(str2); |
230 | 708 | res = pcre2_substring_copy_bynumber(match2, 2, (PCRE2_UCHAR8 *)str2, &pcre2len); |
231 | 708 | if (res < 0) { |
232 | 0 | SCLogError("pcre2_substring_copy_bynumber failed"); |
233 | 0 | pcre2_match_data_free(match2); |
234 | 0 | goto error; |
235 | 0 | } |
236 | 708 | if (strcmp("client_hello", str2) == 0) { |
237 | 18 | flags |= DETECT_SSL_STATE_CLIENT_HELLO; |
238 | 18 | if (negate) |
239 | 0 | mask |= DETECT_SSL_STATE_CLIENT_HELLO; |
240 | 690 | } else if (strcmp("server_hello", str2) == 0) { |
241 | 0 | flags |= DETECT_SSL_STATE_SERVER_HELLO; |
242 | 0 | if (negate) |
243 | 0 | mask |= DETECT_SSL_STATE_SERVER_HELLO; |
244 | 690 | } else if (strcmp("client_keyx", str2) == 0) { |
245 | 0 | flags |= DETECT_SSL_STATE_CLIENT_KEYX; |
246 | 0 | if (negate) |
247 | 0 | mask |= DETECT_SSL_STATE_CLIENT_KEYX; |
248 | 690 | } else if (strcmp("server_keyx", str2) == 0) { |
249 | 530 | flags |= DETECT_SSL_STATE_SERVER_KEYX; |
250 | 530 | if (negate) |
251 | 0 | mask |= DETECT_SSL_STATE_SERVER_KEYX; |
252 | 530 | } else if (strcmp("unknown", str2) == 0) { |
253 | 0 | flags |= DETECT_SSL_STATE_UNKNOWN; |
254 | 0 | if (negate) |
255 | 0 | mask |= DETECT_SSL_STATE_UNKNOWN; |
256 | 160 | } else { |
257 | 160 | SCLogError("Found invalid option \"%s\" " |
258 | 160 | "in ssl_state keyword.", |
259 | 160 | str2); |
260 | 160 | pcre2_match_data_free(match2); |
261 | 160 | goto error; |
262 | 160 | } |
263 | | |
264 | 548 | pcre2len = sizeof(str2); |
265 | 548 | res = pcre2_substring_copy_bynumber(match2, 3, (PCRE2_UCHAR8 *)str2, &pcre2len); |
266 | 548 | if (res < 0) { |
267 | 0 | SCLogError("pcre2_substring_copy_bynumber failed"); |
268 | 0 | pcre2_match_data_free(match2); |
269 | 0 | goto error; |
270 | 0 | } |
271 | | |
272 | 548 | memcpy(str1, str2, sizeof(str1)); |
273 | 548 | pcre2_match_data_free(match2); |
274 | 548 | } |
275 | | |
276 | 340 | if ( (ssd = SCMalloc(sizeof(DetectSslStateData))) == NULL) { |
277 | 0 | goto error; |
278 | 0 | } |
279 | 340 | ssd->flags = flags; |
280 | 340 | ssd->mask = mask; |
281 | | |
282 | 340 | pcre2_match_data_free(match); |
283 | 340 | return ssd; |
284 | | |
285 | 4.71k | error: |
286 | 4.71k | if (match) { |
287 | 4.71k | pcre2_match_data_free(match); |
288 | 4.71k | } |
289 | 4.71k | return NULL; |
290 | 340 | } |
291 | | |
292 | | /** |
293 | | * \internal |
294 | | * \brief Setup function for ssl_state keyword. |
295 | | * |
296 | | * \param de_ctx Pointer to the Detection Engine Context. |
297 | | * \param s Pointer to the Current Signature |
298 | | * \param arg String holding the arg. |
299 | | * |
300 | | * \retval 0 On success. |
301 | | * \retval -1 On failure. |
302 | | */ |
303 | | static int DetectSslStateSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) |
304 | 1.02k | { |
305 | 1.02k | DetectSslStateData *ssd = NULL; |
306 | | |
307 | 1.02k | if (SCDetectSignatureSetAppProto(s, ALPROTO_TLS) != 0) |
308 | 13 | return -1; |
309 | | |
310 | 1.01k | ssd = DetectSslStateParse(arg); |
311 | 1.01k | if (ssd == NULL) |
312 | 1.00k | goto error; |
313 | | |
314 | 6 | if (SCSigMatchAppendSMToList( |
315 | 6 | de_ctx, s, DETECT_SSL_STATE, (SigMatchCtx *)ssd, g_tls_generic_list_id) == NULL) { |
316 | 0 | goto error; |
317 | 0 | } |
318 | 6 | return 0; |
319 | | |
320 | 1.00k | error: |
321 | 1.00k | if (ssd != NULL) |
322 | 0 | DetectSslStateFree(de_ctx, ssd); |
323 | 1.00k | return -1; |
324 | 6 | } |
325 | | |
326 | | /** |
327 | | * \brief Free memory associated with DetectSslStateData. |
328 | | * |
329 | | * \param ptr pointer to the data to be freed. |
330 | | */ |
331 | | static void DetectSslStateFree(DetectEngineCtx *de_ctx, void *ptr) |
332 | 340 | { |
333 | 340 | if (ptr != NULL) |
334 | 340 | SCFree(ptr); |
335 | 340 | } |
336 | | |
337 | | #ifdef UNITTESTS |
338 | | #include "tests/detect-ssl-state.c" |
339 | | #endif |