1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/manager/ssl/src/nsCertOverrideService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,870 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsCertOverrideService.h" 1.11 + 1.12 +#include "pkix/pkixtypes.h" 1.13 +#include "nsIX509Cert.h" 1.14 +#include "NSSCertDBTrustDomain.h" 1.15 +#include "nsNSSCertificate.h" 1.16 +#include "nsNSSCertHelper.h" 1.17 +#include "nsCRT.h" 1.18 +#include "nsAppDirectoryServiceDefs.h" 1.19 +#include "nsStreamUtils.h" 1.20 +#include "nsNetUtil.h" 1.21 +#include "nsILineInputStream.h" 1.22 +#include "nsIObserver.h" 1.23 +#include "nsIObserverService.h" 1.24 +#include "nsISupportsPrimitives.h" 1.25 +#include "nsPromiseFlatString.h" 1.26 +#include "nsThreadUtils.h" 1.27 +#include "nsStringBuffer.h" 1.28 +#include "ScopedNSSTypes.h" 1.29 +#include "SharedSSLState.h" 1.30 + 1.31 +#include "nspr.h" 1.32 +#include "pk11pub.h" 1.33 +#include "certdb.h" 1.34 +#include "sechash.h" 1.35 +#include "ssl.h" // For SSL_ClearSessionCache 1.36 + 1.37 +using namespace mozilla; 1.38 +using namespace mozilla::psm; 1.39 + 1.40 +static const char kCertOverrideFileName[] = "cert_override.txt"; 1.41 + 1.42 +void 1.43 +nsCertOverride::convertBitsToString(OverrideBits ob, nsACString &str) 1.44 +{ 1.45 + str.Truncate(); 1.46 + 1.47 + if (ob & ob_Mismatch) 1.48 + str.Append('M'); 1.49 + 1.50 + if (ob & ob_Untrusted) 1.51 + str.Append('U'); 1.52 + 1.53 + if (ob & ob_Time_error) 1.54 + str.Append('T'); 1.55 +} 1.56 + 1.57 +void 1.58 +nsCertOverride::convertStringToBits(const nsACString &str, OverrideBits &ob) 1.59 +{ 1.60 + const nsPromiseFlatCString &flat = PromiseFlatCString(str); 1.61 + const char *walk = flat.get(); 1.62 + 1.63 + ob = ob_None; 1.64 + 1.65 + for ( ; *walk; ++walk) 1.66 + { 1.67 + switch (*walk) 1.68 + { 1.69 + case 'm': 1.70 + case 'M': 1.71 + ob = (OverrideBits)(ob | ob_Mismatch); 1.72 + break; 1.73 + 1.74 + case 'u': 1.75 + case 'U': 1.76 + ob = (OverrideBits)(ob | ob_Untrusted); 1.77 + break; 1.78 + 1.79 + case 't': 1.80 + case 'T': 1.81 + ob = (OverrideBits)(ob | ob_Time_error); 1.82 + break; 1.83 + 1.84 + default: 1.85 + break; 1.86 + } 1.87 + } 1.88 +} 1.89 + 1.90 +NS_IMPL_ISUPPORTS(nsCertOverrideService, 1.91 + nsICertOverrideService, 1.92 + nsIObserver, 1.93 + nsISupportsWeakReference) 1.94 + 1.95 +nsCertOverrideService::nsCertOverrideService() 1.96 + : monitor("nsCertOverrideService.monitor") 1.97 +{ 1.98 +} 1.99 + 1.100 +nsCertOverrideService::~nsCertOverrideService() 1.101 +{ 1.102 +} 1.103 + 1.104 +nsresult 1.105 +nsCertOverrideService::Init() 1.106 +{ 1.107 + if (!NS_IsMainThread()) { 1.108 + NS_NOTREACHED("nsCertOverrideService initialized off main thread"); 1.109 + return NS_ERROR_NOT_SAME_THREAD; 1.110 + } 1.111 + 1.112 + mOidTagForStoringNewHashes = SEC_OID_SHA256; 1.113 + 1.114 + SECOidData *od = SECOID_FindOIDByTag(mOidTagForStoringNewHashes); 1.115 + if (!od) 1.116 + return NS_ERROR_FAILURE; 1.117 + 1.118 + char *dotted_oid = CERT_GetOidString(&od->oid); 1.119 + if (!dotted_oid) 1.120 + return NS_ERROR_FAILURE; 1.121 + 1.122 + mDottedOidForStoringNewHashes = dotted_oid; 1.123 + PR_smprintf_free(dotted_oid); 1.124 + 1.125 + nsCOMPtr<nsIObserverService> observerService = 1.126 + mozilla::services::GetObserverService(); 1.127 + 1.128 + // If we cannot add ourselves as a profile change observer, then we will not 1.129 + // attempt to read/write any settings file. Otherwise, we would end up 1.130 + // reading/writing the wrong settings file after a profile change. 1.131 + if (observerService) { 1.132 + observerService->AddObserver(this, "profile-before-change", true); 1.133 + observerService->AddObserver(this, "profile-do-change", true); 1.134 + // simulate a profile change so we read the current profile's settings file 1.135 + Observe(nullptr, "profile-do-change", nullptr); 1.136 + } 1.137 + 1.138 + SharedSSLState::NoteCertOverrideServiceInstantiated(); 1.139 + return NS_OK; 1.140 +} 1.141 + 1.142 +NS_IMETHODIMP 1.143 +nsCertOverrideService::Observe(nsISupports *, 1.144 + const char *aTopic, 1.145 + const char16_t *aData) 1.146 +{ 1.147 + // check the topic 1.148 + if (!nsCRT::strcmp(aTopic, "profile-before-change")) { 1.149 + // The profile is about to change, 1.150 + // or is going away because the application is shutting down. 1.151 + 1.152 + ReentrantMonitorAutoEnter lock(monitor); 1.153 + 1.154 + if (!nsCRT::strcmp(aData, MOZ_UTF16("shutdown-cleanse"))) { 1.155 + RemoveAllFromMemory(); 1.156 + // delete the storage file 1.157 + if (mSettingsFile) { 1.158 + mSettingsFile->Remove(false); 1.159 + } 1.160 + } else { 1.161 + RemoveAllFromMemory(); 1.162 + } 1.163 + 1.164 + } else if (!nsCRT::strcmp(aTopic, "profile-do-change")) { 1.165 + // The profile has already changed. 1.166 + // Now read from the new profile location. 1.167 + // we also need to update the cached file location 1.168 + 1.169 + ReentrantMonitorAutoEnter lock(monitor); 1.170 + 1.171 + nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mSettingsFile)); 1.172 + if (NS_SUCCEEDED(rv)) { 1.173 + mSettingsFile->AppendNative(NS_LITERAL_CSTRING(kCertOverrideFileName)); 1.174 + } else { 1.175 + mSettingsFile = nullptr; 1.176 + } 1.177 + Read(); 1.178 + 1.179 + } 1.180 + 1.181 + return NS_OK; 1.182 +} 1.183 + 1.184 +void 1.185 +nsCertOverrideService::RemoveAllFromMemory() 1.186 +{ 1.187 + ReentrantMonitorAutoEnter lock(monitor); 1.188 + mSettingsTable.Clear(); 1.189 +} 1.190 + 1.191 +static PLDHashOperator 1.192 +RemoveTemporariesCallback(nsCertOverrideEntry *aEntry, 1.193 + void *aArg) 1.194 +{ 1.195 + if (aEntry && aEntry->mSettings.mIsTemporary) { 1.196 + aEntry->mSettings.mCert = nullptr; 1.197 + return PL_DHASH_REMOVE; 1.198 + } 1.199 + 1.200 + return PL_DHASH_NEXT; 1.201 +} 1.202 + 1.203 +void 1.204 +nsCertOverrideService::RemoveAllTemporaryOverrides() 1.205 +{ 1.206 + { 1.207 + ReentrantMonitorAutoEnter lock(monitor); 1.208 + mSettingsTable.EnumerateEntries(RemoveTemporariesCallback, nullptr); 1.209 + // no need to write, as temporaries are never written to disk 1.210 + } 1.211 +} 1.212 + 1.213 +nsresult 1.214 +nsCertOverrideService::Read() 1.215 +{ 1.216 + ReentrantMonitorAutoEnter lock(monitor); 1.217 + 1.218 + // If we don't have a profile, then we won't try to read any settings file. 1.219 + if (!mSettingsFile) 1.220 + return NS_OK; 1.221 + 1.222 + nsresult rv; 1.223 + nsCOMPtr<nsIInputStream> fileInputStream; 1.224 + rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), mSettingsFile); 1.225 + if (NS_FAILED(rv)) { 1.226 + return rv; 1.227 + } 1.228 + 1.229 + nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv); 1.230 + if (NS_FAILED(rv)) { 1.231 + return rv; 1.232 + } 1.233 + 1.234 + nsAutoCString buffer; 1.235 + bool isMore = true; 1.236 + int32_t hostIndex = 0, algoIndex, fingerprintIndex, overrideBitsIndex, dbKeyIndex; 1.237 + 1.238 + /* file format is: 1.239 + * 1.240 + * host:port \t fingerprint-algorithm \t fingerprint \t override-mask \t dbKey 1.241 + * 1.242 + * where override-mask is a sequence of characters, 1.243 + * M meaning hostname-Mismatch-override 1.244 + * U meaning Untrusted-override 1.245 + * T meaning Time-error-override (expired/not yet valid) 1.246 + * 1.247 + * if this format isn't respected we move onto the next line in the file. 1.248 + */ 1.249 + 1.250 + while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) { 1.251 + if (buffer.IsEmpty() || buffer.First() == '#') { 1.252 + continue; 1.253 + } 1.254 + 1.255 + // this is a cheap, cheesy way of parsing a tab-delimited line into 1.256 + // string indexes, which can be lopped off into substrings. just for 1.257 + // purposes of obfuscation, it also checks that each token was found. 1.258 + // todo: use iterators? 1.259 + if ((algoIndex = buffer.FindChar('\t', hostIndex) + 1) == 0 || 1.260 + (fingerprintIndex = buffer.FindChar('\t', algoIndex) + 1) == 0 || 1.261 + (overrideBitsIndex = buffer.FindChar('\t', fingerprintIndex) + 1) == 0 || 1.262 + (dbKeyIndex = buffer.FindChar('\t', overrideBitsIndex) + 1) == 0) { 1.263 + continue; 1.264 + } 1.265 + 1.266 + const nsASingleFragmentCString &tmp = Substring(buffer, hostIndex, algoIndex - hostIndex - 1); 1.267 + const nsASingleFragmentCString &algo_string = Substring(buffer, algoIndex, fingerprintIndex - algoIndex - 1); 1.268 + const nsASingleFragmentCString &fingerprint = Substring(buffer, fingerprintIndex, overrideBitsIndex - fingerprintIndex - 1); 1.269 + const nsASingleFragmentCString &bits_string = Substring(buffer, overrideBitsIndex, dbKeyIndex - overrideBitsIndex - 1); 1.270 + const nsASingleFragmentCString &db_key = Substring(buffer, dbKeyIndex, buffer.Length() - dbKeyIndex); 1.271 + 1.272 + nsAutoCString host(tmp); 1.273 + nsCertOverride::OverrideBits bits; 1.274 + nsCertOverride::convertStringToBits(bits_string, bits); 1.275 + 1.276 + int32_t port; 1.277 + int32_t portIndex = host.RFindChar(':'); 1.278 + if (portIndex == kNotFound) 1.279 + continue; // Ignore broken entries 1.280 + 1.281 + nsresult portParseError; 1.282 + nsAutoCString portString(Substring(host, portIndex+1)); 1.283 + port = portString.ToInteger(&portParseError); 1.284 + if (NS_FAILED(portParseError)) 1.285 + continue; // Ignore broken entries 1.286 + 1.287 + host.Truncate(portIndex); 1.288 + 1.289 + AddEntryToList(host, port, 1.290 + nullptr, // don't have the cert 1.291 + false, // not temporary 1.292 + algo_string, fingerprint, bits, db_key); 1.293 + } 1.294 + 1.295 + return NS_OK; 1.296 +} 1.297 + 1.298 +static PLDHashOperator 1.299 +WriteEntryCallback(nsCertOverrideEntry *aEntry, 1.300 + void *aArg) 1.301 +{ 1.302 + static const char kTab[] = "\t"; 1.303 + 1.304 + nsIOutputStream *rawStreamPtr = (nsIOutputStream *)aArg; 1.305 + 1.306 + uint32_t unused; 1.307 + 1.308 + if (rawStreamPtr && aEntry) 1.309 + { 1.310 + const nsCertOverride &settings = aEntry->mSettings; 1.311 + if (settings.mIsTemporary) 1.312 + return PL_DHASH_NEXT; 1.313 + 1.314 + nsAutoCString bits_string; 1.315 + nsCertOverride::convertBitsToString(settings.mOverrideBits, 1.316 + bits_string); 1.317 + 1.318 + rawStreamPtr->Write(aEntry->mHostWithPort.get(), aEntry->mHostWithPort.Length(), &unused); 1.319 + rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &unused); 1.320 + rawStreamPtr->Write(settings.mFingerprintAlgOID.get(), 1.321 + settings.mFingerprintAlgOID.Length(), &unused); 1.322 + rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &unused); 1.323 + rawStreamPtr->Write(settings.mFingerprint.get(), 1.324 + settings.mFingerprint.Length(), &unused); 1.325 + rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &unused); 1.326 + rawStreamPtr->Write(bits_string.get(), 1.327 + bits_string.Length(), &unused); 1.328 + rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &unused); 1.329 + rawStreamPtr->Write(settings.mDBKey.get(), settings.mDBKey.Length(), &unused); 1.330 + rawStreamPtr->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &unused); 1.331 + } 1.332 + 1.333 + return PL_DHASH_NEXT; 1.334 +} 1.335 + 1.336 +nsresult 1.337 +nsCertOverrideService::Write() 1.338 +{ 1.339 + ReentrantMonitorAutoEnter lock(monitor); 1.340 + 1.341 + // If we don't have any profile, then we won't try to write any file 1.342 + if (!mSettingsFile) { 1.343 + return NS_OK; 1.344 + } 1.345 + 1.346 + nsresult rv; 1.347 + nsCOMPtr<nsIOutputStream> fileOutputStream; 1.348 + rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream), 1.349 + mSettingsFile, 1.350 + -1, 1.351 + 0600); 1.352 + if (NS_FAILED(rv)) { 1.353 + NS_ERROR("failed to open cert_warn_settings.txt for writing"); 1.354 + return rv; 1.355 + } 1.356 + 1.357 + // get a buffered output stream 4096 bytes big, to optimize writes 1.358 + nsCOMPtr<nsIOutputStream> bufferedOutputStream; 1.359 + rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), fileOutputStream, 4096); 1.360 + if (NS_FAILED(rv)) { 1.361 + return rv; 1.362 + } 1.363 + 1.364 + static const char kHeader[] = 1.365 + "# PSM Certificate Override Settings file" NS_LINEBREAK 1.366 + "# This is a generated file! Do not edit." NS_LINEBREAK; 1.367 + 1.368 + /* see ::Read for file format */ 1.369 + 1.370 + uint32_t unused; 1.371 + bufferedOutputStream->Write(kHeader, sizeof(kHeader) - 1, &unused); 1.372 + 1.373 + nsIOutputStream *rawStreamPtr = bufferedOutputStream; 1.374 + mSettingsTable.EnumerateEntries(WriteEntryCallback, rawStreamPtr); 1.375 + 1.376 + // All went ok. Maybe except for problems in Write(), but the stream detects 1.377 + // that for us 1.378 + nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(bufferedOutputStream); 1.379 + NS_ASSERTION(safeStream, "expected a safe output stream!"); 1.380 + if (safeStream) { 1.381 + rv = safeStream->Finish(); 1.382 + if (NS_FAILED(rv)) { 1.383 + NS_WARNING("failed to save cert warn settings file! possible dataloss"); 1.384 + return rv; 1.385 + } 1.386 + } 1.387 + 1.388 + return NS_OK; 1.389 +} 1.390 + 1.391 +static nsresult 1.392 +GetCertFingerprintByOidTag(nsIX509Cert *aCert, 1.393 + SECOidTag aOidTag, 1.394 + nsCString &fp) 1.395 +{ 1.396 + nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert); 1.397 + if (!cert2) 1.398 + return NS_ERROR_FAILURE; 1.399 + 1.400 + mozilla::pkix::ScopedCERTCertificate nsscert(cert2->GetCert()); 1.401 + if (!nsscert) 1.402 + return NS_ERROR_FAILURE; 1.403 + 1.404 + return GetCertFingerprintByOidTag(nsscert.get(), aOidTag, fp); 1.405 +} 1.406 + 1.407 +static nsresult 1.408 +GetCertFingerprintByDottedOidString(CERTCertificate* nsscert, 1.409 + const nsCString &dottedOid, 1.410 + nsCString &fp) 1.411 +{ 1.412 + SECItem oid; 1.413 + oid.data = nullptr; 1.414 + oid.len = 0; 1.415 + SECStatus srv = SEC_StringToOID(nullptr, &oid, 1.416 + dottedOid.get(), dottedOid.Length()); 1.417 + if (srv != SECSuccess) 1.418 + return NS_ERROR_FAILURE; 1.419 + 1.420 + SECOidTag oid_tag = SECOID_FindOIDTag(&oid); 1.421 + SECITEM_FreeItem(&oid, false); 1.422 + 1.423 + if (oid_tag == SEC_OID_UNKNOWN) 1.424 + return NS_ERROR_FAILURE; 1.425 + 1.426 + return GetCertFingerprintByOidTag(nsscert, oid_tag, fp); 1.427 +} 1.428 + 1.429 +static nsresult 1.430 +GetCertFingerprintByDottedOidString(nsIX509Cert *aCert, 1.431 + const nsCString &dottedOid, 1.432 + nsCString &fp) 1.433 +{ 1.434 + nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert); 1.435 + if (!cert2) 1.436 + return NS_ERROR_FAILURE; 1.437 + 1.438 + mozilla::pkix::ScopedCERTCertificate nsscert(cert2->GetCert()); 1.439 + if (!nsscert) 1.440 + return NS_ERROR_FAILURE; 1.441 + 1.442 + return GetCertFingerprintByDottedOidString(nsscert.get(), dottedOid, fp); 1.443 +} 1.444 + 1.445 +NS_IMETHODIMP 1.446 +nsCertOverrideService::RememberValidityOverride(const nsACString & aHostName, int32_t aPort, 1.447 + nsIX509Cert *aCert, 1.448 + uint32_t aOverrideBits, 1.449 + bool aTemporary) 1.450 +{ 1.451 + NS_ENSURE_ARG_POINTER(aCert); 1.452 + if (aHostName.IsEmpty()) 1.453 + return NS_ERROR_INVALID_ARG; 1.454 + if (aPort < -1) 1.455 + return NS_ERROR_INVALID_ARG; 1.456 + 1.457 + nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert); 1.458 + if (!cert2) 1.459 + return NS_ERROR_FAILURE; 1.460 + 1.461 + mozilla::pkix::ScopedCERTCertificate nsscert(cert2->GetCert()); 1.462 + if (!nsscert) 1.463 + return NS_ERROR_FAILURE; 1.464 + 1.465 + char* nickname = DefaultServerNicknameForCert(nsscert.get()); 1.466 + if (!aTemporary && nickname && *nickname) 1.467 + { 1.468 + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); 1.469 + if (!slot) { 1.470 + PR_Free(nickname); 1.471 + return NS_ERROR_FAILURE; 1.472 + } 1.473 + 1.474 + SECStatus srv = PK11_ImportCert(slot, nsscert.get(), CK_INVALID_HANDLE, 1.475 + nickname, false); 1.476 + if (srv != SECSuccess) { 1.477 + PR_Free(nickname); 1.478 + return NS_ERROR_FAILURE; 1.479 + } 1.480 + } 1.481 + PR_FREEIF(nickname); 1.482 + 1.483 + nsAutoCString fpStr; 1.484 + nsresult rv = GetCertFingerprintByOidTag(nsscert.get(), 1.485 + mOidTagForStoringNewHashes, fpStr); 1.486 + if (NS_FAILED(rv)) 1.487 + return rv; 1.488 + 1.489 + char *dbkey = nullptr; 1.490 + rv = aCert->GetDbKey(&dbkey); 1.491 + if (NS_FAILED(rv) || !dbkey) 1.492 + return rv; 1.493 + 1.494 + // change \n and \r to spaces in the possibly multi-line-base64-encoded key 1.495 + for (char *dbkey_walk = dbkey; 1.496 + *dbkey_walk; 1.497 + ++dbkey_walk) { 1.498 + char c = *dbkey_walk; 1.499 + if (c == '\r' || c == '\n') { 1.500 + *dbkey_walk = ' '; 1.501 + } 1.502 + } 1.503 + 1.504 + { 1.505 + ReentrantMonitorAutoEnter lock(monitor); 1.506 + AddEntryToList(aHostName, aPort, 1.507 + aTemporary ? aCert : nullptr, 1.508 + // keep a reference to the cert for temporary overrides 1.509 + aTemporary, 1.510 + mDottedOidForStoringNewHashes, fpStr, 1.511 + (nsCertOverride::OverrideBits)aOverrideBits, 1.512 + nsDependentCString(dbkey)); 1.513 + Write(); 1.514 + } 1.515 + 1.516 + PR_Free(dbkey); 1.517 + return NS_OK; 1.518 +} 1.519 + 1.520 +NS_IMETHODIMP 1.521 +nsCertOverrideService::HasMatchingOverride(const nsACString & aHostName, int32_t aPort, 1.522 + nsIX509Cert *aCert, 1.523 + uint32_t *aOverrideBits, 1.524 + bool *aIsTemporary, 1.525 + bool *_retval) 1.526 +{ 1.527 + if (aHostName.IsEmpty()) 1.528 + return NS_ERROR_INVALID_ARG; 1.529 + if (aPort < -1) 1.530 + return NS_ERROR_INVALID_ARG; 1.531 + 1.532 + NS_ENSURE_ARG_POINTER(aCert); 1.533 + NS_ENSURE_ARG_POINTER(aOverrideBits); 1.534 + NS_ENSURE_ARG_POINTER(aIsTemporary); 1.535 + NS_ENSURE_ARG_POINTER(_retval); 1.536 + *_retval = false; 1.537 + *aOverrideBits = nsCertOverride::ob_None; 1.538 + 1.539 + nsAutoCString hostPort; 1.540 + GetHostWithPort(aHostName, aPort, hostPort); 1.541 + nsCertOverride settings; 1.542 + 1.543 + { 1.544 + ReentrantMonitorAutoEnter lock(monitor); 1.545 + nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hostPort.get()); 1.546 + 1.547 + if (!entry) 1.548 + return NS_OK; 1.549 + 1.550 + settings = entry->mSettings; // copy 1.551 + } 1.552 + 1.553 + *aOverrideBits = settings.mOverrideBits; 1.554 + *aIsTemporary = settings.mIsTemporary; 1.555 + 1.556 + nsAutoCString fpStr; 1.557 + nsresult rv; 1.558 + 1.559 + if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) { 1.560 + rv = GetCertFingerprintByOidTag(aCert, mOidTagForStoringNewHashes, fpStr); 1.561 + } 1.562 + else { 1.563 + rv = GetCertFingerprintByDottedOidString(aCert, settings.mFingerprintAlgOID, fpStr); 1.564 + } 1.565 + if (NS_FAILED(rv)) 1.566 + return rv; 1.567 + 1.568 + *_retval = settings.mFingerprint.Equals(fpStr); 1.569 + return NS_OK; 1.570 +} 1.571 + 1.572 +NS_IMETHODIMP 1.573 +nsCertOverrideService::GetValidityOverride(const nsACString & aHostName, int32_t aPort, 1.574 + nsACString & aHashAlg, 1.575 + nsACString & aFingerprint, 1.576 + uint32_t *aOverrideBits, 1.577 + bool *aIsTemporary, 1.578 + bool *_found) 1.579 +{ 1.580 + NS_ENSURE_ARG_POINTER(_found); 1.581 + NS_ENSURE_ARG_POINTER(aIsTemporary); 1.582 + NS_ENSURE_ARG_POINTER(aOverrideBits); 1.583 + *_found = false; 1.584 + *aOverrideBits = nsCertOverride::ob_None; 1.585 + 1.586 + nsAutoCString hostPort; 1.587 + GetHostWithPort(aHostName, aPort, hostPort); 1.588 + nsCertOverride settings; 1.589 + 1.590 + { 1.591 + ReentrantMonitorAutoEnter lock(monitor); 1.592 + nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hostPort.get()); 1.593 + 1.594 + if (entry) { 1.595 + *_found = true; 1.596 + settings = entry->mSettings; // copy 1.597 + } 1.598 + } 1.599 + 1.600 + if (*_found) { 1.601 + *aOverrideBits = settings.mOverrideBits; 1.602 + *aIsTemporary = settings.mIsTemporary; 1.603 + aFingerprint = settings.mFingerprint; 1.604 + aHashAlg = settings.mFingerprintAlgOID; 1.605 + } 1.606 + 1.607 + return NS_OK; 1.608 +} 1.609 + 1.610 +nsresult 1.611 +nsCertOverrideService::AddEntryToList(const nsACString &aHostName, int32_t aPort, 1.612 + nsIX509Cert *aCert, 1.613 + const bool aIsTemporary, 1.614 + const nsACString &fingerprintAlgOID, 1.615 + const nsACString &fingerprint, 1.616 + nsCertOverride::OverrideBits ob, 1.617 + const nsACString &dbKey) 1.618 +{ 1.619 + nsAutoCString hostPort; 1.620 + GetHostWithPort(aHostName, aPort, hostPort); 1.621 + 1.622 + { 1.623 + ReentrantMonitorAutoEnter lock(monitor); 1.624 + nsCertOverrideEntry *entry = mSettingsTable.PutEntry(hostPort.get()); 1.625 + 1.626 + if (!entry) { 1.627 + NS_ERROR("can't insert a null entry!"); 1.628 + return NS_ERROR_OUT_OF_MEMORY; 1.629 + } 1.630 + 1.631 + entry->mHostWithPort = hostPort; 1.632 + 1.633 + nsCertOverride &settings = entry->mSettings; 1.634 + settings.mAsciiHost = aHostName; 1.635 + settings.mPort = aPort; 1.636 + settings.mIsTemporary = aIsTemporary; 1.637 + settings.mFingerprintAlgOID = fingerprintAlgOID; 1.638 + settings.mFingerprint = fingerprint; 1.639 + settings.mOverrideBits = ob; 1.640 + settings.mDBKey = dbKey; 1.641 + settings.mCert = aCert; 1.642 + } 1.643 + 1.644 + return NS_OK; 1.645 +} 1.646 + 1.647 +NS_IMETHODIMP 1.648 +nsCertOverrideService::ClearValidityOverride(const nsACString & aHostName, int32_t aPort) 1.649 +{ 1.650 + if (aPort == 0 && 1.651 + aHostName.EqualsLiteral("all:temporary-certificates")) { 1.652 + RemoveAllTemporaryOverrides(); 1.653 + return NS_OK; 1.654 + } 1.655 + nsAutoCString hostPort; 1.656 + GetHostWithPort(aHostName, aPort, hostPort); 1.657 + { 1.658 + ReentrantMonitorAutoEnter lock(monitor); 1.659 + mSettingsTable.RemoveEntry(hostPort.get()); 1.660 + Write(); 1.661 + } 1.662 + SSL_ClearSessionCache(); 1.663 + return NS_OK; 1.664 +} 1.665 + 1.666 +NS_IMETHODIMP 1.667 +nsCertOverrideService::GetAllOverrideHostsWithPorts(uint32_t *aCount, 1.668 + char16_t ***aHostsWithPortsArray) 1.669 +{ 1.670 + return NS_ERROR_NOT_IMPLEMENTED; 1.671 +} 1.672 + 1.673 +static bool 1.674 +matchesDBKey(nsIX509Cert *cert, const char *match_dbkey) 1.675 +{ 1.676 + char *dbkey = nullptr; 1.677 + nsresult rv = cert->GetDbKey(&dbkey); 1.678 + if (NS_FAILED(rv) || !dbkey) 1.679 + return false; 1.680 + 1.681 + bool found_mismatch = false; 1.682 + const char *key1 = dbkey; 1.683 + const char *key2 = match_dbkey; 1.684 + 1.685 + // skip over any whitespace when comparing 1.686 + while (*key1 && *key2) { 1.687 + char c1 = *key1; 1.688 + char c2 = *key2; 1.689 + 1.690 + switch (c1) { 1.691 + case ' ': 1.692 + case '\t': 1.693 + case '\n': 1.694 + case '\r': 1.695 + ++key1; 1.696 + continue; 1.697 + } 1.698 + 1.699 + switch (c2) { 1.700 + case ' ': 1.701 + case '\t': 1.702 + case '\n': 1.703 + case '\r': 1.704 + ++key2; 1.705 + continue; 1.706 + } 1.707 + 1.708 + if (c1 != c2) { 1.709 + found_mismatch = true; 1.710 + break; 1.711 + } 1.712 + 1.713 + ++key1; 1.714 + ++key2; 1.715 + } 1.716 + 1.717 + PR_Free(dbkey); 1.718 + return !found_mismatch; 1.719 +} 1.720 + 1.721 +struct nsCertAndBoolsAndInt 1.722 +{ 1.723 + nsIX509Cert *cert; 1.724 + bool aCheckTemporaries; 1.725 + bool aCheckPermanents; 1.726 + uint32_t counter; 1.727 + 1.728 + SECOidTag mOidTagForStoringNewHashes; 1.729 + nsCString mDottedOidForStoringNewHashes; 1.730 +}; 1.731 + 1.732 +static PLDHashOperator 1.733 +FindMatchingCertCallback(nsCertOverrideEntry *aEntry, 1.734 + void *aArg) 1.735 +{ 1.736 + nsCertAndBoolsAndInt *cai = (nsCertAndBoolsAndInt *)aArg; 1.737 + 1.738 + if (cai && aEntry) 1.739 + { 1.740 + const nsCertOverride &settings = aEntry->mSettings; 1.741 + bool still_ok = true; 1.742 + 1.743 + if ((settings.mIsTemporary && !cai->aCheckTemporaries) 1.744 + || 1.745 + (!settings.mIsTemporary && !cai->aCheckPermanents)) { 1.746 + still_ok = false; 1.747 + } 1.748 + 1.749 + if (still_ok && matchesDBKey(cai->cert, settings.mDBKey.get())) { 1.750 + nsAutoCString cert_fingerprint; 1.751 + nsresult rv; 1.752 + if (settings.mFingerprintAlgOID.Equals(cai->mDottedOidForStoringNewHashes)) { 1.753 + rv = GetCertFingerprintByOidTag(cai->cert, 1.754 + cai->mOidTagForStoringNewHashes, cert_fingerprint); 1.755 + } 1.756 + else { 1.757 + rv = GetCertFingerprintByDottedOidString(cai->cert, 1.758 + settings.mFingerprintAlgOID, cert_fingerprint); 1.759 + } 1.760 + if (NS_SUCCEEDED(rv) && 1.761 + settings.mFingerprint.Equals(cert_fingerprint)) { 1.762 + cai->counter++; 1.763 + } 1.764 + } 1.765 + } 1.766 + 1.767 + return PL_DHASH_NEXT; 1.768 +} 1.769 + 1.770 +NS_IMETHODIMP 1.771 +nsCertOverrideService::IsCertUsedForOverrides(nsIX509Cert *aCert, 1.772 + bool aCheckTemporaries, 1.773 + bool aCheckPermanents, 1.774 + uint32_t *_retval) 1.775 +{ 1.776 + NS_ENSURE_ARG(aCert); 1.777 + NS_ENSURE_ARG(_retval); 1.778 + 1.779 + nsCertAndBoolsAndInt cai; 1.780 + cai.cert = aCert; 1.781 + cai.aCheckTemporaries = aCheckTemporaries; 1.782 + cai.aCheckPermanents = aCheckPermanents; 1.783 + cai.counter = 0; 1.784 + cai.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes; 1.785 + cai.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes; 1.786 + 1.787 + { 1.788 + ReentrantMonitorAutoEnter lock(monitor); 1.789 + mSettingsTable.EnumerateEntries(FindMatchingCertCallback, &cai); 1.790 + } 1.791 + *_retval = cai.counter; 1.792 + return NS_OK; 1.793 +} 1.794 + 1.795 +struct nsCertAndPointerAndCallback 1.796 +{ 1.797 + nsIX509Cert *cert; 1.798 + void *userdata; 1.799 + nsCertOverrideService::CertOverrideEnumerator enumerator; 1.800 + 1.801 + SECOidTag mOidTagForStoringNewHashes; 1.802 + nsCString mDottedOidForStoringNewHashes; 1.803 +}; 1.804 + 1.805 +static PLDHashOperator 1.806 +EnumerateCertOverridesCallback(nsCertOverrideEntry *aEntry, 1.807 + void *aArg) 1.808 +{ 1.809 + nsCertAndPointerAndCallback *capac = (nsCertAndPointerAndCallback *)aArg; 1.810 + 1.811 + if (capac && aEntry) 1.812 + { 1.813 + const nsCertOverride &settings = aEntry->mSettings; 1.814 + 1.815 + if (!capac->cert) { 1.816 + (*capac->enumerator)(settings, capac->userdata); 1.817 + } 1.818 + else { 1.819 + if (matchesDBKey(capac->cert, settings.mDBKey.get())) { 1.820 + nsAutoCString cert_fingerprint; 1.821 + nsresult rv; 1.822 + if (settings.mFingerprintAlgOID.Equals(capac->mDottedOidForStoringNewHashes)) { 1.823 + rv = GetCertFingerprintByOidTag(capac->cert, 1.824 + capac->mOidTagForStoringNewHashes, cert_fingerprint); 1.825 + } 1.826 + else { 1.827 + rv = GetCertFingerprintByDottedOidString(capac->cert, 1.828 + settings.mFingerprintAlgOID, cert_fingerprint); 1.829 + } 1.830 + if (NS_SUCCEEDED(rv) && 1.831 + settings.mFingerprint.Equals(cert_fingerprint)) { 1.832 + (*capac->enumerator)(settings, capac->userdata); 1.833 + } 1.834 + } 1.835 + } 1.836 + } 1.837 + 1.838 + return PL_DHASH_NEXT; 1.839 +} 1.840 + 1.841 +nsresult 1.842 +nsCertOverrideService::EnumerateCertOverrides(nsIX509Cert *aCert, 1.843 + CertOverrideEnumerator enumerator, 1.844 + void *aUserData) 1.845 +{ 1.846 + nsCertAndPointerAndCallback capac; 1.847 + capac.cert = aCert; 1.848 + capac.userdata = aUserData; 1.849 + capac.enumerator = enumerator; 1.850 + capac.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes; 1.851 + capac.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes; 1.852 + 1.853 + { 1.854 + ReentrantMonitorAutoEnter lock(monitor); 1.855 + mSettingsTable.EnumerateEntries(EnumerateCertOverridesCallback, &capac); 1.856 + } 1.857 + return NS_OK; 1.858 +} 1.859 + 1.860 +void 1.861 +nsCertOverrideService::GetHostWithPort(const nsACString & aHostName, int32_t aPort, nsACString& _retval) 1.862 +{ 1.863 + nsAutoCString hostPort(aHostName); 1.864 + if (aPort == -1) { 1.865 + aPort = 443; 1.866 + } 1.867 + if (!hostPort.IsEmpty()) { 1.868 + hostPort.AppendLiteral(":"); 1.869 + hostPort.AppendInt(aPort); 1.870 + } 1.871 + _retval.Assign(hostPort); 1.872 +} 1.873 +