PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/services/network/public/cpp/is_potentially_trustworthy.cc

http://github.com/chromium/chromium
C++ | 352 lines | 219 code | 49 blank | 84 comment | 37 complexity | 17b03099a82ad0f6ded9cf4d5bb794b5 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.0, BSD-2-Clause, LGPL-2.1, MPL-2.0, 0BSD, EPL-1.0, MPL-2.0-no-copyleft-exception, GPL-2.0, BitTorrent-1.0, CPL-1.0, LGPL-3.0, Unlicense, BSD-3-Clause, CC0-1.0, JSON, MIT, GPL-3.0, CC-BY-SA-3.0, AGPL-1.0
  1. // Copyright 2019 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. #include "services/network/public/cpp/is_potentially_trustworthy.h"
  5. #include <algorithm>
  6. #include <iterator>
  7. #include <utility>
  8. #include "base/command_line.h"
  9. #include "base/logging.h"
  10. #include "base/metrics/histogram_macros.h"
  11. #include "base/no_destructor.h"
  12. #include "base/optional.h"
  13. #include "base/sequence_checker.h"
  14. #include "base/strings/pattern.h"
  15. #include "base/strings/string_split.h"
  16. #include "base/strings/string_util.h"
  17. #include "base/strings/stringprintf.h"
  18. #include "build/build_config.h"
  19. #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
  20. #include "net/base/url_util.h"
  21. #include "services/network/public/cpp/network_switches.h"
  22. #include "url/origin.h"
  23. #include "url/scheme_host_port.h"
  24. #include "url/url_canon.h"
  25. #include "url/url_canon_ip.h"
  26. #include "url/url_canon_stdstring.h"
  27. #include "url/url_constants.h"
  28. #include "url/url_util.h"
  29. namespace network {
  30. namespace {
  31. // Given a hostname pattern with a wildcard such as "*.foo.com", returns
  32. // true if |hostname_pattern| meets both of these conditions:
  33. // 1.) A string matching |hostname_pattern| is a valid hostname.
  34. // 2.) Wildcards only appear beyond the eTLD+1. "*.foo.com" is considered
  35. // valid but "*.com" is not.
  36. bool IsValidWildcardPattern(const std::string& hostname_pattern) {
  37. // Replace wildcards with dummy values to check whether a matching origin is
  38. // valid.
  39. std::string wildcards_replaced;
  40. if (!base::ReplaceChars(hostname_pattern, "*", "a", &wildcards_replaced))
  41. return false;
  42. // Construct a SchemeHostPort with a dummy scheme and port to check that the
  43. // hostname is valid.
  44. url::SchemeHostPort scheme_host_port(
  45. GURL(base::StringPrintf("http://%s:80", wildcards_replaced.c_str())));
  46. if (!scheme_host_port.IsValid())
  47. return false;
  48. // Check that wildcards only appear beyond the eTLD+1.
  49. size_t registry_length =
  50. net::registry_controlled_domains::PermissiveGetHostRegistryLength(
  51. hostname_pattern,
  52. net::registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES,
  53. net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
  54. // std::string::npos should only be returned for empty inputs, which should be
  55. // filtered out by the IsValid() check above.
  56. CHECK(registry_length != std::string::npos);
  57. // If there is no registrar portion, the pattern is considered invalid.
  58. if (registry_length == 0)
  59. return false;
  60. // If there is no component before the registrar portion, or if the component
  61. // immediately preceding the registrar portion contains a wildcard, the
  62. // pattern is not considered valid.
  63. std::string host_before_registrar =
  64. hostname_pattern.substr(0, hostname_pattern.size() - registry_length);
  65. std::vector<std::string> components =
  66. base::SplitString(host_before_registrar, ".", base::KEEP_WHITESPACE,
  67. base::SPLIT_WANT_NONEMPTY);
  68. if (components.size() == 0)
  69. return false;
  70. if (components.back().find("*") != std::string::npos)
  71. return false;
  72. return true;
  73. }
  74. // Canonicalizes each component of |hostname_pattern|, making no changes to
  75. // wildcard components or components that fail canonicalization. For example,
  76. // given a |hostname_pattern| of "TeSt.*.%46oo.com", the output will be
  77. // "test.*.foo.com".
  78. std::string CanonicalizePatternComponents(const std::string& hostname_pattern) {
  79. std::string canonical_host; // Do not modify outside of canon_output.
  80. canonical_host.reserve(hostname_pattern.length());
  81. url::StdStringCanonOutput canon_output(&canonical_host);
  82. for (size_t current = 0; current < hostname_pattern.length(); current++) {
  83. size_t begin = current;
  84. // Advance to next "." or end.
  85. current = hostname_pattern.find('.', begin);
  86. if (current == std::string::npos)
  87. current = hostname_pattern.length();
  88. // Try to append the canonicalized version of this component.
  89. int current_len = base::checked_cast<int>(current - begin);
  90. if (hostname_pattern.substr(begin, current_len) == "*" ||
  91. !url::CanonicalizeHostSubstring(
  92. hostname_pattern.data(),
  93. url::Component(base::checked_cast<int>(begin), current_len),
  94. &canon_output)) {
  95. // Failed to canonicalize this component; append as-is.
  96. canon_output.Append(hostname_pattern.substr(begin, current_len).data(),
  97. current_len);
  98. }
  99. if (current < hostname_pattern.length())
  100. canon_output.push_back('.');
  101. }
  102. canon_output.Complete();
  103. return canonical_host;
  104. }
  105. std::vector<std::string> CanonicalizeAllowlist(
  106. const std::vector<std::string>& origins_and_patterns_list,
  107. std::vector<std::string>* rejected_patterns) {
  108. std::vector<std::string> result;
  109. for (const std::string& origin_or_pattern : origins_and_patterns_list) {
  110. if (origin_or_pattern.find("*") != std::string::npos) {
  111. if (IsValidWildcardPattern(origin_or_pattern)) {
  112. std::string canonicalized_pattern =
  113. CanonicalizePatternComponents(origin_or_pattern);
  114. if (!canonicalized_pattern.empty()) {
  115. result.push_back(canonicalized_pattern);
  116. continue;
  117. }
  118. }
  119. LOG(ERROR) << "Allowlisted secure origin pattern " << origin_or_pattern
  120. << " is not valid; ignoring.";
  121. if (rejected_patterns)
  122. rejected_patterns->push_back(origin_or_pattern);
  123. continue;
  124. }
  125. // Drop opaque origins, as they are unequal to any other origins.
  126. url::Origin origin(url::Origin::Create(GURL(origin_or_pattern)));
  127. if (origin.opaque()) {
  128. LOG(ERROR) << "Allowlisted secure origin pattern " << origin_or_pattern
  129. << " is not valid; ignoring.";
  130. if (rejected_patterns)
  131. rejected_patterns->push_back(origin_or_pattern);
  132. continue;
  133. }
  134. result.push_back(origin.Serialize());
  135. }
  136. return result;
  137. }
  138. std::vector<std::string> ParseSecureOriginAllowlist(
  139. const std::string& origins_str,
  140. std::vector<std::string>* rejected_patterns = nullptr) {
  141. std::vector<std::string> origin_patterns = CanonicalizeAllowlist(
  142. base::SplitString(origins_str, ",", base::TRIM_WHITESPACE,
  143. base::SPLIT_WANT_ALL),
  144. rejected_patterns);
  145. return origin_patterns;
  146. }
  147. std::vector<std::string> ParseSecureOriginAllowlistFromCmdline() {
  148. // If kUnsafelyTreatInsecureOriginAsSecure option is given, then treat the
  149. // value as a comma-separated list of origins or origin patterns.
  150. const base::CommandLine& command_line =
  151. *base::CommandLine::ForCurrentProcess();
  152. std::string origins_str = "";
  153. if (command_line.HasSwitch(switches::kUnsafelyTreatInsecureOriginAsSecure)) {
  154. origins_str = command_line.GetSwitchValueASCII(
  155. switches::kUnsafelyTreatInsecureOriginAsSecure);
  156. }
  157. std::vector<std::string> origin_patterns =
  158. ParseSecureOriginAllowlist(origins_str);
  159. #if defined(OS_CHROMEOS)
  160. // For Crostini, we allow access to the default VM/container as a secure
  161. // origin via the hostname penguin.linux.test. We are required to use a
  162. // wildcard for the prefix because we do not know what the port number is.
  163. // https://chromium.googlesource.com/chromiumos/docs/+/master/containers_and_vms.md
  164. origin_patterns.push_back("*.linux.test");
  165. #endif
  166. return origin_patterns;
  167. }
  168. bool IsAllowlisted(const std::vector<std::string>& allowlist,
  169. const url::Origin& origin) {
  170. if (base::Contains(allowlist, origin.Serialize()))
  171. return true;
  172. for (const std::string& origin_or_pattern : allowlist) {
  173. if (base::MatchPattern(origin.host(), origin_or_pattern))
  174. return true;
  175. }
  176. return false;
  177. }
  178. } // namespace
  179. bool IsOriginPotentiallyTrustworthy(const url::Origin& origin) {
  180. // The code below is based on the specification at
  181. // https://www.w3.org/TR/powerful-features/#is-origin-trustworthy.
  182. // 1. If origin is an opaque origin, return "Not Trustworthy".
  183. if (origin.opaque())
  184. return false;
  185. // 2. Assert: origin is a tuple origin.
  186. DCHECK(!origin.opaque());
  187. // 3. If origin’s scheme is either "https" or "wss", return "Potentially
  188. // Trustworthy".
  189. if (GURL::SchemeIsCryptographic(origin.scheme()))
  190. return true;
  191. // 4. If origin’s host component matches one of the CIDR notations 127.0.0.0/8
  192. // or ::1/128 [RFC4632], return "Potentially Trustworthy".
  193. //
  194. // Diverging from the spec a bit here - in addition to the hostnames covered
  195. // by https://www.w3.org/TR/powerful-features/#is-origin-trustworthy, the code
  196. // below also considers "localhost" to be potentially secure.
  197. //
  198. // Cannot just pass |origin.host()| to |HostStringIsLocalhost|, because of the
  199. // need to also strip the brackets from things like "[::1]".
  200. if (net::IsLocalhost(origin.GetURL()))
  201. return true;
  202. // 5. If origin’s scheme component is file, return "Potentially Trustworthy".
  203. //
  204. // This is somewhat redundant with the GetLocalSchemes-based check below.
  205. if (origin.scheme() == url::kFileScheme)
  206. return true;
  207. // 6. If origin’s scheme component is one which the user agent considers to be
  208. // authenticated, return "Potentially Trustworthy".
  209. // Note: See §7.1 Packaged Applications for detail here.
  210. //
  211. // Note that this ignores some schemes that are considered trustworthy by
  212. // higher layers (e.g. see GetSchemesBypassingSecureContextCheck in //chrome).
  213. //
  214. // See also
  215. // - content::ContentClient::AddAdditionalSchemes and
  216. // content::ContentClient::Schemes::local_schemes and
  217. // content::ContentClient::Schemes::secure_schemes
  218. // - url::AddLocalScheme
  219. // - url::AddSecureScheme
  220. if (base::Contains(url::GetSecureSchemes(), origin.scheme()) ||
  221. base::Contains(url::GetLocalSchemes(), origin.scheme())) {
  222. return true;
  223. }
  224. // 7. If origin has been configured as a trustworthy origin, return
  225. // "Potentially Trustworthy".
  226. // Note: See §7.2 Development Environments for detail here.
  227. if (SecureOriginAllowlist::GetInstance().IsOriginAllowlisted(origin))
  228. return true;
  229. // 8. Return "Not Trustworthy".
  230. return false;
  231. }
  232. bool IsUrlPotentiallyTrustworthy(const GURL& url) {
  233. // The code below is based on the specification at
  234. // https://www.w3.org/TR/powerful-features/#is-url-trustworthy.
  235. // 1. If url’s scheme is "data", return "Not Trustworthy".
  236. // Note: This aligns the definition of a secure context with the de facto
  237. // "data: URL as opaque origin" behavior that a majority of today’s
  238. // browsers have agreed upon, rather than the de jure "data: URL inherits
  239. // origin" behavior defined in HTML.
  240. if (url.SchemeIs(url::kDataScheme))
  241. return false;
  242. // 2. If url is "about:blank" or "about:srcdoc", return "Potentially
  243. // Trustworthy".
  244. if (url.SchemeIs(url::kAboutScheme))
  245. return true;
  246. // 3. Return the result of executing §3.2 Is origin potentially trustworthy?
  247. // on url’s origin.
  248. // Note: The origin of blob: and filesystem: URLs is the origin of the
  249. // context in which they were created. Therefore, blobs created in a
  250. // trustworthy origin will themselves be potentially trustworthy.
  251. return IsOriginPotentiallyTrustworthy(url::Origin::Create(url));
  252. }
  253. // static
  254. SecureOriginAllowlist& SecureOriginAllowlist::GetInstance() {
  255. static base::NoDestructor<SecureOriginAllowlist> s_instance;
  256. return *s_instance;
  257. }
  258. std::vector<std::string> SecureOriginAllowlist::GetCurrentAllowlist() {
  259. base::AutoLock lock(lock_);
  260. ParseCmdlineIfNeeded();
  261. std::vector<std::string> result;
  262. result.reserve(cmdline_allowlist_.size() + auxiliary_allowlist_.size());
  263. std::copy(cmdline_allowlist_.begin(), cmdline_allowlist_.end(),
  264. std::back_inserter(result));
  265. std::copy(auxiliary_allowlist_.begin(), auxiliary_allowlist_.end(),
  266. std::back_inserter(result));
  267. return result;
  268. }
  269. void SecureOriginAllowlist::SetAuxiliaryAllowlist(
  270. const std::string& auxiliary_allowlist,
  271. std::vector<std::string>* rejected_patterns) {
  272. std::vector<std::string> parsed_list =
  273. ParseSecureOriginAllowlist(auxiliary_allowlist, rejected_patterns);
  274. base::AutoLock lock(lock_);
  275. auxiliary_allowlist_ = std::move(parsed_list);
  276. }
  277. void SecureOriginAllowlist::ResetForTesting() {
  278. base::AutoLock lock(lock_);
  279. cmdline_allowlist_.clear();
  280. has_cmdline_been_parsed_ = false;
  281. auxiliary_allowlist_.clear();
  282. }
  283. bool SecureOriginAllowlist::IsOriginAllowlisted(const url::Origin& origin) {
  284. base::AutoLock lock(lock_);
  285. ParseCmdlineIfNeeded();
  286. return IsAllowlisted(cmdline_allowlist_, origin) ||
  287. IsAllowlisted(auxiliary_allowlist_, origin);
  288. }
  289. // static
  290. std::vector<std::string> SecureOriginAllowlist::CanonicalizeAllowlistForTesting(
  291. const std::vector<std::string>& allowlist,
  292. std::vector<std::string>* rejected_patterns) {
  293. return CanonicalizeAllowlist(allowlist, rejected_patterns);
  294. }
  295. SecureOriginAllowlist::SecureOriginAllowlist() = default;
  296. void SecureOriginAllowlist::ParseCmdlineIfNeeded() {
  297. lock_.AssertAcquired();
  298. if (!has_cmdline_been_parsed_) {
  299. cmdline_allowlist_ = ParseSecureOriginAllowlistFromCmdline();
  300. has_cmdline_been_parsed_ = true;
  301. }
  302. }
  303. } // namespace network