/src/suricata/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 "debug.h" |
29 | | #include "decode.h" |
30 | | |
31 | | #include "detect.h" |
32 | | #include "detect-parse.h" |
33 | | |
34 | | #include "detect-engine.h" |
35 | | #include "detect-engine-mpm.h" |
36 | | #include "detect-engine-state.h" |
37 | | |
38 | | #include "flow.h" |
39 | | #include "flow-var.h" |
40 | | #include "flow-util.h" |
41 | | |
42 | | #include "util-debug.h" |
43 | | #include "util-unittest.h" |
44 | | #include "util-unittest-helper.h" |
45 | | |
46 | | #include "app-layer.h" |
47 | | #include "app-layer-parser.h" |
48 | | |
49 | | #include "app-layer-ssl.h" |
50 | | #include "detect-tls-version.h" |
51 | | |
52 | | #include "stream-tcp.h" |
53 | | |
54 | | /** |
55 | | * \brief Regex for parsing "id" option, matching number or "number" |
56 | | */ |
57 | 27 | #define PARSE_REGEX "^\\s*([A-z0-9\\.]+|\"[A-z0-9\\.]+\")\\s*$" |
58 | | |
59 | | static DetectParseRegex parse_regex; |
60 | | |
61 | | static int DetectTlsVersionMatch (DetectEngineThreadCtx *, |
62 | | Flow *, uint8_t, void *, void *, |
63 | | const Signature *, const SigMatchCtx *); |
64 | | static int DetectTlsVersionSetup (DetectEngineCtx *, Signature *, const char *); |
65 | | #ifdef UNITTESTS |
66 | | static void DetectTlsVersionRegisterTests(void); |
67 | | #endif |
68 | | static void DetectTlsVersionFree(DetectEngineCtx *, void *); |
69 | | static int g_tls_generic_list_id = 0; |
70 | | |
71 | | /** |
72 | | * \brief Registration function for keyword: tls.version |
73 | | */ |
74 | | void DetectTlsVersionRegister (void) |
75 | 27 | { |
76 | 27 | sigmatch_table[DETECT_AL_TLS_VERSION].name = "tls.version"; |
77 | 27 | sigmatch_table[DETECT_AL_TLS_VERSION].desc = "match on TLS/SSL version"; |
78 | 27 | sigmatch_table[DETECT_AL_TLS_VERSION].url = "/rules/tls-keywords.html#tls-version"; |
79 | 27 | sigmatch_table[DETECT_AL_TLS_VERSION].AppLayerTxMatch = DetectTlsVersionMatch; |
80 | 27 | sigmatch_table[DETECT_AL_TLS_VERSION].Setup = DetectTlsVersionSetup; |
81 | 27 | sigmatch_table[DETECT_AL_TLS_VERSION].Free = DetectTlsVersionFree; |
82 | | #ifdef UNITTESTS |
83 | | sigmatch_table[DETECT_AL_TLS_VERSION].RegisterTests = DetectTlsVersionRegisterTests; |
84 | | #endif |
85 | | |
86 | 27 | DetectSetupParseRegexes(PARSE_REGEX, &parse_regex); |
87 | | |
88 | 27 | g_tls_generic_list_id = DetectBufferTypeRegister("tls_generic"); |
89 | 27 | } |
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 | 0 | { |
106 | 0 | SCEnter(); |
107 | |
|
108 | 0 | const DetectTlsVersionData *tls_data = (const DetectTlsVersionData *)m; |
109 | 0 | SSLState *ssl_state = (SSLState *)state; |
110 | 0 | if (ssl_state == NULL) { |
111 | 0 | SCLogDebug("no tls state, no match"); |
112 | 0 | SCReturnInt(0); |
113 | 0 | } |
114 | | |
115 | 0 | int ret = 0; |
116 | 0 | uint16_t version = 0; |
117 | 0 | SCLogDebug("looking for tls_data->ver 0x%02X (flags 0x%02X)", tls_data->ver, flags); |
118 | |
|
119 | 0 | if (flags & STREAM_TOCLIENT) { |
120 | 0 | version = ssl_state->server_connp.version; |
121 | 0 | SCLogDebug("server (toclient) version is 0x%02X", version); |
122 | 0 | } else if (flags & STREAM_TOSERVER) { |
123 | 0 | version = ssl_state->client_connp.version; |
124 | 0 | SCLogDebug("client (toserver) version is 0x%02X", version); |
125 | 0 | } |
126 | |
|
127 | 0 | if ((tls_data->flags & DETECT_TLS_VERSION_FLAG_RAW) == 0) { |
128 | | /* Match all TLSv1.3 drafts as TLSv1.3 */ |
129 | 0 | if (((version >> 8) & 0xff) == 0x7f) { |
130 | 0 | version = TLS_VERSION_13; |
131 | 0 | } |
132 | 0 | } |
133 | |
|
134 | 0 | if (tls_data->ver == version) { |
135 | 0 | ret = 1; |
136 | 0 | } |
137 | |
|
138 | 0 | SCReturnInt(ret); |
139 | 0 | } |
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 | 1.67k | { |
152 | 1.67k | uint16_t temp; |
153 | 1.67k | DetectTlsVersionData *tls = NULL; |
154 | 1.67k | int ret = 0, res = 0; |
155 | 1.67k | size_t pcre2len; |
156 | | |
157 | 1.67k | ret = DetectParsePcreExec(&parse_regex, str, 0, 0); |
158 | 1.67k | if (ret < 1 || ret > 3) { |
159 | 322 | SCLogError(SC_ERR_PCRE_MATCH, "invalid tls.version option"); |
160 | 322 | goto error; |
161 | 322 | } |
162 | | |
163 | 1.35k | if (ret > 1) { |
164 | 1.35k | char ver_ptr[64]; |
165 | 1.35k | char *tmp_str; |
166 | 1.35k | pcre2len = sizeof(ver_ptr); |
167 | 1.35k | res = pcre2_substring_copy_bynumber( |
168 | 1.35k | parse_regex.match, 1, (PCRE2_UCHAR8 *)ver_ptr, &pcre2len); |
169 | 1.35k | if (res < 0) { |
170 | 6 | SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed"); |
171 | 6 | goto error; |
172 | 6 | } |
173 | | |
174 | | /* We have a correct id option */ |
175 | 1.35k | tls = SCCalloc(1, sizeof(DetectTlsVersionData)); |
176 | 1.35k | if (unlikely(tls == NULL)) |
177 | 0 | goto error; |
178 | | |
179 | 1.35k | tmp_str = ver_ptr; |
180 | | |
181 | | /* Let's see if we need to scape "'s */ |
182 | 1.35k | if (tmp_str[0] == '"') |
183 | 0 | { |
184 | 0 | tmp_str[strlen(tmp_str) - 1] = '\0'; |
185 | 0 | tmp_str += 1; |
186 | 0 | } |
187 | | |
188 | 1.35k | if (strncmp("1.0", tmp_str, 3) == 0) { |
189 | 113 | temp = TLS_VERSION_10; |
190 | 1.23k | } else if (strncmp("1.1", tmp_str, 3) == 0) { |
191 | 329 | temp = TLS_VERSION_11; |
192 | 908 | } else if (strncmp("1.2", tmp_str, 3) == 0) { |
193 | 167 | temp = TLS_VERSION_12; |
194 | 741 | } else if (strncmp("1.3", tmp_str, 3) == 0) { |
195 | 69 | temp = TLS_VERSION_13; |
196 | 672 | } else if ((strncmp("0x", tmp_str, 2) == 0) && (strlen(str) == 6)) { |
197 | 452 | temp = (uint16_t)strtol(tmp_str, NULL, 0); |
198 | 452 | tls->flags |= DETECT_TLS_VERSION_FLAG_RAW; |
199 | 452 | } else { |
200 | 220 | SCLogError(SC_ERR_INVALID_VALUE, "Invalid value"); |
201 | 220 | goto error; |
202 | 220 | } |
203 | | |
204 | 1.13k | tls->ver = temp; |
205 | | |
206 | 1.13k | SCLogDebug("will look for tls %"PRIu16"", tls->ver); |
207 | 1.13k | } |
208 | | |
209 | 1.13k | return tls; |
210 | | |
211 | 548 | error: |
212 | 548 | if (tls != NULL) |
213 | 220 | DetectTlsVersionFree(de_ctx, tls); |
214 | 548 | return NULL; |
215 | | |
216 | 1.35k | } |
217 | | |
218 | | /** |
219 | | * \brief this function is used to add the parsed "id" option |
220 | | * \brief into the current signature |
221 | | * |
222 | | * \param de_ctx pointer to the Detection Engine Context |
223 | | * \param s pointer to the Current Signature |
224 | | * \param idstr pointer to the user provided "id" option |
225 | | * |
226 | | * \retval 0 on Success |
227 | | * \retval -1 on Failure |
228 | | */ |
229 | | static int DetectTlsVersionSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str) |
230 | 1.68k | { |
231 | 1.68k | DetectTlsVersionData *tls = NULL; |
232 | 1.68k | SigMatch *sm = NULL; |
233 | | |
234 | 1.68k | if (DetectSignatureSetAppProto(s, ALPROTO_TLS) != 0) |
235 | 5 | return -1; |
236 | | |
237 | 1.67k | tls = DetectTlsVersionParse(de_ctx, str); |
238 | 1.67k | if (tls == NULL) |
239 | 548 | goto error; |
240 | | |
241 | | /* Okay so far so good, lets get this into a SigMatch |
242 | | * and put it in the Signature. */ |
243 | 1.13k | sm = SigMatchAlloc(); |
244 | 1.13k | if (sm == NULL) |
245 | 0 | goto error; |
246 | | |
247 | 1.13k | sm->type = DETECT_AL_TLS_VERSION; |
248 | 1.13k | sm->ctx = (void *)tls; |
249 | | |
250 | 1.13k | SigMatchAppendSMToList(s, sm, g_tls_generic_list_id); |
251 | | |
252 | 1.13k | return 0; |
253 | | |
254 | 548 | error: |
255 | 548 | if (tls != NULL) |
256 | 0 | DetectTlsVersionFree(de_ctx, tls); |
257 | 548 | if (sm != NULL) |
258 | 0 | SCFree(sm); |
259 | 548 | return -1; |
260 | | |
261 | 1.13k | } |
262 | | |
263 | | /** |
264 | | * \brief this function will free memory associated with DetectTlsVersionData |
265 | | * |
266 | | * \param id_d pointer to DetectTlsVersionData |
267 | | */ |
268 | | static void DetectTlsVersionFree(DetectEngineCtx *de_ctx, void *ptr) |
269 | 1.35k | { |
270 | 1.35k | DetectTlsVersionData *id_d = (DetectTlsVersionData *)ptr; |
271 | 1.35k | SCFree(id_d); |
272 | 1.35k | } |
273 | | |
274 | | #ifdef UNITTESTS |
275 | | #include "tests/detect-tls-version.c" |
276 | | #endif |