diff options
author | Antony Dovgal <tony2001@php.net> | 2007-04-10 22:30:07 +0000 |
---|---|---|
committer | Antony Dovgal <tony2001@php.net> | 2007-04-10 22:30:07 +0000 |
commit | 19aa4a932f6d5cc10c98ffa3f3bd91a4b5673c96 (patch) | |
tree | 62496dc2fbd97c31cf4c045afdee935e07951bcc /main/fopen_wrappers.c | |
parent | e145f1aa2a3406dd24dd83a495074460976a6772 (diff) | |
download | php-git-19aa4a932f6d5cc10c98ffa3f3bd91a4b5673c96.tar.gz |
fix #40931 (open_basedir bypass via symlink and move_uploaded_file())
Diffstat (limited to 'main/fopen_wrappers.c')
-rw-r--r-- | main/fopen_wrappers.c | 66 |
1 files changed, 63 insertions, 3 deletions
diff --git a/main/fopen_wrappers.c b/main/fopen_wrappers.c index ebcdb711fa..0b476cf5e4 100644 --- a/main/fopen_wrappers.c +++ b/main/fopen_wrappers.c @@ -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; |