php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #3697 open_basedir prevents creating files with fopen
Submitted: 2000-03-02 00:57 UTC Modified: 2002-11-28 05:49 UTC
From: djm at web dot us dot uu dot net Assigned:
Status: Closed Package: Feature/Change Request
PHP Version: 4.0 Beta 4 Patch Level 1 OS: BSD/OS 4.0.1
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: djm at web dot us dot uu dot net
New email:
PHP Version: OS:

 

 [2000-03-02 00:57 UTC] djm at web dot us dot uu dot net
I have open_basedir set and I am trying to create a file under that directory using
$fp = fopen("cities.wddx", "w");
However, it fails:
<b>Warning</b>:  open_basedir restriction in effect. File is in wrong directory in <b>/homes/web/testtest5/html/serialize.php</b> on line <b>17</b><br>

Adding some more warning messages to the PHP source code reveals that the stat() toward the end of php_realpath() is the culprit.  Of course it can't stat the file if I'm trying to create it.  A suggested fix:

Index: php_realpath.c
@@ -251,12 +258,25 @@
        }
 
        /* Check if the resolved path is a directory */
-       if (stat(path_construction, &filestat) != 0) return NULL;
+       if (stat(path_construction, &filestat) != 0) {
+               /* It doesn't exist yet; check the directory it's in instead.  */
+               while (*--writepos != '/' && writepos > path_construction)
+                       ;
+               *writepos = 0;
+               if (stat(path_construction, &filestat) != 0) {
+                       return NULL;
+               }
+       }
        if (S_ISDIR(filestat.st_mode)) {
                /* It's a directory, append a / if needed */
                if (*(writepos-1) != '/') {
                        /* Check for overflow */


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2000-03-02 01:05 UTC] djm at web dot us dot uu dot net
I should mention that an appropriate #ifdef PHP_WIN32 case would also have to be added.  Actually, the win32 support could be more cleanly implemented by doing

#ifdef PHP_WIN32
#define DIRSEP '\\'
#else
#define DIRSEP '/'
#endif

instead of duplicating a lot of while loops and ifs.

 [2000-03-02 01:24 UTC] djm at web dot us dot uu dot net
Some more warning messages to alert about exceptional conditions would be helpful.  Also, there's a misformatted warning message due to the appending of " in <script>...".
And, there's a place in the distributed code that's missing the PHP_WIN32 case.
So here's my final (I think) suggested patch:

Index: fopen-wrappers.c
===================================================================
retrieving revision 1.2
diff -u -r1.2 fopen-wrappers.c
--- fopen-wrappers.c    2000/03/01 04:32:29     1.2
+++ fopen-wrappers.c    2000/03/02 06:14:53
@@ -120,21 +120,26 @@
        }
 
        /* Resolve the real path into resolved_name */
-       if ((php_realpath(path, resolved_name) != NULL) && (php_realpath(local_open_basedir, resolved_basedir) != NULL)) {
+       if (php_realpath(path, resolved_name) == NULL) {
+               /* Unable to resolve the real path, return -1 */
+               return -1;
+       } else if (php_realpath(local_open_basedir, resolved_basedir) == NULL) {
+               /* Unable to resolve the real path, return -1 */
+               php_error(E_WARNING, "cannot resolve open_basedir %s", local_open_basedir);
+               return -1;
+       } else {
                /* Check the path */
 #ifdef PHP_WIN32
-               if (strncasecmp(resolved_basedir, resolved_name, strlen(resolved_basedir)) == 0) {
+               if (strncasecmp(resolved_basedir, resolved_name, strlen(resolved_basedir)) == 0)
 #else
-               if (strncmp(resolved_basedir, resolved_name, strlen(resolved_basedir)) == 0) {
+               if (strncmp(resolved_basedir, resolved_name, strlen(resolved_basedir)) == 0)
 #endif
+           {
                        /* File is in the right directory */
                        return 0;
                } else {
                        return -1;
                }
-       } else {
-               /* Unable to resolve the real path, return -1 */
-               return -1;
        }
 }
 
@@ -170,7 +175,7 @@
 
                        ptr = end;
                }
-               php_error(E_WARNING, "open_basedir restriction in effect. File is in wrong directory.");
+               php_error(E_WARNING, "open_basedir restriction in effect. File is in wrong directory");
                efree(pathbuf);
                return -1;
        }
Index: php_realpath.c
===================================================================
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 php_realpath.c
--- php_realpath.c      2000/03/01 04:23:33     1.1.1.1
+++ php_realpath.c      2000/03/02 06:20:56
@@ -97,6 +97,7 @@
                if (getcwd(path_construction, MAXPATHLEN-1) == NULL) {
                        /* Unable to get cwd */
                        resolved_path[0] = 0;
+                       php_error(E_WARNING, "cannot get current directory");
                        return NULL;
                }
                strcat(path_construction, "/");
@@ -213,7 +214,10 @@
                /* Check the current location to see if it is a symlink */
                if((linklength = readlink(path_construction, buf, MAXPATHLEN)) != -1) {
                        /* Check linkcount */
-                       if (linkcount > MAXSYMLINKS) return NULL;
+                       if (linkcount > MAXSYMLINKS) {
+                               php_error(E_WARNING, "detected symlink loop in %s", path);
+                               return NULL;
+                       }
 
                        /* Count this symlink */
                        linkcount++;
@@ -222,7 +226,10 @@
                        buf[linklength] = 0;
 
                        /* Check for overflow */
-                       if ((strlen(workpos) + strlen(buf) + 1) >= MAXPATHLEN) return NULL;
+                       if ((strlen(workpos) + strlen(buf) + 1) >= MAXPATHLEN) {
+                               php_error(E_WARNING, "overflow in %s", path);
+                               return NULL;
+                       }
 
                        /* Remove the symlink-component wrom path_construction */
                        writepos--;                                             /* move to '/' */
@@ -251,12 +258,31 @@
        }
 
        /* Check if the resolved path is a directory */
-       if (stat(path_construction, &filestat) != 0) return NULL;
+       if (stat(path_construction, &filestat) != 0) {
+               /* It doesn't exist yet; check the directory it's in instead.  */
+#ifdef PHP_WIN32
+               while (*--writepos != '\\' && writepos > path_construction)
+#else
+               while (*--writepos != '/' && writepos > path_construction)
+#endif
+                       ;
+               *writepos = 0;
+               if (stat(path_construction, &filestat) != 0) {
+                       return NULL;
+               }
+       }
        if (S_ISDIR(filestat.st_mode)) {
                /* It's a directory, append a / if needed */
+#ifdef PHP_WIN32
+               if (*(writepos-1) != '\\') {
+#else
                if (*(writepos-1) != '/') {
+#endif
                        /* Check for overflow */
-                       if ((strlen(workpos) + 2) >= MAXPATHLEN) return NULL;
+                       if ((strlen(workpos) + 2) >= MAXPATHLEN) {
+                               php_error(E_WARNING, "overflow in %s", path);
+                               return NULL;
+                       }
 
                        *writepos++ = '/';
                        *writepos = 0;

 [2000-07-29 23:59 UTC] zak@php.net
Have you tried a recent release?  If so, does the problem still occur?
 [2000-08-01 01:01 UTC] zak@php.net
DJM reports that problem is fixed. However, he suggests that there is a better solution to the problem than was implemented.  See message below.

"I just checked the CVS version.  The primary bug I reported is fixed in the current sources, though in a somewhat less robust (but simpler) way than I suggested.  I would still like to have more warning messages instead of silent failures in various exceptional conditions, as suggested in my patch; that has not been done so far.  Also, spurious whitespace has been added in a comment in main/php_realpath.c:260:
	/* C    heck for overflow */
"
 [2002-11-28 05:49 UTC] wez@php.net
seems to be fixed.
 
PHP Copyright © 2001-2025 The PHP Group
All rights reserved.
Last updated: Sun Aug 17 01:00:02 2025 UTC