/security/manager/ssl/src/nsCertOverrideService.cpp

http://github.com/zpao/v8monkey · C++ · 931 lines · 708 code · 148 blank · 75 comment · 111 complexity · 42841b47f7beecdaf68e42131d947598 MD5 · raw file

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2. *
  3. * ***** BEGIN LICENSE BLOCK *****
  4. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  5. *
  6. * The contents of this file are subject to the Mozilla Public License Version
  7. * 1.1 (the "License"); you may not use this file except in compliance with
  8. * the License. You may obtain a copy of the License at
  9. * http://www.mozilla.org/MPL/
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. *
  16. * The Original Code is mozilla.org code.
  17. *
  18. * The Initial Developer of the Original Code is
  19. * Red Hat, Inc.
  20. * Portions created by the Initial Developer are Copyright (C) 2006
  21. * the Initial Developer. All Rights Reserved.
  22. *
  23. * Contributor(s):
  24. * Kai Engert <kengert@redhat.com>
  25. * Ehsan Akhgari <ehsan.akhgari@gmail.com>
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either the GNU General Public License Version 2 or later (the "GPL"), or
  29. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. * in which case the provisions of the GPL or the LGPL are applicable instead
  31. * of those above. If you wish to allow use of your version of this file only
  32. * under the terms of either the GPL or the LGPL, and not to allow others to
  33. * use your version of this file under the terms of the MPL, indicate your
  34. * decision by deleting the provisions above and replace them with the notice
  35. * and other provisions required by the GPL or the LGPL. If you do not delete
  36. * the provisions above, a recipient may use your version of this file under
  37. * the terms of any one of the MPL, the GPL or the LGPL.
  38. *
  39. * ***** END LICENSE BLOCK ***** */
  40. #include "nsCertOverrideService.h"
  41. #include "nsIX509Cert.h"
  42. #include "nsNSSCertificate.h"
  43. #include "nsCRT.h"
  44. #include "nsAppDirectoryServiceDefs.h"
  45. #include "nsStreamUtils.h"
  46. #include "nsNetUtil.h"
  47. #include "nsILineInputStream.h"
  48. #include "nsIObserver.h"
  49. #include "nsIObserverService.h"
  50. #include "nsISupportsPrimitives.h"
  51. #include "nsPromiseFlatString.h"
  52. #include "nsThreadUtils.h"
  53. #include "nsStringBuffer.h"
  54. #include "nsAutoPtr.h"
  55. #include "nspr.h"
  56. #include "pk11pub.h"
  57. #include "certdb.h"
  58. #include "sechash.h"
  59. #include "ssl.h" // For SSL_ClearSessionCache
  60. #include "nsNSSCleaner.h"
  61. NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
  62. using namespace mozilla;
  63. static const char kCertOverrideFileName[] = "cert_override.txt";
  64. void
  65. nsCertOverride::convertBitsToString(OverrideBits ob, nsACString &str)
  66. {
  67. str.Truncate();
  68. if (ob & ob_Mismatch)
  69. str.Append('M');
  70. if (ob & ob_Untrusted)
  71. str.Append('U');
  72. if (ob & ob_Time_error)
  73. str.Append('T');
  74. }
  75. void
  76. nsCertOverride::convertStringToBits(const nsACString &str, OverrideBits &ob)
  77. {
  78. const nsPromiseFlatCString &flat = PromiseFlatCString(str);
  79. const char *walk = flat.get();
  80. ob = ob_None;
  81. for ( ; *walk; ++walk)
  82. {
  83. switch (*walk)
  84. {
  85. case 'm':
  86. case 'M':
  87. ob = (OverrideBits)(ob | ob_Mismatch);
  88. break;
  89. case 'u':
  90. case 'U':
  91. ob = (OverrideBits)(ob | ob_Untrusted);
  92. break;
  93. case 't':
  94. case 'T':
  95. ob = (OverrideBits)(ob | ob_Time_error);
  96. break;
  97. default:
  98. break;
  99. }
  100. }
  101. }
  102. NS_IMPL_THREADSAFE_ISUPPORTS3(nsCertOverrideService,
  103. nsICertOverrideService,
  104. nsIObserver,
  105. nsISupportsWeakReference)
  106. nsCertOverrideService::nsCertOverrideService()
  107. : monitor("nsCertOverrideService.monitor")
  108. {
  109. }
  110. nsCertOverrideService::~nsCertOverrideService()
  111. {
  112. }
  113. nsresult
  114. nsCertOverrideService::Init()
  115. {
  116. if (!NS_IsMainThread()) {
  117. NS_NOTREACHED("nsCertOverrideService initialized off main thread");
  118. return NS_ERROR_NOT_SAME_THREAD;
  119. }
  120. if (!mSettingsTable.Init())
  121. return NS_ERROR_OUT_OF_MEMORY;
  122. mOidTagForStoringNewHashes = SEC_OID_SHA256;
  123. SECOidData *od = SECOID_FindOIDByTag(mOidTagForStoringNewHashes);
  124. if (!od)
  125. return NS_ERROR_FAILURE;
  126. char *dotted_oid = CERT_GetOidString(&od->oid);
  127. if (!dotted_oid)
  128. return NS_ERROR_FAILURE;
  129. mDottedOidForStoringNewHashes = dotted_oid;
  130. PR_smprintf_free(dotted_oid);
  131. nsCOMPtr<nsIObserverService> observerService =
  132. mozilla::services::GetObserverService();
  133. // If we cannot add ourselves as a profile change observer, then we will not
  134. // attempt to read/write any settings file. Otherwise, we would end up
  135. // reading/writing the wrong settings file after a profile change.
  136. if (observerService) {
  137. observerService->AddObserver(this, "profile-before-change", true);
  138. observerService->AddObserver(this, "profile-do-change", true);
  139. // simulate a profile change so we read the current profile's settings file
  140. Observe(nsnull, "profile-do-change", nsnull);
  141. }
  142. return NS_OK;
  143. }
  144. NS_IMETHODIMP
  145. nsCertOverrideService::Observe(nsISupports *,
  146. const char *aTopic,
  147. const PRUnichar *aData)
  148. {
  149. // check the topic
  150. if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
  151. // The profile is about to change,
  152. // or is going away because the application is shutting down.
  153. ReentrantMonitorAutoEnter lock(monitor);
  154. if (!nsCRT::strcmp(aData, NS_LITERAL_STRING("shutdown-cleanse").get())) {
  155. RemoveAllFromMemory();
  156. // delete the storage file
  157. if (mSettingsFile) {
  158. mSettingsFile->Remove(false);
  159. }
  160. } else {
  161. RemoveAllFromMemory();
  162. }
  163. } else if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
  164. // The profile has already changed.
  165. // Now read from the new profile location.
  166. // we also need to update the cached file location
  167. ReentrantMonitorAutoEnter lock(monitor);
  168. nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mSettingsFile));
  169. if (NS_SUCCEEDED(rv)) {
  170. mSettingsFile->AppendNative(NS_LITERAL_CSTRING(kCertOverrideFileName));
  171. } else {
  172. mSettingsFile = nsnull;
  173. }
  174. Read();
  175. }
  176. return NS_OK;
  177. }
  178. void
  179. nsCertOverrideService::RemoveAllFromMemory()
  180. {
  181. ReentrantMonitorAutoEnter lock(monitor);
  182. mSettingsTable.Clear();
  183. }
  184. PR_STATIC_CALLBACK(PLDHashOperator)
  185. RemoveTemporariesCallback(nsCertOverrideEntry *aEntry,
  186. void *aArg)
  187. {
  188. if (aEntry && aEntry->mSettings.mIsTemporary) {
  189. aEntry->mSettings.mCert = nsnull;
  190. return PL_DHASH_REMOVE;
  191. }
  192. return PL_DHASH_NEXT;
  193. }
  194. void
  195. nsCertOverrideService::RemoveAllTemporaryOverrides()
  196. {
  197. {
  198. ReentrantMonitorAutoEnter lock(monitor);
  199. mSettingsTable.EnumerateEntries(RemoveTemporariesCallback, nsnull);
  200. // no need to write, as temporaries are never written to disk
  201. }
  202. }
  203. nsresult
  204. nsCertOverrideService::Read()
  205. {
  206. ReentrantMonitorAutoEnter lock(monitor);
  207. // If we don't have a profile, then we won't try to read any settings file.
  208. if (!mSettingsFile)
  209. return NS_OK;
  210. nsresult rv;
  211. nsCOMPtr<nsIInputStream> fileInputStream;
  212. rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), mSettingsFile);
  213. if (NS_FAILED(rv)) {
  214. return rv;
  215. }
  216. nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
  217. if (NS_FAILED(rv)) {
  218. return rv;
  219. }
  220. nsCAutoString buffer;
  221. bool isMore = true;
  222. PRInt32 hostIndex = 0, algoIndex, fingerprintIndex, overrideBitsIndex, dbKeyIndex;
  223. /* file format is:
  224. *
  225. * host:port \t fingerprint-algorithm \t fingerprint \t override-mask \t dbKey
  226. *
  227. * where override-mask is a sequence of characters,
  228. * M meaning hostname-Mismatch-override
  229. * U meaning Untrusted-override
  230. * T meaning Time-error-override (expired/not yet valid)
  231. *
  232. * if this format isn't respected we move onto the next line in the file.
  233. */
  234. while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
  235. if (buffer.IsEmpty() || buffer.First() == '#') {
  236. continue;
  237. }
  238. // this is a cheap, cheesy way of parsing a tab-delimited line into
  239. // string indexes, which can be lopped off into substrings. just for
  240. // purposes of obfuscation, it also checks that each token was found.
  241. // todo: use iterators?
  242. if ((algoIndex = buffer.FindChar('\t', hostIndex) + 1) == 0 ||
  243. (fingerprintIndex = buffer.FindChar('\t', algoIndex) + 1) == 0 ||
  244. (overrideBitsIndex = buffer.FindChar('\t', fingerprintIndex) + 1) == 0 ||
  245. (dbKeyIndex = buffer.FindChar('\t', overrideBitsIndex) + 1) == 0) {
  246. continue;
  247. }
  248. const nsASingleFragmentCString &tmp = Substring(buffer, hostIndex, algoIndex - hostIndex - 1);
  249. const nsASingleFragmentCString &algo_string = Substring(buffer, algoIndex, fingerprintIndex - algoIndex - 1);
  250. const nsASingleFragmentCString &fingerprint = Substring(buffer, fingerprintIndex, overrideBitsIndex - fingerprintIndex - 1);
  251. const nsASingleFragmentCString &bits_string = Substring(buffer, overrideBitsIndex, dbKeyIndex - overrideBitsIndex - 1);
  252. const nsASingleFragmentCString &db_key = Substring(buffer, dbKeyIndex, buffer.Length() - dbKeyIndex);
  253. nsCAutoString host(tmp);
  254. nsCertOverride::OverrideBits bits;
  255. nsCertOverride::convertStringToBits(bits_string, bits);
  256. PRInt32 port;
  257. PRInt32 portIndex = host.RFindChar(':');
  258. if (portIndex == kNotFound)
  259. continue; // Ignore broken entries
  260. PRInt32 portParseError;
  261. nsCAutoString portString(Substring(host, portIndex+1));
  262. port = portString.ToInteger(&portParseError);
  263. if (portParseError)
  264. continue; // Ignore broken entries
  265. host.Truncate(portIndex);
  266. AddEntryToList(host, port,
  267. nsnull, // don't have the cert
  268. false, // not temporary
  269. algo_string, fingerprint, bits, db_key);
  270. }
  271. return NS_OK;
  272. }
  273. PR_STATIC_CALLBACK(PLDHashOperator)
  274. WriteEntryCallback(nsCertOverrideEntry *aEntry,
  275. void *aArg)
  276. {
  277. static const char kTab[] = "\t";
  278. nsIOutputStream *rawStreamPtr = (nsIOutputStream *)aArg;
  279. nsresult rv;
  280. if (rawStreamPtr && aEntry)
  281. {
  282. const nsCertOverride &settings = aEntry->mSettings;
  283. if (settings.mIsTemporary)
  284. return PL_DHASH_NEXT;
  285. nsCAutoString bits_string;
  286. nsCertOverride::convertBitsToString(settings.mOverrideBits,
  287. bits_string);
  288. rawStreamPtr->Write(aEntry->mHostWithPort.get(), aEntry->mHostWithPort.Length(), &rv);
  289. rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
  290. rawStreamPtr->Write(settings.mFingerprintAlgOID.get(),
  291. settings.mFingerprintAlgOID.Length(), &rv);
  292. rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
  293. rawStreamPtr->Write(settings.mFingerprint.get(),
  294. settings.mFingerprint.Length(), &rv);
  295. rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
  296. rawStreamPtr->Write(bits_string.get(),
  297. bits_string.Length(), &rv);
  298. rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
  299. rawStreamPtr->Write(settings.mDBKey.get(), settings.mDBKey.Length(), &rv);
  300. rawStreamPtr->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &rv);
  301. }
  302. return PL_DHASH_NEXT;
  303. }
  304. nsresult
  305. nsCertOverrideService::Write()
  306. {
  307. ReentrantMonitorAutoEnter lock(monitor);
  308. // If we don't have any profile, then we won't try to write any file
  309. if (!mSettingsFile) {
  310. return NS_OK;
  311. }
  312. nsresult rv;
  313. nsCOMPtr<nsIOutputStream> fileOutputStream;
  314. rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream),
  315. mSettingsFile,
  316. -1,
  317. 0600);
  318. if (NS_FAILED(rv)) {
  319. NS_ERROR("failed to open cert_warn_settings.txt for writing");
  320. return rv;
  321. }
  322. // get a buffered output stream 4096 bytes big, to optimize writes
  323. nsCOMPtr<nsIOutputStream> bufferedOutputStream;
  324. rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), fileOutputStream, 4096);
  325. if (NS_FAILED(rv)) {
  326. return rv;
  327. }
  328. static const char kHeader[] =
  329. "# PSM Certificate Override Settings file" NS_LINEBREAK
  330. "# This is a generated file! Do not edit." NS_LINEBREAK;
  331. /* see ::Read for file format */
  332. bufferedOutputStream->Write(kHeader, sizeof(kHeader) - 1, &rv);
  333. nsIOutputStream *rawStreamPtr = bufferedOutputStream;
  334. mSettingsTable.EnumerateEntries(WriteEntryCallback, rawStreamPtr);
  335. // All went ok. Maybe except for problems in Write(), but the stream detects
  336. // that for us
  337. nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(bufferedOutputStream);
  338. NS_ASSERTION(safeStream, "expected a safe output stream!");
  339. if (safeStream) {
  340. rv = safeStream->Finish();
  341. if (NS_FAILED(rv)) {
  342. NS_WARNING("failed to save cert warn settings file! possible dataloss");
  343. return rv;
  344. }
  345. }
  346. return NS_OK;
  347. }
  348. static nsresult
  349. GetCertFingerprintByOidTag(CERTCertificate* nsscert,
  350. SECOidTag aOidTag,
  351. nsCString &fp)
  352. {
  353. unsigned int hash_len = HASH_ResultLenByOidTag(aOidTag);
  354. nsStringBuffer* fingerprint = nsStringBuffer::Alloc(hash_len);
  355. if (!fingerprint)
  356. return NS_ERROR_OUT_OF_MEMORY;
  357. PK11_HashBuf(aOidTag, (unsigned char*)fingerprint->Data(),
  358. nsscert->derCert.data, nsscert->derCert.len);
  359. SECItem fpItem;
  360. fpItem.data = (unsigned char*)fingerprint->Data();
  361. fpItem.len = hash_len;
  362. char *tmpstr = CERT_Hexify(&fpItem, 1);
  363. fp.Assign(tmpstr);
  364. PORT_Free(tmpstr);
  365. fingerprint->Release();
  366. return NS_OK;
  367. }
  368. static nsresult
  369. GetCertFingerprintByOidTag(nsIX509Cert *aCert,
  370. SECOidTag aOidTag,
  371. nsCString &fp)
  372. {
  373. nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert);
  374. if (!cert2)
  375. return NS_ERROR_FAILURE;
  376. CERTCertificate* nsscert = cert2->GetCert();
  377. if (!nsscert)
  378. return NS_ERROR_FAILURE;
  379. CERTCertificateCleaner nsscertCleaner(nsscert);
  380. return GetCertFingerprintByOidTag(nsscert, aOidTag, fp);
  381. }
  382. static nsresult
  383. GetCertFingerprintByDottedOidString(CERTCertificate* nsscert,
  384. const nsCString &dottedOid,
  385. nsCString &fp)
  386. {
  387. SECItem oid;
  388. oid.data = nsnull;
  389. oid.len = 0;
  390. SECStatus srv = SEC_StringToOID(nsnull, &oid,
  391. dottedOid.get(), dottedOid.Length());
  392. if (srv != SECSuccess)
  393. return NS_ERROR_FAILURE;
  394. SECOidTag oid_tag = SECOID_FindOIDTag(&oid);
  395. SECITEM_FreeItem(&oid, false);
  396. if (oid_tag == SEC_OID_UNKNOWN)
  397. return NS_ERROR_FAILURE;
  398. return GetCertFingerprintByOidTag(nsscert, oid_tag, fp);
  399. }
  400. static nsresult
  401. GetCertFingerprintByDottedOidString(nsIX509Cert *aCert,
  402. const nsCString &dottedOid,
  403. nsCString &fp)
  404. {
  405. nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert);
  406. if (!cert2)
  407. return NS_ERROR_FAILURE;
  408. CERTCertificate* nsscert = cert2->GetCert();
  409. if (!nsscert)
  410. return NS_ERROR_FAILURE;
  411. CERTCertificateCleaner nsscertCleaner(nsscert);
  412. return GetCertFingerprintByDottedOidString(nsscert, dottedOid, fp);
  413. }
  414. NS_IMETHODIMP
  415. nsCertOverrideService::RememberValidityOverride(const nsACString & aHostName, PRInt32 aPort,
  416. nsIX509Cert *aCert,
  417. PRUint32 aOverrideBits,
  418. bool aTemporary)
  419. {
  420. NS_ENSURE_ARG_POINTER(aCert);
  421. if (aHostName.IsEmpty())
  422. return NS_ERROR_INVALID_ARG;
  423. if (aPort < -1)
  424. return NS_ERROR_INVALID_ARG;
  425. nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert);
  426. if (!cert2)
  427. return NS_ERROR_FAILURE;
  428. CERTCertificate* nsscert = cert2->GetCert();
  429. if (!nsscert)
  430. return NS_ERROR_FAILURE;
  431. CERTCertificateCleaner nsscertCleaner(nsscert);
  432. char* nickname = nsNSSCertificate::defaultServerNickname(nsscert);
  433. if (!aTemporary && nickname && *nickname)
  434. {
  435. PK11SlotInfo *slot = PK11_GetInternalKeySlot();
  436. if (!slot) {
  437. PR_Free(nickname);
  438. return NS_ERROR_FAILURE;
  439. }
  440. SECStatus srv = PK11_ImportCert(slot, nsscert, CK_INVALID_HANDLE,
  441. nickname, false);
  442. PK11_FreeSlot(slot);
  443. if (srv != SECSuccess) {
  444. PR_Free(nickname);
  445. return NS_ERROR_FAILURE;
  446. }
  447. }
  448. PR_FREEIF(nickname);
  449. nsCAutoString fpStr;
  450. nsresult rv = GetCertFingerprintByOidTag(nsscert,
  451. mOidTagForStoringNewHashes, fpStr);
  452. if (NS_FAILED(rv))
  453. return rv;
  454. char *dbkey = NULL;
  455. rv = aCert->GetDbKey(&dbkey);
  456. if (NS_FAILED(rv) || !dbkey)
  457. return rv;
  458. // change \n and \r to spaces in the possibly multi-line-base64-encoded key
  459. for (char *dbkey_walk = dbkey;
  460. *dbkey_walk;
  461. ++dbkey_walk) {
  462. char c = *dbkey_walk;
  463. if (c == '\r' || c == '\n') {
  464. *dbkey_walk = ' ';
  465. }
  466. }
  467. {
  468. ReentrantMonitorAutoEnter lock(monitor);
  469. AddEntryToList(aHostName, aPort,
  470. aTemporary ? aCert : nsnull,
  471. // keep a reference to the cert for temporary overrides
  472. aTemporary,
  473. mDottedOidForStoringNewHashes, fpStr,
  474. (nsCertOverride::OverrideBits)aOverrideBits,
  475. nsDependentCString(dbkey));
  476. Write();
  477. }
  478. PR_Free(dbkey);
  479. return NS_OK;
  480. }
  481. NS_IMETHODIMP
  482. nsCertOverrideService::HasMatchingOverride(const nsACString & aHostName, PRInt32 aPort,
  483. nsIX509Cert *aCert,
  484. PRUint32 *aOverrideBits,
  485. bool *aIsTemporary,
  486. bool *_retval)
  487. {
  488. if (aHostName.IsEmpty())
  489. return NS_ERROR_INVALID_ARG;
  490. if (aPort < -1)
  491. return NS_ERROR_INVALID_ARG;
  492. NS_ENSURE_ARG_POINTER(aCert);
  493. NS_ENSURE_ARG_POINTER(aOverrideBits);
  494. NS_ENSURE_ARG_POINTER(aIsTemporary);
  495. NS_ENSURE_ARG_POINTER(_retval);
  496. *_retval = false;
  497. *aOverrideBits = nsCertOverride::ob_None;
  498. nsCAutoString hostPort;
  499. GetHostWithPort(aHostName, aPort, hostPort);
  500. nsCertOverride settings;
  501. {
  502. ReentrantMonitorAutoEnter lock(monitor);
  503. nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hostPort.get());
  504. if (!entry)
  505. return NS_OK;
  506. settings = entry->mSettings; // copy
  507. }
  508. *aOverrideBits = settings.mOverrideBits;
  509. *aIsTemporary = settings.mIsTemporary;
  510. nsCAutoString fpStr;
  511. nsresult rv;
  512. if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) {
  513. rv = GetCertFingerprintByOidTag(aCert, mOidTagForStoringNewHashes, fpStr);
  514. }
  515. else {
  516. rv = GetCertFingerprintByDottedOidString(aCert, settings.mFingerprintAlgOID, fpStr);
  517. }
  518. if (NS_FAILED(rv))
  519. return rv;
  520. *_retval = settings.mFingerprint.Equals(fpStr);
  521. return NS_OK;
  522. }
  523. NS_IMETHODIMP
  524. nsCertOverrideService::GetValidityOverride(const nsACString & aHostName, PRInt32 aPort,
  525. nsACString & aHashAlg,
  526. nsACString & aFingerprint,
  527. PRUint32 *aOverrideBits,
  528. bool *aIsTemporary,
  529. bool *_found)
  530. {
  531. NS_ENSURE_ARG_POINTER(_found);
  532. NS_ENSURE_ARG_POINTER(aIsTemporary);
  533. NS_ENSURE_ARG_POINTER(aOverrideBits);
  534. *_found = false;
  535. *aOverrideBits = nsCertOverride::ob_None;
  536. nsCAutoString hostPort;
  537. GetHostWithPort(aHostName, aPort, hostPort);
  538. nsCertOverride settings;
  539. {
  540. ReentrantMonitorAutoEnter lock(monitor);
  541. nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hostPort.get());
  542. if (entry) {
  543. *_found = true;
  544. settings = entry->mSettings; // copy
  545. }
  546. }
  547. if (*_found) {
  548. *aOverrideBits = settings.mOverrideBits;
  549. *aIsTemporary = settings.mIsTemporary;
  550. aFingerprint = settings.mFingerprint;
  551. aHashAlg = settings.mFingerprintAlgOID;
  552. }
  553. return NS_OK;
  554. }
  555. nsresult
  556. nsCertOverrideService::AddEntryToList(const nsACString &aHostName, PRInt32 aPort,
  557. nsIX509Cert *aCert,
  558. const bool aIsTemporary,
  559. const nsACString &fingerprintAlgOID,
  560. const nsACString &fingerprint,
  561. nsCertOverride::OverrideBits ob,
  562. const nsACString &dbKey)
  563. {
  564. nsCAutoString hostPort;
  565. GetHostWithPort(aHostName, aPort, hostPort);
  566. {
  567. ReentrantMonitorAutoEnter lock(monitor);
  568. nsCertOverrideEntry *entry = mSettingsTable.PutEntry(hostPort.get());
  569. if (!entry) {
  570. NS_ERROR("can't insert a null entry!");
  571. return NS_ERROR_OUT_OF_MEMORY;
  572. }
  573. entry->mHostWithPort = hostPort;
  574. nsCertOverride &settings = entry->mSettings;
  575. settings.mAsciiHost = aHostName;
  576. settings.mPort = aPort;
  577. settings.mIsTemporary = aIsTemporary;
  578. settings.mFingerprintAlgOID = fingerprintAlgOID;
  579. settings.mFingerprint = fingerprint;
  580. settings.mOverrideBits = ob;
  581. settings.mDBKey = dbKey;
  582. settings.mCert = aCert;
  583. }
  584. return NS_OK;
  585. }
  586. NS_IMETHODIMP
  587. nsCertOverrideService::ClearValidityOverride(const nsACString & aHostName, PRInt32 aPort)
  588. {
  589. if (aPort == 0 &&
  590. aHostName.EqualsLiteral("all:temporary-certificates")) {
  591. RemoveAllTemporaryOverrides();
  592. return NS_OK;
  593. }
  594. nsCAutoString hostPort;
  595. GetHostWithPort(aHostName, aPort, hostPort);
  596. {
  597. ReentrantMonitorAutoEnter lock(monitor);
  598. mSettingsTable.RemoveEntry(hostPort.get());
  599. Write();
  600. }
  601. SSL_ClearSessionCache();
  602. return NS_OK;
  603. }
  604. NS_IMETHODIMP
  605. nsCertOverrideService::GetAllOverrideHostsWithPorts(PRUint32 *aCount,
  606. PRUnichar ***aHostsWithPortsArray)
  607. {
  608. return NS_ERROR_NOT_IMPLEMENTED;
  609. }
  610. static bool
  611. matchesDBKey(nsIX509Cert *cert, const char *match_dbkey)
  612. {
  613. char *dbkey = NULL;
  614. nsresult rv = cert->GetDbKey(&dbkey);
  615. if (NS_FAILED(rv) || !dbkey)
  616. return false;
  617. bool found_mismatch = false;
  618. const char *key1 = dbkey;
  619. const char *key2 = match_dbkey;
  620. // skip over any whitespace when comparing
  621. while (*key1 && *key2) {
  622. char c1 = *key1;
  623. char c2 = *key2;
  624. switch (c1) {
  625. case ' ':
  626. case '\t':
  627. case '\n':
  628. case '\r':
  629. ++key1;
  630. continue;
  631. }
  632. switch (c2) {
  633. case ' ':
  634. case '\t':
  635. case '\n':
  636. case '\r':
  637. ++key2;
  638. continue;
  639. }
  640. if (c1 != c2) {
  641. found_mismatch = true;
  642. break;
  643. }
  644. ++key1;
  645. ++key2;
  646. }
  647. PR_Free(dbkey);
  648. return !found_mismatch;
  649. }
  650. struct nsCertAndBoolsAndInt
  651. {
  652. nsIX509Cert *cert;
  653. bool aCheckTemporaries;
  654. bool aCheckPermanents;
  655. PRUint32 counter;
  656. SECOidTag mOidTagForStoringNewHashes;
  657. nsCString mDottedOidForStoringNewHashes;
  658. };
  659. PR_STATIC_CALLBACK(PLDHashOperator)
  660. FindMatchingCertCallback(nsCertOverrideEntry *aEntry,
  661. void *aArg)
  662. {
  663. nsCertAndBoolsAndInt *cai = (nsCertAndBoolsAndInt *)aArg;
  664. if (cai && aEntry)
  665. {
  666. const nsCertOverride &settings = aEntry->mSettings;
  667. bool still_ok = true;
  668. if ((settings.mIsTemporary && !cai->aCheckTemporaries)
  669. ||
  670. (!settings.mIsTemporary && !cai->aCheckPermanents)) {
  671. still_ok = false;
  672. }
  673. if (still_ok && matchesDBKey(cai->cert, settings.mDBKey.get())) {
  674. nsCAutoString cert_fingerprint;
  675. nsresult rv;
  676. if (settings.mFingerprintAlgOID.Equals(cai->mDottedOidForStoringNewHashes)) {
  677. rv = GetCertFingerprintByOidTag(cai->cert,
  678. cai->mOidTagForStoringNewHashes, cert_fingerprint);
  679. }
  680. else {
  681. rv = GetCertFingerprintByDottedOidString(cai->cert,
  682. settings.mFingerprintAlgOID, cert_fingerprint);
  683. }
  684. if (NS_SUCCEEDED(rv) &&
  685. settings.mFingerprint.Equals(cert_fingerprint)) {
  686. cai->counter++;
  687. }
  688. }
  689. }
  690. return PL_DHASH_NEXT;
  691. }
  692. NS_IMETHODIMP
  693. nsCertOverrideService::IsCertUsedForOverrides(nsIX509Cert *aCert,
  694. bool aCheckTemporaries,
  695. bool aCheckPermanents,
  696. PRUint32 *_retval)
  697. {
  698. NS_ENSURE_ARG(aCert);
  699. NS_ENSURE_ARG(_retval);
  700. nsCertAndBoolsAndInt cai;
  701. cai.cert = aCert;
  702. cai.aCheckTemporaries = aCheckTemporaries;
  703. cai.aCheckPermanents = aCheckPermanents;
  704. cai.counter = 0;
  705. cai.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes;
  706. cai.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes;
  707. {
  708. ReentrantMonitorAutoEnter lock(monitor);
  709. mSettingsTable.EnumerateEntries(FindMatchingCertCallback, &cai);
  710. }
  711. *_retval = cai.counter;
  712. return NS_OK;
  713. }
  714. struct nsCertAndPointerAndCallback
  715. {
  716. nsIX509Cert *cert;
  717. void *userdata;
  718. nsCertOverrideService::CertOverrideEnumerator enumerator;
  719. SECOidTag mOidTagForStoringNewHashes;
  720. nsCString mDottedOidForStoringNewHashes;
  721. };
  722. PR_STATIC_CALLBACK(PLDHashOperator)
  723. EnumerateCertOverridesCallback(nsCertOverrideEntry *aEntry,
  724. void *aArg)
  725. {
  726. nsCertAndPointerAndCallback *capac = (nsCertAndPointerAndCallback *)aArg;
  727. if (capac && aEntry)
  728. {
  729. const nsCertOverride &settings = aEntry->mSettings;
  730. if (!capac->cert) {
  731. (*capac->enumerator)(settings, capac->userdata);
  732. }
  733. else {
  734. if (matchesDBKey(capac->cert, settings.mDBKey.get())) {
  735. nsCAutoString cert_fingerprint;
  736. nsresult rv;
  737. if (settings.mFingerprintAlgOID.Equals(capac->mDottedOidForStoringNewHashes)) {
  738. rv = GetCertFingerprintByOidTag(capac->cert,
  739. capac->mOidTagForStoringNewHashes, cert_fingerprint);
  740. }
  741. else {
  742. rv = GetCertFingerprintByDottedOidString(capac->cert,
  743. settings.mFingerprintAlgOID, cert_fingerprint);
  744. }
  745. if (NS_SUCCEEDED(rv) &&
  746. settings.mFingerprint.Equals(cert_fingerprint)) {
  747. (*capac->enumerator)(settings, capac->userdata);
  748. }
  749. }
  750. }
  751. }
  752. return PL_DHASH_NEXT;
  753. }
  754. nsresult
  755. nsCertOverrideService::EnumerateCertOverrides(nsIX509Cert *aCert,
  756. CertOverrideEnumerator enumerator,
  757. void *aUserData)
  758. {
  759. nsCertAndPointerAndCallback capac;
  760. capac.cert = aCert;
  761. capac.userdata = aUserData;
  762. capac.enumerator = enumerator;
  763. capac.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes;
  764. capac.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes;
  765. {
  766. ReentrantMonitorAutoEnter lock(monitor);
  767. mSettingsTable.EnumerateEntries(EnumerateCertOverridesCallback, &capac);
  768. }
  769. return NS_OK;
  770. }
  771. void
  772. nsCertOverrideService::GetHostWithPort(const nsACString & aHostName, PRInt32 aPort, nsACString& _retval)
  773. {
  774. nsCAutoString hostPort(aHostName);
  775. if (aPort == -1) {
  776. aPort = 443;
  777. }
  778. if (!hostPort.IsEmpty()) {
  779. hostPort.AppendLiteral(":");
  780. hostPort.AppendInt(aPort);
  781. }
  782. _retval.Assign(hostPort);
  783. }