summaryrefslogtreecommitdiff
path: root/ext/zip/php_zip.c
diff options
context:
space:
mode:
authorPierre Joye <pajoye@php.net>2008-10-23 16:12:55 +0000
committerPierre Joye <pajoye@php.net>2008-10-23 16:12:55 +0000
commit7d5a67a0227842c8a037a0865a244942697f1da5 (patch)
tree8dcbfbc95fe3acc11eba72f3b90345cf44422696 /ext/zip/php_zip.c
parent8a106deb247ee44112d75a2c797b0b02e106299e (diff)
downloadphp-git-7d5a67a0227842c8a037a0865a244942697f1da5.tar.gz
- flatten path and make them relative before extraction
- remove unnecessary export
Diffstat (limited to 'ext/zip/php_zip.c')
-rw-r--r--ext/zip/php_zip.c132
1 files changed, 98 insertions, 34 deletions
diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c
index 40c0e0b7ce..71687a129d 100644
--- a/ext/zip/php_zip.c
+++ b/ext/zip/php_zip.c
@@ -95,51 +95,102 @@ static int le_zip_entry;
# define add_ascii_assoc_long add_assoc_long
#endif
+/* Flatten a path by making a relative path (to .)*/
+static char * php_zip_make_relative_path(char *path, int path_len) /* {{{ */
+{
+ char *path_begin = path;
+ int prev_is_slash = 0;
+ char *e = path + path_len - 1;
+ size_t pos = path_len - 1;
+ size_t i;
+
+ if (IS_SLASH(path[0])) {
+ return path + 1;
+ }
+
+ if (path_len < 1 || path == NULL) {
+ return NULL;
+ }
+
+ i = path_len;
+
+ while (1) {
+ while (i > 0 && !IS_SLASH(path[i])) {
+ i--;
+ }
+
+ if (!i) {
+ return path;
+ }
+
+ if (i >= 2 && (path[i -1] == '.' || path[i -1] == ':')) {
+ /* i is the position of . or :, add 1 for / */
+ path_begin = path + i + 1;
+ break;
+ }
+ i--;
+ }
+
+ return path_begin;
+}
+/* }}} */
+
#ifdef ZEND_ENGINE_2_1
/* {{{ php_zip_extract_file */
-/* TODO: Simplify it */
static int php_zip_extract_file(struct zip * za, char *dest, char *file, int file_len TSRMLS_DC)
{
php_stream_statbuf ssb;
struct zip_file *zf;
struct zip_stat sb;
char b[8192];
-
int n, len, ret;
-
php_stream *stream;
-
char *fullpath;
char *file_dirname_fullpath;
char file_dirname[MAXPATHLEN];
size_t dir_len;
-
char *file_basename;
size_t file_basename_len;
int is_dir_only = 0;
+ char *path_cleaned;
+ size_t path_cleaned_len;
+ cwd_state new_state;
- if (file_len >= MAXPATHLEN || zip_stat(za, file, 0, &sb) != 0) {
+ new_state.cwd = (char*)malloc(1);
+ new_state.cwd[0] = '\0';
+ new_state.cwd_length = 0;
+
+ /* 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);
+ path_cleaned = php_zip_make_relative_path(new_state.cwd, new_state.cwd_length);
+ path_cleaned_len = strlen(path_cleaned);
+
+ if (path_cleaned_len >= MAXPATHLEN || zip_stat(za, file, 0, &sb) != 0) {
return 0;
}
+
/* it is a directory only, see #40228 */
- if (file_len > 1 && file[file_len - 1] == '/') {
+ if (path_cleaned_len > 1 && IS_SLASH(path_cleaned[path_cleaned_len - 1])) {
len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, file);
is_dir_only = 1;
} else {
- memcpy(file_dirname, file, file_len);
- dir_len = php_dirname(file_dirname, file_len);
+ memcpy(file_dirname, path_cleaned, path_cleaned_len);
+ dir_len = php_dirname(file_dirname, path_cleaned_len);
- if (dir_len > 0) {
- len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, file_dirname);
- } else {
+ if (dir_len <= 0 || (dir_len == 1 && file_dirname[0] == '.')) {
len = spprintf(&file_dirname_fullpath, 0, "%s", dest);
+ } else {
+ len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, file_dirname);
}
- php_basename(file, file_len, NULL, 0, &file_basename, (unsigned int *)&file_basename_len TSRMLS_CC);
+ php_basename(path_cleaned, path_cleaned_len, NULL, 0, &file_basename, (unsigned int *)&file_basename_len TSRMLS_CC);
if (OPENBASEDIR_CHECKPATH(file_dirname_fullpath)) {
efree(file_dirname_fullpath);
efree(file_basename);
+ free(new_state.cwd);
return 0;
}
}
@@ -158,28 +209,32 @@ static int php_zip_extract_file(struct zip * za, char *dest, char *file, int fil
}
#endif
- ret = php_stream_mkdir(file_dirname_fullpath, 0777, PHP_STREAM_MKDIR_RECURSIVE, NULL);
+ ret = php_stream_mkdir(file_dirname_fullpath, 0777, PHP_STREAM_MKDIR_RECURSIVE|REPORT_ERRORS, NULL);
if (!ret) {
efree(file_dirname_fullpath);
- efree(file_basename);
+ if (!is_dir_only) {
+ efree(file_basename);
+ free(new_state.cwd);
+ }
return 0;
}
}
/* it is a standalone directory, job done */
- if (file[file_len - 1] == '/') {
+ if (is_dir_only) {
efree(file_dirname_fullpath);
- if (!is_dir_only) {
- efree(file_basename);
- }
+ free(new_state.cwd);
return 1;
}
- len = spprintf(&fullpath, 0, "%s/%s/%s", dest, file_dirname, file_basename);
+ len = spprintf(&fullpath, 0, "%s/%s", file_dirname_fullpath, file_basename);
if (!len) {
efree(file_dirname_fullpath);
efree(file_basename);
+ free(new_state.cwd);
return 0;
+ } else if (len > MAXPATHLEN) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Full extraction path exceed MAXPATHLEN (%i)", MAXPATHLEN);
}
/* check again the full path, not sure if it
@@ -187,8 +242,10 @@ static int php_zip_extract_file(struct zip * za, char *dest, char *file, int fil
* safemode status as its parent folder?
*/
if (OPENBASEDIR_CHECKPATH(fullpath)) {
+ efree(fullpath);
efree(file_dirname_fullpath);
efree(file_basename);
+ free(new_state.cwd);
return 0;
}
@@ -197,8 +254,10 @@ static int php_zip_extract_file(struct zip * za, char *dest, char *file, int fil
efree(fullpath);
efree(file_dirname_fullpath);
efree(file_basename);
+ free(new_state.cwd);
return 0;
}
+
#if (PHP_MAJOR_VERSION < 6)
stream = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL);
#else
@@ -214,6 +273,7 @@ static int php_zip_extract_file(struct zip * za, char *dest, char *file, int fil
efree(fullpath);
efree(file_basename);
efree(file_dirname_fullpath);
+ free(new_state.cwd);
if (n<0) {
return 0;
@@ -472,10 +532,7 @@ int php_zip_glob(char *pattern, int pattern_len, long flags, zval *return_value
/* we assume that any glob pattern will match files from one directory only
so checking the dirname of the first match should be sufficient */
strncpy(cwd, globbuf.gl_pathv[0], MAXPATHLEN);
- if (PG(safe_mode) && (!php_checkuid(cwd, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
- return -1;
- }
- if (php_check_open_basedir(cwd TSRMLS_CC)) {
+ if (OPENBASEDIR_CHECKPATH(cwd)) {
return -1;
}
@@ -537,14 +594,10 @@ int php_zip_pcre(char *regexp, int regexp_len, char *path, int path_len, zval *r
}
#endif
- /* we assume that any glob pattern will match files from one directory only
- so checking the dirname of the first match should be sufficient */
- if (PG(safe_mode) && (!php_checkuid(path, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
- return -1;
- }
- if (php_check_open_basedir(path TSRMLS_CC)) {
+ if (OPENBASEDIR_CHECKPATH(path)) {
return -1;
}
+
files_cnt = php_stream_scandir(path, &namelist, NULL, (void *) php_stream_dirent_alphasort);
if (files_cnt > 0) {
@@ -670,7 +723,7 @@ ZEND_END_ARG_INFO()
/* }}} */
/* {{{ zend_function_entry */
-static zend_function_entry zip_functions[] = {
+static const zend_function_entry zip_functions[] = {
ZEND_RAW_FENTRY("zip_open", zif_zip_open, arginfo_zip_open, 0)
ZEND_RAW_FENTRY("zip_close", zif_zip_close, arginfo_zip_close, 0)
ZEND_RAW_FENTRY("zip_read", zif_zip_read, arginfo_zip_read, 0)
@@ -1091,6 +1144,7 @@ static PHP_NAMED_FUNCTION(zif_zip_open)
{
char *filename;
int filename_len;
+ char resolved_path[MAXPATHLEN + 1];
zip_rsrc *rsrc_int;
int err = 0;
@@ -1098,13 +1152,22 @@ static PHP_NAMED_FUNCTION(zif_zip_open)
return;
}
+ if (filename_len == 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty string as source");
+ RETURN_FALSE;
+ }
+
if (OPENBASEDIR_CHECKPATH(filename)) {
RETURN_FALSE;
}
+ if(!expand_filepath(filename, resolved_path TSRMLS_CC)) {
+ RETURN_FALSE;
+ }
+
rsrc_int = (zip_rsrc *)emalloc(sizeof(zip_rsrc));
- rsrc_int->za = zip_open(filename, 0, &err);
+ rsrc_int->za = zip_open(resolved_path, 0, &err);
if (rsrc_int->za == NULL) {
efree(rsrc_int);
RETURN_LONG((long)err);
@@ -1402,6 +1465,7 @@ static ZIPARCHIVE_METHOD(open)
efree(ze_obj->filename);
ze_obj->filename = NULL;
}
+
intern = zip_open(resolved_path, flags, &err);
if (!intern || err) {
RETURN_LONG((long)err);
@@ -1486,11 +1550,11 @@ static ZIPARCHIVE_METHOD(addEmptyDir)
&dirname, &dirname_len) == FAILURE) {
return;
}
+
if (dirname_len<1) {
RETURN_FALSE;
}
-
if (dirname[dirname_len-1] != '/') {
s=(char *)emalloc(dirname_len+2);
strcpy(s, dirname);
@@ -2360,7 +2424,6 @@ static ZIPARCHIVE_METHOD(extractTo)
}
}
}
-
RETURN_TRUE;
}
/* }}} */
@@ -2420,6 +2483,7 @@ static void php_zip_get_from(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
buffer = safe_emalloc(len, 1, 2);
n = zip_fread(zf, buffer, len);
if (n < 1) {
+ efree(buffer);
RETURN_EMPTY_STRING();
}