From a8e133eafabcaa72e1766ce9200b8c6723ea07a1 Mon Sep 17 00:00:00 2001 From: Antony Dovgal Date: Tue, 10 Apr 2007 22:31:27 +0000 Subject: [PATCH] MFB: fix #40931 (open_basedir bypass via symlink and move_uploaded_file()) --- main/fopen_wrappers.c | 66 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/main/fopen_wrappers.c b/main/fopen_wrappers.c index 93020f78d38..843246e7eb5 100644 --- a/main/fopen_wrappers.c +++ b/main/fopen_wrappers.c @@ -147,8 +147,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)) { @@ -156,8 +160,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) { @@ -167,7 +227,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;