summaryrefslogtreecommitdiff
path: root/ext/session/session.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/session/session.c')
-rw-r--r--ext/session/session.c474
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() */