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 |