o Fixed Bug #12121: chdir and safe_mode

- [ 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 ]
This commit is contained in:
James E. Flemer 2001-07-13 18:21:21 +00:00
parent b9325a36a8
commit 771e3e498f
9 changed files with 166 additions and 81 deletions

View 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);

View 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;
}
/* }}} */

View 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)

View 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;

View 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;
}

View 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);

View 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 =

View 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:
; End:

View 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:
; End: