1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "content/browser/file_system/browser_file_system_helper.h"
6 
7 #include <stddef.h>
8 #include <memory>
9 #include <string>
10 #include <utility>
11 #include <vector>
12 
13 #include "base/bind.h"
14 #include "base/command_line.h"
15 #include "base/files/file_path.h"
16 #include "base/sequenced_task_runner.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/task/lazy_thread_pool_task_runner.h"
19 #include "base/task/post_task.h"
20 #include "content/browser/child_process_security_policy_impl.h"
21 #include "content/public/browser/browser_context.h"
22 #include "content/public/browser/browser_task_traits.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/child_process_security_policy.h"
25 #include "content/public/browser/content_browser_client.h"
26 #include "content/public/common/content_client.h"
27 #include "content/public/common/content_switches.h"
28 #include "content/public/common/drop_data.h"
29 #include "content/public/common/url_constants.h"
30 #include "net/base/filename_util.h"
31 #include "storage/browser/file_system/external_mount_points.h"
32 #include "storage/browser/file_system/file_permission_policy.h"
33 #include "storage/browser/file_system/file_system_backend.h"
34 #include "storage/browser/file_system/file_system_context.h"
35 #include "storage/browser/file_system/file_system_operation_runner.h"
36 #include "storage/browser/file_system/file_system_options.h"
37 #include "storage/browser/file_system/file_system_url.h"
38 #include "storage/browser/file_system/isolated_context.h"
39 #include "storage/browser/quota/quota_manager.h"
40 #include "third_party/leveldatabase/leveldb_chrome.h"
41 #include "url/gurl.h"
42 #include "url/url_constants.h"
43 
44 using storage::FileSystemOptions;
45 
46 namespace content {
47 
48 namespace {
49 
50 // All FileSystemContexts currently need to share the same sequence per sharing
51 // global objects: https://codereview.chromium.org/2883403002#msg14.
52 base::LazyThreadPoolSequencedTaskRunner g_fileapi_task_runner =
53     LAZY_THREAD_POOL_SEQUENCED_TASK_RUNNER_INITIALIZER(
54         base::TaskTraits(base::MayBlock(),
55                          base::TaskPriority::USER_VISIBLE,
56                          base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN));
57 
CreateBrowserFileSystemOptions(bool is_incognito)58 FileSystemOptions CreateBrowserFileSystemOptions(bool is_incognito) {
59   FileSystemOptions::ProfileMode profile_mode =
60       is_incognito ? FileSystemOptions::PROFILE_MODE_INCOGNITO
61                    : FileSystemOptions::PROFILE_MODE_NORMAL;
62   std::vector<std::string> additional_allowed_schemes;
63   GetContentClient()->browser()->GetAdditionalAllowedSchemesForFileSystem(
64       &additional_allowed_schemes);
65   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
66           switches::kAllowFileAccessFromFiles)) {
67     additional_allowed_schemes.push_back(url::kFileScheme);
68   }
69   return FileSystemOptions(profile_mode, is_incognito,
70                            additional_allowed_schemes);
71 }
72 
CheckCanReadFileSystemFileOnUIThread(int process_id,const storage::FileSystemURL & url)73 bool CheckCanReadFileSystemFileOnUIThread(int process_id,
74                                           const storage::FileSystemURL& url) {
75   DCHECK_CURRENTLY_ON(BrowserThread::UI);
76   ChildProcessSecurityPolicyImpl* policy =
77       ChildProcessSecurityPolicyImpl::GetInstance();
78   return policy->CanReadFileSystemFile(process_id, url);
79 }
80 
GrantReadAccessOnUIThread(int process_id,const base::FilePath & platform_path)81 void GrantReadAccessOnUIThread(int process_id,
82                                const base::FilePath& platform_path) {
83   DCHECK_CURRENTLY_ON(BrowserThread::UI);
84   ChildProcessSecurityPolicyImpl* policy =
85       ChildProcessSecurityPolicyImpl::GetInstance();
86   if (!policy->CanReadFile(process_id, platform_path)) {
87     policy->GrantReadFile(process_id, platform_path);
88   }
89 }
90 
91 // Helper function that used by SyncGetPlatformPath() to get the platform
92 // path, grant read access, and send return the path via a callback.
GetPlatformPathOnFileThread(scoped_refptr<storage::FileSystemContext> context,int process_id,const storage::FileSystemURL & url,SyncGetPlatformPathCB callback,bool can_read_filesystem_file)93 void GetPlatformPathOnFileThread(
94     scoped_refptr<storage::FileSystemContext> context,
95     int process_id,
96     const storage::FileSystemURL& url,
97     SyncGetPlatformPathCB callback,
98     bool can_read_filesystem_file) {
99   DCHECK(context->default_file_task_runner()->RunsTasksInCurrentSequence());
100 
101   if (!can_read_filesystem_file) {
102     std::move(callback).Run(base::FilePath());
103     return;
104   }
105 
106   base::FilePath platform_path;
107   context->operation_runner()->SyncGetPlatformPath(url, &platform_path);
108 
109   base::PostTaskAndReply(
110       FROM_HERE, {BrowserThread::UI},
111       base::BindOnce(&GrantReadAccessOnUIThread, process_id, platform_path),
112       base::BindOnce(std::move(callback), platform_path));
113 }
114 
115 }  // namespace
116 
CreateFileSystemContext(BrowserContext * browser_context,const base::FilePath & profile_path,bool is_incognito,storage::QuotaManagerProxy * quota_manager_proxy)117 scoped_refptr<storage::FileSystemContext> CreateFileSystemContext(
118     BrowserContext* browser_context,
119     const base::FilePath& profile_path,
120     bool is_incognito,
121     storage::QuotaManagerProxy* quota_manager_proxy) {
122   // Setting up additional filesystem backends.
123   std::vector<std::unique_ptr<storage::FileSystemBackend>> additional_backends;
124   GetContentClient()->browser()->GetAdditionalFileSystemBackends(
125       browser_context, profile_path, &additional_backends);
126 
127   // Set up the auto mount handlers for url requests.
128   std::vector<storage::URLRequestAutoMountHandler>
129       url_request_auto_mount_handlers;
130   GetContentClient()->browser()->GetURLRequestAutoMountHandlers(
131       &url_request_auto_mount_handlers);
132 
133   auto options = CreateBrowserFileSystemOptions(
134       browser_context->CanUseDiskWhenOffTheRecord() ? false : is_incognito);
135   scoped_refptr<storage::FileSystemContext> file_system_context =
136       new storage::FileSystemContext(
137           base::CreateSingleThreadTaskRunner({BrowserThread::IO}).get(),
138           g_fileapi_task_runner.Get().get(),
139           BrowserContext::GetMountPoints(browser_context),
140           browser_context->GetSpecialStoragePolicy(), quota_manager_proxy,
141           std::move(additional_backends), url_request_auto_mount_handlers,
142           profile_path, options);
143 
144   for (const storage::FileSystemType& type :
145        file_system_context->GetFileSystemTypes()) {
146     ChildProcessSecurityPolicyImpl::GetInstance()
147         ->RegisterFileSystemPermissionPolicy(
148             type, storage::FileSystemContext::GetPermissionPolicy(type));
149   }
150 
151   return file_system_context;
152 }
153 
FileSystemURLIsValid(storage::FileSystemContext * context,const storage::FileSystemURL & url)154 bool FileSystemURLIsValid(storage::FileSystemContext* context,
155                           const storage::FileSystemURL& url) {
156   if (!url.is_valid())
157     return false;
158 
159   return context->GetFileSystemBackend(url.type()) != nullptr;
160 }
161 
SyncGetPlatformPath(storage::FileSystemContext * context,int process_id,const GURL & path,SyncGetPlatformPathCB callback)162 void SyncGetPlatformPath(storage::FileSystemContext* context,
163                          int process_id,
164                          const GURL& path,
165                          SyncGetPlatformPathCB callback) {
166   DCHECK(context->default_file_task_runner()->RunsTasksInCurrentSequence());
167   storage::FileSystemURL url(context->CrackURL(path));
168   if (!FileSystemURLIsValid(context, url)) {
169     // Note: Posting a task here so this function always returns
170     // before the callback is called no matter which path is taken.
171     base::PostTask(FROM_HERE,
172                    base::BindOnce(std::move(callback), base::FilePath()));
173     return;
174   }
175 
176   // Make sure if this file is ok to be read (in the current architecture
177   // which means roughly same as the renderer is allowed to get the platform
178   // path to the file).
179   base::PostTaskAndReplyWithResult(
180       FROM_HERE, {BrowserThread::UI},
181       base::BindOnce(&CheckCanReadFileSystemFileOnUIThread, process_id, url),
182       base::BindOnce(&GetPlatformPathOnFileThread,
183                      scoped_refptr<storage::FileSystemContext>(context),
184                      process_id, url, std::move(callback)));
185 }
186 
PrepareDropDataForChildProcess(DropData * drop_data,ChildProcessSecurityPolicyImpl * security_policy,int child_id,const storage::FileSystemContext * file_system_context)187 void PrepareDropDataForChildProcess(
188     DropData* drop_data,
189     ChildProcessSecurityPolicyImpl* security_policy,
190     int child_id,
191     const storage::FileSystemContext* file_system_context) {
192 #if defined(OS_CHROMEOS)
193   // The externalfile:// scheme is used in Chrome OS to open external files in a
194   // browser tab.
195   // TODO(https://crbug.com/858972): This seems like it could be forged by the
196   // renderer. This probably needs to check that this didn't originate from the
197   // renderer... Also, this probably can just be GrantRequestURL (which doesn't
198   // yet exist) instead of GrantCommitURL.
199   if (drop_data->url.SchemeIs(content::kExternalFileScheme))
200     security_policy->GrantCommitURL(child_id, drop_data->url);
201 #endif
202 
203   // The filenames vector represents a capability to access the given files.
204   storage::IsolatedContext::FileInfoSet files;
205   for (auto& filename : drop_data->filenames) {
206     // Make sure we have the same display_name as the one we register.
207     if (filename.display_name.empty()) {
208       std::string name;
209       files.AddPath(filename.path, &name);
210       filename.display_name = base::FilePath::FromUTF8Unsafe(name);
211     } else {
212       files.AddPathWithName(filename.path,
213                             filename.display_name.AsUTF8Unsafe());
214     }
215     // A dragged file may wind up as the value of an input element, or it
216     // may be used as the target of a navigation instead.  We don't know
217     // which will happen at this point, so generously grant both access
218     // and request permissions to the specific file to cover both cases.
219     // We do not give it the permission to request all file:// URLs.
220     security_policy->GrantRequestSpecificFileURL(
221         child_id, net::FilePathToFileURL(filename.path));
222 
223     // If the renderer already has permission to read these paths, we don't need
224     // to re-grant them. This prevents problems with DnD for files in the CrOS
225     // file manager--the file manager already had read/write access to those
226     // directories, but dragging a file would cause the read/write access to be
227     // overwritten with read-only access, making them impossible to delete or
228     // rename until the renderer was killed.
229     if (!security_policy->CanReadFile(child_id, filename.path))
230       security_policy->GrantReadFile(child_id, filename.path);
231   }
232 
233   storage::IsolatedContext* isolated_context =
234       storage::IsolatedContext::GetInstance();
235   DCHECK(isolated_context);
236 
237   if (!files.fileset().empty()) {
238     std::string filesystem_id =
239         isolated_context->RegisterDraggedFileSystem(files);
240     if (!filesystem_id.empty()) {
241       // Grant the permission iff the ID is valid.
242       security_policy->GrantReadFileSystem(child_id, filesystem_id);
243     }
244     drop_data->filesystem_id = base::UTF8ToUTF16(filesystem_id);
245   }
246 
247   for (auto& file_system_file : drop_data->file_system_files) {
248     storage::FileSystemURL file_system_url =
249         file_system_context->CrackURL(file_system_file.url);
250 
251     std::string register_name;
252     storage::IsolatedContext::ScopedFSHandle filesystem =
253         isolated_context->RegisterFileSystemForPath(
254             file_system_url.type(), file_system_url.filesystem_id(),
255             file_system_url.path(), &register_name);
256 
257     if (filesystem.is_valid()) {
258       // Grant the permission iff the ID is valid. This will also keep the FS
259       // alive after |filesystem| goes out of scope.
260       security_policy->GrantReadFileSystem(child_id, filesystem.id());
261     }
262 
263     // Note: We are using the origin URL provided by the sender here. It may be
264     // different from the receiver's.
265     file_system_file.url = GURL(
266         storage::GetIsolatedFileSystemRootURIString(
267             file_system_url.origin().GetURL(), filesystem.id(), std::string())
268             .append(register_name));
269     file_system_file.filesystem_id = filesystem.id();
270   }
271 }
272 
273 }  // namespace content
274