mirror of
https://github.com/php/php-src.git
synced 2025-01-23 04:04:16 +08:00
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:
parent
b9325a36a8
commit
771e3e498f
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 =
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user