Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | #include "nsContentBlocker.h" |
michael@0 | 6 | #include "nsIContent.h" |
michael@0 | 7 | #include "nsIURI.h" |
michael@0 | 8 | #include "nsIServiceManager.h" |
michael@0 | 9 | #include "nsIDocShellTreeItem.h" |
michael@0 | 10 | #include "nsIPrefService.h" |
michael@0 | 11 | #include "nsIPrefBranch.h" |
michael@0 | 12 | #include "nsIDocShell.h" |
michael@0 | 13 | #include "nsString.h" |
michael@0 | 14 | #include "nsContentPolicyUtils.h" |
michael@0 | 15 | #include "nsIObjectLoadingContent.h" |
michael@0 | 16 | #include "mozilla/ArrayUtils.h" |
michael@0 | 17 | |
michael@0 | 18 | // Possible behavior pref values |
michael@0 | 19 | // Those map to the nsIPermissionManager values where possible |
michael@0 | 20 | #define BEHAVIOR_ACCEPT nsIPermissionManager::ALLOW_ACTION |
michael@0 | 21 | #define BEHAVIOR_REJECT nsIPermissionManager::DENY_ACTION |
michael@0 | 22 | #define BEHAVIOR_NOFOREIGN 3 |
michael@0 | 23 | |
michael@0 | 24 | // From nsIContentPolicy |
michael@0 | 25 | static const char *kTypeString[] = {"other", |
michael@0 | 26 | "script", |
michael@0 | 27 | "image", |
michael@0 | 28 | "stylesheet", |
michael@0 | 29 | "object", |
michael@0 | 30 | "document", |
michael@0 | 31 | "subdocument", |
michael@0 | 32 | "refresh", |
michael@0 | 33 | "xbl", |
michael@0 | 34 | "ping", |
michael@0 | 35 | "xmlhttprequest", |
michael@0 | 36 | "objectsubrequest", |
michael@0 | 37 | "dtd", |
michael@0 | 38 | "font", |
michael@0 | 39 | "media", |
michael@0 | 40 | "websocket", |
michael@0 | 41 | "csp_report", |
michael@0 | 42 | "xslt"}; |
michael@0 | 43 | |
michael@0 | 44 | #define NUMBER_OF_TYPES MOZ_ARRAY_LENGTH(kTypeString) |
michael@0 | 45 | uint8_t nsContentBlocker::mBehaviorPref[NUMBER_OF_TYPES]; |
michael@0 | 46 | |
michael@0 | 47 | NS_IMPL_ISUPPORTS(nsContentBlocker, |
michael@0 | 48 | nsIContentPolicy, |
michael@0 | 49 | nsIObserver, |
michael@0 | 50 | nsISupportsWeakReference) |
michael@0 | 51 | |
michael@0 | 52 | nsContentBlocker::nsContentBlocker() |
michael@0 | 53 | { |
michael@0 | 54 | memset(mBehaviorPref, BEHAVIOR_ACCEPT, NUMBER_OF_TYPES); |
michael@0 | 55 | } |
michael@0 | 56 | |
michael@0 | 57 | nsresult |
michael@0 | 58 | nsContentBlocker::Init() |
michael@0 | 59 | { |
michael@0 | 60 | nsresult rv; |
michael@0 | 61 | mPermissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv); |
michael@0 | 62 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 63 | |
michael@0 | 64 | nsCOMPtr<nsIPrefService> prefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); |
michael@0 | 65 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 66 | |
michael@0 | 67 | nsCOMPtr<nsIPrefBranch> prefBranch; |
michael@0 | 68 | rv = prefService->GetBranch("permissions.default.", getter_AddRefs(prefBranch)); |
michael@0 | 69 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 70 | |
michael@0 | 71 | // Migrate old image blocker pref |
michael@0 | 72 | nsCOMPtr<nsIPrefBranch> oldPrefBranch; |
michael@0 | 73 | oldPrefBranch = do_QueryInterface(prefService); |
michael@0 | 74 | int32_t oldPref; |
michael@0 | 75 | rv = oldPrefBranch->GetIntPref("network.image.imageBehavior", &oldPref); |
michael@0 | 76 | if (NS_SUCCEEDED(rv) && oldPref) { |
michael@0 | 77 | int32_t newPref; |
michael@0 | 78 | switch (oldPref) { |
michael@0 | 79 | default: |
michael@0 | 80 | newPref = BEHAVIOR_ACCEPT; |
michael@0 | 81 | break; |
michael@0 | 82 | case 1: |
michael@0 | 83 | newPref = BEHAVIOR_NOFOREIGN; |
michael@0 | 84 | break; |
michael@0 | 85 | case 2: |
michael@0 | 86 | newPref = BEHAVIOR_REJECT; |
michael@0 | 87 | break; |
michael@0 | 88 | } |
michael@0 | 89 | prefBranch->SetIntPref("image", newPref); |
michael@0 | 90 | oldPrefBranch->ClearUserPref("network.image.imageBehavior"); |
michael@0 | 91 | } |
michael@0 | 92 | |
michael@0 | 93 | |
michael@0 | 94 | // The branch is not a copy of the prefservice, but a new object, because |
michael@0 | 95 | // it is a non-default branch. Adding obeservers to it will only work if |
michael@0 | 96 | // we make sure that the object doesn't die. So, keep a reference to it. |
michael@0 | 97 | mPrefBranchInternal = do_QueryInterface(prefBranch, &rv); |
michael@0 | 98 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 99 | |
michael@0 | 100 | rv = mPrefBranchInternal->AddObserver("", this, true); |
michael@0 | 101 | PrefChanged(prefBranch, nullptr); |
michael@0 | 102 | |
michael@0 | 103 | return rv; |
michael@0 | 104 | } |
michael@0 | 105 | |
michael@0 | 106 | #undef LIMIT |
michael@0 | 107 | #define LIMIT(x, low, high, default) ((x) >= (low) && (x) <= (high) ? (x) : (default)) |
michael@0 | 108 | |
michael@0 | 109 | void |
michael@0 | 110 | nsContentBlocker::PrefChanged(nsIPrefBranch *aPrefBranch, |
michael@0 | 111 | const char *aPref) |
michael@0 | 112 | { |
michael@0 | 113 | int32_t val; |
michael@0 | 114 | |
michael@0 | 115 | #define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P)) |
michael@0 | 116 | |
michael@0 | 117 | for(uint32_t i = 0; i < NUMBER_OF_TYPES; ++i) { |
michael@0 | 118 | if (PREF_CHANGED(kTypeString[i]) && |
michael@0 | 119 | NS_SUCCEEDED(aPrefBranch->GetIntPref(kTypeString[i], &val))) |
michael@0 | 120 | mBehaviorPref[i] = LIMIT(val, 1, 3, 1); |
michael@0 | 121 | } |
michael@0 | 122 | |
michael@0 | 123 | } |
michael@0 | 124 | |
michael@0 | 125 | // nsIContentPolicy Implementation |
michael@0 | 126 | NS_IMETHODIMP |
michael@0 | 127 | nsContentBlocker::ShouldLoad(uint32_t aContentType, |
michael@0 | 128 | nsIURI *aContentLocation, |
michael@0 | 129 | nsIURI *aRequestingLocation, |
michael@0 | 130 | nsISupports *aRequestingContext, |
michael@0 | 131 | const nsACString &aMimeGuess, |
michael@0 | 132 | nsISupports *aExtra, |
michael@0 | 133 | nsIPrincipal *aRequestPrincipal, |
michael@0 | 134 | int16_t *aDecision) |
michael@0 | 135 | { |
michael@0 | 136 | *aDecision = nsIContentPolicy::ACCEPT; |
michael@0 | 137 | nsresult rv; |
michael@0 | 138 | |
michael@0 | 139 | // Ony support NUMBER_OF_TYPES content types. that all there is at the |
michael@0 | 140 | // moment, but you never know... |
michael@0 | 141 | if (aContentType > NUMBER_OF_TYPES) |
michael@0 | 142 | return NS_OK; |
michael@0 | 143 | |
michael@0 | 144 | // we can't do anything without this |
michael@0 | 145 | if (!aContentLocation) |
michael@0 | 146 | return NS_OK; |
michael@0 | 147 | |
michael@0 | 148 | // The final type of an object tag may mutate before it reaches |
michael@0 | 149 | // shouldProcess, so we cannot make any sane blocking decisions here |
michael@0 | 150 | if (aContentType == nsIContentPolicy::TYPE_OBJECT) |
michael@0 | 151 | return NS_OK; |
michael@0 | 152 | |
michael@0 | 153 | // we only want to check http, https, ftp |
michael@0 | 154 | // for chrome:// and resources and others, no need to check. |
michael@0 | 155 | nsAutoCString scheme; |
michael@0 | 156 | aContentLocation->GetScheme(scheme); |
michael@0 | 157 | if (!scheme.LowerCaseEqualsLiteral("ftp") && |
michael@0 | 158 | !scheme.LowerCaseEqualsLiteral("http") && |
michael@0 | 159 | !scheme.LowerCaseEqualsLiteral("https")) |
michael@0 | 160 | return NS_OK; |
michael@0 | 161 | |
michael@0 | 162 | bool shouldLoad, fromPrefs; |
michael@0 | 163 | rv = TestPermission(aContentLocation, aRequestingLocation, aContentType, |
michael@0 | 164 | &shouldLoad, &fromPrefs); |
michael@0 | 165 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 166 | if (!shouldLoad) { |
michael@0 | 167 | if (fromPrefs) { |
michael@0 | 168 | *aDecision = nsIContentPolicy::REJECT_TYPE; |
michael@0 | 169 | } else { |
michael@0 | 170 | *aDecision = nsIContentPolicy::REJECT_SERVER; |
michael@0 | 171 | } |
michael@0 | 172 | } |
michael@0 | 173 | |
michael@0 | 174 | return NS_OK; |
michael@0 | 175 | } |
michael@0 | 176 | |
michael@0 | 177 | NS_IMETHODIMP |
michael@0 | 178 | nsContentBlocker::ShouldProcess(uint32_t aContentType, |
michael@0 | 179 | nsIURI *aContentLocation, |
michael@0 | 180 | nsIURI *aRequestingLocation, |
michael@0 | 181 | nsISupports *aRequestingContext, |
michael@0 | 182 | const nsACString &aMimeGuess, |
michael@0 | 183 | nsISupports *aExtra, |
michael@0 | 184 | nsIPrincipal *aRequestPrincipal, |
michael@0 | 185 | int16_t *aDecision) |
michael@0 | 186 | { |
michael@0 | 187 | // For loads where aRequestingContext is chrome, we should just |
michael@0 | 188 | // accept. Those are most likely toplevel loads in windows, and |
michael@0 | 189 | // chrome generally knows what it's doing anyway. |
michael@0 | 190 | nsCOMPtr<nsIDocShellTreeItem> item = |
michael@0 | 191 | do_QueryInterface(NS_CP_GetDocShellFromContext(aRequestingContext)); |
michael@0 | 192 | |
michael@0 | 193 | if (item && item->ItemType() == nsIDocShellTreeItem::typeChrome) { |
michael@0 | 194 | *aDecision = nsIContentPolicy::ACCEPT; |
michael@0 | 195 | return NS_OK; |
michael@0 | 196 | } |
michael@0 | 197 | |
michael@0 | 198 | // For objects, we only check policy in shouldProcess, as the final type isn't |
michael@0 | 199 | // determined until the channel is open -- We don't want to block images in |
michael@0 | 200 | // object tags because plugins are disallowed. |
michael@0 | 201 | // NOTE that this bypasses the aContentLocation checks in ShouldLoad - this is |
michael@0 | 202 | // intentional, as aContentLocation may be null for plugins that load by type |
michael@0 | 203 | // (e.g. java) |
michael@0 | 204 | if (aContentType == nsIContentPolicy::TYPE_OBJECT) { |
michael@0 | 205 | *aDecision = nsIContentPolicy::ACCEPT; |
michael@0 | 206 | |
michael@0 | 207 | bool shouldLoad, fromPrefs; |
michael@0 | 208 | nsresult rv = TestPermission(aContentLocation, aRequestingLocation, |
michael@0 | 209 | aContentType, &shouldLoad, &fromPrefs); |
michael@0 | 210 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 211 | if (!shouldLoad) { |
michael@0 | 212 | if (fromPrefs) { |
michael@0 | 213 | *aDecision = nsIContentPolicy::REJECT_TYPE; |
michael@0 | 214 | } else { |
michael@0 | 215 | *aDecision = nsIContentPolicy::REJECT_SERVER; |
michael@0 | 216 | } |
michael@0 | 217 | } |
michael@0 | 218 | return NS_OK; |
michael@0 | 219 | } |
michael@0 | 220 | |
michael@0 | 221 | // This isn't a load from chrome or an object tag - Just do a ShouldLoad() |
michael@0 | 222 | // check -- we want the same answer here |
michael@0 | 223 | return ShouldLoad(aContentType, aContentLocation, aRequestingLocation, |
michael@0 | 224 | aRequestingContext, aMimeGuess, aExtra, aRequestPrincipal, |
michael@0 | 225 | aDecision); |
michael@0 | 226 | } |
michael@0 | 227 | |
michael@0 | 228 | nsresult |
michael@0 | 229 | nsContentBlocker::TestPermission(nsIURI *aCurrentURI, |
michael@0 | 230 | nsIURI *aFirstURI, |
michael@0 | 231 | int32_t aContentType, |
michael@0 | 232 | bool *aPermission, |
michael@0 | 233 | bool *aFromPrefs) |
michael@0 | 234 | { |
michael@0 | 235 | *aFromPrefs = false; |
michael@0 | 236 | // This default will also get used if there is an unknown value in the |
michael@0 | 237 | // permission list, or if the permission manager returns unknown values. |
michael@0 | 238 | *aPermission = true; |
michael@0 | 239 | |
michael@0 | 240 | // check the permission list first; if we find an entry, it overrides |
michael@0 | 241 | // default prefs. |
michael@0 | 242 | // Don't forget the aContentType ranges from 1..8, while the |
michael@0 | 243 | // array is indexed 0..7 |
michael@0 | 244 | uint32_t permission; |
michael@0 | 245 | nsresult rv = mPermissionManager->TestPermission(aCurrentURI, |
michael@0 | 246 | kTypeString[aContentType - 1], |
michael@0 | 247 | &permission); |
michael@0 | 248 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 249 | |
michael@0 | 250 | // If there is nothing on the list, use the default. |
michael@0 | 251 | if (!permission) { |
michael@0 | 252 | permission = mBehaviorPref[aContentType - 1]; |
michael@0 | 253 | *aFromPrefs = true; |
michael@0 | 254 | } |
michael@0 | 255 | |
michael@0 | 256 | // Use the fact that the nsIPermissionManager values map to |
michael@0 | 257 | // the BEHAVIOR_* values above. |
michael@0 | 258 | switch (permission) { |
michael@0 | 259 | case BEHAVIOR_ACCEPT: |
michael@0 | 260 | *aPermission = true; |
michael@0 | 261 | break; |
michael@0 | 262 | case BEHAVIOR_REJECT: |
michael@0 | 263 | *aPermission = false; |
michael@0 | 264 | break; |
michael@0 | 265 | |
michael@0 | 266 | case BEHAVIOR_NOFOREIGN: |
michael@0 | 267 | // Third party checking |
michael@0 | 268 | |
michael@0 | 269 | // Need a requesting uri for third party checks to work. |
michael@0 | 270 | if (!aFirstURI) |
michael@0 | 271 | return NS_OK; |
michael@0 | 272 | |
michael@0 | 273 | bool trustedSource = false; |
michael@0 | 274 | rv = aFirstURI->SchemeIs("chrome", &trustedSource); |
michael@0 | 275 | NS_ENSURE_SUCCESS(rv,rv); |
michael@0 | 276 | if (!trustedSource) { |
michael@0 | 277 | rv = aFirstURI->SchemeIs("resource", &trustedSource); |
michael@0 | 278 | NS_ENSURE_SUCCESS(rv,rv); |
michael@0 | 279 | } |
michael@0 | 280 | if (trustedSource) |
michael@0 | 281 | return NS_OK; |
michael@0 | 282 | |
michael@0 | 283 | // compare tails of names checking to see if they have a common domain |
michael@0 | 284 | // we do this by comparing the tails of both names where each tail |
michael@0 | 285 | // includes at least one dot |
michael@0 | 286 | |
michael@0 | 287 | // A more generic method somewhere would be nice |
michael@0 | 288 | |
michael@0 | 289 | nsAutoCString currentHost; |
michael@0 | 290 | rv = aCurrentURI->GetAsciiHost(currentHost); |
michael@0 | 291 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 292 | |
michael@0 | 293 | // Search for two dots, starting at the end. |
michael@0 | 294 | // If there are no two dots found, ++dot will turn to zero, |
michael@0 | 295 | // that will return the entire string. |
michael@0 | 296 | int32_t dot = currentHost.RFindChar('.'); |
michael@0 | 297 | dot = currentHost.RFindChar('.', dot-1); |
michael@0 | 298 | ++dot; |
michael@0 | 299 | |
michael@0 | 300 | // Get the domain, ie the last part of the host (www.domain.com -> domain.com) |
michael@0 | 301 | // This will break on co.uk |
michael@0 | 302 | const nsCSubstring &tail = |
michael@0 | 303 | Substring(currentHost, dot, currentHost.Length() - dot); |
michael@0 | 304 | |
michael@0 | 305 | nsAutoCString firstHost; |
michael@0 | 306 | rv = aFirstURI->GetAsciiHost(firstHost); |
michael@0 | 307 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 308 | |
michael@0 | 309 | // If the tail is longer then the whole firstHost, it will never match |
michael@0 | 310 | if (firstHost.Length() < tail.Length()) { |
michael@0 | 311 | *aPermission = false; |
michael@0 | 312 | return NS_OK; |
michael@0 | 313 | } |
michael@0 | 314 | |
michael@0 | 315 | // Get the last part of the firstUri with the same length as |tail| |
michael@0 | 316 | const nsCSubstring &firstTail = |
michael@0 | 317 | Substring(firstHost, firstHost.Length() - tail.Length(), tail.Length()); |
michael@0 | 318 | |
michael@0 | 319 | // Check that both tails are the same, and that just before the tail in |
michael@0 | 320 | // |firstUri| there is a dot. That means both url are in the same domain |
michael@0 | 321 | if ((firstHost.Length() > tail.Length() && |
michael@0 | 322 | firstHost.CharAt(firstHost.Length() - tail.Length() - 1) != '.') || |
michael@0 | 323 | !tail.Equals(firstTail)) { |
michael@0 | 324 | *aPermission = false; |
michael@0 | 325 | } |
michael@0 | 326 | break; |
michael@0 | 327 | } |
michael@0 | 328 | |
michael@0 | 329 | return NS_OK; |
michael@0 | 330 | } |
michael@0 | 331 | |
michael@0 | 332 | NS_IMETHODIMP |
michael@0 | 333 | nsContentBlocker::Observe(nsISupports *aSubject, |
michael@0 | 334 | const char *aTopic, |
michael@0 | 335 | const char16_t *aData) |
michael@0 | 336 | { |
michael@0 | 337 | NS_ASSERTION(!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic), |
michael@0 | 338 | "unexpected topic - we only deal with pref changes!"); |
michael@0 | 339 | |
michael@0 | 340 | if (mPrefBranchInternal) |
michael@0 | 341 | PrefChanged(mPrefBranchInternal, NS_LossyConvertUTF16toASCII(aData).get()); |
michael@0 | 342 | return NS_OK; |
michael@0 | 343 | } |