summaryrefslogtreecommitdiffstats
path: root/chromium/content/browser/isolated_origin_util.cc
blob: 570491adfbc47c38d7217868914a585f820231bc (plain)
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
// Copyright (c) 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string>

#include "content/browser/isolated_origin_util.h"

#include "base/logging.h"
#include "base/strings/string_util.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "url/gurl.h"

const char* kAllSubdomainsWildcard = "[*.]";

namespace content {

IsolatedOriginPattern::IsolatedOriginPattern(base::StringPiece pattern)
    : isolate_all_subdomains_(false), is_valid_(false) {
  Parse(pattern);
}

IsolatedOriginPattern::IsolatedOriginPattern(const url::Origin& origin)
    : IsolatedOriginPattern(origin.GetURL().spec()) {}

IsolatedOriginPattern::~IsolatedOriginPattern() = default;
IsolatedOriginPattern::IsolatedOriginPattern(
    const IsolatedOriginPattern& other) = default;
IsolatedOriginPattern& IsolatedOriginPattern::operator=(
    const IsolatedOriginPattern& other) = default;
IsolatedOriginPattern::IsolatedOriginPattern(IsolatedOriginPattern&& other) =
    default;
IsolatedOriginPattern& IsolatedOriginPattern::operator=(
    IsolatedOriginPattern&& other) = default;

bool IsolatedOriginPattern::Parse(const base::StringPiece& unparsed_pattern) {
  pattern_ = unparsed_pattern.as_string();
  origin_ = url::Origin();
  isolate_all_subdomains_ = false;
  is_valid_ = false;

  size_t host_begin = unparsed_pattern.find(url::kStandardSchemeSeparator);
  if (host_begin == base::StringPiece::npos || host_begin == 0)
    return false;

  // Skip over the scheme separator.
  host_begin += strlen(url::kStandardSchemeSeparator);
  if (host_begin >= unparsed_pattern.size())
    return false;

  base::StringPiece scheme_part = unparsed_pattern.substr(0, host_begin);
  base::StringPiece host_part = unparsed_pattern.substr(host_begin);

  // Empty schemes or hosts are invalid for isolation purposes.
  if (host_part.size() == 0)
    return false;

  if (base::StartsWith(host_part, kAllSubdomainsWildcard)) {
    isolate_all_subdomains_ = true;
    host_part.remove_prefix(strlen(kAllSubdomainsWildcard));
  }

  GURL conformant_url(base::JoinString({scheme_part, host_part}, ""));
  origin_ = url::Origin::Create(conformant_url);

  // Ports are ignored when matching isolated origins (see also
  // https://crbug.com/914511).
  const std::string& scheme = origin_.scheme();
  int default_port = url::DefaultPortForScheme(scheme.data(), scheme.length());
  if (origin_.port() != default_port) {
    LOG(ERROR) << "Ignoring port number in isolated origin: " << origin_;
    origin_ = url::Origin::Create(GURL(
        origin_.scheme() + url::kStandardSchemeSeparator + origin_.host()));
  }

  // Can't isolate subdomains of an IP address, must be a valid isolated origin
  // after processing.
  if ((conformant_url.HostIsIPAddress() && isolate_all_subdomains_) ||
      !IsolatedOriginUtil::IsValidIsolatedOrigin(origin_)) {
    origin_ = url::Origin();
    isolate_all_subdomains_ = false;
    return false;
  }

  DCHECK(!is_valid_ || !origin_.opaque());
  is_valid_ = true;
  return true;
}

// static
bool IsolatedOriginUtil::DoesOriginMatchIsolatedOrigin(
    const url::Origin& origin,
    const url::Origin& isolated_origin) {
  // Don't match subdomains if the isolated origin is an IP address.
  if (isolated_origin.GetURL().HostIsIPAddress())
    return origin == isolated_origin;

  // Compare scheme and hostname, but don't compare ports - see
  // https://crbug.com/914511.
  if (origin.scheme() != isolated_origin.scheme())
    return false;

  // Subdomains of an isolated origin are considered to be in the same isolated
  // origin.
  return origin.DomainIs(isolated_origin.host());
}

// static
bool IsolatedOriginUtil::IsValidIsolatedOrigin(const url::Origin& origin) {
  if (origin.opaque())
    return false;

  // Isolated origins should have HTTP or HTTPS schemes.  Hosts in other
  // schemes may not be compatible with subdomain matching.
  GURL origin_gurl = origin.GetURL();
  if (!origin_gurl.SchemeIsHTTPOrHTTPS())
    return false;

  // IP addresses are allowed.
  if (origin_gurl.HostIsIPAddress())
    return true;

  // Disallow hosts such as http://co.uk/, which don't have a valid
  // registry-controlled domain.  This prevents subdomain matching from
  // grouping unrelated sites on a registry into the same origin.
  const bool has_registry_domain =
      net::registry_controlled_domains::HostHasRegistryControlledDomain(
          origin.host(),
          net::registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES,
          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
  if (!has_registry_domain)
    return false;

  // For now, disallow hosts with a trailing dot.
  // TODO(alexmos): Enabling this would require carefully thinking about
  // whether hosts without a trailing dot should match it.
  if (origin.host().back() == '.')
    return false;

  return true;
}

}  // namespace content