]> granicus.if.org Git - php/commitdiff
fix #40931 (open_basedir bypass via symlink and move_uploaded_file())
authorAntony Dovgal <tony2001@php.net>
Tue, 10 Apr 2007 22:30:07 +0000 (22:30 +0000)
committerAntony Dovgal <tony2001@php.net>
Tue, 10 Apr 2007 22:30:07 +0000 (22:30 +0000)
NEWS
main/fopen_wrappers.c

diff --git a/NEWS b/NEWS
index 08b326be82b7ba080ca2212d1d0257100bb7a825..176a1b13ae4d195151d0d1d0088cde47cc8c98eb 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -57,6 +57,8 @@ PHP                                                                        NEWS
 - Fixed bug #40998 (long session array keys are truncated). (Tony)
 - Implement feature request #40947, allow a single filter as argument 
   for filter_var_array (Pierre)
+- Fixed bug #40931 (open_basedir bypass via symlink and move_uploaded_file()). 
+  (Tony)
 - Fixed bug #40921 (php_default_post_reader crashes when post_max_size is
   exceeded). (trickie at gmail dot com, Ilia)
 - Fixed bug #40915 (addcslashes unexpected behavior with binary input). (Tony)
index ebcdb711fa78e17f357efc3d96569ab742baf580..0b476cf5e47f5f94b2b1f7870bd41c66df624594 100644 (file)
@@ -90,8 +90,12 @@ PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path
        char resolved_name[MAXPATHLEN];
        char resolved_basedir[MAXPATHLEN];
        char local_open_basedir[MAXPATHLEN];
+       char path_tmp[MAXPATHLEN];
+       char *path_file;
        int resolved_basedir_len;
        int resolved_name_len;
+       int path_len;
+       int nesting_level = 0;
        
        /* Special case basedir==".": Use script-directory */
        if (strcmp(basedir, ".") || !VCWD_GETCWD(local_open_basedir, MAXPATHLEN)) {
@@ -99,8 +103,64 @@ PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path
                strlcpy(local_open_basedir, basedir, sizeof(local_open_basedir));
        }
 
-       /* Resolve the real path into resolved_name */
-       if ((expand_filepath(path, resolved_name TSRMLS_CC) != NULL) && (expand_filepath(local_open_basedir, resolved_basedir TSRMLS_CC) != NULL)) {
+       path_len = strlen(path);
+       if (path_len > (MAXPATHLEN - 1)) {
+               /* empty and too long paths are invalid */
+               return -1;
+       }
+
+       /* normalize and expand path */
+       if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) {
+               return -1;
+       }
+       
+       path_len = strlen(resolved_name);
+       memcpy(path_tmp, resolved_name, path_len + 1); /* safe */
+
+       while (VCWD_REALPATH(path_tmp, resolved_name) == NULL) {
+#ifdef HAVE_SYMLINK
+               if (nesting_level == 0) {
+                       int ret;
+                       char buf[MAXPATHLEN];
+                       
+                       ret = readlink(path_tmp, buf, MAXPATHLEN - 1);
+                       if (ret < 0) {
+                               /* not a broken symlink, move along.. */
+                       } else {
+                               /* put the real path into the path buffer */
+                               memcpy(path_tmp, buf, ret);
+                               path_tmp[ret] = '\0';
+                       }
+               }
+#endif
+
+#if defined(PHP_WIN32) || defined(NETWARE)
+               path_file = strrchr(path_tmp, DEFAULT_SLASH);
+               if (!path_file) {
+                       path_file = strrchr(path_tmp, '/');
+               }
+#else
+               path_file = strrchr(path_tmp, DEFAULT_SLASH);
+#endif
+               if (!path_file) {
+                       /* none of the path components exist. definitely not in open_basedir.. */
+                       return -1;
+               } else {
+                       path_len = path_file - path_tmp + 1;
+#if defined(PHP_WIN32) || defined(NETWARE)
+                       if (path_len > 1 && path_tmp[path_len - 2] == ':') {
+                               /* this is c:\,  */
+                               path_tmp[path_len] = '\0';
+                       }
+#else
+                       path_tmp[path_len - 1] = '\0';
+#endif
+               }
+               nesting_level++;
+       }
+
+       /* Resolve open_basedir to resolved_basedir */
+       if (expand_filepath(local_open_basedir, resolved_basedir TSRMLS_CC) != NULL) {
                /* Handler for basedirs that end with a / */
                resolved_basedir_len = strlen(resolved_basedir);
                if (basedir[strlen(basedir) - 1] == PHP_DIR_SEPARATOR) {
@@ -110,7 +170,7 @@ PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path
                        }
                }
 
-               if (path[strlen(path)-1] == PHP_DIR_SEPARATOR) {
+               if (path_tmp[path_len - 1] == PHP_DIR_SEPARATOR) {
                        resolved_name_len = strlen(resolved_name);
                        if (resolved_name[resolved_name_len - 1] != PHP_DIR_SEPARATOR) {
                                resolved_name[resolved_name_len] = PHP_DIR_SEPARATOR;