/src/suricata7/src/detect-http-header-names.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2017 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 Victor Julien <victor@inliniac.net> |
29 | | * |
30 | | * Implements support http_header_names |
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-mpm.h" |
41 | | #include "detect-engine-state.h" |
42 | | #include "detect-engine-prefilter.h" |
43 | | #include "detect-engine-content-inspection.h" |
44 | | #include "detect-content.h" |
45 | | #include "detect-pcre.h" |
46 | | #include "detect-http-header-common.h" |
47 | | #include "detect-http-header-names.h" |
48 | | |
49 | | #include "flow.h" |
50 | | #include "flow-var.h" |
51 | | #include "flow-util.h" |
52 | | |
53 | | #include "util-debug.h" |
54 | | #include "util-unittest.h" |
55 | | #include "util-unittest-helper.h" |
56 | | #include "util-spm.h" |
57 | | #include "util-print.h" |
58 | | |
59 | | #include "app-layer.h" |
60 | | #include "app-layer-parser.h" |
61 | | |
62 | | #include "app-layer-htp.h" |
63 | | #include "detect-http-header.h" |
64 | | #include "stream-tcp.h" |
65 | | |
66 | 146 | #define KEYWORD_NAME "http.header_names" |
67 | 73 | #define KEYWORD_NAME_LEGACY "http_header_names" |
68 | 73 | #define KEYWORD_DOC "http-keywords.html#http-header-names" |
69 | 803 | #define BUFFER_NAME "http_header_names" |
70 | 73 | #define BUFFER_DESC "http header names" |
71 | | static int g_buffer_id = 0; |
72 | | static int g_keyword_thread_id = 0; |
73 | | |
74 | | #define BUFFER_SIZE_STEP 256 |
75 | | static HttpHeaderThreadDataConfig g_td_config = { BUFFER_SIZE_STEP }; |
76 | | |
77 | | static uint8_t *GetBufferForTX( |
78 | | htp_tx_t *tx, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, uint32_t *buffer_len) |
79 | 21.9k | { |
80 | 21.9k | *buffer_len = 0; |
81 | | |
82 | 21.9k | HttpHeaderThreadData *hdr_td = NULL; |
83 | 21.9k | HttpHeaderBuffer *buf = |
84 | 21.9k | HttpHeaderGetBufferSpace(det_ctx, f, flags, g_keyword_thread_id, &hdr_td); |
85 | 21.9k | if (unlikely(buf == NULL)) { |
86 | 0 | return NULL; |
87 | 0 | } |
88 | | |
89 | 21.9k | htp_table_t *headers; |
90 | 21.9k | if (flags & STREAM_TOSERVER) { |
91 | 11.8k | if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, tx, flags) <= |
92 | 11.8k | HTP_REQUEST_HEADERS) |
93 | 10.4k | return NULL; |
94 | 1.37k | headers = tx->request_headers; |
95 | 10.0k | } else { |
96 | 10.0k | if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, tx, flags) <= |
97 | 10.0k | HTP_RESPONSE_HEADERS) |
98 | 9.25k | return NULL; |
99 | 801 | headers = tx->response_headers; |
100 | 801 | } |
101 | 2.17k | if (headers == NULL) |
102 | 0 | return NULL; |
103 | | |
104 | | /* fill the buffer. \r\nName1\r\nName2\r\n\r\n */ |
105 | 2.17k | size_t i = 0; |
106 | 2.17k | size_t no_of_headers = htp_table_size(headers); |
107 | 5.86k | for (; i < no_of_headers; i++) { |
108 | 3.68k | htp_header_t *h = htp_table_get_index(headers, i, NULL); |
109 | 3.68k | size_t size = bstr_size(h->name) + 2; // for \r\n |
110 | 3.68k | if (i == 0) |
111 | 1.23k | size += 2; |
112 | 3.68k | if (i + 1 == no_of_headers) |
113 | 1.23k | size += 2; |
114 | | |
115 | 3.68k | SCLogDebug("size %"PRIuMAX" + buf->len %u vs buf->size %u", |
116 | 3.68k | (uintmax_t)size, buf->len, buf->size); |
117 | 3.68k | if (size + buf->len > buf->size) { |
118 | 63 | if (HttpHeaderExpandBuffer(hdr_td, buf, size) != 0) { |
119 | 0 | return NULL; |
120 | 0 | } |
121 | 63 | } |
122 | | |
123 | | /* start with a \r\n */ |
124 | 3.68k | if (i == 0) { |
125 | 1.23k | buf->buffer[buf->len++] = '\r'; |
126 | 1.23k | buf->buffer[buf->len++] = '\n'; |
127 | 1.23k | } |
128 | | |
129 | 3.68k | memcpy(buf->buffer + buf->len, bstr_ptr(h->name), bstr_size(h->name)); |
130 | 3.68k | buf->len += bstr_size(h->name); |
131 | 3.68k | buf->buffer[buf->len++] = '\r'; |
132 | 3.68k | buf->buffer[buf->len++] = '\n'; |
133 | | |
134 | | /* end with an extra \r\n */ |
135 | 3.68k | if (i + 1 == no_of_headers) { |
136 | 1.23k | buf->buffer[buf->len++] = '\r'; |
137 | 1.23k | buf->buffer[buf->len++] = '\n'; |
138 | 1.23k | } |
139 | 3.68k | } |
140 | | |
141 | 2.17k | *buffer_len = buf->len; |
142 | 2.17k | return buf->buffer; |
143 | 2.17k | } |
144 | | |
145 | | static InspectionBuffer *GetBuffer1ForTX(DetectEngineThreadCtx *det_ctx, |
146 | | const DetectEngineTransforms *transforms, Flow *f, const uint8_t flow_flags, void *txv, |
147 | | const int list_id) |
148 | 23.1k | { |
149 | 23.1k | InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); |
150 | 23.1k | if (buffer->inspect == NULL) { |
151 | 21.9k | uint32_t rawdata_len = 0; |
152 | 21.9k | uint8_t *rawdata = GetBufferForTX(txv, det_ctx, f, flow_flags, &rawdata_len); |
153 | 21.9k | if (rawdata_len == 0) |
154 | 20.6k | return NULL; |
155 | | |
156 | 1.25k | InspectionBufferSetup(det_ctx, list_id, buffer, rawdata, rawdata_len); |
157 | 1.25k | InspectionBufferApplyTransforms(buffer, transforms); |
158 | 1.25k | } |
159 | | |
160 | 2.47k | return buffer; |
161 | 23.1k | } |
162 | | |
163 | | static InspectionBuffer *GetBuffer2ForTX(DetectEngineThreadCtx *det_ctx, |
164 | | const DetectEngineTransforms *transforms, Flow *_f, const uint8_t flow_flags, void *txv, |
165 | | const int list_id) |
166 | 1.03k | { |
167 | 1.03k | InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); |
168 | 1.03k | if (buffer->inspect == NULL) { |
169 | 940 | uint32_t b_len = 0; |
170 | 940 | const uint8_t *b = NULL; |
171 | | |
172 | 940 | if (rs_http2_tx_get_header_names(txv, flow_flags, &b, &b_len) != 1) |
173 | 692 | return NULL; |
174 | 248 | if (b == NULL || b_len == 0) |
175 | 0 | return NULL; |
176 | | |
177 | 248 | InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len); |
178 | 248 | InspectionBufferApplyTransforms(buffer, transforms); |
179 | 248 | } |
180 | | |
181 | 340 | return buffer; |
182 | 1.03k | } |
183 | | |
184 | | /** |
185 | | * \brief The setup function for the http.header_names keyword for a signature. |
186 | | * |
187 | | * \param de_ctx Pointer to the detection engine context. |
188 | | * \param s Pointer to signature for the current Signature being parsed |
189 | | * from the rules. |
190 | | * \param m Pointer to the head of the SigMatchs for the current rule |
191 | | * being parsed. |
192 | | * \param arg Pointer to the string holding the keyword value. |
193 | | * |
194 | | * \retval 0 On success. |
195 | | * \retval -1 On failure. |
196 | | */ |
197 | | static int DetectHttpHeaderNamesSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) |
198 | 4.03k | { |
199 | 4.03k | if (DetectBufferSetActiveList(de_ctx, s, g_buffer_id) < 0) |
200 | 4 | return -1; |
201 | | |
202 | 4.02k | if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0) |
203 | 4 | return -1; |
204 | | |
205 | 4.02k | return 0; |
206 | 4.02k | } |
207 | | |
208 | | /** |
209 | | * \brief Registers the keyword handlers for the "http.header_names" keyword. |
210 | | */ |
211 | | void DetectHttpHeaderNamesRegister(void) |
212 | 73 | { |
213 | 73 | sigmatch_table[DETECT_AL_HTTP_HEADER_NAMES].name = KEYWORD_NAME; |
214 | 73 | sigmatch_table[DETECT_AL_HTTP_HEADER_NAMES].alias = KEYWORD_NAME_LEGACY; |
215 | 73 | sigmatch_table[DETECT_AL_HTTP_HEADER_NAMES].desc = BUFFER_NAME " sticky buffer"; |
216 | 73 | sigmatch_table[DETECT_AL_HTTP_HEADER_NAMES].url = "/rules/" KEYWORD_DOC; |
217 | 73 | sigmatch_table[DETECT_AL_HTTP_HEADER_NAMES].Setup = DetectHttpHeaderNamesSetup; |
218 | | |
219 | 73 | sigmatch_table[DETECT_AL_HTTP_HEADER_NAMES].flags |= SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER; |
220 | | |
221 | | /* http1 */ |
222 | 73 | DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, |
223 | 73 | GetBuffer1ForTX, ALPROTO_HTTP1, HTP_REQUEST_HEADERS); |
224 | 73 | DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister, |
225 | 73 | GetBuffer1ForTX, ALPROTO_HTTP1, HTP_RESPONSE_HEADERS); |
226 | | |
227 | 73 | DetectAppLayerInspectEngineRegister2(BUFFER_NAME, ALPROTO_HTTP1, SIG_FLAG_TOSERVER, |
228 | 73 | HTP_REQUEST_HEADERS, DetectEngineInspectBufferGeneric, GetBuffer1ForTX); |
229 | 73 | DetectAppLayerInspectEngineRegister2(BUFFER_NAME, ALPROTO_HTTP1, SIG_FLAG_TOCLIENT, |
230 | 73 | HTP_RESPONSE_HEADERS, DetectEngineInspectBufferGeneric, GetBuffer1ForTX); |
231 | | |
232 | | /* http2 */ |
233 | 73 | DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, |
234 | 73 | GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataClient); |
235 | 73 | DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister, |
236 | 73 | GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataServer); |
237 | | |
238 | 73 | DetectAppLayerInspectEngineRegister2(BUFFER_NAME, ALPROTO_HTTP2, SIG_FLAG_TOSERVER, |
239 | 73 | HTTP2StateDataClient, DetectEngineInspectBufferGeneric, GetBuffer2ForTX); |
240 | 73 | DetectAppLayerInspectEngineRegister2(BUFFER_NAME, ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, |
241 | 73 | HTTP2StateDataServer, DetectEngineInspectBufferGeneric, GetBuffer2ForTX); |
242 | | |
243 | 73 | DetectBufferTypeSetDescriptionByName(BUFFER_NAME, |
244 | 73 | BUFFER_DESC); |
245 | | |
246 | 73 | g_buffer_id = DetectBufferTypeGetByName(BUFFER_NAME); |
247 | | |
248 | 73 | g_keyword_thread_id = DetectRegisterThreadCtxGlobalFuncs(KEYWORD_NAME, |
249 | 73 | HttpHeaderThreadDataInit, &g_td_config, HttpHeaderThreadDataFree); |
250 | | |
251 | 73 | SCLogDebug("keyword %s registered. Thread id %d. " |
252 | 73 | "Buffer %s registered. Buffer id %d", |
253 | 73 | KEYWORD_NAME, g_keyword_thread_id, |
254 | 73 | BUFFER_NAME, g_buffer_id); |
255 | 73 | } |