1 /*
2 * Copyright (C) 2007 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config.h"
30 #include "SecurityOrigin.h"
31
32 #include "CString.h"
33 #include "FrameLoader.h"
34 #include "KURL.h"
35 #include "PlatformString.h"
36 #include <wtf/StdLibExtras.h>
37
38 namespace WebCore {
39
isDefaultPortForProtocol(unsigned short port,const String & protocol)40 static bool isDefaultPortForProtocol(unsigned short port, const String& protocol)
41 {
42 if (protocol.isEmpty())
43 return false;
44
45 typedef HashMap<String, unsigned> DefaultPortsMap;
46 DEFINE_STATIC_LOCAL(DefaultPortsMap, defaultPorts, ());
47 if (defaultPorts.isEmpty()) {
48 defaultPorts.set("http", 80);
49 defaultPorts.set("https", 443);
50 defaultPorts.set("ftp", 21);
51 defaultPorts.set("ftps", 990);
52 }
53 return defaultPorts.get(protocol) == port;
54 }
55
SecurityOrigin(const KURL & url)56 SecurityOrigin::SecurityOrigin(const KURL& url)
57 : m_protocol(url.protocol().isNull() ? "" : url.protocol().lower())
58 , m_host(url.host().isNull() ? "" : url.host().lower())
59 , m_port(url.port())
60 , m_noAccess(false)
61 , m_domainWasSetInDOM(false)
62 {
63 // These protocols do not create security origins; the owner frame provides the origin
64 if (m_protocol == "about" || m_protocol == "javascript")
65 m_protocol = "";
66
67 // data: URLs are not allowed access to anything other than themselves.
68 if (m_protocol == "data")
69 m_noAccess = true;
70
71 // document.domain starts as m_host, but can be set by the DOM.
72 m_domain = m_host;
73
74 // By default, only local SecurityOrigins can load local resources.
75 m_canLoadLocalResources = isLocal();
76
77 if (isDefaultPortForProtocol(m_port, m_protocol))
78 m_port = 0;
79 }
80
SecurityOrigin(const SecurityOrigin * other)81 SecurityOrigin::SecurityOrigin(const SecurityOrigin* other)
82 : m_protocol(other->m_protocol.copy())
83 , m_host(other->m_host.copy())
84 , m_domain(other->m_domain.copy())
85 , m_port(other->m_port)
86 , m_noAccess(other->m_noAccess)
87 , m_domainWasSetInDOM(other->m_domainWasSetInDOM)
88 , m_canLoadLocalResources(other->m_canLoadLocalResources)
89 {
90 }
91
isEmpty() const92 bool SecurityOrigin::isEmpty() const
93 {
94 return m_protocol.isEmpty();
95 }
96
create(const KURL & url)97 PassRefPtr<SecurityOrigin> SecurityOrigin::create(const KURL& url)
98 {
99 if (!url.isValid())
100 return adoptRef(new SecurityOrigin(KURL()));
101 return adoptRef(new SecurityOrigin(url));
102 }
103
createEmpty()104 PassRefPtr<SecurityOrigin> SecurityOrigin::createEmpty()
105 {
106 return create(KURL());
107 }
108
copy()109 PassRefPtr<SecurityOrigin> SecurityOrigin::copy()
110 {
111 return adoptRef(new SecurityOrigin(this));
112 }
113
setDomainFromDOM(const String & newDomain)114 void SecurityOrigin::setDomainFromDOM(const String& newDomain)
115 {
116 m_domainWasSetInDOM = true;
117 m_domain = newDomain.lower();
118 }
119
canAccess(const SecurityOrigin * other) const120 bool SecurityOrigin::canAccess(const SecurityOrigin* other) const
121 {
122 if (isLocal())
123 return true;
124
125 if (m_noAccess || other->m_noAccess)
126 return false;
127
128 // Here are two cases where we should permit access:
129 //
130 // 1) Neither document has set document.domain. In this case, we insist
131 // that the scheme, host, and port of the URLs match.
132 //
133 // 2) Both documents have set document.domain. In this case, we insist
134 // that the documents have set document.domain to the same value and
135 // that the scheme of the URLs match.
136 //
137 // This matches the behavior of Firefox 2 and Internet Explorer 6.
138 //
139 // Internet Explorer 7 and Opera 9 are more strict in that they require
140 // the port numbers to match when both pages have document.domain set.
141 //
142 // FIXME: Evaluate whether we can tighten this policy to require matched
143 // port numbers.
144 //
145 // Opera 9 allows access when only one page has set document.domain, but
146 // this is a security vulnerability.
147
148 if (m_protocol == other->m_protocol) {
149 if (!m_domainWasSetInDOM && !other->m_domainWasSetInDOM) {
150 if (m_host == other->m_host && m_port == other->m_port)
151 return true;
152 } else if (m_domainWasSetInDOM && other->m_domainWasSetInDOM) {
153 if (m_domain == other->m_domain)
154 return true;
155 }
156 }
157
158 return false;
159 }
160
canRequest(const KURL & url) const161 bool SecurityOrigin::canRequest(const KURL& url) const
162 {
163 if (isLocal())
164 return true;
165
166 if (m_noAccess)
167 return false;
168
169 RefPtr<SecurityOrigin> targetOrigin = SecurityOrigin::create(url);
170
171 // We call isSameSchemeHostPort here instead of canAccess because we want
172 // to ignore document.domain effects.
173 return isSameSchemeHostPort(targetOrigin.get());
174 }
175
grantLoadLocalResources()176 void SecurityOrigin::grantLoadLocalResources()
177 {
178 // This method exists only to support backwards compatibility with older
179 // versions of WebKit. Granting privileges to some, but not all, documents
180 // in a SecurityOrigin is a security hazard because the documents without
181 // the privilege can obtain the privilege by injecting script into the
182 // documents that have been granted the privilege.
183 ASSERT(FrameLoader::allowSubstituteDataAccessToLocal());
184 m_canLoadLocalResources = true;
185 }
186
isLocal() const187 bool SecurityOrigin::isLocal() const
188 {
189 return FrameLoader::shouldTreatSchemeAsLocal(m_protocol);
190 }
191
isSecureTransitionTo(const KURL & url) const192 bool SecurityOrigin::isSecureTransitionTo(const KURL& url) const
193 {
194 // New window created by the application
195 if (isEmpty())
196 return true;
197
198 RefPtr<SecurityOrigin> other = SecurityOrigin::create(url);
199 return canAccess(other.get());
200 }
201
toString() const202 String SecurityOrigin::toString() const
203 {
204 if (isEmpty())
205 return "null";
206
207 if (m_noAccess)
208 return "null";
209
210 if (m_protocol == "file")
211 return String("file://");
212
213 Vector<UChar> result;
214 result.reserveCapacity(m_protocol.length() + m_host.length() + 10);
215 append(result, m_protocol);
216 append(result, "://");
217 append(result, m_host);
218
219 if (m_port) {
220 append(result, ":");
221 append(result, String::number(m_port));
222 }
223
224 return String::adopt(result);
225 }
226
createFromString(const String & originString)227 PassRefPtr<SecurityOrigin> SecurityOrigin::createFromString(const String& originString)
228 {
229 return SecurityOrigin::create(KURL(KURL(), originString));
230 }
231
232 static const char SeparatorCharacter = '_';
233
createFromDatabaseIdentifier(const String & databaseIdentifier)234 PassRefPtr<SecurityOrigin> SecurityOrigin::createFromDatabaseIdentifier(const String& databaseIdentifier)
235 {
236 // Make sure there's a first separator
237 int separator1 = databaseIdentifier.find(SeparatorCharacter);
238 if (separator1 == -1)
239 return create(KURL());
240
241 // Make sure there's a second separator
242 int separator2 = databaseIdentifier.find(SeparatorCharacter, separator1 + 1);
243 if (separator2 == -1)
244 return create(KURL());
245
246 // Make sure there's not a third separator
247 if (databaseIdentifier.reverseFind(SeparatorCharacter) != separator2)
248 return create(KURL());
249
250 // Make sure the port section is a valid port number or doesn't exist
251 bool portOkay;
252 int port = databaseIdentifier.right(databaseIdentifier.length() - separator2 - 1).toInt(&portOkay);
253 if (!portOkay && separator2 + 1 == static_cast<int>(databaseIdentifier.length()))
254 return create(KURL());
255
256 if (port < 0 || port > 65535)
257 return create(KURL());
258
259 // Split out the 3 sections of data
260 String protocol = databaseIdentifier.substring(0, separator1);
261 String host = databaseIdentifier.substring(separator1 + 1, separator2 - separator1 - 1);
262 return create(KURL(protocol + "://" + host + ":" + String::number(port)));
263 }
264
databaseIdentifier() const265 String SecurityOrigin::databaseIdentifier() const
266 {
267 DEFINE_STATIC_LOCAL(String, separatorString, (&SeparatorCharacter, 1));
268 return m_protocol + separatorString + m_host + separatorString + String::number(m_port);
269 }
270
equal(const SecurityOrigin * other) const271 bool SecurityOrigin::equal(const SecurityOrigin* other) const
272 {
273 if (!isSameSchemeHostPort(other))
274 return false;
275
276 if (m_domainWasSetInDOM != other->m_domainWasSetInDOM)
277 return false;
278
279 if (m_domainWasSetInDOM && m_domain != other->m_domain)
280 return false;
281
282 return true;
283 }
284
isSameSchemeHostPort(const SecurityOrigin * other) const285 bool SecurityOrigin::isSameSchemeHostPort(const SecurityOrigin* other) const
286 {
287 if (m_host != other->m_host)
288 return false;
289
290 if (m_protocol != other->m_protocol)
291 return false;
292
293 if (m_port != other->m_port)
294 return false;
295
296 return true;
297 }
298
299 } // namespace WebCore
300