/// <summary> /// Merges the path with the current working directory /// to get a canonicalized absolute pathname representing the same file. /// </summary> /// <remarks> /// This method is an analogy of <c>main/safe_mode.c: php_checkuid</c>. /// Looks for the file in the <c>include_path</c> and checks for <c>open_basedir</c> restrictions. /// </remarks> /// <param name="path">An absolute or relative path to a file.</param> /// <param name="wrapper">The wrapper found for the specified file or <c>null</c> if the path resolution fails.</param> /// <param name="mode">The checking mode of the <see cref="CheckAccess"/> method (file, directory etc.).</param> /// <param name="options">Additional options for the <see cref="CheckAccess"/> method.</param> /// <returns><c>true</c> if all the resolution and checking passed without an error, <b>false</b> otherwise.</returns> /// <exception cref="PhpException">Security violation - when the target file /// lays outside the tree defined by <c>open_basedir</c> configuration option.</exception> public static bool ResolvePath(ref string path, out StreamWrapper wrapper, CheckAccessMode mode, CheckAccessOptions options) { // Path will contain the absolute path without file:// or the complete URL; filename is the relative path. string filename, scheme = GetSchemeInternal(path, out filename); wrapper = StreamWrapper.GetWrapper(scheme, (StreamOptions)options); if (wrapper == null) return false; if (wrapper.IsUrl) { // Note: path contains the whole URL, filename the same without the scheme:// portion. // What to check more? } else if (scheme != "php") { // SILVERLIGHT: ?? what to do here ?? // PhpException.Throw(PhpError.Warning, CoreResources.GetString("stream_file_access_denied", path)); } return true; }
/// <summary> /// Check StatInternal input parameters. /// </summary> /// <param name="path">The path passed to stat().</param> /// <param name="quiet">Wheter to suppress warning message if argument is empty.</param> /// <param name="wrapper">If passed, it will contain valid StremWrapper to the given <paramref name="path"/>.</param> /// <returns>True if check passed.</returns> private static bool StatInternalCheck(ref string path, bool quiet, out StreamWrapper wrapper) { wrapper = null; if (String.IsNullOrEmpty(path)) { PhpException.Throw(PhpError.Warning, LibResources.GetString("arg:empty", "path")); return(false); } CheckAccessOptions options = CheckAccessOptions.Empty; if (quiet) { options |= CheckAccessOptions.Quiet; } if (!PhpStream.ResolvePath(ref path, out wrapper, CheckAccessMode.FileOrDirectory, options)) { return(false); } // check passed return(true); }
/// <summary> /// Merges the path with the current working directory /// to get a canonicalized absolute pathname representing the same file. /// </summary> /// <remarks> /// This method is an analogy of <c>main/safe_mode.c: php_checkuid</c>. /// Looks for the file in the <c>include_path</c> and checks for <c>open_basedir</c> restrictions. /// </remarks> /// <param name="path">An absolute or relative path to a file.</param> /// <param name="wrapper">The wrapper found for the specified file or <c>null</c> if the path resolution fails.</param> /// <param name="mode">The checking mode of the <see cref="CheckAccess"/> method (file, directory etc.).</param> /// <param name="options">Additional options for the <see cref="CheckAccess"/> method.</param> /// <returns><c>true</c> if all the resolution and checking passed without an error, <b>false</b> otherwise.</returns> /// <exception cref="PhpException">Security violation - when the target file /// lays outside the tree defined by <c>open_basedir</c> configuration option.</exception> public static bool ResolvePath(ref string path, out StreamWrapper wrapper, CheckAccessMode mode, CheckAccessOptions options) { // Path will contain the absolute path without file:// or the complete URL; filename is the relative path. string filename, scheme = GetSchemeInternal(path, out filename); wrapper = StreamWrapper.GetWrapper(scheme, (StreamOptions)options); if (wrapper == null) return false; if (wrapper.IsUrl) { // Note: path contains the whole URL, filename the same without the scheme:// portion. // What to check more? } else if (scheme != "php") { try { // Filename contains the original path without the scheme:// portion, check for include path. bool isInclude = false; if ((options & CheckAccessOptions.UseIncludePath) > 0) { isInclude = CheckIncludePath(filename, ref path); } // Path will now contain an absolute path (either to an include or actual directory). if (!isInclude) { path = Path.GetFullPath(Path.Combine(ScriptContext.CurrentContext.WorkingDirectory, filename)); } } catch (Exception) { if ((options & CheckAccessOptions.Quiet) == 0) PhpException.Throw(PhpError.Warning, CoreResources.GetString("stream_filename_invalid", FileSystemUtils.StripPassword(path))); return false; } GlobalConfiguration global_config = Configuration.Global; // Note: extensions check open_basedir too -> double check.. if (!global_config.SafeMode.IsPathAllowed(path)) { if ((options & CheckAccessOptions.Quiet) == 0) PhpException.Throw(PhpError.Warning, CoreResources.GetString("open_basedir_effect", path, global_config.SafeMode.GetAllowedPathPrefixesJoin())); return false; } // Replace all '/' with '\'. // path = path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); if (Environment.OSVersion.Platform != PlatformID.Unix) { Debug.Assert( path.IndexOf(Path.AltDirectorySeparatorChar) == -1 || (Path.AltDirectorySeparatorChar == Path.DirectorySeparatorChar), // on Mono, so ignore it string.Format("'{0}' should not contain '{1}' char.", path, Path.AltDirectorySeparatorChar)); } // The file wrapper expects an absolute path w/o the scheme, others expect the scheme://url. if (scheme != "file") { path = String.Format("{0}://{1}", scheme, path); } } return true; }
/// <summary> /// Performs all checks on a path passed to a PHP function. /// </summary> /// <remarks> /// <para> /// This method performs a check similar to <c>safe_mode.c: php_checkuid_ex()</c> /// together with <c>open_basedir</c> check. /// </para> /// <para> /// The <paramref name="filename"/> may be one of the following: /// <list type="bullet"> /// <item>A relative path. The path is resolved regarding the <c>include_path</c> too if required /// and checking continues as in the next case.</item> /// <item>An absolute path. The file or directory is checked for existence and for access permissions<sup>1</sup> /// according to the given <paramref name="mode"/>.</item> /// </list> /// <sup>1</sup> Regarding the <c>open_basedir</c> configuration option. /// File access permissions are checked at the time of file manipulation /// (opening, copying etc.). /// </para> /// </remarks> /// <param name="filename">A resolved path. Must be an absolute path to a local file.</param> /// <param name="mode">One of the <see cref="CheckAccessMode"/>.</param> /// <param name="options"><c>true</c> to suppress error messages.</param> /// <returns><c>true</c> if the function may continue with file access, /// <c>false</c>to fail.</returns> /// <exception cref="PhpException">If the file can not be accessed /// and the <see cref="CheckAccessOptions.Quiet"/> is not set.</exception> public static bool CheckAccess(string filename, CheckAccessMode mode, CheckAccessOptions options) { Debug.Assert(Path.IsPathRooted(filename)); string url = FileSystemUtils.StripPassword(filename); bool quiet = (options & CheckAccessOptions.Quiet) > 0; switch (mode) { case CheckAccessMode.FileMayExist: break; case CheckAccessMode.FileExists: if (!File.Exists(filename)) { if (!quiet) PhpException.Throw(PhpError.Warning, CoreResources.GetString("stream_file_not_exists", url)); return false; } break; case CheckAccessMode.FileNotExists: if (File.Exists(filename)) { if (!quiet) PhpException.Throw(PhpError.Warning, CoreResources.GetString("stream_file_exists", url)); return false; } break; case CheckAccessMode.FileOrDirectory: if ((!Directory.Exists(filename)) && (!File.Exists(filename))) { if (!quiet) PhpException.Throw(PhpError.Warning, CoreResources.GetString("stream_path_not_exists", url)); return false; } break; case CheckAccessMode.Directory: if (!Directory.Exists(filename)) { if (!quiet) PhpException.Throw(PhpError.Warning, CoreResources.GetString("stream_directory_not_exists", url)); return false; } break; default: Debug.Assert(false); return false; } return true; }