/src/suricata7/libhtp/htp/htp_content_handlers.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*************************************************************************** |
2 | | * Copyright (c) 2009-2010 Open Information Security Foundation |
3 | | * Copyright (c) 2010-2013 Qualys, Inc. |
4 | | * All rights reserved. |
5 | | * |
6 | | * Redistribution and use in source and binary forms, with or without |
7 | | * modification, are permitted provided that the following conditions are |
8 | | * met: |
9 | | * |
10 | | * - Redistributions of source code must retain the above copyright |
11 | | * notice, this list of conditions and the following disclaimer. |
12 | | |
13 | | * - Redistributions in binary form must reproduce the above copyright |
14 | | * notice, this list of conditions and the following disclaimer in the |
15 | | * documentation and/or other materials provided with the distribution. |
16 | | |
17 | | * - Neither the name of the Qualys, Inc. nor the names of its |
18 | | * contributors may be used to endorse or promote products derived from |
19 | | * this software without specific prior written permission. |
20 | | * |
21 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
22 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
23 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
24 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
25 | | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
26 | | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
27 | | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
28 | | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
29 | | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
30 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
31 | | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
32 | | ***************************************************************************/ |
33 | | |
34 | | /** |
35 | | * @file |
36 | | * @author Ivan Ristic <ivanr@webkreator.com> |
37 | | */ |
38 | | |
39 | | #include "htp_config_auto.h" |
40 | | |
41 | | #include "htp_private.h" |
42 | | |
43 | | /** |
44 | | * This callback function feeds request body data to a Urlencoded parser |
45 | | * and, later, feeds the parsed parameters to the correct structures. |
46 | | * |
47 | | * @param[in] d |
48 | | * @return HTP_OK on success, HTP_ERROR on failure. |
49 | | */ |
50 | 0 | htp_status_t htp_ch_urlencoded_callback_request_body_data(htp_tx_data_t *d) { |
51 | 0 | htp_tx_t *tx = d->tx; |
52 | | |
53 | | // Check that we were not invoked again after the finalization. |
54 | 0 | if (tx->request_urlenp_body->params == NULL) return HTP_ERROR; |
55 | | |
56 | 0 | if (d->data != NULL) { |
57 | | // Process one chunk of data. |
58 | 0 | htp_urlenp_parse_partial(tx->request_urlenp_body, d->data, d->len); |
59 | 0 | } else { |
60 | | // Finalize parsing. |
61 | 0 | htp_urlenp_finalize(tx->request_urlenp_body); |
62 | | |
63 | | // Add all parameters to the transaction. |
64 | 0 | bstr *name = NULL; |
65 | 0 | bstr *value = NULL; |
66 | |
|
67 | 0 | for (size_t i = 0, n = htp_table_size(tx->request_urlenp_body->params); i < n; i++) { |
68 | 0 | value = htp_table_get_index(tx->request_urlenp_body->params, i, &name); |
69 | |
|
70 | 0 | htp_param_t *param = calloc(1, sizeof (htp_param_t)); |
71 | 0 | if (param == NULL) return HTP_ERROR; |
72 | | |
73 | 0 | param->name = name; |
74 | 0 | param->value = value; |
75 | 0 | param->source = HTP_SOURCE_BODY; |
76 | 0 | param->parser_id = HTP_PARSER_URLENCODED; |
77 | 0 | param->parser_data = NULL; |
78 | |
|
79 | 0 | if (htp_tx_req_add_param(tx, param) != HTP_OK) { |
80 | 0 | free(param); |
81 | 0 | return HTP_ERROR; |
82 | 0 | } |
83 | 0 | } |
84 | | |
85 | | // All the parameter data is now owned by the transaction, and |
86 | | // the parser table used to store it is no longer needed. The |
87 | | // line below will destroy just the table, leaving keys intact. |
88 | 0 | htp_table_destroy_ex(tx->request_urlenp_body->params); |
89 | 0 | tx->request_urlenp_body->params = NULL; |
90 | 0 | } |
91 | | |
92 | 0 | return HTP_OK; |
93 | 0 | } |
94 | | |
95 | | /** |
96 | | * Determine if the request has a Urlencoded body, and, if it does, create and |
97 | | * attach an instance of the Urlencoded parser to the transaction. |
98 | | * |
99 | | * @param[in] connp |
100 | | * @return HTP_OK if a new parser has been setup, HTP_DECLINED if the MIME type |
101 | | * is not appropriate for this parser, and HTP_ERROR on failure. |
102 | | */ |
103 | 0 | htp_status_t htp_ch_urlencoded_callback_request_headers(htp_tx_t *tx) { |
104 | | // Check the request content type to see if it matches our MIME type. |
105 | 0 | if ((tx->request_content_type == NULL) || (!bstr_begins_with_c(tx->request_content_type, HTP_URLENCODED_MIME_TYPE))) { |
106 | | #ifdef HTP_DEBUG |
107 | | fprintf(stderr, "htp_ch_urlencoded_callback_request_headers: Body not URLENCODED\n"); |
108 | | #endif |
109 | |
|
110 | 0 | return HTP_DECLINED; |
111 | 0 | } |
112 | | |
113 | | #ifdef HTP_DEBUG |
114 | | fprintf(stderr, "htp_ch_urlencoded_callback_request_headers: Parsing URLENCODED body\n"); |
115 | | #endif |
116 | | |
117 | | // Create parser instance. |
118 | 0 | tx->request_urlenp_body = htp_urlenp_create(tx); |
119 | 0 | if (tx->request_urlenp_body == NULL) return HTP_ERROR; |
120 | | |
121 | | // Register a request body data callback. |
122 | 0 | htp_tx_register_request_body_data(tx, htp_ch_urlencoded_callback_request_body_data); |
123 | |
|
124 | 0 | return HTP_OK; |
125 | 0 | } |
126 | | |
127 | | /** |
128 | | * Parses request query string, if present. |
129 | | * |
130 | | * @param[in] connp |
131 | | * @param[in] raw_data |
132 | | * @param[in] raw_len |
133 | | * @return HTP_OK if query string was parsed, HTP_DECLINED if there was no query |
134 | | * string, and HTP_ERROR on failure. |
135 | | */ |
136 | 0 | htp_status_t htp_ch_urlencoded_callback_request_line(htp_tx_t *tx) { |
137 | | // Proceed only if there's something for us to parse. |
138 | 0 | if ((tx->parsed_uri->query == NULL) || (bstr_len(tx->parsed_uri->query) == 0)) { |
139 | 0 | return HTP_DECLINED; |
140 | 0 | } |
141 | | |
142 | | // We have a non-zero length query string. |
143 | | |
144 | 0 | tx->request_urlenp_query = htp_urlenp_create(tx); |
145 | 0 | if (tx->request_urlenp_query == NULL) return HTP_ERROR; |
146 | | |
147 | 0 | if (htp_urlenp_parse_complete(tx->request_urlenp_query, bstr_ptr(tx->parsed_uri->query), |
148 | 0 | bstr_len(tx->parsed_uri->query)) != HTP_OK) { |
149 | 0 | htp_urlenp_destroy(tx->request_urlenp_query); |
150 | 0 | return HTP_ERROR; |
151 | 0 | } |
152 | | |
153 | | // Add all parameters to the transaction. |
154 | | |
155 | 0 | bstr *name = NULL; |
156 | 0 | bstr *value = NULL; |
157 | 0 | for (size_t i = 0, n = htp_table_size(tx->request_urlenp_query->params); i < n; i++) { |
158 | 0 | value = htp_table_get_index(tx->request_urlenp_query->params, i, &name); |
159 | |
|
160 | 0 | htp_param_t *param = calloc(1, sizeof (htp_param_t)); |
161 | 0 | if (param == NULL) return HTP_ERROR; |
162 | | |
163 | 0 | param->name = name; |
164 | 0 | param->value = value; |
165 | 0 | param->source = HTP_SOURCE_QUERY_STRING; |
166 | 0 | param->parser_id = HTP_PARSER_URLENCODED; |
167 | 0 | param->parser_data = NULL; |
168 | |
|
169 | 0 | if (htp_tx_req_add_param(tx, param) != HTP_OK) { |
170 | 0 | free(param); |
171 | 0 | return HTP_ERROR; |
172 | 0 | } |
173 | 0 | } |
174 | | |
175 | | // All the parameter data is now owned by the transaction, and |
176 | | // the parser table used to store it is no longer needed. The |
177 | | // line below will destroy just the table, leaving keys intact. |
178 | 0 | htp_table_destroy_ex(tx->request_urlenp_query->params); |
179 | 0 | tx->request_urlenp_query->params = NULL; |
180 | |
|
181 | 0 | htp_urlenp_destroy(tx->request_urlenp_query); |
182 | 0 | tx->request_urlenp_query = NULL; |
183 | |
|
184 | 0 | return HTP_OK; |
185 | 0 | } |
186 | | |
187 | | /** |
188 | | * Finalize Multipart processing. |
189 | | * |
190 | | * @param[in] d |
191 | | * @return HTP_OK on success, HTP_ERROR on failure. |
192 | | */ |
193 | 0 | htp_status_t htp_ch_multipart_callback_request_body_data(htp_tx_data_t *d) { |
194 | 0 | htp_tx_t *tx = d->tx; |
195 | | |
196 | | // Check that we were not invoked again after the finalization. |
197 | 0 | if (tx->request_mpartp->gave_up_data == 1) return HTP_ERROR; |
198 | | |
199 | 0 | if (d->data != NULL) { |
200 | | // Process one chunk of data. |
201 | 0 | htp_mpartp_parse(tx->request_mpartp, d->data, d->len); |
202 | 0 | } else { |
203 | | // Finalize parsing. |
204 | 0 | htp_mpartp_finalize(tx->request_mpartp); |
205 | |
|
206 | 0 | htp_multipart_t *body = htp_mpartp_get_multipart(tx->request_mpartp); |
207 | |
|
208 | 0 | for (size_t i = 0, n = htp_list_size(body->parts); i < n; i++) { |
209 | 0 | htp_multipart_part_t *part = htp_list_get(body->parts, i); |
210 | | |
211 | | // Use text parameters. |
212 | 0 | if (part->type == MULTIPART_PART_TEXT) { |
213 | 0 | htp_param_t *param = calloc(1, sizeof (htp_param_t)); |
214 | 0 | if (param == NULL) return HTP_ERROR; |
215 | 0 | param->name = part->name; |
216 | 0 | param->value = part->value; |
217 | 0 | param->source = HTP_SOURCE_BODY; |
218 | 0 | param->parser_id = HTP_PARSER_MULTIPART; |
219 | 0 | param->parser_data = part; |
220 | |
|
221 | 0 | if (htp_tx_req_add_param(tx, param) != HTP_OK) { |
222 | 0 | free(param); |
223 | 0 | return HTP_ERROR; |
224 | 0 | } |
225 | 0 | } |
226 | 0 | } |
227 | | |
228 | | // Tell the parser that it no longer owns names |
229 | | // and values of MULTIPART_PART_TEXT parts. |
230 | 0 | tx->request_mpartp->gave_up_data = 1; |
231 | 0 | } |
232 | | |
233 | 0 | return HTP_OK; |
234 | 0 | } |
235 | | |
236 | | /** |
237 | | * Inspect request headers and register the Multipart request data hook |
238 | | * if it contains a multipart/form-data body. |
239 | | * |
240 | | * @param[in] connp |
241 | | * @return HTP_OK if a new parser has been setup, HTP_DECLINED if the MIME type |
242 | | * is not appropriate for this parser, and HTP_ERROR on failure. |
243 | | */ |
244 | 0 | htp_status_t htp_ch_multipart_callback_request_headers(htp_tx_t *tx) { |
245 | | #ifdef HTP_DEBUG |
246 | | fprintf(stderr, "htp_ch_multipart_callback_request_headers: Need to determine if multipart body is present\n"); |
247 | | #endif |
248 | | |
249 | | // The field tx->request_content_type does not contain the entire C-T |
250 | | // value and so we cannot use it to look for a boundary, but we can |
251 | | // use it for a quick check to determine if the C-T header exists. |
252 | 0 | if (tx->request_content_type == NULL) { |
253 | | #ifdef HTP_DEBUG |
254 | | fprintf(stderr, "htp_ch_multipart_callback_request_headers: Not multipart body (no C-T header)\n"); |
255 | | #endif |
256 | |
|
257 | 0 | return HTP_DECLINED; |
258 | 0 | } |
259 | | |
260 | | // Look for a boundary. |
261 | | |
262 | 0 | htp_header_t *ct = htp_table_get_c(tx->request_headers, "content-type"); |
263 | 0 | if (ct == NULL) return HTP_ERROR; |
264 | | |
265 | 0 | bstr *boundary = NULL; |
266 | 0 | uint64_t flags = 0; |
267 | |
|
268 | 0 | htp_status_t rc = htp_mpartp_find_boundary(ct->value, &boundary, &flags); |
269 | 0 | if (rc != HTP_OK) { |
270 | | #ifdef HTP_DEBUG |
271 | | if (rc == HTP_DECLINED) { |
272 | | fprintf(stderr, "htp_ch_multipart_callback_request_headers: Not multipart body\n"); |
273 | | } |
274 | | #endif |
275 | | |
276 | | // No boundary (HTP_DECLINED) or error (HTP_ERROR). |
277 | 0 | return rc; |
278 | 0 | } |
279 | | |
280 | 0 | if (boundary == NULL) return HTP_ERROR; |
281 | | |
282 | | // Create a Multipart parser instance. |
283 | 0 | tx->request_mpartp = htp_mpartp_create(tx->connp->cfg, boundary, flags); |
284 | 0 | if (tx->request_mpartp == NULL) { |
285 | 0 | bstr_free(boundary); |
286 | 0 | return HTP_ERROR; |
287 | 0 | } |
288 | | |
289 | | // Configure file extraction. |
290 | 0 | if (tx->cfg->extract_request_files) { |
291 | 0 | tx->request_mpartp->extract_files = 1; |
292 | 0 | tx->request_mpartp->extract_dir = tx->connp->cfg->tmpdir; |
293 | 0 | } |
294 | | |
295 | | // Register a request body data callback. |
296 | 0 | htp_tx_register_request_body_data(tx, htp_ch_multipart_callback_request_body_data); |
297 | |
|
298 | 0 | return HTP_OK; |
299 | 0 | } |