Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; 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 "CertBlocklist.h"
8 :
9 : #include "mozilla/Assertions.h"
10 : #include "mozilla/Base64.h"
11 : #include "mozilla/Casting.h"
12 : #include "mozilla/IntegerPrintfMacros.h"
13 : #include "mozilla/Logging.h"
14 : #include "mozilla/Preferences.h"
15 : #include "mozilla/Unused.h"
16 : #include "nsAppDirectoryServiceDefs.h"
17 : #include "nsDependentString.h"
18 : #include "nsDirectoryServiceUtils.h"
19 : #include "nsICryptoHash.h"
20 : #include "nsIFileStreams.h"
21 : #include "nsILineInputStream.h"
22 : #include "nsISafeOutputStream.h"
23 : #include "nsIX509Cert.h"
24 : #include "nsNetCID.h"
25 : #include "nsNetUtil.h"
26 : #include "nsPromiseFlatString.h"
27 : #include "nsTHashtable.h"
28 : #include "nsThreadUtils.h"
29 : #include "pkix/Input.h"
30 : #include "prtime.h"
31 :
32 0 : NS_IMPL_ISUPPORTS(CertBlocklist, nsICertBlocklist)
33 :
34 : using namespace mozilla;
35 : using namespace mozilla::pkix;
36 :
37 : #define PREF_BACKGROUND_UPDATE_TIMER "app.update.lastUpdateTime.blocklist-background-update-timer"
38 : #define PREF_BLOCKLIST_ONECRL_CHECKED "services.blocklist.onecrl.checked"
39 : #define PREF_MAX_STALENESS_IN_SECONDS "security.onecrl.maximum_staleness_in_seconds"
40 : #define PREF_ONECRL_VIA_AMO "security.onecrl.via.amo"
41 :
42 : static LazyLogModule gCertBlockPRLog("CertBlock");
43 :
44 : uint32_t CertBlocklist::sLastBlocklistUpdate = 0U;
45 : uint32_t CertBlocklist::sLastKintoUpdate = 0U;
46 : uint32_t CertBlocklist::sMaxStaleness = 0U;
47 : bool CertBlocklist::sUseAMO = true;
48 :
49 0 : CertBlocklistItem::CertBlocklistItem(const uint8_t* DNData,
50 : size_t DNLength,
51 : const uint8_t* otherData,
52 : size_t otherLength,
53 0 : CertBlocklistItemMechanism itemMechanism)
54 : : mIsCurrent(false)
55 0 : , mItemMechanism(itemMechanism)
56 : {
57 0 : mDNData = new uint8_t[DNLength];
58 0 : memcpy(mDNData, DNData, DNLength);
59 0 : mDNLength = DNLength;
60 :
61 0 : mOtherData = new uint8_t[otherLength];
62 0 : memcpy(mOtherData, otherData, otherLength);
63 0 : mOtherLength = otherLength;
64 0 : }
65 :
66 0 : CertBlocklistItem::CertBlocklistItem(const CertBlocklistItem& aItem)
67 : {
68 0 : mDNLength = aItem.mDNLength;
69 0 : mDNData = new uint8_t[mDNLength];
70 0 : memcpy(mDNData, aItem.mDNData, mDNLength);
71 :
72 0 : mOtherLength = aItem.mOtherLength;
73 0 : mOtherData = new uint8_t[mOtherLength];
74 0 : memcpy(mOtherData, aItem.mOtherData, mOtherLength);
75 :
76 0 : mItemMechanism = aItem.mItemMechanism;
77 :
78 0 : mIsCurrent = aItem.mIsCurrent;
79 0 : }
80 :
81 0 : CertBlocklistItem::~CertBlocklistItem()
82 : {
83 0 : delete[] mDNData;
84 0 : delete[] mOtherData;
85 0 : }
86 :
87 : nsresult
88 0 : CertBlocklistItem::ToBase64(nsACString& b64DNOut, nsACString& b64OtherOut)
89 : {
90 0 : nsDependentCSubstring DNString(BitwiseCast<char*, uint8_t*>(mDNData),
91 0 : mDNLength);
92 0 : nsDependentCSubstring otherString(BitwiseCast<char*, uint8_t*>(mOtherData),
93 0 : mOtherLength);
94 0 : nsresult rv = Base64Encode(DNString, b64DNOut);
95 0 : if (NS_FAILED(rv)) {
96 0 : return rv;
97 : }
98 0 : rv = Base64Encode(otherString, b64OtherOut);
99 0 : return rv;
100 : }
101 :
102 : bool
103 0 : CertBlocklistItem::operator==(const CertBlocklistItem& aItem) const
104 : {
105 0 : if (aItem.mItemMechanism != mItemMechanism) {
106 0 : return false;
107 : }
108 0 : if (aItem.mDNLength != mDNLength ||
109 0 : aItem.mOtherLength != mOtherLength) {
110 0 : return false;
111 : }
112 0 : return memcmp(aItem.mDNData, mDNData, mDNLength) == 0 &&
113 0 : memcmp(aItem.mOtherData, mOtherData, mOtherLength) == 0;
114 : }
115 :
116 : uint32_t
117 0 : CertBlocklistItem::Hash() const
118 : {
119 : uint32_t hash;
120 : // there's no requirement for a serial to be as large as the size of the hash
121 : // key; if it's smaller, fall back to the first octet (otherwise, the last
122 : // four)
123 0 : if (mItemMechanism == BlockByIssuerAndSerial &&
124 0 : mOtherLength >= sizeof(hash)) {
125 0 : memcpy(&hash, mOtherData + mOtherLength - sizeof(hash), sizeof(hash));
126 : } else {
127 0 : hash = *mOtherData;
128 : }
129 0 : return hash;
130 : }
131 :
132 0 : CertBlocklist::CertBlocklist()
133 : : mMutex("CertBlocklist::mMutex")
134 : , mModified(false)
135 : , mBackingFileIsInitialized(false)
136 0 : , mBackingFile(nullptr)
137 : {
138 0 : }
139 :
140 0 : CertBlocklist::~CertBlocklist()
141 : {
142 : Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged,
143 : PREF_BACKGROUND_UPDATE_TIMER,
144 0 : this);
145 : Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged,
146 : PREF_MAX_STALENESS_IN_SECONDS,
147 0 : this);
148 : Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged,
149 : PREF_ONECRL_VIA_AMO,
150 0 : this);
151 : Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged,
152 : PREF_BLOCKLIST_ONECRL_CHECKED,
153 0 : this);
154 0 : }
155 :
156 : nsresult
157 0 : CertBlocklist::Init()
158 : {
159 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, ("CertBlocklist::Init"));
160 :
161 : // Init must be on main thread for getting the profile directory
162 0 : if (!NS_IsMainThread()) {
163 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
164 : ("CertBlocklist::Init - called off main thread"));
165 0 : return NS_ERROR_NOT_SAME_THREAD;
166 : }
167 :
168 : // Register preference callbacks
169 : nsresult rv =
170 : Preferences::RegisterCallbackAndCall(CertBlocklist::PreferenceChanged,
171 : PREF_BACKGROUND_UPDATE_TIMER,
172 0 : this);
173 0 : if (NS_FAILED(rv)) {
174 0 : return rv;
175 : }
176 : rv = Preferences::RegisterCallbackAndCall(CertBlocklist::PreferenceChanged,
177 : PREF_MAX_STALENESS_IN_SECONDS,
178 0 : this);
179 0 : if (NS_FAILED(rv)) {
180 0 : return rv;
181 : }
182 : rv = Preferences::RegisterCallbackAndCall(CertBlocklist::PreferenceChanged,
183 : PREF_ONECRL_VIA_AMO,
184 0 : this);
185 0 : if (NS_FAILED(rv)) {
186 0 : return rv;
187 : }
188 : rv = Preferences::RegisterCallbackAndCall(CertBlocklist::PreferenceChanged,
189 : PREF_BLOCKLIST_ONECRL_CHECKED,
190 0 : this);
191 0 : if (NS_FAILED(rv)) {
192 0 : return rv;
193 : }
194 :
195 : // Get the profile directory
196 0 : rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
197 0 : getter_AddRefs(mBackingFile));
198 0 : if (NS_FAILED(rv) || !mBackingFile) {
199 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
200 : ("CertBlocklist::Init - couldn't get profile dir"));
201 : // Since we're returning NS_OK here, set mBackingFile to a safe value.
202 : // (We need initialization to succeed and CertBlocklist to be in a
203 : // well-defined state if the profile directory doesn't exist.)
204 0 : mBackingFile = nullptr;
205 0 : return NS_OK;
206 : }
207 0 : rv = mBackingFile->Append(NS_LITERAL_STRING("revocations.txt"));
208 0 : if (NS_FAILED(rv)) {
209 0 : return rv;
210 : }
211 0 : nsAutoCString path;
212 0 : rv = mBackingFile->GetNativePath(path);
213 0 : if (NS_FAILED(rv)) {
214 0 : return rv;
215 : }
216 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
217 : ("CertBlocklist::Init certList path: %s", path.get()));
218 :
219 0 : return NS_OK;
220 : }
221 :
222 : nsresult
223 0 : CertBlocklist::EnsureBackingFileInitialized(MutexAutoLock& lock)
224 : {
225 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
226 : ("CertBlocklist::EnsureBackingFileInitialized"));
227 0 : if (mBackingFileIsInitialized || !mBackingFile) {
228 0 : return NS_OK;
229 : }
230 :
231 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
232 : ("CertBlocklist::EnsureBackingFileInitialized - not initialized"));
233 :
234 0 : bool exists = false;
235 0 : nsresult rv = mBackingFile->Exists(&exists);
236 0 : if (NS_FAILED(rv)) {
237 0 : return rv;
238 : }
239 0 : if (!exists) {
240 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
241 : ("CertBlocklist::EnsureBackingFileInitialized no revocations file"));
242 0 : return NS_OK;
243 : }
244 :
245 : // Load the revocations file into the cert blocklist
246 : nsCOMPtr<nsIFileInputStream> fileStream(
247 0 : do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
248 0 : if (NS_FAILED(rv)) {
249 0 : return rv;
250 : }
251 :
252 0 : rv = fileStream->Init(mBackingFile, -1, -1, false);
253 0 : if (NS_FAILED(rv)) {
254 0 : return rv;
255 : }
256 :
257 0 : nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(fileStream, &rv));
258 0 : nsAutoCString line;
259 0 : nsAutoCString DN;
260 0 : nsAutoCString other;
261 : CertBlocklistItemMechanism mechanism;
262 : // read in the revocations file. The file format is as follows: each line
263 : // contains a comment, base64 encoded DER for a DN, base64 encoded DER for a
264 : // serial number or a Base64 encoded SHA256 hash of a public key. Comment
265 : // lines start with '#', serial number lines, ' ' (a space), public key hashes
266 : // with '\t' (a tab) and anything else is assumed to be a DN.
267 0 : bool more = true;
268 0 : do {
269 0 : rv = lineStream->ReadLine(line, &more);
270 0 : if (NS_FAILED(rv)) {
271 0 : break;
272 : }
273 : // ignore comments and empty lines
274 0 : if (line.IsEmpty() || line.First() == '#') {
275 0 : continue;
276 : }
277 0 : if (line.First() != ' ' && line.First() != '\t') {
278 0 : DN = line;
279 0 : continue;
280 : }
281 0 : other = line;
282 0 : if (line.First() == ' ') {
283 0 : mechanism = BlockByIssuerAndSerial;
284 : } else {
285 0 : mechanism = BlockBySubjectAndPubKey;
286 : }
287 0 : other.Trim(" \t", true, false, false);
288 : // Serial numbers and public key hashes 'belong' to the last DN line seen;
289 : // if no DN has been seen, the serial number or public key hash is ignored.
290 0 : if (DN.IsEmpty() || other.IsEmpty()) {
291 0 : continue;
292 : }
293 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
294 : ("CertBlocklist::EnsureBackingFileInitialized adding: %s %s",
295 : DN.get(), other.get()));
296 :
297 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
298 : ("CertBlocklist::EnsureBackingFileInitialized - pre-decode"));
299 :
300 0 : rv = AddRevokedCertInternal(DN, other, mechanism, CertOldFromLocalCache,
301 : lock);
302 :
303 0 : if (NS_FAILED(rv)) {
304 : // we warn here, rather than abandoning, since we need to
305 : // ensure that as many items as possible are read
306 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
307 : ("CertBlocklist::EnsureBackingFileInitialized adding revoked cert "
308 : "failed"));
309 : }
310 : } while (more);
311 0 : mBackingFileIsInitialized = true;
312 0 : return NS_OK;
313 : }
314 :
315 : NS_IMETHODIMP
316 0 : CertBlocklist::RevokeCertBySubjectAndPubKey(const nsACString& aSubject,
317 : const nsACString& aPubKeyHash)
318 : {
319 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
320 : ("CertBlocklist::RevokeCertBySubjectAndPubKey - subject is: %s and pubKeyHash: %s",
321 : PromiseFlatCString(aSubject).get(),
322 : PromiseFlatCString(aPubKeyHash).get()));
323 0 : MutexAutoLock lock(mMutex);
324 :
325 : return AddRevokedCertInternal(aSubject, aPubKeyHash,
326 : BlockBySubjectAndPubKey,
327 0 : CertNewFromBlocklist, lock);
328 : }
329 :
330 : NS_IMETHODIMP
331 0 : CertBlocklist::RevokeCertByIssuerAndSerial(const nsACString& aIssuer,
332 : const nsACString& aSerialNumber)
333 : {
334 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
335 : ("CertBlocklist::RevokeCertByIssuerAndSerial - issuer is: %s and serial: %s",
336 : PromiseFlatCString(aIssuer).get(),
337 : PromiseFlatCString(aSerialNumber).get()));
338 0 : MutexAutoLock lock(mMutex);
339 :
340 : return AddRevokedCertInternal(aIssuer, aSerialNumber,
341 : BlockByIssuerAndSerial,
342 0 : CertNewFromBlocklist, lock);
343 : }
344 :
345 : nsresult
346 0 : CertBlocklist::AddRevokedCertInternal(const nsACString& aEncodedDN,
347 : const nsACString& aEncodedOther,
348 : CertBlocklistItemMechanism aMechanism,
349 : CertBlocklistItemState aItemState,
350 : MutexAutoLock& /*proofOfLock*/)
351 : {
352 0 : nsCString decodedDN;
353 0 : nsCString decodedOther;
354 :
355 0 : nsresult rv = Base64Decode(aEncodedDN, decodedDN);
356 0 : if (NS_FAILED(rv)) {
357 0 : return rv;
358 : }
359 0 : rv = Base64Decode(aEncodedOther, decodedOther);
360 0 : if (NS_FAILED(rv)) {
361 0 : return rv;
362 : }
363 :
364 : CertBlocklistItem item(
365 : BitwiseCast<const uint8_t*, const char*>(decodedDN.get()),
366 0 : decodedDN.Length(),
367 : BitwiseCast<const uint8_t*, const char*>(decodedOther.get()),
368 0 : decodedOther.Length(),
369 0 : aMechanism);
370 :
371 0 : if (aItemState == CertNewFromBlocklist) {
372 : // We want SaveEntries to be a no-op if no new entries are added.
373 0 : nsGenericHashKey<CertBlocklistItem>* entry = mBlocklist.GetEntry(item);
374 0 : if (!entry) {
375 0 : mModified = true;
376 : } else {
377 : // Ensure that any existing item is replaced by a fresh one so we can
378 : // use mIsCurrent to decide which entries to write out.
379 0 : mBlocklist.RemoveEntry(entry);
380 : }
381 0 : item.mIsCurrent = true;
382 : }
383 0 : mBlocklist.PutEntry(item);
384 :
385 0 : return NS_OK;
386 : }
387 :
388 : // Write a line for a given string in the output stream
389 : nsresult
390 0 : WriteLine(nsIOutputStream* outputStream, const nsACString& string)
391 : {
392 0 : nsAutoCString line(string);
393 0 : line.Append('\n');
394 :
395 0 : const char* data = line.get();
396 0 : uint32_t length = line.Length();
397 0 : nsresult rv = NS_OK;
398 0 : while (NS_SUCCEEDED(rv) && length) {
399 0 : uint32_t bytesWritten = 0;
400 0 : rv = outputStream->Write(data, length, &bytesWritten);
401 0 : if (NS_FAILED(rv)) {
402 0 : return rv;
403 : }
404 : // if no data is written, something is wrong
405 0 : if (!bytesWritten) {
406 0 : return NS_ERROR_FAILURE;
407 : }
408 0 : length -= bytesWritten;
409 0 : data += bytesWritten;
410 : }
411 0 : return rv;
412 : }
413 :
414 : // void saveEntries();
415 : // Store the blockist in a text file containing base64 encoded issuers and
416 : // serial numbers.
417 : //
418 : // Each item is stored on a separate line; each issuer is followed by its
419 : // revoked serial numbers, indented by one space.
420 : //
421 : // lines starting with a # character are ignored
422 : NS_IMETHODIMP
423 0 : CertBlocklist::SaveEntries()
424 : {
425 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
426 : ("CertBlocklist::SaveEntries - not initialized"));
427 0 : MutexAutoLock lock(mMutex);
428 0 : if (!mModified) {
429 0 : return NS_OK;
430 : }
431 :
432 0 : nsresult rv = EnsureBackingFileInitialized(lock);
433 0 : if (NS_FAILED(rv)) {
434 0 : return rv;
435 : }
436 :
437 0 : if (!mBackingFile) {
438 : // We allow this to succeed with no profile directory for tests
439 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
440 : ("CertBlocklist::SaveEntries no file in profile to write to"));
441 0 : return NS_OK;
442 : }
443 :
444 : // Data needed for writing blocklist items out to the revocations file
445 0 : IssuerTable issuerTable;
446 0 : BlocklistStringSet issuers;
447 0 : nsCOMPtr<nsIOutputStream> outputStream;
448 :
449 0 : rv = NS_NewAtomicFileOutputStream(getter_AddRefs(outputStream),
450 0 : mBackingFile, -1, -1, 0);
451 0 : if (NS_FAILED(rv)) {
452 0 : return rv;
453 : }
454 :
455 0 : rv = WriteLine(outputStream,
456 0 : NS_LITERAL_CSTRING("# Auto generated contents. Do not edit."));
457 0 : if (NS_FAILED(rv)) {
458 0 : return rv;
459 : }
460 :
461 : // Sort blocklist items into lists of serials for each issuer
462 0 : for (auto iter = mBlocklist.Iter(); !iter.Done(); iter.Next()) {
463 0 : CertBlocklistItem item = iter.Get()->GetKey();
464 0 : if (!item.mIsCurrent) {
465 0 : continue;
466 : }
467 :
468 0 : nsAutoCString encDN;
469 0 : nsAutoCString encOther;
470 :
471 0 : nsresult rv = item.ToBase64(encDN, encOther);
472 0 : if (NS_FAILED(rv)) {
473 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
474 : ("CertBlocklist::SaveEntries writing revocation data failed"));
475 0 : return NS_ERROR_FAILURE;
476 : }
477 :
478 : // If it's a subject / public key block, write it straight out
479 0 : if (item.mItemMechanism == BlockBySubjectAndPubKey) {
480 0 : WriteLine(outputStream, encDN);
481 0 : WriteLine(outputStream, NS_LITERAL_CSTRING("\t") + encOther);
482 0 : continue;
483 : }
484 :
485 : // Otherwise, we have to group entries by issuer
486 0 : issuers.PutEntry(encDN);
487 0 : BlocklistStringSet* issuerSet = issuerTable.Get(encDN);
488 0 : if (!issuerSet) {
489 0 : issuerSet = new BlocklistStringSet();
490 0 : issuerTable.Put(encDN, issuerSet);
491 : }
492 0 : issuerSet->PutEntry(encOther);
493 : }
494 :
495 0 : for (auto iter = issuers.Iter(); !iter.Done(); iter.Next()) {
496 0 : nsCStringHashKey* hashKey = iter.Get();
497 0 : nsAutoPtr<BlocklistStringSet> issuerSet;
498 0 : issuerTable.Remove(hashKey->GetKey(), &issuerSet);
499 :
500 0 : nsresult rv = WriteLine(outputStream, hashKey->GetKey());
501 0 : if (NS_FAILED(rv)) {
502 0 : break;
503 : }
504 :
505 : // Write serial data to the output stream
506 0 : for (auto iter = issuerSet->Iter(); !iter.Done(); iter.Next()) {
507 0 : nsresult rv = WriteLine(outputStream,
508 0 : NS_LITERAL_CSTRING(" ") + iter.Get()->GetKey());
509 0 : if (NS_FAILED(rv)) {
510 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
511 : ("CertBlocklist::SaveEntries writing revocation data failed"));
512 0 : return NS_ERROR_FAILURE;
513 : }
514 : }
515 : }
516 :
517 0 : nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(outputStream);
518 0 : MOZ_ASSERT(safeStream, "expected a safe output stream!");
519 0 : if (!safeStream) {
520 0 : return NS_ERROR_FAILURE;
521 : }
522 0 : rv = safeStream->Finish();
523 0 : if (NS_FAILED(rv)) {
524 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
525 : ("CertBlocklist::SaveEntries saving revocation data failed"));
526 0 : return rv;
527 : }
528 0 : mModified = false;
529 0 : return NS_OK;
530 : }
531 :
532 : NS_IMETHODIMP
533 0 : CertBlocklist::IsCertRevoked(const uint8_t* aIssuer,
534 : uint32_t aIssuerLength,
535 : const uint8_t* aSerial,
536 : uint32_t aSerialLength,
537 : const uint8_t* aSubject,
538 : uint32_t aSubjectLength,
539 : const uint8_t* aPubKey,
540 : uint32_t aPubKeyLength,
541 : bool* _retval)
542 : {
543 0 : MutexAutoLock lock(mMutex);
544 :
545 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
546 : ("CertBlocklist::IsCertRevoked?"));
547 0 : nsresult rv = EnsureBackingFileInitialized(lock);
548 0 : if (NS_FAILED(rv)) {
549 0 : return rv;
550 : }
551 :
552 0 : Input issuer;
553 0 : Input serial;
554 0 : if (issuer.Init(aIssuer, aIssuerLength) != Success) {
555 0 : return NS_ERROR_FAILURE;
556 : }
557 0 : if (serial.Init(aSerial, aSerialLength) != Success) {
558 0 : return NS_ERROR_FAILURE;
559 : }
560 :
561 : CertBlocklistItem issuerSerial(aIssuer, aIssuerLength, aSerial, aSerialLength,
562 0 : BlockByIssuerAndSerial);
563 :
564 0 : nsAutoCString encDN;
565 0 : nsAutoCString encOther;
566 :
567 0 : issuerSerial.ToBase64(encDN, encOther);
568 0 : if (NS_FAILED(rv)) {
569 0 : return rv;
570 : }
571 :
572 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
573 : ("CertBlocklist::IsCertRevoked issuer %s - serial %s",
574 : encDN.get(), encOther.get()));
575 :
576 0 : *_retval = mBlocklist.Contains(issuerSerial);
577 :
578 0 : if (*_retval) {
579 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
580 : ("certblocklist::IsCertRevoked found by issuer / serial"));
581 0 : return NS_OK;
582 : }
583 :
584 0 : nsCOMPtr<nsICryptoHash> crypto;
585 0 : crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
586 :
587 0 : rv = crypto->Init(nsICryptoHash::SHA256);
588 0 : if (NS_FAILED(rv)) {
589 0 : return rv;
590 : }
591 :
592 0 : rv = crypto->Update(aPubKey, aPubKeyLength);
593 0 : if (NS_FAILED(rv)) {
594 0 : return rv;
595 : }
596 :
597 0 : nsCString hashString;
598 0 : rv = crypto->Finish(false, hashString);
599 0 : if (NS_FAILED(rv)) {
600 0 : return rv;
601 : }
602 :
603 : CertBlocklistItem subjectPubKey(
604 : aSubject,
605 : static_cast<size_t>(aSubjectLength),
606 : BitwiseCast<const uint8_t*, const char*>(hashString.get()),
607 0 : hashString.Length(),
608 0 : BlockBySubjectAndPubKey);
609 :
610 0 : rv = subjectPubKey.ToBase64(encDN, encOther);
611 0 : if (NS_FAILED(rv)) {
612 0 : return rv;
613 : }
614 :
615 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
616 : ("CertBlocklist::IsCertRevoked subject %s - pubKey hash %s",
617 : encDN.get(), encOther.get()));
618 0 : *_retval = mBlocklist.Contains(subjectPubKey);
619 :
620 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
621 : ("CertBlocklist::IsCertRevoked by subject / pubkey? %s",
622 : *_retval ? "true" : "false"));
623 :
624 0 : return NS_OK;
625 : }
626 :
627 : NS_IMETHODIMP
628 0 : CertBlocklist::IsBlocklistFresh(bool* _retval)
629 : {
630 0 : MutexAutoLock lock(mMutex);
631 0 : *_retval = false;
632 :
633 0 : uint32_t now = uint32_t(PR_Now() / PR_USEC_PER_SEC);
634 0 : uint32_t lastUpdate = sUseAMO ? sLastBlocklistUpdate : sLastKintoUpdate;
635 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
636 : ("CertBlocklist::IsBlocklistFresh using AMO? %i lastUpdate is %i",
637 : sUseAMO, lastUpdate));
638 :
639 0 : if (now > lastUpdate) {
640 0 : int64_t interval = now - lastUpdate;
641 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
642 : ("CertBlocklist::IsBlocklistFresh we're after the last BlocklistUpdate "
643 : "interval is %" PRId64 ", staleness %u", interval, sMaxStaleness));
644 0 : *_retval = sMaxStaleness > interval;
645 : }
646 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
647 : ("CertBlocklist::IsBlocklistFresh ? %s", *_retval ? "true" : "false"));
648 0 : return NS_OK;
649 : }
650 :
651 :
652 : /* static */
653 : void
654 0 : CertBlocklist::PreferenceChanged(const char* aPref, void* aClosure)
655 :
656 : {
657 0 : auto blocklist = static_cast<CertBlocklist*>(aClosure);
658 0 : MutexAutoLock lock(blocklist->mMutex);
659 :
660 0 : MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
661 : ("CertBlocklist::PreferenceChanged %s changed", aPref));
662 0 : if (strcmp(aPref, PREF_BACKGROUND_UPDATE_TIMER) == 0) {
663 0 : sLastBlocklistUpdate = Preferences::GetUint(PREF_BACKGROUND_UPDATE_TIMER,
664 : uint32_t(0));
665 0 : } else if (strcmp(aPref, PREF_BLOCKLIST_ONECRL_CHECKED) == 0) {
666 0 : sLastKintoUpdate = Preferences::GetUint(PREF_BLOCKLIST_ONECRL_CHECKED,
667 : uint32_t(0));
668 0 : } else if (strcmp(aPref, PREF_MAX_STALENESS_IN_SECONDS) == 0) {
669 0 : sMaxStaleness = Preferences::GetUint(PREF_MAX_STALENESS_IN_SECONDS,
670 : uint32_t(0));
671 0 : } else if (strcmp(aPref, PREF_ONECRL_VIA_AMO) == 0) {
672 0 : sUseAMO = Preferences::GetBool(PREF_ONECRL_VIA_AMO, true);
673 : }
674 0 : }
|