blob: 47ab8a0c695da3767a3c4cb8221f503e96fa97ef [file] [log] [blame]
/*
* Copyright (C) 2007 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 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
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "SecurityOrigin.h"
#include "Document.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameTree.h"
#include "KURL.h"
#include "PlatformString.h"
namespace WebCore {
static bool isDefaultPortForProtocol(unsigned short port, String protocol)
{
if (protocol.isEmpty())
return false;
static HashMap<String, unsigned> defaultPorts;
if (defaultPorts.isEmpty()) {
defaultPorts.set("http", 80);
defaultPorts.set("https", 443);
defaultPorts.set("ftp", 21);
defaultPorts.set("ftps", 990);
}
return defaultPorts.get(protocol) == port;
}
SecurityOrigin::SecurityOrigin(const String& protocol, const String& host, unsigned short port)
: m_protocol(protocol.isNull() ? "" : protocol.lower())
, m_host(host.isNull() ? "" : host.lower())
, m_port(port)
, m_portSet(port)
, m_noAccess(false)
, m_domainWasSetInDOM(false)
{
// These protocols do not create security origins; the owner frame provides the origin
if (m_protocol == "about" || m_protocol == "javascript")
m_protocol = "";
// data: URLs are not allowed access to anything other than themselves.
if (m_protocol == "data")
m_noAccess = true;
if (isDefaultPortForProtocol(m_port, m_protocol)) {
m_port = 0;
m_portSet = false;
}
}
bool SecurityOrigin::isEmpty() const
{
return m_protocol.isEmpty();
}
PassRefPtr<SecurityOrigin> SecurityOrigin::create(const String& protocol, const String& host, unsigned short port, SecurityOrigin* ownerFrameOrigin)
{
RefPtr<SecurityOrigin> origin = new SecurityOrigin(protocol, host, port);
// If we do not obtain a meaningful origin from the URL, then we try to find one
// via the frame hierarchy.
// We alias the SecurityOrigins to match Firefox, see Bug 15313
// http://bugs.webkit.org/show_bug.cgi?id=15313
if (origin->isEmpty() && ownerFrameOrigin)
return ownerFrameOrigin;
return origin.release();
}
PassRefPtr<SecurityOrigin> SecurityOrigin::createForFrame(Frame* frame)
{
if (!frame)
return create("", "", 0, 0);
FrameLoader* loader = frame->loader();
KURL url = loader->url();
Frame* ownerFrame = frame->tree()->parent();
if (!ownerFrame)
ownerFrame = loader->opener();
SecurityOrigin* ownerFrameOrigin = 0;
if (ownerFrame && ownerFrame->document())
ownerFrameOrigin = ownerFrame->document()->securityOrigin();
return create(url.protocol(), url.host(), url.port(), ownerFrameOrigin);
}
PassRefPtr<SecurityOrigin> SecurityOrigin::copy()
{
return create(m_protocol.copy(), m_host.copy(), m_port, 0);
}
void SecurityOrigin::setDomainFromDOM(const String& newDomain)
{
m_domainWasSetInDOM = true;
m_host = newDomain.lower();
}
bool SecurityOrigin::canAccess(const SecurityOrigin* other, Reason& reason) const
{
if (FrameLoader::shouldTreatSchemeAsLocal(m_protocol))
return true;
if (m_noAccess || other->m_noAccess) {
reason = SecurityOrigin::GenericMismatch;
return false;
}
// Here are three cases where we should permit access:
//
// 1) Neither document has set document.domain. In this case, we insist
// that the scheme, host, and port of the URLs match.
//
// 2) Both documents have set document.domain. In this case, we insist
// that the documents have set document.domain to the same value and
// that the scheme of the URLs match.
//
// 3) As a special case if only one of the documents has set document.domain but
// there is a host and port match we deny access but signal this to the client.
// In this case Window::allowsAccessFrom() will recheck against the lexical global
// object and allow access if that check passes.
//
// This matches the behavior of Firefox 2 and Internet Explorer 6.
//
// Internet Explorer 7 and Opera 9 are more strict in that they require
// the port numbers to match when both pages have document.domain set.
//
// FIXME: Evaluate whether we can tighten this policy to require matched
// port numbers.
//
// Opera 9 allows access when only one page has set document.domain, but
// this is a security vulnerability.
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;
} else if (m_domainWasSetInDOM && other->m_domainWasSetInDOM) {
if (m_host == other->m_host)
return true;
} else {
if (m_host == other->m_host && m_port == other->m_port) {
reason = DomainSetInDOMMismatch;
return false;
}
}
}
reason = SecurityOrigin::GenericMismatch;
return false;
}
bool SecurityOrigin::isSecureTransitionTo(const KURL& url) const
{
// New window created by the application
if (isEmpty())
return true;
if (FrameLoader::shouldTreatSchemeAsLocal(m_protocol))
return true;
return equalIgnoringCase(m_host, String(url.host())) && equalIgnoringCase(m_protocol, String(url.protocol())) && m_port == url.port();
}
String SecurityOrigin::toString() const
{
return m_protocol + ":" + m_host + ":" + String::number(m_port);
}
static const char SeparatorCharacter = '_';
PassRefPtr<SecurityOrigin> SecurityOrigin::createFromIdentifier(const String& stringIdentifier)
{
// Make sure there's a first separator
int separator1 = stringIdentifier.find(SeparatorCharacter);
if (separator1 == -1)
return create("", "", 0, 0);
// Make sure there's a second separator
int separator2 = stringIdentifier.find(SeparatorCharacter, separator1 + 1);
if (separator2 == -1)
return create("", "", 0, 0);
// Make sure there's not a third separator
if (stringIdentifier.reverseFind(SeparatorCharacter) != separator2)
return create("", "", 0, 0);
// Make sure the port section is a valid port number or doesn't exist
bool portOkay;
int port = stringIdentifier.right(stringIdentifier.length() - separator2 - 1).toInt(&portOkay);
if (!portOkay && separator2 + 1 == static_cast<int>(stringIdentifier.length()))
return create("", "", 0, 0);
if (port < 0 || port > 65535)
return create("", "", 0, 0);
// Split out the 3 sections of data
String protocol = stringIdentifier.substring(0, separator1);
String host = stringIdentifier.substring(separator1 + 1, separator2 - separator1 - 1);
return create(protocol, host, port, 0);
}
String SecurityOrigin::stringIdentifier() const
{
static String separatorString = String(&SeparatorCharacter, 1);
return m_protocol + separatorString + m_host + separatorString + String::number(m_port);
}
} // namespace WebCore