[DOC] implement full signature support for tar-based archives. By default, data archives do

not have a signature, but can have one set via setSignatureAlgorithm()
This commit is contained in:
Greg Beaver 2008-06-13 22:07:44 +00:00
parent 5ff4deaa53
commit 4f9daeed11
35 changed files with 315 additions and 25 deletions

Binary file not shown.

View File

@ -420,7 +420,7 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_
int phar_free_alias(phar_archive_data *phar, char *alias, int alias_len TSRMLS_DC);
int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, char *alias, int alias_len, char **error TSRMLS_DC);
int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
int phar_verify_signature(php_stream *fp, size_t end_of_phar, int sig_type, char *sig, int sig_len, char *fname, char **signature, int *signature_len, char **error TSRMLS_DC);
int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_type, char *sig, int sig_len, char *fname, char **signature, int *signature_len, char **error TSRMLS_DC);
int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signature, int *signature_length, char **error TSRMLS_DC);
/* utility functions */

View File

@ -2707,11 +2707,6 @@ PHP_METHOD(Phar, setSignatureAlgorithm)
"Cannot set signature algorithm, phar is read-only");
return;
}
if (phar_obj->arc.archive->is_tar) {
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
"Cannot set signature algorithm, not possible with tar-based phar archives");
return;
}
if (phar_obj->arc.archive->is_zip) {
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
"Cannot set signature algorithm, not possible with zip-based phar archives");

View File

@ -243,6 +243,86 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias,
size = entry.uncompressed_filesize = entry.compressed_filesize =
phar_tar_number(hdr->size, sizeof(hdr->size));
if (((!old && hdr->prefix[0] == 0) || old) && strlen(hdr->name) == sizeof(".phar/signature.bin")-1 && !strncmp(hdr->name, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
size_t read;
if (size > 511) {
if (error) {
spprintf(error, 4096, "phar error: tar-based phar \"%s\" has signature that is larger than 511 bytes, cannot process", fname);
}
bail:
php_stream_close(fp);
zend_hash_destroy(&myphar->manifest);
myphar->manifest.arBuckets = 0;
zend_hash_destroy(&myphar->mounted_dirs);
myphar->mounted_dirs.arBuckets = 0;
pefree(myphar, myphar->is_persistent);
return FAILURE;
}
read = php_stream_read(fp, buf, size);
if (read != size) {
if (error) {
spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be read", fname);
}
goto bail;
}
#ifdef WORDS_BIGENDIAN
# define PHAR_GET_32(buffer) \
(((((unsigned char*)(buffer))[3]) << 24) \
| ((((unsigned char*)(buffer))[2]) << 16) \
| ((((unsigned char*)(buffer))[1]) << 8) \
| (((unsigned char*)(buffer))[0]))
#else
# define PHAR_GET_32(buffer) (php_uint32) *(buffer)
#endif
if (FAILURE == phar_verify_signature(fp, php_stream_tell(fp) - size - 512, PHAR_GET_32(buf), buf + 8, PHAR_GET_32(buf + 4), fname, &myphar->signature, &myphar->sig_len, error TSRMLS_CC)) {
if (error) {
char *save = *error;
spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be verified: %s", fname, save);
efree(save);
}
goto bail;
}
/* signature checked out, let's ensure this is the last file in the phar */
size = ((size+511)&~511) + 512;
if (((hdr->typeflag == 0) || (hdr->typeflag == TAR_FILE)) && size > 0) {
/* this is not good enough - seek succeeds even on truncated tars */
php_stream_seek(fp, size, SEEK_CUR);
if ((uint)php_stream_tell(fp) > totalsize) {
if (error) {
spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
}
php_stream_close(fp);
zend_hash_destroy(&myphar->manifest);
myphar->manifest.arBuckets = 0;
zend_hash_destroy(&myphar->mounted_dirs);
myphar->mounted_dirs.arBuckets = 0;
pefree(myphar, myphar->is_persistent);
return FAILURE;
}
}
read = php_stream_read(fp, buf, sizeof(buf));
if (read != sizeof(buf)) {
if (error) {
spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
}
php_stream_close(fp);
zend_hash_destroy(&myphar->manifest);
myphar->manifest.arBuckets = 0;
zend_hash_destroy(&myphar->mounted_dirs);
myphar->mounted_dirs.arBuckets = 0;
pefree(myphar, myphar->is_persistent);
return FAILURE;
}
hdr = (tar_header*) buf;
sum1 = phar_tar_number(hdr->checksum, sizeof(hdr->checksum));
if (sum1 == 0 && phar_tar_checksum(buf, sizeof(buf)) == 0) {
break;
}
if (error) {
spprintf(error, 4096, "phar error: \"%s\" has entries after signature, invalid phar", fname);
}
goto bail;
}
if (!old && hdr->prefix[0] != 0) {
char name[256];
@ -419,6 +499,21 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias,
return FAILURE;
}
} while (read != 0);
/* ensure signature set */
if (PHAR_G(require_hash) && !myphar->signature) {
php_stream_close(fp);
zend_hash_destroy(&myphar->manifest);
myphar->manifest.arBuckets = 0;
zend_hash_destroy(&myphar->mounted_dirs);
myphar->mounted_dirs.arBuckets = 0;
pefree(myphar, myphar->is_persistent);
if (error) {
spprintf(error, 0, "tar-based phar \"%s\" does not have a signature", fname);
}
return FAILURE;
}
myphar->fname = pestrndup(fname, fname_len, myphar->is_persistent);
#ifdef PHP_WIN32
phar_unixify_path_separators(myphar->fname, fname_len);
@ -712,9 +807,9 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defau
phar_entry_info entry = {0};
static const char newstub[] = "<?php // tar-based phar archive stub file\n__HALT_COMPILER();";
php_stream *oldfile, *newfile, *stubfile;
int closeoldfile, free_user_stub;
int closeoldfile, free_user_stub, signature_length;
struct _phar_pass_tar_info pass;
char *buf;
char *buf, *signature, sigbuf[8];
entry.flags = PHAR_ENT_PERM_DEF_FILE;
entry.timestamp = time(NULL);
@ -734,7 +829,6 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defau
entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
entry.filename_len = sizeof(".phar/alias.txt")-1;
entry.fp = php_stream_fopen_tmpfile();
entry.crc32 = phar_tar_checksum(phar->alias, phar->alias_len);
if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
if (error) {
spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname);
@ -926,6 +1020,62 @@ nostub:
zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_writeheaders, (void *) &pass TSRMLS_CC);
/* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
if (!phar->is_data || phar->sig_flags) {
if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, error TSRMLS_CC)) {
if (error) {
char *save = *error;
spprintf(error, 0, "phar error: unable to write signature to tar-based phar: %s", save);
efree(save);
}
if (closeoldfile) {
php_stream_close(oldfile);
}
php_stream_close(newfile);
return EOF;
}
entry.filename = ".phar/signature.bin";
entry.filename_len = sizeof(".phar/signature.bin")-1;
entry.fp = php_stream_fopen_tmpfile();
#ifdef WORDS_BIGENDIAN
# define PHAR_SET_32(var, buffer) \
*(php_uint32 *)(var) = (((((unsigned char*)(buffer))[3]) << 24) \
| ((((unsigned char*)(buffer))[2]) << 16) \
| ((((unsigned char*)(buffer))[1]) << 8) \
| (((unsigned char*)(buffer))[0]))
#else
# define PHAR_SET_32(var, buffer) *(php_uint32 *)(var) = (php_uint32) (buffer)
#endif
PHAR_SET_32(sigbuf, phar->sig_flags);
PHAR_SET_32(sigbuf + 4, signature_length);
if (8 != php_stream_write(entry.fp, sigbuf, 8) || signature_length != php_stream_write(entry.fp, signature, signature_length)) {
efree(signature);
if (error) {
spprintf(error, 0, "phar error: unable to write signature to tar-based phar %s", phar->fname);
}
if (closeoldfile) {
php_stream_close(oldfile);
}
php_stream_close(newfile);
return EOF;
}
efree(signature);
entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
/* throw out return value and write the signature */
entry.filename_len = phar_tar_writeheaders((void *)&entry, (void *)&pass);
if (error && *error) {
if (closeoldfile) {
php_stream_close(oldfile);
}
/* error is set by writeheaders */
php_stream_close(newfile);
return EOF;
}
} /* signature */
/* add final zero blocks */
buf = (char *) ecalloc(1024, 1);
php_stream_write(newfile, buf, 1024);

View File

@ -77,11 +77,6 @@ $a->setSignatureAlgorithm(Phar::MD5);
echo $e->getMessage() . "\n";
}
try {
$b->setSignatureAlgorithm(Phar::MD5);
} catch (Exception $e) {
echo $e->getMessage() . "\n";
}
try {
$c->setSignatureAlgorithm(Phar::MD5);
} catch (Exception $e) {
echo $e->getMessage() . "\n";
@ -165,7 +160,6 @@ A Phar stub cannot be set in a plain tar archive
Warning: Phar::setDefaultStub() expects parameter 1 to be string, array given in %sbadparameters.php on line %d
Cannot change stub: phar.readonly=1
Cannot set signature algorithm, phar is read-only
Cannot set signature algorithm, not possible with tar-based phar archives
Cannot set signature algorithm, not possible with zip-based phar archives
Warning: Phar::compress() expects parameter 1 to be long, array given in %sbadparameters.php on line %d

View File

@ -139,12 +139,6 @@ $data->setDefaultStub();
} catch (Exception $e) {
echo $e->getMessage() . "\n";
}
try {
$data->setSignatureAlgorithm(Phar::MD5);
} catch (Exception $e) {
echo $e->getMessage() . "\n";
}
try {
$tgz->convertToData(Phar::TAR, Phar::GZ, '.phar.tgz.oops');
} catch (Exception $e) {
@ -211,7 +205,6 @@ Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2
A Phar stub cannot be set in a plain tar archive
A Phar alias cannot be set in a plain tar archive
A Phar stub cannot be set in a plain tar archive
Cannot set signature algorithm, not possible with tar-based phar archives
data phar "%sphar_convert_again2.phar.tgz.oops" has invalid extension phar.tgz.oops
phar "%sphar_convert_again2.tgz.oops" has invalid extension tgz.oops
data phar "%sphar_convert_again2.phar/.tgz.oops" has invalid extension phar/.tgz.oops

View File

@ -10,7 +10,7 @@ phar.readonly=0
--FILE--
<?php
$fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar';
$p = new Phar(dirname(__FILE__) . '/brandnewphar.phar', 0, 'brandnewphar.phar');
$p = new Phar($fname);
$p['file1.txt'] = 'hi';
var_dump($p->getSignature());
$p->setSignatureAlgorithm(Phar::MD5);
@ -42,7 +42,7 @@ echo $e->getMessage();
===DONE===
--CLEAN--
<?php
unlink(dirname(__FILE__) . '/brandnewphar.phar');
unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phar');
?>
--EXPECTF--
array(2) {

View File

@ -5,6 +5,7 @@ Phar: test stat function interceptions and is_file/is_link edge cases (PHP 5.2)
<?php if (substr(phpversion(), 0, 3) != '5.2') die("skip PHP 5.2 required for this test");?>
--INI--
phar.readonly=0
phar.require_hash=0
--FILE--
<?php
Phar::interceptFileFuncs();

View File

@ -5,6 +5,7 @@ Phar: test stat function interceptions and is_file/is_link edge cases (PHP 5.3+)
<?php if (substr(phpversion(), 0, 3) == '5.2') die("skip PHP 5.3+ required for this test");?>
--INI--
phar.readonly=0
phar.require_hash=0
--FILE--
<?php
Phar::interceptFileFuncs();

View File

@ -2,6 +2,8 @@
Phar: tar with huge filenames
--SKIPIF--
<?php if (!extension_loaded("phar")) die("skip"); ?>
--INI--
phar.require_hash=0
--FILE--
<?php
$fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.tar';

View File

@ -2,6 +2,8 @@
Phar: tar with hard link and symbolic link
--SKIPIF--
<?php if (!extension_loaded("phar")) die("skip"); ?>
--INI--
phar.require_hash=0
--FILE--
<?php
$fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.tar';

View File

@ -2,6 +2,8 @@
Phar: tar with link to absolute path
--SKIPIF--
<?php if (!extension_loaded("phar")) die("skip"); ?>
--INI--
phar.require_hash=0
--FILE--
<?php
try {

View File

@ -2,6 +2,8 @@
Phar: tar with link to root directory file from root directory file
--SKIPIF--
<?php if (!extension_loaded("phar")) die("skip"); ?>
--INI--
phar.require_hash=0
--FILE--
<?php
try {

View File

@ -2,6 +2,8 @@
Phar: tar with relative link to subdirectory file from subdirectory file
--SKIPIF--
<?php if (!extension_loaded("phar")) die("skip"); ?>
--INI--
phar.require_hash=0
--FILE--
<?php
try {

View File

@ -0,0 +1,84 @@
--TEST--
Phar::setSupportedSignatures() with hash, tar-based
--SKIPIF--
<?php if (!extension_loaded("phar")) die("skip"); ?>
<?php if (!extension_loaded("hash")) die("skip extension hash required"); ?>
<?php if (!extension_loaded("openssl")) die("skip extension openssl required"); ?>
--INI--
phar.require_hash=0
phar.readonly=0
--FILE--
<?php
$fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.tar';
$p = new Phar($fname);
$p['file1.txt'] = 'hi';
var_dump($p->getSignature());
$p->setSignatureAlgorithm(Phar::MD5);
var_dump($p->getSignature());
$p->setSignatureAlgorithm(Phar::SHA1);
var_dump($p->getSignature());
try {
$p->setSignatureAlgorithm(Phar::SHA256);
var_dump($p->getSignature());
} catch (Exception $e) {
echo $e->getMessage();
}
try {
$p->setSignatureAlgorithm(Phar::SHA512);
var_dump($p->getSignature());
} catch (Exception $e) {
echo $e->getMessage();
}
try {
$private = openssl_get_privatekey(file_get_contents(dirname(dirname(__FILE__)) . '/files/private.pem'));
$pkey = '';
openssl_pkey_export($private, $pkey);
$p->setSignatureAlgorithm(Phar::OPENSSL, $pkey);
var_dump($p->getSignature());
} catch (Exception $e) {
echo $e->getMessage();
}
?>
===DONE===
--CLEAN--
<?php
unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phar.tar');
?>
--EXPECTF--
array(2) {
["hash"]=>
string(%d) "%s"
["hash_type"]=>
string(5) "SHA-1"
}
array(2) {
["hash"]=>
string(%d) "%s"
["hash_type"]=>
string(3) "MD5"
}
array(2) {
["hash"]=>
string(%d) "%s"
["hash_type"]=>
string(5) "SHA-1"
}
array(2) {
["hash"]=>
string(%d) "%s"
["hash_type"]=>
string(7) "SHA-256"
}
array(2) {
["hash"]=>
string(%d) "%s"
["hash_type"]=>
string(7) "SHA-512"
}
array(2) {
["hash"]=>
string(%d) "%s"
["hash_type"]=>
string(7) "OpenSSL"
}
===DONE===

View File

@ -0,0 +1,56 @@
--TEST--
Phar: tar-based phar, require_hash=1, no signature
--SKIPIF--
<?php if (!extension_loaded('phar')) die('skip'); ?>
<?php if (!extension_loaded("spl")) die("skip SPL not available"); ?>
--INI--
phar.readonly=1
phar.require_hash=0
--FILE--
<?php
ini_set('phar.require_hash', 1);
include dirname(__FILE__) . '/files/tarmaker.php.inc';
$fname = dirname(__FILE__) . '/tar_004.phar.tar';
$alias = 'phar://' . $fname;
$fname2 = dirname(__FILE__) . '/tar_004.tar';
$tar = new tarmaker($fname, 'none');
$tar->init();
$tar->addFile('tar_004.php', '<?php var_dump(__FILE__);');
$tar->addFile('internal/file/here', "hi there!\n");
$tar->close();
try {
$phar = new Phar($fname);
var_dump($phar->getStub());
} catch (Exception $e) {
echo $e->getMessage()."\n";
}
ini_set('phar.require_hash', 0);
try {
$phar = new PharData($fname2);
$phar['file'] = 'hi';
var_dump($phar->getSignature());
$phar->setSignatureAlgorithm(Phar::MD5);
var_dump($phar->getSignature());
} catch (Exception $e) {
echo $e->getMessage()."\n";
}
?>
===DONE===
--CLEAN--
<?php
@unlink(dirname(__FILE__) . '/tar_004.phar.tar');
@unlink(dirname(__FILE__) . '/tar_004.tar');
?>
--EXPECTF--
tar-based phar "%star_004.phar.tar" does not have a signature
bool(false)
array(2) {
["hash"]=>
string(32) "%s"
["hash_type"]=>
string(3) "MD5"
}
===DONE===

View File

@ -5,6 +5,7 @@ Phar: tar-based phar, valid 1
<?php if (!extension_loaded("spl")) die("skip SPL not available"); ?>
--INI--
phar.readonly=0
phar.require_hash=0
--FILE--
<?php
include dirname(__FILE__) . '/files/tarmaker.php.inc';

View File

@ -5,6 +5,7 @@ Phar: tar-based phar, tar phar with stub, mapPhar()
<?php if (!extension_loaded("spl")) die("skip SPL not available"); ?>
--INI--
phar.readonly=0
phar.require_hash=0
--FILE--
<?php
include dirname(__FILE__) . '/files/tarmaker.php.inc';

View File

@ -6,6 +6,7 @@ Phar: tar-based phar, bzipped tar
<?php if (!extension_loaded("bz2")) die("skip bz2 not available"); ?>
--INI--
phar.readonly=0
phar.require_hash=0
--FILE--
<?php
include dirname(__FILE__) . '/files/tarmaker.php.inc';

View File

@ -7,6 +7,7 @@ Phar: tar-based phar, gzipped tar
<?php if (version_compare(phpversion(), '5.2.6', '<')) die("skip zlib is buggy in PHP < 5.2.6"); ?>
--INI--
phar.readonly=0
phar.require_hash=0
--FILE--
<?php
include dirname(__FILE__) . '/files/tarmaker.php.inc';

View File

@ -5,6 +5,7 @@ Phar: tar-based phar, third-party tar with no stub, Phar->getStub()
<?php if (!extension_loaded("spl")) die("skip SPL not available"); ?>
--INI--
phar.readonly=1
phar.require_hash=0
--FILE--
<?php
include dirname(__FILE__) . '/files/tarmaker.php.inc';

View File

@ -5,6 +5,7 @@ Phar: test broken app
<?php if (!extension_loaded("zlib")) die("skip zlib not available"); ?>
--INI--
phar.readonly=0
phar.require_hash=0
--FILE--
<?php

View File

@ -1466,7 +1466,7 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end,
}
#endif /* #ifndef PHAR_HAVE_OPENSSL */
int phar_verify_signature(php_stream *fp, size_t end_of_phar, int sig_type, char *sig, int sig_len, char *fname, char **signature, int *signature_len, char **error TSRMLS_DC) /* {{{ */
int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_type, char *sig, int sig_len, char *fname, char **signature, int *signature_len, char **error TSRMLS_DC) /* {{{ */
{
int read_size, len;
off_t read_len;