security/manager/ssl/src/nsCertOverrideService.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial