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