diff options
Diffstat (limited to 'ext/session/session.c')
-rw-r--r-- | ext/session/session.c | 474 |
1 files changed, 182 insertions, 292 deletions
diff --git a/ext/session/session.c b/ext/session/session.c index daea59c4ff..8d60ac249a 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 7 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2017 The PHP Group | + | Copyright (c) 1997-2018 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -40,13 +40,11 @@ #include "rfc1867.h" #include "php_variables.h" #include "php_session.h" -#include "ext/standard/md5.h" -#include "ext/standard/sha1.h" +#include "ext/standard/php_random.h" #include "ext/standard/php_var.h" #include "ext/date/php_date.h" #include "ext/standard/php_lcg.h" #include "ext/standard/url_scanner_ex.h" -#include "ext/standard/php_rand.h" /* for RAND_MAX */ #include "ext/standard/info.h" #include "zend_smart_str.h" #include "ext/standard/url.h" @@ -81,6 +79,8 @@ zend_class_entry *php_session_update_timestamp_class_entry; /* SessionUpdateTimestampInterface */ zend_class_entry *php_session_update_timestamp_iface_entry; +#define PS_MAX_SID_LENGTH 256 + /* *********** * Helpers * *********** */ @@ -97,6 +97,7 @@ zend_class_entry *php_session_update_timestamp_iface_entry; #define APPLY_TRANS_SID (PS(use_trans_sid) && !PS(use_only_cookies)) static void php_session_send_cookie(void); +static void php_session_abort(void); /* Initialized in MINIT, readonly otherwise. */ static int my_module_number = 0; @@ -105,8 +106,10 @@ static int my_module_number = 0; static inline void php_rinit_session_globals(void) /* {{{ */ { /* Do NOT init PS(mod_user_names) here! */ + /* TODO: These could be moved to MINIT and removed. These should be initialized by php_rshutdown_session_globals() always when execution is finished. */ PS(id) = NULL; PS(session_status) = php_session_none; + PS(in_save_handler) = 0; PS(mod_data) = NULL; PS(mod_user_is_open) = 0; PS(define_sid) = 1; @@ -133,10 +136,15 @@ static inline void php_rshutdown_session_globals(void) /* {{{ */ zend_string_release(PS(id)); PS(id) = NULL; } + if (PS(session_vars)) { zend_string_release(PS(session_vars)); PS(session_vars) = NULL; } + + /* User save handlers may end up directly here by misuse, bugs in user script, etc. */ + /* Set session status to prevent error while restoring save handler INI value. */ + PS(session_status) = php_session_none; } /* }}} */ @@ -253,17 +261,12 @@ static int php_session_decode(zend_string *data) /* {{{ */ static char hexconvtab[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,-"; -enum { - PS_HASH_FUNC_MD5, - PS_HASH_FUNC_SHA1, - PS_HASH_FUNC_OTHER -}; - /* returns a pointer to the byte after the last valid character in out */ -static char *bin_to_readable(char *in, size_t inlen, char *out, char nbits) /* {{{ */ +static size_t bin_to_readable(unsigned char *in, size_t inlen, char *out, char nbits) /* {{{ */ { unsigned char *p, *q; unsigned short w; + size_t len = inlen; int mask; int have; @@ -274,7 +277,7 @@ static char *bin_to_readable(char *in, size_t inlen, char *out, char nbits) /* { have = 0; mask = (1 << nbits) - 1; - while (1) { + while (inlen--) { if (have < nbits) { if (p < q) { w |= *p++ << have; @@ -294,151 +297,24 @@ static char *bin_to_readable(char *in, size_t inlen, char *out, char nbits) /* { } *out = '\0'; - return out; + return len; } /* }}} */ +#define PS_EXTRA_RAND_BYTES 60 + PHPAPI zend_string *php_session_create_id(PS_CREATE_SID_ARGS) /* {{{ */ { - PHP_MD5_CTX md5_context; - PHP_SHA1_CTX sha1_context; -#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH) - void *hash_context = NULL; -#endif - unsigned char *digest; - size_t digest_len; - char *buf; - struct timeval tv; - zval *array; - zval *token; + unsigned char rbuf[PS_MAX_SID_LENGTH + PS_EXTRA_RAND_BYTES]; zend_string *outid; - char *remote_addr = NULL; - - gettimeofday(&tv, NULL); - if ((array = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER") - 1)) && - Z_TYPE_P(array) == IS_ARRAY && - (token = zend_hash_str_find(Z_ARRVAL_P(array), "REMOTE_ADDR", sizeof("REMOTE_ADDR") - 1)) && - Z_TYPE_P(token) == IS_STRING - ) { - remote_addr = Z_STRVAL_P(token); + /* Read additional PS_EXTRA_RAND_BYTES just in case CSPRNG is not safe enough */ + if (php_random_bytes_throw(rbuf, PS(sid_length) + PS_EXTRA_RAND_BYTES) == FAILURE) { + return NULL; } - /* maximum 15+19+19+10 bytes */ - spprintf(&buf, 0, "%.15s%ld" ZEND_LONG_FMT "%0.8F", remote_addr ? remote_addr : "", tv.tv_sec, (zend_long)tv.tv_usec, php_combined_lcg() * 10); - - switch (PS(hash_func)) { - case PS_HASH_FUNC_MD5: - PHP_MD5Init(&md5_context); - PHP_MD5Update(&md5_context, (unsigned char *) buf, strlen(buf)); - digest_len = 16; - break; - case PS_HASH_FUNC_SHA1: - PHP_SHA1Init(&sha1_context); - PHP_SHA1Update(&sha1_context, (unsigned char *) buf, strlen(buf)); - digest_len = 20; - break; -#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH) - case PS_HASH_FUNC_OTHER: - if (!PS(hash_ops)) { - efree(buf); - php_error_docref(NULL, E_ERROR, "Invalid session hash function"); - return NULL; - } - - hash_context = emalloc(PS(hash_ops)->context_size); - PS(hash_ops)->hash_init(hash_context); - PS(hash_ops)->hash_update(hash_context, (unsigned char *) buf, strlen(buf)); - digest_len = PS(hash_ops)->digest_size; - break; -#endif /* HAVE_HASH_EXT */ - default: - efree(buf); - php_error_docref(NULL, E_ERROR, "Invalid session hash function"); - return NULL; - } - efree(buf); - - if (PS(entropy_length) > 0) { -#ifdef PHP_WIN32 - unsigned char rbuf[2048]; - size_t toread = PS(entropy_length); - - if (php_win32_get_random_bytes(rbuf, MIN(toread, sizeof(rbuf))) == SUCCESS){ - - switch (PS(hash_func)) { - case PS_HASH_FUNC_MD5: - PHP_MD5Update(&md5_context, rbuf, toread); - break; - case PS_HASH_FUNC_SHA1: - PHP_SHA1Update(&sha1_context, rbuf, toread); - break; -# if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH) - case PS_HASH_FUNC_OTHER: - PS(hash_ops)->hash_update(hash_context, rbuf, toread); - break; -# endif /* HAVE_HASH_EXT */ - } - } -#else - int fd; - - fd = VCWD_OPEN(PS(entropy_file), O_RDONLY); - if (fd >= 0) { - unsigned char rbuf[2048]; - int n; - int to_read = PS(entropy_length); - - while (to_read > 0) { - n = read(fd, rbuf, MIN(to_read, sizeof(rbuf))); - if (n <= 0) break; - - switch (PS(hash_func)) { - case PS_HASH_FUNC_MD5: - PHP_MD5Update(&md5_context, rbuf, n); - break; - case PS_HASH_FUNC_SHA1: - PHP_SHA1Update(&sha1_context, rbuf, n); - break; -#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH) - case PS_HASH_FUNC_OTHER: - PS(hash_ops)->hash_update(hash_context, rbuf, n); - break; -#endif /* HAVE_HASH_EXT */ - } - to_read -= n; - } - close(fd); - } -#endif - } - - digest = emalloc(digest_len + 1); - switch (PS(hash_func)) { - case PS_HASH_FUNC_MD5: - PHP_MD5Final(digest, &md5_context); - break; - case PS_HASH_FUNC_SHA1: - PHP_SHA1Final(digest, &sha1_context); - break; -#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH) - case PS_HASH_FUNC_OTHER: - PS(hash_ops)->hash_final(digest, hash_context); - efree(hash_context); - break; -#endif /* HAVE_HASH_EXT */ - } - - if (PS(hash_bits_per_character) < 4 - || PS(hash_bits_per_character) > 6) { - PS(hash_bits_per_character) = 4; - - php_error_docref(NULL, E_WARNING, "The ini setting hash_bits_per_character is out of range (should be 4, 5, or 6) - using 4 for now"); - } - - outid = zend_string_alloc((digest_len + 2) * ((8.0f / PS(hash_bits_per_character) + 0.5)), 0); - ZSTR_LEN(outid) = (size_t)(bin_to_readable((char *)digest, digest_len, ZSTR_VAL(outid), (char)PS(hash_bits_per_character)) - (char *)&ZSTR_VAL(outid)); - efree(digest); + outid = zend_string_alloc(PS(sid_length), 0); + ZSTR_LEN(outid) = bin_to_readable(rbuf, PS(sid_length), ZSTR_VAL(outid), (char)PS(sid_bits_per_character)); return outid; } @@ -470,7 +346,7 @@ PHPAPI int php_session_valid_key(const char *key) /* {{{ */ /* Somewhat arbitrary length limit here, but should be way more than anyone needs and avoids file-level warnings later on if we exceed MAX_PATH */ - if (len == 0 || len > 128) { + if (len == 0 || len > PS_MAX_SID_LENGTH) { ret = FAILURE; } @@ -479,31 +355,33 @@ PHPAPI int php_session_valid_key(const char *key) /* {{{ */ /* }}} */ -static void php_session_gc(void) /* {{{ */ +static zend_long php_session_gc(zend_bool immediate) /* {{{ */ { int nrand; + zend_long num = -1; /* GC must be done before reading session data. */ - if ((PS(mod_data) || PS(mod_user_implemented)) && PS(gc_probability) > 0) { - int nrdels = -1; - - nrand = (int) ((float) PS(gc_divisor) * php_combined_lcg()); - if (nrand < PS(gc_probability)) { - PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &nrdels); -#ifdef SESSION_DEBUG - if (nrdels != -1) { - php_error_docref(NULL, E_NOTICE, "purged %d expired session objects", nrdels); - } -#endif + if ((PS(mod_data) || PS(mod_user_implemented))) { + if (immediate) { + PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &num); + return num; + } + nrand = (zend_long) ((float) PS(gc_divisor) * php_combined_lcg()); + if (PS(gc_probability) > 0 && nrand < PS(gc_probability)) { + PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &num); } } + return num; } /* }}} */ static void php_session_initialize(void) /* {{{ */ { zend_string *val = NULL; + PS(session_status) = php_session_active; + if (!PS(mod)) { + PS(session_status) = php_session_disabled; php_error_docref(NULL, E_ERROR, "No storage module chosen - failed to initialize session"); return; } @@ -512,15 +390,20 @@ static void php_session_initialize(void) /* {{{ */ if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name)) == FAILURE /* || PS(mod_data) == NULL */ /* FIXME: open must set valid PS(mod_data) with success */ ) { + php_session_abort(); php_error_docref(NULL, E_ERROR, "Failed to initialize storage module: %s (path: %s)", PS(mod)->s_name, PS(save_path)); return; } /* If there is no ID, use session module to create one */ - if (!PS(id)) { + if (!PS(id) || !ZSTR_VAL(PS(id))[0]) { + if (PS(id)) { + zend_string_release(PS(id)); + } PS(id) = PS(mod)->s_create_sid(&PS(mod_data)); if (!PS(id)) { - php_error_docref(NULL, E_ERROR, "Failed to create session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path)); + php_session_abort(); + zend_throw_error(NULL, "Failed to create session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path)); return; } if (PS(use_cookies)) { @@ -541,20 +424,18 @@ static void php_session_initialize(void) /* {{{ */ } php_session_reset_id(); - PS(session_status) = php_session_active; /* Read data */ php_session_track_init(); if (PS(mod)->s_read(&PS(mod_data), PS(id), &val, PS(gc_maxlifetime)) == FAILURE) { - /* Some broken save handler implementation returns FAILURE for non-existent session ID */ - /* It's better to raise error for this, but disabled error for better compatibility */ - /* - php_error_docref(NULL, E_NOTICE, "Failed to read session data: %s (path: %s)", PS(mod)->s_name, PS(save_path)); - */ + php_session_abort(); + /* FYI: Some broken save handlers return FAILURE for non-existent session ID, this is incorrect */ + php_error_docref(NULL, E_WARNING, "Failed to read session data: %s (path: %s)", PS(mod)->s_name, PS(save_path)); + return; } /* GC must be done after read */ - php_session_gc(); + php_session_gc(0); if (PS(session_vars)) { zend_string_release(PS(session_vars)); @@ -598,11 +479,16 @@ static void php_session_save_current_state(int write) /* {{{ */ } if ((ret == FAILURE) && !EG(exception)) { - php_error_docref(NULL, E_WARNING, "Failed to write session data (%s). Please " - "verify that the current setting of session.save_path " - "is correct (%s)", - PS(mod)->s_name, - PS(save_path)); + if (!PS(mod_user_implemented)) { + php_error_docref(NULL, E_WARNING, "Failed to write session data (%s). Please " + "verify that the current setting of session.save_path " + "is correct (%s)", + PS(mod)->s_name, + PS(save_path)); + } else { + php_error_docref(NULL, E_WARNING, "Failed to write session data using user " + "defined save handler. (session.save_path: %s)", PS(save_path)); + } } } } @@ -760,55 +646,43 @@ static PHP_INI_MH(OnUpdateName) /* {{{ */ } /* }}} */ -static PHP_INI_MH(OnUpdateHashFunc) /* {{{ */ +static PHP_INI_MH(OnUpdateSidLength) /* {{{ */ { zend_long val; char *endptr = NULL; -#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH) - PS(hash_ops) = NULL; -#endif - val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10); - if (endptr && (*endptr == '\0')) { + if (endptr && (*endptr == '\0') + && val >= 22 && val <= PS_MAX_SID_LENGTH) { /* Numeric value */ - PS(hash_func) = val ? 1 : 0; - - return SUCCESS; - } - - if (ZSTR_LEN(new_value) == (sizeof("md5") - 1) && - strncasecmp(ZSTR_VAL(new_value), "md5", sizeof("md5") - 1) == 0) { - PS(hash_func) = PS_HASH_FUNC_MD5; - + PS(sid_length) = val; return SUCCESS; } - if (ZSTR_LEN(new_value) == (sizeof("sha1") - 1) && - strncasecmp(ZSTR_VAL(new_value), "sha1", sizeof("sha1") - 1) == 0) { - PS(hash_func) = PS_HASH_FUNC_SHA1; - - return SUCCESS; - } + php_error_docref(NULL, E_WARNING, "session.configuration 'session.sid_length' must be between 22 and 256."); + return FAILURE; +} +/* }}} */ -#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH) /* {{{ */ +static PHP_INI_MH(OnUpdateSidBits) /* {{{ */ { - php_hash_ops *ops = (php_hash_ops*)php_hash_fetch_ops(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); - - if (ops) { - PS(hash_func) = PS_HASH_FUNC_OTHER; - PS(hash_ops) = ops; + zend_long val; + char *endptr = NULL; + val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10); + if (endptr && (*endptr == '\0') + && val >= 4 && val <=6) { + /* Numeric value */ + PS(sid_bits_per_character) = val; return SUCCESS; } -} -#endif /* HAVE_HASH_EXT }}} */ - php_error_docref(NULL, E_WARNING, "session.configuration 'session.hash_function' must be existing hash function. %s does not exist.", ZSTR_VAL(new_value)); + php_error_docref(NULL, E_WARNING, "session.configuration 'session.sid_bits' must be between 4 and 6."); return FAILURE; } /* }}} */ + static PHP_INI_MH(OnUpdateRfc1867Freq) /* {{{ */ { int tmp; @@ -849,21 +723,11 @@ PHP_INI_BEGIN() STD_PHP_INI_BOOLEAN("session.use_only_cookies", "1", PHP_INI_ALL, OnUpdateBool, use_only_cookies, php_ps_globals, ps_globals) STD_PHP_INI_BOOLEAN("session.use_strict_mode", "0", PHP_INI_ALL, OnUpdateBool, use_strict_mode, php_ps_globals, ps_globals) STD_PHP_INI_ENTRY("session.referer_check", "", PHP_INI_ALL, OnUpdateString, extern_referer_chk, php_ps_globals, ps_globals) -#if HAVE_DEV_URANDOM - STD_PHP_INI_ENTRY("session.entropy_file", "/dev/urandom", PHP_INI_ALL, OnUpdateString, entropy_file, php_ps_globals, ps_globals) - STD_PHP_INI_ENTRY("session.entropy_length", "32", PHP_INI_ALL, OnUpdateLong, entropy_length, php_ps_globals, ps_globals) -#elif HAVE_DEV_ARANDOM - STD_PHP_INI_ENTRY("session.entropy_file", "/dev/arandom", PHP_INI_ALL, OnUpdateString, entropy_file, php_ps_globals, ps_globals) - STD_PHP_INI_ENTRY("session.entropy_length", "32", PHP_INI_ALL, OnUpdateLong, entropy_length, php_ps_globals, ps_globals) -#else - STD_PHP_INI_ENTRY("session.entropy_file", "", PHP_INI_ALL, OnUpdateString, entropy_file, php_ps_globals, ps_globals) - STD_PHP_INI_ENTRY("session.entropy_length", "0", PHP_INI_ALL, OnUpdateLong, entropy_length, php_ps_globals, ps_globals) -#endif STD_PHP_INI_ENTRY("session.cache_limiter", "nocache", PHP_INI_ALL, OnUpdateString, cache_limiter, php_ps_globals, ps_globals) STD_PHP_INI_ENTRY("session.cache_expire", "180", PHP_INI_ALL, OnUpdateLong, cache_expire, php_ps_globals, ps_globals) PHP_INI_ENTRY("session.use_trans_sid", "0", PHP_INI_ALL, OnUpdateTransSid) - PHP_INI_ENTRY("session.hash_function", "0", PHP_INI_ALL, OnUpdateHashFunc) - STD_PHP_INI_ENTRY("session.hash_bits_per_character", "4", PHP_INI_ALL, OnUpdateLong, hash_bits_per_character, php_ps_globals, ps_globals) + PHP_INI_ENTRY("session.sid_length", "32", PHP_INI_ALL, OnUpdateSidLength) + PHP_INI_ENTRY("session.sid_bits_per_character", "4", PHP_INI_ALL, OnUpdateSidBits) STD_PHP_INI_BOOLEAN("session.lazy_write", "1", PHP_INI_ALL, OnUpdateBool, lazy_write, php_ps_globals, ps_globals) /* Upload progress */ @@ -971,13 +835,10 @@ PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */ int namelen; zend_string *name; php_unserialize_data_t var_hash; - int skip = 0; PHP_VAR_UNSERIALIZE_INIT(var_hash); for (p = val; p < endptr; ) { - zval *tmp; - skip = 0; namelen = ((unsigned char)(*p)) & (~PS_BIN_UNDEF); if (namelen < 0 || namelen > PS_BIN_MAX || (p + namelen) >= endptr) { @@ -991,21 +852,12 @@ PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */ p += namelen + 1; - if ((tmp = zend_hash_find(&EG(symbol_table), name))) { - if ((Z_TYPE_P(tmp) == IS_ARRAY && - Z_ARRVAL_P(tmp) == &EG(symbol_table)) || tmp == &PS(http_session_vars)) { - skip = 1; - } - } - if (has_value) { zval *current, rv; current = var_tmp_var(&var_hash); if (php_var_unserialize(current, (const unsigned char **) &p, (const unsigned char *) endptr, &var_hash)) { ZVAL_PTR(&rv, current); - if (!skip) { - php_set_session_var(name, &rv, &var_hash); - } + php_set_session_var(name, &rv, &var_hash); } else { zend_string_release(name); php_session_normalize_vars(); @@ -1067,16 +919,13 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */ zend_string *name; int has_value, retval = SUCCESS; php_unserialize_data_t var_hash; - int skip = 0; PHP_VAR_UNSERIALIZE_INIT(var_hash); p = val; while (p < endptr) { - zval *tmp; q = p; - skip = 0; while (*q != PS_DELIMITER) { if (++q >= endptr) goto break_outer_loop; } @@ -1091,30 +940,19 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */ name = zend_string_init(p, namelen, 0); q++; - if ((tmp = zend_hash_find(&EG(symbol_table), name))) { - if ((Z_TYPE_P(tmp) == IS_ARRAY && - Z_ARRVAL_P(tmp) == &EG(symbol_table)) || tmp == &PS(http_session_vars)) { - skip = 1; - } - } - if (has_value) { zval *current, rv; current = var_tmp_var(&var_hash); if (php_var_unserialize(current, (const unsigned char **)&q, (const unsigned char *)endptr, &var_hash)) { ZVAL_PTR(&rv, current); - if (!skip) { - php_set_session_var(name, &rv, &var_hash); - } + php_set_session_var(name, &rv, &var_hash); } else { zend_string_release(name); retval = FAILURE; goto break_outer_loop; } } else { - if(!skip) { - PS_ADD_VARL(name); - } + PS_ADD_VARL(name); } zend_string_release(name); @@ -1140,7 +978,7 @@ static ps_serializer ps_serializers[MAX_SERIALIZERS + 1] = { PHPAPI int php_session_register_serializer(const char *name, zend_string *(*encode)(PS_SERIALIZER_ENCODE_ARGS), int (*decode)(PS_SERIALIZER_DECODE_ARGS)) /* {{{ */ { - int ret = -1; + int ret = FAILURE; int i; for (i = 0; i < MAX_SERIALIZERS; i++) { @@ -1149,7 +987,7 @@ PHPAPI int php_session_register_serializer(const char *name, zend_string *(*enco ps_serializers[i].encode = encode; ps_serializers[i].decode = decode; ps_serializers[i + 1].name = NULL; - ret = 0; + ret = SUCCESS; break; } } @@ -1171,13 +1009,13 @@ static ps_module *ps_modules[MAX_MODULES + 1] = { PHPAPI int php_session_register_module(ps_module *ptr) /* {{{ */ { - int ret = -1; + int ret = FAILURE; int i; for (i = 0; i < MAX_MODULES; i++) { if (!ps_modules[i]) { ps_modules[i] = ptr; - ret = 0; + ret = SUCCESS; break; } } @@ -1326,11 +1164,13 @@ static int php_session_cache_limiter(void) /* {{{ */ php_session_cache_limiter_t *lim; if (PS(cache_limiter)[0] == '\0') return 0; + if (PS(session_status) != php_session_active) return -1; if (SG(headers_sent)) { const char *output_start_filename = php_output_get_start_filename(); int output_start_lineno = php_output_get_start_lineno(); + php_session_abort(); if (output_start_filename) { php_error_docref(NULL, E_WARNING, "Cannot send session cache limiter - headers already sent (output started at %s:%d)", output_start_filename, output_start_lineno); } else { @@ -1521,6 +1361,7 @@ PHPAPI void php_session_reset_id(void) /* {{{ */ { int module_number = PS(module_number); zval *sid, *data, *ppid; + zend_bool apply_trans_sid; if (!PS(id)) { php_error_docref(NULL, E_WARNING, "Cannot set session ID - session ID is not initialized"); @@ -1561,20 +1402,26 @@ PHPAPI void php_session_reset_id(void) /* {{{ */ } /* Apply trans sid if sid cookie is not set */ - if (APPLY_TRANS_SID - && (data = zend_hash_str_find(&EG(symbol_table), "_COOKIE", sizeof("_COOKIE") - 1))) { - ZVAL_DEREF(data); - if (Z_TYPE_P(data) == IS_ARRAY && (ppid = zend_hash_str_find(Z_ARRVAL_P(data), PS(session_name), strlen(PS(session_name))))) { - ZVAL_DEREF(ppid); - } else { - /* FIXME: Resetting vars are required when - session is stop/start/regenerated. However, - php_url_scanner_reset_vars() resets all vars - including other URL rewrites set by elsewhere. */ - /* php_url_scanner_reset_vars(); */ - php_url_scanner_add_var(PS(session_name), strlen(PS(session_name)), ZSTR_VAL(PS(id)), ZSTR_LEN(PS(id)), 1); + apply_trans_sid = 0; + if (APPLY_TRANS_SID) { + apply_trans_sid = 1; + if (PS(use_cookies) && + (data = zend_hash_str_find(&EG(symbol_table), "_COOKIE", sizeof("_COOKIE") - 1))) { + ZVAL_DEREF(data); + if (Z_TYPE_P(data) == IS_ARRAY && + (ppid = zend_hash_str_find(Z_ARRVAL_P(data), PS(session_name), strlen(PS(session_name))))) { + ZVAL_DEREF(ppid); + apply_trans_sid = 0; + } } } + if (apply_trans_sid) { + zend_string *sname; + sname = zend_string_init(PS(session_name), strlen(PS(session_name)), 0); + php_url_scanner_reset_session_var(sname, 1); /* This may fail when session name has changed */ + zend_string_release(sname); + php_url_scanner_add_session_var(PS(session_name), strlen(PS(session_name)), ZSTR_VAL(PS(id)), ZSTR_LEN(PS(id)), 1); + } } /* }}} */ @@ -1696,8 +1543,8 @@ PHPAPI void php_session_start(void) /* {{{ */ static void php_session_flush(int write) /* {{{ */ { if (PS(session_status) == php_session_active) { - PS(session_status) = php_session_none; php_session_save_current_state(write); + PS(session_status) = php_session_none; } } /* }}} */ @@ -1705,10 +1552,10 @@ static void php_session_flush(int write) /* {{{ */ static void php_session_abort(void) /* {{{ */ { if (PS(session_status) == php_session_active) { - PS(session_status) = php_session_none; if (PS(mod_data) || PS(mod_user_implemented)) { PS(mod)->s_close(&PS(mod_data)); } + PS(session_status) = php_session_none; } } /* }}} */ @@ -2083,13 +1930,13 @@ static PHP_FUNCTION(session_regenerate_id) return; } - if (SG(headers_sent) && PS(use_cookies)) { - php_error_docref(NULL, E_WARNING, "Cannot regenerate session id - headers already sent"); + if (PS(session_status) != php_session_active) { + php_error_docref(NULL, E_WARNING, "Cannot regenerate session id - session is not active"); RETURN_FALSE; } - if (PS(session_status) != php_session_active) { - php_error_docref(NULL, E_WARNING, "Cannot regenerate session id - session is not active"); + if (SG(headers_sent) && PS(use_cookies)) { + php_error_docref(NULL, E_WARNING, "Cannot regenerate session id - headers already sent"); RETURN_FALSE; } @@ -2125,15 +1972,18 @@ static PHP_FUNCTION(session_regenerate_id) PS(session_vars) = NULL; } zend_string_release(PS(id)); - PS(id) = PS(mod)->s_create_sid(&PS(mod_data)); - if (!PS(id)) { + PS(id) = NULL; + + if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name)) == FAILURE) { PS(session_status) = php_session_none; - php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failed to create new session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path)); + zend_throw_error(NULL, "Failed to open session: %s (path: %s)", PS(mod)->s_name, PS(save_path)); RETURN_FALSE; } - if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name)) == FAILURE) { + + PS(id) = PS(mod)->s_create_sid(&PS(mod_data)); + if (!PS(id)) { PS(session_status) = php_session_none; - php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failed to create(open) session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path)); + zend_throw_error(NULL, "Failed to create new session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path)); RETURN_FALSE; } if (PS(use_strict_mode) && PS(mod)->s_validate_sid && @@ -2141,15 +1991,17 @@ static PHP_FUNCTION(session_regenerate_id) zend_string_release(PS(id)); PS(id) = PS(mod)->s_create_sid(&PS(mod_data)); if (!PS(id)) { + PS(mod)->s_close(&PS(mod_data)); PS(session_status) = php_session_none; - php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failed to create session ID by collision: %s (path: %s)", PS(mod)->s_name, PS(save_path)); + zend_throw_error(NULL, "Failed to create session ID by collision: %s (path: %s)", PS(mod)->s_name, PS(save_path)); RETURN_FALSE; } } /* Read is required to make new session data at this point. */ if (PS(mod)->s_read(&PS(mod_data), PS(id), &data, PS(gc_maxlifetime)) == FAILURE) { + PS(mod)->s_close(&PS(mod_data)); PS(session_status) = php_session_none; - php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failed to create(read) session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path)); + zend_throw_error(NULL, "Failed to create(read) session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path)); RETURN_FALSE; } if (data) { @@ -2167,7 +2019,6 @@ static PHP_FUNCTION(session_regenerate_id) /* {{{ proto void session_create_id([string prefix]) Generate new session ID. Intended for user save handlers. */ -#if 0 /* This is not used yet */ static PHP_FUNCTION(session_create_id) { @@ -2188,8 +2039,21 @@ static PHP_FUNCTION(session_create_id) } } - if (PS(session_status) == php_session_active) { - new_id = PS(mod)->s_create_sid(&PS(mod_data)); + if (!PS(in_save_handler) && PS(session_status) == php_session_active) { + int limit = 3; + while (limit--) { + new_id = PS(mod)->s_create_sid(&PS(mod_data)); + if (!PS(mod)->s_validate_sid) { + break; + } else { + /* Detect collision and retry */ + if (PS(mod)->s_validate_sid(&PS(mod_data), new_id) == FAILURE) { + zend_string_release(new_id); + continue; + } + break; + } + } } else { new_id = php_session_create_id(NULL); } @@ -2204,9 +2068,7 @@ static PHP_FUNCTION(session_create_id) } smart_str_0(&id); RETVAL_NEW_STR(id.s); - smart_str_free(&id); } -#endif /* }}} */ /* {{{ proto string session_cache_limiter([string new_cache_limiter]) @@ -2319,11 +2181,6 @@ static PHP_FUNCTION(session_start) RETURN_FALSE; } - if (PS(id) && !(ZSTR_LEN(PS(id)))) { - php_error_docref(NULL, E_WARNING, "Cannot start session with empty session ID"); - RETURN_FALSE; - } - /* set options */ if (options) { ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(options), num_idx, str_idx, value) { @@ -2396,6 +2253,31 @@ static PHP_FUNCTION(session_unset) } /* }}} */ +/* {{{ proto int session_gc(void) + Perform GC and return number of deleted sessions */ +static PHP_FUNCTION(session_gc) +{ + zend_long num; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (PS(session_status) != php_session_active) { + php_error_docref(NULL, E_WARNING, "Session is not active"); + RETURN_FALSE; + } + + num = php_session_gc(1); + if (num < 0) { + RETURN_FALSE; + } + + RETURN_LONG(num); +} +/* }}} */ + + /* {{{ proto void session_write_close(void) Write session data and end session */ static PHP_FUNCTION(session_write_close) @@ -2483,6 +2365,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_session_id, 0, 0, 0) ZEND_ARG_INFO(0, id) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_session_create_id, 0, 0, 0) + ZEND_ARG_INFO(0, prefix) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_session_regenerate_id, 0, 0, 0) ZEND_ARG_INFO(0, delete_old_session) ZEND_END_ARG_INFO() @@ -2571,12 +2457,14 @@ static const zend_function_entry session_functions[] = { PHP_FE(session_module_name, arginfo_session_module_name) PHP_FE(session_save_path, arginfo_session_save_path) PHP_FE(session_id, arginfo_session_id) + PHP_FE(session_create_id, arginfo_session_create_id) PHP_FE(session_regenerate_id, arginfo_session_regenerate_id) PHP_FE(session_decode, arginfo_session_decode) PHP_FE(session_encode, arginfo_session_void) PHP_FE(session_start, arginfo_session_start) PHP_FE(session_destroy, arginfo_session_void) PHP_FE(session_unset, arginfo_session_void) + PHP_FE(session_gc, arginfo_session_void) PHP_FE(session_set_save_handler, arginfo_session_set_save_handler) PHP_FE(session_cache_limiter, arginfo_session_cache_limiter) PHP_FE(session_cache_expire, arginfo_session_cache_expire) @@ -2601,7 +2489,7 @@ static const zend_function_entry php_session_iface_functions[] = { PHP_ABSTRACT_ME(SessionHandlerInterface, write, arginfo_session_class_write) PHP_ABSTRACT_ME(SessionHandlerInterface, destroy, arginfo_session_class_destroy) PHP_ABSTRACT_ME(SessionHandlerInterface, gc, arginfo_session_class_gc) - { NULL, NULL, NULL } + PHP_FE_END }; /* }}} */ @@ -2609,7 +2497,7 @@ static const zend_function_entry php_session_iface_functions[] = { */ static const zend_function_entry php_session_id_iface_functions[] = { PHP_ABSTRACT_ME(SessionIdInterface, create_sid, arginfo_session_class_create_sid) - { NULL, NULL, NULL } + PHP_FE_END }; /* }}} */ @@ -2618,7 +2506,7 @@ static const zend_function_entry php_session_id_iface_functions[] = { static const zend_function_entry php_session_update_timestamp_iface_functions[] = { PHP_ABSTRACT_ME(SessionUpdateTimestampHandlerInterface, validateId, arginfo_session_class_validateId) PHP_ABSTRACT_ME(SessionUpdateTimestampHandlerInterface, updateTimestamp, arginfo_session_class_updateTimestamp) - { NULL, NULL, NULL } + PHP_FE_END }; /* }}} */ @@ -2632,7 +2520,7 @@ static const zend_function_entry php_session_class_functions[] = { PHP_ME(SessionHandler, destroy, arginfo_session_class_destroy, ZEND_ACC_PUBLIC) PHP_ME(SessionHandler, gc, arginfo_session_class_gc, ZEND_ACC_PUBLIC) PHP_ME(SessionHandler, create_sid, arginfo_session_class_create_sid, ZEND_ACC_PUBLIC) - { NULL, NULL, NULL } + PHP_FE_END }; /* }}} */ @@ -2685,9 +2573,11 @@ static PHP_RSHUTDOWN_FUNCTION(session) /* {{{ */ { int i; - zend_try { - php_session_flush(1); - } zend_end_try(); + if (PS(session_status) == php_session_active) { + zend_try { + php_session_flush(1); + } zend_end_try(); + } php_rshutdown_session_globals(); /* this should NOT be done in php_rshutdown_session_globals() */ |