// Copyright 2021 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CONTENT_BROWSER_PROCESS_LOCK_H_ #define CONTENT_BROWSER_PROCESS_LOCK_H_ #include <optional> #include "content/browser/site_info.h" #include "content/browser/url_info.h" #include "content/browser/web_exposed_isolation_info.h" #include "content/public/browser/storage_partition_config.h" #include "content/public/browser/web_exposed_isolation_level.h" #include "url/origin.h" namespace content { class IsolationContext; // ProcessLock is a core part of Site Isolation, which is used to determine // which documents are allowed to load in a process and which site data the // process is allowed to access, based on the SiteInfo principal. // // If a process has a ProcessLock in the "invalid" state, then no SiteInstances // have been associated with the process and access should not be granted to // anything. // // Once a process is associated with its first SiteInstance, it transitions to // the "locked_to_site" or "allow_any_site" state depending on whether the // SiteInstance requires the process to be locked to a specific site or not. // If the SiteInstance does not require the process to be locked to a site, the // process will transition to the "allow_any_site" state and will allow any // site to commit in the process. Such a process can later be upgraded to the // "locked_to_site" state if something later determines that the process should // only allow access to a single site, but only if it hasn't otherwise been used // to render content. Once the process is in the "locked_to_site" state, it will // not be able to access site data from other sites. // // ProcessLock is currently defined in terms of a single SiteInfo with a process // lock URL, but it could be possible to define it in terms of multiple // SiteInfos that are compatible with each other. class CONTENT_EXPORT ProcessLock { public: // Create a lock that that represents a process that is associated with at // least one SiteInstance, but is not locked to a specific site. Any request // that wants to commit in this process must have a StoragePartitionConfig // and web-exposed isolation information (COOP/COEP, for example) that // match the values used to create this lock. static ProcessLock CreateAllowAnySite( const StoragePartitionConfig& storage_partition_config, const WebExposedIsolationInfo& web_exposed_isolation_info); // Create a lock for a specific UrlInfo. This method can be called from both // the UI and IO threads. Locks created with the same parameters must always // be considered equal independent of what thread they are called on. Special // care must be taken since SiteInfos created on different threads don't // always have the same contents for all their fields (e.g. site_url field is // thread dependent). static ProcessLock Create(const IsolationContext& isolation_context, const UrlInfo& url_info); // Returns a ProcessLock representing what the given |site_info| requires. // Note that this may be different from the actual ProcessLock of the // resulting process, in cases where a locked process is not required (e.g., // SiteInfos for http://unisolated.invalid). static ProcessLock FromSiteInfo(const SiteInfo& site_info); ProcessLock(); ProcessLock(const ProcessLock& rhs); ProcessLock& operator=(const ProcessLock& rhs); ~ProcessLock(); // Returns true if no information has been set on the lock. bool is_invalid() const { return !site_info_.has_value(); } // Returns true if the process is locked, but it is not restricted to a // specific site. Any site is allowed to commit in the process as long as // the request's COOP/COEP information matches the info provided when // the lock was created. bool allows_any_site() const { return site_info_.has_value() && site_info_->process_lock_url().is_empty(); } // Returns true if the lock is restricted to a specific site and requires // the request's COOP/COEP information to match the values provided when // the lock was created. bool is_locked_to_site() const { return site_info_.has_value() && !site_info_->process_lock_url().is_empty(); } // Returns the url that corresponds to the SiteInfo the lock is used with. It // will always be the same as the site URL, except in cases where effective // urls are in use. Always empty if the SiteInfo uses the default site url. // TODO(wjmaclean): Delete this accessor once we get to the point where we can // safely just compare ProcessLocks directly. const GURL lock_url() const { return site_info_.has_value() ? site_info_->process_lock_url() : GURL(); } // Returns the site URL of the SiteInfo with which the lock was constructed. // Prefer comparing ProcessLocks directly or using lock_url(), unless you // care about effective URLs. const GURL site_url() const { return site_info_.has_value() ? site_info_->site_url() : GURL(); } // Returns the AgentClusterKey shared by agents allowed in this ProcessLock. std::optional<AgentClusterKey> agent_cluster_key() const { return site_info_.has_value() ? site_info_->agent_cluster_key() : std::nullopt; } // Returns whether this ProcessLock is specific to an origin rather than // including subdomains, such as due to opt-in origin isolation. This resolves // an ambiguity of whether a process with a lock_url() like // "https://foo.example" is allowed to include "https://sub.foo.example" or // not. bool is_origin_keyed_process() const { return site_info_.has_value() && site_info_->requires_origin_keyed_process(); } // True if this ProcessLock is for a sandboxed iframe without // allow-same-origin. // TODO(wjmaclean): This function's return type could mutate to an enum in // future if required for sandboxed iframes that are restricted with different // sandbox flags. bool is_sandboxed() const { return site_info_.has_value() && site_info_->is_sandboxed(); } // If this ProcessLock is for a sandboxed iframe without allow-same-origin, // and per-document grouping has been enabled for kIsolateSandboxedIframes, // then each SiteInfo will have a unique sandbox id encoded as part of the // lock. If per-document grouping is not enabled, this returns // UrlInfo::kInvalidUniqueSandboxId. int unique_sandbox_id() const { return (site_info_.has_value() ? site_info_->unique_sandbox_id() : UrlInfo::kInvalidUniqueSandboxId); } // Returns whether this ProcessLock is specific to PDF contents. bool is_pdf() const { return site_info_.has_value() && site_info_->is_pdf(); } // Returns whether this ProcessLock can only be used for error pages. bool is_error_page() const { return site_info_.has_value() && site_info_->is_error_page(); } // Returns whether this ProcessLock is used for a <webview> guest process. // This may be false for other types of GuestView. bool is_guest() const { return site_info_.has_value() && site_info_->is_guest(); } // Returns whether this ProcessLock is used for a process that exclusively // hosts content inside a <fencedframe>. bool is_fenced() const { return site_info_.has_value() && site_info_->is_fenced(); } // Returns the StoragePartitionConfig that corresponds to the SiteInfo the // lock is used with. StoragePartitionConfig GetStoragePartitionConfig() const; // Returns the cross-origin isolation mode of the BrowsingInstance that all // agents allowed in this ProcessLock belong to. See // https://html.spec.whatwg.org/multipage/document-sequences.html#cross-origin-isolation-mode // This is tracked on ProcessLock because a RenderProcessHost can host only // cross-origin isolated agents or only non-cross-origin isolated agents, not // both. WebExposedIsolationInfo GetWebExposedIsolationInfo() const; // Returns the cross-origin isolated capability of all agents allowed in this // ProcessLock, without taking into account the 'cross-origin-isolated' // permissions policy. This ignores permissions policy because it's currently // possible for agents with the same ProcessLock to have different // 'cross-origin-isolated' permission policies. This can return a lower // isolation level than `GetWebExposedIsolationInfo()` if this ProcessLock // hosts agents that are cross-origin to a top-level document with the // 'isolated application' isolation level. See // https://html.spec.whatwg.org/multipage/webappapis.html#dom-crossoriginisolated WebExposedIsolationLevel GetWebExposedIsolationLevel() const; // Returns whether lock_url() is at least at the granularity of a site (i.e., // a scheme plus eTLD+1, like https://google.com). Also returns true if the // lock is to a more specific origin (e.g., https://accounts.google.com), but // not if the lock is empty or applies to an entire scheme (e.g., file://). bool IsASiteOrOrigin() const; bool matches_scheme(const std::string& scheme) const { return scheme == lock_url().scheme(); } // Returns true if lock_url() has an opaque origin. bool HasOpaqueOrigin() const; // Returns true if |origin| matches the lock's origin. bool MatchesOrigin(const url::Origin& origin) const; // Returns true if the COOP/COEP origin isolation information in this lock // is set and matches the information in |site_info|. // Returns true if the web-exposed isolation level in this lock is set and // matches (or exceeds) the level set in |site_info|.|. bool IsCompatibleWithWebExposedIsolation(const SiteInfo& site_info) const; bool operator==(const ProcessLock& rhs) const; bool operator!=(const ProcessLock& rhs) const; // Defined to allow this object to act as a key for std::map. bool operator<(const ProcessLock& rhs) const; std::string ToString() const; private: explicit ProcessLock(const SiteInfo& site_info); // TODO(creis): Consider tracking multiple compatible SiteInfos in ProcessLock // (e.g., multiple sites when Site Isolation is disabled). This can better // restrict what the process has access to in cases that we currently use an // allows-any-site ProcessLock. std::optional<SiteInfo> site_info_; }; CONTENT_EXPORT std::ostream& operator<<(std::ostream& out, const ProcessLock& process_lock); } // namespace content #endif // CONTENT_BROWSER_PROCESS_LOCK_H_