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(), ®ister_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