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.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "plstr.h"
6 #include "prlog.h"
7 #include "prprf.h"
8 #include "prnetdb.h"
9 #include "nsCRTGlue.h"
10 #include "nsIPermissionManager.h"
11 #include "nsISSLStatus.h"
12 #include "nsISSLStatusProvider.h"
13 #include "nsSiteSecurityService.h"
14 #include "nsIURI.h"
15 #include "nsNetUtil.h"
16 #include "nsThreadUtils.h"
17 #include "nsString.h"
18 #include "nsIScriptSecurityManager.h"
19 #include "nsISocketProvider.h"
20 #include "mozilla/Preferences.h"
21 #include "mozilla/LinkedList.h"
22 #include "nsSecurityHeaderParser.h"
24 // A note about the preload list:
25 // When a site specifically disables sts by sending a header with
26 // 'max-age: 0', we keep a "knockout" value that means "we have no information
27 // regarding the sts state of this host" (any ancestor of "this host" can still
28 // influence its sts status via include subdomains, however).
29 // This prevents the preload list from overriding the site's current
30 // desired sts status. Knockout values are indicated by permission values of
31 // STS_KNOCKOUT.
32 #include "nsSTSPreloadList.inc"
34 #define STS_SET (nsIPermissionManager::ALLOW_ACTION)
35 #define STS_UNSET (nsIPermissionManager::UNKNOWN_ACTION)
36 #define STS_KNOCKOUT (nsIPermissionManager::DENY_ACTION)
38 #if defined(PR_LOGGING)
39 static PRLogModuleInfo *
40 GetSSSLog()
41 {
42 static PRLogModuleInfo *gSSSLog;
43 if (!gSSSLog)
44 gSSSLog = PR_NewLogModule("nsSSService");
45 return gSSSLog;
46 }
47 #endif
49 #define SSSLOG(args) PR_LOG(GetSSSLog(), 4, args)
51 ////////////////////////////////////////////////////////////////////////////////
53 nsSSSHostEntry::nsSSSHostEntry(const char* aHost)
54 : mHost(aHost)
55 , mExpireTime(0)
56 , mStsPermission(STS_UNSET)
57 , mExpired(false)
58 , mIncludeSubdomains(false)
59 {
60 }
62 nsSSSHostEntry::nsSSSHostEntry(const nsSSSHostEntry& toCopy)
63 : mHost(toCopy.mHost)
64 , mExpireTime(toCopy.mExpireTime)
65 , mStsPermission(toCopy.mStsPermission)
66 , mExpired(toCopy.mExpired)
67 , mIncludeSubdomains(toCopy.mIncludeSubdomains)
68 {
69 }
71 ////////////////////////////////////////////////////////////////////////////////
74 nsSiteSecurityService::nsSiteSecurityService()
75 : mUsePreloadList(true)
76 {
77 }
79 nsSiteSecurityService::~nsSiteSecurityService()
80 {
81 }
83 NS_IMPL_ISUPPORTS(nsSiteSecurityService,
84 nsIObserver,
85 nsISiteSecurityService)
87 nsresult
88 nsSiteSecurityService::Init()
89 {
90 nsresult rv;
92 mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
93 NS_ENSURE_SUCCESS(rv, rv);
95 mUsePreloadList = mozilla::Preferences::GetBool("network.stricttransportsecurity.preloadlist", true);
96 mozilla::Preferences::AddStrongObserver(this, "network.stricttransportsecurity.preloadlist");
97 mObserverService = mozilla::services::GetObserverService();
98 if (mObserverService)
99 mObserverService->AddObserver(this, "last-pb-context-exited", false);
101 return NS_OK;
102 }
104 nsresult
105 nsSiteSecurityService::GetHost(nsIURI *aURI, nsACString &aResult)
106 {
107 nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
108 if (!innerURI) return NS_ERROR_FAILURE;
110 nsresult rv = innerURI->GetAsciiHost(aResult);
112 if (NS_FAILED(rv) || aResult.IsEmpty())
113 return NS_ERROR_UNEXPECTED;
115 return NS_OK;
116 }
118 nsresult
119 nsSiteSecurityService::GetPrincipalForURI(nsIURI* aURI,
120 nsIPrincipal** aPrincipal)
121 {
122 nsresult rv;
123 nsCOMPtr<nsIScriptSecurityManager> securityManager =
124 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
125 NS_ENSURE_SUCCESS(rv, rv);
127 // We have to normalize the scheme of the URIs we're using, so just use https.
128 // HSTS information is shared across all ports for a given host.
129 nsAutoCString host;
130 rv = GetHost(aURI, host);
131 NS_ENSURE_SUCCESS(rv, rv);
132 nsCOMPtr<nsIURI> uri;
133 rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("https://") + host);
134 NS_ENSURE_SUCCESS(rv, rv);
136 // We want all apps to share HSTS state, so this is one of the few places
137 // where we do not silo persistent state by extended origin.
138 return securityManager->GetNoAppCodebasePrincipal(uri, aPrincipal);
139 }
141 nsresult
142 nsSiteSecurityService::SetState(uint32_t aType,
143 nsIURI* aSourceURI,
144 int64_t maxage,
145 bool includeSubdomains,
146 uint32_t flags)
147 {
148 // If max-age is zero, that's an indication to immediately remove the
149 // permissions, so here's a shortcut.
150 if (!maxage) {
151 return RemoveState(aType, aSourceURI, flags);
152 }
154 // Expire time is millis from now. Since STS max-age is in seconds, and
155 // PR_Now() is in micros, must equalize the units at milliseconds.
156 int64_t expiretime = (PR_Now() / PR_USEC_PER_MSEC) +
157 (maxage * PR_MSEC_PER_SEC);
159 bool isPrivate = flags & nsISocketProvider::NO_PERMANENT_STORAGE;
161 // record entry for this host with max-age in the permissions manager
162 SSSLOG(("SSS: maxage permission SET, adding permission\n"));
163 nsresult rv = AddPermission(aSourceURI,
164 STS_PERMISSION,
165 (uint32_t) STS_SET,
166 (uint32_t) nsIPermissionManager::EXPIRE_TIME,
167 expiretime,
168 isPrivate);
169 NS_ENSURE_SUCCESS(rv, rv);
171 if (includeSubdomains) {
172 // record entry for this host with include subdomains in the permissions manager
173 SSSLOG(("SSS: subdomains permission SET, adding permission\n"));
174 rv = AddPermission(aSourceURI,
175 STS_SUBDOMAIN_PERMISSION,
176 (uint32_t) STS_SET,
177 (uint32_t) nsIPermissionManager::EXPIRE_TIME,
178 expiretime,
179 isPrivate);
180 NS_ENSURE_SUCCESS(rv, rv);
181 } else { // !includeSubdomains
182 nsAutoCString hostname;
183 rv = GetHost(aSourceURI, hostname);
184 NS_ENSURE_SUCCESS(rv, rv);
186 SSSLOG(("SSS: subdomains permission UNSET, removing any existing ones\n"));
187 rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION, isPrivate);
188 NS_ENSURE_SUCCESS(rv, rv);
189 }
190 return NS_OK;
191 }
193 NS_IMETHODIMP
194 nsSiteSecurityService::RemoveState(uint32_t aType, nsIURI* aURI, uint32_t aFlags)
195 {
196 // Should be called on the main thread (or via proxy) since the permission
197 // manager is used and it's not threadsafe.
198 NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
199 // Only HSTS is supported at the moment.
200 NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
201 NS_ERROR_NOT_IMPLEMENTED);
203 nsAutoCString hostname;
204 nsresult rv = GetHost(aURI, hostname);
205 NS_ENSURE_SUCCESS(rv, rv);
207 bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
209 rv = RemovePermission(hostname, STS_PERMISSION, isPrivate);
210 NS_ENSURE_SUCCESS(rv, rv);
211 SSSLOG(("SSS: deleted maxage permission\n"));
213 rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION, isPrivate);
214 NS_ENSURE_SUCCESS(rv, rv);
215 SSSLOG(("SSS: deleted subdomains permission\n"));
217 return NS_OK;
218 }
220 static bool
221 HostIsIPAddress(const char *hostname)
222 {
223 PRNetAddr hostAddr;
224 return (PR_StringToNetAddr(hostname, &hostAddr) == PR_SUCCESS);
225 }
227 NS_IMETHODIMP
228 nsSiteSecurityService::ProcessHeader(uint32_t aType,
229 nsIURI* aSourceURI,
230 const char* aHeader,
231 uint32_t aFlags,
232 uint64_t *aMaxAge,
233 bool *aIncludeSubdomains)
234 {
235 // Should be called on the main thread (or via proxy) since the permission
236 // manager is used and it's not threadsafe.
237 NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
238 // Only HSTS is supported at the moment.
239 NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
240 NS_ERROR_NOT_IMPLEMENTED);
242 if (aMaxAge != nullptr) {
243 *aMaxAge = 0;
244 }
246 if (aIncludeSubdomains != nullptr) {
247 *aIncludeSubdomains = false;
248 }
250 nsAutoCString host;
251 nsresult rv = GetHost(aSourceURI, host);
252 NS_ENSURE_SUCCESS(rv, rv);
253 if (HostIsIPAddress(host.get())) {
254 /* Don't process headers if a site is accessed by IP address. */
255 return NS_OK;
256 }
258 char * header = NS_strdup(aHeader);
259 if (!header) return NS_ERROR_OUT_OF_MEMORY;
260 rv = ProcessHeaderMutating(aType, aSourceURI, header, aFlags,
261 aMaxAge, aIncludeSubdomains);
262 NS_Free(header);
263 return rv;
264 }
266 nsresult
267 nsSiteSecurityService::ProcessHeaderMutating(uint32_t aType,
268 nsIURI* aSourceURI,
269 char* aHeader,
270 uint32_t aFlags,
271 uint64_t *aMaxAge,
272 bool *aIncludeSubdomains)
273 {
274 SSSLOG(("SSS: processing header '%s'", aHeader));
276 // "Strict-Transport-Security" ":" OWS
277 // STS-d *( OWS ";" OWS STS-d OWS)
278 //
279 // ; STS directive
280 // STS-d = maxAge / includeSubDomains
281 //
282 // maxAge = "max-age" "=" delta-seconds v-ext
283 //
284 // includeSubDomains = [ "includeSubDomains" ]
285 //
286 // The order of the directives is not significant.
287 // All directives must appear only once.
288 // Directive names are case-insensitive.
289 // The entire header is invalid if a directive not conforming to the
290 // syntax is encountered.
291 // Unrecognized directives (that are otherwise syntactically valid) are
292 // ignored, and the rest of the header is parsed as normal.
294 bool foundMaxAge = false;
295 bool foundIncludeSubdomains = false;
296 bool foundUnrecognizedDirective = false;
297 int64_t maxAge = 0;
299 NS_NAMED_LITERAL_CSTRING(max_age_var, "max-age");
300 NS_NAMED_LITERAL_CSTRING(include_subd_var, "includesubdomains");
303 nsSecurityHeaderParser parser(aHeader);
304 nsresult rv = parser.Parse();
305 if (NS_FAILED(rv)) {
306 SSSLOG(("SSS: could not parse header"));
307 return rv;
308 }
309 mozilla::LinkedList<nsSecurityHeaderDirective> *directives = parser.GetDirectives();
311 for (nsSecurityHeaderDirective *directive = directives->getFirst();
312 directive != nullptr; directive = directive->getNext()) {
313 if (directive->mName.Length() == max_age_var.Length() &&
314 directive->mName.EqualsIgnoreCase(max_age_var.get(),
315 max_age_var.Length())) {
316 if (foundMaxAge) {
317 SSSLOG(("SSS: found two max-age directives"));
318 return NS_ERROR_FAILURE;
319 }
321 SSSLOG(("SSS: found max-age directive"));
322 foundMaxAge = true;
324 size_t len = directive->mValue.Length();
325 for (size_t i = 0; i < len; i++) {
326 char chr = directive->mValue.CharAt(i);
327 if (chr < '0' || chr > '9') {
328 SSSLOG(("SSS: invalid value for max-age directive"));
329 return NS_ERROR_FAILURE;
330 }
331 }
333 if (PR_sscanf(directive->mValue.get(), "%lld", &maxAge) != 1) {
334 SSSLOG(("SSS: could not parse delta-seconds"));
335 return NS_ERROR_FAILURE;
336 }
338 SSSLOG(("SSS: parsed delta-seconds: %lld", maxAge));
339 } else if (directive->mName.Length() == include_subd_var.Length() &&
340 directive->mName.EqualsIgnoreCase(include_subd_var.get(),
341 include_subd_var.Length())) {
342 if (foundIncludeSubdomains) {
343 SSSLOG(("SSS: found two includeSubdomains directives"));
344 return NS_ERROR_FAILURE;
345 }
347 SSSLOG(("SSS: found includeSubdomains directive"));
348 foundIncludeSubdomains = true;
350 if (directive->mValue.Length() != 0) {
351 SSSLOG(("SSS: includeSubdomains directive unexpectedly had value '%s'", directive->mValue.get()));
352 return NS_ERROR_FAILURE;
353 }
354 } else {
355 SSSLOG(("SSS: ignoring unrecognized directive '%s'", directive->mName.get()));
356 foundUnrecognizedDirective = true;
357 }
358 }
360 // after processing all the directives, make sure we came across max-age
361 // somewhere.
362 if (!foundMaxAge) {
363 SSSLOG(("SSS: did not encounter required max-age directive"));
364 return NS_ERROR_FAILURE;
365 }
367 // record the successfully parsed header data.
368 SetState(aType, aSourceURI, maxAge, foundIncludeSubdomains, aFlags);
370 if (aMaxAge != nullptr) {
371 *aMaxAge = (uint64_t)maxAge;
372 }
374 if (aIncludeSubdomains != nullptr) {
375 *aIncludeSubdomains = foundIncludeSubdomains;
376 }
378 return foundUnrecognizedDirective ?
379 NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA :
380 NS_OK;
381 }
383 NS_IMETHODIMP
384 nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
385 uint32_t aFlags, bool* aResult)
386 {
387 // Should be called on the main thread (or via proxy) since the permission
388 // manager is used and it's not threadsafe.
389 NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
390 // Only HSTS is supported at the moment.
391 NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
392 NS_ERROR_NOT_IMPLEMENTED);
394 /* An IP address never qualifies as a secure URI. */
395 if (HostIsIPAddress(aHost)) {
396 *aResult = false;
397 return NS_OK;
398 }
400 nsCOMPtr<nsIURI> uri;
401 nsDependentCString hostString(aHost);
402 nsresult rv = NS_NewURI(getter_AddRefs(uri),
403 NS_LITERAL_CSTRING("https://") + hostString);
404 NS_ENSURE_SUCCESS(rv, rv);
405 return IsSecureURI(aType, uri, aFlags, aResult);
406 }
408 int STSPreloadCompare(const void *key, const void *entry)
409 {
410 const char *keyStr = (const char *)key;
411 const nsSTSPreload *preloadEntry = (const nsSTSPreload *)entry;
412 return strcmp(keyStr, preloadEntry->mHost);
413 }
415 // Returns the preload list entry for the given host, if it exists.
416 // Only does exact host matching - the user must decide how to use the returned
417 // data. May return null.
418 const nsSTSPreload *
419 nsSiteSecurityService::GetPreloadListEntry(const char *aHost)
420 {
421 PRTime currentTime = PR_Now();
422 int32_t timeOffset = 0;
423 nsresult rv = mozilla::Preferences::GetInt("test.currentTimeOffsetSeconds",
424 &timeOffset);
425 if (NS_SUCCEEDED(rv)) {
426 currentTime += (PRTime(timeOffset) * PR_USEC_PER_SEC);
427 }
429 if (mUsePreloadList && currentTime < gPreloadListExpirationTime) {
430 return (const nsSTSPreload *) bsearch(aHost,
431 kSTSPreloadList,
432 mozilla::ArrayLength(kSTSPreloadList),
433 sizeof(nsSTSPreload),
434 STSPreloadCompare);
435 }
437 return nullptr;
438 }
440 NS_IMETHODIMP
441 nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI,
442 uint32_t aFlags, bool* aResult)
443 {
444 // Should be called on the main thread (or via proxy) since the permission
445 // manager is used and it's not threadsafe.
446 NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
447 // Only HSTS is supported at the moment.
448 NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
449 NS_ERROR_NOT_IMPLEMENTED);
451 // set default in case if we can't find any STS information
452 *aResult = false;
454 nsAutoCString host;
455 nsresult rv = GetHost(aURI, host);
456 NS_ENSURE_SUCCESS(rv, rv);
458 /* An IP address never qualifies as a secure URI. */
459 if (HostIsIPAddress(host.BeginReading())) {
460 return NS_OK;
461 }
463 // Holepunch chart.apis.google.com and subdomains.
464 if (host.Equals(NS_LITERAL_CSTRING("chart.apis.google.com")) ||
465 StringEndsWith(host, NS_LITERAL_CSTRING(".chart.apis.google.com"))) {
466 return NS_OK;
467 }
469 const nsSTSPreload *preload = nullptr;
470 nsSSSHostEntry *pbEntry = nullptr;
472 bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
473 if (isPrivate) {
474 pbEntry = mPrivateModeHostTable.GetEntry(host.get());
475 }
477 nsCOMPtr<nsIPrincipal> principal;
478 rv = GetPrincipalForURI(aURI, getter_AddRefs(principal));
479 NS_ENSURE_SUCCESS(rv, rv);
481 uint32_t permMgrPermission;
482 rv = mPermMgr->TestExactPermissionFromPrincipal(principal, STS_PERMISSION,
483 &permMgrPermission);
484 NS_ENSURE_SUCCESS(rv, rv);
486 // First check the exact host. This involves first checking for an entry in
487 // the private browsing table. If that entry exists, we don't want to check
488 // in either the permission manager or the preload list. We only want to use
489 // the stored permission if it is not a knockout entry, however.
490 // Additionally, if it is a knockout entry, we want to stop looking for data
491 // on the host, because the knockout entry indicates "we have no information
492 // regarding the sts status of this host".
493 if (pbEntry && pbEntry->mStsPermission != STS_UNSET) {
494 SSSLOG(("Found private browsing table entry for %s", host.get()));
495 if (!pbEntry->IsExpired() && pbEntry->mStsPermission == STS_SET) {
496 *aResult = true;
497 return NS_OK;
498 }
499 }
500 // Next we look in the permission manager. Same story here regarding
501 // knockout entries.
502 else if (permMgrPermission != STS_UNSET) {
503 SSSLOG(("Found permission manager entry for %s", host.get()));
504 if (permMgrPermission == STS_SET) {
505 *aResult = true;
506 return NS_OK;
507 }
508 }
509 // Finally look in the preloaded list. This is the exact host,
510 // so if an entry exists at all, this host is sts.
511 else if (GetPreloadListEntry(host.get())) {
512 SSSLOG(("%s is a preloaded STS host", host.get()));
513 *aResult = true;
514 return NS_OK;
515 }
517 // Used for testing permissions as we walk up the domain tree.
518 nsCOMPtr<nsIURI> domainWalkURI;
519 nsCOMPtr<nsIPrincipal> domainWalkPrincipal;
520 const char *subdomain;
522 SSSLOG(("no HSTS data for %s found, walking up domain", host.get()));
523 uint32_t offset = 0;
524 for (offset = host.FindChar('.', offset) + 1;
525 offset > 0;
526 offset = host.FindChar('.', offset) + 1) {
528 subdomain = host.get() + offset;
530 // If we get an empty string, don't continue.
531 if (strlen(subdomain) < 1) {
532 break;
533 }
535 if (isPrivate) {
536 pbEntry = mPrivateModeHostTable.GetEntry(subdomain);
537 }
539 // normalize all URIs with https://
540 rv = NS_NewURI(getter_AddRefs(domainWalkURI),
541 NS_LITERAL_CSTRING("https://") + Substring(host, offset));
542 NS_ENSURE_SUCCESS(rv, rv);
544 rv = GetPrincipalForURI(domainWalkURI, getter_AddRefs(domainWalkPrincipal));
545 NS_ENSURE_SUCCESS(rv, rv);
547 rv = mPermMgr->TestExactPermissionFromPrincipal(domainWalkPrincipal,
548 STS_PERMISSION,
549 &permMgrPermission);
550 NS_ENSURE_SUCCESS(rv, rv);
552 // Do the same thing as with the exact host, except now we're looking at
553 // ancestor domains of the original host. So, we have to look at the
554 // include subdomains permissions (although we still have to check for the
555 // STS_PERMISSION first to check that this is an sts host and not a
556 // knockout entry - and again, if it is a knockout entry, we stop looking
557 // for data on it and skip to the next higher up ancestor domain).
558 if (pbEntry && pbEntry->mStsPermission != STS_UNSET) {
559 SSSLOG(("Found private browsing table entry for %s", subdomain));
560 if (!pbEntry->IsExpired() && pbEntry->mStsPermission == STS_SET) {
561 *aResult = pbEntry->mIncludeSubdomains;
562 break;
563 }
564 }
565 else if (permMgrPermission != STS_UNSET) {
566 SSSLOG(("Found permission manager entry for %s", subdomain));
567 if (permMgrPermission == STS_SET) {
568 uint32_t subdomainPermission;
569 rv = mPermMgr->TestExactPermissionFromPrincipal(domainWalkPrincipal,
570 STS_SUBDOMAIN_PERMISSION,
571 &subdomainPermission);
572 NS_ENSURE_SUCCESS(rv, rv);
573 *aResult = (subdomainPermission == STS_SET);
574 break;
575 }
576 }
577 // This is an ancestor, so if we get a match, we have to check if the
578 // preloaded entry includes subdomains.
579 else if ((preload = GetPreloadListEntry(subdomain)) != nullptr) {
580 if (preload->mIncludeSubdomains) {
581 SSSLOG(("%s is a preloaded STS host", subdomain));
582 *aResult = true;
583 break;
584 }
585 }
587 SSSLOG(("no HSTS data for %s found, walking up domain", subdomain));
588 }
590 // Use whatever we ended up with, which defaults to false.
591 return NS_OK;
592 }
595 // Verify the trustworthiness of the security info (are there any cert errors?)
596 NS_IMETHODIMP
597 nsSiteSecurityService::ShouldIgnoreHeaders(nsISupports* aSecurityInfo,
598 bool* aResult)
599 {
600 nsresult rv;
601 bool tlsIsBroken = false;
602 nsCOMPtr<nsISSLStatusProvider> sslprov = do_QueryInterface(aSecurityInfo);
603 NS_ENSURE_TRUE(sslprov, NS_ERROR_FAILURE);
605 nsCOMPtr<nsISSLStatus> sslstat;
606 rv = sslprov->GetSSLStatus(getter_AddRefs(sslstat));
607 NS_ENSURE_SUCCESS(rv, rv);
608 NS_ENSURE_TRUE(sslstat, NS_ERROR_FAILURE);
610 bool trustcheck;
611 rv = sslstat->GetIsDomainMismatch(&trustcheck);
612 NS_ENSURE_SUCCESS(rv, rv);
613 tlsIsBroken = tlsIsBroken || trustcheck;
615 rv = sslstat->GetIsNotValidAtThisTime(&trustcheck);
616 NS_ENSURE_SUCCESS(rv, rv);
617 tlsIsBroken = tlsIsBroken || trustcheck;
619 rv = sslstat->GetIsUntrusted(&trustcheck);
620 NS_ENSURE_SUCCESS(rv, rv);
621 tlsIsBroken = tlsIsBroken || trustcheck;
623 *aResult = tlsIsBroken;
624 return NS_OK;
625 }
627 //------------------------------------------------------------
628 // nsSiteSecurityService::nsIObserver
629 //------------------------------------------------------------
631 NS_IMETHODIMP
632 nsSiteSecurityService::Observe(nsISupports *subject,
633 const char *topic,
634 const char16_t *data)
635 {
636 if (strcmp(topic, "last-pb-context-exited") == 0) {
637 mPrivateModeHostTable.Clear();
638 }
639 else if (strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
640 mUsePreloadList = mozilla::Preferences::GetBool("network.stricttransportsecurity.preloadlist", true);
641 }
643 return NS_OK;
644 }
646 //------------------------------------------------------------
647 // Functions to overlay the permission manager calls in case
648 // we're in private browsing mode.
649 //------------------------------------------------------------
650 nsresult
651 nsSiteSecurityService::AddPermission(nsIURI *aURI,
652 const char *aType,
653 uint32_t aPermission,
654 uint32_t aExpireType,
655 int64_t aExpireTime,
656 bool aIsPrivate)
657 {
658 // Private mode doesn't address user-set (EXPIRE_NEVER) permissions: let
659 // those be stored persistently.
660 if (!aIsPrivate || aExpireType == nsIPermissionManager::EXPIRE_NEVER) {
661 // Not in private mode, or manually-set permission
662 nsCOMPtr<nsIPrincipal> principal;
663 nsresult rv = GetPrincipalForURI(aURI, getter_AddRefs(principal));
664 NS_ENSURE_SUCCESS(rv, rv);
666 return mPermMgr->AddFromPrincipal(principal, aType, aPermission,
667 aExpireType, aExpireTime);
668 }
670 nsAutoCString host;
671 nsresult rv = GetHost(aURI, host);
672 NS_ENSURE_SUCCESS(rv, rv);
673 SSSLOG(("AddPermission for entry for %s", host.get()));
675 // Update in mPrivateModeHostTable only, so any changes will be rolled
676 // back when exiting private mode.
678 // Note: EXPIRE_NEVER permissions should trump anything that shows up in
679 // the HTTP header, so if there's an EXPIRE_NEVER permission already
680 // don't store anything new.
681 // Currently there's no way to get the type of expiry out of the
682 // permission manager, but that's okay since there's nothing that stores
683 // EXPIRE_NEVER permissions.
685 // PutEntry returns an existing entry if there already is one, or it
686 // creates a new one if there isn't.
687 nsSSSHostEntry* entry = mPrivateModeHostTable.PutEntry(host.get());
688 if (!entry) {
689 return NS_ERROR_OUT_OF_MEMORY;
690 }
691 SSSLOG(("Created private mode entry for %s", host.get()));
693 // AddPermission() will be called twice if the STS header encountered has
694 // includeSubdomains (first for the main permission and second for the
695 // subdomains permission). If AddPermission() gets called a second time
696 // with the STS_SUBDOMAIN_PERMISSION, we just have to flip that bit in
697 // the nsSSSHostEntry.
698 if (strcmp(aType, STS_SUBDOMAIN_PERMISSION) == 0) {
699 entry->mIncludeSubdomains = true;
700 }
701 else if (strcmp(aType, STS_PERMISSION) == 0) {
702 entry->mStsPermission = aPermission;
703 }
705 // Also refresh the expiration time.
706 entry->SetExpireTime(aExpireTime);
707 return NS_OK;
708 }
710 nsresult
711 nsSiteSecurityService::RemovePermission(const nsCString &aHost,
712 const char *aType,
713 bool aIsPrivate)
714 {
715 // Build up a principal for use with the permission manager.
716 // normalize all URIs with https://
717 nsCOMPtr<nsIURI> uri;
718 nsresult rv = NS_NewURI(getter_AddRefs(uri),
719 NS_LITERAL_CSTRING("https://") + aHost);
720 NS_ENSURE_SUCCESS(rv, rv);
722 nsCOMPtr<nsIPrincipal> principal;
723 rv = GetPrincipalForURI(uri, getter_AddRefs(principal));
724 NS_ENSURE_SUCCESS(rv, rv);
726 if (!aIsPrivate) {
727 // Not in private mode: remove permissions persistently.
728 // This means setting the permission to STS_KNOCKOUT in case
729 // this host is on the preload list (so we can override it).
730 return mPermMgr->AddFromPrincipal(principal, aType,
731 STS_KNOCKOUT,
732 nsIPermissionManager::EXPIRE_NEVER, 0);
733 }
735 // Make changes in mPrivateModeHostTable only, so any changes will be
736 // rolled back when exiting private mode.
737 nsSSSHostEntry* entry = mPrivateModeHostTable.GetEntry(aHost.get());
739 if (!entry) {
740 entry = mPrivateModeHostTable.PutEntry(aHost.get());
741 if (!entry) {
742 return NS_ERROR_OUT_OF_MEMORY;
743 }
744 SSSLOG(("Created private mode deleted mask for %s", aHost.get()));
745 }
747 if (strcmp(aType, STS_PERMISSION) == 0) {
748 entry->mStsPermission = STS_KNOCKOUT;
749 }
750 else if (strcmp(aType, STS_SUBDOMAIN_PERMISSION) == 0) {
751 entry->mIncludeSubdomains = false;
752 }
754 return NS_OK;
755 }