/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2006 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Rasmus Lerdorf | | Jim Winstead | +----------------------------------------------------------------------+ */ /* $Id: fopen_wrappers.c,v 1.175.2.3.2.1 2006/07/01 11:35:34 nlopess Exp $ */ /* {{{ includes */ #include "php.h" #include "php_globals.h" #include "SAPI.h" #include #include #include #include #include #include #ifdef PHP_WIN32 #define O_RDONLY _O_RDONLY #include "win32/param.h" #else #include #endif #include "safe_mode.h" #include "ext/standard/head.h" #include "ext/standard/php_standard.h" #include "zend_compile.h" #include "php_network.h" #if HAVE_PWD_H #ifdef PHP_WIN32 #include "win32/pwd.h" #else #include #endif #endif #include #if HAVE_SYS_SOCKET_H #include #endif #ifndef S_ISREG #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #endif #ifdef PHP_WIN32 #include #elif defined(NETWARE) && defined(USE_WINSOCK) #include #else #include #include #if HAVE_ARPA_INET_H #include #endif #endif #if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE) #undef AF_UNIX #endif #if defined(AF_UNIX) #include #endif /* }}} */ /* {{{ php_check_specific_open_basedir When open_basedir is not NULL, check if the given filename is located in open_basedir. Returns -1 if error or not in the open_basedir, else 0 When open_basedir is NULL, always return 0 */ PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path TSRMLS_DC) { char resolved_name[MAXPATHLEN]; char resolved_basedir[MAXPATHLEN]; char local_open_basedir[MAXPATHLEN]; int resolved_basedir_len; int resolved_name_len; /* Special case basedir==".": Use script-directory */ if (strcmp(basedir, ".") || !VCWD_GETCWD(local_open_basedir, MAXPATHLEN)) { /* Else use the unmodified 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)) { /* Handler for basedirs that end with a / */ resolved_basedir_len = strlen(resolved_basedir); if (basedir[strlen(basedir) - 1] == PHP_DIR_SEPARATOR) { if (resolved_basedir[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) { resolved_basedir[resolved_basedir_len] = PHP_DIR_SEPARATOR; resolved_basedir[++resolved_basedir_len] = '\0'; } } if (path[strlen(path)-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; resolved_name[++resolved_name_len] = '\0'; } } /* Check the path */ #if defined(PHP_WIN32) || defined(NETWARE) if (strncasecmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) { #else if (strncmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) { #endif /* File is in the right directory */ return 0; } else { return -1; } } else { /* Unable to resolve the real path, return -1 */ return -1; } } /* }}} */ PHPAPI int php_check_open_basedir(const char *path TSRMLS_DC) { return php_check_open_basedir_ex(path, 1 TSRMLS_CC); } /* {{{ php_check_open_basedir */ PHPAPI int php_check_open_basedir_ex(const char *path, int warn TSRMLS_DC) { /* Only check when open_basedir is available */ if (PG(open_basedir) && *PG(open_basedir)) { char *pathbuf; char *ptr; char *end; pathbuf = estrdup(PG(open_basedir)); ptr = pathbuf; while (ptr && *ptr) { end = strchr(ptr, DEFAULT_DIR_SEPARATOR); if (end != NULL) { *end = '\0'; end++; } if (php_check_specific_open_basedir(ptr, path TSRMLS_CC) == 0) { efree(pathbuf); return 0; } ptr = end; } if (warn) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s)", path, PG(open_basedir)); } efree(pathbuf); errno = EPERM; /* we deny permission to open it */ return -1; } /* Nothing to check... */ return 0; } /* }}} */ /* {{{ php_check_safe_mode_include_dir */ PHPAPI int php_check_safe_mode_include_dir(const char *path TSRMLS_DC) { if (PG(safe_mode)) { if (PG(safe_mode_include_dir) && *PG(safe_mode_include_dir)) { char *pathbuf; char *ptr; char *end; char resolved_name[MAXPATHLEN]; /* Resolve the real path into resolved_name */ if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) return -1; pathbuf = estrdup(PG(safe_mode_include_dir)); ptr = pathbuf; while (ptr && *ptr) { end = strchr(ptr, DEFAULT_DIR_SEPARATOR); if (end != NULL) { *end = '\0'; end++; } /* Check the path */ #ifdef PHP_WIN32 if (strncasecmp(ptr, resolved_name, strlen(ptr)) == 0) #else if (strncmp(ptr, resolved_name, strlen(ptr)) == 0) #endif { /* File is in the right directory */ efree(pathbuf); return 0; } ptr = end; } efree(pathbuf); } return -1; } /* Nothing to check... */ return 0; } /* }}} */ /* {{{ php_fopen_and_set_opened_path */ static FILE *php_fopen_and_set_opened_path(const char *path, const char *mode, char **opened_path TSRMLS_DC) { FILE *fp; if (php_check_open_basedir((char *)path TSRMLS_CC)) { return NULL; } fp = VCWD_FOPEN(path, mode); if (fp && opened_path) { *opened_path = expand_filepath(path, NULL TSRMLS_CC); } return fp; } /* }}} */ /* {{{ php_fopen_primary_script */ PHPAPI int php_fopen_primary_script(zend_file_handle *file_handle TSRMLS_DC) { FILE *fp; struct stat st; char *path_info, *filename; int length; filename = SG(request_info).path_translated; path_info = SG(request_info).request_uri; #if HAVE_PWD_H if (PG(user_dir) && *PG(user_dir) && path_info && '/' == path_info[0] && '~' == path_info[1]) { char user[32]; struct passwd *pw; char *s = strchr(path_info + 2, '/'); filename = NULL; /* discard the original filename, it must not be used */ if (s) { /* if there is no path name after the file, do not bother */ /* to try open the directory */ length = s - (path_info + 2); if (length > (int)sizeof(user) - 1) length = sizeof(user) - 1; memcpy(user, path_info + 2, length); user[length] = '\0'; pw = getpwnam(user); if (pw && pw->pw_dir) { filename = emalloc(strlen(PG(user_dir)) + strlen(path_info) + strlen(pw->pw_dir) + 4); if (filename) { sprintf(filename, "%s%c%s%c%s", pw->pw_dir, PHP_DIR_SEPARATOR, PG(user_dir), PHP_DIR_SEPARATOR, s+1); /* Safe */ STR_FREE(SG(request_info).path_translated); SG(request_info).path_translated = filename; } } } } else #endif if (PG(doc_root) && path_info) { length = strlen(PG(doc_root)); if (IS_ABSOLUTE_PATH(PG(doc_root), length)) { filename = emalloc(length + strlen(path_info) + 2); if (filename) { memcpy(filename, PG(doc_root), length); if (!IS_SLASH(filename[length - 1])) { /* length is never 0 */ filename[length++] = PHP_DIR_SEPARATOR; } if (IS_SLASH(path_info[0])) { length--; } strcpy(filename + length, path_info); STR_FREE(SG(request_info).path_translated); SG(request_info).path_translated = filename; } } } /* if doc_root && path_info */ if (!filename) { /* we have to free SG(request_info).path_translated here because php_destroy_request_info assumes that it will get freed when the include_names hash is emptied, but we're not adding it in this case */ STR_FREE(SG(request_info).path_translated); SG(request_info).path_translated = NULL; return FAILURE; } fp = VCWD_FOPEN(filename, "rb"); /* refuse to open anything that is not a regular file */ if (fp && (0 > fstat(fileno(fp), &st) || !S_ISREG(st.st_mode))) { fclose(fp); fp = NULL; } if (!fp) { STR_FREE(SG(request_info).path_translated); /* for same reason as above */ SG(request_info).path_translated = NULL; return FAILURE; } file_handle->opened_path = expand_filepath(filename, NULL TSRMLS_CC); if (!(SG(options) & SAPI_OPTION_NO_CHDIR)) { VCWD_CHDIR_FILE(filename); } SG(request_info).path_translated = filename; file_handle->filename = SG(request_info).path_translated; file_handle->free_filename = 0; file_handle->handle.fp = fp; file_handle->type = ZEND_HANDLE_FP; return SUCCESS; } /* }}} */ /* {{{ php_fopen_with_path * Tries to open a file with a PATH-style list of directories. * If the filename starts with "." or "/", the path is ignored. */ PHPAPI FILE *php_fopen_with_path(const char *filename, const char *mode, const char *path, char **opened_path TSRMLS_DC) { char *pathbuf, *ptr, *end; char *exec_fname; char trypath[MAXPATHLEN]; struct stat sb; FILE *fp; int path_length; int filename_length; int exec_fname_length; if (opened_path) { *opened_path = NULL; } if(!filename) { return NULL; } filename_length = strlen(filename); /* 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 TSRMLS_CC); } /* * files in safe_mode_include_dir (or subdir) are excluded from * safe mode GID/UID checks */ /* Absolute path open */ if (IS_ABSOLUTE_PATH(filename, filename_length)) { if ((php_check_safe_mode_include_dir(filename TSRMLS_CC)) == 0) /* filename is in safe_mode_include_dir (or subdir) */ return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC); 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 TSRMLS_CC); } if (!path || (path && !*path)) { 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 TSRMLS_CC); } /* check in provided path */ /* append the calling scripts' current working directory * as a fall back case */ if (zend_is_executing(TSRMLS_C)) { exec_fname = zend_get_executed_filename(TSRMLS_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] == '[') || exec_fname_length<=0) { /* [no active file] or no path */ pathbuf = estrdup(path); } else { pathbuf = (char *) emalloc(exec_fname_length + path_length +1 +1); memcpy(pathbuf, path, path_length); pathbuf[path_length] = DEFAULT_DIR_SEPARATOR; memcpy(pathbuf+path_length+1, exec_fname, exec_fname_length); pathbuf[path_length + exec_fname_length +1] = '\0'; } } else { pathbuf = estrdup(path); } ptr = pathbuf; while (ptr && *ptr) { end = strchr(ptr, DEFAULT_DIR_SEPARATOR); if (end != NULL) { *end = '\0'; end++; } snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename); if (PG(safe_mode)) { if (VCWD_STAT(trypath, &sb) == 0) { /* file exists ... check permission */ if ((php_check_safe_mode_include_dir(trypath TSRMLS_CC) == 0) || php_checkuid(trypath, mode, CHECKUID_CHECK_MODE_PARAM)) /* UID ok, or trypath is in safe_mode_include_dir */ fp = php_fopen_and_set_opened_path(trypath, mode, opened_path TSRMLS_CC); else fp = NULL; efree(pathbuf); return fp; } } fp = php_fopen_and_set_opened_path(trypath, mode, opened_path TSRMLS_CC); if (fp) { efree(pathbuf); return fp; } ptr = end; } /* end provided path */ efree(pathbuf); return NULL; } /* }}} */ /* {{{ php_strip_url_passwd */ PHPAPI char *php_strip_url_passwd(char *url) { register char *p, *url_start; if (url == NULL) { return ""; } p = url; while (*p) { if (*p==':' && *(p+1)=='/' && *(p+2)=='/') { /* found protocol */ url_start = p = p+3; while (*p) { if (*p=='@') { int i; for (i=0; i<3 && url_startMAXPATHLEN-1?MAXPATHLEN-1:new_state.cwd_length; memcpy(real_path, new_state.cwd, copy_len); real_path[copy_len]='\0'; } else { real_path = estrndup(new_state.cwd, new_state.cwd_length); } free(new_state.cwd); return real_path; } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 */