diff options
| author | Pierre Joye <pajoye@php.net> | 2008-10-23 16:12:55 +0000 | 
|---|---|---|
| committer | Pierre Joye <pajoye@php.net> | 2008-10-23 16:12:55 +0000 | 
| commit | 7d5a67a0227842c8a037a0865a244942697f1da5 (patch) | |
| tree | 8dcbfbc95fe3acc11eba72f3b90345cf44422696 /ext/zip/php_zip.c | |
| parent | 8a106deb247ee44112d75a2c797b0b02e106299e (diff) | |
| download | php-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.c | 132 | 
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();  	}  | 
