pajoye Thu Feb 5 19:53:22 2009 UTC Modified files: (Branch: PHP_5_2) /php-src/ext/zip php_zip.c php_zip.h Log: - Fixed a crash on extract in zip when files or directories entry names contain a relative path. (affects only 5.2 as it is a limitation in 5.2's virtual_file_ex)
http://cvs.php.net/viewvc.cgi/php-src/ext/zip/php_zip.c?r1=1.1.2.48&r2=1.1.2.49&diff_format=u Index: php-src/ext/zip/php_zip.c diff -u php-src/ext/zip/php_zip.c:1.1.2.48 php-src/ext/zip/php_zip.c:1.1.2.49 --- php-src/ext/zip/php_zip.c:1.1.2.48 Fri Jan 2 00:12:29 2009 +++ php-src/ext/zip/php_zip.c Thu Feb 5 19:53:22 2009 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_zip.c,v 1.1.2.48 2009/01/02 00:12:29 pajoye Exp $ */ +/* $Id: php_zip.c,v 1.1.2.49 2009/02/05 19:53:22 pajoye Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -31,6 +31,10 @@ #include "lib/zip.h" #include "lib/zipint.h" +#ifdef PHP_WIN32 +#include "tsrm_virtual_cwd.h" +#endif + /* zip_open is a macro for renaming libzip zipopen, so we need to use PHP_NAMED_FUNCTION */ static PHP_NAMED_FUNCTION(zif_zip_open); static PHP_NAMED_FUNCTION(zif_zip_read); @@ -86,6 +90,284 @@ # define add_ascii_assoc_long add_assoc_long #endif +static int php_zip_realpath_r(char *path, int start, int len, int *ll, time_t *t, int use_realpath, int is_dir, int *link_is_dir TSRMLS_DC) /* {{{ */ +{ + int i, j; + char *tmp; + + while (1) { + if (len <= start) { + return start; + } + + i = len; + while (i > start && !IS_SLASH(path[i-1])) { + i--; + } + + if (i == len || + (i == len - 1 && path[i] == '.')) { + /* remove double slashes and '.' */ + len = i - 1; + is_dir = 1; + continue; + } else if (i == len - 2 && path[i] == '.' && path[i+1] == '.') { + /* remove '..' and previous directory */ + if (i - 1 <= start) { + return start ? start : len; + } + j = php_zip_realpath_r(path, start, i-1, ll, t, use_realpath, 1, NULL TSRMLS_CC); + if (j > start) { + j--; + while (j > start && !IS_SLASH(path[j])) { + j--; + } + if (!start) { + /* leading '..' must not be removed in case of relative path */ + if (j == 0 && path[0] == '.' && path[1] == '.' && + IS_SLASH(path[2])) { + path[3] = '.'; + path[4] = '.'; + path[5] = DEFAULT_SLASH; + j = 5; + } else if (j > 0 && + path[j+1] == '.' && path[j+2] == '.' && + IS_SLASH(path[j+3])) { + j += 4; + path[j++] = '.'; + path[j++] = '.'; + path[j] = DEFAULT_SLASH; + } + } + } else if (!start && !j) { + /* leading '..' must not be removed in case of relative path */ + path[0] = '.'; + path[1] = '.'; + path[2] = DEFAULT_SLASH; + j = 2; + } + return j; + } + + path[len] = 0; + +#ifdef PHP_WIN32 + tmp = tsrm_do_alloca(len+1); + memcpy(tmp, path, len+1); +#elif defined(NETWARE) + + tmp = tsrm_do_alloca(len+1); + memcpy(tmp, path, len+1); +#else + tmp = tsrm_do_alloca(len+1); + memcpy(tmp, path, len+1); + + { +#endif + if (i - 1 <= start) { + j = start; + } else { + /* some leading directories may be unaccessable */ + j = php_zip_realpath_r(path, start, i-1, ll, t, use_realpath, 1, NULL TSRMLS_CC); + if (j > start) { + path[j++] = DEFAULT_SLASH; + } + } +#ifdef PHP_WIN32 + if (j < 0 || j + len - i >= MAXPATHLEN-1) { + tsrm_free_alloca(tmp); + + return -1; + } + { + /* use the original file or directory name as it wasn't found */ + memcpy(path+j, tmp+i, len-i+1); + j += (len-i); + } +#else + if (j < 0 || j + len - i >= MAXPATHLEN-1) { + tsrm_free_alloca(tmp); + return -1; + } + memcpy(path+j, tmp+i, len-i+1); + j += (len-i); + } +#endif + + tsrm_free_alloca(tmp); + return j; + } +} +/* }}} */ + +#define CWD_STATE_FREE(s) \ + free((s)->cwd); + + +#define CWD_STATE_COPY(d, s) \ + (d)->cwd_length = (s)->cwd_length; \ + (d)->cwd = (char *) malloc((s)->cwd_length+1); \ + memcpy((d)->cwd, (s)->cwd, (s)->cwd_length+1); + +#ifdef PHP_WIN32 +extern virtual_cwd_globals cwd_globals; +#endif + +/* Resolve path relatively to state and put the real path into state */ +/* returns 0 for ok, 1 for error */ +int php_zip_virtual_file_ex(cwd_state *state, const char *path, int use_realpath) /* {{{ */ +{ + int path_length = strlen(path); + char resolved_path[MAXPATHLEN]; + int start = 1; + int ll = 0; + time_t t; + int ret; + int add_slash; + TSRMLS_FETCH(); + + if (path_length == 0 || path_length >= MAXPATHLEN-1) { + return 1; + } + + /* cwd_length can be 0 when getcwd() fails. + * This can happen under solaris when a dir does not have read permissions + * but *does* have execute permissions */ + if (!IS_ABSOLUTE_PATH(path, path_length)) { + if (state->cwd_length == 0) { + /* resolve relative path */ + start = 0; + memcpy(resolved_path , path, path_length + 1); + } else { + int state_cwd_length = state->cwd_length; + +#ifdef PHP_WIN32 + if (IS_SLASH(path[0])) { + if (state->cwd[1] == ':') { + /* Copy only the drive name */ + state_cwd_length = 2; + } else if (IS_UNC_PATH(state->cwd, state->cwd_length)) { + /* Copy only the share name */ + state_cwd_length = 2; + while (IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + while (state->cwd[state_cwd_length] && + !IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + while (IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + while (state->cwd[state_cwd_length] && + !IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + } + } +#endif + if (path_length + state_cwd_length + 1 >= MAXPATHLEN-1) { + return 1; + } + memcpy(resolved_path, state->cwd, state_cwd_length); + resolved_path[state_cwd_length] = DEFAULT_SLASH; + memcpy(resolved_path + state_cwd_length + 1, path, path_length + 1); + path_length += state_cwd_length + 1; + } + } else { +#ifdef PHP_WIN32 + if (path_length > 2 && path[1] == ':' && !IS_SLASH(path[2])) { + resolved_path[0] = path[0]; + resolved_path[1] = ':'; + resolved_path[2] = DEFAULT_SLASH; + memcpy(resolved_path + 3, path + 2, path_length - 1); + path_length++; + } else +#endif + memcpy(resolved_path, path, path_length + 1); + } + +#ifdef PHP_WIN32 + if (memchr(resolved_path, '*', path_length) || + memchr(resolved_path, '?', path_length)) { + return 1; + } +#endif + +#ifdef PHP_WIN32 + if (IS_UNC_PATH(resolved_path, path_length)) { + /* skip UNC name */ + resolved_path[0] = DEFAULT_SLASH; + resolved_path[1] = DEFAULT_SLASH; + start = 2; + while (!IS_SLASH(resolved_path[start])) { + if (resolved_path[start] == 0) { + goto verify; + } + resolved_path[start] = toupper(resolved_path[start]); + start++; + } + resolved_path[start++] = DEFAULT_SLASH; + while (!IS_SLASH(resolved_path[start])) { + if (resolved_path[start] == 0) { + goto verify; + } + resolved_path[start] = toupper(resolved_path[start]); + start++; + } + resolved_path[start++] = DEFAULT_SLASH; + } else if (IS_ABSOLUTE_PATH(resolved_path, path_length)) { + /* skip DRIVE name */ + resolved_path[0] = toupper(resolved_path[0]); + resolved_path[2] = DEFAULT_SLASH; + start = 3; + } +#elif defined(NETWARE) + if (IS_ABSOLUTE_PATH(resolved_path, path_length)) { + /* skip VOLUME name */ + start = 0; + while (start != ':') { + if (resolved_path[start] == 0) return -1; + start++; + } + start++; + if (!IS_SLASH(resolved_path[start])) return -1; + resolved_path[start++] = DEFAULT_SLASH; + } +#endif + + add_slash = (use_realpath != CWD_REALPATH) && path_length > 0 && IS_SLASH(resolved_path[path_length-1]); + /* No cache used */ + t = 0; + path_length = php_zip_realpath_r(resolved_path, start, path_length, &ll, &t, use_realpath, 0, NULL TSRMLS_CC); + + if (path_length < 0) { + errno = ENOENT; + return 1; + } + + if (!start && !path_length) { + resolved_path[path_length++] = '.'; + } + if (add_slash && path_length && !IS_SLASH(resolved_path[path_length-1])) { + if (path_length >= MAXPATHLEN-1) { + return -1; + } + resolved_path[path_length++] = DEFAULT_SLASH; + } + resolved_path[path_length] = 0; + +#ifdef PHP_WIN32 +verify: +#endif + state->cwd_length = path_length; + state->cwd = (char *) realloc(state->cwd, state->cwd_length+1); + memcpy(state->cwd, resolved_path, state->cwd_length+1); + ret = 0; + return (ret); +} +/* }}} */ + /* Flatten a path by creating a relative path (to .) */ static char * php_zip_make_relative_path(char *path, int path_len) /* {{{ */ { @@ -150,7 +432,9 @@ /* Clean/normlize the path and then transform any path (absolute or relative) to a path relative to cwd (../../mydir/foo.txt > mydir/foo.txt) */ - virtual_file_ex(&new_state, file, NULL, CWD_EXPAND); + if (php_zip_virtual_file_ex(&new_state, file, CWD_EXPAND) == 1) { + return 0; + } path_cleaned = php_zip_make_relative_path(new_state.cwd, new_state.cwd_length); path_cleaned_len = strlen(path_cleaned); @@ -2273,7 +2557,7 @@ php_info_print_table_start(); php_info_print_table_row(2, "Zip", "enabled"); - php_info_print_table_row(2, "Extension Version","$Id: php_zip.c,v 1.1.2.48 2009/01/02 00:12:29 pajoye Exp $"); + php_info_print_table_row(2, "Extension Version","$Id: php_zip.c,v 1.1.2.49 2009/02/05 19:53:22 pajoye Exp $"); php_info_print_table_row(2, "Zip version", PHP_ZIP_VERSION_STRING); php_info_print_table_row(2, "Libzip version", "0.9.0"); http://cvs.php.net/viewvc.cgi/php-src/ext/zip/php_zip.h?r1=1.10.2.8&r2=1.10.2.9&diff_format=u Index: php-src/ext/zip/php_zip.h diff -u php-src/ext/zip/php_zip.h:1.10.2.8 php-src/ext/zip/php_zip.h:1.10.2.9 --- php-src/ext/zip/php_zip.h:1.10.2.8 Wed Dec 31 11:17:47 2008 +++ php-src/ext/zip/php_zip.h Thu Feb 5 19:53:22 2009 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_zip.h,v 1.10.2.8 2008/12/31 11:17:47 sebastian Exp $ */ +/* $Id: php_zip.h,v 1.10.2.9 2009/02/05 19:53:22 pajoye Exp $ */ #ifndef PHP_ZIP_H #define PHP_ZIP_H @@ -24,9 +24,7 @@ extern zend_module_entry zip_module_entry; #define phpext_zip_ptr &zip_module_entry -#ifdef ZTS #include "TSRM.h" -#endif #include "lib/zip.h"
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php