diff options
Diffstat (limited to 'ext/phar/util.c')
-rw-r--r-- | ext/phar/util.c | 283 |
1 files changed, 236 insertions, 47 deletions
diff --git a/ext/phar/util.c b/ext/phar/util.c index 909f4053aa..008bd3f5ea 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -96,6 +96,7 @@ php_stream *phar_get_efp(phar_entry_info *entry, int follow_links TSRMLS_DC) /* return phar_get_efp(link_entry, 1 TSRMLS_CC); } } + if (phar_get_fp_type(entry TSRMLS_CC) == PHAR_FP) { if (!phar_get_entrypfp(entry TSRMLS_CC)) { /* re-open just in time for cases where our refcount reached 0 on the phar archive */ @@ -124,6 +125,7 @@ int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t positi if (!fp) { return -1; } + if (follow_links) { phar_entry_info *t; t = phar_get_link_source(entry TSRMLS_CC); @@ -131,27 +133,33 @@ int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t positi entry = t; } } + if (entry->is_dir) { return 0; } + eoffset = phar_get_fp_offset(entry TSRMLS_CC); + switch (whence) { - case SEEK_END : + case SEEK_END: temp = eoffset + entry->uncompressed_filesize + offset; break; - case SEEK_CUR : + case SEEK_CUR: temp = eoffset + position + offset; break; - case SEEK_SET : + case SEEK_SET: temp = eoffset + offset; break; } + if (temp > eoffset + (off_t) entry->uncompressed_filesize) { return -1; } + if (temp < eoffset) { return -1; } + return php_stream_seek(fp, temp, SEEK_SET); } /* }}} */ @@ -199,12 +207,14 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, filename_len = strlen(entry.tmp); filename = entry.tmp; + /* only check openbasedir for files, not for phar streams */ if (!is_phar && php_check_open_basedir(filename TSRMLS_CC)) { efree(entry.tmp); efree(entry.filename); return FAILURE; } + entry.is_mounted = 1; entry.is_crc_checked = 1; entry.fp_type = PHAR_TMP; @@ -214,6 +224,7 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, efree(entry.filename); return FAILURE; } + if (ssb.sb.st_mode & S_IFDIR) { entry.is_dir = 1; if (SUCCESS != zend_hash_add(&phar->mounted_dirs, entry.filename, path_len, (void *)&(entry.filename), sizeof(char *), NULL)) { @@ -226,10 +237,13 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, entry.is_dir = 0; entry.uncompressed_filesize = entry.compressed_filesize = ssb.sb.st_size; } + entry.flags = ssb.sb.st_mode; + if (SUCCESS == zend_hash_add(&phar->manifest, entry.filename, path_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { return SUCCESS; } + efree(entry.tmp); efree(entry.filename); return FAILURE; @@ -252,6 +266,7 @@ char *phar_find_in_include_path(char *filename, int filename_len, phar_archive_d if (!zend_is_executing(TSRMLS_C) || !PHAR_G(cwd)) { return phar_save_resolve_path(filename, filename_len TSRMLS_CC); } + fname = zend_get_executed_filename(TSRMLS_C); fname_len = strlen(fname); @@ -279,8 +294,10 @@ splitted: if (pphar) { *pphar = phar; } + try_len = filename_len; test = phar_fix_filepath(estrndup(filename, filename_len), &try_len, 1 TSRMLS_CC); + if (*test == '/') { if (zend_hash_exists(&(phar->manifest), test + 1, try_len - 1)) { spprintf(&ret, 0, "phar://%s%s", arch, test); @@ -298,10 +315,12 @@ splitted: } efree(test); } + spprintf(&path, MAXPATHLEN, "phar://%s/%s%c%s", arch, PHAR_G(cwd), DEFAULT_DIR_SEPARATOR, PG(include_path)); efree(arch); ret = php_resolve_path(filename, filename_len, path TSRMLS_CC); efree(path); + if (ret && strlen(ret) > 8 && !strncmp(ret, "phar://", 7)) { char *arch; int arch_len, ret_len; @@ -312,13 +331,17 @@ splitted: if (SUCCESS != phar_split_fname(ret, ret_len, &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) { return ret; } + zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar); + if (!pphar && PHAR_G(manifest_cached)) { zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar); } + efree(arch); efree(entry); } + return ret; #else /* PHP 5.2 */ char resolved_path[MAXPATHLEN]; @@ -338,12 +361,15 @@ splitted: if (!zend_is_executing(TSRMLS_C) || !PHAR_G(cwd)) { goto doit; } + fname = zend_get_executed_filename(TSRMLS_C); + if (SUCCESS != phar_split_fname(fname, strlen(fname), &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) { goto doit; } efree(entry); + if (*filename == '.') { int try_len; @@ -351,8 +377,10 @@ splitted: efree(arch); goto doit; } + try_len = filename_len; test = phar_fix_filepath(estrndup(filename, filename_len), &try_len, 1 TSRMLS_CC); + if (*test == '/') { if (zend_hash_exists(&(phar->manifest), test + 1, try_len - 1)) { spprintf(&ret, 0, "phar://%s%s", arch, test); @@ -368,23 +396,23 @@ splitted: return ret; } } + efree(test); } - efree(arch); + efree(arch); doit: - if (*filename == '.' || - IS_ABSOLUTE_PATH(filename, filename_len) || - !path || - !*path) { + if (*filename == '.' || IS_ABSOLUTE_PATH(filename, filename_len) || !path || !*path) { if (tsrm_realpath(filename, resolved_path TSRMLS_CC)) { return estrdup(resolved_path); } else { return NULL; } } + /* test for stream wrappers and return */ for (p = filename; p - filename < filename_len && (isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'); ++p, ++n); + if (n < filename_len - 3 && (*p == ':') && (!strncmp("//", p+1, 2) || ( filename_len > 4 && !memcmp("data", filename, 4)))) { /* found stream wrapper, this is an absolute path until stream wrappers implement realpath */ return estrndup(filename, filename_len); @@ -401,6 +429,7 @@ doit: maybe_stream = 0; goto not_stream; } + for (p = ptr, n = 0; p < end && (isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'); ++p, ++n); if (n == end - ptr && *p && !strncmp("//", p+1, 2)) { @@ -417,6 +446,7 @@ not_stream: ptr = end + 1; continue; } + memcpy(trypath, ptr, end-ptr); len = end-ptr; trypath[end-ptr] = '/'; @@ -428,6 +458,7 @@ not_stream: if (len + 1 + filename_len + 1 >= MAXPATHLEN) { break; } + memcpy(trypath, ptr, len); trypath[len] = '/'; memcpy(trypath+len+1, filename, filename_len+1); @@ -456,19 +487,23 @@ not_stream: if (wrapper == &php_stream_phar_wrapper) { char *arch, *entry; int arch_len, entry_len, ret_len; - + ret_len = strlen(trypath); /* found phar:// */ if (SUCCESS != phar_split_fname(trypath, ret_len, &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) { return estrndup(trypath, ret_len); } + zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar); + if (!pphar && PHAR_G(manifest_cached)) { zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar); } + efree(arch); efree(entry); + return estrndup(trypath, ret_len); } return estrdup(trypath); @@ -483,8 +518,7 @@ not_stream: } } /* end provided path */ - /* check in calling scripts' current working directory as a fall back case - */ + /* check in calling scripts' current working directory as a fall back case */ if (zend_is_executing(TSRMLS_C)) { char *exec_fname = zend_get_executed_filename(TSRMLS_C); int exec_fname_length = strlen(exec_fname); @@ -492,29 +526,32 @@ not_stream: int n = 0; while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length])); - if (exec_fname && exec_fname[0] != '[' && - exec_fname_length > 0 && - exec_fname_length + 1 + filename_len + 1 < MAXPATHLEN) { + if (exec_fname && exec_fname[0] != '[' && + exec_fname_length > 0 && + exec_fname_length + 1 + filename_len + 1 < MAXPATHLEN) { memcpy(trypath, exec_fname, exec_fname_length + 1); memcpy(trypath+exec_fname_length + 1, filename, filename_len+1); /* search for stream wrapper */ for (p = trypath; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; ++p, ++n); + if (n < exec_fname_length - 3 && (*p == ':') && (n > 1) && (!strncmp("//", p+1, 2) || !memcmp("data", trypath, 4))) { char *actual; - + wrapper = php_stream_locate_url_wrapper(trypath, &actual, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC); + if (wrapper == &php_plain_files_wrapper) { /* this should never technically happen, but we'll leave it here for completeness */ strncpy(trypath, actual, MAXPATHLEN); } else if (!wrapper) { - /* if wrapper is NULL, there was a mal-formed include_path stream wrapper + /* if wrapper is NULL, there was a malformed include_path stream wrapper this also should be impossible */ return NULL; } else { return estrdup(trypath); } } + if (tsrm_realpath(trypath, resolved_path TSRMLS_CC)) { return estrdup(resolved_path); } @@ -546,19 +583,24 @@ int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char if (!ret) { return FAILURE; } + *ret = NULL; + if (error) { *error = NULL; } + if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, error TSRMLS_CC)) { return FAILURE; } + if (for_write && PHAR_G(readonly) && !phar->is_data) { if (error) { spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, disabled by ini setting", path, fname); } return FAILURE; } + if (!path_len) { if (error) { spprintf(error, 4096, "phar error: file \"\" in phar \"%s\" cannot be empty", fname); @@ -581,6 +623,7 @@ really_get_entry: return FAILURE; } } + if (for_write && phar->is_persistent) { if (FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) { if (error) { @@ -591,24 +634,28 @@ really_get_entry: goto really_get_entry; } } + if (entry->is_modified && !for_write) { if (error) { spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for reading, writable file pointers are open", path, fname); } return FAILURE; } + if (entry->fp_refcount && for_write) { if (error) { spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, readable file pointers are open", path, fname); } return FAILURE; } + if (entry->is_deleted) { if (!for_create) { return FAILURE; } entry->is_deleted = 0; } + if (entry->is_dir) { *ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data)); (*ret)->position = 0; @@ -618,12 +665,15 @@ really_get_entry: (*ret)->internal_file = entry; (*ret)->is_zip = entry->is_zip; (*ret)->is_tar = entry->is_tar; + if (!phar->is_persistent) { ++(entry->phar->refcount); ++(entry->fp_refcount); } + return SUCCESS; } + if (entry->fp_type == PHAR_MOD) { if (for_trunc) { if (FAILURE == phar_create_writeable_entry(phar, entry, error TSRMLS_CC)) { @@ -638,6 +688,7 @@ really_get_entry: entry->link = NULL; entry->tar_type = (entry->tar_type ? TAR_FILE : 0); } + if (for_write) { if (for_trunc) { if (FAILURE == phar_create_writeable_entry(phar, entry, error TSRMLS_CC)) { @@ -654,6 +705,7 @@ really_get_entry: } } } + *ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data)); (*ret)->position = 0; (*ret)->phar = phar; @@ -663,10 +715,12 @@ really_get_entry: (*ret)->is_tar = entry->is_tar; (*ret)->fp = phar_get_efp(entry, 1 TSRMLS_CC); (*ret)->zero = phar_get_fp_offset(entry TSRMLS_CC); + if (!phar->is_persistent) { ++(entry->fp_refcount); ++(entry->phar->refcount); } + return SUCCESS; } /* }}} */ @@ -720,6 +774,7 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char etemp.filename_len = path_len; etemp.fp_type = PHAR_MOD; etemp.fp = php_stream_fopen_tmpfile(); + if (!etemp.fp) { if (error) { spprintf(error, 0, "phar error: unable to create temporary file"); @@ -727,6 +782,7 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char efree(ret); return NULL; } + etemp.fp_refcount = 1; if (allow_dir == 2) { @@ -739,6 +795,7 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char } else { etemp.flags = etemp.old_flags = PHAR_ENT_PERM_DEF_FILE; } + phar_add_virtual_dirs(phar, path, path_len TSRMLS_CC); etemp.is_modified = 1; etemp.timestamp = time(0); @@ -746,10 +803,12 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char etemp.phar = phar; etemp.filename = estrndup(path, path_len); etemp.is_zip = phar->is_zip; + if (phar->is_tar) { etemp.is_tar = phar->is_tar; etemp.tar_type = TAR_FILE; } + if (FAILURE == zend_hash_add(&phar->manifest, etemp.filename, path_len, (void*)&etemp, sizeof(phar_entry_info), (void **) &entry)) { php_stream_close(etemp.fp); if (error) { @@ -759,7 +818,7 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char efree(etemp.filename); return NULL; } - + if (!entry) { php_stream_close(etemp.fp); efree(etemp.filename); @@ -775,23 +834,23 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char ret->is_zip = entry->is_zip; ret->is_tar = entry->is_tar; ret->internal_file = entry; + return ret; } /* }}} */ /* initialize a phar_archive_data's read-only fp for existing phar data */ -int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC) /* {{{ */ +int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC) /* {{{ */ { if (phar_get_pharfp(phar TSRMLS_CC)) { return SUCCESS; } - #if PHP_MAJOR_VERSION < 6 if (PG(safe_mode) && (!php_checkuid(phar->fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) { return FAILURE; } #endif - + if (php_check_open_basedir(phar->fname TSRMLS_CC)) { return FAILURE; } @@ -801,6 +860,7 @@ int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC) /* {{{ */ if (!phar_get_pharfp(phar TSRMLS_CC)) { return FAILURE; } + return SUCCESS; } /* }}} */ @@ -813,21 +873,24 @@ int phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **er if (FAILURE == phar_open_entry_fp(source, error, 1 TSRMLS_CC)) { return FAILURE; } + if (dest->link) { efree(dest->link); dest->link = NULL; dest->tar_type = (dest->tar_type ? TAR_FILE : 0); } + dest->fp_type = PHAR_MOD; dest->offset = 0; dest->is_modified = 1; dest->fp = php_stream_fopen_tmpfile(); - phar_seek_efp(source, 0, SEEK_SET, 0, 1 TSRMLS_CC); link = phar_get_link_source(source TSRMLS_CC); + if (!link) { link = source; } + if (link->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(link, 0 TSRMLS_CC), dest->fp, link->uncompressed_filesize)) { php_stream_close(dest->fp); dest->fp_type = PHAR_FP; @@ -836,13 +899,14 @@ int phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **er } return FAILURE; } + return SUCCESS; } /* }}} */ /* open and decompress a compressed phar entry */ -int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TSRMLS_DC) /* {{{ */ +int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TSRMLS_DC) /* {{{ */ { php_stream_filter *filter; phar_archive_data *phar = entry->phar; @@ -852,30 +916,34 @@ int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TS if (follow_links && entry->link) { phar_entry_info *link_entry = phar_get_link_source(entry TSRMLS_CC); - if (link_entry && link_entry != entry) { return phar_open_entry_fp(link_entry, error, 1 TSRMLS_CC); } } + if (entry->fp_type == PHAR_TMP) { if (!entry->fp) { entry->fp = php_stream_open_wrapper(entry->tmp, "rb", STREAM_MUST_SEEK|0, NULL); } return SUCCESS; } + if (entry->fp_type != PHAR_FP) { /* either newly created or already modified */ return SUCCESS; } + if (!phar_get_pharfp(phar TSRMLS_CC)) { if (FAILURE == phar_open_archive_fp(phar TSRMLS_CC)) { spprintf(error, 4096, "phar error: Cannot open phar archive \"%s\" for reading", phar->fname); return FAILURE; } } + if ((entry->old_flags && !(entry->old_flags & PHAR_ENT_COMPRESSION_MASK)) || !(entry->flags & PHAR_ENT_COMPRESSION_MASK)) { return SUCCESS; } + if (!phar_get_entrypufp(entry TSRMLS_CC)) { phar_set_entrypufp(entry, php_stream_fopen_tmpfile() TSRMLS_CC); if (!phar_get_entrypufp(entry TSRMLS_CC)) { @@ -885,38 +953,44 @@ int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TS } ufp = phar_get_entrypufp(entry TSRMLS_CC); + if ((filtername = phar_decompress_filter(entry, 0)) != NULL) { filter = php_stream_filter_create(filtername, NULL, 0 TSRMLS_CC); } else { filter = NULL; } + if (!filter) { spprintf(error, 4096, "phar error: unable to read phar \"%s\" (cannot create %s filter while decompressing file \"%s\")", phar->fname, phar_decompress_filter(entry, 1), entry->filename); return FAILURE; } + /* now we can safely use proper decompression */ /* save the new offset location within ufp */ php_stream_seek(ufp, 0, SEEK_END); loc = php_stream_tell(ufp); php_stream_filter_append(&ufp->writefilters, filter); php_stream_seek(phar_get_entrypfp(entry TSRMLS_CC), phar_get_fp_offset(entry TSRMLS_CC), SEEK_SET); + if (php_stream_copy_to_stream(phar_get_entrypfp(entry TSRMLS_CC), ufp, entry->compressed_filesize) != entry->compressed_filesize) { spprintf(error, 4096, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", phar->fname, entry->filename); php_stream_filter_remove(filter, 1 TSRMLS_CC); return FAILURE; } + php_stream_filter_flush(filter, 1); php_stream_flush(ufp); php_stream_filter_remove(filter, 1 TSRMLS_CC); + if (php_stream_tell(ufp) - loc != (off_t) entry->uncompressed_filesize) { spprintf(error, 4096, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", phar->fname, entry->filename); return FAILURE; } entry->old_flags = entry->flags; + /* this is now the new location of the file contents within this fp */ phar_set_fp_type(entry, PHAR_UFP, loc TSRMLS_CC); - return SUCCESS; } /* }}} */ @@ -974,22 +1048,27 @@ int phar_create_writeable_entry(phar_archive_data *phar, phar_entry_info *entry, entry->offset = 0; return SUCCESS; } + if (error) { *error = NULL; } + /* open a new temp file for writing */ if (entry->link) { efree(entry->link); entry->link = NULL; entry->tar_type = (entry->tar_type ? TAR_FILE : 0); } + entry->fp = php_stream_fopen_tmpfile(); + if (!entry->fp) { if (error) { spprintf(error, 0, "phar error: unable to create temporary file"); } return FAILURE; } + entry->old_flags = entry->flags; entry->is_modified = 1; phar->is_modified = 1; @@ -1020,9 +1099,11 @@ int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC) /* {{ fp = php_stream_fopen_tmpfile(); phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC); link = phar_get_link_source(entry TSRMLS_CC); + if (!link) { link = entry; } + if (link->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(link, 0 TSRMLS_CC), fp, link->uncompressed_filesize)) { if (error) { spprintf(error, 4096, "phar error: cannot separate entry file \"%s\" contents in phar archive \"%s\" for write access", entry->filename, entry->phar->fname); @@ -1069,13 +1150,16 @@ int phar_free_alias(phar_archive_data *phar, char *alias, int alias_len TSRMLS_D if (phar->refcount || phar->is_persistent) { return FAILURE; } + /* this archive has no open references, so emit an E_STRICT and remove it */ if (zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) { return FAILURE; } + /* invalidate phar cache */ PHAR_G(last_phar) = NULL; PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; + return SUCCESS; } /* }}} */ @@ -1096,10 +1180,13 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch if (error) { *error = NULL; } + *archive = NULL; + if (PHAR_G(last_phar) && fname_len == PHAR_G(last_phar_name_len) && !memcmp(fname, PHAR_G(last_phar_name), fname_len)) { *archive = PHAR_G(last_phar); if (alias && alias_len) { + if (!PHAR_G(last_phar)->is_temporary_alias && (alias_len != PHAR_G(last_phar)->alias_len || memcmp(PHAR_G(last_phar)->alias, alias, alias_len))) { if (error) { spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, PHAR_G(last_phar)->fname, fname); @@ -1107,20 +1194,25 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch *archive = NULL; return FAILURE; } + if (PHAR_G(last_phar)->alias_len && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), PHAR_G(last_phar)->alias, PHAR_G(last_phar)->alias_len, (void**)&fd_ptr)) { zend_hash_del(&(PHAR_GLOBALS->phar_alias_map), PHAR_G(last_phar)->alias, PHAR_G(last_phar)->alias_len); } - zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&(*archive), sizeof(phar_archive_data*), NULL); + + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&(*archive), sizeof(phar_archive_data*), NULL); PHAR_G(last_alias) = alias; PHAR_G(last_alias_len) = alias_len; } + return SUCCESS; } + if (alias && alias_len && PHAR_G(last_phar) && alias_len == PHAR_G(last_alias_len) && !memcmp(alias, PHAR_G(last_alias), alias_len)) { fd = PHAR_G(last_phar); fd_ptr = &fd; goto alias_success; } + if (alias && alias_len) { ahash = zend_inline_hash_func(alias, alias_len); if (SUCCESS == zend_hash_quick_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, ahash, (void**)&fd_ptr)) { @@ -1135,6 +1227,7 @@ alias_success: } return FAILURE; } + *archive = *fd_ptr; fd = *fd_ptr; PHAR_G(last_phar) = fd; @@ -1142,20 +1235,25 @@ alias_success: PHAR_G(last_phar_name_len) = fd->fname_len; PHAR_G(last_alias) = alias; PHAR_G(last_alias_len) = alias_len; + return SUCCESS; } + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_quick_find(&cached_alias, alias, alias_len, ahash, (void **)&fd_ptr)) { goto alias_success; } } + fhash = zend_inline_hash_func(fname, fname_len); my_realpath = NULL; save = fname; save_len = fname_len; + if (fname && fname_len) { if (SUCCESS == zend_hash_quick_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, fhash, (void**)&fd_ptr)) { *archive = *fd_ptr; fd = *fd_ptr; + if (alias && alias_len) { if (!fd->is_temporary_alias && (alias_len != fd->alias_len || memcmp(fd->alias, alias, alias_len))) { if (error) { @@ -1163,21 +1261,27 @@ alias_success: } return FAILURE; } + if (fd->alias_len && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), fd->alias, fd->alias_len, (void**)&fd_ptr)) { zend_hash_del(&(PHAR_GLOBALS->phar_alias_map), fd->alias, fd->alias_len); } - zend_hash_quick_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, ahash, (void*)&fd, sizeof(phar_archive_data*), NULL); + + zend_hash_quick_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, ahash, (void*)&fd, sizeof(phar_archive_data*), NULL); } + PHAR_G(last_phar) = fd; PHAR_G(last_phar_name) = fd->fname; PHAR_G(last_phar_name_len) = fd->fname_len; PHAR_G(last_alias) = fd->alias; PHAR_G(last_alias_len) = fd->alias_len; + return SUCCESS; } + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_quick_find(&cached_phars, fname, fname_len, fhash, (void**)&fd_ptr)) { *archive = *fd_ptr; fd = *fd_ptr; + /* this could be problematic - alias should never be different from manifest alias for cached phars */ if (!fd->is_temporary_alias && alias && alias_len) { @@ -1188,34 +1292,43 @@ alias_success: return FAILURE; } } + PHAR_G(last_phar) = fd; PHAR_G(last_phar_name) = fd->fname; PHAR_G(last_phar_name_len) = fd->fname_len; PHAR_G(last_alias) = fd->alias; PHAR_G(last_alias_len) = fd->alias_len; + return SUCCESS; } + if (SUCCESS == zend_hash_quick_find(&(PHAR_GLOBALS->phar_alias_map), save, save_len, fhash, (void**)&fd_ptr)) { fd = *archive = *fd_ptr; + PHAR_G(last_phar) = fd; PHAR_G(last_phar_name) = fd->fname; PHAR_G(last_phar_name_len) = fd->fname_len; PHAR_G(last_alias) = fd->alias; PHAR_G(last_alias_len) = fd->alias_len; + return SUCCESS; } + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_quick_find(&cached_alias, save, save_len, fhash, (void**)&fd_ptr)) { fd = *archive = *fd_ptr; + PHAR_G(last_phar) = fd; PHAR_G(last_phar_name) = fd->fname; PHAR_G(last_phar_name_len) = fd->fname_len; PHAR_G(last_alias) = fd->alias; PHAR_G(last_alias_len) = fd->alias_len; + return SUCCESS; } /* not found, try converting \ to / */ my_realpath = expand_filepath(fname, my_realpath TSRMLS_CC); + if (my_realpath) { fname_len = strlen(my_realpath); fname = my_realpath; @@ -1226,26 +1339,34 @@ alias_success: phar_unixify_path_separators(fname, fname_len); #endif fhash = zend_inline_hash_func(fname, fname_len); + if (SUCCESS == zend_hash_quick_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, fhash, (void**)&fd_ptr)) { realpath_success: *archive = *fd_ptr; fd = *fd_ptr; + if (alias && alias_len) { - zend_hash_quick_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, ahash, (void*)&fd, sizeof(phar_archive_data*), NULL); + zend_hash_quick_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, ahash, (void*)&fd, sizeof(phar_archive_data*), NULL); } + efree(my_realpath); + PHAR_G(last_phar) = fd; PHAR_G(last_phar_name) = fd->fname; PHAR_G(last_phar_name_len) = fd->fname_len; PHAR_G(last_alias) = fd->alias; PHAR_G(last_alias_len) = fd->alias_len; + return SUCCESS; } + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_quick_find(&cached_phars, fname, fname_len, fhash, (void**)&fd_ptr)) { goto realpath_success; } + efree(my_realpath); } + return FAILURE; } /* }}} */ @@ -1278,13 +1399,14 @@ char * phar_decompress_filter(phar_entry_info * entry, int return_unknown) /* {{ } else { flags = entry->flags; } + switch (flags & PHAR_ENT_COMPRESSION_MASK) { - case PHAR_ENT_COMPRESSED_GZ: - return "zlib.inflate"; - case PHAR_ENT_COMPRESSED_BZ2: - return "bzip2.decompress"; - default: - return return_unknown ? "unknown" : NULL; + case PHAR_ENT_COMPRESSED_GZ: + return "zlib.inflate"; + case PHAR_ENT_COMPRESSED_BZ2: + return "bzip2.decompress"; + default: + return return_unknown ? "unknown" : NULL; } } /* }}} */ @@ -1324,12 +1446,14 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + if (!path_len && !dir) { if (error) { spprintf(error, 4096, "phar error: invalid path \"%s\" must not be empty", path); } return NULL; } + if (phar_path_check(&path, &path_len, &pcr_error) > pcr_is_ok) { if (error) { spprintf(error, 4096, "phar error: invalid path \"%s\" contains %s", path, pcr_error); @@ -1340,12 +1464,14 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in if (!phar->manifest.arBuckets) { return NULL; } + if (is_dir) { if (!path_len || path_len == 1) { return NULL; } path_len--; } + if (SUCCESS == zend_hash_find(&phar->manifest, path, path_len, (void**)&entry)) { if (entry->is_deleted) { /* entry is deleted, but has not been flushed to disk yet */ @@ -1366,6 +1492,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return entry; } + if (dir) { if (zend_hash_exists(&phar->virtual_dirs, path, path_len)) { /* a file or directory exists in a sub-directory of this path */ @@ -1378,6 +1505,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in return entry; } } + if (phar->mounted_dirs.arBuckets && zend_hash_num_elements(&phar->mounted_dirs)) { phar_zstr key; char *str_key; @@ -1406,17 +1534,21 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + if (!entry->tmp || !entry->is_mounted) { if (error) { spprintf(error, 4096, "phar internal error: mounted path \"%s\" is not properly initialized as a mounted path", str_key); } return NULL; } + test_len = spprintf(&test, MAXPATHLEN, "%s%s", entry->tmp, path + keylen); + if (SUCCESS != php_stream_stat_path(test, &ssb)) { efree(test); return NULL; } + if (ssb.sb.st_mode & S_IFDIR && !dir) { efree(test); if (error) { @@ -1424,6 +1556,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + if ((ssb.sb.st_mode & S_IFDIR) == 0 && dir) { efree(test); /* user requested a directory, we must return one */ @@ -1432,6 +1565,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + /* mount the file just in time */ if (SUCCESS != phar_mount_entry(phar, test, test_len, path, path_len TSRMLS_CC)) { efree(test); @@ -1440,7 +1574,9 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + efree(test); + if (SUCCESS != zend_hash_find(&phar->manifest, path, path_len, (void**)&entry)) { if (error) { spprintf(error, 4096, "phar error: path \"%s\" exists as file \"%s\" and could not be retrieved after being mounted", path, test); @@ -1451,6 +1587,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } } } + return NULL; } /* }}} */ @@ -1494,13 +1631,13 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, php_stream_rewind(fp); Z_TYPE_P(zdata) = IS_STRING; Z_STRLEN_P(zdata) = end; + if (end != (off_t) php_stream_copy_to_mem(fp, &(Z_STRVAL_P(zdata)), (size_t) end, 0)) { zval_dtor(zdata); zval_dtor(zsig); zval_dtor(zkey); return FAILURE; } - #if PHP_VERSION_ID < 50300 if (FAILURE == zend_fcall_info_init(openssl, &fci, &fcc TSRMLS_CC)) { #else @@ -1542,9 +1679,9 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, efree(zsig); return FAILURE; } + zval_dtor(openssl); efree(openssl); - #if PHP_VERSION_ID < 50300 --(zdata->refcount); --(zsig->refcount); @@ -1562,9 +1699,10 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, efree(zdata); zval_dtor(zkey); efree(zkey); + switch (Z_TYPE_P(retval_ptr)) { default: - case IS_LONG : + case IS_LONG: zval_dtor(zsig); efree(zsig); if (1 == Z_LVAL_P(retval_ptr)) { @@ -1573,7 +1711,7 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, } efree(retval_ptr); return FAILURE; - case IS_BOOL : + case IS_BOOL: efree(retval_ptr); if (Z_BVAL_P(retval_ptr)) { *signature = estrndup(Z_STRVAL_P(zsig), Z_STRLEN_P(zsig)); @@ -1611,7 +1749,6 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ php_uint32 pubkey_len; char *pubkey = NULL, *pfile; php_stream *pfp; - #ifndef PHAR_HAVE_OPENSSL if (!zend_hash_exists(&module_registry, "openssl", sizeof("openssl"))) { if (error) { @@ -1624,30 +1761,38 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ spprintf(&pfile, 0, "%s.pubkey", fname); pfp = php_stream_open_wrapper(pfile, "rb", 0, NULL); efree(pfile); + if (!pfp || !(pubkey_len = php_stream_copy_to_mem(pfp, &pubkey, PHP_STREAM_COPY_ALL, 0)) || !pubkey) { if (error) { spprintf(error, 0, "openssl public key could not be read"); } return FAILURE; } + php_stream_close(pfp); #ifndef PHAR_HAVE_OPENSSL tempsig = sig_len; + if (FAILURE == phar_call_openssl_signverify(0, fp, end_of_phar, pubkey, pubkey_len, &sig, &tempsig TSRMLS_CC)) { if (pubkey) { efree(pubkey); } + if (error) { spprintf(error, 0, "openssl signature could not be verified"); } + return FAILURE; } + if (pubkey) { efree(pubkey); } + sig_len = tempsig; #else in = BIO_new_mem_buf(pubkey, pubkey_len); + if (NULL == in) { efree(pubkey); if (error) { @@ -1655,9 +1800,11 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ } return FAILURE; } + key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL); BIO_free(in); efree(pubkey); + if (NULL == key) { if (error) { spprintf(error, 0, "openssl signature could not be processed"); @@ -1666,47 +1813,56 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ } EVP_VerifyInit(&md_ctx, mdtype); - read_len = end_of_phar; + if (read_len > sizeof(buf)) { read_size = sizeof(buf); } else { read_size = (int)read_len; } + php_stream_seek(fp, 0, SEEK_SET); + while (read_size && (len = php_stream_read(fp, (char*)buf, read_size)) > 0) { EVP_VerifyUpdate (&md_ctx, buf, len); read_len -= (off_t)len; + if (read_len < read_size) { read_size = (int)read_len; } } + if (EVP_VerifyFinal(&md_ctx, (unsigned char *)sig, sig_len, key) != 1) { /* 1: signature verified, 0: signature does not match, -1: failed signature operation */ EVP_MD_CTX_cleanup(&md_ctx); + if (error) { spprintf(error, 0, "broken openssl signature"); } + return FAILURE; } + EVP_MD_CTX_cleanup(&md_ctx); #endif - + *signature_len = phar_hex_str((const char*)sig, sig_len, signature TSRMLS_CC); } break; #if HAVE_HASH_EXT case PHAR_SIG_SHA512: { unsigned char digest[64]; - PHP_SHA512_CTX context; + PHP_SHA512_CTX context; PHP_SHA512Init(&context); read_len = end_of_phar; + if (read_len > sizeof(buf)) { read_size = sizeof(buf); } else { read_size = (int)read_len; } + while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { PHP_SHA512Update(&context, buf, len); read_len -= (off_t)len; @@ -1714,6 +1870,7 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ read_size = (int)read_len; } } + PHP_SHA512Final(digest, &context); if (memcmp(digest, sig, sizeof(digest))) { @@ -1728,15 +1885,17 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ } case PHAR_SIG_SHA256: { unsigned char digest[32]; - PHP_SHA256_CTX context; + PHP_SHA256_CTX context; PHP_SHA256Init(&context); read_len = end_of_phar; + if (read_len > sizeof(buf)) { read_size = sizeof(buf); } else { read_size = (int)read_len; } + while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { PHP_SHA256Update(&context, buf, len); read_len -= (off_t)len; @@ -1744,6 +1903,7 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ read_size = (int)read_len; } } + PHP_SHA256Final(digest, &context); if (memcmp(digest, sig, sizeof(digest))) { @@ -1770,11 +1930,13 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ PHP_SHA1Init(&context); read_len = end_of_phar; + if (read_len > sizeof(buf)) { read_size = sizeof(buf); } else { read_size = (int)read_len; } + while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { PHP_SHA1Update(&context, buf, len); read_len -= (off_t)len; @@ -1782,6 +1944,7 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ read_size = (int)read_len; } } + PHP_SHA1Final(digest, &context); if (memcmp(digest, sig, sizeof(digest))) { @@ -1800,11 +1963,13 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ PHP_MD5Init(&context); read_len = end_of_phar; + if (read_len > sizeof(buf)) { read_size = sizeof(buf); } else { read_size = (int)read_len; } + while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { PHP_MD5Update(&context, buf, len); read_len -= (off_t)len; @@ -1812,6 +1977,7 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ read_size = (int)read_len; } } + PHP_MD5Final(digest, &context); if (memcmp(digest, sig, sizeof(digest))) { @@ -1850,12 +2016,14 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat #if HAVE_HASH_EXT case PHAR_SIG_SHA512: { unsigned char digest[64]; - PHP_SHA512_CTX context; + PHP_SHA512_CTX context; PHP_SHA512Init(&context); + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { PHP_SHA512Update(&context, buf, sig_len); } + PHP_SHA512Final(digest, &context); *signature = estrndup((char *) digest, 64); *signature_length = 64; @@ -1864,11 +2032,13 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat case PHAR_SIG_SHA256: { unsigned char digest[32]; PHP_SHA256_CTX context; - + PHP_SHA256Init(&context); + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { PHP_SHA256Update(&context, buf, sig_len); } + PHP_SHA256Final(digest, &context); *signature = estrndup((char *) digest, 32); *signature_length = 32; @@ -1880,6 +2050,7 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat if (error) { spprintf(error, 0, "unable to write to phar \"%s\" with requested hash type", phar->fname); } + return FAILURE; #endif case PHAR_SIG_OPENSSL: { @@ -1899,9 +2070,10 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat } return FAILURE; } - key = PEM_read_bio_PrivateKey(in, NULL,NULL, ""); + key = PEM_read_bio_PrivateKey(in, NULL,NULL, ""); BIO_free(in); + if (!key) { if (error) { spprintf(error, 0, "unable to process private key"); @@ -1911,11 +2083,12 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat siglen = EVP_PKEY_size(key); sigbuf = emalloc(siglen + 1); - EVP_SignInit(&md_ctx, mdtype); + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { EVP_SignUpdate(&md_ctx, buf, sig_len); } + if (!EVP_SignFinal (&md_ctx, sigbuf,(unsigned int *)&siglen, key)) { efree(sigbuf); if (error) { @@ -1923,12 +2096,14 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat } return FAILURE; } + sigbuf[siglen] = '\0'; EVP_MD_CTX_cleanup(&md_ctx); #else sigbuf = NULL; siglen = 0; php_stream_seek(fp, 0, SEEK_END); + if (FAILURE == phar_call_openssl_signverify(1, fp, php_stream_tell(fp), PHAR_G(openssl_privatekey), PHAR_G(openssl_privatekey_len), (char **)&sigbuf, &siglen TSRMLS_CC)) { if (error) { spprintf(error, 0, "unable to write phar \"%s\" with requested openssl signature", phar->fname); @@ -1947,9 +2122,11 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat PHP_SHA1_CTX context; PHP_SHA1Init(&context); + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { PHP_SHA1Update(&context, buf, sig_len); } + PHP_SHA1Final(digest, &context); *signature = estrndup((char *) digest, 20); *signature_length = 20; @@ -1960,15 +2137,18 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat PHP_MD5_CTX context; PHP_MD5Init(&context); + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { PHP_MD5Update(&context, buf, sig_len); } + PHP_MD5Final(digest, &context); *signature = estrndup((char *) digest, 16); *signature_length = 16; break; } } + phar->sig_len = phar_hex_str((const char *)*signature, *signature_length, &phar->signature TSRMLS_CC); return SUCCESS; } @@ -1993,15 +2173,19 @@ static void phar_update_cached_entry(void *data, void *argument) /* {{{ */ TSRMLS_FETCH(); entry->phar = (phar_archive_data *)argument; + if (entry->link) { entry->link = estrdup(entry->link); } + if (entry->tmp) { entry->tmp = estrdup(entry->tmp); } + entry->metadata_str.c = 0; entry->filename = estrndup(entry->filename, entry->filename_len); entry->is_persistent = 0; + if (entry->metadata) { if (entry->metadata_len) { /* assume success, we would have failed before */ @@ -2018,7 +2202,6 @@ static void phar_update_cached_entry(void *data, void *argument) /* {{{ */ #else Z_SET_REFCOUNT_P(entry->metadata, 1); #endif - entry->metadata_str.c = NULL; entry->metadata_str.len = 0; } @@ -2038,12 +2221,15 @@ static void phar_copy_cached_phar(phar_archive_data **pphar TSRMLS_DC) /* {{{ */ fname = phar->fname; phar->fname = estrndup(phar->fname, phar->fname_len); phar->ext = phar->fname + (phar->ext - fname); + if (phar->alias) { phar->alias = estrndup(phar->alias, phar->alias_len); } + if (phar->signature) { phar->signature = estrdup(phar->signature); } + if (phar->metadata) { /* assume success, we would have failed before */ if (phar->metadata_len) { @@ -2062,6 +2248,7 @@ static void phar_copy_cached_phar(phar_archive_data **pphar TSRMLS_DC) /* {{{ */ #endif } } + zend_hash_init(&newmanifest, sizeof(phar_entry_info), zend_get_hash_value, destroy_phar_manifest_entry, 0); zend_hash_copy(&newmanifest, &(*pphar)->manifest, NULL, NULL, sizeof(phar_entry_info)); @@ -2089,10 +2276,12 @@ int phar_copy_on_write(phar_archive_data **pphar TSRMLS_DC) /* {{{ */ /* invalidate phar cache */ PHAR_G(last_phar) = NULL; PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; + if (newpphar[0]->alias_len && FAILURE == zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), newpphar[0]->alias, newpphar[0]->alias_len, (void*)newpphar, sizeof(phar_archive_data*), NULL)) { zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), (*pphar)->fname, (*pphar)->fname_len); return FAILURE; } + *pphar = *newpphar; return SUCCESS; } |