/src/mozilla-central/security/manager/ssl/nsCertOverrideService.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "nsCertOverrideService.h" |
8 | | |
9 | | #include "NSSCertDBTrustDomain.h" |
10 | | #include "ScopedNSSTypes.h" |
11 | | #include "SharedSSLState.h" |
12 | | #include "mozilla/Assertions.h" |
13 | | #include "mozilla/Telemetry.h" |
14 | | #include "mozilla/Unused.h" |
15 | | #include "nsAppDirectoryServiceDefs.h" |
16 | | #include "nsCRT.h" |
17 | | #include "nsILineInputStream.h" |
18 | | #include "nsIObserver.h" |
19 | | #include "nsIObserverService.h" |
20 | | #include "nsIOutputStream.h" |
21 | | #include "nsISafeOutputStream.h" |
22 | | #include "nsIX509Cert.h" |
23 | | #include "nsNSSCertHelper.h" |
24 | | #include "nsNSSCertificate.h" |
25 | | #include "nsNSSComponent.h" |
26 | | #include "nsNetUtil.h" |
27 | | #include "nsStreamUtils.h" |
28 | | #include "nsStringBuffer.h" |
29 | | #include "nsThreadUtils.h" |
30 | | #include "ssl.h" // For SSL_ClearSessionCache |
31 | | |
32 | | using namespace mozilla; |
33 | | using namespace mozilla::psm; |
34 | | |
35 | | #define CERT_OVERRIDE_FILE_NAME "cert_override.txt" |
36 | | |
37 | | void |
38 | | nsCertOverride::convertBitsToString(OverrideBits ob, /*out*/ nsACString& str) |
39 | 0 | { |
40 | 0 | str.Truncate(); |
41 | 0 |
|
42 | 0 | if (ob & OverrideBits::Mismatch) { |
43 | 0 | str.Append('M'); |
44 | 0 | } |
45 | 0 |
|
46 | 0 | if (ob & OverrideBits::Untrusted) { |
47 | 0 | str.Append('U'); |
48 | 0 | } |
49 | 0 |
|
50 | 0 | if (ob & OverrideBits::Time) { |
51 | 0 | str.Append('T'); |
52 | 0 | } |
53 | 0 | } |
54 | | |
55 | | void |
56 | | nsCertOverride::convertStringToBits(const nsACString& str, |
57 | | /*out*/ OverrideBits& ob) |
58 | 0 | { |
59 | 0 | ob = OverrideBits::None; |
60 | 0 |
|
61 | 0 | for (uint32_t i = 0; i < str.Length(); i++) { |
62 | 0 | switch (str.CharAt(i)) { |
63 | 0 | case 'm': |
64 | 0 | case 'M': |
65 | 0 | ob |= OverrideBits::Mismatch; |
66 | 0 | break; |
67 | 0 |
|
68 | 0 | case 'u': |
69 | 0 | case 'U': |
70 | 0 | ob |= OverrideBits::Untrusted; |
71 | 0 | break; |
72 | 0 |
|
73 | 0 | case 't': |
74 | 0 | case 'T': |
75 | 0 | ob |= OverrideBits::Time; |
76 | 0 | break; |
77 | 0 |
|
78 | 0 | default: |
79 | 0 | break; |
80 | 0 | } |
81 | 0 | } |
82 | 0 | } |
83 | | |
84 | | NS_IMPL_ISUPPORTS(nsCertOverrideService, |
85 | | nsICertOverrideService, |
86 | | nsIObserver, |
87 | | nsISupportsWeakReference) |
88 | | |
89 | | nsCertOverrideService::nsCertOverrideService() |
90 | | : mMutex("nsCertOverrideService.mutex") |
91 | 0 | { |
92 | 0 | } |
93 | | |
94 | | nsCertOverrideService::~nsCertOverrideService() |
95 | 0 | { |
96 | 0 | } |
97 | | |
98 | | nsresult |
99 | | nsCertOverrideService::Init() |
100 | 0 | { |
101 | 0 | if (!NS_IsMainThread()) { |
102 | 0 | MOZ_ASSERT_UNREACHABLE("nsCertOverrideService initialized off main thread"); |
103 | 0 | return NS_ERROR_NOT_SAME_THREAD; |
104 | 0 | } |
105 | 0 |
|
106 | 0 | nsCOMPtr<nsIObserverService> observerService = |
107 | 0 | mozilla::services::GetObserverService(); |
108 | 0 |
|
109 | 0 | // If we cannot add ourselves as a profile change observer, then we will not |
110 | 0 | // attempt to read/write any settings file. Otherwise, we would end up |
111 | 0 | // reading/writing the wrong settings file after a profile change. |
112 | 0 | if (observerService) { |
113 | 0 | observerService->AddObserver(this, "profile-before-change", true); |
114 | 0 | observerService->AddObserver(this, "profile-do-change", true); |
115 | 0 | // simulate a profile change so we read the current profile's settings file |
116 | 0 | Observe(nullptr, "profile-do-change", nullptr); |
117 | 0 | } |
118 | 0 |
|
119 | 0 | SharedSSLState::NoteCertOverrideServiceInstantiated(); |
120 | 0 | return NS_OK; |
121 | 0 | } |
122 | | |
123 | | NS_IMETHODIMP |
124 | | nsCertOverrideService::Observe(nsISupports *, |
125 | | const char *aTopic, |
126 | | const char16_t *aData) |
127 | 0 | { |
128 | 0 | // check the topic |
129 | 0 | if (!nsCRT::strcmp(aTopic, "profile-before-change")) { |
130 | 0 | // The profile is about to change, |
131 | 0 | // or is going away because the application is shutting down. |
132 | 0 |
|
133 | 0 | RemoveAllFromMemory(); |
134 | 0 | } else if (!nsCRT::strcmp(aTopic, "profile-do-change")) { |
135 | 0 | // The profile has already changed. |
136 | 0 | // Now read from the new profile location. |
137 | 0 | // we also need to update the cached file location |
138 | 0 |
|
139 | 0 | MutexAutoLock lock(mMutex); |
140 | 0 |
|
141 | 0 | nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mSettingsFile)); |
142 | 0 | if (NS_SUCCEEDED(rv)) { |
143 | 0 | mSettingsFile->AppendNative(NS_LITERAL_CSTRING(CERT_OVERRIDE_FILE_NAME)); |
144 | 0 | } else { |
145 | 0 | mSettingsFile = nullptr; |
146 | 0 | } |
147 | 0 | Read(lock); |
148 | 0 | CountPermanentOverrideTelemetry(lock); |
149 | 0 | } |
150 | 0 |
|
151 | 0 | return NS_OK; |
152 | 0 | } |
153 | | |
154 | | void |
155 | | nsCertOverrideService::RemoveAllFromMemory() |
156 | 0 | { |
157 | 0 | MutexAutoLock lock(mMutex); |
158 | 0 | mSettingsTable.Clear(); |
159 | 0 | } |
160 | | |
161 | | void |
162 | | nsCertOverrideService::RemoveAllTemporaryOverrides() |
163 | 0 | { |
164 | 0 | MutexAutoLock lock(mMutex); |
165 | 0 | for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) { |
166 | 0 | nsCertOverrideEntry *entry = iter.Get(); |
167 | 0 | if (entry->mSettings.mIsTemporary) { |
168 | 0 | entry->mSettings.mCert = nullptr; |
169 | 0 | iter.Remove(); |
170 | 0 | } |
171 | 0 | } |
172 | 0 | // no need to write, as temporaries are never written to disk |
173 | 0 | } |
174 | | |
175 | | nsresult |
176 | | nsCertOverrideService::Read(const MutexAutoLock& aProofOfLock) |
177 | 0 | { |
178 | 0 | // If we don't have a profile, then we won't try to read any settings file. |
179 | 0 | if (!mSettingsFile) |
180 | 0 | return NS_OK; |
181 | 0 | |
182 | 0 | nsresult rv; |
183 | 0 | nsCOMPtr<nsIInputStream> fileInputStream; |
184 | 0 | rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), mSettingsFile); |
185 | 0 | if (NS_FAILED(rv)) { |
186 | 0 | return rv; |
187 | 0 | } |
188 | 0 | |
189 | 0 | nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv); |
190 | 0 | if (NS_FAILED(rv)) { |
191 | 0 | return rv; |
192 | 0 | } |
193 | 0 | |
194 | 0 | nsAutoCString buffer; |
195 | 0 | bool isMore = true; |
196 | 0 | int32_t hostIndex = 0, algoIndex, fingerprintIndex, overrideBitsIndex, dbKeyIndex; |
197 | 0 |
|
198 | 0 | /* file format is: |
199 | 0 | * |
200 | 0 | * host:port \t fingerprint-algorithm \t fingerprint \t override-mask \t dbKey |
201 | 0 | * |
202 | 0 | * where override-mask is a sequence of characters, |
203 | 0 | * M meaning hostname-Mismatch-override |
204 | 0 | * U meaning Untrusted-override |
205 | 0 | * T meaning Time-error-override (expired/not yet valid) |
206 | 0 | * |
207 | 0 | * if this format isn't respected we move onto the next line in the file. |
208 | 0 | */ |
209 | 0 |
|
210 | 0 | while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) { |
211 | 0 | if (buffer.IsEmpty() || buffer.First() == '#') { |
212 | 0 | continue; |
213 | 0 | } |
214 | 0 | |
215 | 0 | // this is a cheap, cheesy way of parsing a tab-delimited line into |
216 | 0 | // string indexes, which can be lopped off into substrings. just for |
217 | 0 | // purposes of obfuscation, it also checks that each token was found. |
218 | 0 | // todo: use iterators? |
219 | 0 | if ((algoIndex = buffer.FindChar('\t', hostIndex) + 1) == 0 || |
220 | 0 | (fingerprintIndex = buffer.FindChar('\t', algoIndex) + 1) == 0 || |
221 | 0 | (overrideBitsIndex = buffer.FindChar('\t', fingerprintIndex) + 1) == 0 || |
222 | 0 | (dbKeyIndex = buffer.FindChar('\t', overrideBitsIndex) + 1) == 0) { |
223 | 0 | continue; |
224 | 0 | } |
225 | 0 | |
226 | 0 | const nsACString& tmp = Substring(buffer, hostIndex, algoIndex - hostIndex - 1); |
227 | 0 | // We just ignore the algorithm string. |
228 | 0 | const nsACString& fingerprint = Substring(buffer, fingerprintIndex, overrideBitsIndex - fingerprintIndex - 1); |
229 | 0 | const nsACString& bits_string = Substring(buffer, overrideBitsIndex, dbKeyIndex - overrideBitsIndex - 1); |
230 | 0 | const nsACString& db_key = Substring(buffer, dbKeyIndex, buffer.Length() - dbKeyIndex); |
231 | 0 |
|
232 | 0 | nsAutoCString host(tmp); |
233 | 0 | nsCertOverride::OverrideBits bits; |
234 | 0 | nsCertOverride::convertStringToBits(bits_string, bits); |
235 | 0 |
|
236 | 0 | int32_t port; |
237 | 0 | int32_t portIndex = host.RFindChar(':'); |
238 | 0 | if (portIndex == kNotFound) |
239 | 0 | continue; // Ignore broken entries |
240 | 0 | |
241 | 0 | nsresult portParseError; |
242 | 0 | nsAutoCString portString(Substring(host, portIndex+1)); |
243 | 0 | port = portString.ToInteger(&portParseError); |
244 | 0 | if (NS_FAILED(portParseError)) |
245 | 0 | continue; // Ignore broken entries |
246 | 0 | |
247 | 0 | host.Truncate(portIndex); |
248 | 0 |
|
249 | 0 | AddEntryToList(host, port, |
250 | 0 | nullptr, // don't have the cert |
251 | 0 | false, // not temporary |
252 | 0 | fingerprint, bits, db_key, aProofOfLock); |
253 | 0 | } |
254 | 0 |
|
255 | 0 | return NS_OK; |
256 | 0 | } |
257 | | |
258 | | static const char sSHA256OIDString[] = "OID.2.16.840.1.101.3.4.2.1"; |
259 | | nsresult |
260 | | nsCertOverrideService::Write(const MutexAutoLock& aProofOfLock) |
261 | 0 | { |
262 | 0 | // If we don't have any profile, then we won't try to write any file |
263 | 0 | if (!mSettingsFile) { |
264 | 0 | return NS_OK; |
265 | 0 | } |
266 | 0 | |
267 | 0 | nsresult rv; |
268 | 0 | nsCOMPtr<nsIOutputStream> fileOutputStream; |
269 | 0 | rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream), |
270 | 0 | mSettingsFile, |
271 | 0 | -1, |
272 | 0 | 0600); |
273 | 0 | if (NS_FAILED(rv)) { |
274 | 0 | NS_ERROR("failed to open cert_warn_settings.txt for writing"); |
275 | 0 | return rv; |
276 | 0 | } |
277 | 0 |
|
278 | 0 | // get a buffered output stream 4096 bytes big, to optimize writes |
279 | 0 | nsCOMPtr<nsIOutputStream> bufferedOutputStream; |
280 | 0 | rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), |
281 | 0 | fileOutputStream.forget(), 4096); |
282 | 0 | if (NS_FAILED(rv)) { |
283 | 0 | return rv; |
284 | 0 | } |
285 | 0 | |
286 | 0 | static const char kHeader[] = |
287 | 0 | "# PSM Certificate Override Settings file" NS_LINEBREAK |
288 | 0 | "# This is a generated file! Do not edit." NS_LINEBREAK; |
289 | 0 |
|
290 | 0 | /* see ::Read for file format */ |
291 | 0 |
|
292 | 0 | uint32_t unused; |
293 | 0 | bufferedOutputStream->Write(kHeader, sizeof(kHeader) - 1, &unused); |
294 | 0 |
|
295 | 0 | static const char kTab[] = "\t"; |
296 | 0 | for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) { |
297 | 0 | nsCertOverrideEntry *entry = iter.Get(); |
298 | 0 |
|
299 | 0 | const nsCertOverride &settings = entry->mSettings; |
300 | 0 | if (settings.mIsTemporary) { |
301 | 0 | continue; |
302 | 0 | } |
303 | 0 | |
304 | 0 | nsAutoCString bits_string; |
305 | 0 | nsCertOverride::convertBitsToString(settings.mOverrideBits, bits_string); |
306 | 0 |
|
307 | 0 | bufferedOutputStream->Write(entry->mHostWithPort.get(), |
308 | 0 | entry->mHostWithPort.Length(), &unused); |
309 | 0 | bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused); |
310 | 0 | bufferedOutputStream->Write(sSHA256OIDString, sizeof(sSHA256OIDString) - 1, |
311 | 0 | &unused); |
312 | 0 | bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused); |
313 | 0 | bufferedOutputStream->Write(settings.mFingerprint.get(), |
314 | 0 | settings.mFingerprint.Length(), &unused); |
315 | 0 | bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused); |
316 | 0 | bufferedOutputStream->Write(bits_string.get(), |
317 | 0 | bits_string.Length(), &unused); |
318 | 0 | bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused); |
319 | 0 | bufferedOutputStream->Write(settings.mDBKey.get(), |
320 | 0 | settings.mDBKey.Length(), &unused); |
321 | 0 | bufferedOutputStream->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &unused); |
322 | 0 | } |
323 | 0 |
|
324 | 0 | // All went ok. Maybe except for problems in Write(), but the stream detects |
325 | 0 | // that for us |
326 | 0 | nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(bufferedOutputStream); |
327 | 0 | MOZ_ASSERT(safeStream, "Expected a safe output stream!"); |
328 | 0 | if (safeStream) { |
329 | 0 | rv = safeStream->Finish(); |
330 | 0 | if (NS_FAILED(rv)) { |
331 | 0 | NS_WARNING("failed to save cert warn settings file! possible dataloss"); |
332 | 0 | return rv; |
333 | 0 | } |
334 | 0 | } |
335 | 0 |
|
336 | 0 | return NS_OK; |
337 | 0 | } |
338 | | |
339 | | static nsresult |
340 | | GetCertFingerprintByOidTag(nsIX509Cert *aCert, |
341 | | SECOidTag aOidTag, |
342 | | nsCString &fp) |
343 | 0 | { |
344 | 0 | UniqueCERTCertificate nsscert(aCert->GetCert()); |
345 | 0 | if (!nsscert) { |
346 | 0 | return NS_ERROR_FAILURE; |
347 | 0 | } |
348 | 0 | return GetCertFingerprintByOidTag(nsscert.get(), aOidTag, fp); |
349 | 0 | } |
350 | | |
351 | | NS_IMETHODIMP |
352 | | nsCertOverrideService::RememberValidityOverride(const nsACString& aHostName, |
353 | | int32_t aPort, |
354 | | nsIX509Cert* aCert, |
355 | | uint32_t aOverrideBits, |
356 | | bool aTemporary) |
357 | 0 | { |
358 | 0 | NS_ENSURE_ARG_POINTER(aCert); |
359 | 0 | if (aHostName.IsEmpty()) |
360 | 0 | return NS_ERROR_INVALID_ARG; |
361 | 0 | if (aPort < -1) |
362 | 0 | return NS_ERROR_INVALID_ARG; |
363 | 0 | |
364 | 0 | UniqueCERTCertificate nsscert(aCert->GetCert()); |
365 | 0 | if (!nsscert) { |
366 | 0 | return NS_ERROR_FAILURE; |
367 | 0 | } |
368 | 0 | |
369 | 0 | nsAutoCString nickname; |
370 | 0 | nsresult rv = DefaultServerNicknameForCert(nsscert.get(), nickname); |
371 | 0 | if (!aTemporary && NS_SUCCEEDED(rv)) { |
372 | 0 | UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); |
373 | 0 | if (!slot) { |
374 | 0 | return NS_ERROR_FAILURE; |
375 | 0 | } |
376 | 0 | |
377 | 0 | // This can fail (for example, if we're in read-only mode). Luckily, we |
378 | 0 | // don't even need it to succeed - we always match on the stored hash of the |
379 | 0 | // certificate rather than the full certificate. It makes the display a bit |
380 | 0 | // less informative (since we won't have a certificate to display), but it's |
381 | 0 | // better than failing the entire operation. |
382 | 0 | Unused << PK11_ImportCert(slot.get(), nsscert.get(), CK_INVALID_HANDLE, |
383 | 0 | nickname.get(), false); |
384 | 0 | } |
385 | 0 |
|
386 | 0 | nsAutoCString fpStr; |
387 | 0 | rv = GetCertFingerprintByOidTag(nsscert.get(), SEC_OID_SHA256, fpStr); |
388 | 0 | if (NS_FAILED(rv)) |
389 | 0 | return rv; |
390 | 0 | |
391 | 0 | nsAutoCString dbkey; |
392 | 0 | rv = aCert->GetDbKey(dbkey); |
393 | 0 | if (NS_FAILED(rv)) { |
394 | 0 | return rv; |
395 | 0 | } |
396 | 0 | |
397 | 0 | { |
398 | 0 | MutexAutoLock lock(mMutex); |
399 | 0 | AddEntryToList(aHostName, aPort, |
400 | 0 | aTemporary ? aCert : nullptr, |
401 | 0 | // keep a reference to the cert for temporary overrides |
402 | 0 | aTemporary, |
403 | 0 | fpStr, |
404 | 0 | (nsCertOverride::OverrideBits)aOverrideBits, |
405 | 0 | dbkey, lock); |
406 | 0 | if (!aTemporary) { |
407 | 0 | Write(lock); |
408 | 0 | } |
409 | 0 | } |
410 | 0 |
|
411 | 0 | return NS_OK; |
412 | 0 | } |
413 | | |
414 | | NS_IMETHODIMP |
415 | | nsCertOverrideService::RememberTemporaryValidityOverrideUsingFingerprint( |
416 | | const nsACString& aHostName, |
417 | | int32_t aPort, |
418 | | const nsACString& aCertFingerprint, |
419 | | uint32_t aOverrideBits) |
420 | 0 | { |
421 | 0 | if(aCertFingerprint.IsEmpty() || aHostName.IsEmpty() || (aPort < -1)) { |
422 | 0 | return NS_ERROR_INVALID_ARG; |
423 | 0 | } |
424 | 0 | |
425 | 0 | MutexAutoLock lock(mMutex); |
426 | 0 | AddEntryToList(aHostName, aPort, |
427 | 0 | nullptr, // No cert to keep alive |
428 | 0 | true, // temporary |
429 | 0 | aCertFingerprint, |
430 | 0 | (nsCertOverride::OverrideBits)aOverrideBits, |
431 | 0 | EmptyCString(), // dbkey |
432 | 0 | lock); |
433 | 0 |
|
434 | 0 | return NS_OK; |
435 | 0 | } |
436 | | |
437 | | NS_IMETHODIMP |
438 | | nsCertOverrideService::HasMatchingOverride(const nsACString & aHostName, int32_t aPort, |
439 | | nsIX509Cert *aCert, |
440 | | uint32_t *aOverrideBits, |
441 | | bool *aIsTemporary, |
442 | | bool *_retval) |
443 | 0 | { |
444 | 0 | if (aHostName.IsEmpty()) |
445 | 0 | return NS_ERROR_INVALID_ARG; |
446 | 0 | if (aPort < -1) |
447 | 0 | return NS_ERROR_INVALID_ARG; |
448 | 0 | |
449 | 0 | NS_ENSURE_ARG_POINTER(aCert); |
450 | 0 | NS_ENSURE_ARG_POINTER(aOverrideBits); |
451 | 0 | NS_ENSURE_ARG_POINTER(aIsTemporary); |
452 | 0 | NS_ENSURE_ARG_POINTER(_retval); |
453 | 0 | *_retval = false; |
454 | 0 | *aOverrideBits = static_cast<uint32_t>(nsCertOverride::OverrideBits::None); |
455 | 0 |
|
456 | 0 | nsAutoCString hostPort; |
457 | 0 | GetHostWithPort(aHostName, aPort, hostPort); |
458 | 0 | nsCertOverride settings; |
459 | 0 |
|
460 | 0 | { |
461 | 0 | MutexAutoLock lock(mMutex); |
462 | 0 | nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hostPort.get()); |
463 | 0 |
|
464 | 0 | if (!entry) |
465 | 0 | return NS_OK; |
466 | 0 | |
467 | 0 | settings = entry->mSettings; // copy |
468 | 0 | } |
469 | 0 |
|
470 | 0 | *aOverrideBits = static_cast<uint32_t>(settings.mOverrideBits); |
471 | 0 | *aIsTemporary = settings.mIsTemporary; |
472 | 0 |
|
473 | 0 | nsAutoCString fpStr; |
474 | 0 | nsresult rv; |
475 | 0 |
|
476 | 0 | rv = GetCertFingerprintByOidTag(aCert, SEC_OID_SHA256, fpStr); |
477 | 0 | if (NS_FAILED(rv)) { |
478 | 0 | return rv; |
479 | 0 | } |
480 | 0 | |
481 | 0 | *_retval = settings.mFingerprint.Equals(fpStr); |
482 | 0 | return NS_OK; |
483 | 0 | } |
484 | | |
485 | | nsresult |
486 | | nsCertOverrideService::AddEntryToList(const nsACString &aHostName, int32_t aPort, |
487 | | nsIX509Cert *aCert, |
488 | | const bool aIsTemporary, |
489 | | const nsACString &fingerprint, |
490 | | nsCertOverride::OverrideBits ob, |
491 | | const nsACString &dbKey, |
492 | | const MutexAutoLock& aProofOfLock) |
493 | 0 | { |
494 | 0 | nsAutoCString hostPort; |
495 | 0 | GetHostWithPort(aHostName, aPort, hostPort); |
496 | 0 |
|
497 | 0 | nsCertOverrideEntry *entry = mSettingsTable.PutEntry(hostPort.get()); |
498 | 0 |
|
499 | 0 | if (!entry) { |
500 | 0 | NS_ERROR("can't insert a null entry!"); |
501 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
502 | 0 | } |
503 | 0 |
|
504 | 0 | entry->mHostWithPort = hostPort; |
505 | 0 |
|
506 | 0 | nsCertOverride &settings = entry->mSettings; |
507 | 0 | settings.mAsciiHost = aHostName; |
508 | 0 | settings.mPort = aPort; |
509 | 0 | settings.mIsTemporary = aIsTemporary; |
510 | 0 | settings.mFingerprint = fingerprint; |
511 | 0 | settings.mOverrideBits = ob; |
512 | 0 | settings.mDBKey = dbKey; |
513 | 0 | // remove whitespace from stored dbKey for backwards compatibility |
514 | 0 | settings.mDBKey.StripWhitespace(); |
515 | 0 | settings.mCert = aCert; |
516 | 0 |
|
517 | 0 | return NS_OK; |
518 | 0 | } |
519 | | |
520 | | NS_IMETHODIMP |
521 | | nsCertOverrideService::ClearValidityOverride(const nsACString & aHostName, int32_t aPort) |
522 | 0 | { |
523 | 0 | if (!NS_IsMainThread()) { |
524 | 0 | return NS_ERROR_NOT_SAME_THREAD; |
525 | 0 | } |
526 | 0 | |
527 | 0 | if (aPort == 0 && aHostName.EqualsLiteral("all:temporary-certificates")) { |
528 | 0 | RemoveAllTemporaryOverrides(); |
529 | 0 | return NS_OK; |
530 | 0 | } |
531 | 0 | nsAutoCString hostPort; |
532 | 0 | GetHostWithPort(aHostName, aPort, hostPort); |
533 | 0 | { |
534 | 0 | MutexAutoLock lock(mMutex); |
535 | 0 | mSettingsTable.RemoveEntry(hostPort.get()); |
536 | 0 | Write(lock); |
537 | 0 | } |
538 | 0 |
|
539 | 0 | nsCOMPtr<nsINSSComponent> nss(do_GetService(PSM_COMPONENT_CONTRACTID)); |
540 | 0 | if (nss) { |
541 | 0 | SSL_ClearSessionCache(); |
542 | 0 | } else { |
543 | 0 | return NS_ERROR_NOT_AVAILABLE; |
544 | 0 | } |
545 | 0 | |
546 | 0 | return NS_OK; |
547 | 0 | } |
548 | | |
549 | | void |
550 | | nsCertOverrideService::CountPermanentOverrideTelemetry(const MutexAutoLock& aProofOfLock) |
551 | 0 | { |
552 | 0 | uint32_t overrideCount = 0; |
553 | 0 | for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) { |
554 | 0 | if (!iter.Get()->mSettings.mIsTemporary) { |
555 | 0 | overrideCount++; |
556 | 0 | } |
557 | 0 | } |
558 | 0 | Telemetry::Accumulate(Telemetry::SSL_PERMANENT_CERT_ERROR_OVERRIDES, |
559 | 0 | overrideCount); |
560 | 0 | } |
561 | | |
562 | | static bool |
563 | | matchesDBKey(nsIX509Cert* cert, const nsCString& matchDbKey) |
564 | 0 | { |
565 | 0 | nsAutoCString dbKey; |
566 | 0 | nsresult rv = cert->GetDbKey(dbKey); |
567 | 0 | if (NS_FAILED(rv)) { |
568 | 0 | return false; |
569 | 0 | } |
570 | 0 | return dbKey.Equals(matchDbKey); |
571 | 0 | } |
572 | | |
573 | | NS_IMETHODIMP |
574 | | nsCertOverrideService::IsCertUsedForOverrides(nsIX509Cert *aCert, |
575 | | bool aCheckTemporaries, |
576 | | bool aCheckPermanents, |
577 | | uint32_t *_retval) |
578 | 0 | { |
579 | 0 | NS_ENSURE_ARG(aCert); |
580 | 0 | NS_ENSURE_ARG(_retval); |
581 | 0 |
|
582 | 0 | uint32_t counter = 0; |
583 | 0 | { |
584 | 0 | MutexAutoLock lock(mMutex); |
585 | 0 | for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) { |
586 | 0 | const nsCertOverride &settings = iter.Get()->mSettings; |
587 | 0 |
|
588 | 0 | if (( settings.mIsTemporary && !aCheckTemporaries) || |
589 | 0 | (!settings.mIsTemporary && !aCheckPermanents)) { |
590 | 0 | continue; |
591 | 0 | } |
592 | 0 | |
593 | 0 | if (matchesDBKey(aCert, settings.mDBKey)) { |
594 | 0 | nsAutoCString cert_fingerprint; |
595 | 0 | nsresult rv = GetCertFingerprintByOidTag(aCert, SEC_OID_SHA256, |
596 | 0 | cert_fingerprint); |
597 | 0 | if (NS_SUCCEEDED(rv) && |
598 | 0 | settings.mFingerprint.Equals(cert_fingerprint)) { |
599 | 0 | counter++; |
600 | 0 | } |
601 | 0 | } |
602 | 0 | } |
603 | 0 | } |
604 | 0 | *_retval = counter; |
605 | 0 | return NS_OK; |
606 | 0 | } |
607 | | |
608 | | nsresult |
609 | | nsCertOverrideService::EnumerateCertOverrides(nsIX509Cert *aCert, |
610 | | CertOverrideEnumerator aEnumerator, |
611 | | void *aUserData) |
612 | 0 | { |
613 | 0 | MutexAutoLock lock(mMutex); |
614 | 0 | for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) { |
615 | 0 | const nsCertOverride &settings = iter.Get()->mSettings; |
616 | 0 |
|
617 | 0 | if (!aCert) { |
618 | 0 | aEnumerator(settings, aUserData); |
619 | 0 | } else { |
620 | 0 | if (matchesDBKey(aCert, settings.mDBKey)) { |
621 | 0 | nsAutoCString cert_fingerprint; |
622 | 0 | nsresult rv = GetCertFingerprintByOidTag(aCert, SEC_OID_SHA256, |
623 | 0 | cert_fingerprint); |
624 | 0 | if (NS_SUCCEEDED(rv) && |
625 | 0 | settings.mFingerprint.Equals(cert_fingerprint)) { |
626 | 0 | aEnumerator(settings, aUserData); |
627 | 0 | } |
628 | 0 | } |
629 | 0 | } |
630 | 0 | } |
631 | 0 | return NS_OK; |
632 | 0 | } |
633 | | |
634 | | void |
635 | | nsCertOverrideService::GetHostWithPort(const nsACString & aHostName, int32_t aPort, nsACString& _retval) |
636 | 0 | { |
637 | 0 | nsAutoCString hostPort(aHostName); |
638 | 0 | if (aPort == -1) { |
639 | 0 | aPort = 443; |
640 | 0 | } |
641 | 0 | if (!hostPort.IsEmpty()) { |
642 | 0 | hostPort.Append(':'); |
643 | 0 | hostPort.AppendInt(aPort); |
644 | 0 | } |
645 | 0 | _retval.Assign(hostPort); |
646 | 0 | } |