File indexing completed on 2024-05-05 16:11:43

0001 /*
0002  * Copyright (C) 2007 Apple Inc. All rights reserved.
0003  *
0004  * Redistribution and use in source and binary forms, with or without
0005  * modification, are permitted provided that the following conditions
0006  * are met:
0007  *
0008  * 1.  Redistributions of source code must retain the above copyright
0009  *     notice, this list of conditions and the following disclaimer.
0010  * 2.  Redistributions in binary form must reproduce the above copyright
0011  *     notice, this list of conditions and the following disclaimer in the
0012  *     documentation and/or other materials provided with the distribution.
0013  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
0014  *     its contributors may be used to endorse or promote products derived
0015  *     from this software without specific prior written permission.
0016  *
0017  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
0018  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
0019  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0020  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
0021  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
0022  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0023  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0024  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0027  */
0028 
0029 #include "security_origin.h"
0030 
0031 #include <wtf/RefPtr.h>
0032 
0033 #include <kprotocolinfo.h>
0034 
0035 namespace khtml
0036 {
0037 
0038 static bool isDefaultPortForProtocol(unsigned short port, const QString &proto)
0039 {
0040     return (port == 80  && proto == QLatin1String("http")) ||
0041            (port == 443 && proto == QLatin1String("https"));
0042 }
0043 
0044 SecurityOrigin::SecurityOrigin(const QUrl &url) :
0045     m_protocol(url.scheme())
0046     , m_host(url.host().toLower())
0047     , m_port(url.port())
0048     , m_domainWasSetInDOM(false)
0049     , m_isUnique(false)
0050 {
0051     // These protocols do not create security origins; the owner frame provides the origin
0052     if (m_protocol == "about" || m_protocol == "javascript") {
0053         m_protocol = "";
0054     }
0055 
0056     // For edge case URLs that were probably misparsed, make sure that the origin is unique.
0057     if (m_host.isEmpty() && !m_protocol.isEmpty() && KProtocolInfo::protocolClass(m_protocol) == QLatin1String(":internet")) {
0058         m_isUnique = true;
0059     }
0060 
0061     // document.domain starts as m_host, but can be set by the DOM.
0062     m_domain = m_host;
0063 
0064     if (url.port() == -1 || isDefaultPortForProtocol(m_port, m_protocol)) {
0065         m_port = 0;
0066     }
0067 }
0068 
0069 SecurityOrigin::SecurityOrigin(const SecurityOrigin *other) :
0070     m_protocol(other->m_protocol)
0071     , m_host(other->m_host)
0072     , m_domain(other->m_domain)
0073     , m_port(other->m_port)
0074     , m_domainWasSetInDOM(other->m_domainWasSetInDOM)
0075     , m_isUnique(other->m_isUnique)
0076 {
0077 }
0078 
0079 bool SecurityOrigin::isEmpty() const
0080 {
0081     return m_protocol.isEmpty();
0082 }
0083 
0084 SecurityOrigin *SecurityOrigin::create(const QUrl &url)
0085 {
0086     if (!url.isValid()) {
0087         return new SecurityOrigin(QUrl());
0088     }
0089     return new SecurityOrigin(url);
0090 }
0091 
0092 SecurityOrigin *SecurityOrigin::createEmpty()
0093 {
0094     return create(QUrl());
0095 }
0096 
0097 void SecurityOrigin::setDomainFromDOM(const QString &newDomain)
0098 {
0099     m_domainWasSetInDOM = true;
0100     m_domain = newDomain.toLower();
0101 }
0102 
0103 bool SecurityOrigin::canAccess(const SecurityOrigin *other) const
0104 {
0105     if (isUnique() || other->isUnique()) {
0106         return false;
0107     }
0108 
0109     // Here are two cases where we should permit access:
0110     //
0111     // 1) Neither document has set document.domain. In this case, we insist
0112     //    that the scheme, host, and port of the URLs match.
0113     //
0114     // 2) Both documents have set document.domain. In this case, we insist
0115     //    that the documents have set document.domain to the same value and
0116     //    that the scheme of the URLs match.
0117     //
0118     // This matches the behavior of Firefox 2 and Internet Explorer 6.
0119     //
0120     // Internet Explorer 7 and Opera 9 are more strict in that they require
0121     // the port numbers to match when both pages have document.domain set.
0122     //
0123     // FIXME: Evaluate whether we can tighten this policy to require matched
0124     //        port numbers.
0125     //
0126     // Opera 9 allows access when only one page has set document.domain, but
0127     // this is a security vulnerability.
0128 
0129     if (m_protocol == other->m_protocol) {
0130         if (!m_domainWasSetInDOM && !other->m_domainWasSetInDOM) {
0131             if (m_host == other->m_host && m_port == other->m_port) {
0132                 return true;
0133             }
0134         } else if (m_domainWasSetInDOM && other->m_domainWasSetInDOM) {
0135             if (m_domain == other->m_domain) {
0136                 return true;
0137             }
0138         }
0139     }
0140 
0141     return false;
0142 }
0143 
0144 bool SecurityOrigin::canRequest(const QUrl &url) const
0145 {
0146     if (isUnique()) {
0147         return false;
0148     }
0149 
0150     WTF::RefPtr<SecurityOrigin> targetOrigin = SecurityOrigin::create(url);
0151     if (targetOrigin->isUnique()) {
0152         return false;
0153     }
0154 
0155     // We call isSameSchemeHostPort here instead of canAccess because we want
0156     // to ignore document.domain effects.
0157     if (isSameSchemeHostPort(targetOrigin.get())) {
0158         return true;
0159     }
0160 
0161     return false;
0162 }
0163 
0164 bool SecurityOrigin::taintsCanvas(const QUrl &url) const
0165 {
0166     if (canRequest(url)) {
0167         return false;
0168     }
0169 
0170     // This function exists because we treat data URLs as having a unique origin,
0171     // contrary to the current (9/19/2009) draft of the HTML5 specification.
0172     // We still want to let folks paint data URLs onto untainted canvases, so
0173     // we special case data URLs below. If we change to match HTML5 w.r.t.
0174     // data URL security, then we can remove this function in favor of
0175     // !canRequest.
0176     if (url.scheme() == QLatin1String("data")) {
0177         return false;
0178     }
0179 
0180     return true;
0181 }
0182 
0183 void SecurityOrigin::makeUnique()
0184 {
0185     m_isUnique = true;
0186 }
0187 
0188 QString SecurityOrigin::toString() const
0189 {
0190     if (isEmpty()) {
0191         return "null";
0192     }
0193 
0194     if (isUnique()) {
0195         return "null";
0196     }
0197 
0198     if (m_protocol == "file") {
0199         return QString("file://");
0200     }
0201 
0202     QString result;
0203     result += m_protocol;
0204     result += "://";
0205     result += m_host;
0206 
0207     if (m_port) {
0208         result += ":";
0209         result += QString::number(m_port);
0210     }
0211 
0212     return result;
0213 }
0214 
0215 SecurityOrigin *SecurityOrigin::createFromString(const QString &originString)
0216 {
0217     return SecurityOrigin::create(QUrl(originString));
0218 }
0219 
0220 bool SecurityOrigin::isSameSchemeHostPort(const SecurityOrigin *other) const
0221 {
0222     if (m_host != other->m_host) {
0223         return false;
0224     }
0225 
0226     if (m_protocol != other->m_protocol) {
0227         return false;
0228     }
0229 
0230     if (m_port != other->m_port) {
0231         return false;
0232     }
0233 
0234     return true;
0235 }
0236 
0237 } // namespace khtml