summaryrefslogtreecommitdiff
path: root/ex_imp.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 /ex_imp.c
Inital commit
Diffstat (limited to 'ex_imp.c')
-rw-r--r--ex_imp.c450
1 files changed, 450 insertions, 0 deletions
diff --git a/ex_imp.c b/ex_imp.c
new file mode 100644
index 0000000..f602860
--- /dev/null
+++ b/ex_imp.c
@@ -0,0 +1,450 @@
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: ex_imp.c,v 1.2 2008-01-04 11:23:47 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 "php_suhosin.h"
30#include "ext/standard/php_smart_str.h"
31
32
33#define EXTR_OVERWRITE 0
34#define EXTR_SKIP 1
35#define EXTR_PREFIX_SAME 2
36#define EXTR_PREFIX_ALL 3
37#define EXTR_PREFIX_INVALID 4
38#define EXTR_PREFIX_IF_EXISTS 5
39#define EXTR_IF_EXISTS 6
40
41#define EXTR_REFS 0x100
42
43
44static int php_valid_var_name(char *var_name)
45{
46 int len, i;
47
48 if (!var_name)
49 return 0;
50
51 len = strlen(var_name);
52
53 if (!isalpha((int)((unsigned char *)var_name)[0]) && var_name[0] != '_')
54 return 0;
55
56 if (len > 1) {
57 for (i=1; i<len; i++) {
58 if (!isalnum((int)((unsigned char *)var_name)[i]) && var_name[i] != '_') {
59 return 0;
60 }
61 }
62 }
63
64 if (var_name[0] == 'H') {
65 if ((strcmp(var_name, "HTTP_GET_VARS")==0)||
66 (strcmp(var_name, "HTTP_POST_VARS")==0)||
67 (strcmp(var_name, "HTTP_POST_FILES")==0)||
68 (strcmp(var_name, "HTTP_ENV_VARS")==0)||
69 (strcmp(var_name, "HTTP_SERVER_VARS")==0)||
70 (strcmp(var_name, "HTTP_SESSION_VARS")==0)||
71 (strcmp(var_name, "HTTP_COOKIE_VARS")==0)||
72 (strcmp(var_name, "HTTP_RAW_POST_DATA")==0)) {
73 return 0;
74 }
75 } else if (var_name[0] == '_') {
76 if ((strcmp(var_name, "_COOKIE")==0)||
77 (strcmp(var_name, "_ENV")==0)||
78 (strcmp(var_name, "_FILES")==0)||
79 (strcmp(var_name, "_GET")==0)||
80 (strcmp(var_name, "_POST")==0)||
81 (strcmp(var_name, "_REQUEST")==0)||
82 (strcmp(var_name, "_SESSION")==0)||
83 (strcmp(var_name, "_SERVER")==0)) {
84 return 0;
85 }
86 } else if (strcmp(var_name, "GLOBALS")==0) {
87 return 0;
88 }
89
90 return 1;
91}
92
93
94/* {{{ proto int extract(array var_array [, int extract_type [, string prefix]])
95 Imports variables into symbol table from an array */
96PHP_FUNCTION(suhosin_extract)
97{
98 zval **var_array, **z_extract_type, **prefix;
99 zval **entry, *data;
100 char *var_name;
101 smart_str final_name = {0};
102 ulong num_key;
103 uint var_name_len;
104 int var_exists, extract_type, key_type, count = 0;
105 int extract_refs = 0;
106 HashPosition pos;
107
108 switch (ZEND_NUM_ARGS()) {
109 case 1:
110 if (zend_get_parameters_ex(1, &var_array) == FAILURE) {
111 WRONG_PARAM_COUNT;
112 }
113 extract_type = EXTR_OVERWRITE;
114 break;
115
116 case 2:
117 if (zend_get_parameters_ex(2, &var_array, &z_extract_type) == FAILURE) {
118 WRONG_PARAM_COUNT;
119 }
120 convert_to_long_ex(z_extract_type);
121 extract_type = Z_LVAL_PP(z_extract_type);
122 extract_refs = (extract_type & EXTR_REFS);
123 extract_type &= 0xff;
124 if (extract_type > EXTR_SKIP && extract_type <= EXTR_PREFIX_IF_EXISTS) {
125 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Prefix expected to be specified");
126 return;
127 }
128 break;
129
130 case 3:
131 if (zend_get_parameters_ex(3, &var_array, &z_extract_type, &prefix) == FAILURE) {
132 WRONG_PARAM_COUNT;
133 }
134 convert_to_long_ex(z_extract_type);
135 extract_type = Z_LVAL_PP(z_extract_type);
136 extract_refs = (extract_type & EXTR_REFS);
137 extract_type &= 0xff;
138 convert_to_string_ex(prefix);
139 break;
140
141 default:
142 WRONG_PARAM_COUNT;
143 break;
144 }
145
146#if PHP_VERSION_ID >= 50300
147 if (!EG(active_symbol_table)) {
148 zend_rebuild_symbol_table(TSRMLS_C);
149 }
150#endif
151
152 if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) {
153 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown extract type");
154 return;
155 }
156
157 if (Z_TYPE_PP(var_array) != IS_ARRAY) {
158 php_error_docref(NULL TSRMLS_CC, E_WARNING, "First argument should be an array");
159 return;
160 }
161
162 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(var_array), &pos);
163 while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(var_array), (void **)&entry, &pos) == SUCCESS) {
164 key_type = zend_hash_get_current_key_ex(Z_ARRVAL_PP(var_array), &var_name, &var_name_len, &num_key, 0, &pos);
165 var_exists = 0;
166
167 if (key_type == HASH_KEY_IS_STRING) {
168 var_name_len--;
169 var_exists = zend_hash_exists(EG(active_symbol_table), var_name, var_name_len + 1);
170 } else if (extract_type == EXTR_PREFIX_ALL || extract_type == EXTR_PREFIX_INVALID) {
171 smart_str_appendl(&final_name, Z_STRVAL_PP(prefix), Z_STRLEN_PP(prefix));
172 smart_str_appendc(&final_name, '_');
173 smart_str_append_long(&final_name, num_key);
174 } else {
175 zend_hash_move_forward_ex(Z_ARRVAL_PP(var_array), &pos);
176 continue;
177 }
178
179 switch (extract_type) {
180 case EXTR_IF_EXISTS:
181 if (!var_exists) break;
182 /* break omitted intentionally */
183
184 case EXTR_OVERWRITE:
185 /* GLOBALS protection */
186 if (var_exists && !strcmp(var_name, "GLOBALS")) {
187 break;
188 }
189 smart_str_appendl(&final_name, var_name, var_name_len);
190 break;
191
192 case EXTR_PREFIX_IF_EXISTS:
193 if (var_exists) {
194 smart_str_appendl(&final_name, Z_STRVAL_PP(prefix), Z_STRLEN_PP(prefix));
195 smart_str_appendc(&final_name, '_');
196 smart_str_appendl(&final_name, var_name, var_name_len);
197 }
198 break;
199
200 case EXTR_PREFIX_SAME:
201 if (!var_exists)
202 smart_str_appendl(&final_name, var_name, var_name_len);
203 /* break omitted intentionally */
204
205 case EXTR_PREFIX_ALL:
206 if (final_name.len == 0 && var_name_len != 0) {
207 smart_str_appendl(&final_name, Z_STRVAL_PP(prefix), Z_STRLEN_PP(prefix));
208 smart_str_appendc(&final_name, '_');
209 smart_str_appendl(&final_name, var_name, var_name_len);
210 }
211 break;
212
213 case EXTR_PREFIX_INVALID:
214 if (final_name.len == 0) {
215 if (!php_valid_var_name(var_name)) {
216 smart_str_appendl(&final_name, Z_STRVAL_PP(prefix), Z_STRLEN_PP(prefix));
217 smart_str_appendc(&final_name, '_');
218 smart_str_appendl(&final_name, var_name, var_name_len);
219 } else
220 smart_str_appendl(&final_name, var_name, var_name_len);
221 }
222 break;
223
224 default:
225 if (!var_exists)
226 smart_str_appendl(&final_name, var_name, var_name_len);
227 break;
228 }
229
230 if (final_name.len) {
231 smart_str_0(&final_name);
232 if (php_valid_var_name(final_name.c)) {
233 if (extract_refs) {
234 zval **orig_var;
235
236 if (zend_hash_find(EG(active_symbol_table), final_name.c, final_name.len+1, (void **) &orig_var) == SUCCESS) {
237 SEPARATE_ZVAL_TO_MAKE_IS_REF(entry);
238 zval_add_ref(entry);
239
240 zval_ptr_dtor(orig_var);
241
242 *orig_var = *entry;
243 } else {
244 if (Z_REFCOUNT_PP(var_array) > 1) {
245 SEPARATE_ZVAL_TO_MAKE_IS_REF(entry);
246 } else {
247 Z_SET_ISREF_PP(entry);
248 }
249 zval_add_ref(entry);
250 zend_hash_update(EG(active_symbol_table), final_name.c, final_name.len+1, (void **) entry, sizeof(zval *), NULL);
251 }
252 } else {
253 MAKE_STD_ZVAL(data);
254 *data = **entry;
255 zval_copy_ctor(data);
256
257 ZEND_SET_SYMBOL_WITH_LENGTH(EG(active_symbol_table), final_name.c, final_name.len+1, data, 1, 0);
258 }
259
260 count++;
261 }
262 final_name.len = 0;
263 }
264
265 zend_hash_move_forward_ex(Z_ARRVAL_PP(var_array), &pos);
266 }
267
268 smart_str_free(&final_name);
269
270 RETURN_LONG(count);
271}
272/* }}} */
273
274
275static int copy_request_variable(void *pDest, int num_args, va_list args, zend_hash_key *hash_key)
276{
277 char *prefix, *new_key;
278 uint prefix_len, new_key_len;
279 zval **var = (zval **) pDest;
280 TSRMLS_FETCH();
281
282 if (num_args != 2) {
283 return 0;
284 }
285
286 prefix = va_arg(args, char *);
287 prefix_len = va_arg(args, uint);
288
289 if (!prefix_len) {
290 if (!hash_key->nKeyLength) {
291 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Numeric key detected - possible security hazard.");
292 return 0;
293 } else if (!strcmp(hash_key->arKey, "GLOBALS")) {
294 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Attempted GLOBALS variable overwrite.");
295 return 0;
296 }
297 }
298
299 if (hash_key->nKeyLength) {
300 new_key_len = prefix_len + hash_key->nKeyLength;
301 new_key = (char *) emalloc(new_key_len);
302
303 memcpy(new_key, prefix, prefix_len);
304 memcpy(new_key+prefix_len, hash_key->arKey, hash_key->nKeyLength);
305 } else {
306 new_key_len = spprintf(&new_key, 0, "%s%ld", prefix, hash_key->h);
307 }
308
309 if (new_key[0] == 'H') {
310 if ((strcmp(new_key, "HTTP_GET_VARS")==0)||
311 (strcmp(new_key, "HTTP_POST_VARS")==0)||
312 (strcmp(new_key, "HTTP_POST_FILES")==0)||
313 (strcmp(new_key, "HTTP_ENV_VARS")==0)||
314 (strcmp(new_key, "HTTP_SERVER_VARS")==0)||
315 (strcmp(new_key, "HTTP_SESSION_VARS")==0)||
316 (strcmp(new_key, "HTTP_COOKIE_VARS")==0)||
317 (strcmp(new_key, "HTTP_RAW_POST_DATA")==0)) {
318 efree(new_key);
319 return 0;
320 }
321 } else if (new_key[0] == '_') {
322 if ((strcmp(new_key, "_COOKIE")==0)||
323 (strcmp(new_key, "_ENV")==0)||
324 (strcmp(new_key, "_FILES")==0)||
325 (strcmp(new_key, "_GET")==0)||
326 (strcmp(new_key, "_POST")==0)||
327 (strcmp(new_key, "_REQUEST")==0)||
328 (strcmp(new_key, "_SESSION")==0)||
329 (strcmp(new_key, "_SERVER")==0)) {
330 efree(new_key);
331 return 0;
332 }
333 } else if (strcmp(new_key, "GLOBALS")==0) {
334 efree(new_key);
335 return 0;
336 }
337
338#if PHP_MAJOR_VERSION > 5 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 0)
339 zend_delete_global_variable(new_key, new_key_len-1 TSRMLS_CC);
340#else
341 zend_hash_del(&EG(symbol_table), new_key, new_key_len-1);
342#endif
343 ZEND_SET_SYMBOL_WITH_LENGTH(&EG(symbol_table), new_key, new_key_len, *var, Z_REFCOUNT_PP(var)+1, 0);
344
345 efree(new_key);
346 return 0;
347}
348
349/* {{{ proto bool import_request_variables(string types [, string prefix])
350 Import GET/POST/Cookie variables into the global scope */
351PHP_FUNCTION(suhosin_import_request_variables)
352{
353 zval **z_types, **z_prefix;
354 char *types, *prefix;
355 uint prefix_len;
356 char *p;
357
358 switch (ZEND_NUM_ARGS()) {
359
360 case 1:
361 if (zend_get_parameters_ex(1, &z_types) == FAILURE) {
362 RETURN_FALSE;
363 }
364 prefix = "";
365 prefix_len = 0;
366 break;
367
368 case 2:
369 if (zend_get_parameters_ex(2, &z_types, &z_prefix) == FAILURE) {
370 RETURN_FALSE;
371 }
372 convert_to_string_ex(z_prefix);
373 prefix = Z_STRVAL_PP(z_prefix);
374 prefix_len = Z_STRLEN_PP(z_prefix);
375 break;
376
377 default:
378 ZEND_WRONG_PARAM_COUNT();
379 }
380
381 if (prefix_len == 0) {
382 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "No prefix specified - possible security hazard");
383 }
384
385 convert_to_string_ex(z_types);
386 types = Z_STRVAL_PP(z_types);
387
388 for (p = types; p && *p; p++) {
389 switch (*p) {
390
391 case 'g':
392 case 'G':
393 zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_GET]), (apply_func_args_t) copy_request_variable, 2, prefix, prefix_len);
394 break;
395
396 case 'p':
397 case 'P':
398 zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_POST]), (apply_func_args_t) copy_request_variable, 2, prefix, prefix_len);
399 zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_FILES]), (apply_func_args_t) copy_request_variable, 2, prefix, prefix_len);
400 break;
401
402 case 'c':
403 case 'C':
404 zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_COOKIE]), (apply_func_args_t) copy_request_variable, 2, prefix, prefix_len);
405 break;
406 }
407 }
408}
409/* }}} */
410
411
412/* {{{ suhosin_ex_imp_functions[]
413 */
414function_entry suhosin_ex_imp_functions[] = {
415 PHP_NAMED_FE(extract, PHP_FN(suhosin_extract), NULL)
416 PHP_NAMED_FE(import_request_variables, PHP_FN(suhosin_import_request_variables), NULL)
417 {NULL, NULL, NULL}
418};
419/* }}} */
420
421
422void suhosin_hook_ex_imp()
423{
424 TSRMLS_FETCH();
425
426 /* replace the extract and import_request_variables functions */
427 zend_hash_del(CG(function_table), "extract", sizeof("extract"));
428 zend_hash_del(CG(function_table), "import_request_variables", sizeof("import_request_variables"));
429#ifndef ZEND_ENGINE_2
430 zend_register_functions(suhosin_ex_imp_functions, NULL, MODULE_PERSISTENT TSRMLS_CC);
431#else
432 zend_register_functions(NULL, suhosin_ex_imp_functions, NULL, MODULE_PERSISTENT TSRMLS_CC);
433#endif
434
435
436
437
438}
439
440
441/*
442 * Local variables:
443 * tab-width: 4
444 * c-basic-offset: 4
445 * End:
446 * vim600: noet sw=4 ts=4 fdm=marker
447 * vim<600: noet sw=4 ts=4
448 */
449
450