blob: 3d6770e8996c51c91d262945307047a4c8e83180 [file] [log] [blame]
alexmos4bc26322017-07-01 00:57:141// Copyright (c) 2017 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Andrew Stone6ed99b22019-06-07 06:14:395#include <string>
6
alexmos4bc26322017-07-01 00:57:147#include "content/browser/isolated_origin_util.h"
8
9#include "base/strings/string_util.h"
10#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
11#include "url/gurl.h"
12
Andrew Stone404880d2019-07-10 02:23:3113const char* kAllSubdomainsWildcard = "[*.]";
Andrew Stone6ed99b22019-06-07 06:14:3914
alexmos4bc26322017-07-01 00:57:1415namespace content {
16
Andrew Stone6ed99b22019-06-07 06:14:3917IsolatedOriginPattern::IsolatedOriginPattern(base::StringPiece pattern)
18 : isolate_all_subdomains_(false), is_valid_(false) {
19 Parse(pattern);
20}
21
22IsolatedOriginPattern::IsolatedOriginPattern(const url::Origin& origin)
23 : IsolatedOriginPattern(origin.GetURL().spec()) {}
24
25IsolatedOriginPattern::~IsolatedOriginPattern() = default;
26IsolatedOriginPattern::IsolatedOriginPattern(
27 const IsolatedOriginPattern& other) = default;
28IsolatedOriginPattern& IsolatedOriginPattern::operator=(
29 const IsolatedOriginPattern& other) = default;
30IsolatedOriginPattern::IsolatedOriginPattern(IsolatedOriginPattern&& other) =
31 default;
32IsolatedOriginPattern& IsolatedOriginPattern::operator=(
33 IsolatedOriginPattern&& other) = default;
34
35bool IsolatedOriginPattern::Parse(const base::StringPiece& unparsed_pattern) {
36 pattern_ = unparsed_pattern.as_string();
37 origin_ = url::Origin();
38 isolate_all_subdomains_ = false;
39 is_valid_ = false;
40
41 size_t host_begin = unparsed_pattern.find(url::kStandardSchemeSeparator);
42 if (host_begin == base::StringPiece::npos || host_begin == 0)
43 return false;
44
45 // Skip over the scheme separator.
46 host_begin += strlen(url::kStandardSchemeSeparator);
47 if (host_begin >= unparsed_pattern.size())
48 return false;
49
50 base::StringPiece scheme_part = unparsed_pattern.substr(0, host_begin);
51 base::StringPiece host_part = unparsed_pattern.substr(host_begin);
52
53 // Empty schemes or hosts are invalid for isolation purposes.
54 if (host_part.size() == 0)
55 return false;
56
57 if (host_part.starts_with(kAllSubdomainsWildcard)) {
58 isolate_all_subdomains_ = true;
59 host_part.remove_prefix(strlen(kAllSubdomainsWildcard));
60 }
61
62 GURL conformant_url(base::JoinString({scheme_part, host_part}, ""));
63 origin_ = url::Origin::Create(conformant_url);
64
65 // Ports are ignored when matching isolated origins (see also
66 // https://crbug.com/914511).
67 const std::string& scheme = origin_.scheme();
68 int default_port = url::DefaultPortForScheme(scheme.data(), scheme.length());
69 if (origin_.port() != default_port) {
70 LOG(ERROR) << "Ignoring port number in isolated origin: " << origin_;
71 origin_ = url::Origin::Create(GURL(
72 origin_.scheme() + url::kStandardSchemeSeparator + origin_.host()));
73 }
74
75 // Can't isolate subdomains of an IP address, must be a valid isolated origin
76 // after processing.
77 if ((conformant_url.HostIsIPAddress() && isolate_all_subdomains_) ||
78 !IsolatedOriginUtil::IsValidIsolatedOrigin(origin_)) {
79 origin_ = url::Origin();
80 isolate_all_subdomains_ = false;
81 return false;
82 }
83
Andrew Stone0a177fe22019-06-26 08:12:0484 DCHECK(!is_valid_ || !origin_.opaque());
Andrew Stone6ed99b22019-06-07 06:14:3985 is_valid_ = true;
86 return true;
87}
88
alexmos4bc26322017-07-01 00:57:1489// static
90bool IsolatedOriginUtil::DoesOriginMatchIsolatedOrigin(
91 const url::Origin& origin,
92 const url::Origin& isolated_origin) {
93 // Don't match subdomains if the isolated origin is an IP address.
94 if (isolated_origin.GetURL().HostIsIPAddress())
95 return origin == isolated_origin;
96
Lukasz Anforowicz25420932018-12-18 20:59:2297 // Compare scheme and hostname, but don't compare ports - see
98 // https://crbug.com/914511.
alexmos4bc26322017-07-01 00:57:1499 if (origin.scheme() != isolated_origin.scheme())
100 return false;
101
alexmos4bc26322017-07-01 00:57:14102 // Subdomains of an isolated origin are considered to be in the same isolated
103 // origin.
104 return origin.DomainIs(isolated_origin.host());
105}
106
107// static
108bool IsolatedOriginUtil::IsValidIsolatedOrigin(const url::Origin& origin) {
Chris Palmerab5e5b52018-09-28 19:19:30109 if (origin.opaque())
alexmos4bc26322017-07-01 00:57:14110 return false;
111
112 // Isolated origins should have HTTP or HTTPS schemes. Hosts in other
113 // schemes may not be compatible with subdomain matching.
114 GURL origin_gurl = origin.GetURL();
115 if (!origin_gurl.SchemeIsHTTPOrHTTPS())
116 return false;
117
118 // IP addresses are allowed.
119 if (origin_gurl.HostIsIPAddress())
120 return true;
121
122 // Disallow hosts such as http://co.uk/, which don't have a valid
123 // registry-controlled domain. This prevents subdomain matching from
124 // grouping unrelated sites on a registry into the same origin.
125 const bool has_registry_domain =
126 net::registry_controlled_domains::HostHasRegistryControlledDomain(
127 origin.host(),
128 net::registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES,
129 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
130 if (!has_registry_domain)
131 return false;
132
133 // For now, disallow hosts with a trailing dot.
134 // TODO(alexmos): Enabling this would require carefully thinking about
135 // whether hosts without a trailing dot should match it.
136 if (origin.host().back() == '.')
137 return false;
138
139 return true;
140}
141
142} // namespace content