PageRenderTime 84ms CodeModel.GetById 13ms app.highlight 65ms RepoModel.GetById 2ms app.codeStats 0ms

/security/manager/boot/src/nsStrictTransportSecurityService.cpp

http://github.com/zpao/v8monkey
C++ | 623 lines | 370 code | 96 blank | 157 comment | 68 complexity | 2348f44f42247f352f50a19e3ca7948a MD5 | raw file
  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)
 53PRLogModuleInfo *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
 67nsSTSHostEntry::nsSTSHostEntry(const char* aHost)
 68  : mHost(aHost)
 69  , mExpireTime(0)
 70  , mDeleted(false)
 71  , mIncludeSubdomains(false)
 72{
 73}
 74
 75nsSTSHostEntry::nsSTSHostEntry(const nsSTSHostEntry& toCopy)
 76  : mHost(toCopy.mHost)
 77  , mExpireTime(toCopy.mExpireTime)
 78  , mDeleted(toCopy.mDeleted)
 79  , mIncludeSubdomains(toCopy.mIncludeSubdomains)
 80{
 81}
 82
 83////////////////////////////////////////////////////////////////////////////////
 84
 85
 86nsStrictTransportSecurityService::nsStrictTransportSecurityService()
 87  : mInPrivateMode(false)
 88{
 89}
 90
 91nsStrictTransportSecurityService::~nsStrictTransportSecurityService()
 92{
 93}
 94
 95NS_IMPL_THREADSAFE_ISUPPORTS2(nsStrictTransportSecurityService,
 96                              nsIObserver,
 97                              nsIStrictTransportSecurityService)
 98
 99nsresult
100nsStrictTransportSecurityService::Init()
101{
102   nsresult rv;
103
104   mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
105   NS_ENSURE_SUCCESS(rv, rv);
106
107   // figure out if we're starting in private browsing mode
108   nsCOMPtr<nsIPrivateBrowsingService> pbs =
109     do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
110   if (pbs)
111     pbs->GetPrivateBrowsingEnabled(&mInPrivateMode);
112
113   mObserverService = mozilla::services::GetObserverService();
114   if (mObserverService)
115     mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, false);
116
117   if (mInPrivateMode && !mPrivateModeHostTable.Init())
118     return NS_ERROR_OUT_OF_MEMORY;
119
120   return NS_OK;
121}
122
123nsresult
124nsStrictTransportSecurityService::GetHost(nsIURI *aURI, nsACString &aResult)
125{
126  nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
127  if (!innerURI) return NS_ERROR_FAILURE;
128
129  nsresult rv = innerURI->GetAsciiHost(aResult);
130
131  if (NS_FAILED(rv) || aResult.IsEmpty())
132    return NS_ERROR_UNEXPECTED;
133
134  return NS_OK;
135}
136
137nsresult
138nsStrictTransportSecurityService::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  if (!maxage)
145    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  PRInt64 expiretime = (PR_Now() / 1000) + (maxage * 1000);
150
151  // record entry for this host with max-age in the permissions manager
152  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                              expiretime);
158  NS_ENSURE_SUCCESS(rv, rv);
159
160  if (includeSubdomains) {
161    // record entry for this host with include subdomains in the permissions manager
162    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                       expiretime);
168    NS_ENSURE_SUCCESS(rv, rv);
169  } else { // !includeSubdomains
170    nsCAutoString hostname;
171    rv = GetHost(aSourceURI, hostname);
172    NS_ENSURE_SUCCESS(rv, rv);
173
174    STSLOG(("STS: subdomains permission UNSET, removing any existing ones\n"));
175    rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION);
176    NS_ENSURE_SUCCESS(rv, rv);
177  }
178  return NS_OK;
179}
180
181NS_IMETHODIMP
182nsStrictTransportSecurityService::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  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
187
188  nsCAutoString hostname;
189  nsresult rv = GetHost(aURI, hostname);
190  NS_ENSURE_SUCCESS(rv, rv);
191
192  rv = RemovePermission(hostname, STS_PERMISSION);
193  NS_ENSURE_SUCCESS(rv, rv);
194  STSLOG(("STS: deleted maxage permission\n"));
195
196  rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION);
197  NS_ENSURE_SUCCESS(rv, rv);
198  STSLOG(("STS: deleted subdomains permission\n"));
199
200  return NS_OK;
201}
202
203NS_IMETHODIMP
204nsStrictTransportSecurityService::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  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
210
211  char * header = NS_strdup(aHeader);
212  if (!header) return NS_ERROR_OUT_OF_MEMORY;
213  nsresult rv = ProcessStsHeaderMutating(aSourceURI, header);
214  NS_Free(header);
215  return rv;
216}
217
218nsresult
219nsStrictTransportSecurityService::ProcessStsHeaderMutating(nsIURI* aSourceURI,
220                                                           char* aHeader)
221{
222  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  bool foundMaxAge = false;
237  bool foundUnrecognizedTokens = false;
238  bool includeSubdomains = false;
239  PRInt64 maxAge = 0;
240
241  NS_NAMED_LITERAL_CSTRING(max_age_var, "max-age");
242  NS_NAMED_LITERAL_CSTRING(include_subd_var, "includesubdomains");
243
244  while ((directive = NS_strtok(";", &aHeader))) {
245    //skip leading whitespace
246    directive = NS_strspnp(" \t", directive);
247    STS_PARSER_FAIL_IF(!(*directive), ("error removing initial whitespace\n."));
248
249    if (!PL_strncasecmp(directive, max_age_var.get(), max_age_var.Length())) {
250      // skip directive name
251      directive += max_age_var.Length();
252      // skip leading whitespace
253      directive = NS_strspnp(" \t", directive);
254      STS_PARSER_FAIL_IF(*directive != '=',
255                  ("No equal sign found in max-age directive\n"));
256
257      // skip over the equal sign
258      STS_PARSER_FAIL_IF(*(++directive) == '\0',
259                  ("No delta-seconds present\n"));
260
261      // obtain the delta-seconds value
262      STS_PARSER_FAIL_IF(PR_sscanf(directive, "%lld", &maxAge) != 1,
263                  ("Could not convert delta-seconds\n"));
264      STSLOG(("STS: ProcessStrictTransportHeader() STS found maxage %lld\n", maxAge));
265      foundMaxAge = true;
266
267      // skip max-age value and trailing whitespace
268      directive = NS_strspnp("0123456789 \t", directive);
269
270      // log unknown tokens, but don't fail (for forwards compatibility)
271      if (*directive != '\0') {
272        foundUnrecognizedTokens = true;
273        STSLOG(("Extra stuff in max-age after delta-seconds: %s \n", directive));
274      }
275    }
276    else if (!PL_strncasecmp(directive, include_subd_var.get(), include_subd_var.Length())) {
277      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      if (*directive == '\0' || *directive =='\t' || *directive == ' ') {
283        includeSubdomains = true;
284        STSLOG(("STS: ProcessStrictTransportHeader: obtained subdomains status\n"));
285
286        // skip trailing whitespace
287        directive = NS_strspnp(" \t", directive);
288
289        if (*directive != '\0') {
290          foundUnrecognizedTokens = true;
291          STSLOG(("Extra stuff after includesubdomains: %s\n", directive));
292        }
293      } else {
294        foundUnrecognizedTokens = true;
295        STSLOG(("Unrecognized directive in header: %s\n", directive));
296      }
297    }
298    else {
299      // log unknown directives, but don't fail (for backwards compatibility)
300      foundUnrecognizedTokens = true;
301      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  STS_PARSER_FAIL_IF(!foundMaxAge,
308              ("Parse ERROR: couldn't locate max-age token\n"));
309
310  // record the successfully parsed header data.
311  SetStsState(aSourceURI, maxAge, includeSubdomains);
312
313  return foundUnrecognizedTokens ?
314         NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA :
315         NS_OK;
316}
317
318NS_IMETHODIMP
319nsStrictTransportSecurityService::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  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
324
325  nsCOMPtr<nsIURI> uri;
326  nsDependentCString hostString(aHost);
327  nsresult rv = NS_NewURI(getter_AddRefs(uri),
328                          NS_LITERAL_CSTRING("https://") + hostString);
329  NS_ENSURE_SUCCESS(rv, rv);
330  return IsStsURI(uri, aResult);
331}
332
333NS_IMETHODIMP
334nsStrictTransportSecurityService::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  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  rv = TestPermission(aURI, STS_PERMISSION, &permExact, true);
344  NS_ENSURE_SUCCESS(rv, rv);
345
346  // If any super-domain has the includeSubdomains permission, this is an
347  // STS host.
348  rv = TestPermission(aURI, STS_SUBDOMAIN_PERMISSION, &permGeneral, false);
349  NS_ENSURE_SUCCESS(rv, rv);
350
351  *aResult = ((permExact   == nsIPermissionManager::ALLOW_ACTION) ||
352              (permGeneral == nsIPermissionManager::ALLOW_ACTION));
353  return NS_OK;
354}
355
356
357// Verify the trustworthiness of the security info (are there any cert errors?)
358NS_IMETHODIMP
359nsStrictTransportSecurityService::ShouldIgnoreStsHeader(nsISupports* aSecurityInfo,
360                                                        bool* aResult)
361{
362  nsresult rv;
363  bool tlsIsBroken = false;
364  nsCOMPtr<nsISSLStatusProvider> sslprov = do_QueryInterface(aSecurityInfo);
365  NS_ENSURE_TRUE(sslprov, NS_ERROR_FAILURE);
366
367  nsCOMPtr<nsISSLStatus> sslstat;
368  rv = sslprov->GetSSLStatus(getter_AddRefs(sslstat));
369  NS_ENSURE_SUCCESS(rv, rv);
370  NS_ENSURE_TRUE(sslstat, NS_ERROR_FAILURE);
371
372  bool trustcheck;
373  rv = sslstat->GetIsDomainMismatch(&trustcheck);
374  NS_ENSURE_SUCCESS(rv, rv);
375  tlsIsBroken = tlsIsBroken || trustcheck;
376
377  rv = sslstat->GetIsNotValidAtThisTime(&trustcheck);
378  NS_ENSURE_SUCCESS(rv, rv);
379  tlsIsBroken = tlsIsBroken || trustcheck;
380
381  rv = sslstat->GetIsUntrusted(&trustcheck);
382  NS_ENSURE_SUCCESS(rv, rv);
383  tlsIsBroken = tlsIsBroken || trustcheck;
384
385  *aResult = tlsIsBroken;
386  return NS_OK;
387}
388
389//------------------------------------------------------------
390// nsStrictTransportSecurityService::nsIObserver
391//------------------------------------------------------------
392
393NS_IMETHODIMP
394nsStrictTransportSecurityService::Observe(nsISupports *subject,
395                                          const char *topic,
396                                          const PRUnichar *data)
397{
398  if (strcmp(topic, NS_PRIVATE_BROWSING_SWITCH_TOPIC) == 0) {
399    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      if (!mPrivateModeHostTable.IsInitialized()
404          && !mPrivateModeHostTable.Init()) {
405        return NS_ERROR_OUT_OF_MEMORY;
406      }
407      mInPrivateMode = true;
408    }
409    else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(data)) {
410      mPrivateModeHostTable.Clear();
411      mInPrivateMode = false;
412    }
413  }
414
415  return NS_OK;
416}
417
418//------------------------------------------------------------
419// Functions to overlay the permission manager calls in case
420// we're in private browsing mode.
421//------------------------------------------------------------
422nsresult
423nsStrictTransportSecurityService::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    if (!mInPrivateMode || aExpireType == nsIPermissionManager::EXPIRE_NEVER) {
432      // Not in private mode, or manually-set permission
433      return mPermMgr->Add(aURI, aType, aPermission, aExpireType, aExpireTime);
434    }
435
436    nsCAutoString host;
437    nsresult rv = GetHost(aURI, host);
438    NS_ENSURE_SUCCESS(rv, rv);
439    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    nsSTSHostEntry* entry = mPrivateModeHostTable.PutEntry(host.get());
454    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    if (strcmp(aType, STS_SUBDOMAIN_PERMISSION) == 0) {
462      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    entry->mDeleted = false;
467
468    // Also refresh the expiration time.
469    entry->mExpireTime = aExpireTime;
470    return NS_OK;
471
472}
473
474nsresult
475nsStrictTransportSecurityService::RemovePermission(const nsCString  &aHost,
476                                                   const char       *aType)
477{
478    if (!mInPrivateMode) {
479      // Not in private mode: remove permissions persistently.
480      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    nsSTSHostEntry* entry = mPrivateModeHostTable.GetEntry(aHost.get());
486
487    // Build up an nsIURI for use with the permission manager.
488    nsCOMPtr<nsIURI> uri;
489    nsresult rv = NS_NewURI(getter_AddRefs(uri),
490                            NS_LITERAL_CSTRING("http://") + aHost);
491    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    rv = mPermMgr->TestExactPermission(uri, aType, &permmgrValue);
497    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    if (permmgrValue != nsIPermissionManager::UNKNOWN_ACTION) {
506      // if there's no entry in mPrivateModeHostTable, we have to make one.
507      if (!entry) {
508        entry = mPrivateModeHostTable.PutEntry(aHost.get());
509        STSLOG(("Created private mode deleted mask for for %s", aHost.get()));
510      }
511      entry->mDeleted = true;
512      entry->mIncludeSubdomains = false;
513      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    if (entry) mPrivateModeHostTable.RawRemoveEntry(entry);
520    return NS_OK;
521}
522
523nsresult
524nsStrictTransportSecurityService::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    *aPermission = nsIPermissionManager::UNKNOWN_ACTION;
531
532    if (!mInPrivateMode) {
533      // if not in private mode, just delegate to the permission manager.
534      if (testExact)
535        return mPermMgr->TestExactPermission(aURI, aType, aPermission);
536      else
537        return mPermMgr->TestPermission(aURI, aType, aPermission);
538    }
539
540    nsCAutoString host;
541    nsresult rv = GetHost(aURI, host);
542    if (NS_FAILED(rv)) return NS_OK;
543
544    nsSTSHostEntry *entry;
545    PRUint32 actualExactPermission;
546    PRUint32 offset = 0;
547    PRInt64 now = PR_Now() / 1000;
548
549    // Used for testing permissions as we walk up the domain tree.
550    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    do {
557      entry = mPrivateModeHostTable.GetEntry(host.get() + offset);
558      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      if (entry && (now > entry->mExpireTime)) {
565        STSLOG(("Deleting expired PM Table entry for %s", host.get()+offset));
566        entry->mDeleted = true;
567        entry->mIncludeSubdomains = false;
568      }
569
570      rv = NS_NewURI(getter_AddRefs(domainWalkURI),
571                      NS_LITERAL_CSTRING("http://") + Substring(host, offset));
572      NS_ENSURE_SUCCESS(rv, rv);
573
574      rv = mPermMgr->TestExactPermission(domainWalkURI,
575                                          aType,
576                                          &actualExactPermission);
577      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      if (!entry) {
584        if (actualExactPermission != nsIPermissionManager::UNKNOWN_ACTION) {
585          // no cached data but a permission in the permission manager so use
586          // it and stop looking.
587          *aPermission = actualExactPermission;
588          STSLOG(("no PM Table entry for %s, using permmgr", host.get()+offset));
589          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      else if (entry->mDeleted || (strcmp(aType, STS_SUBDOMAIN_PERMISSION) == 0
597                                  && !entry->mIncludeSubdomains)) {
598        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        *aPermission = nsIPermissionManager::ALLOW_ACTION;
607        STSLOG(("PM Table entry for %s: forcing", host.get()+offset));
608        break;
609      }
610
611      // Don't continue walking up the host segments if the test was for an
612      // exact match only.
613      if (testExact) break;
614
615      STSLOG(("no PM Table entry or permmgr data for %s, walking up domain",
616              host.get()+offset));
617      // walk up the host segments
618      offset = host.FindChar('.', offset) + 1;
619    } while (offset > 0);
620
621    // Use whatever we ended up with, which defaults to UNKNOWN_ACTION.
622    return NS_OK;
623}