• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jasonish / suricata / 23019292042

12 Mar 2026 07:08PM UTC coverage: 79.245% (-0.004%) from 79.249%
23019292042

push

github

jasonish
github-ci: add schema ordering check for yaml schema

266163 of 335873 relevant lines covered (79.25%)

4877167.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.11
/src/detect-http-header.c
1
/* Copyright (C) 2007-2022 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
 * \ingroup httplayer
20
 *
21
 * @{
22
 */
23

24

25
/**
26
 * \file
27
 *
28
 * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
29
 *
30
 * Implements support for http_header keyword.
31
 */
32

33
#include "suricata-common.h"
34
#include "threads.h"
35
#include "decode.h"
36

37
#include "detect.h"
38
#include "detect-parse.h"
39
#include "detect-engine.h"
40
#include "detect-engine-buffer.h"
41
#include "detect-engine-mpm.h"
42
#include "detect-engine-state.h"
43
#include "detect-engine-prefilter.h"
44
#include "detect-engine-content-inspection.h"
45
#include "detect-content.h"
46
#include "detect-pcre.h"
47

48
#include "util-debug.h"
49
#include "util-print.h"
50
#include "util-memcmp.h"
51
#include "util-profiling.h"
52
#include "util-validate.h"
53

54
#include "app-layer.h"
55
#include "app-layer-parser.h"
56

57
#include "app-layer-htp.h"
58
#include "detect-http-header.h"
59
#include "detect-http-header-common.h"
60

61
static int DetectHttpHeaderSetup(DetectEngineCtx *, Signature *, const char *);
62
#ifdef UNITTESTS
63
static void DetectHttpHeaderRegisterTests(void);
64
#endif
65
static int g_http_header_buffer_id = 0;
66
static int g_keyword_thread_id = 0;
67
static int g_http2_thread_id = 0;
68

69
#define BUFFER_SIZE_STEP    1024
70
static HttpHeaderThreadDataConfig g_td_config = { BUFFER_SIZE_STEP };
71

72
static uint8_t *GetBufferForTX(
73
        htp_tx_t *tx, DetectEngineThreadCtx *det_ctx, uint8_t flags, uint32_t *buffer_len)
74
{
5,467✔
75
    *buffer_len = 0;
5,467✔
76

77
    HttpHeaderThreadData *hdr_td = NULL;
5,467✔
78
    HttpHeaderBuffer *buf = HttpHeaderGetBufferSpace(det_ctx, flags, g_keyword_thread_id, &hdr_td);
5,467✔
79
    if (unlikely(buf == NULL)) {
5,467✔
80
        return NULL;
×
81
    }
×
82

83
    const htp_headers_t *headers;
5,467✔
84
    if (flags & STREAM_TOSERVER) {
5,467✔
85
        if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, tx, flags) <=
2,954✔
86
                HTP_REQUEST_PROGRESS_HEADERS)
2,954✔
87
            return NULL;
317✔
88
        headers = htp_tx_request_headers(tx);
2,637✔
89
    } else {
2,753✔
90
        if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, tx, flags) <=
2,513✔
91
                HTP_RESPONSE_PROGRESS_HEADERS)
2,513✔
92
            return NULL;
93✔
93
        headers = htp_tx_response_headers(tx);
2,420✔
94
    }
2,420✔
95
    if (headers == NULL)
5,057✔
96
        return NULL;
×
97

98
    size_t i = 0;
5,057✔
99
    size_t no_of_headers = htp_headers_size(headers);
5,057✔
100
    for (; i < no_of_headers; i++) {
38,077✔
101
        const htp_header_t *h = htp_headers_get_index(headers, i);
33,020✔
102
        size_t size1 = htp_header_name_len(h);
33,020✔
103
        size_t size2 = htp_header_value_len(h);
33,020✔
104

105
        if (flags & STREAM_TOSERVER) {
33,020✔
106
            if (size1 == 6 && SCMemcmpLowercase("cookie", htp_header_name_ptr(h), 6) == 0) {
13,931✔
107
                continue;
1,137✔
108
            }
1,137✔
109
        } else {
19,910✔
110
            if (size1 == 10 && SCMemcmpLowercase("set-cookie", htp_header_name_ptr(h), 10) == 0) {
19,089✔
111
                continue;
162✔
112
            }
162✔
113
        }
19,089✔
114

115
        size_t size = size1 + size2 + 4;
31,721✔
116
#if 0
117
        if (i + 1 == no_of_headers)
118
            size += 2;
119
#endif
120
        if (size + buf->len > buf->size) {
31,721✔
121
            if (HttpHeaderExpandBuffer(hdr_td, buf, size) != 0) {
3✔
122
                return NULL;
×
123
            }
×
124
        }
3✔
125

126
        memcpy(buf->buffer + buf->len, htp_header_name_ptr(h), htp_header_name_len(h));
31,721✔
127
        buf->len += htp_header_name_len(h);
31,721✔
128
        buf->buffer[buf->len++] = ':';
31,721✔
129
        buf->buffer[buf->len++] = ' ';
31,721✔
130
        memcpy(buf->buffer + buf->len, htp_header_value_ptr(h), htp_header_value_len(h));
31,721✔
131
        buf->len += htp_header_value_len(h);
31,721✔
132
        buf->buffer[buf->len++] = '\r';
31,721✔
133
        buf->buffer[buf->len++] = '\n';
31,721✔
134
#if 0 // looks like this breaks existing rules
135
        if (i + 1 == no_of_headers) {
136
            buf->buffer[buf->len++] = '\r';
137
            buf->buffer[buf->len++] = '\n';
138
        }
139
#endif
140
    }
31,721✔
141

142
    *buffer_len = buf->len;
5,057✔
143
    return buf->buffer;
5,057✔
144
}
5,057✔
145

146
static InspectionBuffer *GetBuffer2ForTX(DetectEngineThreadCtx *det_ctx,
147
        const DetectEngineTransforms *transforms, Flow *_f, const uint8_t flow_flags, void *txv,
148
        const int list_id)
149
{
1,059✔
150
    InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
1,059✔
151
    if (buffer->inspect == NULL) {
1,059✔
152
        uint32_t b_len = 0;
1,039✔
153
        const uint8_t *b = NULL;
1,039✔
154

155
        void *thread_buf = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, g_http2_thread_id);
1,039✔
156
        if (thread_buf == NULL)
1,039✔
157
            return NULL;
×
158
        if (SCHttp2TxGetHeaders(txv, flow_flags, &b, &b_len, thread_buf) != 1)
1,039✔
159
            return NULL;
592✔
160
        if (b == NULL || b_len == 0)
447✔
161
            return NULL;
×
162

163
        InspectionBufferSetupAndApplyTransforms(det_ctx, list_id, buffer, b, b_len, transforms);
447✔
164
    }
447✔
165

166
    return buffer;
467✔
167
}
1,059✔
168

169
static InspectionBuffer *GetData1(DetectEngineThreadCtx *det_ctx,
170
        const DetectEngineTransforms *transforms, Flow *_f, const uint8_t flags, void *txv,
171
        const int list_id)
172
{
731✔
173
    InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
731✔
174
    if (buffer->inspect == NULL) {
731✔
175
        uint32_t data_len = 0;
14✔
176
        uint8_t *data = GetBufferForTX(txv, det_ctx, flags, &data_len);
14✔
177
        InspectionBufferSetupAndApplyTransforms(
14✔
178
                det_ctx, list_id, buffer, data, data_len, transforms);
14✔
179
    }
14✔
180

181
    return buffer;
731✔
182
}
731✔
183

184
typedef struct PrefilterMpmHttpHeaderCtx {
185
    int list_id;
186
    const MpmCtx *mpm_ctx;
187
    const DetectEngineTransforms *transforms;
188
} PrefilterMpmHttpHeaderCtx;
189

190
/** \brief Generic Mpm prefilter callback
191
 *
192
 *  \param det_ctx detection engine thread ctx
193
 *  \param p packet to inspect
194
 *  \param f flow to inspect
195
 *  \param txv tx to inspect
196
 *  \param pectx inspection context
197
 */
198
static void PrefilterMpmHttpHeader(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
199
        Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags)
200
{
5,694✔
201
    SCEnter();
5,694✔
202

203
    const PrefilterMpmHttpHeaderCtx *ctx = pectx;
5,694✔
204
    const MpmCtx *mpm_ctx = ctx->mpm_ctx;
5,694✔
205
    SCLogDebug("running on list %d", ctx->list_id);
5,694✔
206

207
    const int list_id = ctx->list_id;
5,694✔
208
    InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
5,694✔
209
    if (buffer->inspect == NULL) {
5,694✔
210
        uint32_t rawdata_len = 0;
5,452✔
211
        uint8_t *rawdata = GetBufferForTX(txv, det_ctx, flags, &rawdata_len);
5,452✔
212
        if (rawdata_len == 0)
5,452✔
213
            return;
733✔
214

215
        /* setup buffer and apply transforms */
216
        InspectionBufferSetupAndApplyTransforms(
4,719✔
217
                det_ctx, list_id, buffer, rawdata, rawdata_len, ctx->transforms);
4,719✔
218
    }
4,719✔
219

220
    const uint32_t data_len = buffer->inspect_len;
4,961✔
221
    const uint8_t *data = buffer->inspect;
4,961✔
222

223
    SCLogDebug("mpm'ing buffer:");
4,961✔
224
    //PrintRawDataFp(stdout, data, data_len);
225

226
    if (data != NULL && data_len >= mpm_ctx->minlen) {
4,962✔
227
        (void)mpm_table[mpm_ctx->mpm_type].Search(
4,958✔
228
                mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, data, data_len);
4,958✔
229
        PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len);
4,958✔
230
    }
4,958✔
231
}
4,961✔
232

233
static void PrefilterMpmHttpTrailer(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
234
        Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags)
235
{
4,927✔
236
    SCEnter();
4,927✔
237

238
    htp_tx_t *tx = txv;
4,927✔
239
    const HtpTxUserData *htud = (const HtpTxUserData *)htp_tx_get_user_data(tx);
4,927✔
240
    /* if the request wasn't flagged as having a trailer, we skip */
241
    if (((flags & STREAM_TOSERVER) && !htud->request_has_trailers) ||
4,927✔
242
            ((flags & STREAM_TOCLIENT) && !htud->response_has_trailers)) {
4,927✔
243
        SCReturn;
4,592✔
244
    }
4,592✔
245
    PrefilterMpmHttpHeader(det_ctx, pectx, p, f, txv, idx, _txd, flags);
335✔
246
    SCReturn;
335✔
247
}
4,927✔
248

249
static void PrefilterMpmHttpHeaderFree(void *ptr)
250
{
4,219✔
251
    SCFree(ptr);
4,219✔
252
}
4,219✔
253

254
static int PrefilterMpmHttpHeaderRequestRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
255
        MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id)
256
{
1,116✔
257
    SCEnter();
1,116✔
258

259
    /* header */
260
    PrefilterMpmHttpHeaderCtx *pectx = SCCalloc(1, sizeof(*pectx));
1,116✔
261
    if (pectx == NULL)
1,116✔
262
        return -1;
×
263
    pectx->list_id = list_id;
1,116✔
264
    pectx->mpm_ctx = mpm_ctx;
1,116✔
265
    pectx->transforms = &mpm_reg->transforms;
1,116✔
266

267
    int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpHeader, mpm_reg->app_v2.alproto,
1,116✔
268
            HTP_REQUEST_PROGRESS_HEADERS, pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
1,116✔
269
    if (r != 0) {
1,116✔
270
        SCFree(pectx);
×
271
        return r;
×
272
    }
×
273

274
    /* trailer */
275
    pectx = SCCalloc(1, sizeof(*pectx));
1,116✔
276
    if (pectx == NULL)
1,116✔
277
        return -1;
×
278
    pectx->list_id = list_id;
1,116✔
279
    pectx->mpm_ctx = mpm_ctx;
1,116✔
280
    pectx->transforms = &mpm_reg->transforms;
1,116✔
281

282
    r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpTrailer, mpm_reg->app_v2.alproto,
1,116✔
283
            HTP_REQUEST_PROGRESS_TRAILER, pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
1,116✔
284
    if (r != 0) {
1,116✔
285
        SCFree(pectx);
×
286
    }
×
287
    return r;
1,116✔
288
}
1,116✔
289

290
static int PrefilterMpmHttpHeaderResponseRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
291
        MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id)
292
{
713✔
293
    SCEnter();
713✔
294

295
    /* header */
296
    PrefilterMpmHttpHeaderCtx *pectx = SCCalloc(1, sizeof(*pectx));
713✔
297
    if (pectx == NULL)
713✔
298
        return -1;
×
299
    pectx->list_id = list_id;
713✔
300
    pectx->mpm_ctx = mpm_ctx;
713✔
301
    pectx->transforms = &mpm_reg->transforms;
713✔
302

303
    int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpHeader, mpm_reg->app_v2.alproto,
713✔
304
            HTP_RESPONSE_PROGRESS_HEADERS, pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
713✔
305
    if (r != 0) {
713✔
306
        SCFree(pectx);
×
307
        return r;
×
308
    }
×
309

310
    /* trailer */
311
    pectx = SCCalloc(1, sizeof(*pectx));
713✔
312
    if (pectx == NULL)
713✔
313
        return -1;
×
314
    pectx->list_id = list_id;
713✔
315
    pectx->mpm_ctx = mpm_ctx;
713✔
316
    pectx->transforms = &mpm_reg->transforms;
713✔
317

318
    r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpTrailer, mpm_reg->app_v2.alproto,
713✔
319
            HTP_RESPONSE_PROGRESS_TRAILER, pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
713✔
320
    if (r != 0) {
713✔
321
        SCFree(pectx);
×
322
    }
×
323
    return r;
713✔
324
}
713✔
325

326
/**
327
 * \brief The setup function for the http_header keyword for a signature.
328
 *
329
 * \param de_ctx Pointer to the detection engine context.
330
 * \param s      Pointer to signature for the current Signature being parsed
331
 *               from the rules.
332
 * \param m      Pointer to the head of the SigMatchs for the current rule
333
 *               being parsed.
334
 * \param arg    Pointer to the string holding the keyword value.
335
 *
336
 * \retval  0 On success.
337
 * \retval -1 On failure.
338
 */
339
static int DetectHttpHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
340
{
1,029✔
341
    return DetectEngineContentModifierBufferSetup(
1,029✔
342
            de_ctx, s, arg, DETECT_HTTP_HEADER_CM, g_http_header_buffer_id, ALPROTO_HTTP1);
1,029✔
343
}
1,029✔
344

345
/**
346
 * \brief this function setup the http.header keyword used in the rule
347
 *
348
 * \param de_ctx   Pointer to the Detection Engine Context
349
 * \param s        Pointer to the Signature to which the current keyword belongs
350
 * \param str      Should hold an empty string always
351
 *
352
 * \retval 0       On success
353
 */
354
static int DetectHttpHeaderSetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str)
355
{
6,326✔
356
    if (SCDetectBufferSetActiveList(de_ctx, s, g_http_header_buffer_id) < 0)
6,326✔
357
        return -1;
22✔
358
    if (SCDetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0)
6,304✔
359
        return -1;
176✔
360
    return 0;
6,128✔
361
}
6,304✔
362

363
/**
364
 * \brief Registers the keyword handlers for the "http_header" keyword.
365
 */
366
void DetectHttpHeaderRegister(void)
367
{
2,223✔
368
    /* http_header content modifier */
369
    sigmatch_table[DETECT_HTTP_HEADER_CM].name = "http_header";
2,223✔
370
    sigmatch_table[DETECT_HTTP_HEADER_CM].desc =
2,223✔
371
            "content modifier to match only on the HTTP header-buffer";
2,223✔
372
    sigmatch_table[DETECT_HTTP_HEADER_CM].url =
2,223✔
373
            "/rules/http-keywords.html#http-header-and-http-raw-header";
2,223✔
374
    sigmatch_table[DETECT_HTTP_HEADER_CM].Setup = DetectHttpHeaderSetup;
2,223✔
375
#ifdef UNITTESTS
3✔
376
    sigmatch_table[DETECT_HTTP_HEADER_CM].RegisterTests = DetectHttpHeaderRegisterTests;
3✔
377
#endif
3✔
378
    sigmatch_table[DETECT_HTTP_HEADER_CM].flags |= SIGMATCH_NOOPT;
2,223✔
379
    sigmatch_table[DETECT_HTTP_HEADER_CM].flags |= SIGMATCH_INFO_CONTENT_MODIFIER;
2,223✔
380
    sigmatch_table[DETECT_HTTP_HEADER_CM].alternative = DETECT_HTTP_HEADER;
2,223✔
381

382
    /* http.header sticky buffer */
383
    sigmatch_table[DETECT_HTTP_HEADER].name = "http.header";
2,223✔
384
    sigmatch_table[DETECT_HTTP_HEADER].desc = "sticky buffer to match on the normalized HTTP header-buffer";
2,223✔
385
    sigmatch_table[DETECT_HTTP_HEADER].url = "/rules/http-keywords.html#http-header-and-http-raw-header";
2,223✔
386
    sigmatch_table[DETECT_HTTP_HEADER].Setup = DetectHttpHeaderSetupSticky;
2,223✔
387
    sigmatch_table[DETECT_HTTP_HEADER].flags |= SIGMATCH_NOOPT;
2,223✔
388
    sigmatch_table[DETECT_HTTP_HEADER].flags |= SIGMATCH_INFO_STICKY_BUFFER;
2,223✔
389

390
    DetectAppLayerInspectEngineRegister("http_header", ALPROTO_HTTP1, SIG_FLAG_TOSERVER,
2,223✔
391
            HTP_REQUEST_PROGRESS_HEADERS, DetectEngineInspectBufferGeneric, GetData1);
2,223✔
392
    DetectAppLayerMpmRegister("http_header", SIG_FLAG_TOSERVER, 2,
2,223✔
393
            PrefilterMpmHttpHeaderRequestRegister, NULL, ALPROTO_HTTP1,
2,223✔
394
            0); /* not used, registered twice: HEADERS/TRAILER */
2,223✔
395

396
    DetectAppLayerInspectEngineRegister("http_header", ALPROTO_HTTP1, SIG_FLAG_TOCLIENT,
2,223✔
397
            HTP_RESPONSE_PROGRESS_HEADERS, DetectEngineInspectBufferGeneric, GetData1);
2,223✔
398
    DetectAppLayerMpmRegister("http_header", SIG_FLAG_TOCLIENT, 2,
2,223✔
399
            PrefilterMpmHttpHeaderResponseRegister, NULL, ALPROTO_HTTP1,
2,223✔
400
            0); /* not used, registered twice: HEADERS/TRAILER */
2,223✔
401

402
    DetectAppLayerInspectEngineRegister("http_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
2,223✔
403
            HTTP2StateDataClient, DetectEngineInspectBufferGeneric, GetBuffer2ForTX);
2,223✔
404
    DetectAppLayerMpmRegister("http_header", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister,
2,223✔
405
            GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataClient);
2,223✔
406

407
    DetectAppLayerInspectEngineRegister("http_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT,
2,223✔
408
            HTTP2StateDataServer, DetectEngineInspectBufferGeneric, GetBuffer2ForTX);
2,223✔
409
    DetectAppLayerMpmRegister("http_header", SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister,
2,223✔
410
            GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataServer);
2,223✔
411

412
    DetectBufferTypeSetDescriptionByName("http_header",
2,223✔
413
            "http headers");
2,223✔
414

415
    g_http_header_buffer_id = DetectBufferTypeGetByName("http_header");
2,223✔
416

417
    g_keyword_thread_id = DetectRegisterThreadCtxGlobalFuncs("http_header",
2,223✔
418
            HttpHeaderThreadDataInit, &g_td_config, HttpHeaderThreadDataFree);
2,223✔
419
    g_http2_thread_id = DetectRegisterThreadCtxGlobalFuncs(
2,223✔
420
            "http2.header", SCHttp2ThreadBufDataInit, NULL, SCHttp2ThreadBufDataFree);
2,223✔
421
}
2,223✔
422

423
static int g_http_request_header_buffer_id = 0;
424
static int g_http_response_header_buffer_id = 0;
425
static int g_request_header_thread_id = 0;
426
static int g_response_header_thread_id = 0;
427
static int g_h2_request_header_thread_id = 0;
428
static int g_h2_response_header_thread_id = 0;
429

430
typedef struct HttpMultiBufItem {
431
    uint8_t *buffer;
432
    uint32_t len;
433
} HttpMultiBufItem;
434

435
typedef struct HttpMultiBufHeaderThreadData {
436
    // array of items, being defined as a buffer with its length just above
437
    HttpMultiBufItem *items;
438
    // capacity of items (size of allocation)
439
    size_t cap;
440
    // length of items (number in use)
441
    size_t len;
442
} HttpMultiBufHeaderThreadData;
443

444
static void *HttpMultiBufHeaderThreadDataInit(void *data)
445
{
96,962✔
446
    HttpMultiBufHeaderThreadData *td = SCCalloc(1, sizeof(*td));
96,962✔
447

448
    /* This return value check to satisfy our Cocci malloc checks. */
449
    if (td == NULL) {
96,962✔
450
        SCLogError("failed to allocate %" PRIuMAX " bytes: %s", (uintmax_t)sizeof(*td),
×
451
                strerror(errno));
×
452
        return NULL;
×
453
    }
×
454
    return td;
96,962✔
455
}
96,962✔
456

457
static void HttpMultiBufHeaderThreadDataFree(void *data)
458
{
96,960✔
459
    HttpMultiBufHeaderThreadData *td = data;
96,960✔
460
    for (size_t i = 0; i < td->cap; i++) {
97,288✔
461
        SCFree(td->items[i].buffer);
328✔
462
    }
328✔
463
    SCFree(td->items);
96,960✔
464
    SCFree(td);
96,960✔
465
}
96,960✔
466

467
static bool GetHttp2HeaderData(DetectEngineThreadCtx *det_ctx, const void *txv, const uint8_t flags,
468
        uint32_t local_id, const uint8_t **buf, uint32_t *buf_len)
469
{
1,536✔
470
    int kw_thread_id;
1,536✔
471
    if (flags & STREAM_TOSERVER) {
1,536✔
472
        kw_thread_id = g_h2_request_header_thread_id;
1,532✔
473
    } else {
1,532✔
474
        kw_thread_id = g_h2_response_header_thread_id;
4✔
475
    }
4✔
476
    void *hdr_td = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, kw_thread_id);
1,536✔
477
    if (unlikely(hdr_td == NULL)) {
1,536✔
478
        return false;
×
479
    }
×
480
    return SCHttp2TxGetHeader(hdr_td, txv, flags, local_id, buf, buf_len);
1,536✔
481
}
1,536✔
482

483
static bool GetHttp1HeaderData(DetectEngineThreadCtx *det_ctx, const void *txv, const uint8_t flags,
484
        uint32_t local_id, const uint8_t **buf, uint32_t *buf_len)
485
{
1,724✔
486
    SCEnter();
1,724✔
487

488
    int kw_thread_id;
1,724✔
489
    if (flags & STREAM_TOSERVER) {
1,724✔
490
        kw_thread_id = g_request_header_thread_id;
1,572✔
491
    } else {
1,583✔
492
        kw_thread_id = g_response_header_thread_id;
152✔
493
    }
152✔
494
    HttpMultiBufHeaderThreadData *hdr_td =
1,724✔
495
            DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, kw_thread_id);
1,724✔
496
    if (unlikely(hdr_td == NULL)) {
1,724✔
497
        return false;
×
498
    }
×
499

500
    htp_tx_t *tx = (htp_tx_t *)txv;
1,724✔
501
    const htp_headers_t *headers;
1,724✔
502
    if (flags & STREAM_TOSERVER) {
1,724✔
503
        headers = htp_tx_request_headers(tx);
1,572✔
504
    } else {
1,583✔
505
        headers = htp_tx_response_headers(tx);
152✔
506
    }
152✔
507
    size_t no_of_headers = htp_headers_size(headers);
1,724✔
508
    if (local_id == 0) {
1,724✔
509
        // We initialize a big buffer on first item
510
        // Then, we will just use parts of it
511
        hdr_td->len = 0;
360✔
512
        if (hdr_td->cap < no_of_headers) {
360✔
513
            void *new_buffer = SCRealloc(hdr_td->items, no_of_headers * sizeof(HttpMultiBufItem));
93✔
514
            if (unlikely(new_buffer == NULL)) {
93✔
515
                return NULL;
×
516
            }
×
517
            hdr_td->items = new_buffer;
93✔
518
            // zeroes the new part of the items
519
            memset(hdr_td->items + hdr_td->cap, 0,
93✔
520
                    (no_of_headers - hdr_td->cap) * sizeof(HttpMultiBufItem));
93✔
521
            hdr_td->cap = no_of_headers;
93✔
522
        }
93✔
523
        for (size_t i = 0; i < no_of_headers; i++) {
1,728✔
524
            const htp_header_t *h = htp_headers_get_index(headers, i);
1,368✔
525
            uint32_t size1 = (uint32_t)htp_header_name_len(h);
1,368✔
526
            uint32_t size2 = (uint32_t)htp_header_value_len(h);
1,368✔
527
            uint32_t size = size1 + size2 + 2;
1,368✔
528
            if (hdr_td->items[i].len < size) {
1,368✔
529
                // Use realloc, as this pointer is not freed until HttpMultiBufHeaderThreadDataFree
530
                void *tmp = SCRealloc(hdr_td->items[i].buffer, size);
481✔
531
                if (unlikely(tmp == NULL)) {
481✔
532
                    return NULL;
×
533
                }
×
534
                hdr_td->items[i].buffer = tmp;
481✔
535
            }
481✔
536
            memcpy(hdr_td->items[i].buffer, htp_header_name_ptr(h), size1);
1,368✔
537
            hdr_td->items[i].buffer[size1] = ':';
1,368✔
538
            hdr_td->items[i].buffer[size1 + 1] = ' ';
1,368✔
539
            memcpy(hdr_td->items[i].buffer + size1 + 2, htp_header_value_ptr(h), size2);
1,368✔
540
            hdr_td->items[i].len = size;
1,368✔
541
        }
1,368✔
542
        hdr_td->len = no_of_headers;
360✔
543
    }
360✔
544

545
    // cbdata->local_id is the index of the requested header buffer
546
    // hdr_td->len is the number of header buffers
547
    if (local_id < hdr_td->len) {
1,724✔
548
        // we have one valid header buffer
549
        *buf = hdr_td->items[local_id].buffer;
1,367✔
550
        *buf_len = hdr_td->items[local_id].len;
1,367✔
551
        return true;
1,367✔
552
    } // else there are no more header buffer to get
1,367✔
553
    return false;
357✔
554
}
1,724✔
555

556
static int DetectHTTPRequestHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
557
{
3,281✔
558
    if (SCDetectBufferSetActiveList(de_ctx, s, g_http_request_header_buffer_id) < 0)
3,281✔
559
        return -1;
30✔
560

561
    if (SCDetectSignatureSetAppProto(s, ALPROTO_HTTP) != 0)
3,251✔
562
        return -1;
19✔
563

564
    return 0;
3,232✔
565
}
3,251✔
566

567
void DetectHttpRequestHeaderRegister(void)
568
{
2,223✔
569
    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].name = "http.request_header";
2,223✔
570
    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].desc =
2,223✔
571
            "sticky buffer to match on only one HTTP header name and value";
2,223✔
572
    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].url = "/rules/http-keywords.html#request_header";
2,223✔
573
    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].Setup = DetectHTTPRequestHeaderSetup;
2,223✔
574
    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].flags |=
2,223✔
575
            SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER | SIGMATCH_INFO_MULTI_BUFFER;
2,223✔
576

577
    DetectAppLayerMultiRegister("http_request_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
2,223✔
578
            HTTP2StateOpen, GetHttp2HeaderData, 2);
2,223✔
579
    DetectAppLayerMultiRegister("http_request_header", ALPROTO_HTTP1, SIG_FLAG_TOSERVER,
2,223✔
580
            HTP_REQUEST_PROGRESS_HEADERS, GetHttp1HeaderData, 2);
2,223✔
581

582
    DetectBufferTypeSetDescriptionByName("http_request_header", "HTTP header name and value");
2,223✔
583
    g_http_request_header_buffer_id = DetectBufferTypeGetByName("http_request_header");
2,223✔
584
    DetectBufferTypeSupportsMultiInstance("http_request_header");
2,223✔
585
    g_request_header_thread_id = DetectRegisterThreadCtxGlobalFuncs("http_request_header",
2,223✔
586
            HttpMultiBufHeaderThreadDataInit, NULL, HttpMultiBufHeaderThreadDataFree);
2,223✔
587
    g_h2_request_header_thread_id = DetectRegisterThreadCtxGlobalFuncs("http2_request_header",
2,223✔
588
            SCHttp2ThreadMultiBufDataInit, NULL, SCHttp2ThreadMultiBufDataFree);
2,223✔
589
}
2,223✔
590

591
static int DetectHTTPResponseHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
592
{
209✔
593
    if (SCDetectBufferSetActiveList(de_ctx, s, g_http_response_header_buffer_id) < 0)
209✔
594
        return -1;
1✔
595

596
    if (SCDetectSignatureSetAppProto(s, ALPROTO_HTTP) != 0)
208✔
597
        return -1;
1✔
598

599
    return 0;
207✔
600
}
208✔
601

602
void DetectHttpResponseHeaderRegister(void)
603
{
2,223✔
604
    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].name = "http.response_header";
2,223✔
605
    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].desc =
2,223✔
606
            "sticky buffer to match on only one HTTP header name and value";
2,223✔
607
    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].url = "/rules/http2-keywords.html#response_header";
2,223✔
608
    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].Setup = DetectHTTPResponseHeaderSetup;
2,223✔
609
    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].flags |=
2,223✔
610
            SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER | SIGMATCH_INFO_MULTI_BUFFER;
2,223✔
611

612
    DetectAppLayerMultiRegister("http_response_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT,
2,223✔
613
            HTTP2StateOpen, GetHttp2HeaderData, 2);
2,223✔
614
    DetectAppLayerMultiRegister("http_response_header", ALPROTO_HTTP1, SIG_FLAG_TOCLIENT,
2,223✔
615
            HTP_RESPONSE_PROGRESS_HEADERS, GetHttp1HeaderData, 2);
2,223✔
616

617
    DetectBufferTypeSetDescriptionByName("http_response_header", "HTTP header name and value");
2,223✔
618
    g_http_response_header_buffer_id = DetectBufferTypeGetByName("http_response_header");
2,223✔
619
    DetectBufferTypeSupportsMultiInstance("http_response_header");
2,223✔
620
    g_response_header_thread_id = DetectRegisterThreadCtxGlobalFuncs("http_response_header",
2,223✔
621
            HttpMultiBufHeaderThreadDataInit, NULL, HttpMultiBufHeaderThreadDataFree);
2,223✔
622
    g_h2_response_header_thread_id = DetectRegisterThreadCtxGlobalFuncs("http2_response_header",
2,223✔
623
            SCHttp2ThreadMultiBufDataInit, NULL, SCHttp2ThreadMultiBufDataFree);
624
}
2,223✔
625

626
/************************************Unittests*********************************/
627

628
#ifdef UNITTESTS
629
#include "tests/detect-http-header.c"
630
#endif
631

632
/**
633
 * @}
634
 */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc