diff options
Diffstat (limited to 'ext/session/mod_files.c')
-rw-r--r-- | ext/session/mod_files.c | 98 |
1 files changed, 56 insertions, 42 deletions
diff --git a/ext/session/mod_files.c b/ext/session/mod_files.c index 004d9d4637..cb61c8a221 100644 --- a/ext/session/mod_files.c +++ b/ext/session/mod_files.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 5 | + | PHP Version 7 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2013 The PHP Group | + | Copyright (c) 1997-2014 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 | @@ -50,14 +50,20 @@ #define FILE_PREFIX "sess_" +#ifdef PHP_WIN32 +# ifndef O_NOFOLLOW +# define O_NOFOLLOW 0 +# endif +#endif + typedef struct { - int fd; char *lastkey; char *basedir; size_t basedir_len; size_t dirdepth; size_t st_size; int filemode; + int fd; } ps_files; ps_module ps_mod_files = { @@ -115,6 +121,9 @@ static void ps_files_close(ps_files *data) static void ps_files_open(ps_files *data, const char *key TSRMLS_DC) { char buf[MAXPATHLEN]; +#if !defined(O_NOFOLLOW) || !defined(PHP_WIN32) + struct stat sbuf; +#endif if (data->fd < 0 || !data->lastkey || strcmp(key, data->lastkey)) { if (data->lastkey) { @@ -135,22 +144,26 @@ static void ps_files_open(ps_files *data, const char *key TSRMLS_DC) data->lastkey = estrdup(key); + /* O_NOFOLLOW to prevent us from following evil symlinks */ +#ifdef O_NOFOLLOW + data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY | O_NOFOLLOW, data->filemode); +#else + /* Check to make sure that the opened file is not outside of allowable dirs. + This is not 100% safe but it's hard to do something better without O_NOFOLLOW */ + if(PG(open_basedir) && lstat(buf, &sbuf) == 0 && S_ISLNK(sbuf.st_mode) && php_check_open_basedir(buf TSRMLS_CC)) { + return; + } data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY, data->filemode); +#endif if (data->fd != -1) { #ifndef PHP_WIN32 - /* check to make sure that the opened file is not a symlink, linking to data outside of allowable dirs */ - if (PG(open_basedir)) { - struct stat sbuf; - - if (fstat(data->fd, &sbuf)) { - close(data->fd); - return; - } - if (S_ISLNK(sbuf.st_mode) && php_check_open_basedir(buf TSRMLS_CC)) { - close(data->fd); - return; - } + /* check that this session file was created by us or root – we + don't want to end up accepting the sessions of another webapp */ + if (fstat(data->fd, &sbuf) || (sbuf.st_uid != 0 && sbuf.st_uid != getuid() && sbuf.st_uid != geteuid())) { + close(data->fd); + data->fd = -1; + return; } #endif flock(data->fd, LOCK_EX); @@ -174,7 +187,7 @@ static int ps_files_cleanup_dir(const char *dirname, int maxlifetime TSRMLS_DC) DIR *dir; char dentry[sizeof(struct dirent) + MAXPATHLEN]; struct dirent *entry = (struct dirent *) &dentry; - struct stat sbuf; + zend_stat_t sbuf; char buf[MAXPATHLEN]; time_t now; int nrdels = 0; @@ -225,7 +238,7 @@ static int ps_files_cleanup_dir(const char *dirname, int maxlifetime TSRMLS_DC) static int ps_files_key_exists(ps_files *data, const char *key TSRMLS_DC) { char buf[MAXPATHLEN]; - struct stat sbuf; + zend_stat_t sbuf; if (!key || !ps_files_path_create(buf, sizeof(buf), data, key)) { return FAILURE; @@ -270,7 +283,7 @@ PS_OPEN_FUNC(files) if (argc > 1) { errno = 0; - dirdepth = (size_t) strtol(argv[0], NULL, 10); + dirdepth = (size_t) ZEND_STRTOL(argv[0], NULL, 10); if (errno == ERANGE) { php_error(E_WARNING, "The first parameter in session.save_path is invalid"); return FAILURE; @@ -279,7 +292,7 @@ PS_OPEN_FUNC(files) if (argc > 2) { errno = 0; - filemode = strtol(argv[1], NULL, 8); + filemode = ZEND_STRTOL(argv[1], NULL, 8); if (errno == ERANGE || filemode < 0 || filemode > 07777) { php_error(E_WARNING, "The second parameter in session.save_path is invalid"); return FAILURE; @@ -322,19 +335,19 @@ PS_CLOSE_FUNC(files) PS_READ_FUNC(files) { - long n; - struct stat sbuf; + zend_long n; + zend_stat_t sbuf; PS_FILES_DATA; /* If strict mode, check session id existence */ if (PS(use_strict_mode) && - ps_files_key_exists(data, key TSRMLS_CC) == FAILURE) { + ps_files_key_exists(data, key? key->val : NULL TSRMLS_CC) == FAILURE) { /* key points to PS(id), but cannot change here. */ if (key) { - efree(PS(id)); + zend_string_release(PS(id)); PS(id) = NULL; } - PS(id) = PS(mod)->s_create_sid((void **)&data, NULL TSRMLS_CC); + PS(id) = PS(mod)->s_create_sid((void **)&data TSRMLS_CC); if (!PS(id)) { return FAILURE; } @@ -342,31 +355,32 @@ PS_READ_FUNC(files) PS(send_cookie) = 1; } php_session_reset_id(TSRMLS_C); + PS(session_status) = php_session_active; } - ps_files_open(data, PS(id) TSRMLS_CC); + ps_files_open(data, PS(id)->val TSRMLS_CC); if (data->fd < 0) { return FAILURE; } - if (fstat(data->fd, &sbuf)) { + if (zend_fstat(data->fd, &sbuf)) { return FAILURE; } - data->st_size = *vallen = sbuf.st_size; + data->st_size = sbuf.st_size; if (sbuf.st_size == 0) { *val = STR_EMPTY_ALLOC(); return SUCCESS; } - *val = emalloc(sbuf.st_size); + *val = zend_string_alloc(sbuf.st_size, 0); #if defined(HAVE_PREAD) - n = pread(data->fd, *val, sbuf.st_size, 0); + n = pread(data->fd, (*val)->val, (*val)->len, 0); #else lseek(data->fd, 0, SEEK_SET); - n = read(data->fd, *val, sbuf.st_size); + n = read(data->fd, (*val)->val, (*val)->len); #endif if (n != sbuf.st_size) { @@ -375,7 +389,7 @@ PS_READ_FUNC(files) } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "read returned less bytes than requested"); } - efree(*val); + zend_string_release(*val); return FAILURE; } @@ -384,28 +398,28 @@ PS_READ_FUNC(files) PS_WRITE_FUNC(files) { - long n; + zend_long n; PS_FILES_DATA; - ps_files_open(data, key TSRMLS_CC); + ps_files_open(data, key->val TSRMLS_CC); if (data->fd < 0) { return FAILURE; } /* Truncate file if the amount of new data is smaller than the existing data set. */ - if (vallen < (int)data->st_size) { + if (val->len < (int)data->st_size) { php_ignore_value(ftruncate(data->fd, 0)); } #if defined(HAVE_PWRITE) - n = pwrite(data->fd, val, vallen, 0); + n = pwrite(data->fd, val->val, val->len, 0); #else lseek(data->fd, 0, SEEK_SET); - n = write(data->fd, val, vallen); + n = write(data->fd, val->val, val->len); #endif - if (n != vallen) { + if (n != val->len) { if (n == -1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "write failed: %s (%d)", strerror(errno), errno); } else { @@ -422,7 +436,7 @@ PS_DESTROY_FUNC(files) char buf[MAXPATHLEN]; PS_FILES_DATA; - if (!ps_files_path_create(buf, sizeof(buf), data, key)) { + if (!ps_files_path_create(buf, sizeof(buf), data, key->val)) { return FAILURE; } @@ -458,16 +472,16 @@ PS_GC_FUNC(files) PS_CREATE_SID_FUNC(files) { - char *sid; + zend_string *sid; int maxfail = 3; PS_FILES_DATA; do { - sid = php_session_create_id((void **)&data, newlen TSRMLS_CC); + sid = php_session_create_id((void**)&data TSRMLS_CC); /* Check collision */ - if (data && ps_files_key_exists(data, sid TSRMLS_CC) == SUCCESS) { + if (data && ps_files_key_exists(data, sid? sid->val : NULL TSRMLS_CC) == SUCCESS) { if (sid) { - efree(sid); + zend_string_release(sid); sid = NULL; } if (!(maxfail--)) { |