From 6ee8bb0027657b66660d0ff7d91a9a99ce25a3ef Mon Sep 17 00:00:00 2001 From: Stefan Date: Wed, 24 Mar 2010 15:14:36 +0100 Subject: Merged changes/fixes in import_request_variables()/extract() from upstream PHP --- ex_imp.c | 357 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 327 insertions(+), 30 deletions(-) diff --git a/ex_imp.c b/ex_imp.c index b260bb1..0241ca5 100644 --- a/ex_imp.c +++ b/ex_imp.c @@ -41,21 +41,33 @@ #define EXTR_REFS 0x100 -static int php_valid_var_name(char *var_name) +static int php_valid_var_name(char *var_name, int len) /* {{{ */ { - int len, i; + int i, ch; if (!var_name) return 0; - - len = strlen(var_name); - - if (!isalpha((int)((unsigned char *)var_name)[0]) && var_name[0] != '_') + + /* These are allowed as first char: [a-zA-Z_\x7f-\xff] */ + ch = (int)((unsigned char *)var_name)[0]; + if (var_name[0] != '_' && + (ch < 65 /* A */ || /* Z */ ch > 90) && + (ch < 97 /* a */ || /* z */ ch > 122) && + (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255) + ) { return 0; - + } + + /* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */ if (len > 1) { - for (i=1; i 57) && + (ch < 65 /* A */ || /* Z */ ch > 90) && + (ch < 97 /* a */ || /* z */ ch > 122) && + (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255) + ) { return 0; } } @@ -95,7 +107,162 @@ static int php_valid_var_name(char *var_name) Imports variables into symbol table from an array */ PHP_FUNCTION(suhosin_extract) { - zval **var_array, **z_extract_type, **prefix; +#if PHP_VERSION_ID >= 50300 + zval *var_array, *prefix = NULL; + long extract_type = EXTR_OVERWRITE; + zval **entry, *data; + char *var_name; + ulong num_key; + uint var_name_len; + int var_exists, key_type, count = 0; + int extract_refs = 0; + HashPosition pos; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|lz/", &var_array, &extract_type, &prefix) == FAILURE) { + return; + } + + extract_refs = (extract_type & EXTR_REFS); + extract_type &= 0xff; + + if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid extract type"); + return; + } + + if (extract_type > EXTR_SKIP && extract_type <= EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "specified extract type requires the prefix parameter"); + return; + } + + if (prefix) { + convert_to_string(prefix); + if (Z_STRLEN_P(prefix) && !php_valid_var_name(Z_STRVAL_P(prefix), Z_STRLEN_P(prefix))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "prefix is not a valid identifier"); + return; + } + } + + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + } + + /* var_array is passed by ref for the needs of EXTR_REFS (needs to + * work on the original array to create refs to its members) + * simulate pass_by_value if EXTR_REFS is not used */ + if (!extract_refs) { + SEPARATE_ARG_IF_REF(var_array); + } + + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(var_array), &pos); + while (zend_hash_get_current_data_ex(Z_ARRVAL_P(var_array), (void **)&entry, &pos) == SUCCESS) { + zval final_name; + + ZVAL_NULL(&final_name); + + key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(var_array), &var_name, &var_name_len, &num_key, 0, &pos); + var_exists = 0; + + if (key_type == HASH_KEY_IS_STRING) { + var_name_len--; + var_exists = zend_hash_exists(EG(active_symbol_table), var_name, var_name_len + 1); + } else if (key_type == HASH_KEY_IS_LONG && (extract_type == EXTR_PREFIX_ALL || extract_type == EXTR_PREFIX_INVALID)) { + zval num; + + ZVAL_LONG(&num, num_key); + convert_to_string(&num); + php_prefix_varname(&final_name, prefix, Z_STRVAL(num), Z_STRLEN(num), 1 TSRMLS_CC); + zval_dtor(&num); + } else { + zend_hash_move_forward_ex(Z_ARRVAL_P(var_array), &pos); + continue; + } + + switch (extract_type) { + case EXTR_IF_EXISTS: + if (!var_exists) break; + /* break omitted intentionally */ + + case EXTR_OVERWRITE: + /* GLOBALS protection */ + if (var_exists && var_name_len == sizeof("GLOBALS") && !strcmp(var_name, "GLOBALS")) { + break; + } + if (var_exists && var_name_len == sizeof("this") && !strcmp(var_name, "this") && EG(scope) && EG(scope)->name_length != 0) { + break; + } + ZVAL_STRINGL(&final_name, var_name, var_name_len, 1); + break; + + case EXTR_PREFIX_IF_EXISTS: + if (var_exists) { + php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC); + } + break; + + case EXTR_PREFIX_SAME: + if (!var_exists && var_name_len != 0) { + ZVAL_STRINGL(&final_name, var_name, var_name_len, 1); + } + /* break omitted intentionally */ + + case EXTR_PREFIX_ALL: + if (Z_TYPE(final_name) == IS_NULL && var_name_len != 0) { + php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC); + } + break; + + case EXTR_PREFIX_INVALID: + if (Z_TYPE(final_name) == IS_NULL) { + if (!php_valid_var_name(var_name, var_name_len)) { + php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC); + } else { + ZVAL_STRINGL(&final_name, var_name, var_name_len, 1); + } + } + break; + + default: + if (!var_exists) { + ZVAL_STRINGL(&final_name, var_name, var_name_len, 1); + } + break; + } + + if (Z_TYPE(final_name) != IS_NULL && php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (extract_refs) { + zval **orig_var; + + SEPARATE_ZVAL_TO_MAKE_IS_REF(entry); + zval_add_ref(entry); + + if (zend_hash_find(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, (void **) &orig_var) == SUCCESS) { + zval_ptr_dtor(orig_var); + *orig_var = *entry; + } else { + zend_hash_update(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, (void **) entry, sizeof(zval *), NULL); + } + } else { + MAKE_STD_ZVAL(data); + *data = **entry; + zval_copy_ctor(data); + + ZEND_SET_SYMBOL_WITH_LENGTH(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, data, 1, 0); + } + count++; + } + zval_dtor(&final_name); + + zend_hash_move_forward_ex(Z_ARRVAL_P(var_array), &pos); + } + + if (!extract_refs) { + zval_ptr_dtor(&var_array); + } + + RETURN_LONG(count); +#else + zval **var_array, *orig_var_array, **z_extract_type, **prefix; zval **entry, *data; char *var_name; smart_str final_name = {0}; @@ -143,12 +310,6 @@ PHP_FUNCTION(suhosin_extract) break; } -#if PHP_VERSION_ID >= 50300 - if (!EG(active_symbol_table)) { - zend_rebuild_symbol_table(TSRMLS_C); - } -#endif - if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown extract type"); return; @@ -158,7 +319,15 @@ PHP_FUNCTION(suhosin_extract) php_error_docref(NULL TSRMLS_CC, E_WARNING, "First argument should be an array"); return; } - + + /* var_array is passed by ref for the needs of EXTR_REFS (needs to + * work on the original array to create refs to its members) + * simulate pass_by_value if EXTR_REFS is not used */ + if (!extract_refs) { + orig_var_array = *var_array; + SEPARATE_ARG_IF_REF((*var_array)); + } + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(var_array), &pos); while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(var_array), (void **)&entry, &pos) == SUCCESS) { key_type = zend_hash_get_current_key_ex(Z_ARRVAL_PP(var_array), &var_name, &var_name_len, &num_key, 0, &pos); @@ -183,7 +352,10 @@ PHP_FUNCTION(suhosin_extract) case EXTR_OVERWRITE: /* GLOBALS protection */ - if (var_exists && !strcmp(var_name, "GLOBALS")) { + if (var_exists && var_name_len == sizeof("GLOBALS") && !strcmp(var_name, "GLOBALS")) { + break; + } + if (var_exists && var_name_len == sizeof("this") && !strcmp(var_name, "this") && EG(scope) && EG(scope)->name_length != 0) { break; } smart_str_appendl(&final_name, var_name, var_name_len); @@ -212,7 +384,7 @@ PHP_FUNCTION(suhosin_extract) case EXTR_PREFIX_INVALID: if (final_name.len == 0) { - if (!php_valid_var_name(var_name)) { + if (!php_valid_var_name(var_name, var_name_len)) { smart_str_appendl(&final_name, Z_STRVAL_PP(prefix), Z_STRLEN_PP(prefix)); smart_str_appendc(&final_name, '_'); smart_str_appendl(&final_name, var_name, var_name_len); @@ -229,24 +401,17 @@ PHP_FUNCTION(suhosin_extract) if (final_name.len) { smart_str_0(&final_name); - if (php_valid_var_name(final_name.c)) { + if (php_valid_var_name(final_name.c, final_name.len)) { if (extract_refs) { zval **orig_var; + SEPARATE_ZVAL_TO_MAKE_IS_REF(entry); + zval_add_ref(entry); + if (zend_hash_find(EG(active_symbol_table), final_name.c, final_name.len+1, (void **) &orig_var) == SUCCESS) { - SEPARATE_ZVAL_TO_MAKE_IS_REF(entry); - zval_add_ref(entry); - zval_ptr_dtor(orig_var); - *orig_var = *entry; } else { - if (Z_REFCOUNT_PP(var_array) > 1) { - SEPARATE_ZVAL_TO_MAKE_IS_REF(entry); - } else { - Z_SET_ISREF_PP(entry); - } - zval_add_ref(entry); zend_hash_update(EG(active_symbol_table), final_name.c, final_name.len+1, (void **) entry, sizeof(zval *), NULL); } } else { @@ -265,15 +430,88 @@ PHP_FUNCTION(suhosin_extract) zend_hash_move_forward_ex(Z_ARRVAL_PP(var_array), &pos); } + if (!extract_refs) { + zval_ptr_dtor(var_array); + *var_array = orig_var_array; + } smart_str_free(&final_name); RETURN_LONG(count); +#endif } /* }}} */ static int copy_request_variable(void *pDest, int num_args, va_list args, zend_hash_key *hash_key) { +#if PHP_VERSION_ID >= 50300 + zval *prefix, new_key; + int prefix_len; + zval **var = (zval **) pDest; + + if (num_args != 1) { + return 0; + } + + prefix = va_arg(args, zval *); + prefix_len = Z_STRLEN_P(prefix); + + if (!prefix_len && !hash_key->nKeyLength) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Numeric key detected - possible security hazard"); + return 0; + } + + if (hash_key->nKeyLength) { + php_prefix_varname(&new_key, prefix, hash_key->arKey, hash_key->nKeyLength - 1, 0 TSRMLS_CC); + } else { + zval num; + + ZVAL_LONG(&num, hash_key->h); + convert_to_string(&num); + php_prefix_varname(&new_key, prefix, Z_STRVAL(num), Z_STRLEN(num), 0 TSRMLS_CC); + zval_dtor(&num); + } + + if (php_varname_check(Z_STRVAL(new_key), Z_STRLEN(new_key), 0 TSRMLS_CC) == FAILURE) { + zval_dtor(&new_key); + return 0; + } + + if (Z_STRVAL(new_key)[0] == 'H') { + if ((strcmp(Z_STRVAL(new_key), "HTTP_GET_VARS")==0)|| + (strcmp(Z_STRVAL(new_key), "HTTP_POST_VARS")==0)|| + (strcmp(Z_STRVAL(new_key), "HTTP_POST_FILES")==0)|| + (strcmp(Z_STRVAL(new_key), "HTTP_ENV_VARS")==0)|| + (strcmp(Z_STRVAL(new_key), "HTTP_SERVER_VARS")==0)|| + (strcmp(Z_STRVAL(new_key), "HTTP_SESSION_VARS")==0)|| + (strcmp(Z_STRVAL(new_key), "HTTP_COOKIE_VARS")==0)|| + (strcmp(Z_STRVAL(new_key), "HTTP_RAW_POST_DATA")==0)) { + zval_dtor(&new_key); + return 0; + } + } else if (new_key[0] == '_') { + if ((strcmp(Z_STRVAL(new_key), "_COOKIE")==0)|| + (strcmp(Z_STRVAL(new_key), "_ENV")==0)|| + (strcmp(Z_STRVAL(new_key), "_FILES")==0)|| + (strcmp(Z_STRVAL(new_key), "_GET")==0)|| + (strcmp(Z_STRVAL(new_key), "_POST")==0)|| + (strcmp(Z_STRVAL(new_key), "_REQUEST")==0)|| + (strcmp(Z_STRVAL(new_key), "_SESSION")==0)|| + (strcmp(Z_STRVAL(new_key), "_SERVER")==0)) { + zval_dtor(&new_key); + return 0; + } + } else if (strcmp(Z_STRVAL(new_key), "GLOBALS")==0) { + zval_dtor(&new_key); + return 0; + } + + zend_delete_global_variable(Z_STRVAL(new_key), Z_STRLEN(new_key) TSRMLS_CC); + ZEND_SET_SYMBOL_WITH_LENGTH(&EG(symbol_table), Z_STRVAL(new_key), Z_STRLEN(new_key) + 1, *var, Z_REFCOUNT_PP(var) + 1, 0); + + zval_dtor(&new_key); + return 0; +#else char *prefix, *new_key; uint prefix_len, new_key_len; zval **var = (zval **) pDest; @@ -304,6 +542,7 @@ static int copy_request_variable(void *pDest, int num_args, va_list args, zend_h memcpy(new_key+prefix_len, hash_key->arKey, hash_key->nKeyLength); } else { new_key_len = spprintf(&new_key, 0, "%s%ld", prefix, hash_key->h); + new_key_len++; } if (new_key[0] == 'H') { @@ -344,16 +583,69 @@ static int copy_request_variable(void *pDest, int num_args, va_list args, zend_h efree(new_key); return 0; +#endif } /* {{{ proto bool import_request_variables(string types [, string prefix]) Import GET/POST/Cookie variables into the global scope */ PHP_FUNCTION(suhosin_import_request_variables) { +#if PHP_VERSION_ID >= 50300 + char *types; + int types_len; + zval *prefix = NULL; + char *p; + zend_bool ok = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z/", &types, &types_len, &prefix) == FAILURE) { + return; + } + + if (ZEND_NUM_ARGS() > 1) { + convert_to_string(prefix); + + if (Z_STRLEN_P(prefix) == 0) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "No prefix specified - possible security hazard"); + } + } else { + MAKE_STD_ZVAL(prefix); + ZVAL_EMPTY_STRING(prefix); + } + + for (p = types; p && *p; p++) { + switch (*p) { + + case 'g': + case 'G': + zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_GET]) TSRMLS_CC, (apply_func_args_t) copy_request_variable, 1, prefix); + ok = 1; + break; + + case 'p': + case 'P': + zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_POST]) TSRMLS_CC, (apply_func_args_t) copy_request_variable, 1, prefix); + zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_FILES]) TSRMLS_CC, (apply_func_args_t) copy_request_variable, 1, prefix); + ok = 1; + break; + + case 'c': + case 'C': + zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_COOKIE]) TSRMLS_CC, (apply_func_args_t) copy_request_variable, 1, prefix); + ok = 1; + break; + } + } + + if (ZEND_NUM_ARGS() < 2) { + zval_ptr_dtor(&prefix); + } + RETURN_BOOL(ok); +#else zval **z_types, **z_prefix; char *types, *prefix; uint prefix_len; char *p; + zend_bool ok = 0; switch (ZEND_NUM_ARGS()) { @@ -391,20 +683,25 @@ PHP_FUNCTION(suhosin_import_request_variables) case 'g': case 'G': 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); + ok = 1; break; case 'p': case 'P': 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); 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); + ok = 1; break; case 'c': case 'C': 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); + ok = 1; break; } } + RETURN_BOOL(ok); +#endif } /* }}} */ -- cgit v1.3