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
View Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
If you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
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