/src/suricata7/src/detect-tls-version.c
Line | Count | Source (jump to first uncovered line) |
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 Victor Julien <victor@inliniac.net> |
22 | | * |
23 | | * Implements the tls.version 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 "app-layer-ssl.h" |
49 | | #include "detect-tls-version.h" |
50 | | |
51 | | #include "stream-tcp.h" |
52 | | |
53 | | /** |
54 | | * \brief Regex for parsing "id" option, matching number or "number" |
55 | | */ |
56 | 73 | #define PARSE_REGEX "^\\s*([A-z0-9\\.]+|\"[A-z0-9\\.]+\")\\s*$" |
57 | | |
58 | | static DetectParseRegex parse_regex; |
59 | | |
60 | | static int DetectTlsVersionMatch (DetectEngineThreadCtx *, |
61 | | Flow *, uint8_t, void *, void *, |
62 | | const Signature *, const SigMatchCtx *); |
63 | | static int DetectTlsVersionSetup (DetectEngineCtx *, Signature *, const char *); |
64 | | #ifdef UNITTESTS |
65 | | static void DetectTlsVersionRegisterTests(void); |
66 | | #endif |
67 | | static void DetectTlsVersionFree(DetectEngineCtx *, void *); |
68 | | static int g_tls_generic_list_id = 0; |
69 | | |
70 | | /** |
71 | | * \brief Registration function for keyword: tls.version |
72 | | */ |
73 | | void DetectTlsVersionRegister (void) |
74 | 73 | { |
75 | 73 | sigmatch_table[DETECT_AL_TLS_VERSION].name = "tls.version"; |
76 | 73 | sigmatch_table[DETECT_AL_TLS_VERSION].desc = "match on TLS/SSL version"; |
77 | 73 | sigmatch_table[DETECT_AL_TLS_VERSION].url = "/rules/tls-keywords.html#tls-version"; |
78 | 73 | sigmatch_table[DETECT_AL_TLS_VERSION].AppLayerTxMatch = DetectTlsVersionMatch; |
79 | 73 | sigmatch_table[DETECT_AL_TLS_VERSION].Setup = DetectTlsVersionSetup; |
80 | 73 | sigmatch_table[DETECT_AL_TLS_VERSION].Free = DetectTlsVersionFree; |
81 | | #ifdef UNITTESTS |
82 | | sigmatch_table[DETECT_AL_TLS_VERSION].RegisterTests = DetectTlsVersionRegisterTests; |
83 | | #endif |
84 | | |
85 | 73 | DetectSetupParseRegexes(PARSE_REGEX, &parse_regex); |
86 | | |
87 | 73 | g_tls_generic_list_id = DetectBufferTypeRegister("tls_generic"); |
88 | 73 | } |
89 | | |
90 | | /** |
91 | | * \brief match the specified version on a tls session |
92 | | * |
93 | | * \param t pointer to thread vars |
94 | | * \param det_ctx pointer to the pattern matcher thread |
95 | | * \param p pointer to the current packet |
96 | | * \param m pointer to the sigmatch that we will cast into DetectTlsVersionData |
97 | | * |
98 | | * \retval 0 no match |
99 | | * \retval 1 match |
100 | | */ |
101 | | static int DetectTlsVersionMatch (DetectEngineThreadCtx *det_ctx, |
102 | | Flow *f, uint8_t flags, void *state, void *txv, |
103 | | const Signature *s, const SigMatchCtx *m) |
104 | 0 | { |
105 | 0 | SCEnter(); |
106 | |
|
107 | 0 | const DetectTlsVersionData *tls_data = (const DetectTlsVersionData *)m; |
108 | 0 | SSLState *ssl_state = (SSLState *)state; |
109 | 0 | if (ssl_state == NULL) { |
110 | 0 | SCLogDebug("no tls state, no match"); |
111 | 0 | SCReturnInt(0); |
112 | 0 | } |
113 | | |
114 | 0 | int ret = 0; |
115 | 0 | uint16_t version = 0; |
116 | 0 | SCLogDebug("looking for tls_data->ver 0x%02X (flags 0x%02X)", tls_data->ver, flags); |
117 | |
|
118 | 0 | if (flags & STREAM_TOCLIENT) { |
119 | 0 | version = ssl_state->server_connp.version; |
120 | 0 | SCLogDebug("server (toclient) version is 0x%02X", version); |
121 | 0 | } else if (flags & STREAM_TOSERVER) { |
122 | 0 | version = ssl_state->client_connp.version; |
123 | 0 | SCLogDebug("client (toserver) version is 0x%02X", version); |
124 | 0 | } |
125 | |
|
126 | 0 | if ((tls_data->flags & DETECT_TLS_VERSION_FLAG_RAW) == 0) { |
127 | | /* Match all TLSv1.3 drafts as TLSv1.3 */ |
128 | 0 | if (((version >> 8) & 0xff) == 0x7f) { |
129 | 0 | version = TLS_VERSION_13; |
130 | 0 | } |
131 | 0 | } |
132 | |
|
133 | 0 | if (tls_data->ver == version) { |
134 | 0 | ret = 1; |
135 | 0 | } |
136 | |
|
137 | 0 | SCReturnInt(ret); |
138 | 0 | } |
139 | | |
140 | | /** |
141 | | * \brief This function is used to parse IPV4 ip_id passed via keyword: "id" |
142 | | * |
143 | | * \param de_ctx Pointer to the detection engine context |
144 | | * \param idstr Pointer to the user provided id option |
145 | | * |
146 | | * \retval id_d pointer to DetectTlsVersionData on success |
147 | | * \retval NULL on failure |
148 | | */ |
149 | | static DetectTlsVersionData *DetectTlsVersionParse (DetectEngineCtx *de_ctx, const char *str) |
150 | 644 | { |
151 | 644 | uint16_t temp; |
152 | 644 | DetectTlsVersionData *tls = NULL; |
153 | 644 | int res = 0; |
154 | 644 | size_t pcre2len; |
155 | | |
156 | 644 | pcre2_match_data *match = NULL; |
157 | 644 | int ret = DetectParsePcreExec(&parse_regex, &match, str, 0, 0); |
158 | 644 | if (ret < 1 || ret > 3) { |
159 | 369 | SCLogError("invalid tls.version option"); |
160 | 369 | goto error; |
161 | 369 | } |
162 | | |
163 | 275 | if (ret > 1) { |
164 | 275 | char ver_ptr[64]; |
165 | 275 | char *tmp_str; |
166 | 275 | pcre2len = sizeof(ver_ptr); |
167 | 275 | res = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)ver_ptr, &pcre2len); |
168 | 275 | if (res < 0) { |
169 | 2 | SCLogError("pcre2_substring_copy_bynumber failed"); |
170 | 2 | goto error; |
171 | 2 | } |
172 | | |
173 | | /* We have a correct id option */ |
174 | 273 | tls = SCCalloc(1, sizeof(DetectTlsVersionData)); |
175 | 273 | if (unlikely(tls == NULL)) |
176 | 0 | goto error; |
177 | | |
178 | 273 | tmp_str = ver_ptr; |
179 | | |
180 | | /* Let's see if we need to scape "'s */ |
181 | 273 | if (tmp_str[0] == '"') |
182 | 0 | { |
183 | 0 | tmp_str[strlen(tmp_str) - 1] = '\0'; |
184 | 0 | tmp_str += 1; |
185 | 0 | } |
186 | | |
187 | 273 | if (strncmp("1.0", tmp_str, 3) == 0) { |
188 | 79 | temp = TLS_VERSION_10; |
189 | 194 | } else if (strncmp("1.1", tmp_str, 3) == 0) { |
190 | 10 | temp = TLS_VERSION_11; |
191 | 184 | } else if (strncmp("1.2", tmp_str, 3) == 0) { |
192 | 12 | temp = TLS_VERSION_12; |
193 | 172 | } else if (strncmp("1.3", tmp_str, 3) == 0) { |
194 | 3 | temp = TLS_VERSION_13; |
195 | 169 | } else if ((strncmp("0x", tmp_str, 2) == 0) && (strlen(str) == 6)) { |
196 | 0 | temp = (uint16_t)strtol(tmp_str, NULL, 0); |
197 | 0 | tls->flags |= DETECT_TLS_VERSION_FLAG_RAW; |
198 | 169 | } else { |
199 | 169 | SCLogError("Invalid value"); |
200 | 169 | goto error; |
201 | 169 | } |
202 | | |
203 | 104 | tls->ver = temp; |
204 | | |
205 | 104 | SCLogDebug("will look for tls %"PRIu16"", tls->ver); |
206 | 104 | } |
207 | | |
208 | 104 | pcre2_match_data_free(match); |
209 | 104 | return tls; |
210 | | |
211 | 540 | error: |
212 | 540 | if (match) { |
213 | 540 | pcre2_match_data_free(match); |
214 | 540 | } |
215 | 540 | if (tls != NULL) |
216 | 169 | DetectTlsVersionFree(de_ctx, tls); |
217 | 540 | return NULL; |
218 | | |
219 | 275 | } |
220 | | |
221 | | /** |
222 | | * \brief this function is used to add the parsed "id" option |
223 | | * \brief into the current signature |
224 | | * |
225 | | * \param de_ctx pointer to the Detection Engine Context |
226 | | * \param s pointer to the Current Signature |
227 | | * \param idstr pointer to the user provided "id" option |
228 | | * |
229 | | * \retval 0 on Success |
230 | | * \retval -1 on Failure |
231 | | */ |
232 | | static int DetectTlsVersionSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str) |
233 | 12 | { |
234 | 12 | DetectTlsVersionData *tls = NULL; |
235 | 12 | SigMatch *sm = NULL; |
236 | | |
237 | 12 | if (DetectSignatureSetAppProto(s, ALPROTO_TLS) != 0) |
238 | 4 | return -1; |
239 | | |
240 | 8 | tls = DetectTlsVersionParse(de_ctx, str); |
241 | 8 | if (tls == NULL) |
242 | 8 | goto error; |
243 | | |
244 | | /* Okay so far so good, lets get this into a SigMatch |
245 | | * and put it in the Signature. */ |
246 | 0 | sm = SigMatchAlloc(); |
247 | 0 | if (sm == NULL) |
248 | 0 | goto error; |
249 | | |
250 | 0 | sm->type = DETECT_AL_TLS_VERSION; |
251 | 0 | sm->ctx = (void *)tls; |
252 | |
|
253 | 0 | SigMatchAppendSMToList(s, sm, g_tls_generic_list_id); |
254 | |
|
255 | 0 | return 0; |
256 | | |
257 | 8 | error: |
258 | 8 | if (tls != NULL) |
259 | 0 | DetectTlsVersionFree(de_ctx, tls); |
260 | 8 | if (sm != NULL) |
261 | 0 | SCFree(sm); |
262 | 8 | return -1; |
263 | |
|
264 | 0 | } |
265 | | |
266 | | /** |
267 | | * \brief this function will free memory associated with DetectTlsVersionData |
268 | | * |
269 | | * \param id_d pointer to DetectTlsVersionData |
270 | | */ |
271 | | static void DetectTlsVersionFree(DetectEngineCtx *de_ctx, void *ptr) |
272 | 273 | { |
273 | 273 | DetectTlsVersionData *id_d = (DetectTlsVersionData *)ptr; |
274 | 273 | SCFree(id_d); |
275 | 273 | } |
276 | | |
277 | | #ifdef UNITTESTS |
278 | | #include "tests/detect-tls-version.c" |
279 | | #endif |