php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #54128 ZIP_ER_OPEN when ZipArchive::open() on temp file
Submitted: 2011-03-01 15:28 UTC Modified: 2020-12-04 17:37 UTC
Votes:13
Avg. Score:3.5 ± 0.9
Reproduced:7 of 9 (77.8%)
Same Version:4 (57.1%)
Same OS:2 (28.6%)
From: vavra at 602 dot cz Assigned: cmb (profile)
Status: Not a bug Package: Zip Related
PHP Version: 5.3.5 OS: Windows 2003
Private report: No CVE-ID: None
 [2011-03-01 15:28 UTC] vavra at 602 dot cz
Description:
------------
On Windows 2003, IIS we use php as CGI. The php process runs as a IUSR_XXXX user. When we want to unzip a file, we get error 11 (ZIP_ER_OPEN).

I tracked this by Process Monitor and I saw that ZipArchive::open() tries to list a directory. We use temp directory. On Windows 2003 it is C:\Windows\Temp.
After listing this directory ZipArchive::open() returns 11. And doesn't continue at work.

When I add right "List Folder" for user IUSR_XXXX the open() call succeeds.
I think it should be a kind of bug of c-runtime. I searched for ZIP_ER_OPEN in php source and this is returned nearly after calls of fopen() and stat(). It's odd that fopen() and stat() makes directory listing....



Test script:
---------------
//without result testing:

$zipfile = tempnam(sys_get_temp_dir(),'zip');
file_put_contents($zipfile, $a_zip_file_content);
$zip = new ZipArchive();

$a_zip_file_content_reread = file_get_contents($zipfile, $a_zip_file_conent);
//$a_zip_file_conent_reread has bean successfully read and has the same content as $a_zip_file_content


$res=$zip->open($zipfile);
if ($res!==true)
 echo "Failed open a file: ".$res;



Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-03-01 15:55 UTC] johannes@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: pajoye
 [2011-03-01 15:55 UTC] johannes@php.net
I assume this is expected bahvior. At least on UNIX/Linux systems stat is defined as

     The stat()  function  obtains  information  about  the  file
     pointed  to  by  path. Read, write, or execute permission of
     the named file is not required, but all  directories  listed
     in the path name leading to the file must be searchable.

I assume it is similar on Windows.

If the zip extension is accessing the temp dir this should probably be documented.

Assigning to Pierre who knows zip and windows better :-)
 [2011-03-02 08:47 UTC] vavra at 602 dot cz
Well, I've made another test. I've written a small c program compiled by VC9 containing calls of stat and fopen. In php I've called this program via exec(). When IUSR_XXXX has no right to list the temp folder the result was: stat() failed with -1, fopen succeeded.

So I think function _zip_file_exists() in zip/lib/zip_open.c should be rewritten. Instead of calling stat, it should be called fopen. Stat calls is made only for existence detection, no fields from struct stat are read. So you can replace stat without any limitations and users of ZipArchive::open will no longer be confused by behaviour of stat function.
 [2011-03-02 09:56 UTC] pajoye@php.net
zip_file_exists is about testing the existence of a file so stat usage is 
perfectly valid in this case. 

We also have to test if the file exists, because of the options available on 
zip_open (create, overwrite, etc.).
 [2011-03-02 10:44 UTC] vavra at 602 dot cz
The function _zip_file_exists() is used only once in zip_open() after creating new or reading for open. The usage is not so universal. There is no need for testing of existence of file for which we have no read access. It's used in zip_open() and zip_open() reads or writes to a file. So you are testing existence of file and you have to have at least a read permission. So if you reimplement _zip_file_exists() with fopen you will not lose anything and users of ZipArchive will quite earn.

Case where implementation of some file_exists() via fopen cannot be used is for example implementaion of tempnam - function which tries to generate unique file and can randomly generate some filename. There is a probability that filename exists and you have no read right.

Again in zip_open() such universal behaviour of file_exists() function isn't required. So I thing you can reimplement it and you can spare a lot of time of ZipArchive users. See my test case: file_get_contents succeeded but ZipArchive:open not. Isn't it weird?
 [2011-03-02 10:50 UTC] pajoye@php.net
The bug is a side effect of the permission issue. But we have (I repeat: we have) 
to do the existence check for the reason I explained earlier.

I will see what can be done to still make it works but you better have to fix your 
perms instead.
 [2011-03-02 11:01 UTC] vavra at 602 dot cz
The right permission for temp folder are default right permission for Windows Server systems. Microsoft probably has a security reason for not allowing IUSR_XXXX user for listing a temp dir.

Yes I can fix it by changing permissions or not using Windows Temp dir.

I do not fordid you not testing file existence. I offer not do it by stat function ;-)
 [2011-03-02 11:08 UTC] pajoye@php.net
What I mean by alternative solutions. Just not sure now if it is worth it. Also 
sharing one temp for all vhost is not a wised idea :)
 [2011-03-02 15:53 UTC] carsten_sttgt at gmx dot de
> I assume it is similar on Windows.
Yes. In this case the needed X-Bit (X = directory traversal) is still set. Thus you can "cd" to this directory. But on Windows we have an additional right RD (list directory). This one is not allowed, and so you can't do a "dir" in this directory.
-> thus stat should normally work in this folder for own files.


> Also sharing one temp for all vhost is not a wised idea :)

If all vhosts have a (scripts are executed with a) different SID, that's not a problem. Only the creator SID (and admin/system) have full access to it own files in this folder, but no rights to files created from other SID's. (a little bit like 1777 on *nix. But an *nix you can still list other files. On Win not.)


But back to the topic and let me extend the testscript:
| <?php
| $zipfile = tempnam(sys_get_temp_dir(), 'zip');
| $zip = new ZipArchive();
| 
| $res = $zip->open($zipfile, ZIPARCHIVE::CREATE);
| if ($res !== true) {
|     printf("Can't create file (%d)", $res);
| }
| var_dump($res);
| $zip->close();
| 
| $res = $zip->open($zipfile);
| if ($res !== true) {
|     printf("Can't open file (%d)", $res);
| }
| 
| 
| unlink($zipfile);
| ?>

The result:
| boolean true
| Can't open file (11)

I can create a new Zip-File in this folder, but can't open an existing one.


BTW stat(). Here's an example with PHP stat() (and my favorite realpath):
| <?php
| $temp = tmpfile();
| $filedata = stream_get_meta_data($temp);
| var_dump(stat($filedata['uri']));
| var_dump(realpath($filedata['uri']));
| ?>

The result:
| array (size=26)
|   0 => int 2
|   ...
| boolean false

tmpfile, fopen, stat, whatever is working in this dir. But realpath fails...
 [2011-03-02 16:56 UTC] pajoye@php.net
-PHP Version: 5.2.17 +PHP Version: 5.3.5
 [2011-03-02 16:56 UTC] pajoye@php.net
@carsten_sttgt at gmx dot de
Can you please for my own sanity keep separate issues separated? Thanks.

The stat problem here is totally unrelated to realpath_r. There is a reason why 
we do it (see my other comment) and I already said that I have to see what else 
we can do to work around this problem without adding more platform specific 
changes in this implementation.
 [2011-03-02 19:31 UTC] carsten_sttgt at gmx dot de
> thus stat should normally work in this folder for own files.

Ups, must correct me. stat (crt) is really not working, even "traverse folder/execute file" is allowed.


But I must agree with vavra. Checking the existence of a file is normally done with (f)open or access. Someone should report this to the libzip team.
 [2011-03-02 19:37 UTC] pajoye@php.net
No, stat exists for that.
 [2011-03-02 19:53 UTC] carsten_sttgt at gmx dot de
hmmm,
> We also have to test if the file exists,

man 2 access
   access, eaccess, faccessat -- check accessibility of a file

man 2 stat
   stat, lstat, fstat, fstatat -- get file status

I think access is the winner (especially if there is a known problem with stat)
 [2011-03-02 23:33 UTC] pajoye@php.net
This problem is new to me and again, there are reasons why we use it.

In any case, thanks for the feedback, the problem is identified and has sufficient 
info. I will try to figure a portable way to make this special case working.
 [2017-10-24 07:33 UTC] kalle@php.net
-Status: Assigned +Status: Open -Assigned To: pajoye +Assigned To:
 [2020-12-04 17:37 UTC] cmb@php.net
-Status: Open +Status: Not a bug -Assigned To: +Assigned To: cmb
 [2020-12-04 17:37 UTC] cmb@php.net
> So I think function _zip_file_exists() in zip/lib/zip_open.c
> should be rewritten.

That file belongs to the bundled libzip (which is unbundled as of
PHP 7.4).  If there are still issue, please report these
upstream[1].

[1] <https://libzip.org/>
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Apr 16 15:01:29 2024 UTC