summaryrefslogtreecommitdiff
path: root/ufilter.c
diff options
context:
space:
mode:
Diffstat (limited to 'ufilter.c')
-rw-r--r--ufilter.c425
1 files changed, 425 insertions, 0 deletions
diff --git a/ufilter.c b/ufilter.c
new file mode 100644
index 0000000..cb36a67
--- /dev/null
+++ b/ufilter.c
@@ -0,0 +1,425 @@
1/*
2 +----------------------------------------------------------------------+
3 | Suhosin Version 1 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006-2007 The Hardened-PHP Project |
6 | Copyright (c) 2007-2016 SektionEins GmbH |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
16 | Authors: Stefan Esser <sesser@sektioneins.de> |
17 | Ben Fuhrmannek <ben.fuhrmannek@sektioneins.de> |
18 +----------------------------------------------------------------------+
19*/
20/*
21 $Id: ufilter.c,v 1.1.1.1 2007-11-28 01:15:35 sesser Exp $
22*/
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include "php.h"
29#include "php_ini.h"
30#include "ext/standard/info.h"
31#include "php_suhosin7.h"
32#include "php_variables.h"
33#include "suhosin_rfc1867.h"
34#include "ext/standard/php_var.h"
35
36// #if !HAVE_RFC1867_CALLBACK
37// PHP_SUHOSIN_API int (*php_rfc1867_callback)(unsigned int event, void *event_data, void **extra) = NULL;
38// #endif
39//
40
41/* {{{ SAPI_UPLOAD_VARNAME_FILTER_FUNC
42 */
43static int check_fileupload_varname(char *varname)
44{
45 char *index, *prev_index = NULL, *var;
46 unsigned int var_len, total_len, depth = 0;
47
48 var = estrdup(varname);
49
50 /* Normalize the variable name */
51 suhosin_normalize_varname(var);
52
53 /* Find length of variable name */
54 index = strchr(var, '[');
55 total_len = strlen(var);
56 var_len = index ? index-var : total_len;
57
58 /* Drop this variable if it exceeds the varname/total length limit */
59 if (SUHOSIN7_G(max_varname_length) && SUHOSIN7_G(max_varname_length) < var_len) {
60 suhosin_log(S_FILES, "configured request variable name length limit exceeded - dropped variable '%s'", var);
61 if (!SUHOSIN7_G(simulation)) {
62 goto return_failure;
63 }
64 }
65 if (SUHOSIN7_G(max_totalname_length) && SUHOSIN7_G(max_totalname_length) < total_len) {
66 suhosin_log(S_FILES, "configured request variable total name length limit exceeded - dropped variable '%s'", var);
67 if (!SUHOSIN7_G(simulation)) {
68 goto return_failure;
69 }
70 }
71 if (SUHOSIN7_G(max_post_name_length) && SUHOSIN7_G(max_post_name_length) < var_len) {
72 suhosin_log(S_FILES, "configured POST variable name length limit exceeded - dropped variable '%s'", var);
73 if (!SUHOSIN7_G(simulation)) {
74 goto return_failure;
75 }
76 }
77 if (SUHOSIN7_G(max_post_totalname_length) && SUHOSIN7_G(max_post_totalname_length) < var_len) {
78 suhosin_log(S_FILES, "configured POST variable total name length limit exceeded - dropped variable '%s'", var);
79 if (!SUHOSIN7_G(simulation)) {
80 goto return_failure;
81 }
82 }
83
84 /* Find out array depth */
85 while (index) {
86 char *index_end;
87 unsigned int index_length;
88
89 /* overjump '[' */
90 index++;
91
92 /* increase array depth */
93 depth++;
94
95 index_end = strchr(index, ']');
96 if (index_end == NULL) {
97 index_end = index+strlen(index);
98 }
99
100 index_length = index_end - index;
101
102 if (SUHOSIN7_G(max_array_index_length) && SUHOSIN7_G(max_array_index_length) < index_length) {
103 suhosin_log(S_FILES, "configured request variable array index length limit exceeded - dropped variable '%s'", var);
104 if (!SUHOSIN7_G(simulation)) {
105 goto return_failure;
106 }
107 }
108 if (SUHOSIN7_G(max_post_array_index_length) && SUHOSIN7_G(max_post_array_index_length) < index_length) {
109 suhosin_log(S_FILES, "configured POST variable array index length limit exceeded - dropped variable '%s'", var);
110 if (!SUHOSIN7_G(simulation)) {
111 goto return_failure;
112 }
113 }
114
115 /* index whitelist/blacklist */
116 if (SUHOSIN7_G(array_index_whitelist) && *(SUHOSIN7_G(array_index_whitelist))) {
117 if (suhosin_strnspn(index, index_length, SUHOSIN7_G(array_index_whitelist)) != index_length) {
118 suhosin_log(S_VARS, "array index contains not whitelisted characters - dropped variable '%s'", var);
119 if (!SUHOSIN7_G(simulation)) {
120 goto return_failure;
121 }
122 }
123 } else if (SUHOSIN7_G(array_index_blacklist) && *(SUHOSIN7_G(array_index_blacklist))) {
124 if (suhosin_strncspn(index, index_length, SUHOSIN7_G(array_index_blacklist)) != index_length) {
125 suhosin_log(S_VARS, "array index contains blacklisted characters - dropped variable '%s'", var);
126 if (!SUHOSIN7_G(simulation)) {
127 goto return_failure;
128 }
129 }
130 }
131
132
133 index = strchr(index, '[');
134 }
135
136 /* Drop this variable if it exceeds the array depth limit */
137 if (SUHOSIN7_G(max_array_depth) && SUHOSIN7_G(max_array_depth) < depth) {
138 suhosin_log(S_FILES, "configured request variable array depth limit exceeded - dropped variable '%s'", var);
139 if (!SUHOSIN7_G(simulation)) {
140 goto return_failure;
141 }
142 }
143 if (SUHOSIN7_G(max_post_array_depth) && SUHOSIN7_G(max_post_array_depth) < depth) {
144 suhosin_log(S_FILES, "configured POST variable array depth limit exceeded - dropped variable '%s'", var);
145 if (!SUHOSIN7_G(simulation)) {
146 goto return_failure;
147 }
148 }
149
150
151 /* Drop this variable if it is one of GLOBALS, _GET, _POST, ... */
152 /* This is to protect several silly scripts that do globalizing themself */
153 if (suhosin_is_protected_varname(var, var_len)) {
154 suhosin_log(S_FILES, "tried to register forbidden variable '%s' through FILE variables", var);
155 if (!SUHOSIN7_G(simulation)) {
156 goto return_failure;
157 }
158 }
159
160 efree(var);
161 return SUCCESS;
162
163return_failure:
164 efree(var);
165 return FAILURE;
166}
167/* }}} */
168
169#ifdef SUHOSIN7_EXPERIMENTAL
170static inline int suhosin_validate_utf8_multibyte(const char* cp, size_t maxlen)
171{
172 if (maxlen < 2 || !(*cp & 0x80)) { return 0; }
173 if ((*cp & 0xe0) == 0xc0 && // 1st byte is 110xxxxx
174 (*(cp+1) & 0xc0) == 0x80 && // 2nd byte is 10xxxxxx
175 (*cp & 0x1e)) { // overlong check 110[xxxx]x 10xxxxxx
176 return 2;
177 }
178 if (maxlen < 3) { return 0; }
179 if ((*cp & 0xf0) == 0xe0 && // 1st byte is 1110xxxx
180 (*(cp+1) & 0xc0) == 0x80 && // 2nd byte is 10xxxxxx
181 (*(cp+2) & 0xc0) == 0x80 && // 3rd byte is 10xxxxxx
182 ((*cp & 0x0f) | (*(cp+1) & 0x20))) { // 1110[xxxx] 10[x]xxxxx 10xxxxxx
183 return 3;
184 }
185 if (maxlen < 4) { return 0; }
186 if ((*cp & 0xf8) == 0xf0 && // 1st byte is 11110xxx
187 (*(cp+1) & 0xc0) == 0x80 && // 2nd byte is 10xxxxxx
188 (*(cp+2) & 0xc0) == 0x80 && // 3rd byte is 10xxxxxx
189 (*(cp+3) & 0xc0) == 0x80 && // 4th byte is 10xxxxxx
190 ((*cp & 0x07) | (*(cp+1) & 0x30))) { // 11110[xxx] 10[xx]xxxx 10xxxxxx 10xxxxxx
191 return 4;
192 }
193 return 0;
194}
195#endif
196
197int suhosin_rfc1867_filter(unsigned int event, void *event_data, void **extra)
198{
199 int retval = SUCCESS;
200
201 SDEBUG("rfc1867_filter %u", event);
202
203 switch (event) {
204 case MULTIPART_EVENT_START:
205 case MULTIPART_EVENT_FORMDATA:
206 /* nothing todo */
207 break;
208
209 case MULTIPART_EVENT_FILE_START:
210 {
211 multipart_event_file_start *mefs = (multipart_event_file_start *) event_data;
212
213 /* Drop if no more variables flag is set */
214 if (SUHOSIN7_G(no_more_uploads)) {
215 goto continue_with_failure;
216 }
217
218 /* Drop this fileupload if the limit is reached */
219 if (SUHOSIN7_G(upload_limit) && SUHOSIN7_G(upload_limit) <= SUHOSIN7_G(num_uploads)) {
220 suhosin_log(S_FILES, "configured fileupload limit exceeded - file dropped");
221 if (!SUHOSIN7_G(simulation)) {
222 SUHOSIN7_G(no_more_uploads) = 1;
223 goto continue_with_failure;
224 }
225 }
226
227 if (check_fileupload_varname(mefs->name) == FAILURE) {
228 goto continue_with_failure;
229 }
230 }
231
232 break;
233
234 case MULTIPART_EVENT_FILE_DATA:
235
236 if (SUHOSIN7_G(upload_disallow_elf)) {
237 multipart_event_file_data *mefd = (multipart_event_file_data *) event_data;
238
239 if (mefd->offset == 0 && mefd->length > 10) {
240 if (mefd->data[0] == 0x7F && mefd->data[1] == 'E' && mefd->data[2] == 'L' && mefd->data[3] == 'F') {
241 suhosin_log(S_FILES, "uploaded file is an ELF executable - file dropped");
242 if (!SUHOSIN7_G(simulation)) {
243 goto continue_with_failure;
244 }
245 }
246 }
247 }
248
249 if (SUHOSIN7_G(upload_disallow_binary)) {
250
251 multipart_event_file_data *mefd = (multipart_event_file_data *) event_data;
252
253 char *cp, *cpend;
254 int n;
255 cpend = mefd->data + mefd->length;
256 for (cp = mefd->data; cp < cpend; cp++) {
257 if (*cp >= 32 || isspace(*cp)) {
258 continue;
259 }
260#ifdef SUHOSIN7_EXPERIMENTAL
261 if ((*cp & 0x80) && SUHOSIN7_G(upload_allow_utf8)) {
262 SDEBUG("checking char %x", *cp);
263 if ((n = suhosin_validate_utf8_multibyte(cp, cpend-cp))) { // valid UTF8 multibyte character
264 cp += n - 1;
265 continue;
266 }
267 }
268#endif
269 suhosin_log(S_FILES, "uploaded file contains binary data - file dropped");
270 if (!SUHOSIN7_G(simulation)) {
271 goto continue_with_failure;
272 }
273 break;
274 }
275 }
276
277 if (SUHOSIN7_G(upload_remove_binary)) {
278
279 multipart_event_file_data *mefd = (multipart_event_file_data *) event_data;
280 size_t i, j;
281 int n;
282
283 for (i=0, j=0; i<mefd->length; i++) {
284 if (mefd->data[i] >= 32 || isspace(mefd->data[i])) {
285 mefd->data[j++] = mefd->data[i];
286 }
287#ifdef SUHOSIN7_EXPERIMENTAL
288 else if (SUHOSIN7_G(upload_allow_utf8) && mefd->data[i] & 0x80) {
289 n = suhosin_validate_utf8_multibyte(mefd->data + i, mefd->length - i);
290 if (!n) { continue; }
291 while (n--) {
292 mefd->data[j++] = mefd->data[i++];
293 }
294 i--;
295 }
296#endif
297 }
298 mefd->data[j] = '\0';
299
300 SDEBUG("removing binary %zu %zu",i,j);
301 /* IMPORTANT FOR DAISY CHAINING */
302 mefd->length = j;
303 if (mefd->newlength) {
304 *mefd->newlength = j;
305 }
306 }
307
308 break;
309
310 case MULTIPART_EVENT_FILE_END:
311
312 if (SUHOSIN7_G(upload_verification_script)) {
313 multipart_event_file_end *mefe = (multipart_event_file_end *) event_data;
314 char cmd[8192];
315 FILE *in;
316 int first=1;
317 struct stat st;
318 char *sname = SUHOSIN7_G(upload_verification_script);
319
320 /* ignore files that will get deleted anyway */
321 if (mefe->cancel_upload) {
322 break;
323 }
324
325 /* ignore empty scriptnames */
326 while (isspace(*sname)) ++sname;
327 if (*sname == 0) {
328 SUHOSIN7_G(num_uploads)++;
329 break;
330 }
331
332 if (VCWD_STAT(sname, &st) < 0) {
333 suhosin_log(S_FILES, "unable to find fileupload verification script %s - file dropped", sname);
334 if (!SUHOSIN7_G(simulation)) {
335 goto continue_with_failure;
336 } else {
337 goto continue_with_next;
338 }
339 }
340 if (access(sname, X_OK|R_OK) < 0) {
341 suhosin_log(S_FILES, "fileupload verification script %s is not executable - file dropped", sname);
342 if (!SUHOSIN7_G(simulation)) {
343 goto continue_with_failure;
344 } else {
345 goto continue_with_next;
346 }
347 }
348
349 ap_php_snprintf(cmd, sizeof(cmd), "%s %s 2>&1", sname, mefe->temp_filename);
350
351 if ((in = VCWD_POPEN(cmd, "r")) == NULL) {
352 suhosin_log(S_FILES, "unable to execute fileupload verification script %s - file dropped", sname);
353 if (!SUHOSIN7_G(simulation)) {
354 goto continue_with_failure;
355 } else {
356 goto continue_with_next;
357 }
358 }
359
360 retval = FAILURE;
361
362 /* read and forget the result */
363 while (1) {
364 int readbytes = fread(cmd, 1, sizeof(cmd), in);
365 if (readbytes<=0) {
366 break;
367 }
368 if (first) {
369 if (strncmp(cmd, "sh: ", 4) == 0) {
370 /* assume this is an error */
371 suhosin_log(S_FILES, "error while executing fileupload verification script %s - file dropped", sname);
372 if (!SUHOSIN7_G(simulation)) {
373 goto continue_with_failure;
374 } else {
375 goto continue_with_next;
376 }
377 } else {
378 retval = atoi(cmd) == 1 ? SUCCESS : FAILURE;
379 first = 0;
380 }
381 }
382 }
383 pclose(in);
384 }
385
386 if (retval != SUCCESS) {
387 suhosin_log(S_FILES, "fileupload verification script disallows file - file dropped");
388 if (!SUHOSIN7_G(simulation)) {
389 goto continue_with_failure;
390 }
391 }
392
393 SUHOSIN7_G(num_uploads)++;
394 break;
395
396 case MULTIPART_EVENT_END:
397 /* nothing todo */
398 break;
399
400 default:
401 /* unknown: return failure */
402 goto continue_with_failure;
403 }
404continue_with_next:
405// #if HAVE_RFC1867_CALLBACK
406 if (php_rfc1867_callback != NULL) {
407 return php_rfc1867_callback(event, event_data, extra);
408 }
409// #endif
410 return SUCCESS;
411continue_with_failure:
412 SUHOSIN7_G(abort_request) = 1;
413 return FAILURE;
414}
415
416
417
418/*
419 * Local variables:
420 * tab-width: 4
421 * c-basic-offset: 4
422 * End:
423 * vim600: sw=4 ts=4 fdm=marker
424 * vim<600: sw=4 ts=4
425 */