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 Strict-Transport-Security.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * 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 : * Sid Stamm <sid@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 "plstr.h"
39 : #include "prlog.h"
40 : #include "prprf.h"
41 : #include "nsCRTGlue.h"
42 : #include "nsIPermissionManager.h"
43 : #include "nsIPrivateBrowsingService.h"
44 : #include "nsISSLStatus.h"
45 : #include "nsISSLStatusProvider.h"
46 : #include "nsStrictTransportSecurityService.h"
47 : #include "nsIURI.h"
48 : #include "nsNetUtil.h"
49 : #include "nsThreadUtils.h"
50 : #include "nsStringGlue.h"
51 :
52 : #if defined(PR_LOGGING)
53 1464 : PRLogModuleInfo *gSTSLog = PR_NewLogModule("nsSTSService");
54 : #endif
55 :
56 : #define STSLOG(args) PR_LOG(gSTSLog, 4, args)
57 :
58 : #define STS_PARSER_FAIL_IF(test,args) \
59 : if (test) { \
60 : STSLOG(args); \
61 : return NS_ERROR_FAILURE; \
62 : }
63 :
64 :
65 : ////////////////////////////////////////////////////////////////////////////////
66 :
67 0 : nsSTSHostEntry::nsSTSHostEntry(const char* aHost)
68 : : mHost(aHost)
69 : , mExpireTime(0)
70 : , mDeleted(false)
71 0 : , mIncludeSubdomains(false)
72 : {
73 0 : }
74 :
75 0 : nsSTSHostEntry::nsSTSHostEntry(const nsSTSHostEntry& toCopy)
76 : : mHost(toCopy.mHost)
77 : , mExpireTime(toCopy.mExpireTime)
78 : , mDeleted(toCopy.mDeleted)
79 0 : , mIncludeSubdomains(toCopy.mIncludeSubdomains)
80 : {
81 0 : }
82 :
83 : ////////////////////////////////////////////////////////////////////////////////
84 :
85 :
86 266 : nsStrictTransportSecurityService::nsStrictTransportSecurityService()
87 266 : : mInPrivateMode(false)
88 : {
89 266 : }
90 :
91 528 : nsStrictTransportSecurityService::~nsStrictTransportSecurityService()
92 : {
93 1056 : }
94 :
95 2940 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsStrictTransportSecurityService,
96 : nsIObserver,
97 : nsIStrictTransportSecurityService)
98 :
99 : nsresult
100 266 : nsStrictTransportSecurityService::Init()
101 : {
102 : nsresult rv;
103 :
104 266 : mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
105 266 : NS_ENSURE_SUCCESS(rv, rv);
106 :
107 : // figure out if we're starting in private browsing mode
108 : nsCOMPtr<nsIPrivateBrowsingService> pbs =
109 532 : do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
110 266 : if (pbs)
111 266 : pbs->GetPrivateBrowsingEnabled(&mInPrivateMode);
112 :
113 266 : mObserverService = mozilla::services::GetObserverService();
114 266 : if (mObserverService)
115 266 : mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, false);
116 :
117 266 : if (mInPrivateMode && !mPrivateModeHostTable.Init())
118 0 : return NS_ERROR_OUT_OF_MEMORY;
119 :
120 266 : return NS_OK;
121 : }
122 :
123 : nsresult
124 24 : nsStrictTransportSecurityService::GetHost(nsIURI *aURI, nsACString &aResult)
125 : {
126 48 : nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
127 24 : if (!innerURI) return NS_ERROR_FAILURE;
128 :
129 24 : nsresult rv = innerURI->GetAsciiHost(aResult);
130 :
131 24 : if (NS_FAILED(rv) || aResult.IsEmpty())
132 0 : return NS_ERROR_UNEXPECTED;
133 :
134 24 : return NS_OK;
135 : }
136 :
137 : nsresult
138 28 : nsStrictTransportSecurityService::SetStsState(nsIURI* aSourceURI,
139 : PRInt64 maxage,
140 : bool includeSubdomains)
141 : {
142 : // If max-age is zero, that's an indication to immediately remove the
143 : // permissions, so here's a shortcut.
144 28 : if (!maxage)
145 0 : return RemoveStsState(aSourceURI);
146 :
147 : // Expire time is millis from now. Since STS max-age is in seconds, and
148 : // PR_Now() is in micros, must equalize the units at milliseconds.
149 28 : PRInt64 expiretime = (PR_Now() / 1000) + (maxage * 1000);
150 :
151 : // record entry for this host with max-age in the permissions manager
152 28 : STSLOG(("STS: maxage permission SET, adding permission\n"));
153 : nsresult rv = AddPermission(aSourceURI,
154 : STS_PERMISSION,
155 : (PRUint32) nsIPermissionManager::ALLOW_ACTION,
156 : (PRUint32) nsIPermissionManager::EXPIRE_TIME,
157 28 : expiretime);
158 28 : NS_ENSURE_SUCCESS(rv, rv);
159 :
160 28 : if (includeSubdomains) {
161 : // record entry for this host with include subdomains in the permissions manager
162 10 : STSLOG(("STS: subdomains permission SET, adding permission\n"));
163 : rv = AddPermission(aSourceURI,
164 : STS_SUBDOMAIN_PERMISSION,
165 : (PRUint32) nsIPermissionManager::ALLOW_ACTION,
166 : (PRUint32) nsIPermissionManager::EXPIRE_TIME,
167 10 : expiretime);
168 10 : NS_ENSURE_SUCCESS(rv, rv);
169 : } else { // !includeSubdomains
170 36 : nsCAutoString hostname;
171 18 : rv = GetHost(aSourceURI, hostname);
172 18 : NS_ENSURE_SUCCESS(rv, rv);
173 :
174 18 : STSLOG(("STS: subdomains permission UNSET, removing any existing ones\n"));
175 18 : rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION);
176 18 : NS_ENSURE_SUCCESS(rv, rv);
177 : }
178 28 : return NS_OK;
179 : }
180 :
181 : NS_IMETHODIMP
182 0 : nsStrictTransportSecurityService::RemoveStsState(nsIURI* aURI)
183 : {
184 : // Should be called on the main thread (or via proxy) since the permission
185 : // manager is used and it's not threadsafe.
186 0 : NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
187 :
188 0 : nsCAutoString hostname;
189 0 : nsresult rv = GetHost(aURI, hostname);
190 0 : NS_ENSURE_SUCCESS(rv, rv);
191 :
192 0 : rv = RemovePermission(hostname, STS_PERMISSION);
193 0 : NS_ENSURE_SUCCESS(rv, rv);
194 0 : STSLOG(("STS: deleted maxage permission\n"));
195 :
196 0 : rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION);
197 0 : NS_ENSURE_SUCCESS(rv, rv);
198 0 : STSLOG(("STS: deleted subdomains permission\n"));
199 :
200 0 : return NS_OK;
201 : }
202 :
203 : NS_IMETHODIMP
204 40 : nsStrictTransportSecurityService::ProcessStsHeader(nsIURI* aSourceURI,
205 : const char* aHeader)
206 : {
207 : // Should be called on the main thread (or via proxy) since the permission
208 : // manager is used and it's not threadsafe.
209 40 : NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
210 :
211 40 : char * header = NS_strdup(aHeader);
212 40 : if (!header) return NS_ERROR_OUT_OF_MEMORY;
213 40 : nsresult rv = ProcessStsHeaderMutating(aSourceURI, header);
214 40 : NS_Free(header);
215 40 : return rv;
216 : }
217 :
218 : nsresult
219 40 : nsStrictTransportSecurityService::ProcessStsHeaderMutating(nsIURI* aSourceURI,
220 : char* aHeader)
221 : {
222 40 : STSLOG(("STS: ProcessStrictTransportHeader(%s)\n", aHeader));
223 :
224 : // "Strict-Transport-Security" ":" OWS
225 : // STS-d *( OWS ";" OWS STS-d OWS)
226 : //
227 : // ; STS directive
228 : // STS-d = maxAge / includeSubDomains
229 : //
230 : // maxAge = "max-age" "=" delta-seconds v-ext
231 : //
232 : // includeSubDomains = [ "includeSubDomains" ]
233 :
234 : const char* directive;
235 :
236 40 : bool foundMaxAge = false;
237 40 : bool foundUnrecognizedTokens = false;
238 40 : bool includeSubdomains = false;
239 40 : PRInt64 maxAge = 0;
240 :
241 80 : NS_NAMED_LITERAL_CSTRING(max_age_var, "max-age");
242 80 : NS_NAMED_LITERAL_CSTRING(include_subd_var, "includesubdomains");
243 :
244 124 : while ((directive = NS_strtok(";", &aHeader))) {
245 : //skip leading whitespace
246 50 : directive = NS_strspnp(" \t", directive);
247 50 : STS_PARSER_FAIL_IF(!(*directive), ("error removing initial whitespace\n."));
248 :
249 50 : if (!PL_strncasecmp(directive, max_age_var.get(), max_age_var.Length())) {
250 : // skip directive name
251 34 : directive += max_age_var.Length();
252 : // skip leading whitespace
253 34 : directive = NS_strspnp(" \t", directive);
254 34 : STS_PARSER_FAIL_IF(*directive != '=',
255 : ("No equal sign found in max-age directive\n"));
256 :
257 : // skip over the equal sign
258 33 : STS_PARSER_FAIL_IF(*(++directive) == '\0',
259 : ("No delta-seconds present\n"));
260 :
261 : // obtain the delta-seconds value
262 33 : STS_PARSER_FAIL_IF(PR_sscanf(directive, "%lld", &maxAge) != 1,
263 : ("Could not convert delta-seconds\n"));
264 28 : STSLOG(("STS: ProcessStrictTransportHeader() STS found maxage %lld\n", maxAge));
265 28 : foundMaxAge = true;
266 :
267 : // skip max-age value and trailing whitespace
268 28 : directive = NS_strspnp("0123456789 \t", directive);
269 :
270 : // log unknown tokens, but don't fail (for forwards compatibility)
271 28 : if (*directive != '\0') {
272 3 : foundUnrecognizedTokens = true;
273 3 : STSLOG(("Extra stuff in max-age after delta-seconds: %s \n", directive));
274 : }
275 : }
276 16 : else if (!PL_strncasecmp(directive, include_subd_var.get(), include_subd_var.Length())) {
277 12 : directive += include_subd_var.Length();
278 :
279 : // only record "includesubdomains" if it is a token by itself... for
280 : // example, don't set includeSubdomains = true if the directive is
281 : // "includesubdomainsFooBar".
282 12 : if (*directive == '\0' || *directive =='\t' || *directive == ' ') {
283 11 : includeSubdomains = true;
284 11 : STSLOG(("STS: ProcessStrictTransportHeader: obtained subdomains status\n"));
285 :
286 : // skip trailing whitespace
287 11 : directive = NS_strspnp(" \t", directive);
288 :
289 22 : if (*directive != '\0') {
290 0 : foundUnrecognizedTokens = true;
291 0 : STSLOG(("Extra stuff after includesubdomains: %s\n", directive));
292 : }
293 : } else {
294 1 : foundUnrecognizedTokens = true;
295 1 : STSLOG(("Unrecognized directive in header: %s\n", directive));
296 : }
297 : }
298 : else {
299 : // log unknown directives, but don't fail (for backwards compatibility)
300 4 : foundUnrecognizedTokens = true;
301 4 : STSLOG(("Unrecognized directive in header: %s\n", directive));
302 : }
303 : }
304 :
305 : // after processing all the directives, make sure we came across max-age
306 : // somewhere.
307 34 : STS_PARSER_FAIL_IF(!foundMaxAge,
308 : ("Parse ERROR: couldn't locate max-age token\n"));
309 :
310 : // record the successfully parsed header data.
311 28 : SetStsState(aSourceURI, maxAge, includeSubdomains);
312 :
313 : return foundUnrecognizedTokens ?
314 : NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA :
315 28 : NS_OK;
316 : }
317 :
318 : NS_IMETHODIMP
319 0 : nsStrictTransportSecurityService::IsStsHost(const char* aHost, bool* aResult)
320 : {
321 : // Should be called on the main thread (or via proxy) since the permission
322 : // manager is used and it's not threadsafe.
323 0 : NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
324 :
325 0 : nsCOMPtr<nsIURI> uri;
326 0 : nsDependentCString hostString(aHost);
327 0 : nsresult rv = NS_NewURI(getter_AddRefs(uri),
328 0 : NS_LITERAL_CSTRING("https://") + hostString);
329 0 : NS_ENSURE_SUCCESS(rv, rv);
330 0 : return IsStsURI(uri, aResult);
331 : }
332 :
333 : NS_IMETHODIMP
334 5051 : nsStrictTransportSecurityService::IsStsURI(nsIURI* aURI, bool* aResult)
335 : {
336 : // Should be called on the main thread (or via proxy) since the permission
337 : // manager is used and it's not threadsafe.
338 5051 : NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
339 :
340 : nsresult rv;
341 : PRUint32 permExact, permGeneral;
342 : // If this domain has the forcehttps permission, this is an STS host.
343 5051 : rv = TestPermission(aURI, STS_PERMISSION, &permExact, true);
344 5051 : NS_ENSURE_SUCCESS(rv, rv);
345 :
346 : // If any super-domain has the includeSubdomains permission, this is an
347 : // STS host.
348 5051 : rv = TestPermission(aURI, STS_SUBDOMAIN_PERMISSION, &permGeneral, false);
349 5051 : NS_ENSURE_SUCCESS(rv, rv);
350 :
351 : *aResult = ((permExact == nsIPermissionManager::ALLOW_ACTION) ||
352 5051 : (permGeneral == nsIPermissionManager::ALLOW_ACTION));
353 5051 : return NS_OK;
354 : }
355 :
356 :
357 : // Verify the trustworthiness of the security info (are there any cert errors?)
358 : NS_IMETHODIMP
359 4 : nsStrictTransportSecurityService::ShouldIgnoreStsHeader(nsISupports* aSecurityInfo,
360 : bool* aResult)
361 : {
362 : nsresult rv;
363 4 : bool tlsIsBroken = false;
364 8 : nsCOMPtr<nsISSLStatusProvider> sslprov = do_QueryInterface(aSecurityInfo);
365 4 : NS_ENSURE_TRUE(sslprov, NS_ERROR_FAILURE);
366 :
367 8 : nsCOMPtr<nsISSLStatus> sslstat;
368 4 : rv = sslprov->GetSSLStatus(getter_AddRefs(sslstat));
369 4 : NS_ENSURE_SUCCESS(rv, rv);
370 4 : NS_ENSURE_TRUE(sslstat, NS_ERROR_FAILURE);
371 :
372 : bool trustcheck;
373 4 : rv = sslstat->GetIsDomainMismatch(&trustcheck);
374 4 : NS_ENSURE_SUCCESS(rv, rv);
375 4 : tlsIsBroken = tlsIsBroken || trustcheck;
376 :
377 4 : rv = sslstat->GetIsNotValidAtThisTime(&trustcheck);
378 4 : NS_ENSURE_SUCCESS(rv, rv);
379 4 : tlsIsBroken = tlsIsBroken || trustcheck;
380 :
381 4 : rv = sslstat->GetIsUntrusted(&trustcheck);
382 4 : NS_ENSURE_SUCCESS(rv, rv);
383 4 : tlsIsBroken = tlsIsBroken || trustcheck;
384 :
385 4 : *aResult = tlsIsBroken;
386 4 : return NS_OK;
387 : }
388 :
389 : //------------------------------------------------------------
390 : // nsStrictTransportSecurityService::nsIObserver
391 : //------------------------------------------------------------
392 :
393 : NS_IMETHODIMP
394 4 : nsStrictTransportSecurityService::Observe(nsISupports *subject,
395 : const char *topic,
396 : const PRUnichar *data)
397 : {
398 4 : if (strcmp(topic, NS_PRIVATE_BROWSING_SWITCH_TOPIC) == 0) {
399 4 : if(NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(data)) {
400 : // Indication to start recording stuff locally and not writing changes
401 : // out to the permission manager.
402 :
403 3 : if (!mPrivateModeHostTable.IsInitialized()
404 1 : && !mPrivateModeHostTable.Init()) {
405 0 : return NS_ERROR_OUT_OF_MEMORY;
406 : }
407 2 : mInPrivateMode = true;
408 : }
409 2 : else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(data)) {
410 2 : mPrivateModeHostTable.Clear();
411 2 : mInPrivateMode = false;
412 : }
413 : }
414 :
415 4 : return NS_OK;
416 : }
417 :
418 : //------------------------------------------------------------
419 : // Functions to overlay the permission manager calls in case
420 : // we're in private browsing mode.
421 : //------------------------------------------------------------
422 : nsresult
423 38 : nsStrictTransportSecurityService::AddPermission(nsIURI *aURI,
424 : const char *aType,
425 : PRUint32 aPermission,
426 : PRUint32 aExpireType,
427 : PRInt64 aExpireTime)
428 : {
429 : // Private mode doesn't address user-set (EXPIRE_NEVER) permissions: let
430 : // those be stored persistently.
431 38 : if (!mInPrivateMode || aExpireType == nsIPermissionManager::EXPIRE_NEVER) {
432 : // Not in private mode, or manually-set permission
433 38 : return mPermMgr->Add(aURI, aType, aPermission, aExpireType, aExpireTime);
434 : }
435 :
436 0 : nsCAutoString host;
437 0 : nsresult rv = GetHost(aURI, host);
438 0 : NS_ENSURE_SUCCESS(rv, rv);
439 0 : STSLOG(("AddPermission for entry for for %s", host.get()));
440 :
441 : // Update in mPrivateModeHostTable only, so any changes will be rolled
442 : // back when exiting private mode.
443 :
444 : // Note: EXPIRE_NEVER permissions should trump anything that shows up in
445 : // the HTTP header, so if there's an EXPIRE_NEVER permission already
446 : // don't store anything new.
447 : // Currently there's no way to get the type of expiry out of the
448 : // permission manager, but that's okay since there's nothing that stores
449 : // EXPIRE_NEVER permissions.
450 :
451 : // PutEntry returns an existing entry if there already is one, or it
452 : // creates a new one if there isn't.
453 0 : nsSTSHostEntry* entry = mPrivateModeHostTable.PutEntry(host.get());
454 0 : STSLOG(("Created private mode entry for for %s", host.get()));
455 :
456 : // AddPermission() will be called twice if the STS header encountered has
457 : // includeSubdomains (first for the main permission and second for the
458 : // subdomains permission). If AddPermission() gets called a second time
459 : // with the STS_SUBDOMAIN_PERMISSION, we just have to flip that bit in
460 : // the nsSTSHostEntry.
461 0 : if (strcmp(aType, STS_SUBDOMAIN_PERMISSION) == 0) {
462 0 : entry->mIncludeSubdomains = true;
463 : }
464 : // for the case where PutEntry() returned an existing host entry, make
465 : // sure it's not set as deleted (which might have happened in the past).
466 0 : entry->mDeleted = false;
467 :
468 : // Also refresh the expiration time.
469 0 : entry->mExpireTime = aExpireTime;
470 0 : return NS_OK;
471 :
472 : }
473 :
474 : nsresult
475 18 : nsStrictTransportSecurityService::RemovePermission(const nsCString &aHost,
476 : const char *aType)
477 : {
478 18 : if (!mInPrivateMode) {
479 : // Not in private mode: remove permissions persistently.
480 18 : return mPermMgr->Remove(aHost, aType);
481 : }
482 :
483 : // Make changes in mPrivateModeHostTable only, so any changes will be
484 : // rolled back when exiting private mode.
485 0 : nsSTSHostEntry* entry = mPrivateModeHostTable.GetEntry(aHost.get());
486 :
487 : // Build up an nsIURI for use with the permission manager.
488 0 : nsCOMPtr<nsIURI> uri;
489 0 : nsresult rv = NS_NewURI(getter_AddRefs(uri),
490 0 : NS_LITERAL_CSTRING("http://") + aHost);
491 0 : NS_ENSURE_SUCCESS(rv, rv);
492 :
493 : // Check to see if there's STS data stored for this host in the
494 : // permission manager (probably set outside private mode).
495 : PRUint32 permmgrValue;
496 0 : rv = mPermMgr->TestExactPermission(uri, aType, &permmgrValue);
497 0 : NS_ENSURE_SUCCESS(rv, rv);
498 :
499 : // If there is STS data in the permission manager, store a "deleted" mask
500 : // for the permission in mPrivateModeHostTable (either update
501 : // mPrivateModeHostTable to have the deleted mask, or add one).
502 : // This is because we don't want removals that happen in private mode to
503 : // be reflected when private mode is exited -- but while in private mode
504 : // we still want the effect of the removal.
505 0 : if (permmgrValue != nsIPermissionManager::UNKNOWN_ACTION) {
506 : // if there's no entry in mPrivateModeHostTable, we have to make one.
507 0 : if (!entry) {
508 0 : entry = mPrivateModeHostTable.PutEntry(aHost.get());
509 0 : STSLOG(("Created private mode deleted mask for for %s", aHost.get()));
510 : }
511 0 : entry->mDeleted = true;
512 0 : entry->mIncludeSubdomains = false;
513 0 : return NS_OK;
514 : }
515 :
516 : // Otherwise, permission doesn't exist in the real permission manager, so
517 : // there's nothing to "pretend" to delete. I'ts ok to delete any copy in
518 : // mPrivateModeHostTable.
519 0 : if (entry) mPrivateModeHostTable.RawRemoveEntry(entry);
520 0 : return NS_OK;
521 : }
522 :
523 : nsresult
524 10102 : nsStrictTransportSecurityService::TestPermission(nsIURI *aURI,
525 : const char *aType,
526 : PRUint32 *aPermission,
527 : bool testExact)
528 : {
529 : // set default for if we can't find any STS information
530 10102 : *aPermission = nsIPermissionManager::UNKNOWN_ACTION;
531 :
532 10102 : if (!mInPrivateMode) {
533 : // if not in private mode, just delegate to the permission manager.
534 10096 : if (testExact)
535 5048 : return mPermMgr->TestExactPermission(aURI, aType, aPermission);
536 : else
537 5048 : return mPermMgr->TestPermission(aURI, aType, aPermission);
538 : }
539 :
540 12 : nsCAutoString host;
541 6 : nsresult rv = GetHost(aURI, host);
542 6 : if (NS_FAILED(rv)) return NS_OK;
543 :
544 : nsSTSHostEntry *entry;
545 : PRUint32 actualExactPermission;
546 6 : PRUint32 offset = 0;
547 6 : PRInt64 now = PR_Now() / 1000;
548 :
549 : // Used for testing permissions as we walk up the domain tree.
550 12 : nsCOMPtr<nsIURI> domainWalkURI;
551 :
552 : // In parallel, loop over private mode cache and also the real permission
553 : // manager--ignoring any masked as "deleted" in the local cache. We have
554 : // to do this here since the most specific permission in *either* the
555 : // permission manager or mPrivateModeHostTable should be used.
556 3 : do {
557 6 : entry = mPrivateModeHostTable.GetEntry(host.get() + offset);
558 6 : STSLOG(("Checking PM Table entry and permmgr for %s", host.get()+offset));
559 :
560 : // flag as deleted any entries encountered that have expired. We only
561 : // flag the nsSTSHostEntry because there could be some data in the
562 : // permission manager that -- if not in private mode -- would have been
563 : // overwritten by newly encountered STS data.
564 6 : if (entry && (now > entry->mExpireTime)) {
565 0 : STSLOG(("Deleting expired PM Table entry for %s", host.get()+offset));
566 0 : entry->mDeleted = true;
567 0 : entry->mIncludeSubdomains = false;
568 : }
569 :
570 6 : rv = NS_NewURI(getter_AddRefs(domainWalkURI),
571 12 : NS_LITERAL_CSTRING("http://") + Substring(host, offset));
572 6 : NS_ENSURE_SUCCESS(rv, rv);
573 :
574 6 : rv = mPermMgr->TestExactPermission(domainWalkURI,
575 : aType,
576 6 : &actualExactPermission);
577 6 : NS_ENSURE_SUCCESS(rv, rv);
578 :
579 : // There are three cases as we walk up the hostname testing
580 : // permissions:
581 : // 1. There's no entry in mPrivateModeHostTable for this host; rely
582 : // on data in the permission manager
583 6 : if (!entry) {
584 6 : if (actualExactPermission != nsIPermissionManager::UNKNOWN_ACTION) {
585 : // no cached data but a permission in the permission manager so use
586 : // it and stop looking.
587 0 : *aPermission = actualExactPermission;
588 0 : STSLOG(("no PM Table entry for %s, using permmgr", host.get()+offset));
589 0 : break;
590 : }
591 : }
592 : // 2. There's a "deleted" mask in mPrivateModeHostTable for this host
593 : // or we're looking for includeSubdomain information and it's not set:
594 : // any data in the permission manager must be ignored, since the
595 : // permission would have been deleted if not in private mode.
596 0 : else if (entry->mDeleted || (strcmp(aType, STS_SUBDOMAIN_PERMISSION) == 0
597 0 : && !entry->mIncludeSubdomains)) {
598 0 : STSLOG(("no entry at all for %s, walking up", host.get()+offset));
599 : // keep looking
600 : }
601 : // 3. There's a non-deleted entry in mPrivateModeHostTable for this
602 : // host, so it should be used.
603 : else {
604 : // All STS permissions' values are ALLOW_ACTION or they are not
605 : // known (as in, not set or turned off).
606 0 : *aPermission = nsIPermissionManager::ALLOW_ACTION;
607 0 : STSLOG(("PM Table entry for %s: forcing", host.get()+offset));
608 0 : break;
609 : }
610 :
611 : // Don't continue walking up the host segments if the test was for an
612 : // exact match only.
613 6 : if (testExact) break;
614 :
615 3 : STSLOG(("no PM Table entry or permmgr data for %s, walking up domain",
616 : host.get()+offset));
617 : // walk up the host segments
618 3 : offset = host.FindChar('.', offset) + 1;
619 : } while (offset > 0);
620 :
621 : // Use whatever we ended up with, which defaults to UNKNOWN_ACTION.
622 6 : return NS_OK;
623 4392 : }
|