mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Zip: add support for encrypted archive
This commit is contained in:
parent
859a6505d3
commit
402eeb8598
8 changed files with 256 additions and 17 deletions
2
NEWS
2
NEWS
|
@ -160,6 +160,6 @@ PHP NEWS
|
|||
. Use Zend MM for allocation in bundled libxmlrpc (Joe)
|
||||
|
||||
- ZIP:
|
||||
. Fixed bug #70103 (ZipArchive::addGlob ignores remove_all_path option). (cmb)
|
||||
. Add support for encrypted archives. (Remi)
|
||||
|
||||
<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>
|
||||
|
|
12
UPGRADING
12
UPGRADING
|
@ -100,6 +100,18 @@ PHP 7.2 UPGRADE NOTES
|
|||
(https://wiki.php.net/rfc/argon2_password_hash).
|
||||
. proc_nice() is now supported on Windows platforms.
|
||||
|
||||
- Zip:
|
||||
. read/write encrypted archive, relying on libzip 1.2.0,
|
||||
using new methods:
|
||||
ZipArchive::setEncryptionName($name, $method [, $password]);
|
||||
ZipArchive::setEncryptionIndex($index, $method [, $password]);
|
||||
and new constants:
|
||||
ZipArchive::EM_NONE
|
||||
ZipArchive::EM_AES_128
|
||||
ZipArchive::EM_AES_192
|
||||
ZipArchive::EM_AES_256
|
||||
. accept 'password' from zip stream context
|
||||
|
||||
========================================
|
||||
3. Changes in SAPI modules
|
||||
========================================
|
||||
|
|
|
@ -99,6 +99,15 @@ if test "$PHP_ZIP" != "no"; then
|
|||
-L$LIBZIP_LIBDIR
|
||||
])
|
||||
|
||||
PHP_CHECK_LIBRARY(zip, zip_file_set_encryption,
|
||||
[
|
||||
PHP_ADD_LIBRARY_WITH_PATH(zip, $LIBZIP_LIBDIR, ZIP_SHARED_LIBADD)
|
||||
AC_DEFINE(HAVE_ENCRYPTION, 1, [Libzip >= 1.2.0 with encryption support])
|
||||
], [
|
||||
], [
|
||||
-L$LIBZIP_LIBDIR
|
||||
])
|
||||
|
||||
AC_DEFINE(HAVE_ZIP,1,[ ])
|
||||
PHP_NEW_EXTENSION(zip, php_zip.c zip_stream.c, $ext_shared,, $LIBZIP_CFLAGS)
|
||||
PHP_SUBST(ZIP_SHARED_LIBADD)
|
||||
|
|
49
ext/zip/examples/encryption.php
Normal file
49
ext/zip/examples/encryption.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
error_reporting(E_ALL);
|
||||
if (!extension_loaded('zip')) {
|
||||
dl('zip.so');
|
||||
}
|
||||
|
||||
$name = __DIR__ . '/encrypted.zip';
|
||||
$pass = 'secret';
|
||||
$file = 'foo.php';
|
||||
|
||||
echo "== Create with per file password\n";
|
||||
|
||||
$zip = new ZipArchive;
|
||||
$zip->open($name, ZIPARCHIVE::CREATE | ZipArchive::OVERWRITE);
|
||||
$zip->addFile(__FILE__, $file);
|
||||
$zip->setEncryptionName($file, ZipArchive::EM_AES_256, $pass);
|
||||
$zip->close();
|
||||
|
||||
echo "== Create with global password\n";
|
||||
|
||||
$zip = new ZipArchive;
|
||||
$zip->open($name, ZIPARCHIVE::CREATE | ZipArchive::OVERWRITE);
|
||||
$zip->setPassword($pass);
|
||||
$zip->addFile(__FILE__, $file);
|
||||
$zip->setEncryptionName($file, ZipArchive::EM_AES_256);
|
||||
$zip->close();
|
||||
|
||||
echo "== Stat\n";
|
||||
|
||||
$zip->open($name);
|
||||
print_r($zip->statName($file));
|
||||
|
||||
echo "== Read\n";
|
||||
|
||||
$zip->setPassword($pass);
|
||||
$text = $zip->getFromName($file);
|
||||
printf("Size = %d\n", strlen($text));
|
||||
$zip->close();
|
||||
|
||||
echo "== Stream with context\n";
|
||||
|
||||
$ctx = stream_context_create(array(
|
||||
'zip' => array(
|
||||
'password' => $pass
|
||||
)
|
||||
));
|
||||
$text = file_get_contents("zip://$name#$file", false, $ctx);
|
||||
printf("Size = %d\n", strlen($text));
|
||||
|
|
@ -379,6 +379,20 @@ static int php_zip_parse_options(zval *options, zend_long *remove_all_path, char
|
|||
/* }}} */
|
||||
|
||||
/* {{{ RETURN_SB(sb) */
|
||||
#ifdef HAVE_ENCRYPTION
|
||||
#define RETURN_SB(sb) \
|
||||
{ \
|
||||
array_init(return_value); \
|
||||
add_ascii_assoc_string(return_value, "name", (char *)(sb)->name); \
|
||||
add_ascii_assoc_long(return_value, "index", (zend_long) (sb)->index); \
|
||||
add_ascii_assoc_long(return_value, "crc", (zend_long) (sb)->crc); \
|
||||
add_ascii_assoc_long(return_value, "size", (zend_long) (sb)->size); \
|
||||
add_ascii_assoc_long(return_value, "mtime", (zend_long) (sb)->mtime); \
|
||||
add_ascii_assoc_long(return_value, "comp_size", (zend_long) (sb)->comp_size); \
|
||||
add_ascii_assoc_long(return_value, "comp_method", (zend_long) (sb)->comp_method); \
|
||||
add_ascii_assoc_long(return_value, "encryption_method", (zend_long) (sb)->encryption_method); \
|
||||
}
|
||||
#else
|
||||
#define RETURN_SB(sb) \
|
||||
{ \
|
||||
array_init(return_value); \
|
||||
|
@ -390,6 +404,7 @@ static int php_zip_parse_options(zval *options, zend_long *remove_all_path, char
|
|||
add_ascii_assoc_long(return_value, "comp_size", (zend_long) (sb)->comp_size); \
|
||||
add_ascii_assoc_long(return_value, "comp_method", (zend_long) (sb)->comp_method); \
|
||||
}
|
||||
#endif
|
||||
/* }}} */
|
||||
|
||||
static int php_zip_status(struct zip *za) /* {{{ */
|
||||
|
@ -2222,6 +2237,74 @@ static ZIPARCHIVE_METHOD(getExternalAttributesIndex)
|
|||
/* }}} */
|
||||
#endif /* ifdef ZIP_OPSYS_DEFAULT */
|
||||
|
||||
#ifdef HAVE_ENCRYPTION
|
||||
/* {{{ proto bool ZipArchive::setEncryptionName(string name, int method, [string password])
|
||||
Set encryption method for file in zip, using its name */
|
||||
static ZIPARCHIVE_METHOD(setEncryptionName)
|
||||
{
|
||||
struct zip *intern;
|
||||
zval *self = getThis();
|
||||
zend_long method;
|
||||
zip_int64_t idx;
|
||||
char *name, *password = NULL;
|
||||
size_t name_len, password_len;
|
||||
|
||||
if (!self) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
ZIP_FROM_OBJECT(intern, self);
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|s",
|
||||
&name, &name_len, &method, &password, &password_len) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (name_len < 1) {
|
||||
php_error_docref(NULL, E_NOTICE, "Empty string as entry name");
|
||||
}
|
||||
|
||||
idx = zip_name_locate(intern, name, 0);
|
||||
if (idx < 0) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
if (zip_file_set_encryption(intern, idx, (zip_uint16_t)method, password)) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
RETURN_TRUE;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto bool ZipArchive::setEncryptionIndex(int index, int method, [string password])
|
||||
Set encryption method for file in zip, using its index */
|
||||
static ZIPARCHIVE_METHOD(setEncryptionIndex)
|
||||
{
|
||||
struct zip *intern;
|
||||
zval *self = getThis();
|
||||
zend_long index, method;
|
||||
char *password = NULL;
|
||||
size_t password_len;
|
||||
|
||||
if (!self) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
ZIP_FROM_OBJECT(intern, self);
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|s",
|
||||
&index, &method, &password, &password_len) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (zip_file_set_encryption(intern, index, (zip_uint16_t)method, password)) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
RETURN_TRUE;
|
||||
}
|
||||
/* }}} */
|
||||
#endif
|
||||
|
||||
/* {{{ proto string ZipArchive::getCommentName(string name[, int flags])
|
||||
Returns the comment of an entry using its name */
|
||||
static ZIPARCHIVE_METHOD(getCommentName)
|
||||
|
@ -2952,6 +3035,20 @@ ZEND_END_ARG_INFO()
|
|||
#endif /* ifdef ZIP_OPSYS_DEFAULT */
|
||||
/* }}} */
|
||||
|
||||
#ifdef HAVE_ENCRYPTION
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setencryption_name, 0, 0, 2)
|
||||
ZEND_ARG_INFO(0, name)
|
||||
ZEND_ARG_INFO(0, method)
|
||||
ZEND_ARG_INFO(0, password)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setencryption_index, 0, 0, 2)
|
||||
ZEND_ARG_INFO(0, index)
|
||||
ZEND_ARG_INFO(0, method)
|
||||
ZEND_ARG_INFO(0, password)
|
||||
ZEND_END_ARG_INFO()
|
||||
#endif
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setcompname, 0, 0, 2)
|
||||
ZEND_ARG_INFO(0, name)
|
||||
ZEND_ARG_INFO(0, method)
|
||||
|
@ -3003,6 +3100,10 @@ static const zend_function_entry zip_class_functions[] = {
|
|||
ZIPARCHIVE_ME(getExternalAttributesIndex, arginfo_ziparchive_getextattrindex, ZEND_ACC_PUBLIC)
|
||||
ZIPARCHIVE_ME(setCompressionName, arginfo_ziparchive_setcompname, ZEND_ACC_PUBLIC)
|
||||
ZIPARCHIVE_ME(setCompressionIndex, arginfo_ziparchive_setcompindex, ZEND_ACC_PUBLIC)
|
||||
#ifdef HAVE_ENCRYPTION
|
||||
ZIPARCHIVE_ME(setEncryptionName, arginfo_ziparchive_setencryption_name, ZEND_ACC_PUBLIC)
|
||||
ZIPARCHIVE_ME(setEncryptionIndex, arginfo_ziparchive_setencryption_index, ZEND_ACC_PUBLIC)
|
||||
#endif
|
||||
PHP_FE_END
|
||||
};
|
||||
/* }}} */
|
||||
|
@ -3047,6 +3148,7 @@ static PHP_MINIT_FUNCTION(zip)
|
|||
REGISTER_ZIP_CLASS_CONST_LONG("FL_NODIR", ZIP_FL_NODIR);
|
||||
REGISTER_ZIP_CLASS_CONST_LONG("FL_COMPRESSED", ZIP_FL_COMPRESSED);
|
||||
REGISTER_ZIP_CLASS_CONST_LONG("FL_UNCHANGED", ZIP_FL_UNCHANGED);
|
||||
|
||||
#ifdef ZIP_FL_ENC_GUESS
|
||||
/* Default filename encoding policy. */
|
||||
REGISTER_ZIP_CLASS_CONST_LONG("FL_ENC_GUESS", ZIP_FL_ENC_GUESS);
|
||||
|
@ -3064,20 +3166,6 @@ static PHP_MINIT_FUNCTION(zip)
|
|||
REGISTER_ZIP_CLASS_CONST_LONG("FL_ENC_CP437", ZIP_FL_ENC_CP437);
|
||||
#endif
|
||||
|
||||
/* XXX The below are rather not implemented or to check whether makes sense to expose. */
|
||||
/*#ifdef ZIP_FL_RECOMPRESS
|
||||
REGISTER_ZIP_CLASS_CONST_LONG("FL_RECOMPRESS", ZIP_FL_RECOMPRESS);
|
||||
#endif
|
||||
#ifdef ZIP_FL_ENCRYPTED
|
||||
REGISTER_ZIP_CLASS_CONST_LONG("FL_ENCRYPTED", ZIP_FL_ENCRYPTED);
|
||||
#endif
|
||||
#ifdef ZIP_FL_LOCAL
|
||||
REGISTER_ZIP_CLASS_CONST_LONG("FL_LOCAL", ZIP_FL_LOCAL);
|
||||
#endif
|
||||
#ifdef ZIP_FL_CENTRAL
|
||||
REGISTER_ZIP_CLASS_CONST_LONG("FL_CENTRAL", ZIP_FL_CENTRAL);
|
||||
#endif */
|
||||
|
||||
REGISTER_ZIP_CLASS_CONST_LONG("CM_DEFAULT", ZIP_CM_DEFAULT);
|
||||
REGISTER_ZIP_CLASS_CONST_LONG("CM_STORE", ZIP_CM_STORE);
|
||||
REGISTER_ZIP_CLASS_CONST_LONG("CM_SHRINK", ZIP_CM_SHRINK);
|
||||
|
@ -3147,6 +3235,13 @@ static PHP_MINIT_FUNCTION(zip)
|
|||
REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_DEFAULT", ZIP_OPSYS_DEFAULT);
|
||||
#endif /* ifdef ZIP_OPSYS_DEFAULT */
|
||||
|
||||
#ifdef HAVE_ENCRYPTION
|
||||
REGISTER_ZIP_CLASS_CONST_LONG("EM_NONE", ZIP_EM_NONE);
|
||||
REGISTER_ZIP_CLASS_CONST_LONG("EM_AES_128", ZIP_EM_AES_128);
|
||||
REGISTER_ZIP_CLASS_CONST_LONG("EM_AES_192", ZIP_EM_AES_192);
|
||||
REGISTER_ZIP_CLASS_CONST_LONG("EM_AES_256", ZIP_EM_AES_256);
|
||||
#endif
|
||||
|
||||
php_register_url_stream_wrapper("zip", &php_stream_zip_wrapper);
|
||||
|
||||
le_zip_dir = zend_register_list_destructors_ex(php_zip_free_dir, NULL, le_zip_dir_name, module_number);
|
||||
|
|
|
@ -37,7 +37,7 @@ extern zend_module_entry zip_module_entry;
|
|||
#define ZIP_OVERWRITE ZIP_TRUNCATE
|
||||
#endif
|
||||
|
||||
#define PHP_ZIP_VERSION "1.13.5"
|
||||
#define PHP_ZIP_VERSION "1.14.0-dev"
|
||||
|
||||
#define ZIP_OPENBASEDIR_CHECKPATH(filename) php_check_open_basedir(filename)
|
||||
|
||||
|
|
66
ext/zip/tests/oo_encryption.phpt
Normal file
66
ext/zip/tests/oo_encryption.phpt
Normal file
|
@ -0,0 +1,66 @@
|
|||
--TEST--
|
||||
ZipArchive::setEncryption*() functions
|
||||
--SKIPIF--
|
||||
<?php
|
||||
/* $Id$ */
|
||||
if (!extension_loaded('zip')) die('skip');
|
||||
if (!method_exists('ZipArchive', 'setEncryptionName')) die('skip encrytion not supported');
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$name = __DIR__ . '/encrypted.zip';
|
||||
$pass = 'secret';
|
||||
|
||||
echo "== Write\n";
|
||||
$zip = new ZipArchive;
|
||||
$r = $zip->open($name, ZIPARCHIVE::CREATE);
|
||||
// Clear
|
||||
$zip->addFromString('foo.txt', 'foo');
|
||||
// Encrypted
|
||||
$zip->addFromString('bar.txt', 'bar');
|
||||
var_dump($zip->setEncryptionName('bar.txt', 9999, $pass)); // Fails
|
||||
var_dump($zip->setEncryptionName('bar.txt', ZipArchive::EM_AES_256, $pass));
|
||||
$zip->close();
|
||||
|
||||
echo "== Read\n";
|
||||
$r = $zip->open($name);
|
||||
$s = $zip->statName('foo.txt');
|
||||
var_dump($s['encryption_method'] === ZipArchive::EM_NONE);
|
||||
$s = $zip->statName('bar.txt');
|
||||
var_dump($s['encryption_method'] === ZipArchive::EM_AES_256);
|
||||
var_dump($zip->getFromName('foo.txt')); // Clear, ok
|
||||
var_dump($zip->getFromName('bar.txt')); // Encrypted, fails
|
||||
$zip->setPassword($pass);
|
||||
var_dump($zip->getFromName('bar.txt')); // Ecnrypted, ok
|
||||
$zip->close();
|
||||
|
||||
echo "== Stream\n";
|
||||
var_dump(file_get_contents("zip://$name#foo.txt")); // Clear, ok
|
||||
var_dump(file_get_contents("zip://$name#bar.txt")); // Encrypted, fails
|
||||
$ctx = stream_context_create(array('zip' => array('password' => $pass)));
|
||||
var_dump(file_get_contents("zip://$name#bar.txt", false, $ctx)); // Ecnrypted, ok
|
||||
?>
|
||||
== Done
|
||||
--CLEAN--
|
||||
<?php
|
||||
$name = __DIR__ . '/encrypted.zip';
|
||||
@unlink($name);
|
||||
?>
|
||||
--EXPECTF--
|
||||
== Write
|
||||
bool(false)
|
||||
bool(true)
|
||||
== Read
|
||||
bool(true)
|
||||
bool(true)
|
||||
string(3) "foo"
|
||||
bool(false)
|
||||
string(3) "bar"
|
||||
== Stream
|
||||
string(3) "foo"
|
||||
|
||||
Warning: file_get_contents(%s): failed to open stream: operation failed in %s on line %d
|
||||
bool(false)
|
||||
string(3) "bar"
|
||||
== Done
|
|
@ -309,6 +309,14 @@ php_stream *php_stream_zip_opener(php_stream_wrapper *wrapper,
|
|||
|
||||
za = zip_open(file_dirname, ZIP_CREATE, &err);
|
||||
if (za) {
|
||||
zval *tmpzval;
|
||||
|
||||
if (NULL != (tmpzval = php_stream_context_get_option(context, "zip", "password"))) {
|
||||
if (Z_TYPE_P(tmpzval) != IS_STRING || zip_set_default_password(za, Z_STRVAL_P(tmpzval))) {
|
||||
php_error_docref(NULL, E_WARNING, "Can't set zip password");
|
||||
}
|
||||
}
|
||||
|
||||
zf = zip_fopen(za, fragment, 0);
|
||||
if (zf) {
|
||||
self = emalloc(sizeof(*self));
|
||||
|
@ -348,7 +356,7 @@ static php_stream_wrapper_ops zip_stream_wops = {
|
|||
NULL, /* rename */
|
||||
NULL, /* mkdir */
|
||||
NULL, /* rmdir */
|
||||
NULL
|
||||
NULL /* metadata */
|
||||
};
|
||||
|
||||
php_stream_wrapper php_stream_zip_wrapper = {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue