1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231

content / browser / process_lock.h [blame]

// 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_