OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/child_process_security_policy_impl.h" | 5 #include "content/browser/child_process_security_policy_impl.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
195 void RevokeReadRawCookies() { | 195 void RevokeReadRawCookies() { |
196 can_read_raw_cookies_ = false; | 196 can_read_raw_cookies_ = false; |
197 } | 197 } |
198 | 198 |
199 void GrantPermissionForMidiSysEx() { | 199 void GrantPermissionForMidiSysEx() { |
200 can_send_midi_sysex_ = true; | 200 can_send_midi_sysex_ = true; |
201 } | 201 } |
202 | 202 |
203 // Determine whether permission has been granted to commit |url|. | 203 // Determine whether permission has been granted to commit |url|. |
204 bool CanCommitURL(const GURL& url) { | 204 bool CanCommitURL(const GURL& url) { |
205 DCHECK(!url.SchemeIsBlob() && !url.SchemeIsFileSystem()) | |
206 << "inner_url extraction should be done already."; | |
205 // Having permission to a scheme implies permission to all of its URLs. | 207 // Having permission to a scheme implies permission to all of its URLs. |
206 SchemeMap::const_iterator scheme_judgment( | 208 SchemeMap::const_iterator scheme_judgment( |
207 scheme_policy_.find(url.scheme())); | 209 scheme_policy_.find(url.scheme())); |
208 if (scheme_judgment != scheme_policy_.end()) | 210 if (scheme_judgment != scheme_policy_.end()) |
209 return scheme_judgment->second; | 211 return scheme_judgment->second; |
210 | 212 |
211 // Otherwise, check for permission for specific origin. | 213 // Otherwise, check for permission for specific origin. |
212 if (base::ContainsKey(origin_set_, url::Origin(url))) | 214 if (base::ContainsKey(origin_set_, url::Origin(url))) |
213 return true; | 215 return true; |
214 | 216 |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
319 DISALLOW_COPY_AND_ASSIGN(SecurityState); | 321 DISALLOW_COPY_AND_ASSIGN(SecurityState); |
320 }; | 322 }; |
321 | 323 |
322 ChildProcessSecurityPolicyImpl::ChildProcessSecurityPolicyImpl() { | 324 ChildProcessSecurityPolicyImpl::ChildProcessSecurityPolicyImpl() { |
323 // We know about these schemes and believe them to be safe. | 325 // We know about these schemes and believe them to be safe. |
324 RegisterWebSafeScheme(url::kHttpScheme); | 326 RegisterWebSafeScheme(url::kHttpScheme); |
325 RegisterWebSafeScheme(url::kHttpsScheme); | 327 RegisterWebSafeScheme(url::kHttpsScheme); |
326 RegisterWebSafeScheme(url::kFtpScheme); | 328 RegisterWebSafeScheme(url::kFtpScheme); |
327 RegisterWebSafeScheme(url::kDataScheme); | 329 RegisterWebSafeScheme(url::kDataScheme); |
328 RegisterWebSafeScheme("feed"); | 330 RegisterWebSafeScheme("feed"); |
331 | |
332 // TODO(nick): blob: and filesystem: schemes embed other origins, | |
333 // so we should not treat them as web safe. Remove callers of | |
334 // IsWebSafeScheme(), and then eliminate the next two lines. | |
Charlie Reis
2016/09/29 21:39:37
Let's list https://crbug.com/651534 here, too.
ncarter (slow)
2016/09/29 22:04:02
Done.
| |
329 RegisterWebSafeScheme(url::kBlobScheme); | 335 RegisterWebSafeScheme(url::kBlobScheme); |
330 RegisterWebSafeScheme(url::kFileSystemScheme); | 336 RegisterWebSafeScheme(url::kFileSystemScheme); |
331 | 337 |
332 // We know about the following pseudo schemes and treat them specially. | 338 // We know about the following pseudo schemes and treat them specially. |
333 RegisterPseudoScheme(url::kAboutScheme); | 339 RegisterPseudoScheme(url::kAboutScheme); |
334 RegisterPseudoScheme(url::kJavaScriptScheme); | 340 RegisterPseudoScheme(url::kJavaScriptScheme); |
335 RegisterPseudoScheme(kViewSourceScheme); | 341 RegisterPseudoScheme(kViewSourceScheme); |
336 RegisterPseudoScheme(kHttpSuboriginScheme); | 342 RegisterPseudoScheme(kHttpSuboriginScheme); |
337 RegisterPseudoScheme(kHttpsSuboriginScheme); | 343 RegisterPseudoScheme(kHttpsSuboriginScheme); |
338 } | 344 } |
339 | 345 |
340 ChildProcessSecurityPolicyImpl::~ChildProcessSecurityPolicyImpl() { | 346 ChildProcessSecurityPolicyImpl::~ChildProcessSecurityPolicyImpl() { |
341 web_safe_schemes_.clear(); | |
342 pseudo_schemes_.clear(); | |
343 security_state_.clear(); | |
344 } | 347 } |
345 | 348 |
346 // static | 349 // static |
347 ChildProcessSecurityPolicy* ChildProcessSecurityPolicy::GetInstance() { | 350 ChildProcessSecurityPolicy* ChildProcessSecurityPolicy::GetInstance() { |
348 return ChildProcessSecurityPolicyImpl::GetInstance(); | 351 return ChildProcessSecurityPolicyImpl::GetInstance(); |
349 } | 352 } |
350 | 353 |
351 ChildProcessSecurityPolicyImpl* ChildProcessSecurityPolicyImpl::GetInstance() { | 354 ChildProcessSecurityPolicyImpl* ChildProcessSecurityPolicyImpl::GetInstance() { |
352 return base::Singleton<ChildProcessSecurityPolicyImpl>::get(); | 355 return base::Singleton<ChildProcessSecurityPolicyImpl>::get(); |
353 } | 356 } |
(...skipping 12 matching lines...) Expand all Loading... | |
366 | 369 |
367 void ChildProcessSecurityPolicyImpl::Remove(int child_id) { | 370 void ChildProcessSecurityPolicyImpl::Remove(int child_id) { |
368 base::AutoLock lock(lock_); | 371 base::AutoLock lock(lock_); |
369 security_state_.erase(child_id); | 372 security_state_.erase(child_id); |
370 worker_map_.erase(child_id); | 373 worker_map_.erase(child_id); |
371 } | 374 } |
372 | 375 |
373 void ChildProcessSecurityPolicyImpl::RegisterWebSafeScheme( | 376 void ChildProcessSecurityPolicyImpl::RegisterWebSafeScheme( |
374 const std::string& scheme) { | 377 const std::string& scheme) { |
375 base::AutoLock lock(lock_); | 378 base::AutoLock lock(lock_); |
376 DCHECK_EQ(0U, web_safe_schemes_.count(scheme)) << "Add schemes at most once."; | 379 DCHECK_EQ(0U, schemes_okay_to_request_in_any_process_.count(scheme)) |
380 << "Add schemes at most once."; | |
377 DCHECK_EQ(0U, pseudo_schemes_.count(scheme)) | 381 DCHECK_EQ(0U, pseudo_schemes_.count(scheme)) |
378 << "Web-safe implies not pseudo."; | 382 << "Web-safe implies not pseudo."; |
379 | 383 |
380 web_safe_schemes_.insert(scheme); | 384 schemes_okay_to_request_in_any_process_.insert(scheme); |
385 schemes_okay_to_commit_in_any_process_.insert(scheme); | |
386 } | |
387 | |
388 void ChildProcessSecurityPolicyImpl::RegisterWebSafeIsolatedScheme( | |
389 const std::string& scheme, | |
390 bool always_allow_in_origin_headers) { | |
391 base::AutoLock lock(lock_); | |
392 DCHECK_EQ(0U, schemes_okay_to_request_in_any_process_.count(scheme)) | |
393 << "Add schemes at most once."; | |
394 DCHECK_EQ(0U, pseudo_schemes_.count(scheme)) | |
395 << "Web-safe implies not pseudo."; | |
396 | |
397 schemes_okay_to_request_in_any_process_.insert(scheme); | |
398 if (always_allow_in_origin_headers) | |
399 schemes_okay_to_appear_as_origin_headers_.insert(scheme); | |
381 } | 400 } |
382 | 401 |
383 bool ChildProcessSecurityPolicyImpl::IsWebSafeScheme( | 402 bool ChildProcessSecurityPolicyImpl::IsWebSafeScheme( |
384 const std::string& scheme) { | 403 const std::string& scheme) { |
385 base::AutoLock lock(lock_); | 404 base::AutoLock lock(lock_); |
386 | 405 |
387 return base::ContainsKey(web_safe_schemes_, scheme); | 406 return base::ContainsKey(schemes_okay_to_request_in_any_process_, scheme); |
388 } | 407 } |
389 | 408 |
390 void ChildProcessSecurityPolicyImpl::RegisterPseudoScheme( | 409 void ChildProcessSecurityPolicyImpl::RegisterPseudoScheme( |
391 const std::string& scheme) { | 410 const std::string& scheme) { |
392 base::AutoLock lock(lock_); | 411 base::AutoLock lock(lock_); |
393 DCHECK_EQ(0U, pseudo_schemes_.count(scheme)) << "Add schemes at most once."; | 412 DCHECK_EQ(0U, pseudo_schemes_.count(scheme)) << "Add schemes at most once."; |
394 DCHECK_EQ(0U, web_safe_schemes_.count(scheme)) | 413 DCHECK_EQ(0U, schemes_okay_to_request_in_any_process_.count(scheme)) |
414 << "Pseudo implies not web-safe."; | |
415 DCHECK_EQ(0U, schemes_okay_to_commit_in_any_process_.count(scheme)) | |
395 << "Pseudo implies not web-safe."; | 416 << "Pseudo implies not web-safe."; |
396 | 417 |
397 pseudo_schemes_.insert(scheme); | 418 pseudo_schemes_.insert(scheme); |
398 } | 419 } |
399 | 420 |
400 bool ChildProcessSecurityPolicyImpl::IsPseudoScheme( | 421 bool ChildProcessSecurityPolicyImpl::IsPseudoScheme( |
401 const std::string& scheme) { | 422 const std::string& scheme) { |
402 base::AutoLock lock(lock_); | 423 base::AutoLock lock(lock_); |
403 | 424 |
404 return base::ContainsKey(pseudo_schemes_, scheme); | 425 return base::ContainsKey(pseudo_schemes_, scheme); |
405 } | 426 } |
406 | 427 |
407 void ChildProcessSecurityPolicyImpl::GrantRequestURL( | 428 void ChildProcessSecurityPolicyImpl::GrantRequestURL( |
408 int child_id, const GURL& url) { | 429 int child_id, const GURL& url) { |
409 | 430 |
410 if (!url.is_valid()) | 431 if (!url.is_valid()) |
411 return; // Can't grant the capability to request invalid URLs. | 432 return; // Can't grant the capability to request invalid URLs. |
412 | 433 |
413 if (IsWebSafeScheme(url.scheme())) | 434 if (IsWebSafeScheme(url.scheme())) |
414 return; // The scheme has already been whitelisted for every child process. | 435 return; // The scheme has already been whitelisted for every child process. |
415 | 436 |
416 if (IsPseudoScheme(url.scheme())) { | 437 if (IsPseudoScheme(url.scheme())) { |
417 return; // Can't grant the capability to request pseudo schemes. | 438 return; // Can't grant the capability to request pseudo schemes. |
418 } | 439 } |
419 | 440 |
441 if (url.SchemeIsBlob() || url.SchemeIsFileSystem()) { | |
442 return; // Don't grant blanket access to blob: or filesystem: schemes. | |
443 } | |
444 | |
420 { | 445 { |
421 base::AutoLock lock(lock_); | 446 base::AutoLock lock(lock_); |
422 SecurityStateMap::iterator state = security_state_.find(child_id); | 447 SecurityStateMap::iterator state = security_state_.find(child_id); |
423 if (state == security_state_.end()) | 448 if (state == security_state_.end()) |
424 return; | 449 return; |
425 | 450 |
426 // When the child process has been commanded to request this scheme, | 451 // When the child process has been commanded to request this scheme, |
427 // we grant it the capability to request all URLs of that scheme. | 452 // we grant it the capability to request all URLs of that scheme. |
428 state->second->GrantScheme(url.scheme()); | 453 state->second->GrantScheme(url.scheme()); |
429 } | 454 } |
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
599 // Every child process can request <about:blank>. | 624 // Every child process can request <about:blank>. |
600 if (base::LowerCaseEqualsASCII(url.spec(), url::kAboutBlankURL)) | 625 if (base::LowerCaseEqualsASCII(url.spec(), url::kAboutBlankURL)) |
601 return true; | 626 return true; |
602 // URLs like <about:version>, <about:crash>, <view-source:...> shouldn't be | 627 // URLs like <about:version>, <about:crash>, <view-source:...> shouldn't be |
603 // requestable by any child process. Also, this case covers | 628 // requestable by any child process. Also, this case covers |
604 // <javascript:...>, which should be handled internally by the process and | 629 // <javascript:...>, which should be handled internally by the process and |
605 // not kicked up to the browser. | 630 // not kicked up to the browser. |
606 return false; | 631 return false; |
607 } | 632 } |
608 | 633 |
609 if (IsMalformedBlobUrl(url)) | 634 // Blob and filesystem URLs require special treatment, since they embed an |
610 return false; | 635 // inner origin. |
636 if (url.SchemeIsBlob() || url.SchemeIsFileSystem()) { | |
637 if (IsMalformedBlobUrl(url)) | |
638 return false; | |
639 | |
640 url::Origin origin(url); | |
641 return origin.unique() || IsWebSafeScheme(origin.scheme()) || | |
642 CanCommitURL(child_id, GURL(origin.Serialize())); | |
643 } | |
644 | |
645 if (IsWebSafeScheme(url.scheme())) | |
646 return true; | |
611 | 647 |
612 // If the process can commit the URL, it can request it. | 648 // If the process can commit the URL, it can request it. |
613 if (CanCommitURL(child_id, url)) | 649 if (CanCommitURL(child_id, url)) |
614 return true; | 650 return true; |
615 | 651 |
616 // Also allow URLs destined for ShellExecute and not the browser itself. | 652 // Also allow URLs destined for ShellExecute and not the browser itself. |
617 return !GetContentClient()->browser()->IsHandledURL(url) && | 653 return !GetContentClient()->browser()->IsHandledURL(url) && |
618 !net::URLRequest::IsHandledURL(url); | 654 !net::URLRequest::IsHandledURL(url); |
619 } | 655 } |
620 | 656 |
621 bool ChildProcessSecurityPolicyImpl::CanCommitURL(int child_id, | 657 bool ChildProcessSecurityPolicyImpl::CanCommitURL(int child_id, |
622 const GURL& url) { | 658 const GURL& url) { |
623 if (!url.is_valid()) | 659 if (!url.is_valid()) |
624 return false; // Can't commit invalid URLs. | 660 return false; // Can't commit invalid URLs. |
625 | 661 |
626 // Of all the pseudo schemes, only about:blank is allowed to commit. | 662 // Of all the pseudo schemes, only about:blank is allowed to commit. |
627 if (IsPseudoScheme(url.scheme())) | 663 if (IsPseudoScheme(url.scheme())) |
628 return base::LowerCaseEqualsASCII(url.spec(), url::kAboutBlankURL); | 664 return base::LowerCaseEqualsASCII(url.spec(), url::kAboutBlankURL); |
629 | 665 |
630 if (IsMalformedBlobUrl(url)) | 666 // Blob and filesystem URLs require special treatment; validate the inner |
631 return false; | 667 // origin they embed. |
668 if (url.SchemeIsBlob() || url.SchemeIsFileSystem()) { | |
669 if (IsMalformedBlobUrl(url)) | |
670 return false; | |
632 | 671 |
633 // TODO(creis): Tighten this for Site Isolation, so that a URL from a site | 672 url::Origin origin(url); |
634 // that is isolated can only be committed in a process dedicated to that site. | 673 return origin.unique() || CanCommitURL(child_id, GURL(origin.Serialize())); |
635 // CanRequestURL should still allow all web-safe schemes. See | 674 } |
636 // https://crbug.com/515309. | |
637 if (IsWebSafeScheme(url.scheme())) | |
638 return true; // The scheme has been white-listed for every child process. | |
639 | 675 |
640 { | 676 { |
641 base::AutoLock lock(lock_); | 677 base::AutoLock lock(lock_); |
642 | 678 |
679 // Most schemes can commit in any process. Note that we check | |
680 // schemes_okay_to_commit_in_any_process_ here, which is stricter than | |
681 // IsWebSafeScheme(). | |
682 // | |
683 // TODO(creis, nick): https://crbug.com/515309: in generalized Site | |
684 // Isolation and/or --site-per-process, there will be no such thing as a | |
685 // scheme that is okay to commit in any process. Instead, an URL from a site | |
686 // that is isolated may only be committed in a process dedicated to that | |
687 // site, so CanCommitURL will need to rely on explicit, per-process grants. | |
688 // Note how today, even with extension isolation, the line below does not | |
689 // enforce that http pages cannot commit in an extension process. | |
Charlie Reis
2016/09/29 21:39:37
Good point.
ncarter (slow)
2016/09/29 22:04:02
Done.
| |
690 if (base::ContainsKey(schemes_okay_to_commit_in_any_process_, url.scheme())) | |
691 return true; | |
692 | |
643 SecurityStateMap::iterator state = security_state_.find(child_id); | 693 SecurityStateMap::iterator state = security_state_.find(child_id); |
644 if (state == security_state_.end()) | 694 if (state == security_state_.end()) |
645 return false; | 695 return false; |
646 | 696 |
647 // Otherwise, we consult the child process's security state to see if it is | 697 // Otherwise, we consult the child process's security state to see if it is |
648 // allowed to commit the URL. | 698 // allowed to commit the URL. |
649 return state->second->CanCommitURL(url); | 699 return state->second->CanCommitURL(url); |
650 } | 700 } |
651 } | 701 } |
652 | 702 |
653 bool ChildProcessSecurityPolicyImpl::CanSetAsOriginHeader(int child_id, | 703 bool ChildProcessSecurityPolicyImpl::CanSetAsOriginHeader(int child_id, |
654 const GURL& url) { | 704 const GURL& url) { |
655 if (!url.is_valid()) | 705 if (!url.is_valid()) |
656 return false; // Can't set invalid URLs as origin headers. | 706 return false; // Can't set invalid URLs as origin headers. |
657 | 707 |
658 // Suborigin URLs are a special case and are allowed to be an origin header. | 708 // Suborigin URLs are a special case and are allowed to be an origin header. |
659 if (url.scheme() == kHttpSuboriginScheme || | 709 if (url.scheme() == kHttpSuboriginScheme || |
660 url.scheme() == kHttpsSuboriginScheme) { | 710 url.scheme() == kHttpsSuboriginScheme) { |
661 DCHECK(IsPseudoScheme(url.scheme())); | 711 DCHECK(IsPseudoScheme(url.scheme())); |
662 return true; | 712 return true; |
663 } | 713 } |
664 | 714 |
665 return CanCommitURL(child_id, url); | 715 // If this process can commit |url|, it can use |url| as an origin for |
716 // outbound requests. | |
717 if (CanCommitURL(child_id, url)) | |
718 return true; | |
719 | |
720 // Allow schemes which may come from scripts executing in isolated worlds; | |
721 // XHRs issued by such scripts reflect the script origin rather than the | |
722 // document origin. | |
723 { | |
724 base::AutoLock lock(lock_); | |
725 if (base::ContainsKey(schemes_okay_to_appear_as_origin_headers_, | |
Charlie Reis
2016/09/29 21:39:37
Now I see why Joel's CanSetAsOriginHeader refactor
ncarter (slow)
2016/09/29 22:04:02
Yeah, well-timed ... except that it'll complicate
| |
726 url.scheme())) | |
727 return true; | |
728 } | |
729 return false; | |
666 } | 730 } |
667 | 731 |
668 bool ChildProcessSecurityPolicyImpl::CanReadFile(int child_id, | 732 bool ChildProcessSecurityPolicyImpl::CanReadFile(int child_id, |
669 const base::FilePath& file) { | 733 const base::FilePath& file) { |
670 return HasPermissionsForFile(child_id, file, READ_FILE_GRANT); | 734 return HasPermissionsForFile(child_id, file, READ_FILE_GRANT); |
671 } | 735 } |
672 | 736 |
673 bool ChildProcessSecurityPolicyImpl::CanReadAllFiles( | 737 bool ChildProcessSecurityPolicyImpl::CanReadAllFiles( |
674 int child_id, | 738 int child_id, |
675 const std::vector<base::FilePath>& files) { | 739 const std::vector<base::FilePath>& files) { |
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
899 base::AutoLock lock(lock_); | 963 base::AutoLock lock(lock_); |
900 | 964 |
901 SecurityStateMap::iterator state = security_state_.find(child_id); | 965 SecurityStateMap::iterator state = security_state_.find(child_id); |
902 if (state == security_state_.end()) | 966 if (state == security_state_.end()) |
903 return false; | 967 return false; |
904 | 968 |
905 return state->second->can_send_midi_sysex(); | 969 return state->second->can_send_midi_sysex(); |
906 } | 970 } |
907 | 971 |
908 } // namespace content | 972 } // namespace content |
OLD | NEW |