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

OISF / suricata / 22550902417

01 Mar 2026 07:32PM UTC coverage: 68.401% (-5.3%) from 73.687%
22550902417

Pull #14922

github

web-flow
github-actions: bump actions/upload-artifact from 6.0.0 to 7.0.0

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #14922: github-actions: bump actions/upload-artifact from 6.0.0 to 7.0.0

218243 of 319063 relevant lines covered (68.4%)

3284926.58 hits per line

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

71.06
/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

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

71
static uint8_t *GetBufferForTX(
72
        htp_tx_t *tx, DetectEngineThreadCtx *det_ctx, uint8_t flags, uint32_t *buffer_len)
73
{
4,875✔
74
    *buffer_len = 0;
4,875✔
75

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

82
    const htp_headers_t *headers;
4,875✔
83
    if (flags & STREAM_TOSERVER) {
4,875✔
84
        if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, tx, flags) <=
2,547✔
85
                HTP_REQUEST_PROGRESS_HEADERS)
2,547✔
86
            return NULL;
283✔
87
        headers = htp_tx_request_headers(tx);
2,264✔
88
    } else {
2,379✔
89
        if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, tx, flags) <=
2,328✔
90
                HTP_RESPONSE_PROGRESS_HEADERS)
2,328✔
91
            return NULL;
28✔
92
        headers = htp_tx_response_headers(tx);
2,300✔
93
    }
2,300✔
94
    if (headers == NULL)
4,564✔
95
        return NULL;
×
96

97
    size_t i = 0;
4,564✔
98
    size_t no_of_headers = htp_headers_size(headers);
4,564✔
99
    for (; i < no_of_headers; i++) {
35,122✔
100
        const htp_header_t *h = htp_headers_get_index(headers, i);
30,558✔
101
        size_t size1 = htp_header_name_len(h);
30,558✔
102
        size_t size2 = htp_header_value_len(h);
30,558✔
103

104
        if (flags & STREAM_TOSERVER) {
30,558✔
105
            if (size1 == 6 && SCMemcmpLowercase("cookie", htp_header_name_ptr(h), 6) == 0) {
12,338✔
106
                continue;
1,038✔
107
            }
1,038✔
108
        } else {
18,332✔
109
            if (size1 == 10 && SCMemcmpLowercase("set-cookie", htp_header_name_ptr(h), 10) == 0) {
18,220✔
110
                continue;
162✔
111
            }
162✔
112
        }
18,220✔
113

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

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

141
    *buffer_len = buf->len;
4,564✔
142
    return buf->buffer;
4,564✔
143
}
4,564✔
144

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

154
        if (SCHttp2TxGetHeaders(txv, flow_flags, &b, &b_len) != 1)
356✔
155
            return NULL;
198✔
156
        if (b == NULL || b_len == 0)
158✔
157
            return NULL;
×
158

159
        InspectionBufferSetupAndApplyTransforms(det_ctx, list_id, buffer, b, b_len, transforms);
158✔
160
    }
158✔
161

162
    return buffer;
166✔
163
}
364✔
164

165
static InspectionBuffer *GetData1(DetectEngineThreadCtx *det_ctx,
166
        const DetectEngineTransforms *transforms, Flow *_f, const uint8_t flags, void *txv,
167
        const int list_id)
168
{
522✔
169
    InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
522✔
170
    if (buffer->inspect == NULL) {
522✔
171
        uint32_t data_len = 0;
12✔
172
        uint8_t *data = GetBufferForTX(txv, det_ctx, flags, &data_len);
12✔
173
        InspectionBufferSetupAndApplyTransforms(
12✔
174
                det_ctx, list_id, buffer, data, data_len, transforms);
12✔
175
    }
12✔
176

177
    return buffer;
522✔
178
}
522✔
179

180
typedef struct PrefilterMpmHttpHeaderCtx {
181
    int list_id;
182
    const MpmCtx *mpm_ctx;
183
    const DetectEngineTransforms *transforms;
184
} PrefilterMpmHttpHeaderCtx;
185

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

199
    const PrefilterMpmHttpHeaderCtx *ctx = pectx;
5,097✔
200
    const MpmCtx *mpm_ctx = ctx->mpm_ctx;
5,097✔
201
    SCLogDebug("running on list %d", ctx->list_id);
5,097✔
202

203
    const int list_id = ctx->list_id;
5,097✔
204
    InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
5,097✔
205
    if (buffer->inspect == NULL) {
5,097✔
206
        uint32_t rawdata_len = 0;
4,864✔
207
        uint8_t *rawdata = GetBufferForTX(txv, det_ctx, flags, &rawdata_len);
4,864✔
208
        if (rawdata_len == 0)
4,864✔
209
            return;
536✔
210

211
        /* setup buffer and apply transforms */
212
        InspectionBufferSetupAndApplyTransforms(
4,328✔
213
                det_ctx, list_id, buffer, rawdata, rawdata_len, ctx->transforms);
4,328✔
214
    }
4,328✔
215

216
    const uint32_t data_len = buffer->inspect_len;
4,561✔
217
    const uint8_t *data = buffer->inspect;
4,561✔
218

219
    SCLogDebug("mpm'ing buffer:");
4,561✔
220
    //PrintRawDataFp(stdout, data, data_len);
221

222
    if (data != NULL && data_len >= mpm_ctx->minlen) {
4,563✔
223
        (void)mpm_table[mpm_ctx->mpm_type].Search(
4,563✔
224
                mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, data, data_len);
4,563✔
225
        PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len);
4,563✔
226
    }
4,563✔
227
}
4,561✔
228

229
static void PrefilterMpmHttpTrailer(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
230
        Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags)
231
{
4,458✔
232
    SCEnter();
4,458✔
233

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

245
static void PrefilterMpmHttpHeaderFree(void *ptr)
246
{
320✔
247
    SCFree(ptr);
320✔
248
}
320✔
249

250
static int PrefilterMpmHttpHeaderRequestRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
251
        MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id)
252
{
76✔
253
    SCEnter();
76✔
254

255
    /* header */
256
    PrefilterMpmHttpHeaderCtx *pectx = SCCalloc(1, sizeof(*pectx));
76✔
257
    if (pectx == NULL)
76✔
258
        return -1;
×
259
    pectx->list_id = list_id;
76✔
260
    pectx->mpm_ctx = mpm_ctx;
76✔
261
    pectx->transforms = &mpm_reg->transforms;
76✔
262

263
    int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpHeader, mpm_reg->app_v2.alproto,
76✔
264
            HTP_REQUEST_PROGRESS_HEADERS, pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
76✔
265
    if (r != 0) {
76✔
266
        SCFree(pectx);
×
267
        return r;
×
268
    }
×
269

270
    /* trailer */
271
    pectx = SCCalloc(1, sizeof(*pectx));
76✔
272
    if (pectx == NULL)
76✔
273
        return -1;
×
274
    pectx->list_id = list_id;
76✔
275
    pectx->mpm_ctx = mpm_ctx;
76✔
276
    pectx->transforms = &mpm_reg->transforms;
76✔
277

278
    r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpTrailer, mpm_reg->app_v2.alproto,
76✔
279
            HTP_REQUEST_PROGRESS_TRAILER, pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
76✔
280
    if (r != 0) {
76✔
281
        SCFree(pectx);
×
282
    }
×
283
    return r;
76✔
284
}
76✔
285

286
static int PrefilterMpmHttpHeaderResponseRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
287
        MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id)
288
{
55✔
289
    SCEnter();
55✔
290

291
    /* header */
292
    PrefilterMpmHttpHeaderCtx *pectx = SCCalloc(1, sizeof(*pectx));
55✔
293
    if (pectx == NULL)
55✔
294
        return -1;
×
295
    pectx->list_id = list_id;
55✔
296
    pectx->mpm_ctx = mpm_ctx;
55✔
297
    pectx->transforms = &mpm_reg->transforms;
55✔
298

299
    int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpHeader, mpm_reg->app_v2.alproto,
55✔
300
            HTP_RESPONSE_PROGRESS_HEADERS, pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
55✔
301
    if (r != 0) {
55✔
302
        SCFree(pectx);
×
303
        return r;
×
304
    }
×
305

306
    /* trailer */
307
    pectx = SCCalloc(1, sizeof(*pectx));
55✔
308
    if (pectx == NULL)
55✔
309
        return -1;
×
310
    pectx->list_id = list_id;
55✔
311
    pectx->mpm_ctx = mpm_ctx;
55✔
312
    pectx->transforms = &mpm_reg->transforms;
55✔
313

314
    r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpTrailer, mpm_reg->app_v2.alproto,
55✔
315
            HTP_RESPONSE_PROGRESS_TRAILER, pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
55✔
316
    if (r != 0) {
55✔
317
        SCFree(pectx);
×
318
    }
×
319
    return r;
55✔
320
}
55✔
321

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

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

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

378
    /* http.header sticky buffer */
379
    sigmatch_table[DETECT_HTTP_HEADER].name = "http.header";
37✔
380
    sigmatch_table[DETECT_HTTP_HEADER].desc = "sticky buffer to match on the normalized HTTP header-buffer";
37✔
381
    sigmatch_table[DETECT_HTTP_HEADER].url = "/rules/http-keywords.html#http-header-and-http-raw-header";
37✔
382
    sigmatch_table[DETECT_HTTP_HEADER].Setup = DetectHttpHeaderSetupSticky;
37✔
383
    sigmatch_table[DETECT_HTTP_HEADER].flags |= SIGMATCH_NOOPT;
37✔
384
    sigmatch_table[DETECT_HTTP_HEADER].flags |= SIGMATCH_INFO_STICKY_BUFFER;
37✔
385

386
    DetectAppLayerInspectEngineRegister("http_header", ALPROTO_HTTP1, SIG_FLAG_TOSERVER,
37✔
387
            HTP_REQUEST_PROGRESS_HEADERS, DetectEngineInspectBufferGeneric, GetData1);
37✔
388
    DetectAppLayerMpmRegister("http_header", SIG_FLAG_TOSERVER, 2,
37✔
389
            PrefilterMpmHttpHeaderRequestRegister, NULL, ALPROTO_HTTP1,
37✔
390
            0); /* not used, registered twice: HEADERS/TRAILER */
37✔
391

392
    DetectAppLayerInspectEngineRegister("http_header", ALPROTO_HTTP1, SIG_FLAG_TOCLIENT,
37✔
393
            HTP_RESPONSE_PROGRESS_HEADERS, DetectEngineInspectBufferGeneric, GetData1);
37✔
394
    DetectAppLayerMpmRegister("http_header", SIG_FLAG_TOCLIENT, 2,
37✔
395
            PrefilterMpmHttpHeaderResponseRegister, NULL, ALPROTO_HTTP1,
37✔
396
            0); /* not used, registered twice: HEADERS/TRAILER */
37✔
397

398
    DetectAppLayerInspectEngineRegister("http_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
37✔
399
            HTTP2StateDataClient, DetectEngineInspectBufferGeneric, GetBuffer2ForTX);
37✔
400
    DetectAppLayerMpmRegister("http_header", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister,
37✔
401
            GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataClient);
37✔
402

403
    DetectAppLayerInspectEngineRegister("http_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT,
37✔
404
            HTTP2StateDataServer, DetectEngineInspectBufferGeneric, GetBuffer2ForTX);
37✔
405
    DetectAppLayerMpmRegister("http_header", SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister,
37✔
406
            GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataServer);
37✔
407

408
    DetectBufferTypeSetDescriptionByName("http_header",
37✔
409
            "http headers");
37✔
410

411
    g_http_header_buffer_id = DetectBufferTypeGetByName("http_header");
37✔
412

413
    g_keyword_thread_id = DetectRegisterThreadCtxGlobalFuncs("http_header",
37✔
414
            HttpHeaderThreadDataInit, &g_td_config, HttpHeaderThreadDataFree);
37✔
415
}
37✔
416

417
static int g_http_request_header_buffer_id = 0;
418
static int g_http_response_header_buffer_id = 0;
419
static int g_request_header_thread_id = 0;
420
static int g_response_header_thread_id = 0;
421

422
typedef struct HttpMultiBufItem {
423
    uint8_t *buffer;
424
    uint32_t len;
425
} HttpMultiBufItem;
426

427
typedef struct HttpMultiBufHeaderThreadData {
428
    // array of items, being defined as a buffer with its length just above
429
    HttpMultiBufItem *items;
430
    // capacity of items (size of allocation)
431
    size_t cap;
432
    // length of items (number in use)
433
    size_t len;
434
} HttpMultiBufHeaderThreadData;
435

436
static void *HttpMultiBufHeaderThreadDataInit(void *data)
437
{
13,136✔
438
    HttpMultiBufHeaderThreadData *td = SCCalloc(1, sizeof(*td));
13,136✔
439

440
    /* This return value check to satisfy our Cocci malloc checks. */
441
    if (td == NULL) {
13,136✔
442
        SCLogError("failed to allocate %" PRIuMAX " bytes: %s", (uintmax_t)sizeof(*td),
×
443
                strerror(errno));
×
444
        return NULL;
×
445
    }
×
446
    return td;
13,136✔
447
}
13,136✔
448

449
static void HttpMultiBufHeaderThreadDataFree(void *data)
450
{
13,136✔
451
    HttpMultiBufHeaderThreadData *td = data;
13,136✔
452
    for (size_t i = 0; i < td->cap; i++) {
13,136✔
453
        SCFree(td->items[i].buffer);
×
454
    }
×
455
    SCFree(td->items);
13,136✔
456
    SCFree(td);
13,136✔
457
}
13,136✔
458

459
static bool GetHttp1HeaderData(DetectEngineThreadCtx *det_ctx, const void *txv, const uint8_t flags,
460
        uint32_t local_id, const uint8_t **buf, uint32_t *buf_len)
461
{
×
462
    SCEnter();
×
463

464
    int kw_thread_id;
×
465
    if (flags & STREAM_TOSERVER) {
×
466
        kw_thread_id = g_request_header_thread_id;
×
467
    } else {
×
468
        kw_thread_id = g_response_header_thread_id;
×
469
    }
×
470
    HttpMultiBufHeaderThreadData *hdr_td =
×
471
            DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, kw_thread_id);
×
472
    if (unlikely(hdr_td == NULL)) {
×
473
        return false;
×
474
    }
×
475

476
    htp_tx_t *tx = (htp_tx_t *)txv;
×
477
    const htp_headers_t *headers;
×
478
    if (flags & STREAM_TOSERVER) {
×
479
        headers = htp_tx_request_headers(tx);
×
480
    } else {
×
481
        headers = htp_tx_response_headers(tx);
×
482
    }
×
483
    size_t no_of_headers = htp_headers_size(headers);
×
484
    if (local_id == 0) {
×
485
        // We initialize a big buffer on first item
486
        // Then, we will just use parts of it
487
        hdr_td->len = 0;
×
488
        if (hdr_td->cap < no_of_headers) {
×
489
            void *new_buffer = SCRealloc(hdr_td->items, no_of_headers * sizeof(HttpMultiBufItem));
×
490
            if (unlikely(new_buffer == NULL)) {
×
491
                return NULL;
×
492
            }
×
493
            hdr_td->items = new_buffer;
×
494
            // zeroes the new part of the items
495
            memset(hdr_td->items + hdr_td->cap, 0,
×
496
                    (no_of_headers - hdr_td->cap) * sizeof(HttpMultiBufItem));
×
497
            hdr_td->cap = no_of_headers;
×
498
        }
×
499
        for (size_t i = 0; i < no_of_headers; i++) {
×
500
            const htp_header_t *h = htp_headers_get_index(headers, i);
×
501
            uint32_t size1 = (uint32_t)htp_header_name_len(h);
×
502
            uint32_t size2 = (uint32_t)htp_header_value_len(h);
×
503
            uint32_t size = size1 + size2 + 2;
×
504
            if (hdr_td->items[i].len < size) {
×
505
                // Use realloc, as this pointer is not freed until HttpMultiBufHeaderThreadDataFree
506
                void *tmp = SCRealloc(hdr_td->items[i].buffer, size);
×
507
                if (unlikely(tmp == NULL)) {
×
508
                    return NULL;
×
509
                }
×
510
                hdr_td->items[i].buffer = tmp;
×
511
            }
×
512
            memcpy(hdr_td->items[i].buffer, htp_header_name_ptr(h), size1);
×
513
            hdr_td->items[i].buffer[size1] = ':';
×
514
            hdr_td->items[i].buffer[size1 + 1] = ' ';
×
515
            memcpy(hdr_td->items[i].buffer + size1 + 2, htp_header_value_ptr(h), size2);
×
516
            hdr_td->items[i].len = size;
×
517
        }
×
518
        hdr_td->len = no_of_headers;
×
519
    }
×
520

521
    // cbdata->local_id is the index of the requested header buffer
522
    // hdr_td->len is the number of header buffers
523
    if (local_id < hdr_td->len) {
×
524
        // we have one valid header buffer
525
        *buf = hdr_td->items[local_id].buffer;
×
526
        *buf_len = hdr_td->items[local_id].len;
×
527
        return true;
×
528
    } // else there are no more header buffer to get
×
529
    return false;
×
530
}
×
531

532
static int DetectHTTPRequestHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
533
{
×
534
    if (SCDetectBufferSetActiveList(de_ctx, s, g_http_request_header_buffer_id) < 0)
×
535
        return -1;
×
536

537
    if (SCDetectSignatureSetAppProto(s, ALPROTO_HTTP) != 0)
×
538
        return -1;
×
539

540
    return 0;
×
541
}
×
542

543
void DetectHttpRequestHeaderRegister(void)
544
{
37✔
545
    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].name = "http.request_header";
37✔
546
    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].desc =
37✔
547
            "sticky buffer to match on only one HTTP header name and value";
37✔
548
    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].url = "/rules/http-keywords.html#request_header";
37✔
549
    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].Setup = DetectHTTPRequestHeaderSetup;
37✔
550
    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].flags |=
37✔
551
            SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER | SIGMATCH_INFO_MULTI_BUFFER;
37✔
552

553
    DetectAppLayerMultiRegister("http_request_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
37✔
554
            HTTP2StateOpen, SCHttp2TxGetHeader, 2);
37✔
555
    DetectAppLayerMultiRegister("http_request_header", ALPROTO_HTTP1, SIG_FLAG_TOSERVER,
37✔
556
            HTP_REQUEST_PROGRESS_HEADERS, GetHttp1HeaderData, 2);
37✔
557

558
    DetectBufferTypeSetDescriptionByName("http_request_header", "HTTP header name and value");
37✔
559
    g_http_request_header_buffer_id = DetectBufferTypeGetByName("http_request_header");
37✔
560
    DetectBufferTypeSupportsMultiInstance("http_request_header");
37✔
561
    g_request_header_thread_id = DetectRegisterThreadCtxGlobalFuncs("http_request_header",
37✔
562
            HttpMultiBufHeaderThreadDataInit, NULL, HttpMultiBufHeaderThreadDataFree);
37✔
563
}
37✔
564

565
static int DetectHTTPResponseHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
566
{
×
567
    if (SCDetectBufferSetActiveList(de_ctx, s, g_http_response_header_buffer_id) < 0)
×
568
        return -1;
×
569

570
    if (SCDetectSignatureSetAppProto(s, ALPROTO_HTTP) != 0)
×
571
        return -1;
×
572

573
    return 0;
×
574
}
×
575

576
void DetectHttpResponseHeaderRegister(void)
577
{
37✔
578
    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].name = "http.response_header";
37✔
579
    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].desc =
37✔
580
            "sticky buffer to match on only one HTTP header name and value";
37✔
581
    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].url = "/rules/http2-keywords.html#response_header";
37✔
582
    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].Setup = DetectHTTPResponseHeaderSetup;
37✔
583
    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].flags |=
37✔
584
            SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER | SIGMATCH_INFO_MULTI_BUFFER;
37✔
585

586
    DetectAppLayerMultiRegister("http_response_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT,
37✔
587
            HTTP2StateOpen, SCHttp2TxGetHeader, 2);
37✔
588
    DetectAppLayerMultiRegister("http_response_header", ALPROTO_HTTP1, SIG_FLAG_TOCLIENT,
37✔
589
            HTP_RESPONSE_PROGRESS_HEADERS, GetHttp1HeaderData, 2);
37✔
590

591
    DetectBufferTypeSetDescriptionByName("http_response_header", "HTTP header name and value");
37✔
592
    g_http_response_header_buffer_id = DetectBufferTypeGetByName("http_response_header");
37✔
593
    DetectBufferTypeSupportsMultiInstance("http_response_header");
37✔
594
    g_response_header_thread_id = DetectRegisterThreadCtxGlobalFuncs("http_response_header",
37✔
595
            HttpMultiBufHeaderThreadDataInit, NULL, HttpMultiBufHeaderThreadDataFree);
596
}
37✔
597

598
/************************************Unittests*********************************/
599

600
#ifdef UNITTESTS
601
#include "tests/detect-http-header.c"
602
#endif
603

604
/**
605
 * @}
606
 */
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