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