Coverage Report

Created: 2026-11-12 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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