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 content blocker code.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Michiel van Leeuwen <mvl@exedo.nl>.
18 : * Portions created by the Initial Developer are Copyright (C) 2004
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPL"), or
25 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "nsContentBlocker.h"
38 : #include "nsIDocument.h"
39 : #include "nsIContent.h"
40 : #include "nsIURI.h"
41 : #include "nsIServiceManager.h"
42 : #include "nsIDocShellTreeItem.h"
43 : #include "nsIPrefService.h"
44 : #include "nsIPrefBranch.h"
45 : #include "nsIDocShell.h"
46 : #include "nsString.h"
47 : #include "nsContentPolicyUtils.h"
48 : #include "nsIObjectLoadingContent.h"
49 :
50 : // Possible behavior pref values
51 : // Those map to the nsIPermissionManager values where possible
52 : #define BEHAVIOR_ACCEPT nsIPermissionManager::ALLOW_ACTION
53 : #define BEHAVIOR_REJECT nsIPermissionManager::DENY_ACTION
54 : #define BEHAVIOR_NOFOREIGN 3
55 :
56 : // From nsIContentPolicy
57 : static const char *kTypeString[] = {"other",
58 : "script",
59 : "image",
60 : "stylesheet",
61 : "object",
62 : "document",
63 : "subdocument",
64 : "refresh",
65 : "xbl",
66 : "ping",
67 : "xmlhttprequest",
68 : "objectsubrequest",
69 : "dtd",
70 : "font",
71 : "media",
72 : "websocket"};
73 :
74 : #define NUMBER_OF_TYPES NS_ARRAY_LENGTH(kTypeString)
75 : PRUint8 nsContentBlocker::mBehaviorPref[NUMBER_OF_TYPES];
76 :
77 63 : NS_IMPL_ISUPPORTS3(nsContentBlocker,
78 : nsIContentPolicy,
79 : nsIObserver,
80 : nsSupportsWeakReference)
81 :
82 3 : nsContentBlocker::nsContentBlocker()
83 : {
84 3 : memset(mBehaviorPref, BEHAVIOR_ACCEPT, NUMBER_OF_TYPES);
85 3 : }
86 :
87 : nsresult
88 3 : nsContentBlocker::Init()
89 : {
90 : nsresult rv;
91 3 : mPermissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
92 3 : NS_ENSURE_SUCCESS(rv, rv);
93 :
94 6 : nsCOMPtr<nsIPrefService> prefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
95 3 : NS_ENSURE_SUCCESS(rv, rv);
96 :
97 6 : nsCOMPtr<nsIPrefBranch> prefBranch;
98 3 : rv = prefService->GetBranch("permissions.default.", getter_AddRefs(prefBranch));
99 3 : NS_ENSURE_SUCCESS(rv, rv);
100 :
101 : // Migrate old image blocker pref
102 6 : nsCOMPtr<nsIPrefBranch> oldPrefBranch;
103 3 : oldPrefBranch = do_QueryInterface(prefService);
104 : PRInt32 oldPref;
105 3 : rv = oldPrefBranch->GetIntPref("network.image.imageBehavior", &oldPref);
106 3 : if (NS_SUCCEEDED(rv) && oldPref) {
107 : PRInt32 newPref;
108 0 : switch (oldPref) {
109 : default:
110 0 : newPref = BEHAVIOR_ACCEPT;
111 0 : break;
112 : case 1:
113 0 : newPref = BEHAVIOR_NOFOREIGN;
114 0 : break;
115 : case 2:
116 0 : newPref = BEHAVIOR_REJECT;
117 0 : break;
118 : }
119 0 : prefBranch->SetIntPref("image", newPref);
120 0 : oldPrefBranch->ClearUserPref("network.image.imageBehavior");
121 : }
122 :
123 :
124 : // The branch is not a copy of the prefservice, but a new object, because
125 : // it is a non-default branch. Adding obeservers to it will only work if
126 : // we make sure that the object doesn't die. So, keep a reference to it.
127 3 : mPrefBranchInternal = do_QueryInterface(prefBranch, &rv);
128 3 : NS_ENSURE_SUCCESS(rv, rv);
129 :
130 3 : rv = mPrefBranchInternal->AddObserver("", this, true);
131 3 : PrefChanged(prefBranch, nsnull);
132 :
133 3 : return rv;
134 : }
135 :
136 : #undef LIMIT
137 : #define LIMIT(x, low, high, default) ((x) >= (low) && (x) <= (high) ? (x) : (default))
138 :
139 : void
140 3 : nsContentBlocker::PrefChanged(nsIPrefBranch *aPrefBranch,
141 : const char *aPref)
142 : {
143 : PRInt32 val;
144 :
145 : #define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P))
146 :
147 51 : for(PRUint32 i = 0; i < NUMBER_OF_TYPES; ++i) {
148 96 : if (PREF_CHANGED(kTypeString[i]) &&
149 48 : NS_SUCCEEDED(aPrefBranch->GetIntPref(kTypeString[i], &val)))
150 3 : mBehaviorPref[i] = LIMIT(val, 1, 3, 1);
151 : }
152 :
153 3 : }
154 :
155 : // nsIContentPolicy Implementation
156 : NS_IMETHODIMP
157 10 : nsContentBlocker::ShouldLoad(PRUint32 aContentType,
158 : nsIURI *aContentLocation,
159 : nsIURI *aRequestingLocation,
160 : nsISupports *aRequestingContext,
161 : const nsACString &aMimeGuess,
162 : nsISupports *aExtra,
163 : PRInt16 *aDecision)
164 : {
165 10 : *aDecision = nsIContentPolicy::ACCEPT;
166 : nsresult rv;
167 :
168 : // Ony support NUMBER_OF_TYPES content types. that all there is at the
169 : // moment, but you never know...
170 10 : if (aContentType > NUMBER_OF_TYPES)
171 0 : return NS_OK;
172 :
173 : // we can't do anything without this
174 10 : if (!aContentLocation)
175 0 : return NS_OK;
176 :
177 : // we only want to check http, https, ftp
178 : // for chrome:// and resources and others, no need to check.
179 20 : nsCAutoString scheme;
180 10 : aContentLocation->GetScheme(scheme);
181 30 : if (!scheme.LowerCaseEqualsLiteral("ftp") &&
182 10 : !scheme.LowerCaseEqualsLiteral("http") &&
183 10 : !scheme.LowerCaseEqualsLiteral("https"))
184 10 : return NS_OK;
185 :
186 : bool shouldLoad, fromPrefs;
187 : rv = TestPermission(aContentLocation, aRequestingLocation, aContentType,
188 0 : &shouldLoad, &fromPrefs);
189 0 : NS_ENSURE_SUCCESS(rv, rv);
190 0 : if (!shouldLoad) {
191 0 : if (fromPrefs) {
192 0 : *aDecision = nsIContentPolicy::REJECT_TYPE;
193 : } else {
194 0 : *aDecision = nsIContentPolicy::REJECT_SERVER;
195 : }
196 : }
197 0 : if (aContentType != nsIContentPolicy::TYPE_OBJECT || aMimeGuess.IsEmpty())
198 0 : return NS_OK;
199 :
200 : // For TYPE_OBJECT we should check what aMimeGuess might tell us
201 : // about what sort of object it is.
202 : nsCOMPtr<nsIObjectLoadingContent> objectLoader =
203 0 : do_QueryInterface(aRequestingContext);
204 0 : if (!objectLoader)
205 0 : return NS_OK;
206 :
207 : PRUint32 contentType;
208 0 : rv = objectLoader->GetContentTypeForMIMEType(aMimeGuess, &contentType);
209 0 : if (NS_FAILED(rv))
210 0 : return rv;
211 :
212 0 : switch (contentType) {
213 : case nsIObjectLoadingContent::TYPE_IMAGE:
214 0 : aContentType = nsIContentPolicy::TYPE_IMAGE;
215 0 : break;
216 : case nsIObjectLoadingContent::TYPE_DOCUMENT:
217 0 : aContentType = nsIContentPolicy::TYPE_SUBDOCUMENT;
218 0 : break;
219 : default:
220 0 : return NS_OK;
221 : }
222 :
223 0 : NS_ASSERTION(aContentType != nsIContentPolicy::TYPE_OBJECT,
224 : "Shouldn't happen. Infinite loops are bad!");
225 :
226 : // Found a type that tells us more about what we're loading. Try
227 : // the permissions check again!
228 : return ShouldLoad(aContentType, aContentLocation, aRequestingLocation,
229 0 : aRequestingContext, aMimeGuess, aExtra, aDecision);
230 : }
231 :
232 : NS_IMETHODIMP
233 0 : nsContentBlocker::ShouldProcess(PRUint32 aContentType,
234 : nsIURI *aContentLocation,
235 : nsIURI *aRequestingLocation,
236 : nsISupports *aRequestingContext,
237 : const nsACString &aMimeGuess,
238 : nsISupports *aExtra,
239 : PRInt16 *aDecision)
240 : {
241 : // For loads where aRequestingContext is chrome, we should just
242 : // accept. Those are most likely toplevel loads in windows, and
243 : // chrome generally knows what it's doing anyway.
244 : nsCOMPtr<nsIDocShellTreeItem> item =
245 0 : do_QueryInterface(NS_CP_GetDocShellFromContext(aRequestingContext));
246 :
247 0 : if (item) {
248 : PRInt32 type;
249 0 : item->GetItemType(&type);
250 0 : if (type == nsIDocShellTreeItem::typeChrome) {
251 0 : *aDecision = nsIContentPolicy::ACCEPT;
252 0 : return NS_OK;
253 : }
254 : }
255 :
256 : // This isn't a load from chrome. Just do a ShouldLoad() check --
257 : // we want the same answer here
258 : return ShouldLoad(aContentType, aContentLocation, aRequestingLocation,
259 0 : aRequestingContext, aMimeGuess, aExtra, aDecision);
260 : }
261 :
262 : nsresult
263 0 : nsContentBlocker::TestPermission(nsIURI *aCurrentURI,
264 : nsIURI *aFirstURI,
265 : PRInt32 aContentType,
266 : bool *aPermission,
267 : bool *aFromPrefs)
268 : {
269 0 : *aFromPrefs = false;
270 : // This default will also get used if there is an unknown value in the
271 : // permission list, or if the permission manager returns unknown values.
272 0 : *aPermission = true;
273 :
274 : // check the permission list first; if we find an entry, it overrides
275 : // default prefs.
276 : // Don't forget the aContentType ranges from 1..8, while the
277 : // array is indexed 0..7
278 : PRUint32 permission;
279 0 : nsresult rv = mPermissionManager->TestPermission(aCurrentURI,
280 : kTypeString[aContentType - 1],
281 0 : &permission);
282 0 : NS_ENSURE_SUCCESS(rv, rv);
283 :
284 : // If there is nothing on the list, use the default.
285 0 : if (!permission) {
286 0 : permission = mBehaviorPref[aContentType - 1];
287 0 : *aFromPrefs = true;
288 : }
289 :
290 : // Use the fact that the nsIPermissionManager values map to
291 : // the BEHAVIOR_* values above.
292 0 : switch (permission) {
293 : case BEHAVIOR_ACCEPT:
294 0 : *aPermission = true;
295 0 : break;
296 : case BEHAVIOR_REJECT:
297 0 : *aPermission = false;
298 0 : break;
299 :
300 : case BEHAVIOR_NOFOREIGN:
301 : // Third party checking
302 :
303 : // Need a requesting uri for third party checks to work.
304 0 : if (!aFirstURI)
305 0 : return NS_OK;
306 :
307 0 : bool trustedSource = false;
308 0 : rv = aFirstURI->SchemeIs("chrome", &trustedSource);
309 0 : NS_ENSURE_SUCCESS(rv,rv);
310 0 : if (!trustedSource) {
311 0 : rv = aFirstURI->SchemeIs("resource", &trustedSource);
312 0 : NS_ENSURE_SUCCESS(rv,rv);
313 : }
314 0 : if (trustedSource)
315 0 : return NS_OK;
316 :
317 : // compare tails of names checking to see if they have a common domain
318 : // we do this by comparing the tails of both names where each tail
319 : // includes at least one dot
320 :
321 : // A more generic method somewhere would be nice
322 :
323 0 : nsCAutoString currentHost;
324 0 : rv = aCurrentURI->GetAsciiHost(currentHost);
325 0 : NS_ENSURE_SUCCESS(rv, rv);
326 :
327 : // Search for two dots, starting at the end.
328 : // If there are no two dots found, ++dot will turn to zero,
329 : // that will return the entire string.
330 0 : PRInt32 dot = currentHost.RFindChar('.');
331 0 : dot = currentHost.RFindChar('.', dot-1);
332 0 : ++dot;
333 :
334 : // Get the domain, ie the last part of the host (www.domain.com -> domain.com)
335 : // This will break on co.uk
336 : const nsCSubstring &tail =
337 0 : Substring(currentHost, dot, currentHost.Length() - dot);
338 :
339 0 : nsCAutoString firstHost;
340 0 : rv = aFirstURI->GetAsciiHost(firstHost);
341 0 : NS_ENSURE_SUCCESS(rv, rv);
342 :
343 : // If the tail is longer then the whole firstHost, it will never match
344 0 : if (firstHost.Length() < tail.Length()) {
345 0 : *aPermission = false;
346 0 : return NS_OK;
347 : }
348 :
349 : // Get the last part of the firstUri with the same length as |tail|
350 : const nsCSubstring &firstTail =
351 0 : Substring(firstHost, firstHost.Length() - tail.Length(), tail.Length());
352 :
353 : // Check that both tails are the same, and that just before the tail in
354 : // |firstUri| there is a dot. That means both url are in the same domain
355 0 : if ((firstHost.Length() > tail.Length() &&
356 0 : firstHost.CharAt(firstHost.Length() - tail.Length() - 1) != '.') ||
357 0 : !tail.Equals(firstTail)) {
358 0 : *aPermission = false;
359 : }
360 : break;
361 : }
362 :
363 0 : return NS_OK;
364 : }
365 :
366 : NS_IMETHODIMP
367 0 : nsContentBlocker::Observe(nsISupports *aSubject,
368 : const char *aTopic,
369 : const PRUnichar *aData)
370 : {
371 0 : NS_ASSERTION(!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic),
372 : "unexpected topic - we only deal with pref changes!");
373 :
374 0 : if (mPrefBranchInternal)
375 0 : PrefChanged(mPrefBranchInternal, NS_LossyConvertUTF16toASCII(aData).get());
376 0 : return NS_OK;
377 : }
|