diff options
Diffstat (limited to 'Source/WebCore/page/SecurityOrigin.cpp')
-rw-r--r-- | Source/WebCore/page/SecurityOrigin.cpp | 328 |
1 files changed, 133 insertions, 195 deletions
diff --git a/Source/WebCore/page/SecurityOrigin.cpp b/Source/WebCore/page/SecurityOrigin.cpp index 2be30d44f..38fa1e9a4 100644 --- a/Source/WebCore/page/SecurityOrigin.cpp +++ b/Source/WebCore/page/SecurityOrigin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -36,14 +36,12 @@ #include "SecurityPolicy.h" #include "ThreadableBlobRegistry.h" #include <wtf/MainThread.h> +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> #include <wtf/text/StringBuilder.h> namespace WebCore { -const int InvalidPort = 0; -const int MaxAllowedPort = 65535; - static bool schemeRequiresHost(const URL& url) { // We expect URLs with these schemes to have authority components. If the @@ -54,11 +52,9 @@ static bool schemeRequiresHost(const URL& url) bool SecurityOrigin::shouldUseInnerURL(const URL& url) { -#if ENABLE(BLOB) // FIXME: Blob URLs don't have inner URLs. Their form is "blob:<inner-origin>/<UUID>", so treating the part after "blob:" as a URL is incorrect. - if (url.protocolIs("blob")) + if (url.protocolIsBlob()) return true; -#endif UNUSED_PARAM(url); return false; } @@ -68,22 +64,16 @@ bool SecurityOrigin::shouldUseInnerURL(const URL& url) // security origin can be parsed using this algorithm. URL SecurityOrigin::extractInnerURL(const URL& url) { - if (url.innerURL()) - return *url.innerURL(); // FIXME: Update this callsite to use the innerURL member function when // we finish implementing it. return URL(ParsedURLString, decodeURLEscapeSequences(url.path())); } -static PassRefPtr<SecurityOrigin> getCachedOrigin(const URL& url) +static RefPtr<SecurityOrigin> getCachedOrigin(const URL& url) { -#if ENABLE(BLOB) - if (url.protocolIs("blob")) + if (url.protocolIsBlob()) return ThreadableBlobRegistry::getCachedOrigin(url); -#else - UNUSED_PARAM(url); -#endif - return 0; + return nullptr; } static bool shouldTreatAsUniqueOrigin(const URL& url) @@ -102,11 +92,7 @@ static bool shouldTreatAsUniqueOrigin(const URL& url) if (schemeRequiresHost(innerURL) && innerURL.host().isEmpty()) return true; - // SchemeRegistry needs a lower case protocol because it uses HashMaps - // that assume the scheme has already been canonicalized. - String protocol = innerURL.protocol().lower(); - - if (SchemeRegistry::shouldTreatURLSchemeAsNoAccess(protocol)) + if (SchemeRegistry::shouldTreatURLSchemeAsNoAccess(innerURL.protocol().toStringWithoutCopying())) return true; // This is the common case. @@ -114,41 +100,28 @@ static bool shouldTreatAsUniqueOrigin(const URL& url) } SecurityOrigin::SecurityOrigin(const URL& url) - : m_protocol(url.protocol().isNull() ? "" : url.protocol().lower()) - , m_host(url.host().isNull() ? "" : url.host().lower()) + : m_protocol(url.protocol().isNull() ? emptyString() : url.protocol().toString().convertToASCIILowercase()) + , m_host(url.host().isNull() ? emptyString() : url.host().convertToASCIILowercase()) , m_port(url.port()) - , m_isUnique(false) - , m_universalAccess(false) - , m_domainWasSetInDOM(false) - , m_storageBlockingPolicy(AllowAllStorage) - , m_enforceFilePathSeparation(false) - , m_needsDatabaseIdentifierQuirkForFiles(false) { // document.domain starts as m_host, but can be set by the DOM. m_domain = m_host; - if (isDefaultPortForProtocol(m_port, m_protocol)) - m_port = InvalidPort; + if (m_port && isDefaultPortForProtocol(m_port.value(), m_protocol)) + m_port = std::nullopt; // By default, only local SecurityOrigins can load local resources. m_canLoadLocalResources = isLocal(); if (m_canLoadLocalResources) - m_filePath = url.path(); // In case enforceFilePathSeparation() is called. + m_filePath = url.fileSystemPath(); // In case enforceFilePathSeparation() is called. } SecurityOrigin::SecurityOrigin() - : m_protocol("") - , m_host("") - , m_domain("") - , m_port(InvalidPort) + : m_protocol(emptyString()) + , m_host(emptyString()) + , m_domain(emptyString()) , m_isUnique(true) - , m_universalAccess(false) - , m_domainWasSetInDOM(false) - , m_canLoadLocalResources(false) - , m_storageBlockingPolicy(AllowAllStorage) - , m_enforceFilePathSeparation(false) - , m_needsDatabaseIdentifierQuirkForFiles(false) { } @@ -164,76 +137,64 @@ SecurityOrigin::SecurityOrigin(const SecurityOrigin* other) , m_canLoadLocalResources(other->m_canLoadLocalResources) , m_storageBlockingPolicy(other->m_storageBlockingPolicy) , m_enforceFilePathSeparation(other->m_enforceFilePathSeparation) - , m_needsDatabaseIdentifierQuirkForFiles(other->m_needsDatabaseIdentifierQuirkForFiles) + , m_needsStorageAccessFromFileURLsQuirk(other->m_needsStorageAccessFromFileURLsQuirk) { } -PassRefPtr<SecurityOrigin> SecurityOrigin::create(const URL& url) +Ref<SecurityOrigin> SecurityOrigin::create(const URL& url) { - RefPtr<SecurityOrigin> cachedOrigin = getCachedOrigin(url); - if (cachedOrigin.get()) - return cachedOrigin; - - if (shouldTreatAsUniqueOrigin(url)) { - RefPtr<SecurityOrigin> origin = adoptRef(new SecurityOrigin()); - - if (url.protocolIs("file")) { - // Unfortunately, we can't represent all unique origins exactly - // the same way because we need to produce a quirky database - // identifier for file URLs due to persistent storage in some - // embedders of WebKit. - origin->m_needsDatabaseIdentifierQuirkForFiles = true; - } + if (RefPtr<SecurityOrigin> cachedOrigin = getCachedOrigin(url)) + return cachedOrigin.releaseNonNull(); - return origin.release(); - } + if (shouldTreatAsUniqueOrigin(url)) + return adoptRef(*new SecurityOrigin); if (shouldUseInnerURL(url)) - return adoptRef(new SecurityOrigin(extractInnerURL(url))); + return adoptRef(*new SecurityOrigin(extractInnerURL(url))); - return adoptRef(new SecurityOrigin(url)); + return adoptRef(*new SecurityOrigin(url)); } -PassRefPtr<SecurityOrigin> SecurityOrigin::createUnique() +Ref<SecurityOrigin> SecurityOrigin::createUnique() { - RefPtr<SecurityOrigin> origin = adoptRef(new SecurityOrigin()); - ASSERT(origin->isUnique()); - return origin.release(); + Ref<SecurityOrigin> origin(adoptRef(*new SecurityOrigin)); + ASSERT(origin.get().isUnique()); + return origin; } -PassRefPtr<SecurityOrigin> SecurityOrigin::isolatedCopy() const +Ref<SecurityOrigin> SecurityOrigin::isolatedCopy() const { - return adoptRef(new SecurityOrigin(this)); + return adoptRef(*new SecurityOrigin(this)); } void SecurityOrigin::setDomainFromDOM(const String& newDomain) { m_domainWasSetInDOM = true; - m_domain = newDomain.lower(); + m_domain = newDomain.convertToASCIILowercase(); } bool SecurityOrigin::isSecure(const URL& url) { // Invalid URLs are secure, as are URLs which have a secure protocol. - if (!url.isValid() || SchemeRegistry::shouldTreatURLSchemeAsSecure(url.protocol())) + if (!url.isValid() || SchemeRegistry::shouldTreatURLSchemeAsSecure(url.protocol().toStringWithoutCopying())) return true; // URLs that wrap inner URLs are secure if those inner URLs are secure. - if (shouldUseInnerURL(url) && SchemeRegistry::shouldTreatURLSchemeAsSecure(extractInnerURL(url).protocol())) + if (shouldUseInnerURL(url) && SchemeRegistry::shouldTreatURLSchemeAsSecure(extractInnerURL(url).protocol().toStringWithoutCopying())) return true; return false; } -bool SecurityOrigin::canAccess(const SecurityOrigin* other) const +bool SecurityOrigin::canAccess(const SecurityOrigin& other) const { if (m_universalAccess) return true; - if (this == other) + if (this == &other) return true; - if (isUnique() || other->isUnique()) + if (isUnique() || other.isUnique()) return false; // Here are two cases where we should permit access: @@ -257,30 +218,30 @@ bool SecurityOrigin::canAccess(const SecurityOrigin* other) const // this is a security vulnerability. bool canAccess = false; - if (m_protocol == other->m_protocol) { - if (!m_domainWasSetInDOM && !other->m_domainWasSetInDOM) { - if (m_host == other->m_host && m_port == other->m_port) + if (m_protocol == other.m_protocol) { + if (!m_domainWasSetInDOM && !other.m_domainWasSetInDOM) { + if (m_host == other.m_host && m_port == other.m_port) canAccess = true; - } else if (m_domainWasSetInDOM && other->m_domainWasSetInDOM) { - if (m_domain == other->m_domain) + } else if (m_domainWasSetInDOM && other.m_domainWasSetInDOM) { + if (m_domain == other.m_domain) canAccess = true; } } if (canAccess && isLocal()) - canAccess = passesFileCheck(other); + canAccess = passesFileCheck(other); return canAccess; } -bool SecurityOrigin::passesFileCheck(const SecurityOrigin* other) const +bool SecurityOrigin::passesFileCheck(const SecurityOrigin& other) const { - ASSERT(isLocal() && other->isLocal()); + ASSERT(isLocal() && other.isLocal()); - if (!m_enforceFilePathSeparation && !other->m_enforceFilePathSeparation) + if (!m_enforceFilePathSeparation && !other.m_enforceFilePathSeparation) return true; - return (m_filePath == other->m_filePath); + return (m_filePath == other.m_filePath); } bool SecurityOrigin::canRequest(const URL& url) const @@ -294,7 +255,7 @@ bool SecurityOrigin::canRequest(const URL& url) const if (isUnique()) return false; - RefPtr<SecurityOrigin> targetOrigin = SecurityOrigin::create(url); + Ref<SecurityOrigin> targetOrigin(SecurityOrigin::create(url)); if (targetOrigin->isUnique()) return false; @@ -304,35 +265,18 @@ bool SecurityOrigin::canRequest(const URL& url) const if (isSameSchemeHostPort(targetOrigin.get())) return true; - if (SecurityPolicy::isAccessWhiteListed(this, targetOrigin.get())) + if (SecurityPolicy::isAccessWhiteListed(this, &targetOrigin.get())) return true; return false; } -bool SecurityOrigin::taintsCanvas(const URL& url) const +bool SecurityOrigin::canReceiveDragData(const SecurityOrigin& dragInitiator) const { - if (canRequest(url)) - return false; - - // This function exists because we treat data URLs as having a unique origin, - // contrary to the current (9/19/2009) draft of the HTML5 specification. - // We still want to let folks paint data URLs onto untainted canvases, so - // we special case data URLs below. If we change to match HTML5 w.r.t. - // data URL security, then we can remove this function in favor of - // !canRequest. - if (url.protocolIsData()) - return false; - - return true; -} - -bool SecurityOrigin::canReceiveDragData(const SecurityOrigin* dragInitiator) const -{ - if (this == dragInitiator) + if (this == &dragInitiator) return true; - return canAccess(dragInitiator); + return canAccess(dragInitiator); } // This is a hack to allow keep navigation to http/https feeds working. To remove this @@ -360,16 +304,21 @@ bool SecurityOrigin::canDisplay(const URL& url) const if (m_universalAccess) return true; - String protocol = url.protocol().lower(); +#if !PLATFORM(IOS) + if (m_protocol == "file" && url.isLocalFile() && !filesHaveSameVolume(m_filePath, url.fileSystemPath())) + return false; +#endif if (isFeedWithNestedProtocolInHTTPFamily(url)) return true; + String protocol = url.protocol().toString(); + if (SchemeRegistry::canDisplayOnlyIfCanRequest(protocol)) return canRequest(url); if (SchemeRegistry::shouldTreatURLSchemeAsDisplayIsolated(protocol)) - return m_protocol == protocol || SecurityPolicy::isAccessToURLWhiteListed(this, url); + return equalIgnoringASCIICase(m_protocol, protocol) || SecurityPolicy::isAccessToURLWhiteListed(this, url); if (SecurityPolicy::restrictAccessToLocal() && SchemeRegistry::shouldTreatURLSchemeAsLocal(protocol)) return canLoadLocalResources() || SecurityPolicy::isAccessToURLWhiteListed(this, url); @@ -382,6 +331,9 @@ bool SecurityOrigin::canAccessStorage(const SecurityOrigin* topOrigin, ShouldAll if (isUnique()) return false; + if (isLocal() && !needsStorageAccessFromFileURLsQuirk() && !m_universalAccess && shouldAllowFromThirdParty != AlwaysAllowFromThirdParty) + return false; + if (m_storageBlockingPolicy == BlockAllStorage) return false; @@ -395,7 +347,10 @@ bool SecurityOrigin::canAccessStorage(const SecurityOrigin* topOrigin, ShouldAll if (shouldAllowFromThirdParty == AlwaysAllowFromThirdParty) return true; - if ((m_storageBlockingPolicy == BlockThirdPartyStorage || topOrigin->m_storageBlockingPolicy == BlockThirdPartyStorage) && topOrigin->isThirdParty(this)) + if (m_universalAccess) + return true; + + if ((m_storageBlockingPolicy == BlockThirdPartyStorage || topOrigin->m_storageBlockingPolicy == BlockThirdPartyStorage) && !topOrigin->isSameOriginAs(*this)) return false; return true; @@ -410,18 +365,15 @@ SecurityOrigin::Policy SecurityOrigin::canShowNotifications() const return Ask; } -bool SecurityOrigin::isThirdParty(const SecurityOrigin* child) const +bool SecurityOrigin::isSameOriginAs(const SecurityOrigin& other) const { - if (child->m_universalAccess) - return false; + if (this == &other) + return true; - if (this == child) + if (isUnique() || other.isUnique()) return false; - if (isUnique() || child->isUnique()) - return true; - - return !isSameSchemeHostPort(child); + return isSameSchemeHostPort(other); } void SecurityOrigin::grantLoadLocalResources() @@ -438,18 +390,24 @@ void SecurityOrigin::grantUniversalAccess() m_universalAccess = true; } -#if ENABLE(CACHE_PARTITIONING) -String SecurityOrigin::cachePartition() const +void SecurityOrigin::grantStorageAccessFromFileURLsQuirk() +{ + m_needsStorageAccessFromFileURLsQuirk = true; +} + +String SecurityOrigin::domainForCachePartition() const { if (m_storageBlockingPolicy != BlockThirdPartyStorage) - return String(); + return emptyString(); + + if (isHTTPFamily()) + return host(); - if (m_protocol != "http" && m_protocol != "https") - return String(); + if (SchemeRegistry::shouldPartitionCacheForURLScheme(m_protocol)) + return host(); - return host(); + return emptyString(); } -#endif void SecurityOrigin::enforceFilePathSeparation() { @@ -465,99 +423,79 @@ bool SecurityOrigin::isLocal() const String SecurityOrigin::toString() const { if (isUnique()) - return "null"; + return ASCIILiteral("null"); if (m_protocol == "file" && m_enforceFilePathSeparation) - return "null"; + return ASCIILiteral("null"); return toRawString(); } String SecurityOrigin::toRawString() const { if (m_protocol == "file") - return "file://"; + return ASCIILiteral("file://"); StringBuilder result; result.reserveCapacity(m_protocol.length() + m_host.length() + 10); result.append(m_protocol); - result.append("://"); + result.appendLiteral("://"); result.append(m_host); if (m_port) { result.append(':'); - result.appendNumber(m_port); + result.appendNumber(m_port.value()); } return result.toString(); } -PassRefPtr<SecurityOrigin> SecurityOrigin::createFromString(const String& originString) +static inline bool areOriginsMatching(const SecurityOrigin& origin1, const SecurityOrigin& origin2) { - return SecurityOrigin::create(URL(URL(), originString)); -} + if (origin1.isUnique() || origin2.isUnique()) + return origin1.isUnique() == origin2.isUnique(); -static const char separatorCharacter = '_'; - -PassRefPtr<SecurityOrigin> SecurityOrigin::createFromDatabaseIdentifier(const String& databaseIdentifier) -{ - // Make sure there's a first separator - size_t separator1 = databaseIdentifier.find(separatorCharacter); - if (separator1 == notFound) - return create(URL()); - - // Make sure there's a second separator - size_t separator2 = databaseIdentifier.reverseFind(separatorCharacter); - if (separator2 == notFound) - return create(URL()); - - // Ensure there were at least 2 separator characters. Some hostnames on intranets have - // underscores in them, so we'll assume that any additional underscores are part of the host. - if (separator1 == separator2) - return create(URL()); - - // Make sure the port section is a valid port number or doesn't exist - bool portOkay; - int port = databaseIdentifier.right(databaseIdentifier.length() - separator2 - 1).toInt(&portOkay); - bool portAbsent = (separator2 == databaseIdentifier.length() - 1); - if (!(portOkay || portAbsent)) - return create(URL()); - - if (port < 0 || port > MaxAllowedPort) - return create(URL()); - - // Split out the 3 sections of data - String protocol = databaseIdentifier.substring(0, separator1); - String host = databaseIdentifier.substring(separator1 + 1, separator2 - separator1 - 1); - - host = decodeURLEscapeSequences(host); - return create(URL(URL(), protocol + "://" + host + ":" + String::number(port) + "/")); + if (origin1.protocol() != origin2.protocol()) + return false; + + if (origin1.protocol() == "file") + return true; + + if (origin1.host() != origin2.host()) + return false; + + return origin1.port() == origin2.port(); } -PassRefPtr<SecurityOrigin> SecurityOrigin::create(const String& protocol, const String& host, int port) +// This function mimics the result of string comparison of serialized origins +bool originsMatch(const SecurityOrigin& origin1, const SecurityOrigin& origin2) { - if (port < 0 || port > MaxAllowedPort) - return createUnique(); - String decodedHost = decodeURLEscapeSequences(host); - return create(URL(URL(), protocol + "://" + host + ":" + String::number(port) + "/")); + if (&origin1 == &origin2) + return true; + + bool result = areOriginsMatching(origin1, origin2); + ASSERT(result == (origin1.toString() == origin2.toString())); + return result; } -String SecurityOrigin::databaseIdentifier() const +bool originsMatch(const SecurityOrigin* origin1, const SecurityOrigin* origin2) { - // Historically, we've used the following (somewhat non-sensical) string - // for the databaseIdentifier of local files. We used to compute this - // string because of a bug in how we handled the scheme for file URLs. - // Now that we've fixed that bug, we still need to produce this string - // to avoid breaking existing persistent state. - if (m_needsDatabaseIdentifierQuirkForFiles) - return "file__0"; + if (!origin1 || !origin2) + return origin1 == origin2; - StringBuilder stringBuilder; - stringBuilder.append(m_protocol); - stringBuilder.append(separatorCharacter); - stringBuilder.append(encodeForFileName(m_host)); - stringBuilder.append(separatorCharacter); - stringBuilder.appendNumber(m_port); + return originsMatch(*origin1, *origin2); +} - return stringBuilder.toString(); +Ref<SecurityOrigin> SecurityOrigin::createFromString(const String& originString) +{ + return SecurityOrigin::create(URL(URL(), originString)); +} + +Ref<SecurityOrigin> SecurityOrigin::create(const String& protocol, const String& host, std::optional<uint16_t> port) +{ + String decodedHost = decodeURLEscapeSequences(host); + auto origin = create(URL(URL(), protocol + "://" + host + "/")); + if (port && !isDefaultPortForProtocol(*port, protocol)) + origin->m_port = port; + return origin; } bool SecurityOrigin::equal(const SecurityOrigin* other) const @@ -565,7 +503,7 @@ bool SecurityOrigin::equal(const SecurityOrigin* other) const if (other == this) return true; - if (!isSameSchemeHostPort(other)) + if (!isSameSchemeHostPort(*other)) return false; if (m_domainWasSetInDOM != other->m_domainWasSetInDOM) @@ -577,15 +515,15 @@ bool SecurityOrigin::equal(const SecurityOrigin* other) const return true; } -bool SecurityOrigin::isSameSchemeHostPort(const SecurityOrigin* other) const +bool SecurityOrigin::isSameSchemeHostPort(const SecurityOrigin& other) const { - if (m_host != other->m_host) + if (m_host != other.m_host) return false; - if (m_protocol != other->m_protocol) + if (m_protocol != other.m_protocol) return false; - if (m_port != other->m_port) + if (m_port != other.m_port) return false; if (isLocal() && !passesFileCheck(other)) @@ -594,10 +532,10 @@ bool SecurityOrigin::isSameSchemeHostPort(const SecurityOrigin* other) const return true; } -String SecurityOrigin::urlWithUniqueSecurityOrigin() +URL SecurityOrigin::urlWithUniqueSecurityOrigin() { ASSERT(isMainThread()); - DEFINE_STATIC_LOCAL(const String, uniqueSecurityOriginURL, (ASCIILiteral("data:,"))); + static NeverDestroyed<URL> uniqueSecurityOriginURL(ParsedURLString, ASCIILiteral("data:,")); return uniqueSecurityOriginURL; } |