1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is third party utility code.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * the Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2010
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Daniel Witte (dwitte@mozilla.com)
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "ThirdPartyUtil.h"
39 : #include "nsNetUtil.h"
40 : #include "nsIServiceManager.h"
41 : #include "nsIHttpChannelInternal.h"
42 : #include "nsIDOMWindow.h"
43 : #include "nsILoadContext.h"
44 : #include "nsIPrincipal.h"
45 : #include "nsIScriptObjectPrincipal.h"
46 : #include "nsThreadUtils.h"
47 :
48 52 : NS_IMPL_ISUPPORTS1(ThirdPartyUtil, mozIThirdPartyUtil)
49 :
50 : nsresult
51 4 : ThirdPartyUtil::Init()
52 : {
53 4 : NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_AVAILABLE);
54 :
55 : nsresult rv;
56 4 : mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
57 4 : return rv;
58 : }
59 :
60 : // Determine if aFirstDomain is a different base domain to aSecondURI; or, if
61 : // the concept of base domain does not apply, determine if the two hosts are not
62 : // string-identical.
63 : nsresult
64 25 : ThirdPartyUtil::IsThirdPartyInternal(const nsCString& aFirstDomain,
65 : nsIURI* aSecondURI,
66 : bool* aResult)
67 : {
68 25 : NS_ASSERTION(aSecondURI, "null URI!");
69 :
70 : // Get the base domain for aSecondURI.
71 50 : nsCString secondDomain;
72 25 : nsresult rv = GetBaseDomain(aSecondURI, secondDomain);
73 25 : if (NS_FAILED(rv))
74 0 : return rv;
75 :
76 : // Check strict equality.
77 25 : *aResult = aFirstDomain != secondDomain;
78 25 : return NS_OK;
79 : }
80 :
81 : // Get the URI associated with a window.
82 : already_AddRefed<nsIURI>
83 0 : ThirdPartyUtil::GetURIFromWindow(nsIDOMWindow* aWin)
84 : {
85 0 : nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(aWin);
86 0 : NS_ENSURE_TRUE(scriptObjPrin, NULL);
87 :
88 0 : nsIPrincipal* prin = scriptObjPrin->GetPrincipal();
89 0 : NS_ENSURE_TRUE(prin, NULL);
90 :
91 0 : nsCOMPtr<nsIURI> result;
92 0 : prin->GetURI(getter_AddRefs(result));
93 0 : return result.forget();
94 : }
95 :
96 : // Determine if aFirstURI is third party with respect to aSecondURI. See docs
97 : // for mozIThirdPartyUtil.
98 : NS_IMETHODIMP
99 9 : ThirdPartyUtil::IsThirdPartyURI(nsIURI* aFirstURI,
100 : nsIURI* aSecondURI,
101 : bool* aResult)
102 : {
103 9 : NS_ENSURE_ARG(aFirstURI);
104 7 : NS_ENSURE_ARG(aSecondURI);
105 6 : NS_ASSERTION(aResult, "null outparam pointer");
106 :
107 12 : nsCString firstHost;
108 6 : nsresult rv = GetBaseDomain(aFirstURI, firstHost);
109 6 : if (NS_FAILED(rv))
110 0 : return rv;
111 :
112 6 : return IsThirdPartyInternal(firstHost, aSecondURI, aResult);
113 : }
114 :
115 : // Determine if any URI of the window hierarchy of aWindow is foreign with
116 : // respect to aSecondURI. See docs for mozIThirdPartyUtil.
117 : NS_IMETHODIMP
118 0 : ThirdPartyUtil::IsThirdPartyWindow(nsIDOMWindow* aWindow,
119 : nsIURI* aURI,
120 : bool* aResult)
121 : {
122 0 : NS_ENSURE_ARG(aWindow);
123 0 : NS_ASSERTION(aResult, "null outparam pointer");
124 :
125 : bool result;
126 :
127 : // Get the URI of the window, and its base domain.
128 0 : nsCOMPtr<nsIURI> currentURI = GetURIFromWindow(aWindow);
129 0 : NS_ENSURE_TRUE(currentURI, NS_ERROR_INVALID_ARG);
130 :
131 0 : nsCString bottomDomain;
132 0 : nsresult rv = GetBaseDomain(currentURI, bottomDomain);
133 0 : if (NS_FAILED(rv))
134 0 : return rv;
135 :
136 0 : if (aURI) {
137 : // Determine whether aURI is foreign with respect to currentURI.
138 0 : rv = IsThirdPartyInternal(bottomDomain, aURI, &result);
139 0 : if (NS_FAILED(rv))
140 0 : return rv;
141 :
142 0 : if (result) {
143 0 : *aResult = true;
144 0 : return NS_OK;
145 : }
146 : }
147 :
148 0 : nsCOMPtr<nsIDOMWindow> current = aWindow, parent;
149 0 : nsCOMPtr<nsIURI> parentURI;
150 0 : do {
151 0 : rv = current->GetParent(getter_AddRefs(parent));
152 0 : NS_ENSURE_SUCCESS(rv, rv);
153 :
154 0 : if (SameCOMIdentity(parent, current)) {
155 : // We're at the topmost content window. We already know the answer.
156 0 : *aResult = false;
157 0 : return NS_OK;
158 : }
159 :
160 0 : parentURI = GetURIFromWindow(parent);
161 0 : NS_ENSURE_TRUE(parentURI, NS_ERROR_INVALID_ARG);
162 :
163 0 : rv = IsThirdPartyInternal(bottomDomain, parentURI, &result);
164 0 : if (NS_FAILED(rv))
165 0 : return rv;
166 :
167 0 : if (result) {
168 0 : *aResult = true;
169 0 : return NS_OK;
170 : }
171 :
172 0 : current = parent;
173 0 : currentURI = parentURI;
174 : } while (1);
175 :
176 : NS_NOTREACHED("should've returned");
177 : return NS_ERROR_UNEXPECTED;
178 : }
179 :
180 : // Determine if the URI associated with aChannel or any URI of the window
181 : // hierarchy associated with the channel is foreign with respect to aSecondURI.
182 : // See docs for mozIThirdPartyUtil.
183 : NS_IMETHODIMP
184 34 : ThirdPartyUtil::IsThirdPartyChannel(nsIChannel* aChannel,
185 : nsIURI* aURI,
186 : bool* aResult)
187 : {
188 34 : NS_ENSURE_ARG(aChannel);
189 21 : NS_ASSERTION(aResult, "null outparam pointer");
190 :
191 : nsresult rv;
192 21 : bool doForce = false;
193 : nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
194 42 : do_QueryInterface(aChannel);
195 21 : if (httpChannelInternal) {
196 21 : rv = httpChannelInternal->GetForceAllowThirdPartyCookie(&doForce);
197 21 : NS_ENSURE_SUCCESS(rv, rv);
198 :
199 : // If aURI was not supplied, and we're forcing, then we're by definition
200 : // not foreign. If aURI was supplied, we still want to check whether it's
201 : // foreign with respect to the channel URI. (The forcing only applies to
202 : // whatever window hierarchy exists above the channel.)
203 21 : if (doForce && !aURI) {
204 1 : *aResult = false;
205 1 : return NS_OK;
206 : }
207 : }
208 :
209 : // Obtain the URI from the channel, and its base domain.
210 40 : nsCOMPtr<nsIURI> channelURI;
211 20 : aChannel->GetURI(getter_AddRefs(channelURI));
212 20 : NS_ENSURE_TRUE(channelURI, NS_ERROR_INVALID_ARG);
213 :
214 40 : nsCString channelDomain;
215 20 : rv = GetBaseDomain(channelURI, channelDomain);
216 20 : if (NS_FAILED(rv))
217 0 : return rv;
218 :
219 20 : if (aURI) {
220 : // Determine whether aURI is foreign with respect to channelURI.
221 : bool result;
222 19 : rv = IsThirdPartyInternal(channelDomain, aURI, &result);
223 19 : if (NS_FAILED(rv))
224 0 : return rv;
225 :
226 : // If it's foreign, or we're forcing, we're done.
227 19 : if (result || doForce) {
228 16 : *aResult = result;
229 16 : return NS_OK;
230 : }
231 : }
232 :
233 : // Find the associated window and its parent window.
234 8 : nsCOMPtr<nsILoadContext> ctx;
235 4 : NS_QueryNotificationCallbacks(aChannel, ctx);
236 4 : if (!ctx) return NS_ERROR_INVALID_ARG;
237 :
238 : // If there is no window, the consumer kicking off the load didn't provide one
239 : // to the channel. This is limited to loads of certain types of resources. If
240 : // those loads require cookies, the forceAllowThirdPartyCookie property should
241 : // be set on the channel.
242 0 : nsCOMPtr<nsIDOMWindow> ourWin, parentWin;
243 0 : ctx->GetAssociatedWindow(getter_AddRefs(ourWin));
244 0 : if (!ourWin) return NS_ERROR_INVALID_ARG;
245 :
246 0 : ourWin->GetParent(getter_AddRefs(parentWin));
247 0 : NS_ENSURE_TRUE(parentWin, NS_ERROR_INVALID_ARG);
248 :
249 : // Check whether this is the document channel for this window (representing a
250 : // load of a new page). In that situation we want to avoid comparing
251 : // channelURI to ourWin, since what's in ourWin right now will be replaced as
252 : // the channel loads. This covers the case of a freshly kicked-off load
253 : // (e.g. the user typing something in the location bar, or clicking on a
254 : // bookmark), where the window's URI hasn't yet been set, and will be bogus.
255 : // It also covers situations where a subframe is navigated to someting that
256 : // is same-origin with all its ancestors. This is a bit of a nasty hack, but
257 : // we will hopefully flag these channels better later.
258 : nsLoadFlags flags;
259 0 : rv = aChannel->GetLoadFlags(&flags);
260 0 : NS_ENSURE_SUCCESS(rv, rv);
261 :
262 0 : if (flags & nsIChannel::LOAD_DOCUMENT_URI) {
263 0 : if (SameCOMIdentity(ourWin, parentWin)) {
264 : // We only need to compare aURI to the channel URI -- the window's will be
265 : // bogus. We already know the answer.
266 0 : *aResult = false;
267 0 : return NS_OK;
268 : }
269 :
270 : // Make sure to still compare to ourWin's ancestors
271 0 : ourWin = parentWin;
272 : }
273 :
274 : // Check the window hierarchy. This covers most cases for an ordinary page
275 : // load from the location bar.
276 0 : return IsThirdPartyWindow(ourWin, channelURI, aResult);
277 : }
278 :
279 : // Get the base domain for aHostURI; e.g. for "www.bbc.co.uk", this would be
280 : // "bbc.co.uk". Only properly-formed URI's are tolerated, though a trailing
281 : // dot may be present. If aHostURI is an IP address, an alias such as
282 : // 'localhost', an eTLD such as 'co.uk', or the empty string, aBaseDomain will
283 : // be the exact host. The result of this function should only be used in exact
284 : // string comparisons, since substring comparisons will not be valid for the
285 : // special cases elided above.
286 : NS_IMETHODIMP
287 51 : ThirdPartyUtil::GetBaseDomain(nsIURI* aHostURI,
288 : nsACString& aBaseDomain)
289 : {
290 : // Get the base domain. this will fail if the host contains a leading dot,
291 : // more than one trailing dot, or is otherwise malformed.
292 51 : nsresult rv = mTLDService->GetBaseDomain(aHostURI, 0, aBaseDomain);
293 51 : if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
294 : rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
295 : // aHostURI is either an IP address, an alias such as 'localhost', an eTLD
296 : // such as 'co.uk', or the empty string. Uses the normalized host in such
297 : // cases.
298 11 : rv = aHostURI->GetAsciiHost(aBaseDomain);
299 : }
300 51 : NS_ENSURE_SUCCESS(rv, rv);
301 :
302 : // aHostURI (and thus aBaseDomain) may be the string '.'. If so, fail.
303 51 : if (aBaseDomain.Length() == 1 && aBaseDomain.Last() == '.')
304 0 : return NS_ERROR_INVALID_ARG;
305 :
306 : // Reject any URIs without a host that aren't file:// URIs. This makes it the
307 : // only way we can get a base domain consisting of the empty string, which
308 : // means we can safely perform foreign tests on such URIs where "not foreign"
309 : // means "the involved URIs are all file://".
310 51 : if (aBaseDomain.IsEmpty()) {
311 5 : bool isFileURI = false;
312 5 : aHostURI->SchemeIs("file", &isFileURI);
313 5 : NS_ENSURE_TRUE(isFileURI, NS_ERROR_INVALID_ARG);
314 : }
315 :
316 51 : return NS_OK;
317 : }
|