summaryrefslogtreecommitdiff
path: root/ext/phar/zip.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/phar/zip.c')
-rw-r--r--ext/phar/zip.c196
1 files changed, 190 insertions, 6 deletions
diff --git a/ext/phar/zip.c b/ext/phar/zip.c
index ee365d0d1..383561ab8 100644
--- a/ext/phar/zip.c
+++ b/ext/phar/zip.c
@@ -172,6 +172,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias,
phar_archive_data *mydata = NULL;
phar_entry_info entry = {0};
char *p = buf, *ext, *actual_alias = NULL;
+ char *metadata = NULL;
size = php_stream_tell(fp);
@@ -222,7 +223,6 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias,
/* read in archive comment, if any */
if (PHAR_GET_16(locator.comment_len)) {
- char *metadata;
metadata = p + sizeof(locator);
@@ -299,6 +299,30 @@ foundit:
entry.is_zip = 1;
entry.fp_type = PHAR_FP;
entry.is_persistent = mydata->is_persistent;
+#define PHAR_ZIP_FAIL_FREE(errmsg, save) \
+ zend_hash_destroy(&mydata->manifest); \
+ mydata->manifest.arBuckets = 0; \
+ zend_hash_destroy(&mydata->mounted_dirs); \
+ mydata->mounted_dirs.arBuckets = 0; \
+ zend_hash_destroy(&mydata->virtual_dirs); \
+ mydata->virtual_dirs.arBuckets = 0; \
+ php_stream_close(fp); \
+ if (mydata->metadata) { \
+ zval_dtor(mydata->metadata); \
+ } \
+ if (mydata->signature) { \
+ efree(mydata->signature); \
+ } \
+ if (error) { \
+ spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \
+ } \
+ pefree(mydata->fname, mydata->is_persistent); \
+ if (mydata->alias) { \
+ pefree(mydata->alias, mydata->is_persistent); \
+ } \
+ pefree(mydata, mydata->is_persistent); \
+ efree(save); \
+ return FAILURE;
#define PHAR_ZIP_FAIL(errmsg) \
zend_hash_destroy(&mydata->manifest); \
mydata->manifest.arBuckets = 0; \
@@ -310,6 +334,9 @@ foundit:
if (mydata->metadata) { \
zval_dtor(mydata->metadata); \
} \
+ if (mydata->signature) { \
+ efree(mydata->signature); \
+ } \
if (error) { \
spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \
} \
@@ -323,6 +350,7 @@ foundit:
/* add each central directory item to the manifest */
for (i = 0; i < PHAR_GET_16(locator.count); ++i) {
phar_zip_central_dir_file zipentry;
+ off_t beforeus = php_stream_tell(fp);
if (sizeof(zipentry) != php_stream_read(fp, (char *) &zipentry, sizeof(zipentry))) {
PHAR_ZIP_FAIL("unable to read central directory entry, truncated");
@@ -374,6 +402,58 @@ foundit:
entry.is_dir = 0;
}
+ if (entry.filename_len == sizeof(".phar/signature.bin")-1 && !strncmp(entry.filename, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
+ size_t read;
+ php_stream *sigfile;
+ off_t now;
+ char *sig;
+
+ now = php_stream_tell(fp);
+ pefree(entry.filename, entry.is_persistent);
+ sigfile = php_stream_fopen_tmpfile();
+
+ php_stream_seek(fp, 0, SEEK_SET);
+ /* copy file contents + local headers and zip comment, if any, to be hashed for signature */
+ phar_stream_copy_to_stream(fp, sigfile, entry.header_offset, NULL);
+ /* seek to central directory */
+ php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET);
+ /* copy central directory header */
+ phar_stream_copy_to_stream(fp, sigfile, beforeus - PHAR_GET_32(locator.cdir_offset), NULL);
+ if (metadata) {
+ php_stream_write(sigfile, metadata, PHAR_GET_16(locator.comment_len));
+ }
+ php_stream_seek(fp, sizeof(phar_zip_file_header) + entry.header_offset + entry.filename_len + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
+ sig = (char *) emalloc(entry.uncompressed_filesize);
+ read = php_stream_read(fp, sig, entry.uncompressed_filesize);
+ if (read != entry.uncompressed_filesize) {
+ php_stream_close(sigfile);
+ efree(sig);
+ PHAR_ZIP_FAIL("signature cannot be read");
+ }
+ mydata->sig_flags = PHAR_GET_32(sig);
+ if (FAILURE == phar_verify_signature(sigfile, php_stream_tell(sigfile), mydata->sig_flags, sig + 8, entry.uncompressed_filesize - 8, fname, &mydata->signature, &mydata->sig_len, error TSRMLS_CC)) {
+ efree(sig);
+ if (error) {
+ char *save;
+ php_stream_close(sigfile);
+ spprintf(&save, 4096, "signature cannot be verified: %s", *error);
+ efree(*error);
+ PHAR_ZIP_FAIL_FREE(save, save);
+ } else {
+ php_stream_close(sigfile);
+ PHAR_ZIP_FAIL("signature cannot be verified");
+ }
+ }
+ php_stream_close(sigfile);
+ efree(sig);
+ /* signature checked out, let's ensure this is the last file in the phar */
+ if (i != PHAR_GET_16(locator.count) - 1) {
+ PHAR_ZIP_FAIL("entries exist after signature, invalid phar");
+ }
+
+ continue;
+ }
+
phar_add_virtual_dirs(mydata, entry.filename, entry.filename_len TSRMLS_CC);
if (PHAR_GET_16(zipentry.extra_len)) {
@@ -520,7 +600,11 @@ foundit:
php_stream_filter_append(&fp->readfilters, filter);
+#if PHP_MAJOR_VERSION >= 6
+ if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, (void **) &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
+#else
if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
+#endif
pefree(entry.filename, entry.is_persistent);
#if PHP_VERSION_ID < 50207
PHAR_ZIP_FAIL("unable to read in alias, truncated (PHP 5.2.7 and newer has a potential fix for this problem)");
@@ -541,7 +625,11 @@ foundit:
php_stream_filter_append(&fp->readfilters, filter);
+#if PHP_MAJOR_VERSION >= 6
+ if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, (void **) &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
+#else
if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
+#endif
pefree(entry.filename, entry.is_persistent);
#if PHP_VERSION_ID < 50207
PHAR_ZIP_FAIL("unable to read in alias, truncated (PHP 5.2.7 and newer has a potential fix for this problem)");
@@ -552,7 +640,11 @@ foundit:
php_stream_filter_flush(filter, 1);
php_stream_filter_remove(filter, 1 TSRMLS_CC);
} else {
+#if PHP_MAJOR_VERSION >= 6
+ if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, (void **) &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
+#else
if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
+#endif
pefree(entry.filename, entry.is_persistent);
PHAR_ZIP_FAIL("unable to read in alias, truncated");
}
@@ -1000,6 +1092,76 @@ continue_dir:
}
/* }}} */
+static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pass *pass,
+ smart_str *metadata TSRMLS_DC) /* {{{ */
+{
+ /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
+ if (!phar->is_data || phar->sig_flags) {
+ int signature_length;
+ char *signature, sigbuf[8];
+ phar_entry_info entry = {0};
+ php_stream *newfile;
+ off_t tell, st;
+
+ newfile = php_stream_fopen_tmpfile();
+ st = tell = php_stream_tell(pass->filefp);
+ /* copy the local files, central directory, and the zip comment to generate the hash */
+ php_stream_seek(pass->filefp, 0, SEEK_SET);
+ phar_stream_copy_to_stream(pass->filefp, newfile, tell, NULL);
+ tell = php_stream_tell(pass->centralfp);
+ php_stream_seek(pass->centralfp, 0, SEEK_SET);
+ phar_stream_copy_to_stream(pass->centralfp, newfile, tell, NULL);
+ if (metadata->c) {
+ php_stream_write(newfile, metadata->c, metadata->len);
+ }
+
+ if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, pass->error TSRMLS_CC)) {
+ if (pass->error) {
+ char *save = *(pass->error);
+ spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar: %s", save);
+ efree(save);
+ }
+
+ php_stream_close(newfile);
+ return FAILURE;
+ }
+
+ entry.filename = ".phar/signature.bin";
+ entry.filename_len = sizeof(".phar/signature.bin")-1;
+ entry.fp = php_stream_fopen_tmpfile();
+ entry.fp_type = PHAR_MOD;
+ entry.is_modified = 1;
+
+ PHAR_SET_32(sigbuf, phar->sig_flags);
+ PHAR_SET_32(sigbuf + 4, signature_length);
+
+ if (8 != (int)php_stream_write(entry.fp, sigbuf, 8) || signature_length != (int)php_stream_write(entry.fp, signature, signature_length)) {
+ efree(signature);
+ if (pass->error) {
+ spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar %s", phar->fname);
+ }
+
+ php_stream_close(newfile);
+ return FAILURE;
+ }
+
+ efree(signature);
+ entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
+ entry.phar = phar;
+ /* throw out return value and write the signature */
+ phar_zip_changed_apply((void *)&entry, (void *)pass TSRMLS_CC);
+ php_stream_close(newfile);
+
+ if (pass->error && *(pass->error)) {
+ /* error is set by writeheaders */
+ php_stream_close(newfile);
+ return FAILURE;
+ }
+ } /* signature */
+ return SUCCESS;
+}
+/* }}} */
+
int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defaultstub, char **error TSRMLS_DC) /* {{{ */
{
char *pos;
@@ -1084,7 +1246,11 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau
user_stub = 0;
+#if PHP_MAJOR_VERSION >= 6
+ if (!(len = php_stream_copy_to_mem(stubfile, (void **) &user_stub, len, 0)) || !user_stub) {
+#else
if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
+#endif
if (error) {
spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", phar->fname);
}
@@ -1214,10 +1380,24 @@ fperror:
memset(&eocd, 0, sizeof(eocd));
strncpy(eocd.signature, "PK\5\6", 4);
- PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest));
- PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest));
+ if (!phar->is_data && !phar->sig_flags) {
+ phar->sig_flags = PHAR_SIG_SHA1;
+ }
+ if (phar->sig_flags) {
+ PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest) + 1);
+ PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest) + 1);
+ } else {
+ PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest));
+ PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest));
+ }
zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass TSRMLS_CC);
+ if (phar->metadata) {
+ /* set phar metadata */
+ PHP_VAR_SERIALIZE_INIT(metadata_hash);
+ php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC);
+ PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
+ }
if (temperr) {
if (error) {
spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr);
@@ -1226,6 +1406,9 @@ fperror:
temperror:
php_stream_close(pass.centralfp);
nocentralerror:
+ if (phar->metadata) {
+ smart_str_free(&main_metadata_str);
+ }
php_stream_close(pass.filefp);
if (closeoldfile) {
php_stream_close(oldfile);
@@ -1233,6 +1416,10 @@ nocentralerror:
return EOF;
}
+ if (FAILURE == phar_zip_applysignature(phar, &pass, &main_metadata_str TSRMLS_CC)) {
+ goto temperror;
+ }
+
/* save zip */
cdir_size = php_stream_tell(pass.centralfp);
cdir_offset = php_stream_tell(pass.filefp);
@@ -1255,9 +1442,6 @@ nocentralerror:
if (phar->metadata) {
/* set phar metadata */
- PHP_VAR_SERIALIZE_INIT(metadata_hash);
- php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC);
- PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
PHAR_SET_16(eocd.comment_len, main_metadata_str.len);
if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {