PageRenderTime 104ms CodeModel.GetById 23ms app.highlight 73ms RepoModel.GetById 2ms app.codeStats 0ms

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