summaryrefslogtreecommitdiff
path: root/ufilter.c
diff options
context:
space:
mode:
authorStefan Esser2010-02-21 11:44:54 +0100
committerStefan Esser2010-02-21 11:44:54 +0100
commit36dbfacbe64697d959f524e537b15b73c090d898 (patch)
treef1c7ce1409b0e7765fc72d550546967fcf0f9717 /ufilter.c
Inital commit
Diffstat (limited to 'ufilter.c')
-rw-r--r--ufilter.c367
1 files changed, 367 insertions, 0 deletions
diff --git a/ufilter.c b/ufilter.c
new file mode 100644
index 0000000..c2c64c2
--- /dev/null
+++ b/ufilter.c
@@ -0,0 +1,367 @@
1/*
2 +----------------------------------------------------------------------+
3 | Suhosin Version 1 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006-2007 The Hardened-PHP Project |
6 | Copyright (c) 2007 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 | Author: Stefan Esser <sesser@sektioneins.de> |
17 +----------------------------------------------------------------------+
18*/
19/*
20 $Id: ufilter.c,v 1.1.1.1 2007-11-28 01:15:35 sesser Exp $
21*/
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "php.h"
28#include "php_ini.h"
29#include "ext/standard/info.h"
30#include "php_suhosin.h"
31#include "php_variables.h"
32#include "suhosin_rfc1867.h"
33
34#if !HAVE_RFC1867_CALLBACK
35PHP_SUHOSIN_API int (*php_rfc1867_callback)(unsigned int event, void *event_data, void **extra TSRMLS_DC) = NULL;
36#endif
37
38static int is_protected_varname(char *var, int var_len)
39{
40 switch (var_len) {
41 case 18:
42 if (memcmp(var, "HTTP_RAW_POST_DATA", 18)==0) goto protected_varname2;
43 break;
44 case 17:
45 if (memcmp(var, "HTTP_SESSION_VARS", 17)==0) goto protected_varname2;
46 break;
47 case 16:
48 if (memcmp(var, "HTTP_SERVER_VARS", 16)==0) goto protected_varname2;
49 if (memcmp(var, "HTTP_COOKIE_VARS", 16)==0) goto protected_varname2;
50 break;
51 case 15:
52 if (memcmp(var, "HTTP_POST_FILES", 15)==0) goto protected_varname2;
53 break;
54 case 14:
55 if (memcmp(var, "HTTP_POST_VARS", 14)==0) goto protected_varname2;
56 break;
57 case 13:
58 if (memcmp(var, "HTTP_GET_VARS", 13)==0) goto protected_varname2;
59 if (memcmp(var, "HTTP_ENV_VARS", 13)==0) goto protected_varname2;
60 break;
61 case 8:
62 if (memcmp(var, "_SESSION", 8)==0) goto protected_varname2;
63 if (memcmp(var, "_REQUEST", 8)==0) goto protected_varname2;
64 break;
65 case 7:
66 if (memcmp(var, "GLOBALS", 7)==0) goto protected_varname2;
67 if (memcmp(var, "_COOKIE", 7)==0) goto protected_varname2;
68 if (memcmp(var, "_SERVER", 7)==0) goto protected_varname2;
69 break;
70 case 6:
71 if (memcmp(var, "_FILES", 6)==0) goto protected_varname2;
72 break;
73 case 5:
74 if (memcmp(var, "_POST", 5)==0) goto protected_varname2;
75 break;
76 case 4:
77 if (memcmp(var, "_ENV", 4)==0) goto protected_varname2;
78 if (memcmp(var, "_GET", 4)==0) goto protected_varname2;
79 break;
80 }
81
82 return 0;
83protected_varname2:
84 return 1;
85}
86
87/* {{{ SAPI_UPLOAD_VARNAME_FILTER_FUNC
88 */
89static int check_fileupload_varname(char *varname)
90{
91 char *index, *prev_index = NULL, *var;
92 unsigned int var_len, total_len, depth = 0;
93 TSRMLS_FETCH();
94
95 var = estrdup(varname);
96
97 /* Normalize the variable name */
98 normalize_varname(var);
99
100 /* Find length of variable name */
101 index = strchr(var, '[');
102 total_len = strlen(var);
103 var_len = index ? index-var : total_len;
104
105 /* Drop this variable if it exceeds the varname/total length limit */
106 if (SUHOSIN_G(max_varname_length) && SUHOSIN_G(max_varname_length) < var_len) {
107 suhosin_log(S_FILES, "configured request variable name length limit exceeded - dropped variable '%s'", var);
108 if (!SUHOSIN_G(simulation)) {
109 goto return_failure;
110 }
111 }
112 if (SUHOSIN_G(max_totalname_length) && SUHOSIN_G(max_totalname_length) < total_len) {
113 suhosin_log(S_FILES, "configured request variable total name length limit exceeded - dropped variable '%s'", var);
114 if (!SUHOSIN_G(simulation)) {
115 goto return_failure;
116 }
117 }
118 if (SUHOSIN_G(max_post_name_length) && SUHOSIN_G(max_post_name_length) < var_len) {
119 suhosin_log(S_FILES, "configured POST variable name length limit exceeded - dropped variable '%s'", var);
120 if (!SUHOSIN_G(simulation)) {
121 goto return_failure;
122 }
123 }
124 if (SUHOSIN_G(max_post_totalname_length) && SUHOSIN_G(max_post_totalname_length) < var_len) {
125 suhosin_log(S_FILES, "configured POST variable total name length limit exceeded - dropped variable '%s'", var);
126 if (!SUHOSIN_G(simulation)) {
127 goto return_failure;
128 }
129 }
130
131 /* Find out array depth */
132 while (index) {
133 unsigned int index_length;
134
135 depth++;
136 index = strchr(index+1, '[');
137
138 if (prev_index) {
139 index_length = index ? index - 1 - prev_index - 1: strlen(prev_index);
140
141 if (SUHOSIN_G(max_array_index_length) && SUHOSIN_G(max_array_index_length) < index_length) {
142 suhosin_log(S_FILES, "configured request variable array index length limit exceeded - dropped variable '%s'", var);
143 if (!SUHOSIN_G(simulation)) {
144 goto return_failure;
145 }
146 }
147 if (SUHOSIN_G(max_post_array_index_length) && SUHOSIN_G(max_post_array_index_length) < index_length) {
148 suhosin_log(S_FILES, "configured POST variable array index length limit exceeded - dropped variable '%s'", var);
149 if (!SUHOSIN_G(simulation)) {
150 goto return_failure;
151 }
152 }
153 prev_index = index;
154 }
155
156 }
157
158 /* Drop this variable if it exceeds the array depth limit */
159 if (SUHOSIN_G(max_array_depth) && SUHOSIN_G(max_array_depth) < depth) {
160 suhosin_log(S_FILES, "configured request variable array depth limit exceeded - dropped variable '%s'", var);
161 if (!SUHOSIN_G(simulation)) {
162 goto return_failure;
163 }
164 }
165 if (SUHOSIN_G(max_post_array_depth) && SUHOSIN_G(max_post_array_depth) < depth) {
166 suhosin_log(S_FILES, "configured POST variable array depth limit exceeded - dropped variable '%s'", var);
167 if (!SUHOSIN_G(simulation)) {
168 goto return_failure;
169 }
170 }
171
172
173 /* Drop this variable if it is one of GLOBALS, _GET, _POST, ... */
174 /* This is to protect several silly scripts that do globalizing themself */
175
176 if (is_protected_varname(var, var_len)) {
177 suhosin_log(S_FILES, "tried to register forbidden variable '%s' through FILE variables", var);
178 if (!SUHOSIN_G(simulation)) {
179 goto return_failure;
180 }
181 }
182
183 efree(var);
184 return SUCCESS;
185
186return_failure:
187 efree(var);
188 return FAILURE;
189}
190/* }}} */
191
192int suhosin_rfc1867_filter(unsigned int event, void *event_data, void **extra TSRMLS_DC)
193{
194 int retval = SUCCESS;
195
196 SDEBUG("rfc1867_filter %u", event);
197
198 switch (event) {
199 case MULTIPART_EVENT_START:
200 case MULTIPART_EVENT_FORMDATA:
201 /* nothing todo */
202 break;
203
204 case MULTIPART_EVENT_FILE_START:
205 {
206 multipart_event_file_start *mefs = (multipart_event_file_start *) event_data;
207
208 /* Drop if no more variables flag is set */
209 if (SUHOSIN_G(no_more_uploads)) {
210 goto continue_with_failure;
211 }
212
213 /* Drop this fileupload if the limit is reached */
214 if (SUHOSIN_G(upload_limit) && SUHOSIN_G(upload_limit) <= SUHOSIN_G(num_uploads)) {
215 suhosin_log(S_FILES, "configured fileupload limit exceeded - file dropped");
216 if (!SUHOSIN_G(simulation)) {
217 SUHOSIN_G(no_more_uploads) = 1;
218 goto continue_with_failure;
219 }
220 }
221
222
223 if (check_fileupload_varname(mefs->name) == FAILURE) {
224 goto continue_with_failure;
225 }
226 }
227
228 break;
229
230 case MULTIPART_EVENT_FILE_DATA:
231
232 if (SUHOSIN_G(upload_disallow_elf)) {
233 multipart_event_file_data *mefd = (multipart_event_file_data *) event_data;
234
235 if (mefd->offset == 0 && mefd->length > 10) {
236 if (mefd->data[0] == 0x7F && mefd->data[1] == 'E' && mefd->data[2] == 'L' && mefd->data[3] == 'F') {
237 suhosin_log(S_FILES, "uploaded file is an ELF executable - file dropped");
238 if (!SUHOSIN_G(simulation)) {
239 goto continue_with_failure;
240 }
241 }
242 }
243 }
244
245 if (SUHOSIN_G(upload_disallow_binary)) {
246
247 multipart_event_file_data *mefd = (multipart_event_file_data *) event_data;
248 size_t i;
249
250 for (i=0; i<mefd->length; i++) {
251 if (mefd->data[i] < 32 && !isspace(mefd->data[i])) {
252 suhosin_log(S_FILES, "uploaded file contains binary data - file dropped");
253 if (!SUHOSIN_G(simulation)) {
254 goto continue_with_failure;
255 }
256 }
257 }
258 }
259
260 if (SUHOSIN_G(upload_remove_binary)) {
261
262 multipart_event_file_data *mefd = (multipart_event_file_data *) event_data;
263 size_t i, j;
264
265 for (i=0, j=0; i<mefd->length; i++) {
266 if (mefd->data[i] >= 32 || isspace(mefd->data[i])) {
267 mefd->data[j++] = mefd->data[i];
268 }
269 }
270 SDEBUG("removing binary %u %u",i,j);
271 /* IMPORTANT FOR DAISY CHAINING */
272 mefd->length = j;
273 if (mefd->newlength) {
274 *mefd->newlength = j;
275 }
276 }
277
278 break;
279
280 case MULTIPART_EVENT_FILE_END:
281
282 if (SUHOSIN_G(upload_verification_script)) {
283 multipart_event_file_end *mefe = (multipart_event_file_end *) event_data;
284 char cmd[8192];
285 FILE *in;
286 int first=1;
287 char *sname = SUHOSIN_G(upload_verification_script);
288
289 /* ignore files that will get deleted anyway */
290 if (mefe->cancel_upload) {
291 break;
292 }
293
294 /* ignore empty scriptnames */
295 while (isspace(*sname)) ++sname;
296 if (*sname == 0) {
297 SUHOSIN_G(num_uploads)++;
298 break;
299 }
300
301 ap_php_snprintf(cmd, sizeof(cmd), "%s %s", sname, mefe->temp_filename);
302
303 if ((in=VCWD_POPEN(cmd, "r"))==NULL) {
304 suhosin_log(S_FILES, "unable to execute fileupload verification script %s - file dropped", sname);
305 if (!SUHOSIN_G(simulation)) {
306 goto continue_with_failure;
307 } else {
308 goto continue_with_next;
309 }
310 }
311
312 retval = FAILURE;
313
314 /* read and forget the result */
315 while (1) {
316 int readbytes = fread(cmd, 1, sizeof(cmd), in);
317 if (readbytes<=0) {
318 break;
319 }
320 if (first) {
321 retval = atoi(cmd) == 1 ? SUCCESS : FAILURE;
322 first = 0;
323 }
324 }
325 pclose(in);
326 }
327
328 if (retval != SUCCESS) {
329 suhosin_log(S_FILES, "fileupload verification script disallows file - file dropped");
330 if (!SUHOSIN_G(simulation)) {
331 goto continue_with_failure;
332 }
333 }
334
335 SUHOSIN_G(num_uploads)++;
336 break;
337
338 case MULTIPART_EVENT_END:
339 /* nothing todo */
340 break;
341
342 default:
343 /* unknown: return failure */
344 goto continue_with_failure;
345 }
346continue_with_next:
347#if HAVE_RFC1867_CALLBACK
348 if (php_rfc1867_callback != NULL) {
349 return php_rfc1867_callback(event, event_data, extra TSRMLS_CC);
350 }
351#endif
352 return SUCCESS;
353continue_with_failure:
354 SUHOSIN_G(abort_request) = 1;
355 return FAILURE;
356}
357
358
359
360/*
361 * Local variables:
362 * tab-width: 4
363 * c-basic-offset: 4
364 * End:
365 * vim600: sw=4 ts=4 fdm=marker
366 * vim<600: sw=4 ts=4
367 */