]> granicus.if.org Git - php/commitdiff
o Fixed Bug #12121: chdir and safe_mode
authorJames E. Flemer <jflemer@php.net>
Fri, 13 Jul 2001 18:21:21 +0000 (18:21 +0000)
committerJames E. Flemer <jflemer@php.net>
Fri, 13 Jul 2001 18:21:21 +0000 (18:21 +0000)
  - [ main/safe_mode.h ] added new checkuid mode:
    CHECKUID_ALLOW_ONLY_FILE: skips directory check if file check
fails

  - [ ext/standard/dir.c ] changed php_checkuid() to use
    CHECKUID_ALLOW_ONLY_FILE instead of CHECKUID_ALLOW_ONLY_DIR

  - [ main/safe_mode.c ] added code for new checkuid mode

o Fixed Bug #12119: safe mode owner check can be bypassed with symlink
  - [ main/safe_mode.c ] use VCWD_REALPATH to resolve destination
    of symlink before trimming filename

o New Feature: safe_mode_include_dir (php.ini directive)
  - Allows bypassing UID/GID checks when including files
    from the directory in safe_mode_include_dir and its
subdirectories. (safe_mode must be on, directory must
also be in include_path or full path must be used when
including)

o Fixed Feature: safe_mode_gid (php.ini directive)
  - Correctly check (and report) UID/GID bits on directories

o Changed include() fall back to scripts cwd implementation
  - CWD added to the (local) search path in php_fopen_with_path()
    instead of seperate case. [ main/fopen_wrappers.c ]

ext/standard/dir.c
main/fopen_wrappers.c
main/main.c
main/php_globals.h
main/safe_mode.c
main/safe_mode.h
php.ini-dist
php.ini-optimized
php.ini-recommended

index ae6acdce5db523940b232b82e7ab682cbaa4b64d..927bf958d70f6ff2a66d81ba33a2712606d218ba 100644 (file)
@@ -282,7 +282,7 @@ PHP_FUNCTION(chdir)
        }
        convert_to_string_ex(arg);
 
-       if (PG(safe_mode) && !php_checkuid((*arg)->value.str.val, NULL, CHECKUID_ALLOW_ONLY_DIR)) {
+       if (PG(safe_mode) && !php_checkuid((*arg)->value.str.val, NULL, CHECKUID_ALLOW_ONLY_FILE)) {
                RETURN_FALSE;
        }
        ret = VCWD_CHDIR((*arg)->value.str.val);
index 721ac0b554fea32ee073e54a55549b605e12ed1c..e45e52b97e588b45899a735b95b68ad27b89fc9d 100644 (file)
@@ -389,20 +389,64 @@ PHPAPI FILE *php_fopen_primary_script(void)
 PHPAPI FILE *php_fopen_with_path(char *filename, char *mode, char *path, char **opened_path)
 {
        char *pathbuf, *ptr, *end;
+       char *exec_fname;
        char trypath[MAXPATHLEN];
+       char trydir[MAXPATHLEN];
+       char safe_mode_include_dir[MAXPATHLEN];
        struct stat sb;
        FILE *fp;
+       int path_length;
        int filename_length;
+       int safe_mode_include_dir_length;
+       int exec_fname_length;
        PLS_FETCH();
+       ELS_FETCH();
 
        if (opened_path) {
                *opened_path = NULL;
        }
+       
+       if(!filename) {
+               return NULL;
+       }
 
        filename_length = strlen(filename);
-
-       /* Absolute & relative path open */
-       if ((*filename == '.') || (IS_ABSOLUTE_PATH(filename, filename_length))) {
+       
+       /* Relative path open */
+       if (*filename == '.') {
+               if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) {
+                       return NULL;
+               }
+               return php_fopen_and_set_opened_path(filename, mode, opened_path);
+       }
+       
+       /*
+        * files in safe_mode_include_dir (or subdir) are excluded from
+        * safe mode GID/UID checks
+        */
+       *safe_mode_include_dir       = 0;
+       safe_mode_include_dir_length = 0;
+       if(PG(safe_mode_include_dir) && VCWD_REALPATH(PG(safe_mode_include_dir), safe_mode_include_dir)) {
+               safe_mode_include_dir_length = strlen(safe_mode_include_dir);
+       }
+       
+       /* Absolute path open */
+       if (IS_ABSOLUTE_PATH(filename, filename_length)) {
+               /* Check to see if file is in safe_mode_include_dir (or subdir) */
+               if (PG(safe_mode) && *safe_mode_include_dir && VCWD_REALPATH(filename, trypath)) {
+#ifdef PHP_WIN32
+                       if (strncasecmp(safe_mode_include_dir, trypath, safe_mode_include_dir_length) == 0)
+#else
+                       if (strncmp(safe_mode_include_dir, trypath, safe_mode_include_dir_length) == 0)
+#endif
+                       {
+                               /* absolute path matches safe_mode_include_dir */
+                               fp = php_fopen_and_set_opened_path(trypath, mode, opened_path);
+                               if (fp) {
+                                       return fp;
+                               }
+                       }
+               }
                if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) {
                        return NULL;
                }
@@ -415,7 +459,31 @@ PHPAPI FILE *php_fopen_with_path(char *filename, char *mode, char *path, char **
                }
                return php_fopen_and_set_opened_path(filename, mode, opened_path);
        }
-       pathbuf = estrdup(path);
+
+       /* check in provided path */
+       /* append the calling scripts' current working directory
+        * as a fall back case
+        */
+       exec_fname = zend_get_executed_filename(ELS_C);
+       exec_fname_length = strlen(exec_fname);
+       path_length = strlen(path);
+
+       while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length])) {
+       }
+       if (exec_fname && exec_fname[0] == '[') {
+               /* [no active file] */
+               exec_fname_length = 0;
+       }
+       
+       pathbuf = (char *) emalloc(exec_fname_length + path_length +1 +1);
+       memcpy(pathbuf, path, path_length);
+#ifdef PHP_WIN32
+       pathbuf[path_length] = ';';
+#else
+       pathbuf[path_length] = ':';
+#endif
+       memcpy(pathbuf+path_length+1, exec_fname, exec_fname_length);
+       pathbuf[path_length + exec_fname_length +1] = '\0';
 
        ptr = pathbuf;
 
@@ -430,6 +498,22 @@ PHPAPI FILE *php_fopen_with_path(char *filename, char *mode, char *path, char **
                        end++;
                }
                snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename);
+               /* Check to see trypath is in safe_mode_include_dir (or subdir) */
+               if (PG(safe_mode) && *safe_mode_include_dir && VCWD_REALPATH(trypath, trydir)) {
+#ifdef PHP_WIN32
+                       if (strncasecmp(safe_mode_include_dir, trydir, safe_mode_include_dir_length) == 0)
+#else
+                       if (strncmp(safe_mode_include_dir, trydir, safe_mode_include_dir_length) == 0)
+#endif
+                       {
+                               /* trypath is in safe_mode_include_dir */
+                               fp = php_fopen_and_set_opened_path(trydir, mode, opened_path);
+                               if (fp) {
+                                       efree(pathbuf);
+                                       return fp;
+                               }
+                       }
+               }
                if (PG(safe_mode)) {
                        if (VCWD_STAT(trypath, &sb) == 0 && (!php_checkuid(trypath, mode, CHECKUID_CHECK_MODE_PARAM))) {
                                efree(pathbuf);
@@ -442,37 +526,10 @@ PHPAPI FILE *php_fopen_with_path(char *filename, char *mode, char *path, char **
                        return fp;
                }
                ptr = end;
-       }
+       } /* end provided path */
 
        efree(pathbuf);
-
-       {
-               char *exec_fname;
-               int exec_fname_len;
-               ELS_FETCH();
-
-               exec_fname = zend_get_executed_filename(ELS_C);
-               exec_fname_len = strlen(exec_fname);
-
-               pathbuf = (char *) emalloc(exec_fname_len+filename_length+1+1); /* Over allocate to save time */
-               memcpy(pathbuf, exec_fname, exec_fname_len+1);
-
-               while ((--exec_fname_len >= 0) && !IS_SLASH(pathbuf[exec_fname_len])) {
-               }
-               pathbuf[exec_fname_len] = DEFAULT_SLASH;
-               memcpy(&pathbuf[exec_fname_len+1], filename, filename_length+1);
-
-               if (PG(safe_mode)) {
-                       if (VCWD_STAT(pathbuf, &sb) == 0 && (!php_checkuid(pathbuf, mode, CHECKUID_CHECK_MODE_PARAM))) {
-                               efree(pathbuf);
-                               return NULL;
-                       }
-               }
-               fp = php_fopen_and_set_opened_path(pathbuf, mode, opened_path);
-               efree(pathbuf);
-               return fp;
-       }
-       return NULL; /* Not really needed anymore */
+       return NULL;
 }
 /* }}} */
  
index 57dabfa85257fbab222e2362fa972c29deb583b7..2c3810cc3133bbe5f39720872e26c08a30c2a306 100644 (file)
@@ -215,6 +215,7 @@ PHP_INI_BEGIN()
        STD_PHP_INI_BOOLEAN("register_argc_argv",       "1",            PHP_INI_ALL,            OnUpdateBool,                   register_argc_argv,             php_core_globals,       core_globals)
        STD_PHP_INI_BOOLEAN("register_globals",         "1",            PHP_INI_ALL,            OnUpdateBool,                   register_globals,               php_core_globals,       core_globals)
        STD_PHP_INI_BOOLEAN("safe_mode",                        "0",            PHP_INI_SYSTEM,         OnUpdateBool,                   safe_mode,                              php_core_globals,       core_globals)
+       STD_PHP_INI_ENTRY("safe_mode_include_dir",      NULL,           PHP_INI_SYSTEM,         OnUpdateString,                 safe_mode_include_dir,  php_core_globals,       core_globals)
        STD_PHP_INI_BOOLEAN("safe_mode_gid",                    "0",            PHP_INI_SYSTEM,         OnUpdateBool,                   safe_mode_gid,                          php_core_globals,       core_globals)
        STD_PHP_INI_BOOLEAN("short_open_tag",DEFAULT_SHORT_OPEN_TAG,    PHP_INI_SYSTEM|PHP_INI_PERDIR,          OnUpdateBool,                   short_tags,                             zend_compiler_globals,  compiler_globals)
        STD_PHP_INI_BOOLEAN("sql.safe_mode",            "0",            PHP_INI_SYSTEM,         OnUpdateBool,                   sql_safe_mode,                  php_core_globals,       core_globals)
index f426b9ed89fb53c0b09f700484e5272dcabdabd2..56de68d7e1aefd4682e67b1bb457734f16c6c16e 100644 (file)
@@ -68,6 +68,7 @@ struct _php_core_globals {
        zend_bool implicit_flush;
 
        zend_bool safe_mode;
+       char *safe_mode_include_dir;
        zend_bool safe_mode_gid;
        zend_bool sql_safe_mode;
        zend_bool enable_dl;
index e251d37b260e68ed8b7b5ddad151010a5ba3f45e..f20fe516c4b6ecf9279292f833510ec4cd2a555a 100644 (file)
@@ -48,6 +48,7 @@ PHPAPI int php_checkuid(const char *filename, char *fopen_mode, int mode)
        struct stat sb;
        int ret;
        long uid=0L, gid=0L, duid=0L, dgid=0L;
+       char path[MAXPATHLEN];
        char *s;
        PLS_FETCH();
 
@@ -71,8 +72,12 @@ PHPAPI int php_checkuid(const char *filename, char *fopen_mode, int mode)
                return 1;
        }
                
+       /* First we see if the file is owned by the same user...
+        * If that fails, passthrough and check directory...
+        */
        if (mode != CHECKUID_ALLOW_ONLY_DIR) {
-               ret = VCWD_STAT(filename, &sb);
+               VCWD_REALPATH(filename, path);
+               ret = VCWD_STAT(path, &sb);
                if (ret < 0) {
                        if (mode == CHECKUID_DISALLOW_FILE_NOT_EXISTS) {
                                php_error(E_WARNING, "Unable to access %s", filename);
@@ -83,63 +88,67 @@ PHPAPI int php_checkuid(const char *filename, char *fopen_mode, int mode)
                        }
                } else {
                        uid = sb.st_uid;
+                       gid = sb.st_gid;
                        if (uid == php_getuid()) {
                                return 1;
+                       } else if (PG(safe_mode_gid) && gid == php_getgid()) {
+                               return 1;
                        }
                }
-       }
-       s = strrchr(filename,'/');
 
-       /* This loop gets rid of trailing slashes which could otherwise be
-        * used to confuse the function.
-        */
-       while(s && *(s+1)=='\0' && s>filename) {
-               *s='\0';
-               s = strrchr(filename,'/');
-       }
+               /* Trim off filename */
+               if (s = strrchr(path,DEFAULT_SLASH)) {
+                       *s = '\0';
+               }
+       } else { /* CHECKUID_ALLOW_ONLY_DIR */
+               s = strrchr(filename,DEFAULT_SLASH);
 
-       if (s) {
-               *s='\0';
-               ret = VCWD_STAT(filename, &sb);
-               *s='/';
+               if (s) {
+                       *s = '\0';
+                       VCWD_REALPATH(filename, path);
+                       *s = DEFAULT_SLASH;
+               } else {
+                       VCWD_GETCWD(path, MAXPATHLEN);
+               }
+       } /* end CHECKUID_ALLOW_ONLY_DIR */
+       
+       if (mode != CHECKUID_ALLOW_ONLY_FILE) {
+               /* check directory */
+               ret = VCWD_STAT(path, &sb);
                if (ret < 0) {
                        php_error(E_WARNING, "Unable to access %s", filename);
                        return 0;
                }
                duid = sb.st_uid;
-       } else {
-               char cwd[MAXPATHLEN];
-               if (!VCWD_GETCWD(cwd, MAXPATHLEN)) {
-                       php_error(E_WARNING, "Unable to access current working directory");
-                       return 0;
-               }
-               ret = VCWD_STAT(cwd, &sb);
-               if (ret < 0) {
-                       php_error(E_WARNING, "Unable to access %s", cwd);
-                       return 0;
-               }
-               duid = sb.st_uid;
-       }
-       if (duid == (uid=php_getuid())) {
-               return 1;
-       } else if (PG(safe_mode_gid) && dgid == (gid=php_getgid())) {
-               return 1;
-       } else {
-               SLS_FETCH();
+               dgid = sb.st_gid;
+               if (duid == php_getuid()) {
+                       return 1;
+               } else if (PG(safe_mode_gid) && dgid == php_getgid()) {
+                       return 1;
+               } else {
+                       SLS_FETCH();
 
-               if (SG(rfc1867_uploaded_files)) {
-                       if (zend_hash_exists(SG(rfc1867_uploaded_files), (char *) filename, strlen(filename)+1)) {
-                               return 1;
+                       if (SG(rfc1867_uploaded_files)) {
+                               if (zend_hash_exists(SG(rfc1867_uploaded_files), (char *) filename, strlen(filename)+1)) {
+                                       return 1;
+                               }
                        }
                }
+       }
 
-               if (PG(safe_mode_gid)) {
-                       php_error(E_WARNING, "SAFE MODE Restriction in effect.  The script whose uid/gid is %ld/%ld is not allowed to access %s owned by uid/gid %ld/%ld", uid, gid, filename, duid, dgid);
-               } else {
-                       php_error(E_WARNING, "SAFE MODE Restriction in effect.  The script whose uid is %ld is not allowed to access %s owned by uid %ld", uid, filename, duid);
-               }                       
-               return 0;
+       if (mode == CHECKUID_ALLOW_ONLY_DIR) {
+               uid = duid;
+               gid = dgid;
+               if (s) {
+                       *s = 0;
+               }
        }
+       if (PG(safe_mode_gid)) {
+               php_error(E_WARNING, "SAFE MODE Restriction in effect.  The script whose uid/gid is %ld/%ld is not allowed to access %s owned by uid/gid %ld/%ld", php_getuid(), php_getgid(), filename, uid, gid);
+       } else {
+               php_error(E_WARNING, "SAFE MODE Restriction in effect.  The script whose uid is %ld is not allowed to access %s owned by uid %ld", php_getuid(), filename, uid);
+       }                       
+       return 0;
 }
 
 
index 9f012689f4be547ed93bd5a5555d8afd9bbcf82d..307c55707845ee66e8822c74a6f23423b66b8575 100644 (file)
@@ -7,6 +7,7 @@
 #define CHECKUID_CHECK_FILE_AND_DIR 2
 #define CHECKUID_ALLOW_ONLY_DIR 3
 #define CHECKUID_CHECK_MODE_PARAM 4
+#define CHECKUID_ALLOW_ONLY_FILE 5
 
 extern PHPAPI int php_checkuid(const char *filename, char *fopen_mode, int mode);
 extern PHPAPI char *php_get_current_user(void);
index 325937c2b9d208fd01119bfc07540e555373e148..9d61b913f1fbe256a89fe92388e92a2c5edca34f 100644 (file)
@@ -116,6 +116,12 @@ safe_mode = Off
 ; then turn on safe_mode_gid.
 safe_mode_gid = Off
 
+; When safe_mode is on, UID/GID checks are bypassed when
+; including files from this directory and its subdirectories.
+; (directory must also be in include_path or full path must
+; be used when including)
+safe_mode_include_dir =                                                                
+
 ; When safe_mode is on, only executables located in the safe_mode_exec_dir
 ; will be allowed to be executed via the exec family of functions.
 safe_mode_exec_dir =
index e20205c0574df82473f6109b5a613f5e1f87c170..e9698521b7abccce321735d5cf6e8b2a6cb332a6 100644 (file)
@@ -84,7 +84,12 @@ safe_mode            =       Off
 safe_mode_gid  =       Off                                                             ; By default, Safe Mode does a UID compare
                                                                                                        ; check when opening files. If you want to
                                                                                                        ; relax this to a GID compare, then turn on
-                                                                                                       ; safe_mode_gid.
+                                                                                                       ; safe_mode_gid. (safe_mode must be On)
+safe_mode_include_dir =                                                                ; When safe_mode is on, UID/GID checks are
+                                                                                                       ; bypassed when including files from this
+                                                                                                       ; directory and its subdirectories. (directory
+                                                                                                       ; must also be in include_path or full path
+                                                                                                       ; must be used when including)
 safe_mode_exec_dir     =
 safe_mode_allowed_env_vars = PHP_                                      ; Setting certain environment variables
                                                                                                        ; may be a potential security breach.
@@ -532,4 +537,4 @@ sockets.use_system_read             =       Off     ; Use the system read() function instead of
 
 ; Local Variables:
 ; tab-width: 4
-; End:
\ No newline at end of file
+; End:
index e20205c0574df82473f6109b5a613f5e1f87c170..e9698521b7abccce321735d5cf6e8b2a6cb332a6 100644 (file)
@@ -84,7 +84,12 @@ safe_mode            =       Off
 safe_mode_gid  =       Off                                                             ; By default, Safe Mode does a UID compare
                                                                                                        ; check when opening files. If you want to
                                                                                                        ; relax this to a GID compare, then turn on
-                                                                                                       ; safe_mode_gid.
+                                                                                                       ; safe_mode_gid. (safe_mode must be On)
+safe_mode_include_dir =                                                                ; When safe_mode is on, UID/GID checks are
+                                                                                                       ; bypassed when including files from this
+                                                                                                       ; directory and its subdirectories. (directory
+                                                                                                       ; must also be in include_path or full path
+                                                                                                       ; must be used when including)
 safe_mode_exec_dir     =
 safe_mode_allowed_env_vars = PHP_                                      ; Setting certain environment variables
                                                                                                        ; may be a potential security breach.
@@ -532,4 +537,4 @@ sockets.use_system_read             =       Off     ; Use the system read() function instead of
 
 ; Local Variables:
 ; tab-width: 4
-; End:
\ No newline at end of file
+; End: