Coverage Report

Created: 2025-07-23 07:29

/src/suricata7/src/detect-http-method.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2007-2019 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 Brian Rectanus <brectanu@gmail.com>
29
 *
30
 * Implements the http_method keyword
31
 */
32
33
#include "suricata-common.h"
34
#include "threads.h"
35
#include "decode.h"
36
#include "detect.h"
37
38
#include "detect-parse.h"
39
#include "detect-engine.h"
40
#include "detect-engine-mpm.h"
41
#include "detect-engine-prefilter.h"
42
#include "detect-content.h"
43
#include "detect-pcre.h"
44
45
#include "flow.h"
46
#include "flow-var.h"
47
#include "flow-util.h"
48
49
#include "util-debug.h"
50
#include "util-unittest.h"
51
#include "util-unittest-helper.h"
52
#include "util-spm.h"
53
54
#include "app-layer.h"
55
#include "app-layer-parser.h"
56
57
#include "app-layer-htp.h"
58
#include "detect-http-method.h"
59
#include "stream-tcp.h"
60
61
static int g_http_method_buffer_id = 0;
62
static int DetectHttpMethodSetup(DetectEngineCtx *, Signature *, const char *);
63
static int DetectHttpMethodSetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str);
64
#ifdef UNITTESTS
65
void DetectHttpMethodRegisterTests(void);
66
#endif
67
void DetectHttpMethodFree(void *);
68
static bool DetectHttpMethodValidateCallback(const Signature *s, const char **sigerror);
69
static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
70
        const DetectEngineTransforms *transforms, Flow *_f,
71
        const uint8_t _flow_flags, void *txv, const int list_id);
72
static InspectionBuffer *GetData2(DetectEngineThreadCtx *det_ctx,
73
        const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv,
74
        const int list_id);
75
76
/**
77
 * \brief Registration function for keyword: http_method
78
 */
79
void DetectHttpMethodRegister(void)
80
73
{
81
    /* http_method content modifier */
82
73
    sigmatch_table[DETECT_AL_HTTP_METHOD].name = "http_method";
83
73
    sigmatch_table[DETECT_AL_HTTP_METHOD].desc = "content modifier to match only on the HTTP method-buffer";
84
73
    sigmatch_table[DETECT_AL_HTTP_METHOD].url = "/rules/http-keywords.html#http-method";
85
73
    sigmatch_table[DETECT_AL_HTTP_METHOD].Match = NULL;
86
73
    sigmatch_table[DETECT_AL_HTTP_METHOD].Setup = DetectHttpMethodSetup;
87
#ifdef UNITTESTS
88
    sigmatch_table[DETECT_AL_HTTP_METHOD].RegisterTests = DetectHttpMethodRegisterTests;
89
#endif
90
73
    sigmatch_table[DETECT_AL_HTTP_METHOD].flags |= SIGMATCH_NOOPT|SIGMATCH_INFO_CONTENT_MODIFIER;
91
73
    sigmatch_table[DETECT_AL_HTTP_METHOD].alternative = DETECT_HTTP_METHOD;
92
93
    /* http.method sticky buffer */
94
73
    sigmatch_table[DETECT_HTTP_METHOD].name = "http.method";
95
73
    sigmatch_table[DETECT_HTTP_METHOD].desc = "sticky buffer to match specifically and only on the HTTP method buffer";
96
73
    sigmatch_table[DETECT_HTTP_METHOD].url = "/rules/http-keywords.html#http-method";
97
73
    sigmatch_table[DETECT_HTTP_METHOD].Setup = DetectHttpMethodSetupSticky;
98
73
    sigmatch_table[DETECT_HTTP_METHOD].flags |= SIGMATCH_NOOPT|SIGMATCH_INFO_STICKY_BUFFER;
99
100
73
    DetectAppLayerInspectEngineRegister2("http_method", ALPROTO_HTTP1, SIG_FLAG_TOSERVER,
101
73
            HTP_REQUEST_LINE, DetectEngineInspectBufferGeneric, GetData);
102
103
73
    DetectAppLayerMpmRegister2("http_method", SIG_FLAG_TOSERVER, 4, PrefilterGenericMpmRegister,
104
73
            GetData, ALPROTO_HTTP1, HTP_REQUEST_LINE);
105
106
73
    DetectAppLayerInspectEngineRegister2("http_method", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
107
73
            HTTP2StateDataClient, DetectEngineInspectBufferGeneric, GetData2);
108
109
73
    DetectAppLayerMpmRegister2("http_method", SIG_FLAG_TOSERVER, 4, PrefilterGenericMpmRegister,
110
73
            GetData2, ALPROTO_HTTP2, HTTP2StateDataClient);
111
112
73
    DetectBufferTypeSetDescriptionByName("http_method",
113
73
            "http request method");
114
115
73
    DetectBufferTypeRegisterValidateCallback("http_method",
116
73
            DetectHttpMethodValidateCallback);
117
118
73
    g_http_method_buffer_id = DetectBufferTypeGetByName("http_method");
119
120
73
    SCLogDebug("registering http_method rule option");
121
73
}
122
123
/**
124
 * \brief This function is used to add the parsed "http_method" option
125
 *        into the current signature.
126
 *
127
 * \param de_ctx Pointer to the Detection Engine Context.
128
 * \param s      Pointer to the Current Signature.
129
 * \param str    Pointer to the user provided option string.
130
 *
131
 * \retval  0 on Success.
132
 * \retval -1 on Failure.
133
 */
134
static int DetectHttpMethodSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
135
2.04k
{
136
2.04k
    return DetectEngineContentModifierBufferSetup(
137
2.04k
            de_ctx, s, str, DETECT_AL_HTTP_METHOD, g_http_method_buffer_id, ALPROTO_HTTP1);
138
2.04k
}
139
140
/**
141
 * \brief this function setup the http.method keyword used in the rule
142
 *
143
 * \param de_ctx   Pointer to the Detection Engine Context
144
 * \param s        Pointer to the Signature to which the current keyword belongs
145
 * \param str      Should hold an empty string always
146
 *
147
 * \retval 0       On success
148
 */
149
static int DetectHttpMethodSetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str)
150
22.3k
{
151
22.3k
    if (DetectBufferSetActiveList(de_ctx, s, g_http_method_buffer_id) < 0)
152
14
        return -1;
153
154
22.2k
    if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0)
155
99
        return -1;
156
157
22.1k
    return 0;
158
22.2k
}
159
160
/**
161
 *  \retval 1 valid
162
 *  \retval 0 invalid
163
 */
164
static bool DetectHttpMethodValidateCallback(const Signature *s, const char **sigerror)
165
101k
{
166
205k
    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
167
104k
        if (s->init_data->buffers[x].id != (uint32_t)g_http_method_buffer_id)
168
3.44k
            continue;
169
101k
        const SigMatch *sm = s->init_data->buffers[x].head;
170
207k
        for (; sm != NULL; sm = sm->next) {
171
106k
            if (sm->type != DETECT_CONTENT)
172
87.5k
                continue;
173
19.4k
            const DetectContentData *cd = (const DetectContentData *)sm->ctx;
174
19.4k
            if (cd->content && cd->content_len) {
175
19.4k
                if (cd->content[cd->content_len - 1] == 0x20) {
176
482
                    *sigerror = "http_method pattern with trailing space";
177
482
                    SCLogError("%s", *sigerror);
178
482
                    return false;
179
18.9k
                } else if (cd->content[0] == 0x20) {
180
102
                    *sigerror = "http_method pattern with leading space";
181
102
                    SCLogError("%s", *sigerror);
182
102
                    return false;
183
18.8k
                } else if (cd->content[cd->content_len - 1] == 0x09) {
184
30
                    *sigerror = "http_method pattern with trailing tab";
185
30
                    SCLogError("%s", *sigerror);
186
30
                    return false;
187
18.8k
                } else if (cd->content[0] == 0x09) {
188
210
                    *sigerror = "http_method pattern with leading tab";
189
210
                    SCLogError("%s", *sigerror);
190
210
                    return false;
191
210
                }
192
19.4k
            }
193
19.4k
        }
194
101k
    }
195
100k
    return true;
196
101k
}
197
198
static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
199
        const DetectEngineTransforms *transforms, Flow *_f,
200
        const uint8_t _flow_flags, void *txv, const int list_id)
201
835
{
202
835
    InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
203
835
    if (buffer->inspect == NULL) {
204
506
        htp_tx_t *tx = (htp_tx_t *)txv;
205
206
506
        if (tx->request_method == NULL)
207
117
            return NULL;
208
209
389
        const uint32_t data_len = bstr_len(tx->request_method);
210
389
        const uint8_t *data = bstr_ptr(tx->request_method);
211
212
389
        InspectionBufferSetup(det_ctx, list_id, buffer, data, data_len);
213
389
        InspectionBufferApplyTransforms(buffer, transforms);
214
389
    }
215
216
718
    return buffer;
217
835
}
218
219
static InspectionBuffer *GetData2(DetectEngineThreadCtx *det_ctx,
220
        const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv,
221
        const int list_id)
222
899
{
223
899
    InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
224
899
    if (buffer->inspect == NULL) {
225
852
        uint32_t b_len = 0;
226
852
        const uint8_t *b = NULL;
227
228
852
        if (rs_http2_tx_get_method(txv, &b, &b_len) != 1)
229
552
            return NULL;
230
300
        if (b == NULL || b_len == 0)
231
0
            return NULL;
232
233
300
        InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len);
234
300
        InspectionBufferApplyTransforms(buffer, transforms);
235
300
    }
236
237
347
    return buffer;
238
899
}
239
240
#ifdef UNITTESTS
241
#include "tests/detect-http-method.c"
242
#endif
243
244
/**
245
 * @}
246
 */