suricata
detect-ssl-version.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2025 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 Gurvinder Singh <gurvindersinghdahiya@gmail.com>
22  *
23  * Implements the ssl_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-helper.h"
43 
44 #include "app-layer.h"
45 #include "app-layer-parser.h"
46 
47 #include "detect-ssl-version.h"
48 
49 #include "stream-tcp.h"
50 #include "app-layer-ssl.h"
51 
52 static int DetectSslVersionMatch(DetectEngineThreadCtx *,
53  Flow *, uint8_t, void *, void *,
54  const Signature *, const SigMatchCtx *);
55 static int DetectSslVersionSetup(DetectEngineCtx *, Signature *, const char *);
56 #ifdef UNITTESTS
57 static void DetectSslVersionRegisterTests(void);
58 #endif
59 static void DetectSslVersionFree(DetectEngineCtx *, void *);
60 static int g_tls_generic_list_id = 0;
61 
62 /**
63  * \brief Registration function for keyword: ssl_version
64  */
66 {
67  sigmatch_table[DETECT_SSL_VERSION].name = "ssl_version";
68  sigmatch_table[DETECT_SSL_VERSION].desc = "match version of SSL/TLS record";
69  sigmatch_table[DETECT_SSL_VERSION].url = "/rules/tls-keywords.html#ssl-version";
70  sigmatch_table[DETECT_SSL_VERSION].AppLayerTxMatch = DetectSslVersionMatch;
71  sigmatch_table[DETECT_SSL_VERSION].Setup = DetectSslVersionSetup;
72  sigmatch_table[DETECT_SSL_VERSION].Free = DetectSslVersionFree;
73  /* ssl_version matches the record version, which can be seen before the
74  * hello is decoded: a miss must stay revisitable across records. */
76 #ifdef UNITTESTS
77  sigmatch_table[DETECT_SSL_VERSION].RegisterTests = DetectSslVersionRegisterTests;
78 #endif
79 
80  g_tls_generic_list_id = DetectBufferTypeRegister("tls_generic");
81 }
82 
83 /**
84  * \brief match the specified version on a ssl session
85  *
86  * \param t pointer to thread vars
87  * \param det_ctx pointer to the pattern matcher thread
88  * \param p pointer to the current packet
89  * \param m pointer to the sigmatch that we will cast into DetectSslVersionData
90  *
91  * \retval 0 no match
92  * \retval 1 match
93  */
94 static int DetectSslVersionMatch(DetectEngineThreadCtx *det_ctx,
95  Flow *f, uint8_t flags, void *state, void *txv,
96  const Signature *s, const SigMatchCtx *m)
97 {
98  SCEnter();
99 
100  int ret = 0;
101  uint16_t ver = 0;
102  bool sig_ver = false;
103 
104  const DetectSslVersionData *ssl = (const DetectSslVersionData *)m;
105  const SSLState *app_state = (SSLState *)state;
106  if (app_state == NULL) {
107  SCLogDebug("no app state, no match");
108  SCReturnInt(0);
109  }
110 
111  if (flags & STREAM_TOCLIENT) {
112  SCLogDebug("server (toclient) version is 0x%02X",
113  app_state->server_connp.version);
114  ver = app_state->server_connp.version;
115  } else if (flags & STREAM_TOSERVER) {
116  SCLogDebug("client (toserver) version is 0x%02X",
117  app_state->client_connp.version);
118  ver = app_state->client_connp.version;
119  }
120 
121  switch (ver) {
122  case SSL_VERSION_2:
123  if (ssl->data[SSLv2])
124  ret = 1;
125  sig_ver = true;
126  break;
127  case SSL_VERSION_3:
128  if (ssl->data[SSLv3])
129  ret = 1;
130  sig_ver = true;
131  break;
132  case TLS_VERSION_10:
133  if (ssl->data[TLS10])
134  ret = 1;
135  sig_ver = true;
136  break;
137  case TLS_VERSION_11:
138  if (ssl->data[TLS11])
139  ret = 1;
140  sig_ver = true;
141  break;
142  case TLS_VERSION_12:
143  if (ssl->data[TLS12])
144  ret = 1;
145  sig_ver = true;
146  break;
147  case TLS_VERSION_13_DRAFT28:
148  case TLS_VERSION_13_DRAFT27:
149  case TLS_VERSION_13_DRAFT26:
150  case TLS_VERSION_13_DRAFT25:
151  case TLS_VERSION_13_DRAFT24:
152  case TLS_VERSION_13_DRAFT23:
153  case TLS_VERSION_13_DRAFT22:
154  case TLS_VERSION_13_DRAFT21:
155  case TLS_VERSION_13_DRAFT20:
156  case TLS_VERSION_13_DRAFT19:
157  case TLS_VERSION_13_DRAFT18:
158  case TLS_VERSION_13_DRAFT17:
159  case TLS_VERSION_13_DRAFT16:
160  case TLS_VERSION_13_PRE_DRAFT16:
161  case TLS_VERSION_13:
162  if (ssl->data[TLS13])
163  ret = 1;
164  sig_ver = true;
165  break;
166  }
167 
168  if (!sig_ver)
169  SCReturnInt(0);
170 
171  // matches if ret == 1 and negate is false
172  // or if ret == 0 and negate is true
173  SCReturnInt(ret ^ (ssl->negate ? 1 : 0));
174 }
175 
177  const char *word;
178  int index;
179 };
180 
182  { "sslv2", SSLv2 },
183  { "sslv3", SSLv3 },
184  { "tls1.0", TLS10 },
185  { "tls1.1", TLS11 },
186  { "tls1.2", TLS12 },
187  { "tls1.3", TLS13 },
188 };
189 
190 /**
191  * \brief This function is used to parse ssl_version data passed via
192  * keyword: "ssl_version"
193  *
194  * \param de_ctx Pointer to the detection engine context
195  * \param str Pointer to the user provided options
196  *
197  * \retval ssl pointer to DetectSslVersionData on success
198  * \retval NULL on failure
199  */
200 static DetectSslVersionData *DetectSslVersionParse(DetectEngineCtx *de_ctx, const char *str)
201 {
202  const char *tmp_str = str;
203  size_t tmp_len = 0;
204 
205  /* We have a correct ssl_version options */
207  if (unlikely(ssl == NULL))
208  goto error;
209 
210  // skip leading space
211  while (tmp_str[0] != 0 && isspace(tmp_str[0])) {
212  tmp_str++;
213  }
214  if (tmp_str[0] == 0) {
215  SCLogError("Invalid empty value");
216  goto error;
217  }
218  if (tmp_str[0] == '!') {
219  ssl->negate = true;
220  tmp_str++;
221  }
222  // iterate every version separated by comma
223  while (tmp_str[0] != 0) {
224  // counts word length
225  tmp_len = 0;
226  while (tmp_str[tmp_len] != 0 && !isspace(tmp_str[tmp_len]) && tmp_str[tmp_len] != ',') {
227  tmp_len++;
228  }
229 
230  bool is_keyword = false;
231  for (size_t i = 0; i < TLS_SIZE; i++) {
232  if (tmp_len == strlen(ssl_version_keywords[i].word) &&
233  strncasecmp(ssl_version_keywords[i].word, tmp_str, tmp_len) == 0) {
234  if (ssl->data[ssl_version_keywords[i].index]) {
235  SCLogError("Invalid duplicate value");
236  goto error;
237  }
238  ssl->data[ssl_version_keywords[i].index] = true;
239  is_keyword = true;
240  break;
241  }
242  }
243  if (!is_keyword) {
244  SCLogError("Invalid unknown value");
245  goto error;
246  }
247 
248  tmp_str += tmp_len;
249  while (isspace(tmp_str[0]) || tmp_str[0] == ',') {
250  tmp_str++;
251  }
252  }
253 
254  return ssl;
255 
256 error:
257  if (ssl != NULL)
258  DetectSslVersionFree(de_ctx, ssl);
259  return NULL;
260 }
261 
262 /**
263  * \brief this function is used to add the parsed "id" option
264  * \brief into the current signature
265  *
266  * \param de_ctx pointer to the Detection Engine Context
267  * \param s pointer to the Current Signature
268  * \param idstr pointer to the user provided "id" option
269  *
270  * \retval 0 on Success
271  * \retval -1 on Failure
272  */
273 static int DetectSslVersionSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str)
274 {
276  return -1;
277 
278  DetectSslVersionData *ssl = DetectSslVersionParse(de_ctx, str);
279  if (ssl == NULL)
280  return -1;
281 
282  /* Okay so far so good, lets get this into a SigMatch
283  * and put it in the Signature. */
284 
286  de_ctx, s, DETECT_SSL_VERSION, (SigMatchCtx *)ssl, g_tls_generic_list_id) == NULL) {
287  DetectSslVersionFree(de_ctx, ssl);
288  return -1;
289  }
290 
291  return 0;
292 }
293 
294 /**
295  * \brief this function will free memory associated with DetectSslVersionData
296  *
297  * \param id_d pointer to DetectSslVersionData
298  */
299 void DetectSslVersionFree(DetectEngineCtx *de_ctx, void *ptr)
300 {
301  if (ptr != NULL)
302  SCFree(ptr);
303 }
304 
305 #ifdef UNITTESTS
307 #endif
SigTableElmt_::url
const char * url
Definition: detect.h:1545
SSLState_
SSLv[2.0|3.[0|1|2|3]] state structure.
Definition: app-layer-ssl.h:263
TLS10
@ TLS10
Definition: detect-ssl-version.h:31
detect-engine.h
SigTableElmt_::desc
const char * desc
Definition: detect.h:1544
sigmatch_table
SigTableElmt * sigmatch_table
Definition: detect-parse.c:78
SigTableElmt_::Free
void(* Free)(DetectEngineCtx *, void *)
Definition: detect.h:1529
flow-util.h
SigTableElmt_::name
const char * name
Definition: detect.h:1542
TLS12
@ TLS12
Definition: detect-ssl-version.h:33
stream-tcp.h
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SSLState_::client_connp
SSLStateConnp client_connp
Definition: app-layer-ssl.h:284
SigTableElmt_::flags
uint32_t flags
Definition: detect.h:1533
ALPROTO_TLS
@ ALPROTO_TLS
Definition: app-layer-protos.h:39
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
SSLState_::server_connp
SSLStateConnp server_connp
Definition: app-layer-ssl.h:285
threads.h
Flow_
Flow data structure.
Definition: flow.h:359
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:995
SigTableElmt_::AppLayerTxMatch
int(* AppLayerTxMatch)(DetectEngineThreadCtx *, Flow *, uint8_t flags, void *alstate, void *txv, const Signature *, const SigMatchCtx *)
Definition: detect.h:1507
DetectSslVersionData_::negate
bool negate
Definition: detect-ssl-version.h:41
SSLVersionKeywords::word
const char * word
Definition: detect-ssl-version.c:177
SSLVersionKeywords
Definition: detect-ssl-version.c:176
m
SCMutex m
Definition: flow-hash.h:6
detect-ssl-version.c
SCDetectSignatureSetAppProto
int SCDetectSignatureSetAppProto(Signature *s, AppProto alproto)
Definition: detect-parse.c:2613
TLS13
@ TLS13
Definition: detect-ssl-version.h:34
SigTableElmt_::Setup
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1524
TLS_SIZE
@ TLS_SIZE
Definition: detect-ssl-version.h:36
util-unittest-helper.h
SSLv3
@ SSLv3
Definition: detect-ssl-version.h:30
SSLVersionKeywords::index
int index
Definition: detect-ssl-version.c:178
decode.h
util-debug.h
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:22
DetectEngineThreadCtx_
Definition: detect.h:1316
TLS11
@ TLS11
Definition: detect-ssl-version.h:32
SCEnter
#define SCEnter(...)
Definition: util-debug.h:284
detect-engine-mpm.h
SCSigMatchAppendSMToList
SigMatch * SCSigMatchAppendSMToList(DetectEngineCtx *de_ctx, Signature *s, uint16_t type, SigMatchCtx *ctx, const int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:424
detect.h
DetectSslVersionData_
Definition: detect-ssl-version.h:39
app-layer-parser.h
detect-engine-state.h
Data structures and function prototypes for keeping state for the detection engine.
ssl_version_keywords
struct SSLVersionKeywords ssl_version_keywords[TLS_SIZE]
Definition: detect-ssl-version.c:181
detect-ssl-version.h
SigMatchCtx_
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition: detect.h:355
DetectBufferTypeRegister
int DetectBufferTypeRegister(const char *name)
Definition: detect-engine.c:1388
flags
uint8_t flags
Definition: decode-gre.h:0
suricata-common.h
SSLv2
@ SSLv2
Definition: detect-ssl-version.h:29
DETECT_SSL_VERSION
@ DETECT_SSL_VERSION
Definition: detect-engine-register.h:202
str
#define str(s)
Definition: suricata-common.h:313
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:274
SCFree
#define SCFree(p)
Definition: util-mem.h:61
detect-parse.h
Signature_
Signature container.
Definition: detect.h:692
DetectSslVersionRegister
void DetectSslVersionRegister(void)
Registration function for keyword: ssl_version.
Definition: detect-ssl-version.c:65
flow.h
SIGMATCH_STATEFUL
#define SIGMATCH_STATEFUL
Definition: detect-engine-register.h:356
DetectSslVersionData_::data
bool data[TLS_SIZE]
Definition: detect-ssl-version.h:43
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:288
flow-var.h
app-layer-ssl.h
SigTableElmt_::RegisterTests
void(* RegisterTests)(void)
Definition: detect.h:1531
app-layer.h
f
Flow f
Definition: fuzz_dataset.c:32
SSLStateConnp_::version
uint16_t version
Definition: app-layer-ssl.h:186