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