diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 40c098d8901c..1640c7b10ac3 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -74,6 +74,17 @@ static int le_zip_entry; # define add_ascii_assoc_string add_assoc_string # define add_ascii_assoc_long add_assoc_long +static bool php_zip_file_set_encryption(struct zip *intern, zend_long index, zend_long method, char *password) { + // FIXME: is a workaround to reset/free the password in case of consecutive calls. + // when libzip 1.11.5 is available, we can save this call in this case. + if (UNEXPECTED(zip_file_set_encryption(intern, (zip_uint64_t)index, ZIP_EM_NONE, NULL) < 0)) { + php_error_docref(NULL, E_WARNING, "password reset failed"); + return false; + } + + return (zip_file_set_encryption(intern, (zip_uint64_t)index, (zip_uint16_t)method, password) == 0); +} + /* Flatten a path by making a relative path (to .)*/ static char * php_zip_make_relative_path(char *path, size_t path_len) /* {{{ */ { @@ -367,14 +378,22 @@ static zend_result php_zip_parse_options(HashTable *options, zip_options *opts) php_error_docref(NULL, E_WARNING, "Option \"comp_method\" must be of type int, %s given", zend_zval_value_name(option)); } - opts->comp_method = zval_get_long(option); + zend_long comp_method = zval_get_long(option); + if (comp_method < 0 || comp_method > INT_MAX) { + php_error_docref(NULL, E_WARNING, "Option \"comp_method\" must be between 0 and %d", INT_MAX); + } + opts->comp_method = (zip_int32_t)comp_method; if ((option = zend_hash_str_find(options, "comp_flags", sizeof("comp_flags") - 1)) != NULL) { if (Z_TYPE_P(option) != IS_LONG) { php_error_docref(NULL, E_WARNING, "Option \"comp_flags\" must be of type int, %s given", zend_zval_value_name(option)); } - opts->comp_flags = zval_get_long(option); + zend_long comp_flags = zval_get_long(option); + if (comp_flags < 0 || comp_flags > USHRT_MAX) { + php_error_docref(NULL, E_WARNING, "Option \"comp_flags\" must be between 0 and %u", USHRT_MAX); + } + opts->comp_flags = (zip_uint32_t)comp_flags; } } @@ -1752,12 +1771,7 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* } #ifdef HAVE_ENCRYPTION if (opts.enc_method >= 0) { - if (UNEXPECTED(zip_file_set_encryption(ze_obj->za, ze_obj->last_id, ZIP_EM_NONE, NULL) < 0)) { - zend_array_destroy(Z_ARR_P(return_value)); - php_error_docref(NULL, E_WARNING, "password reset failed"); - RETURN_FALSE; - } - if (zip_file_set_encryption(ze_obj->za, ze_obj->last_id, opts.enc_method, opts.enc_password)) { + if (!php_zip_file_set_encryption(ze_obj->za, ze_obj->last_id, opts.enc_method, opts.enc_password)) { zend_array_destroy(Z_ARR_P(return_value)); RETURN_FALSE; } @@ -2280,15 +2294,7 @@ PHP_METHOD(ZipArchive, setEncryptionName) RETURN_FALSE; } - if (UNEXPECTED(zip_file_set_encryption(intern, idx, ZIP_EM_NONE, NULL) < 0)) { - php_error_docref(NULL, E_WARNING, "password reset failed"); - RETURN_FALSE; - } - - if (zip_file_set_encryption(intern, idx, (zip_uint16_t)method, password)) { - RETURN_FALSE; - } - RETURN_TRUE; + RETURN_BOOL(php_zip_file_set_encryption(intern, idx, method, password)); } /* }}} */ @@ -2308,12 +2314,7 @@ PHP_METHOD(ZipArchive, setEncryptionIndex) ZIP_FROM_OBJECT(intern, self); - if (UNEXPECTED(zip_file_set_encryption(intern, index, ZIP_EM_NONE, NULL) < 0)) { - php_error_docref(NULL, E_WARNING, "password reset failed"); - RETURN_FALSE; - } - - RETURN_BOOL(zip_file_set_encryption(intern, index, (zip_uint16_t)method, password) == 0); + RETURN_BOOL(php_zip_file_set_encryption(intern, index, method, password)); } /* }}} */ #endif @@ -2391,13 +2392,23 @@ PHP_METHOD(ZipArchive, setCompressionName) RETURN_THROWS(); } - ZIP_FROM_OBJECT(intern, this); - if (name_len == 0) { zend_argument_must_not_be_empty_error(1); RETURN_THROWS(); } + if (comp_method < -1 || comp_method > INT_MAX) { + zend_argument_value_error(2, "must be between 0 and %d", INT_MAX); + RETURN_THROWS(); + } + + if (comp_flags < 0 || comp_flags > USHRT_MAX) { + // comp_flags is cast down accordingly in libzip, zip_entry_t compression_level is of zip_uint16_t + zend_argument_value_error(3, "must be between 0 and %u", USHRT_MAX); + RETURN_THROWS(); + } + + ZIP_FROM_OBJECT(intern, this); idx = zip_name_locate(intern, name, 0); if (idx < 0) { @@ -2422,6 +2433,21 @@ PHP_METHOD(ZipArchive, setCompressionIndex) RETURN_THROWS(); } + if (index < 0) { + RETURN_FALSE; + } + + if (comp_method < -1 || comp_method > INT_MAX) { + zend_argument_value_error(2, "must be between -1 and %d", INT_MAX); + RETURN_THROWS(); + } + + if (comp_flags < 0 || comp_flags > USHRT_MAX) { + // comp_flags is cast down accordingly in libzip, zip_entry_t compression_level is of zip_uint16_t + zend_argument_value_error(3, "must be between 0 and %u", USHRT_MAX); + RETURN_THROWS(); + } + ZIP_FROM_OBJECT(intern, this); RETURN_BOOL(zip_set_file_compression(intern, (zip_uint64_t)index, @@ -2444,13 +2470,13 @@ PHP_METHOD(ZipArchive, setMtimeName) RETURN_THROWS(); } - ZIP_FROM_OBJECT(intern, this); - if (name_len == 0) { zend_argument_must_not_be_empty_error(1); RETURN_THROWS(); } + ZIP_FROM_OBJECT(intern, this); + idx = zip_name_locate(intern, name, 0); if (idx < 0) { @@ -2516,17 +2542,15 @@ PHP_METHOD(ZipArchive, deleteName) RETURN_THROWS(); } - ZIP_FROM_OBJECT(intern, self); - if (name_len < 1) { RETURN_FALSE; } + ZIP_FROM_OBJECT(intern, self); + PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb); - if (zip_delete(intern, sb.index)) { - RETURN_FALSE; - } - RETURN_TRUE; + + RETURN_BOOL(zip_delete(intern, sb.index) == 0); } /* }}} */ @@ -2547,13 +2571,13 @@ PHP_METHOD(ZipArchive, renameIndex) RETURN_FALSE; } - ZIP_FROM_OBJECT(intern, self); - if (new_name_len == 0) { zend_argument_must_not_be_empty_error(2); RETURN_THROWS(); } + ZIP_FROM_OBJECT(intern, self); + RETURN_BOOL(zip_file_rename(intern, index, (const char *)new_name, 0) == 0); } /* }}} */ @@ -2595,12 +2619,12 @@ PHP_METHOD(ZipArchive, unchangeIndex) RETURN_THROWS(); } - ZIP_FROM_OBJECT(intern, self); - if (index < 0) { RETURN_FALSE; } + ZIP_FROM_OBJECT(intern, self); + RETURN_BOOL(zip_unchange(intern, index) == 0); } /* }}} */ @@ -2618,12 +2642,12 @@ PHP_METHOD(ZipArchive, unchangeName) RETURN_THROWS(); } - ZIP_FROM_OBJECT(intern, self); - if (name_len < 1) { RETURN_FALSE; } + ZIP_FROM_OBJECT(intern, self); + PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb); RETURN_BOOL(zip_unchange(intern, sb.index) == 0); @@ -2687,8 +2711,6 @@ PHP_METHOD(ZipArchive, extractTo) Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(files_ht, files_str) ZEND_PARSE_PARAMETERS_END(); - ZIP_FROM_OBJECT(intern, self); - if (pathto_len < 1) { RETURN_FALSE; } @@ -2701,6 +2723,7 @@ PHP_METHOD(ZipArchive, extractTo) } uint32_t nelems, i; + ZIP_FROM_OBJECT(intern, self); if (files_str) { if (!php_zip_extract_file(intern, pathto, ZSTR_VAL(files_str), ZSTR_LEN(files_str), -1)) { @@ -2797,7 +2820,7 @@ static void php_zip_get_from(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ RETURN_FALSE; } - buffer = zend_string_safe_alloc(1, len, 0, 0); + buffer = zend_string_safe_alloc(1, len, 0, false); n = zip_fread(zf, ZSTR_VAL(buffer), ZSTR_LEN(buffer)); if (n < 1) { zend_string_efree(buffer); @@ -3006,6 +3029,10 @@ PHP_METHOD(ZipArchive, isCompressionMethodSupported) if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &method, &enc) == FAILURE) { return; } + if (method < -1 || method > INT_MAX) { + zend_argument_value_error(1, "must be between -1 and %d", INT_MAX); + RETURN_THROWS(); + } RETVAL_BOOL(zip_compression_method_supported((zip_int32_t)method, enc)); } /* }}} */ @@ -3019,6 +3046,10 @@ PHP_METHOD(ZipArchive, isEncryptionMethodSupported) if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &method, &enc) == FAILURE) { return; } + if (method < 0 || method > USHRT_MAX) { + zend_argument_value_error(1, "must be between 0 and %u", USHRT_MAX); + RETURN_THROWS(); + } RETVAL_BOOL(zip_encryption_method_supported((zip_uint16_t)method, enc)); } /* }}} */ diff --git a/ext/zip/tests/oo_addglob2.phpt b/ext/zip/tests/oo_addglob2.phpt index 517c0b7fd7fd..a98a22271205 100644 --- a/ext/zip/tests/oo_addglob2.phpt +++ b/ext/zip/tests/oo_addglob2.phpt @@ -33,10 +33,26 @@ if (!$zip->addGlob($dirname . 'foo.*', GLOB_BRACE, $options)) { $options = [ 'remove_all_path' => true, 'comp_method' => ZipArchive::CM_STORE, - 'comp_flags' => 5, + 'comp_flags' => PHP_INT_MIN, 'enc_method' => ZipArchive::EM_AES_256, 'enc_password' => 'secret', ]; + +try { + $zip->addGlob($dirname. 'bar.*', GLOB_BRACE, $options); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +$options['comp_flags'] = 65536; + +try { + $zip->addGlob($dirname. 'bar.*', GLOB_BRACE, $options); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +$options['comp_flags'] = 5; if (!$zip->addGlob($dirname . 'bar.*', GLOB_BRACE, $options)) { echo "failed 2\n"; } @@ -61,6 +77,10 @@ $dirname = __DIR__ . '/'; include $dirname . 'utils.inc'; rmdir_rf(__DIR__ . '/__tmp_oo_addglob2/'); ?> ---EXPECT-- +--EXPECTF-- + +Warning: ZipArchive::addGlob(): Option "comp_flags" must be between 0 and 65535 in %s on line %d + +Warning: ZipArchive::addGlob(): Option "comp_flags" must be between 0 and 65535 in %s on line %d 0: foo.txt, comp=8, enc=0 1: bar.txt, comp=0, enc=259 diff --git a/ext/zip/tests/oo_setcompression.phpt b/ext/zip/tests/oo_setcompression.phpt index 6b3291463659..486a912e7909 100644 --- a/ext/zip/tests/oo_setcompression.phpt +++ b/ext/zip/tests/oo_setcompression.phpt @@ -28,6 +28,30 @@ var_dump($zip->setCompressionName('entry2.txt', ZipArchive::CM_DEFAULT)); var_dump($zip->setCompressionName('dir/entry3.txt', ZipArchive::CM_STORE)); var_dump($zip->setCompressionName('entry4.txt', ZipArchive::CM_DEFLATE)); +try { + $zip->setCompressionName('entry5.txt', PHP_INT_MIN); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + $zip->setCompressionName('entry5.txt', PHP_INT_MAX); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + $zip->setCompressionIndex(4, PHP_INT_MIN); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + $zip->setCompressionIndex(4, PHP_INT_MAX); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + var_dump($zip->setCompressionIndex(4, ZipArchive::CM_STORE)); var_dump($zip->setCompressionIndex(5, ZipArchive::CM_DEFLATE)); var_dump($zip->setCompressionIndex(6, ZipArchive::CM_DEFAULT)); @@ -57,6 +81,10 @@ unlink($tmpfile); bool(true) bool(true) bool(true) +ZipArchive::setCompressionName(): Argument #2 ($method) must be between 0 and %d +ZipArchive::setCompressionName(): Argument #2 ($method) must be between 0 and %d +ZipArchive::setCompressionIndex(): Argument #2 ($method) must be between -1 and %d +ZipArchive::setCompressionIndex(): Argument #2 ($method) must be between -1 and %d bool(true) bool(true) bool(true) diff --git a/ext/zip/zip_stream.c b/ext/zip/zip_stream.c index 76e4588f249e..50f097de3c87 100644 --- a/ext/zip/zip_stream.c +++ b/ext/zip/zip_stream.c @@ -150,7 +150,7 @@ static int php_zip_ops_stat(php_stream *stream, php_stream_statbuf *ssb) /* {{{ fragment++; if (ZIP_OPENBASEDIR_CHECKPATH(file_dirname)) { - zend_string_release_ex(file_basename, 0); + zend_string_release_ex(file_basename, false); return -1; } @@ -159,7 +159,7 @@ static int php_zip_ops_stat(php_stream *stream, php_stream_statbuf *ssb) /* {{{ memset(ssb, 0, sizeof(php_stream_statbuf)); if (zip_stat(za, fragment, ZIP_FL_NOCASE, &sb) != 0) { zip_close(za); - zend_string_release_ex(file_basename, 0); + zend_string_release_ex(file_basename, false); return -1; } zip_close(za); @@ -183,7 +183,7 @@ static int php_zip_ops_stat(php_stream *stream, php_stream_statbuf *ssb) /* {{{ #endif ssb->sb.st_ino = -1; } - zend_string_release_ex(file_basename, 0); + zend_string_release_ex(file_basename, false); return 0; } /* }}} */ @@ -312,7 +312,7 @@ php_stream *php_stream_zip_opener(php_stream_wrapper *wrapper, fragment++; if (ZIP_OPENBASEDIR_CHECKPATH(file_dirname)) { - zend_string_release_ex(file_basename, 0); + zend_string_release_ex(file_basename, false); return NULL; } @@ -344,14 +344,14 @@ php_stream *php_stream_zip_opener(php_stream_wrapper *wrapper, } if (opened_path) { - *opened_path = zend_string_init(path, strlen(path), 0); + *opened_path = zend_string_init(path, path_len, false); } } else { zip_close(za); } } - zend_string_release_ex(file_basename, 0); + zend_string_release_ex(file_basename, false); if (!stream) { return NULL;