| 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): https://crbug.com/651534 blob: and filesystem: schemes embed |
| 333 // other origins, so we should not treat them as web safe. Remove callers of |
| 334 // IsWebSafeScheme(), and then eliminate the next two lines. |
| 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. |
| 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_, |
| 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 |