Coverage Report

Created: 2026-01-16 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata/src/detect-urilen.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 Gurvinder Singh <gurvindersighdahiya@gmail.com>
22
 *
23
 * Implements the urilen keyword
24
 */
25
26
#include "suricata-common.h"
27
#include "app-layer.h"
28
#include "app-layer-protos.h"
29
#include "app-layer-htp.h"
30
#include "util-unittest.h"
31
#include "util-unittest-helper.h"
32
33
#include "detect.h"
34
#include "detect-parse.h"
35
#include "detect-engine.h"
36
#include "detect-engine-state.h"
37
#include "detect-engine-build.h"
38
#include "detect-content.h"
39
#include "detect-engine-uint.h"
40
41
#include "detect-urilen.h"
42
#include "util-debug.h"
43
#include "util-byte.h"
44
#include "flow-util.h"
45
#include "stream-tcp.h"
46
47
48
/*prototypes*/
49
static int DetectUrilenSetup (DetectEngineCtx *, Signature *, const char *);
50
static void DetectUrilenFree (DetectEngineCtx *, void *);
51
#ifdef UNITTESTS
52
static void DetectUrilenRegisterTests (void);
53
#endif
54
static int g_http_uri_buffer_id = 0;
55
static int g_http_raw_uri_buffer_id = 0;
56
57
/**
58
 * \brief Registration function for urilen: keyword
59
 */
60
61
void DetectUrilenRegister(void)
62
73
{
63
73
    sigmatch_table[DETECT_URILEN].name = "urilen";
64
73
    sigmatch_table[DETECT_URILEN].desc = "match on the length of the HTTP uri";
65
73
    sigmatch_table[DETECT_URILEN].url = "/rules/http-keywords.html#urilen";
66
73
    sigmatch_table[DETECT_URILEN].Match = NULL;
67
73
    sigmatch_table[DETECT_URILEN].Setup = DetectUrilenSetup;
68
73
    sigmatch_table[DETECT_URILEN].Free = DetectUrilenFree;
69
#ifdef UNITTESTS
70
    sigmatch_table[DETECT_URILEN].RegisterTests = DetectUrilenRegisterTests;
71
#endif
72
73
73
    g_http_uri_buffer_id = DetectBufferTypeRegister("http_uri");
74
73
    g_http_raw_uri_buffer_id = DetectBufferTypeRegister("http_raw_uri");
75
73
}
76
77
/**
78
 * \brief This function is used to parse urilen options passed via urilen: keyword
79
 *
80
 * \param urilenstr Pointer to the user provided urilen options
81
 *
82
 * \retval urilend pointer to DetectUrilenData on success
83
 * \retval NULL on failure
84
 */
85
86
static DetectUrilenData *DetectUrilenParse (const char *urilenstr)
87
3.50k
{
88
3.50k
    return SCDetectUrilenParse(urilenstr);
89
3.50k
}
90
91
/**
92
 * \brief this function is used to parse urilen data into the current signature
93
 *
94
 * \param de_ctx pointer to the Detection Engine Context
95
 * \param s pointer to the Current Signature
96
 * \param urilenstr pointer to the user provided urilen options
97
 *
98
 * \retval 0 on Success
99
 * \retval -1 on Failure
100
 */
101
static int DetectUrilenSetup (DetectEngineCtx *de_ctx, Signature *s, const char *urilenstr)
102
1.92k
{
103
1.92k
    SCEnter();
104
1.92k
    DetectUrilenData *urilend = NULL;
105
106
1.92k
    if (SCDetectSignatureSetAppProto(s, ALPROTO_HTTP) != 0)
107
184
        return -1;
108
109
1.74k
    urilend = DetectUrilenParse(urilenstr);
110
1.74k
    if (urilend == NULL)
111
761
        goto error;
112
113
983
    if (urilend->raw_buffer) {
114
135
        if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_URILEN, (SigMatchCtx *)urilend,
115
135
                    g_http_raw_uri_buffer_id) == NULL) {
116
0
            goto error;
117
0
        }
118
848
    } else {
119
848
        if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_URILEN, (SigMatchCtx *)urilend,
120
848
                    g_http_uri_buffer_id) == NULL) {
121
0
            goto error;
122
0
        }
123
848
    }
124
125
983
    SCReturnInt(0);
126
127
761
error:
128
761
    DetectUrilenFree(de_ctx, urilend);
129
761
    SCReturnInt(-1);
130
983
}
131
132
/**
133
 * \brief this function will free memory associated with DetectUrilenData
134
 *
135
 * \param ptr pointer to DetectUrilenData
136
 */
137
static void DetectUrilenFree(DetectEngineCtx *de_ctx, void *ptr)
138
3.50k
{
139
3.50k
    if (ptr == NULL)
140
929
        return;
141
142
2.57k
    DetectUrilenData *urilend = (DetectUrilenData *)ptr;
143
2.57k
    SCDetectUrilenFree(urilend);
144
2.57k
}
145
146
/** \brief set prefilter dsize pair
147
 *  \param s signature to get dsize value from
148
 */
149
void DetectUrilenApplyToContent(Signature *s, int list)
150
76.3k
{
151
83.6k
    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
152
82.6k
        if (s->init_data->buffers[x].id != (uint32_t)list)
153
6.50k
            continue;
154
155
76.1k
        uint16_t high = UINT16_MAX;
156
76.1k
        bool found = false;
157
158
230k
        for (SigMatch *sm = s->init_data->buffers[x].head; sm != NULL; sm = sm->next) {
159
154k
            if (sm->type != DETECT_URILEN)
160
151k
                continue;
161
162
3.21k
            DetectUrilenData *dd = (DetectUrilenData *)sm->ctx;
163
164
3.21k
            switch (dd->du16.mode) {
165
279
                case DETECT_UINT_LT:
166
279
                    if (dd->du16.arg1 < UINT16_MAX) {
167
279
                        high = dd->du16.arg1 + 1;
168
279
                    }
169
279
                    break;
170
1
                case DETECT_UINT_LTE:
171
                    // fallthrough
172
2.40k
                case DETECT_UINT_EQ:
173
2.40k
                    high = dd->du16.arg1;
174
2.40k
                    break;
175
55
                case DETECT_UINT_RA:
176
55
                    if (dd->du16.arg2 < UINT16_MAX) {
177
55
                        high = dd->du16.arg2 + 1;
178
55
                    }
179
55
                    break;
180
71
                case DETECT_UINT_NE:
181
                    // fallthrough
182
135
                case DETECT_UINT_GTE:
183
                    // fallthrough
184
470
                case DETECT_UINT_GT:
185
470
                    high = UINT16_MAX;
186
470
                    break;
187
3.21k
            }
188
3.21k
            found = true;
189
3.21k
        }
190
191
        // skip 65535 to avoid mismatch on uri > 64k
192
76.1k
        if (!found || high == UINT16_MAX)
193
75.3k
            return;
194
195
818
        SCLogDebug("high %u", high);
196
197
6.37k
        for (SigMatch *sm = s->init_data->buffers[x].head; sm != NULL; sm = sm->next) {
198
5.56k
            if (sm->type != DETECT_CONTENT) {
199
3.93k
                continue;
200
3.93k
            }
201
1.62k
            DetectContentData *cd = (DetectContentData *)sm->ctx;
202
1.62k
            if (cd == NULL) {
203
0
                continue;
204
0
            }
205
206
1.62k
            if (cd->depth == 0 || cd->depth > high) {
207
675
                cd->depth = high;
208
675
                cd->flags |= DETECT_CONTENT_DEPTH;
209
675
                SCLogDebug("updated %u, content %u to have depth %u "
210
675
                           "because of urilen.",
211
675
                        s->id, cd->id, cd->depth);
212
675
            }
213
1.62k
        }
214
818
    }
215
76.3k
}
216
217
bool DetectUrilenValidateContent(
218
        const Signature *s, const char **sigerror, const DetectBufferType *dbt)
219
68.6k
{
220
145k
    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
221
76.4k
        if (s->init_data->buffers[x].id != (uint32_t)dbt->id)
222
7.82k
            continue;
223
190k
        for (const SigMatch *sm = s->init_data->buffers[x].head; sm != NULL; sm = sm->next) {
224
121k
            if (sm->type != DETECT_CONTENT) {
225
58.8k
                continue;
226
58.8k
            }
227
62.9k
            DetectContentData *cd = (DetectContentData *)sm->ctx;
228
62.9k
            if (cd == NULL) {
229
0
                continue;
230
0
            }
231
232
62.9k
            if (cd->depth && cd->depth < cd->content_len) {
233
16
                *sigerror = "depth or urilen smaller than content len";
234
16
                SCLogError("depth or urilen %u smaller "
235
16
                           "than content len %u",
236
16
                        cd->depth, cd->content_len);
237
16
                return false;
238
16
            }
239
62.9k
        }
240
68.6k
    }
241
68.6k
    return true;
242
68.6k
}
243
244
#ifdef UNITTESTS
245
246
#include "stream.h"
247
#include "stream-tcp-private.h"
248
#include "stream-tcp-reassemble.h"
249
#include "detect-engine-mpm.h"
250
#include "app-layer-parser.h"
251
#include "detect-engine-alert.h"
252
253
/** \test   Test the Urilen keyword setup */
254
static int DetectUrilenParseTest01(void)
255
{
256
    DetectUrilenData *urilend = DetectUrilenParse("10");
257
    FAIL_IF_NULL(urilend);
258
    FAIL_IF(urilend->du16.arg1 != 10);
259
    FAIL_IF(urilend->du16.mode != DETECT_UINT_EQ);
260
    FAIL_IF(urilend->raw_buffer);
261
262
    DetectUrilenFree(NULL, urilend);
263
    PASS;
264
}
265
266
/** \test   Test the Urilen keyword setup */
267
static int DetectUrilenParseTest02(void)
268
{
269
    DetectUrilenData *urilend = DetectUrilenParse(" < 10  ");
270
    FAIL_IF_NULL(urilend);
271
    FAIL_IF(urilend->du16.arg1 != 10);
272
    FAIL_IF(urilend->du16.mode != DETECT_UINT_LT);
273
    FAIL_IF(urilend->raw_buffer);
274
275
    DetectUrilenFree(NULL, urilend);
276
    PASS;
277
}
278
279
/** \test   Test the Urilen keyword setup */
280
static int DetectUrilenParseTest03(void)
281
{
282
    DetectUrilenData *urilend = DetectUrilenParse(" > 10 ");
283
    FAIL_IF_NULL(urilend);
284
    FAIL_IF(urilend->du16.arg1 != 10);
285
    FAIL_IF(urilend->du16.mode != DETECT_UINT_GT);
286
    FAIL_IF(urilend->raw_buffer);
287
288
    DetectUrilenFree(NULL, urilend);
289
    PASS;
290
}
291
292
/** \test   Test the Urilen keyword setup */
293
static int DetectUrilenParseTest04(void)
294
{
295
    DetectUrilenData *urilend = DetectUrilenParse(" 5 <> 10 ");
296
    FAIL_IF_NULL(urilend);
297
    FAIL_IF(urilend->du16.arg1 != 5);
298
    FAIL_IF(urilend->du16.arg2 != 10);
299
    FAIL_IF(urilend->du16.mode != DETECT_UINT_RA);
300
    FAIL_IF(urilend->raw_buffer);
301
302
    DetectUrilenFree(NULL, urilend);
303
    PASS;
304
}
305
306
/** \test   Test the Urilen keyword setup */
307
static int DetectUrilenParseTest05(void)
308
{
309
    DetectUrilenData *urilend = DetectUrilenParse("5<>10,norm");
310
    FAIL_IF_NULL(urilend);
311
    FAIL_IF(urilend->du16.arg1 != 5);
312
    FAIL_IF(urilend->du16.arg2 != 10);
313
    FAIL_IF(urilend->du16.mode != DETECT_UINT_RA);
314
    FAIL_IF(urilend->raw_buffer);
315
316
    DetectUrilenFree(NULL, urilend);
317
    PASS;
318
}
319
320
/** \test   Test the Urilen keyword setup */
321
static int DetectUrilenParseTest06(void)
322
{
323
    DetectUrilenData *urilend = DetectUrilenParse("5<>10,raw");
324
    FAIL_IF_NULL(urilend);
325
    FAIL_IF(urilend->du16.arg1 != 5);
326
    FAIL_IF(urilend->du16.arg2 != 10);
327
    FAIL_IF(urilend->du16.mode != DETECT_UINT_RA);
328
    FAIL_IF(!urilend->raw_buffer);
329
330
    DetectUrilenFree(NULL, urilend);
331
    PASS;
332
}
333
334
/** \test   Test the Urilen keyword setup */
335
static int DetectUrilenParseTest07(void)
336
{
337
    DetectUrilenData *urilend = DetectUrilenParse(">10, norm ");
338
    FAIL_IF_NULL(urilend);
339
    FAIL_IF(urilend->du16.arg1 != 10);
340
    FAIL_IF(urilend->du16.mode != DETECT_UINT_GT);
341
    FAIL_IF(urilend->raw_buffer);
342
343
    DetectUrilenFree(NULL, urilend);
344
    PASS;
345
}
346
347
/** \test   Test the Urilen keyword setup */
348
static int DetectUrilenParseTest08(void)
349
{
350
    DetectUrilenData *urilend = DetectUrilenParse("<10, norm ");
351
    FAIL_IF_NULL(urilend);
352
    FAIL_IF(urilend->du16.arg1 != 10);
353
    FAIL_IF(urilend->du16.mode != DETECT_UINT_LT);
354
    FAIL_IF(urilend->raw_buffer);
355
356
    DetectUrilenFree(NULL, urilend);
357
    PASS;
358
}
359
360
/** \test   Test the Urilen keyword setup */
361
static int DetectUrilenParseTest09(void)
362
{
363
    DetectUrilenData *urilend = DetectUrilenParse(">10, raw ");
364
    FAIL_IF_NULL(urilend);
365
    FAIL_IF(urilend->du16.arg1 != 10);
366
    FAIL_IF(urilend->du16.mode != DETECT_UINT_GT);
367
    FAIL_IF(!urilend->raw_buffer);
368
369
    DetectUrilenFree(NULL, urilend);
370
    PASS;
371
}
372
373
/** \test   Test the Urilen keyword setup */
374
static int DetectUrilenParseTest10(void)
375
{
376
    DetectUrilenData *urilend = DetectUrilenParse("<10, raw ");
377
    FAIL_IF_NULL(urilend);
378
    FAIL_IF(urilend->du16.arg1 != 10);
379
    FAIL_IF(urilend->du16.mode != DETECT_UINT_LT);
380
    FAIL_IF(!urilend->raw_buffer);
381
382
    DetectUrilenFree(NULL, urilend);
383
    PASS;
384
}
385
386
/**
387
 * \brief this function is used to initialize the detection engine context and
388
 *        setup the signature with passed values.
389
 *
390
 */
391
392
static int DetectUrilenInitTest(DetectEngineCtx **de_ctx, Signature **sig,
393
                                DetectUrilenData **urilend, const char *str)
394
{
395
    char fullstr[1024];
396
    int result = 0;
397
398
    *de_ctx = NULL;
399
    *sig = NULL;
400
401
    if (snprintf(fullstr, 1024, "alert ip any any -> any any (msg:\"Urilen "
402
                                "test\"; urilen:%s; sid:1;)", str) >= 1024) {
403
        goto end;
404
    }
405
406
    *de_ctx = DetectEngineCtxInit();
407
    if (*de_ctx == NULL) {
408
        goto end;
409
    }
410
411
    (*de_ctx)->flags |= DE_QUIET;
412
413
    (*de_ctx)->sig_list = SigInit(*de_ctx, fullstr);
414
    if ((*de_ctx)->sig_list == NULL) {
415
        goto end;
416
    }
417
418
    *sig = (*de_ctx)->sig_list;
419
420
    *urilend = DetectUrilenParse(str);
421
422
    result = 1;
423
424
end:
425
    return result;
426
}
427
428
/**
429
 * \test DetectUrilenSetpTest01 is a test for setting up an valid urilen values
430
 *       with valid "<>" operator and include spaces arround the given values.
431
 *       In the test the values are setup with initializing the detection engine
432
 *       context and setting up the signature itself.
433
 */
434
435
static int DetectUrilenSetpTest01(void)
436
{
437
    DetectUrilenData *urilend = NULL;
438
    Signature *sig = NULL;
439
    DetectEngineCtx *de_ctx = NULL;
440
441
    uint8_t res = DetectUrilenInitTest(&de_ctx, &sig, &urilend, "1 <> 3");
442
    FAIL_IF(res == 0);
443
    FAIL_IF_NULL(urilend);
444
    FAIL_IF_NOT(urilend->du16.arg1 == 1);
445
    FAIL_IF_NOT(urilend->du16.arg2 == 3);
446
    FAIL_IF_NOT(urilend->du16.mode == DETECT_UINT_RA);
447
448
    DetectUrilenFree(NULL, urilend);
449
    DetectEngineCtxFree(de_ctx);
450
    PASS;
451
}
452
453
/** \test Check a signature with given urilen */
454
static int DetectUrilenSigTest01(void)
455
{
456
    Flow f;
457
    uint8_t httpbuf1[] = "POST /suricata HTTP/1.0\r\n"
458
                         "Host: foo.bar.tld\r\n"
459
                         "\r\n";
460
    uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
461
    TcpSession ssn;
462
    ThreadVars th_v;
463
    DetectEngineThreadCtx *det_ctx = NULL;
464
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
465
466
    memset(&th_v, 0, sizeof(th_v));
467
    StatsThreadInit(&th_v.stats);
468
    memset(&f, 0, sizeof(f));
469
    memset(&ssn, 0, sizeof(ssn));
470
471
    Packet *p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
472
473
    FLOW_INITIALIZE(&f);
474
    f.protoctx = (void *)&ssn;
475
    f.proto = IPPROTO_TCP;
476
    f.flags |= FLOW_IPV4;
477
478
    p->flow = &f;
479
    p->flowflags |= FLOW_PKT_TOSERVER;
480
    p->flowflags |= FLOW_PKT_ESTABLISHED;
481
    p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
482
    f.alproto = ALPROTO_HTTP1;
483
484
    StreamTcpInitConfig(true);
485
486
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
487
    FAIL_IF_NULL(de_ctx);
488
    de_ctx->flags |= DE_QUIET;
489
490
    Signature *s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
491
                                                 "(msg:\"Testing urilen\"; "
492
                                                 "urilen: <5; sid:1;)");
493
    FAIL_IF_NULL(s);
494
    s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
495
                                      "(msg:\"Testing http_method\"; "
496
                                      "urilen: >5; sid:2;)");
497
    FAIL_IF_NULL(s);
498
499
    SigGroupBuild(de_ctx);
500
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
501
502
    int r = AppLayerParserParse(
503
            NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf1, httplen1);
504
    FAIL_IF(r != 0);
505
506
    HtpState *htp_state = f.alstate;
507
    FAIL_IF_NULL(htp_state);
508
509
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
510
511
    FAIL_IF(PacketAlertCheck(p, 1));
512
    FAIL_IF(!PacketAlertCheck(p, 2));
513
514
    UTHFreePackets(&p, 1);
515
    FLOW_DESTROY(&f);
516
517
    AppLayerParserThreadCtxFree(alp_tctx);
518
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
519
    DetectEngineCtxFree(de_ctx);
520
    StreamTcpFreeConfig(true);
521
    StatsThreadCleanup(&th_v.stats);
522
    PASS;
523
}
524
525
/**
526
 * \brief this function registers unit tests for DetectUrilen
527
 */
528
void DetectUrilenRegisterTests(void)
529
{
530
    UtRegisterTest("DetectUrilenParseTest01", DetectUrilenParseTest01);
531
    UtRegisterTest("DetectUrilenParseTest02", DetectUrilenParseTest02);
532
    UtRegisterTest("DetectUrilenParseTest03", DetectUrilenParseTest03);
533
    UtRegisterTest("DetectUrilenParseTest04", DetectUrilenParseTest04);
534
    UtRegisterTest("DetectUrilenParseTest05", DetectUrilenParseTest05);
535
    UtRegisterTest("DetectUrilenParseTest06", DetectUrilenParseTest06);
536
    UtRegisterTest("DetectUrilenParseTest07", DetectUrilenParseTest07);
537
    UtRegisterTest("DetectUrilenParseTest08", DetectUrilenParseTest08);
538
    UtRegisterTest("DetectUrilenParseTest09", DetectUrilenParseTest09);
539
    UtRegisterTest("DetectUrilenParseTest10", DetectUrilenParseTest10);
540
    UtRegisterTest("DetectUrilenSetpTest01", DetectUrilenSetpTest01);
541
    UtRegisterTest("DetectUrilenSigTest01", DetectUrilenSigTest01);
542
}
543
#endif /* UNITTESTS */