| 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."; | |
| 207 // Having permission to a scheme implies permission to all of its URLs. | 205 // Having permission to a scheme implies permission to all of its URLs. |
| 208 SchemeMap::const_iterator scheme_judgment( | 206 SchemeMap::const_iterator scheme_judgment( |
| 209 scheme_policy_.find(url.scheme())); | 207 scheme_policy_.find(url.scheme())); |
| 210 if (scheme_judgment != scheme_policy_.end()) | 208 if (scheme_judgment != scheme_policy_.end()) |
| 211 return scheme_judgment->second; | 209 return scheme_judgment->second; |
| 212 | 210 |
| 213 // Otherwise, check for permission for specific origin. | 211 // Otherwise, check for permission for specific origin. |
| 214 if (base::ContainsKey(origin_set_, url::Origin(url))) | 212 if (base::ContainsKey(origin_set_, url::Origin(url))) |
| 215 return true; | 213 return true; |
| 216 | 214 |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 321 DISALLOW_COPY_AND_ASSIGN(SecurityState); | 319 DISALLOW_COPY_AND_ASSIGN(SecurityState); |
| 322 }; | 320 }; |
| 323 | 321 |
| 324 ChildProcessSecurityPolicyImpl::ChildProcessSecurityPolicyImpl() { | 322 ChildProcessSecurityPolicyImpl::ChildProcessSecurityPolicyImpl() { |
| 325 // We know about these schemes and believe them to be safe. | 323 // We know about these schemes and believe them to be safe. |
| 326 RegisterWebSafeScheme(url::kHttpScheme); | 324 RegisterWebSafeScheme(url::kHttpScheme); |
| 327 RegisterWebSafeScheme(url::kHttpsScheme); | 325 RegisterWebSafeScheme(url::kHttpsScheme); |
| 328 RegisterWebSafeScheme(url::kFtpScheme); | 326 RegisterWebSafeScheme(url::kFtpScheme); |
| 329 RegisterWebSafeScheme(url::kDataScheme); | 327 RegisterWebSafeScheme(url::kDataScheme); |
| 330 RegisterWebSafeScheme("feed"); | 328 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. | |
| 335 RegisterWebSafeScheme(url::kBlobScheme); | 329 RegisterWebSafeScheme(url::kBlobScheme); |
| 336 RegisterWebSafeScheme(url::kFileSystemScheme); | 330 RegisterWebSafeScheme(url::kFileSystemScheme); |
| 337 | 331 |
| 338 // We know about the following pseudo schemes and treat them specially. | 332 // We know about the following pseudo schemes and treat them specially. |
| 339 RegisterPseudoScheme(url::kAboutScheme); | 333 RegisterPseudoScheme(url::kAboutScheme); |
| 340 RegisterPseudoScheme(url::kJavaScriptScheme); | 334 RegisterPseudoScheme(url::kJavaScriptScheme); |
| 341 RegisterPseudoScheme(kViewSourceScheme); | 335 RegisterPseudoScheme(kViewSourceScheme); |
| 342 RegisterPseudoScheme(kHttpSuboriginScheme); | 336 RegisterPseudoScheme(kHttpSuboriginScheme); |
| 343 RegisterPseudoScheme(kHttpsSuboriginScheme); | 337 RegisterPseudoScheme(kHttpsSuboriginScheme); |
| 344 } | 338 } |
| 345 | 339 |
| 346 ChildProcessSecurityPolicyImpl::~ChildProcessSecurityPolicyImpl() { | 340 ChildProcessSecurityPolicyImpl::~ChildProcessSecurityPolicyImpl() { |
| 341 web_safe_schemes_.clear(); |
| 342 pseudo_schemes_.clear(); |
| 343 security_state_.clear(); |
| 347 } | 344 } |
| 348 | 345 |
| 349 // static | 346 // static |
| 350 ChildProcessSecurityPolicy* ChildProcessSecurityPolicy::GetInstance() { | 347 ChildProcessSecurityPolicy* ChildProcessSecurityPolicy::GetInstance() { |
| 351 return ChildProcessSecurityPolicyImpl::GetInstance(); | 348 return ChildProcessSecurityPolicyImpl::GetInstance(); |
| 352 } | 349 } |
| 353 | 350 |
| 354 ChildProcessSecurityPolicyImpl* ChildProcessSecurityPolicyImpl::GetInstance() { | 351 ChildProcessSecurityPolicyImpl* ChildProcessSecurityPolicyImpl::GetInstance() { |
| 355 return base::Singleton<ChildProcessSecurityPolicyImpl>::get(); | 352 return base::Singleton<ChildProcessSecurityPolicyImpl>::get(); |
| 356 } | 353 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 369 | 366 |
| 370 void ChildProcessSecurityPolicyImpl::Remove(int child_id) { | 367 void ChildProcessSecurityPolicyImpl::Remove(int child_id) { |
| 371 base::AutoLock lock(lock_); | 368 base::AutoLock lock(lock_); |
| 372 security_state_.erase(child_id); | 369 security_state_.erase(child_id); |
| 373 worker_map_.erase(child_id); | 370 worker_map_.erase(child_id); |
| 374 } | 371 } |
| 375 | 372 |
| 376 void ChildProcessSecurityPolicyImpl::RegisterWebSafeScheme( | 373 void ChildProcessSecurityPolicyImpl::RegisterWebSafeScheme( |
| 377 const std::string& scheme) { | 374 const std::string& scheme) { |
| 378 base::AutoLock lock(lock_); | 375 base::AutoLock lock(lock_); |
| 379 DCHECK_EQ(0U, schemes_okay_to_request_in_any_process_.count(scheme)) | 376 DCHECK_EQ(0U, web_safe_schemes_.count(scheme)) << "Add schemes at most once."; |
| 380 << "Add schemes at most once."; | |
| 381 DCHECK_EQ(0U, pseudo_schemes_.count(scheme)) | 377 DCHECK_EQ(0U, pseudo_schemes_.count(scheme)) |
| 382 << "Web-safe implies not pseudo."; | 378 << "Web-safe implies not pseudo."; |
| 383 | 379 |
| 384 schemes_okay_to_request_in_any_process_.insert(scheme); | 380 web_safe_schemes_.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); | |
| 400 } | 381 } |
| 401 | 382 |
| 402 bool ChildProcessSecurityPolicyImpl::IsWebSafeScheme( | 383 bool ChildProcessSecurityPolicyImpl::IsWebSafeScheme( |
| 403 const std::string& scheme) { | 384 const std::string& scheme) { |
| 404 base::AutoLock lock(lock_); | 385 base::AutoLock lock(lock_); |
| 405 | 386 |
| 406 return base::ContainsKey(schemes_okay_to_request_in_any_process_, scheme); | 387 return base::ContainsKey(web_safe_schemes_, scheme); |
| 407 } | 388 } |
| 408 | 389 |
| 409 void ChildProcessSecurityPolicyImpl::RegisterPseudoScheme( | 390 void ChildProcessSecurityPolicyImpl::RegisterPseudoScheme( |
| 410 const std::string& scheme) { | 391 const std::string& scheme) { |
| 411 base::AutoLock lock(lock_); | 392 base::AutoLock lock(lock_); |
| 412 DCHECK_EQ(0U, pseudo_schemes_.count(scheme)) << "Add schemes at most once."; | 393 DCHECK_EQ(0U, pseudo_schemes_.count(scheme)) << "Add schemes at most once."; |
| 413 DCHECK_EQ(0U, schemes_okay_to_request_in_any_process_.count(scheme)) | 394 DCHECK_EQ(0U, web_safe_schemes_.count(scheme)) |
| 414 << "Pseudo implies not web-safe."; | |
| 415 DCHECK_EQ(0U, schemes_okay_to_commit_in_any_process_.count(scheme)) | |
| 416 << "Pseudo implies not web-safe."; | 395 << "Pseudo implies not web-safe."; |
| 417 | 396 |
| 418 pseudo_schemes_.insert(scheme); | 397 pseudo_schemes_.insert(scheme); |
| 419 } | 398 } |
| 420 | 399 |
| 421 bool ChildProcessSecurityPolicyImpl::IsPseudoScheme( | 400 bool ChildProcessSecurityPolicyImpl::IsPseudoScheme( |
| 422 const std::string& scheme) { | 401 const std::string& scheme) { |
| 423 base::AutoLock lock(lock_); | 402 base::AutoLock lock(lock_); |
| 424 | 403 |
| 425 return base::ContainsKey(pseudo_schemes_, scheme); | 404 return base::ContainsKey(pseudo_schemes_, scheme); |
| 426 } | 405 } |
| 427 | 406 |
| 428 void ChildProcessSecurityPolicyImpl::GrantRequestURL( | 407 void ChildProcessSecurityPolicyImpl::GrantRequestURL( |
| 429 int child_id, const GURL& url) { | 408 int child_id, const GURL& url) { |
| 430 | 409 |
| 431 if (!url.is_valid()) | 410 if (!url.is_valid()) |
| 432 return; // Can't grant the capability to request invalid URLs. | 411 return; // Can't grant the capability to request invalid URLs. |
| 433 | 412 |
| 434 if (IsWebSafeScheme(url.scheme())) | 413 if (IsWebSafeScheme(url.scheme())) |
| 435 return; // The scheme has already been whitelisted for every child process. | 414 return; // The scheme has already been whitelisted for every child process. |
| 436 | 415 |
| 437 if (IsPseudoScheme(url.scheme())) { | 416 if (IsPseudoScheme(url.scheme())) { |
| 438 return; // Can't grant the capability to request pseudo schemes. | 417 return; // Can't grant the capability to request pseudo schemes. |
| 439 } | 418 } |
| 440 | 419 |
| 441 if (url.SchemeIsBlob() || url.SchemeIsFileSystem()) { | |
| 442 return; // Don't grant blanket access to blob: or filesystem: schemes. | |
| 443 } | |
| 444 | |
| 445 { | 420 { |
| 446 base::AutoLock lock(lock_); | 421 base::AutoLock lock(lock_); |
| 447 SecurityStateMap::iterator state = security_state_.find(child_id); | 422 SecurityStateMap::iterator state = security_state_.find(child_id); |
| 448 if (state == security_state_.end()) | 423 if (state == security_state_.end()) |
| 449 return; | 424 return; |
| 450 | 425 |
| 451 // When the child process has been commanded to request this scheme, | 426 // When the child process has been commanded to request this scheme, |
| 452 // we grant it the capability to request all URLs of that scheme. | 427 // we grant it the capability to request all URLs of that scheme. |
| 453 state->second->GrantScheme(url.scheme()); | 428 state->second->GrantScheme(url.scheme()); |
| 454 } | 429 } |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 624 // Every child process can request <about:blank>. | 599 // Every child process can request <about:blank>. |
| 625 if (base::LowerCaseEqualsASCII(url.spec(), url::kAboutBlankURL)) | 600 if (base::LowerCaseEqualsASCII(url.spec(), url::kAboutBlankURL)) |
| 626 return true; | 601 return true; |
| 627 // URLs like <about:version>, <about:crash>, <view-source:...> shouldn't be | 602 // URLs like <about:version>, <about:crash>, <view-source:...> shouldn't be |
| 628 // requestable by any child process. Also, this case covers | 603 // requestable by any child process. Also, this case covers |
| 629 // <javascript:...>, which should be handled internally by the process and | 604 // <javascript:...>, which should be handled internally by the process and |
| 630 // not kicked up to the browser. | 605 // not kicked up to the browser. |
| 631 return false; | 606 return false; |
| 632 } | 607 } |
| 633 | 608 |
| 634 // Blob and filesystem URLs require special treatment, since they embed an | 609 if (IsMalformedBlobUrl(url)) |
| 635 // inner origin. | 610 return false; |
| 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; | |
| 647 | 611 |
| 648 // If the process can commit the URL, it can request it. | 612 // If the process can commit the URL, it can request it. |
| 649 if (CanCommitURL(child_id, url)) | 613 if (CanCommitURL(child_id, url)) |
| 650 return true; | 614 return true; |
| 651 | 615 |
| 652 // Also allow URLs destined for ShellExecute and not the browser itself. | 616 // Also allow URLs destined for ShellExecute and not the browser itself. |
| 653 return !GetContentClient()->browser()->IsHandledURL(url) && | 617 return !GetContentClient()->browser()->IsHandledURL(url) && |
| 654 !net::URLRequest::IsHandledURL(url); | 618 !net::URLRequest::IsHandledURL(url); |
| 655 } | 619 } |
| 656 | 620 |
| 657 bool ChildProcessSecurityPolicyImpl::CanCommitURL(int child_id, | 621 bool ChildProcessSecurityPolicyImpl::CanCommitURL(int child_id, |
| 658 const GURL& url) { | 622 const GURL& url) { |
| 659 if (!url.is_valid()) | 623 if (!url.is_valid()) |
| 660 return false; // Can't commit invalid URLs. | 624 return false; // Can't commit invalid URLs. |
| 661 | 625 |
| 662 // Of all the pseudo schemes, only about:blank is allowed to commit. | 626 // Of all the pseudo schemes, only about:blank is allowed to commit. |
| 663 if (IsPseudoScheme(url.scheme())) | 627 if (IsPseudoScheme(url.scheme())) |
| 664 return base::LowerCaseEqualsASCII(url.spec(), url::kAboutBlankURL); | 628 return base::LowerCaseEqualsASCII(url.spec(), url::kAboutBlankURL); |
| 665 | 629 |
| 666 // Blob and filesystem URLs require special treatment; validate the inner | 630 if (IsMalformedBlobUrl(url)) |
| 667 // origin they embed. | 631 return false; |
| 668 if (url.SchemeIsBlob() || url.SchemeIsFileSystem()) { | |
| 669 if (IsMalformedBlobUrl(url)) | |
| 670 return false; | |
| 671 | 632 |
| 672 url::Origin origin(url); | 633 // TODO(creis): Tighten this for Site Isolation, so that a URL from a site |
| 673 return origin.unique() || CanCommitURL(child_id, GURL(origin.Serialize())); | 634 // that is isolated can only be committed in a process dedicated to that site. |
| 674 } | 635 // CanRequestURL should still allow all web-safe schemes. See |
| 636 // https://crbug.com/515309. |
| 637 if (IsWebSafeScheme(url.scheme())) |
| 638 return true; // The scheme has been white-listed for every child process. |
| 675 | 639 |
| 676 { | 640 { |
| 677 base::AutoLock lock(lock_); | 641 base::AutoLock lock(lock_); |
| 678 | 642 |
| 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 | |
| 693 SecurityStateMap::iterator state = security_state_.find(child_id); | 643 SecurityStateMap::iterator state = security_state_.find(child_id); |
| 694 if (state == security_state_.end()) | 644 if (state == security_state_.end()) |
| 695 return false; | 645 return false; |
| 696 | 646 |
| 697 // Otherwise, we consult the child process's security state to see if it is | 647 // Otherwise, we consult the child process's security state to see if it is |
| 698 // allowed to commit the URL. | 648 // allowed to commit the URL. |
| 699 return state->second->CanCommitURL(url); | 649 return state->second->CanCommitURL(url); |
| 700 } | 650 } |
| 701 } | 651 } |
| 702 | 652 |
| 703 bool ChildProcessSecurityPolicyImpl::CanSetAsOriginHeader(int child_id, | 653 bool ChildProcessSecurityPolicyImpl::CanSetAsOriginHeader(int child_id, |
| 704 const GURL& url) { | 654 const GURL& url) { |
| 705 if (!url.is_valid()) | 655 if (!url.is_valid()) |
| 706 return false; // Can't set invalid URLs as origin headers. | 656 return false; // Can't set invalid URLs as origin headers. |
| 707 | 657 |
| 708 // Suborigin URLs are a special case and are allowed to be an origin header. | 658 // Suborigin URLs are a special case and are allowed to be an origin header. |
| 709 if (url.scheme() == kHttpSuboriginScheme || | 659 if (url.scheme() == kHttpSuboriginScheme || |
| 710 url.scheme() == kHttpsSuboriginScheme) { | 660 url.scheme() == kHttpsSuboriginScheme) { |
| 711 DCHECK(IsPseudoScheme(url.scheme())); | 661 DCHECK(IsPseudoScheme(url.scheme())); |
| 712 return true; | 662 return true; |
| 713 } | 663 } |
| 714 | 664 |
| 715 // If this process can commit |url|, it can use |url| as an origin for | 665 return CanCommitURL(child_id, url); |
| 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; | |
| 730 } | 666 } |
| 731 | 667 |
| 732 bool ChildProcessSecurityPolicyImpl::CanReadFile(int child_id, | 668 bool ChildProcessSecurityPolicyImpl::CanReadFile(int child_id, |
| 733 const base::FilePath& file) { | 669 const base::FilePath& file) { |
| 734 return HasPermissionsForFile(child_id, file, READ_FILE_GRANT); | 670 return HasPermissionsForFile(child_id, file, READ_FILE_GRANT); |
| 735 } | 671 } |
| 736 | 672 |
| 737 bool ChildProcessSecurityPolicyImpl::CanReadAllFiles( | 673 bool ChildProcessSecurityPolicyImpl::CanReadAllFiles( |
| 738 int child_id, | 674 int child_id, |
| 739 const std::vector<base::FilePath>& files) { | 675 const std::vector<base::FilePath>& files) { |
| (...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 963 base::AutoLock lock(lock_); | 899 base::AutoLock lock(lock_); |
| 964 | 900 |
| 965 SecurityStateMap::iterator state = security_state_.find(child_id); | 901 SecurityStateMap::iterator state = security_state_.find(child_id); |
| 966 if (state == security_state_.end()) | 902 if (state == security_state_.end()) |
| 967 return false; | 903 return false; |
| 968 | 904 |
| 969 return state->second->can_send_midi_sysex(); | 905 return state->second->can_send_midi_sysex(); |
| 970 } | 906 } |
| 971 | 907 |
| 972 } // namespace content | 908 } // namespace content |
| OLD | NEW |