/* {{{ expand_filepath_use_realpath */ PHPAPI char *expand_filepath_with_mode(const char *filepath, char *real_path, const char *relative_to, size_t relative_to_len, int realpath_mode) { cwd_state new_state; char cwd[MAXPATHLEN]; int copy_len; int path_len; if (!filepath[0]) { return NULL; } path_len = (int)strlen(filepath); if (IS_ABSOLUTE_PATH(filepath, path_len)) { cwd[0] = '\0'; } else { const char *iam = SG(request_info).path_translated; const char *result; if (relative_to) { if (relative_to_len > MAXPATHLEN-1U) { return NULL; } result = relative_to; memcpy(cwd, relative_to, relative_to_len+1U); } else { result = VCWD_GETCWD(cwd, MAXPATHLEN); } if (!result && (iam != filepath)) { int fdtest = -1; fdtest = VCWD_OPEN(filepath, O_RDONLY); if (fdtest != -1) { /* return a relative file path if for any reason * we cannot cannot getcwd() and the requested, * relatively referenced file is accessible */ copy_len = path_len > MAXPATHLEN - 1 ? MAXPATHLEN - 1 : path_len; if (real_path) { memcpy(real_path, filepath, copy_len); real_path[copy_len] = '\0'; } else { real_path = estrndup(filepath, copy_len); } close(fdtest); return real_path; } else { cwd[0] = '\0'; } } else if (!result) { cwd[0] = '\0'; } } new_state.cwd = estrdup(cwd); new_state.cwd_length = (int)strlen(cwd); if (virtual_file_ex(&new_state, filepath, NULL, realpath_mode)) { efree(new_state.cwd); return NULL; } if (real_path) { copy_len = new_state.cwd_length > MAXPATHLEN - 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); } efree(new_state.cwd); return real_path; }
/* {{{ 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) { 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; size_t path_len; int nesting_level = 0; /* 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)); } 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) == 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) { #if defined(PHP_WIN32) || defined(HAVE_SYMLINK) if (nesting_level == 0) { int ret; char buf[MAXPATHLEN]; ret = php_sys_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 #ifdef PHP_WIN32 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; #ifdef PHP_WIN32 if (path_len > 1 && path_tmp[path_len - 2] == ':') { if (path_len != 3) { return -1; } /* this is c:\ */ path_tmp[path_len] = '\0'; } else { path_tmp[path_len - 1] = '\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) != NULL) { int basedir_len = (int)strlen(basedir); /* Handler for basedirs that end with a / */ resolved_basedir_len = (int)strlen(resolved_basedir); #ifdef PHP_WIN32 if (basedir[basedir_len - 1] == PHP_DIR_SEPARATOR || basedir[basedir_len - 1] == '/') { #else if (basedir[basedir_len - 1] == PHP_DIR_SEPARATOR) { #endif if (resolved_basedir[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) { resolved_basedir[resolved_basedir_len] = PHP_DIR_SEPARATOR; resolved_basedir[++resolved_basedir_len] = '\0'; } } else { resolved_basedir[resolved_basedir_len++] = PHP_DIR_SEPARATOR; resolved_basedir[resolved_basedir_len] = '\0'; } resolved_name_len = (int)strlen(resolved_name); if (path_tmp[path_len - 1] == PHP_DIR_SEPARATOR) { 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 */ #ifdef PHP_WIN32 if (strncasecmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) { #else if (strncmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) { #endif if (resolved_name_len > resolved_basedir_len && resolved_name[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) { return -1; } else { /* File is in the right directory */ return 0; } } else { /* /openbasedir/ and /openbasedir are the same directory */ if (resolved_basedir_len == (resolved_name_len + 1) && resolved_basedir[resolved_basedir_len - 1] == PHP_DIR_SEPARATOR) { #ifdef PHP_WIN32 if (strncasecmp(resolved_basedir, resolved_name, resolved_name_len) == 0) { #else if (strncmp(resolved_basedir, resolved_name, resolved_name_len) == 0) { #endif return 0; } } return -1; } } else { /* Unable to resolve the real path, return -1 */ return -1; } } /* }}} */ PHPAPI int php_check_open_basedir(const char *path) { return php_check_open_basedir_ex(path, 1); } /* {{{ php_check_open_basedir */ PHPAPI int php_check_open_basedir_ex(const char *path, int warn) { /* Only check when open_basedir is available */ if (PG(open_basedir) && *PG(open_basedir)) { char *pathbuf; char *ptr; char *end; /* Check if the path is too long so we can give a more useful error * message. */ if (strlen(path) > (MAXPATHLEN - 1)) { php_error_docref(NULL, E_WARNING, "File name is longer than the maximum allowed path length on this platform (%d): %s", MAXPATHLEN, path); errno = EINVAL; return -1; } 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) == 0) { efree(pathbuf); return 0; } ptr = end; } if (warn) { php_error_docref(NULL, 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_fopen_and_set_opened_path */ static FILE *php_fopen_and_set_opened_path(const char *path, const char *mode, zend_string **opened_path) { FILE *fp; if (php_check_open_basedir((char *)path)) { return NULL; } fp = VCWD_FOPEN(path, mode); if (fp && opened_path) { //TODO :avoid reallocation char *tmp = expand_filepath_with_mode(path, NULL, NULL, 0, CWD_EXPAND); if (tmp) { *opened_path = zend_string_init(tmp, strlen(tmp), 0); efree(tmp); } } return fp; }
/* {{{ proto boolean XMLReader::XML(string source [, string encoding [, int options]]) Sets the string that the XMLReader will parse. */ PHP_METHOD(xmlreader, XML) { zval *id; size_t source_len = 0, encoding_len = 0; zend_long options = 0; xmlreader_object *intern = NULL; char *source, *uri = NULL, *encoding = NULL; int resolved_path_len, ret = 0; char *directory=NULL, resolved_path[MAXPATHLEN]; xmlParserInputBufferPtr inputbfr; xmlTextReaderPtr reader; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!l", &source, &source_len, &encoding, &encoding_len, &options) == FAILURE) { return; } id = getThis(); if (id != NULL && ! instanceof_function(Z_OBJCE_P(id), xmlreader_class_entry)) { id = NULL; } if (id != NULL) { intern = Z_XMLREADER_P(id); xmlreader_free_resources(intern); } if (!source_len) { php_error_docref(NULL, E_WARNING, "Empty string supplied as input"); RETURN_FALSE; } inputbfr = xmlParserInputBufferCreateMem(source, source_len, XML_CHAR_ENCODING_NONE); if (inputbfr != NULL) { /* Get the URI of the current script so that we can set the base directory in libxml */ #if HAVE_GETCWD directory = VCWD_GETCWD(resolved_path, MAXPATHLEN); #elif HAVE_GETWD directory = VCWD_GETWD(resolved_path); #endif if (directory) { resolved_path_len = strlen(resolved_path); if (resolved_path[resolved_path_len - 1] != DEFAULT_SLASH) { resolved_path[resolved_path_len] = DEFAULT_SLASH; resolved_path[++resolved_path_len] = '\0'; } uri = (char *) xmlCanonicPath((const xmlChar *) resolved_path); } reader = xmlNewTextReader(inputbfr, uri); if (reader != NULL) { #if LIBXML_VERSION >= 20628 ret = xmlTextReaderSetup(reader, NULL, uri, encoding, options); #endif if (ret == 0) { if (id == NULL) { object_init_ex(return_value, xmlreader_class_entry); intern = Z_XMLREADER_P(return_value); } else { RETVAL_TRUE; } intern->input = inputbfr; intern->ptr = reader; if (uri) { xmlFree(uri); } return; } } } if (uri) { xmlFree(uri); } if (inputbfr) { xmlFreeParserInputBuffer(inputbfr); } php_error_docref(NULL, E_WARNING, "Unable to load source data"); RETURN_FALSE; }
static int php_do_open_temporary_file(const char *path, const char *pfx, zend_string **opened_path_p) { char *trailing_slash; #ifdef PHP_WIN32 char *opened_path = NULL; size_t opened_path_len; wchar_t *cwdw, *pfxw, pathw[MAXPATHLEN]; #else char opened_path[MAXPATHLEN]; #endif char cwd[MAXPATHLEN]; cwd_state new_state; int fd = -1; #ifndef HAVE_MKSTEMP int open_flags = O_CREAT | O_TRUNC | O_RDWR #ifdef PHP_WIN32 | _O_BINARY #endif ; #endif if (!path || !path[0]) { return -1; } #ifdef PHP_WIN32 if (!php_win32_check_trailing_space(pfx, (const int)strlen(pfx))) { SetLastError(ERROR_INVALID_NAME); return -1; } #endif if (!VCWD_GETCWD(cwd, MAXPATHLEN)) { cwd[0] = '\0'; } new_state.cwd = estrdup(cwd); new_state.cwd_length = (int)strlen(cwd); if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) { efree(new_state.cwd); return -1; } if (IS_SLASH(new_state.cwd[new_state.cwd_length - 1])) { trailing_slash = ""; } else { trailing_slash = "/"; } #ifndef PHP_WIN32 if (snprintf(opened_path, MAXPATHLEN, "%s%s%sXXXXXX", new_state.cwd, trailing_slash, pfx) >= MAXPATHLEN) { efree(new_state.cwd); return -1; } #endif #ifdef PHP_WIN32 cwdw = php_win32_ioutil_any_to_w(new_state.cwd); pfxw = php_win32_ioutil_any_to_w(pfx); if (!cwdw || !pfxw) { free(cwdw); free(pfxw); efree(new_state.cwd); return -1; } if (GetTempFileNameW(cwdw, pfxw, 0, pathw)) { opened_path = php_win32_ioutil_conv_w_to_any(pathw, PHP_WIN32_CP_IGNORE_LEN, &opened_path_len); if (!opened_path || opened_path_len >= MAXPATHLEN) { free(cwdw); free(pfxw); efree(new_state.cwd); return -1; } assert(strlen(opened_path) == opened_path_len); /* Some versions of windows set the temp file to be read-only, * which means that opening it will fail... */ if (VCWD_CHMOD(opened_path, 0600)) { free(cwdw); free(pfxw); efree(new_state.cwd); free(opened_path); return -1; } fd = VCWD_OPEN_MODE(opened_path, open_flags, 0600); } free(cwdw); free(pfxw); #elif defined(HAVE_MKSTEMP) fd = mkstemp(opened_path); #else if (mktemp(opened_path)) { fd = VCWD_OPEN(opened_path, open_flags); } #endif #ifdef PHP_WIN32 if (fd != -1 && opened_path_p) { *opened_path_p = zend_string_init(opened_path, opened_path_len, 0); } free(opened_path); #else if (fd != -1 && opened_path_p) { *opened_path_p = zend_string_init(opened_path, strlen(opened_path), 0); } #endif efree(new_state.cwd); return fd; }
PHPAPI int php_checkuid_ex(const char *filename, const char *fopen_mode, int mode, int flags) { struct stat sb; int ret, nofile=0; long uid=0L, gid=0L, duid=0L, dgid=0L; char path[MAXPATHLEN]; char *s, filenamecopy[MAXPATHLEN]; TSRMLS_FETCH(); path[0] = '\0'; if (!filename) { return 0; /* path must be provided */ } if (strlcpy(filenamecopy, filename, MAXPATHLEN)>=MAXPATHLEN) { return 0; } filename=(char *)&filenamecopy; if (fopen_mode) { if (fopen_mode[0] == 'r') { mode = CHECKUID_DISALLOW_FILE_NOT_EXISTS; } else { mode = CHECKUID_CHECK_FILE_AND_DIR; } } /* 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) { #if HAVE_BROKEN_GETCWD char ftest[MAXPATHLEN]; strcpy(ftest, filename); if (VCWD_GETCWD(ftest, sizeof(ftest)) == NULL) { strcpy(path, filename); } else #endif expand_filepath(filename, path TSRMLS_CC); ret = VCWD_STAT(path, &sb); if (ret < 0) { if (mode == CHECKUID_DISALLOW_FILE_NOT_EXISTS) { if ((flags & CHECKUID_NO_ERRORS) == 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to access %s", filename); } return 0; } else if (mode == CHECKUID_ALLOW_FILE_NOT_EXISTS) { if ((flags & CHECKUID_NO_ERRORS) == 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to access %s", filename); } return 1; } nofile = 1; } 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; } } /* Trim off filename */ if ((s = strrchr(path, DEFAULT_SLASH))) { if (*(s + 1) == '\0' && s != path) { /* make sure that the / is not the last character */ *s = '\0'; s = strrchr(path, DEFAULT_SLASH); } if (s) { if (s == path) { path[1] = '\0'; } else { *s = '\0'; } } } } else { /* CHECKUID_ALLOW_ONLY_DIR */ s = strrchr(filename, DEFAULT_SLASH); if (s == filename) { /* root dir */ path[0] = DEFAULT_SLASH; path[1] = '\0'; } else if (s && *(s + 1) != '\0') { /* make sure that the / is not the last character */ *s = '\0'; VCWD_REALPATH(filename, path); *s = DEFAULT_SLASH; } else { /* Under Solaris, getcwd() can fail if there are no * read permissions on a component of the path, even * though it has the required x permissions */ path[0] = '.'; path[1] = '\0'; VCWD_GETCWD(path, sizeof(path)); } } /* end CHECKUID_ALLOW_ONLY_DIR */ if (mode != CHECKUID_ALLOW_ONLY_FILE) { /* check directory */ ret = VCWD_STAT(path, &sb); if (ret < 0) { if ((flags & CHECKUID_NO_ERRORS) == 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to access %s", filename); } return 0; } duid = sb.st_uid; dgid = sb.st_gid; if (duid == php_getuid()) { return 1; } else if (PG(safe_mode_gid) && dgid == php_getgid()) { return 1; } else { if (SG(rfc1867_uploaded_files)) { if (zend_hash_exists(SG(rfc1867_uploaded_files), (char *) filename, strlen(filename)+1)) { return 1; } } } } if (mode == CHECKUID_ALLOW_ONLY_DIR) { uid = duid; gid = dgid; if (s) { *s = 0; } } if (nofile) { uid = duid; gid = dgid; filename = path; } if ((flags & CHECKUID_NO_ERRORS) == 0) { if (PG(safe_mode_gid)) { php_error_docref(NULL TSRMLS_CC, 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_docref(NULL TSRMLS_CC, 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; }
static void _php_do_opendir(INTERNAL_FUNCTION_PARAMETERS, int createobject) { pval **arg; php_dir *dirp; DIRLS_FETCH(); if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) { WRONG_PARAM_COUNT; } convert_to_string_ex(arg); if (php_check_open_basedir((*arg)->value.str.val)) { RETURN_FALSE; } dirp = emalloc(sizeof(php_dir)); dirp->dir = VCWD_OPENDIR((*arg)->value.str.val); #ifdef PHP_WIN32 if (!dirp->dir || dirp->dir->finished) { if (dirp->dir) { closedir(dirp->dir); } #else if (!dirp->dir) { #endif efree(dirp); php_error(E_WARNING, "OpenDir: %s (errno %d)", strerror(errno), errno); RETURN_FALSE; } dirp->id = zend_list_insert(dirp,le_dirp); php_set_default_dir(dirp->id DIRLS_CC); if (createobject) { object_init_ex(return_value, dir_class_entry_ptr); add_property_stringl(return_value, "path", (*arg)->value.str.val, (*arg)->value.str.len, 1); add_property_resource(return_value, "handle", dirp->id); zend_list_addref(dirp->id); } else { RETURN_RESOURCE(dirp->id); } } /* }}} */ /* {{{ proto int opendir(string path) Open a directory and return a dir_handle */ PHP_FUNCTION(opendir) { _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU,0); } /* }}} */ /* {{{ proto class dir(string directory) Directory class with properties, handle and class and methods read, rewind and close */ PHP_FUNCTION(getdir) { _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU,1); } /* }}} */ /* {{{ proto void closedir([int dir_handle]) Close directory connection identified by the dir_handle */ PHP_FUNCTION(closedir) { pval **id, **tmp, *myself; php_dir *dirp; DIRLS_FETCH(); FETCH_DIRP(); zend_list_delete(dirp->id); if (dirp->id == DIRG(default_dir)) { php_set_default_dir(-1 DIRLS_CC); } } /* }}} */ #if defined(HAVE_CHROOT) && !defined(ZTS) /* {{{ proto int chroot(string directory) Change root directory */ PHP_FUNCTION(chroot) { pval **arg; int ret; if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) { WRONG_PARAM_COUNT; } convert_to_string_ex(arg); ret = chroot((*arg)->value.str.val); if (ret != 0) { php_error(E_WARNING, "chroot: %s (errno %d)", strerror(errno), errno); RETURN_FALSE; } ret = chdir("/"); if (ret != 0) { php_error(E_WARNING, "chdir: %s (errno %d)", strerror(errno), errno); RETURN_FALSE; } RETURN_TRUE; } /* }}} */ #endif /* {{{ proto int chdir(string directory) Change the current directory */ PHP_FUNCTION(chdir) { pval **arg; int ret; PLS_FETCH(); if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) { WRONG_PARAM_COUNT; } convert_to_string_ex(arg); if (PG(safe_mode) && !php_checkuid((*arg)->value.str.val, NULL, CHECKUID_ALLOW_ONLY_DIR)) { RETURN_FALSE; } ret = VCWD_CHDIR((*arg)->value.str.val); if (ret != 0) { php_error(E_WARNING, "ChDir: %s (errno %d)", strerror(errno), errno); RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ proto string getcwd(void) Gets the current directory */ PHP_FUNCTION(getcwd) { char path[MAXPATHLEN]; char *ret=NULL; if (ZEND_NUM_ARGS() != 0) { WRONG_PARAM_COUNT; } #if HAVE_GETCWD ret = VCWD_GETCWD(path, MAXPATHLEN); #elif HAVE_GETWD ret = VCWD_GETWD(path); /* * #warning is not ANSI C * #else * #warning no proper getcwd support for your site */ #endif if (ret) { RETURN_STRING(path,1); } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto void rewinddir([int dir_handle]) Rewind dir_handle back to the start */ PHP_FUNCTION(rewinddir) { pval **id, **tmp, *myself; php_dir *dirp; DIRLS_FETCH(); FETCH_DIRP(); rewinddir(dirp->dir); } /* }}} */ /* {{{ proto string readdir([int dir_handle]) Read directory entry from dir_handle */ PHP_NAMED_FUNCTION(php_if_readdir) { pval **id, **tmp, *myself; php_dir *dirp; char entry[sizeof(struct dirent)+MAXPATHLEN]; struct dirent *result = (struct dirent *)&entry; /* patch for libc5 readdir problems */ DIRLS_FETCH(); FETCH_DIRP(); if (php_readdir_r(dirp->dir, (struct dirent *) entry, &result) == 0 && result) { RETURN_STRINGL(result->d_name, strlen(result->d_name), 1); } RETURN_FALSE; }