Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(391)

Side by Side Diff: content/browser/child_process_security_policy.cc

Issue 9360014: Create a content public browser API around the ChildProcessSecurityPolicy class. The implementati... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/child_process_security_policy.h"
6
7 #include "base/file_path.h"
8 #include "base/logging.h"
9 #include "base/metrics/histogram.h"
10 #include "base/platform_file.h"
11 #include "base/stl_util.h"
12 #include "base/string_util.h"
13 #include "content/browser/site_instance_impl.h"
14 #include "content/public/browser/content_browser_client.h"
15 #include "content/public/common/bindings_policy.h"
16 #include "content/public/common/url_constants.h"
17 #include "googleurl/src/gurl.h"
18 #include "net/url_request/url_request.h"
19 #include "webkit/fileapi/isolated_context.h"
20
21 using content::SiteInstance;
22
23 static const int kReadFilePermissions =
24 base::PLATFORM_FILE_OPEN |
25 base::PLATFORM_FILE_READ |
26 base::PLATFORM_FILE_EXCLUSIVE_READ |
27 base::PLATFORM_FILE_ASYNC;
28
29 static const int kEnumerateDirectoryPermissions =
30 kReadFilePermissions |
31 base::PLATFORM_FILE_ENUMERATE;
32
33 // The SecurityState class is used to maintain per-child process security state
34 // information.
35 class ChildProcessSecurityPolicy::SecurityState {
36 public:
37 SecurityState()
38 : enabled_bindings_(0),
39 can_read_raw_cookies_(false) { }
40
41 ~SecurityState() {
42 scheme_policy_.clear();
43 fileapi::IsolatedContext* isolated_context =
44 fileapi::IsolatedContext::GetInstance();
45 for (FileSystemSet::iterator iter = access_granted_filesystems_.begin();
46 iter != access_granted_filesystems_.end();
47 ++iter) {
48 isolated_context->RevokeIsolatedFileSystem(*iter);
49 }
50 UMA_HISTOGRAM_COUNTS("ChildProcessSecurityPolicy.PerChildFilePermissions",
51 file_permissions_.size());
52 }
53
54 // Grant permission to request URLs with the specified scheme.
55 void GrantScheme(const std::string& scheme) {
56 scheme_policy_[scheme] = true;
57 }
58
59 // Revoke permission to request URLs with the specified scheme.
60 void RevokeScheme(const std::string& scheme) {
61 scheme_policy_[scheme] = false;
62 }
63
64 // Grant certain permissions to a file.
65 void GrantPermissionsForFile(const FilePath& file, int permissions) {
66 FilePath stripped = file.StripTrailingSeparators();
67 file_permissions_[stripped] |= permissions;
68 UMA_HISTOGRAM_COUNTS("ChildProcessSecurityPolicy.FilePermissionPathLength",
69 stripped.value().size());
70 }
71
72 // Revokes all permissions granted to a file.
73 void RevokeAllPermissionsForFile(const FilePath& file) {
74 file_permissions_.erase(file.StripTrailingSeparators());
75 }
76
77 // Grant certain permissions to a file.
78 void GrantAccessFileSystem(const std::string& filesystem_id) {
79 access_granted_filesystems_.insert(filesystem_id);
80 }
81
82 void GrantBindings(int bindings) {
83 enabled_bindings_ |= bindings;
84 }
85
86 void GrantReadRawCookies() {
87 can_read_raw_cookies_ = true;
88 }
89
90 void RevokeReadRawCookies() {
91 can_read_raw_cookies_ = false;
92 }
93
94 // Determine whether permission has been granted to request url.
95 // Schemes that have not been granted default to being denied.
96 bool CanRequestURL(const GURL& url) {
97 SchemeMap::const_iterator judgment(scheme_policy_.find(url.scheme()));
98
99 if (judgment == scheme_policy_.end())
100 return false; // Unmentioned schemes are disallowed.
101
102 return judgment->second;
103 }
104
105 // Determine if the certain permissions have been granted to a file.
106 bool HasPermissionsForFile(const FilePath& file, int permissions) {
107 FilePath current_path = file.StripTrailingSeparators();
108 FilePath last_path;
109 while (current_path != last_path) {
110 if (file_permissions_.find(current_path) != file_permissions_.end())
111 return (file_permissions_[current_path] & permissions) == permissions;
112 last_path = current_path;
113 current_path = current_path.DirName();
114 }
115
116 return false;
117 }
118
119 bool CanUseCookiesForOrigin(const GURL& gurl) {
120 if (origin_lock_.is_empty())
121 return true;
122 GURL site_gurl = SiteInstanceImpl::GetSiteForURL(NULL, gurl);
123 return origin_lock_ == site_gurl;
124 }
125
126 void LockToOrigin(const GURL& gurl) {
127 origin_lock_ = gurl;
128 }
129
130 bool has_web_ui_bindings() const {
131 return enabled_bindings_ & content::BINDINGS_POLICY_WEB_UI;
132 }
133
134 bool can_read_raw_cookies() const {
135 return can_read_raw_cookies_;
136 }
137
138 private:
139 typedef std::map<std::string, bool> SchemeMap;
140 typedef std::map<FilePath, int> FileMap; // bit-set of PlatformFileFlags
141 typedef std::set<std::string> FileSystemSet;
142
143 // Maps URL schemes to whether permission has been granted or revoked:
144 // |true| means the scheme has been granted.
145 // |false| means the scheme has been revoked.
146 // If a scheme is not present in the map, then it has never been granted
147 // or revoked.
148 SchemeMap scheme_policy_;
149
150 // The set of files the child process is permited to upload to the web.
151 FileMap file_permissions_;
152
153 int enabled_bindings_;
154
155 bool can_read_raw_cookies_;
156
157 GURL origin_lock_;
158
159 // The set of isolated filesystems the child process is permitted to access.
160 FileSystemSet access_granted_filesystems_;
161
162 DISALLOW_COPY_AND_ASSIGN(SecurityState);
163 };
164
165 ChildProcessSecurityPolicy::ChildProcessSecurityPolicy() {
166 // We know about these schemes and believe them to be safe.
167 RegisterWebSafeScheme(chrome::kHttpScheme);
168 RegisterWebSafeScheme(chrome::kHttpsScheme);
169 RegisterWebSafeScheme(chrome::kFtpScheme);
170 RegisterWebSafeScheme(chrome::kDataScheme);
171 RegisterWebSafeScheme("feed");
172 RegisterWebSafeScheme(chrome::kBlobScheme);
173 RegisterWebSafeScheme(chrome::kFileSystemScheme);
174
175 // We know about the following pseudo schemes and treat them specially.
176 RegisterPseudoScheme(chrome::kAboutScheme);
177 RegisterPseudoScheme(chrome::kJavaScriptScheme);
178 RegisterPseudoScheme(chrome::kViewSourceScheme);
179 }
180
181 ChildProcessSecurityPolicy::~ChildProcessSecurityPolicy() {
182 web_safe_schemes_.clear();
183 pseudo_schemes_.clear();
184 STLDeleteContainerPairSecondPointers(security_state_.begin(),
185 security_state_.end());
186 security_state_.clear();
187 }
188
189 // static
190 ChildProcessSecurityPolicy* ChildProcessSecurityPolicy::GetInstance() {
191 return Singleton<ChildProcessSecurityPolicy>::get();
192 }
193
194 void ChildProcessSecurityPolicy::Add(int child_id) {
195 base::AutoLock lock(lock_);
196 AddChild(child_id);
197 }
198
199 void ChildProcessSecurityPolicy::AddWorker(int child_id,
200 int main_render_process_id) {
201 base::AutoLock lock(lock_);
202 AddChild(child_id);
203 worker_map_[child_id] = main_render_process_id;
204 }
205
206 void ChildProcessSecurityPolicy::Remove(int child_id) {
207 base::AutoLock lock(lock_);
208 if (!security_state_.count(child_id))
209 return; // May be called multiple times.
210
211 delete security_state_[child_id];
212 security_state_.erase(child_id);
213 worker_map_.erase(child_id);
214 }
215
216 void ChildProcessSecurityPolicy::RegisterWebSafeScheme(
217 const std::string& scheme) {
218 base::AutoLock lock(lock_);
219 DCHECK(web_safe_schemes_.count(scheme) == 0) << "Add schemes at most once.";
220 DCHECK(pseudo_schemes_.count(scheme) == 0) << "Web-safe implies not pseudo.";
221
222 web_safe_schemes_.insert(scheme);
223 }
224
225 bool ChildProcessSecurityPolicy::IsWebSafeScheme(const std::string& scheme) {
226 base::AutoLock lock(lock_);
227
228 return (web_safe_schemes_.find(scheme) != web_safe_schemes_.end());
229 }
230
231 void ChildProcessSecurityPolicy::RegisterPseudoScheme(
232 const std::string& scheme) {
233 base::AutoLock lock(lock_);
234 DCHECK(pseudo_schemes_.count(scheme) == 0) << "Add schemes at most once.";
235 DCHECK(web_safe_schemes_.count(scheme) == 0) <<
236 "Pseudo implies not web-safe.";
237
238 pseudo_schemes_.insert(scheme);
239 }
240
241 bool ChildProcessSecurityPolicy::IsPseudoScheme(const std::string& scheme) {
242 base::AutoLock lock(lock_);
243
244 return (pseudo_schemes_.find(scheme) != pseudo_schemes_.end());
245 }
246
247 void ChildProcessSecurityPolicy::RegisterDisabledSchemes(
248 const std::set<std::string>& schemes) {
249 base::AutoLock lock(lock_);
250 disabled_schemes_ = schemes;
251 }
252
253 bool ChildProcessSecurityPolicy::IsDisabledScheme(const std::string& scheme) {
254 base::AutoLock lock(lock_);
255 return disabled_schemes_.find(scheme) != disabled_schemes_.end();
256 }
257
258 void ChildProcessSecurityPolicy::GrantRequestURL(
259 int child_id, const GURL& url) {
260
261 if (!url.is_valid())
262 return; // Can't grant the capability to request invalid URLs.
263
264 if (IsWebSafeScheme(url.scheme()))
265 return; // The scheme has already been whitelisted for every child process.
266
267 if (IsPseudoScheme(url.scheme())) {
268 // The view-source scheme is a special case of a pseudo-URL that eventually
269 // results in requesting its embedded URL.
270 if (url.SchemeIs(chrome::kViewSourceScheme)) {
271 // URLs with the view-source scheme typically look like:
272 // view-source:http://www.google.com/a
273 // In order to request these URLs, the child_id needs to be able to
274 // request the embedded URL.
275 GrantRequestURL(child_id, GURL(url.path()));
276 }
277
278 return; // Can't grant the capability to request pseudo schemes.
279 }
280
281 {
282 base::AutoLock lock(lock_);
283 SecurityStateMap::iterator state = security_state_.find(child_id);
284 if (state == security_state_.end())
285 return;
286
287 // If the child process has been commanded to request a scheme, then we
288 // grant it the capability to request URLs of that scheme.
289 state->second->GrantScheme(url.scheme());
290 }
291 }
292
293 void ChildProcessSecurityPolicy::GrantReadFile(int child_id,
294 const FilePath& file) {
295 GrantPermissionsForFile(child_id, file, kReadFilePermissions);
296 }
297
298 void ChildProcessSecurityPolicy::GrantReadDirectory(int child_id,
299 const FilePath& directory) {
300 GrantPermissionsForFile(child_id, directory, kEnumerateDirectoryPermissions);
301 }
302
303 void ChildProcessSecurityPolicy::GrantPermissionsForFile(
304 int child_id, const FilePath& file, int permissions) {
305 base::AutoLock lock(lock_);
306
307 SecurityStateMap::iterator state = security_state_.find(child_id);
308 if (state == security_state_.end())
309 return;
310
311 state->second->GrantPermissionsForFile(file, permissions);
312 }
313
314 void ChildProcessSecurityPolicy::RevokeAllPermissionsForFile(
315 int child_id, const FilePath& file) {
316 base::AutoLock lock(lock_);
317
318 SecurityStateMap::iterator state = security_state_.find(child_id);
319 if (state == security_state_.end())
320 return;
321
322 state->second->RevokeAllPermissionsForFile(file);
323 }
324
325 void ChildProcessSecurityPolicy::GrantAccessFileSystem(
326 int child_id, const std::string& filesystem_id) {
327 base::AutoLock lock(lock_);
328
329 SecurityStateMap::iterator state = security_state_.find(child_id);
330 if (state == security_state_.end())
331 return;
332
333 state->second->GrantAccessFileSystem(filesystem_id);
334 }
335
336 void ChildProcessSecurityPolicy::GrantScheme(int child_id,
337 const std::string& scheme) {
338 base::AutoLock lock(lock_);
339
340 SecurityStateMap::iterator state = security_state_.find(child_id);
341 if (state == security_state_.end())
342 return;
343
344 state->second->GrantScheme(scheme);
345 }
346
347 void ChildProcessSecurityPolicy::GrantWebUIBindings(int child_id) {
348 base::AutoLock lock(lock_);
349
350 SecurityStateMap::iterator state = security_state_.find(child_id);
351 if (state == security_state_.end())
352 return;
353
354 state->second->GrantBindings(content::BINDINGS_POLICY_WEB_UI);
355
356 // Web UI bindings need the ability to request chrome: URLs.
357 state->second->GrantScheme(chrome::kChromeUIScheme);
358
359 // Web UI pages can contain links to file:// URLs.
360 state->second->GrantScheme(chrome::kFileScheme);
361 }
362
363 void ChildProcessSecurityPolicy::GrantReadRawCookies(int child_id) {
364 base::AutoLock lock(lock_);
365
366 SecurityStateMap::iterator state = security_state_.find(child_id);
367 if (state == security_state_.end())
368 return;
369
370 state->second->GrantReadRawCookies();
371 }
372
373 void ChildProcessSecurityPolicy::RevokeReadRawCookies(int child_id) {
374 base::AutoLock lock(lock_);
375
376 SecurityStateMap::iterator state = security_state_.find(child_id);
377 if (state == security_state_.end())
378 return;
379
380 state->second->RevokeReadRawCookies();
381 }
382
383 bool ChildProcessSecurityPolicy::CanRequestURL(
384 int child_id, const GURL& url) {
385 if (!url.is_valid())
386 return false; // Can't request invalid URLs.
387
388 if (IsDisabledScheme(url.scheme()))
389 return false; // The scheme is disabled by policy.
390
391 if (IsWebSafeScheme(url.scheme()))
392 return true; // The scheme has been white-listed for every child process.
393
394 if (IsPseudoScheme(url.scheme())) {
395 // There are a number of special cases for pseudo schemes.
396
397 if (url.SchemeIs(chrome::kViewSourceScheme)) {
398 // A view-source URL is allowed if the child process is permitted to
399 // request the embedded URL. Careful to avoid pointless recursion.
400 GURL child_url(url.path());
401 if (child_url.SchemeIs(chrome::kViewSourceScheme) &&
402 url.SchemeIs(chrome::kViewSourceScheme))
403 return false;
404
405 return CanRequestURL(child_id, child_url);
406 }
407
408 if (LowerCaseEqualsASCII(url.spec(), chrome::kAboutBlankURL))
409 return true; // Every child process can request <about:blank>.
410
411 // URLs like <about:memory> and <about:crash> shouldn't be requestable by
412 // any child process. Also, this case covers <javascript:...>, which should
413 // be handled internally by the process and not kicked up to the browser.
414 return false;
415 }
416
417 if (!content::GetContentClient()->browser()->IsHandledURL(url) &&
418 !net::URLRequest::IsHandledURL(url)) {
419 return true; // This URL request is destined for ShellExecute.
420 }
421
422 {
423 base::AutoLock lock(lock_);
424
425 SecurityStateMap::iterator state = security_state_.find(child_id);
426 if (state == security_state_.end())
427 return false;
428
429 // Otherwise, we consult the child process's security state to see if it is
430 // allowed to request the URL.
431 return state->second->CanRequestURL(url);
432 }
433 }
434
435 bool ChildProcessSecurityPolicy::CanReadFile(int child_id,
436 const FilePath& file) {
437 return HasPermissionsForFile(child_id, file, kReadFilePermissions);
438 }
439
440 bool ChildProcessSecurityPolicy::CanReadDirectory(int child_id,
441 const FilePath& directory) {
442 return HasPermissionsForFile(child_id,
443 directory,
444 kEnumerateDirectoryPermissions);
445 }
446
447 bool ChildProcessSecurityPolicy::HasPermissionsForFile(
448 int child_id, const FilePath& file, int permissions) {
449 base::AutoLock lock(lock_);
450 bool result = ChildProcessHasPermissionsForFile(child_id, file, permissions);
451 if (!result) {
452 // If this is a worker thread that has no access to a given file,
453 // let's check that its renderer process has access to that file instead.
454 WorkerToMainProcessMap::iterator iter = worker_map_.find(child_id);
455 if (iter != worker_map_.end() && iter->second != 0) {
456 result = ChildProcessHasPermissionsForFile(iter->second,
457 file,
458 permissions);
459 }
460 }
461 return result;
462 }
463
464 bool ChildProcessSecurityPolicy::HasWebUIBindings(int child_id) {
465 base::AutoLock lock(lock_);
466
467 SecurityStateMap::iterator state = security_state_.find(child_id);
468 if (state == security_state_.end())
469 return false;
470
471 return state->second->has_web_ui_bindings();
472 }
473
474 bool ChildProcessSecurityPolicy::CanReadRawCookies(int child_id) {
475 base::AutoLock lock(lock_);
476
477 SecurityStateMap::iterator state = security_state_.find(child_id);
478 if (state == security_state_.end())
479 return false;
480
481 return state->second->can_read_raw_cookies();
482 }
483
484 void ChildProcessSecurityPolicy::AddChild(int child_id) {
485 if (security_state_.count(child_id) != 0) {
486 NOTREACHED() << "Add child process at most once.";
487 return;
488 }
489
490 security_state_[child_id] = new SecurityState();
491 }
492
493 bool ChildProcessSecurityPolicy::ChildProcessHasPermissionsForFile(
494 int child_id, const FilePath& file, int permissions) {
495 SecurityStateMap::iterator state = security_state_.find(child_id);
496 if (state == security_state_.end())
497 return false;
498 return state->second->HasPermissionsForFile(file, permissions);
499 }
500
501 bool ChildProcessSecurityPolicy::CanUseCookiesForOrigin(int child_id,
502 const GURL& gurl) {
503 base::AutoLock lock(lock_);
504 SecurityStateMap::iterator state = security_state_.find(child_id);
505 if (state == security_state_.end())
506 return false;
507 return state->second->CanUseCookiesForOrigin(gurl);
508 }
509
510 void ChildProcessSecurityPolicy::LockToOrigin(int child_id, const GURL& gurl) {
511 // "gurl" can be currently empty in some cases, such as file://blah.
512 DCHECK(SiteInstanceImpl::GetSiteForURL(NULL, gurl) == gurl);
513 base::AutoLock lock(lock_);
514 SecurityStateMap::iterator state = security_state_.find(child_id);
515 DCHECK(state != security_state_.end());
516 state->second->LockToOrigin(gurl);
517 }
OLDNEW
« no previous file with comments | « content/browser/child_process_security_policy.h ('k') | content/browser/child_process_security_policy_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698