summaryrefslogtreecommitdiffstats
path: root/WebCore/page/SecurityOrigin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/page/SecurityOrigin.cpp')
-rw-r--r--WebCore/page/SecurityOrigin.cpp226
1 files changed, 197 insertions, 29 deletions
diff --git a/WebCore/page/SecurityOrigin.cpp b/WebCore/page/SecurityOrigin.cpp
index fe6efbd..265c643 100644
--- a/WebCore/page/SecurityOrigin.cpp
+++ b/WebCore/page/SecurityOrigin.cpp
@@ -29,7 +29,6 @@
#include "config.h"
#include "SecurityOrigin.h"
-#include "CString.h"
#include "Document.h"
#include "KURL.h"
#include "OriginAccessEntry.h"
@@ -65,6 +64,19 @@ static URLSchemesMap& localSchemes()
return localSchemes;
}
+static URLSchemesMap& secureSchemes()
+{
+ DEFINE_STATIC_LOCAL(URLSchemesMap, secureSchemes, ());
+
+ if (secureSchemes.isEmpty()) {
+ secureSchemes.add("https");
+ secureSchemes.add("about");
+ secureSchemes.add("data");
+ }
+
+ return secureSchemes;
+}
+
static URLSchemesMap& schemesWithUniqueOrigins()
{
DEFINE_STATIC_LOCAL(URLSchemesMap, schemesWithUniqueOrigins, ());
@@ -85,6 +97,7 @@ SecurityOrigin::SecurityOrigin(const KURL& url, SandboxFlags sandboxFlags)
, m_isUnique(isSandboxed(SandboxOrigin) || shouldTreatURLSchemeAsNoAccess(m_protocol))
, m_universalAccess(false)
, m_domainWasSetInDOM(false)
+ , m_enforceFilePathSeparation(false)
{
// These protocols do not create security origins; the owner frame provides the origin
if (m_protocol == "about" || m_protocol == "javascript")
@@ -99,6 +112,8 @@ SecurityOrigin::SecurityOrigin(const KURL& url, SandboxFlags sandboxFlags)
// Directories should never be readable.
if (!url.hasPath() || url.path().endsWith("/"))
m_isUnique = true;
+ // Store the path in case we are doing per-file origin checking.
+ m_filePath = url.path();
}
if (isDefaultPortForProtocol(m_port, m_protocol))
@@ -109,12 +124,15 @@ SecurityOrigin::SecurityOrigin(const SecurityOrigin* other)
: m_sandboxFlags(other->m_sandboxFlags)
, m_protocol(other->m_protocol.threadsafeCopy())
, m_host(other->m_host.threadsafeCopy())
+ , m_encodedHost(other->m_encodedHost.threadsafeCopy())
, m_domain(other->m_domain.threadsafeCopy())
+ , m_filePath(other->m_filePath.threadsafeCopy())
, m_port(other->m_port)
, m_isUnique(other->m_isUnique)
, m_universalAccess(other->m_universalAccess)
, m_domainWasSetInDOM(other->m_domainWasSetInDOM)
, m_canLoadLocalResources(other->m_canLoadLocalResources)
+ , m_enforceFilePathSeparation(other->m_enforceFilePathSeparation)
{
}
@@ -172,7 +190,7 @@ bool SecurityOrigin::isDomainRelaxationForbiddenForURLScheme(const String& schem
}
bool SecurityOrigin::canAccess(const SecurityOrigin* other) const
-{
+{
if (m_universalAccess)
return true;
@@ -199,17 +217,31 @@ bool SecurityOrigin::canAccess(const SecurityOrigin* other) const
// Opera 9 allows access when only one page has set document.domain, but
// 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)
- return true;
+ canAccess = true;
} else if (m_domainWasSetInDOM && other->m_domainWasSetInDOM) {
if (m_domain == other->m_domain)
- return true;
+ canAccess = true;
}
}
-
- return false;
+
+ if (canAccess && isLocal())
+ canAccess = passesFileCheck(other);
+
+ return canAccess;
+}
+
+bool SecurityOrigin::passesFileCheck(const SecurityOrigin* other) const
+{
+ ASSERT(isLocal() && other->isLocal());
+
+ if (!m_enforceFilePathSeparation && !other->m_enforceFilePathSeparation)
+ return true;
+
+ return (m_filePath == other->m_filePath);
}
bool SecurityOrigin::canRequest(const KURL& url) const
@@ -229,12 +261,8 @@ bool SecurityOrigin::canRequest(const KURL& url) const
if (isSameSchemeHostPort(targetOrigin.get()))
return true;
- if (OriginAccessWhiteList* list = originAccessMap().get(toString())) {
- for (size_t i = 0; i < list->size(); ++i) {
- if (list->at(i).matchesOrigin(*targetOrigin))
- return true;
- }
- }
+ if (isAccessWhiteListed(targetOrigin.get()))
+ return true;
return false;
}
@@ -256,15 +284,32 @@ bool SecurityOrigin::taintsCanvas(const KURL& url) const
return true;
}
+bool SecurityOrigin::isAccessWhiteListed(const SecurityOrigin* targetOrigin) const
+{
+ if (OriginAccessWhiteList* list = originAccessMap().get(toString())) {
+ for (size_t i = 0; i < list->size(); ++i) {
+ if (list->at(i).matchesOrigin(*targetOrigin))
+ return true;
+ }
+ }
+ return false;
+}
+
bool SecurityOrigin::canLoad(const KURL& url, const String& referrer, Document* document)
{
if (!shouldTreatURLAsLocal(url.string()))
return true;
- // If we were provided a document, we let its local file policy dictate the result,
- // otherwise we allow local loads only if the supplied referrer is also local.
- if (document)
- return document->securityOrigin()->canLoadLocalResources();
+ // If we were provided a document, we first check if the access has been white listed.
+ // Then we let its local file police dictate the result.
+ // Otherwise we allow local loads only if the supplied referrer is also local.
+ if (document) {
+ SecurityOrigin* documentOrigin = document->securityOrigin();
+ RefPtr<SecurityOrigin> targetOrigin = SecurityOrigin::create(url);
+ if (documentOrigin->isAccessWhiteListed(targetOrigin.get()))
+ return true;
+ return documentOrigin->canLoadLocalResources();
+ }
if (!referrer.isEmpty())
return shouldTreatURLAsLocal(referrer);
return false;
@@ -286,9 +331,10 @@ void SecurityOrigin::grantUniversalAccess()
m_universalAccess = true;
}
-void SecurityOrigin::makeUnique()
+void SecurityOrigin::enforceFilePathSeparation()
{
- m_isUnique = true;
+ ASSERT(isLocal());
+ m_enforceFilePathSeparation = true;
}
bool SecurityOrigin::isLocal() const
@@ -314,8 +360,11 @@ String SecurityOrigin::toString() const
if (isUnique())
return "null";
- if (m_protocol == "file")
- return String("file://");
+ if (m_protocol == "file") {
+ if (m_enforceFilePathSeparation)
+ return "null";
+ return "file://";
+ }
Vector<UChar> result;
result.reserveInitialCapacity(m_protocol.length() + m_host.length() + 10);
@@ -368,13 +417,92 @@ PassRefPtr<SecurityOrigin> SecurityOrigin::createFromDatabaseIdentifier(const St
// 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(KURL(KURL(), protocol + "://" + host + ":" + String::number(port)));
}
+// The following lower-ASCII characters need escaping to be used in a filename
+// across all systems, including Windows:
+// - Unprintable ASCII (00-1F)
+// - Space (20)
+// - Double quote (22)
+// - Percent (25) (escaped because it is our escape character)
+// - Asterisk (2A)
+// - Slash (2F)
+// - Colon (3A)
+// - Less-than (3C)
+// - Greater-than (3E)
+// - Question Mark (3F)
+// - Backslash (5C)
+// - Pipe (7C)
+// - Delete (7F)
+
+static const bool needsEscaping[128] = {
+ /* 00-07 */ true, true, true, true, true, true, true, true,
+ /* 08-0F */ true, true, true, true, true, true, true, true,
+
+ /* 10-17 */ true, true, true, true, true, true, true, true,
+ /* 18-1F */ true, true, true, true, true, true, true, true,
+
+ /* 20-27 */ true, false, true, false, false, true, false, false,
+ /* 28-2F */ false, false, true, false, false, false, false, true,
+
+ /* 30-37 */ false, false, false, false, false, false, false, false,
+ /* 38-3F */ false, false, true, false, true, false, true, true,
+
+ /* 40-47 */ false, false, false, false, false, false, false, false,
+ /* 48-4F */ false, false, false, false, false, false, false, false,
+
+ /* 50-57 */ false, false, false, false, false, false, false, false,
+ /* 58-5F */ false, false, false, false, true, false, false, false,
+
+ /* 60-67 */ false, false, false, false, false, false, false, false,
+ /* 68-6F */ false, false, false, false, false, false, false, false,
+
+ /* 70-77 */ false, false, false, false, false, false, false, false,
+ /* 78-7F */ false, false, false, false, true, false, false, true,
+};
+
+static inline bool shouldEscapeUChar(UChar c)
+{
+ return c > 127 ? false : needsEscaping[c];
+}
+
+static const char hexDigits[17] = "0123456789ABCDEF";
+
+static String encodedHost(const String& host)
+{
+ unsigned length = host.length();
+ Vector<UChar, 512> buffer(length * 3 + 1);
+ UChar* p = buffer.data();
+
+ const UChar* str = host.characters();
+ const UChar* strEnd = str + length;
+
+ while (str < strEnd) {
+ UChar c = *str++;
+ if (shouldEscapeUChar(c)) {
+ *p++ = '%';
+ *p++ = hexDigits[(c >> 4) & 0xF];
+ *p++ = hexDigits[c & 0xF];
+ } else
+ *p++ = c;
+ }
+
+ ASSERT(p - buffer.data() <= static_cast<int>(buffer.size()));
+
+ return String(buffer.data(), p - buffer.data());
+}
+
String SecurityOrigin::databaseIdentifier() const
{
- DEFINE_STATIC_LOCAL(String, separatorString, (&SeparatorCharacter, 1));
- return m_protocol + separatorString + m_host + separatorString + String::number(m_port);
+ String separatorString(&SeparatorCharacter, 1);
+
+ if (m_encodedHost.isEmpty())
+ m_encodedHost = encodedHost(m_host);
+
+ return m_protocol + separatorString + m_encodedHost + separatorString + String::number(m_port);
}
bool SecurityOrigin::equal(const SecurityOrigin* other) const
@@ -405,6 +533,9 @@ bool SecurityOrigin::isSameSchemeHostPort(const SecurityOrigin* other) const
if (m_port != other->m_port)
return false;
+ if (isLocal() && !passesFileCheck(other))
+ return false;
+
return true;
}
@@ -477,6 +608,16 @@ bool SecurityOrigin::shouldTreatURLSchemeAsNoAccess(const String& scheme)
return schemesWithUniqueOrigins().contains(scheme);
}
+void SecurityOrigin::registerURLSchemeAsSecure(const String& scheme)
+{
+ secureSchemes().add(scheme);
+}
+
+bool SecurityOrigin::shouldTreatURLSchemeAsSecure(const String& scheme)
+{
+ return secureSchemes().contains(scheme);
+}
+
bool SecurityOrigin::shouldHideReferrer(const KURL& url, const String& referrer)
{
bool referrerIsSecureURL = protocolIs(referrer, "https");
@@ -508,7 +649,7 @@ bool SecurityOrigin::allowSubstituteDataAccessToLocal()
return localLoadPolicy != SecurityOrigin::AllowLocalLoadsForLocalOnly;
}
-void SecurityOrigin::whiteListAccessFromOrigin(const SecurityOrigin& sourceOrigin, const String& destinationProtocol, const String& destinationDomains, bool allowDestinationSubdomains)
+void SecurityOrigin::addOriginAccessWhitelistEntry(const SecurityOrigin& sourceOrigin, const String& destinationProtocol, const String& destinationDomains, bool allowDestinationSubdomains)
{
ASSERT(isMainThread());
ASSERT(!sourceOrigin.isEmpty());
@@ -516,15 +657,42 @@ void SecurityOrigin::whiteListAccessFromOrigin(const SecurityOrigin& sourceOrigi
return;
String sourceString = sourceOrigin.toString();
- OriginAccessWhiteList* list = originAccessMap().get(sourceString);
- if (!list) {
- list = new OriginAccessWhiteList;
- originAccessMap().set(sourceString, list);
- }
+ pair<OriginAccessMap::iterator, bool> result = originAccessMap().add(sourceString, 0);
+ if (result.second)
+ result.first->second = new OriginAccessWhiteList;
+
+ OriginAccessWhiteList* list = result.first->second;
list->append(OriginAccessEntry(destinationProtocol, destinationDomains, allowDestinationSubdomains ? OriginAccessEntry::AllowSubdomains : OriginAccessEntry::DisallowSubdomains));
}
-void SecurityOrigin::resetOriginAccessWhiteLists()
+void SecurityOrigin::removeOriginAccessWhitelistEntry(const SecurityOrigin& sourceOrigin, const String& destinationProtocol, const String& destinationDomains, bool allowDestinationSubdomains)
+{
+ ASSERT(isMainThread());
+ ASSERT(!sourceOrigin.isEmpty());
+ if (sourceOrigin.isEmpty())
+ return;
+
+ String sourceString = sourceOrigin.toString();
+ OriginAccessMap& map = originAccessMap();
+ OriginAccessMap::iterator it = map.find(sourceString);
+ if (it == map.end())
+ return;
+
+ OriginAccessWhiteList* list = it->second;
+ size_t index = list->find(OriginAccessEntry(destinationProtocol, destinationDomains, allowDestinationSubdomains ? OriginAccessEntry::AllowSubdomains : OriginAccessEntry::DisallowSubdomains));
+ if (index == notFound)
+ return;
+
+ list->remove(index);
+
+ if (!list->isEmpty())
+ return;
+
+ map.remove(it);
+ delete list;
+}
+
+void SecurityOrigin::resetOriginAccessWhitelists()
{
ASSERT(isMainThread());
OriginAccessMap& map = originAccessMap();