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 |