From 667f7ba244f450b95ec9b1a6ed38ade75cbc8a9d Mon Sep 17 00:00:00 2001
From: Bob Vincent <bobvin@pillars.net>
Date: Fri, 10 Jun 2011 17:44:29 -0400
Subject: [PATCH] Issue #1008402 by tekante, pillarsdotnet: Allow the use of symlinks within the files directory.

---
 includes/stream_wrappers.inc |   26 +++++++++++++++++---------
 1 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/includes/stream_wrappers.inc b/includes/stream_wrappers.inc
index 7df1f9dc6b23a8e4bc3daac7f7ddb626174be5ee..1244f12d15663305c23b5f22b27476f0fdcce697 100644
--- a/includes/stream_wrappers.inc
+++ b/includes/stream_wrappers.inc
@@ -361,17 +361,25 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
     if (!isset($uri)) {
       $uri = $this->uri;
     }
-    $path = $this->getDirectoryPath() . '/' . $this->getTarget($uri);
-    $realpath = realpath($path);
-    if (!$realpath) {
-      // This file does not yet exist.
-      $realpath = realpath(dirname($path)) . '/' . basename($path);
-    }
-    $directory = realpath($this->getDirectoryPath());
-    if (!$realpath || !$directory || strpos($realpath, $directory) !== 0) {
+    // This is the target path relative to the files repository.
+    $target = '/' . $this->getTarget($uri);
+    // This is the files repository directory.
+    $repository = realpath($this->getDirectoryPath());
+    // This is the target directory.
+    $target_dir = realpath(dirname($repository . $target)) . '/';
+    // This is the target name, without any directory components.
+    $target_name = basename($repository . $target);
+    // This checks whether the target contains any directory traversal parts.
+    $traversal = preg_match('@(/|\\\\)(\.\.|~)@', $target);
+    // This checks whether the target dir exists within the files repository.
+    $subdirectory = strpos($target_dir, $repository . '/') === 0;
+    if ($traversal && !$subdirectory) {
+      // If the target path contains directory-traversal parts such as
+      // '/..' or '/~', and does not resolve to a subdirectory of the
+      // repository, then return FALSE to avoid a filesystem exploit.
       return FALSE;
     }
-    return $realpath;
+    return $target_dir . $target_name;
   }
 
   /**
-- 
1.7.4.1

