1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 extern crate base64;
6 extern crate byteorder;
7 extern crate crossbeam_utils;
8 #[macro_use]
9 extern crate cstr;
10 #[macro_use]
11 extern crate log;
12 extern crate memmap2;
13 extern crate moz_task;
14 extern crate nserror;
15 extern crate nsstring;
16 #[macro_use]
17 extern crate rental;
18 extern crate rkv;
19 extern crate rust_cascade;
20 extern crate sha2;
21 extern crate thin_vec;
22 extern crate time;
23 #[macro_use]
24 extern crate xpcom;
25 #[macro_use]
26 extern crate malloc_size_of_derive;
27 extern crate storage_variant;
28 extern crate tempfile;
29 
30 extern crate wr_malloc_size_of;
31 use wr_malloc_size_of as malloc_size_of;
32 
33 use byteorder::{LittleEndian, NetworkEndian, ReadBytesExt, WriteBytesExt};
34 use crossbeam_utils::atomic::AtomicCell;
35 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
36 use memmap2::Mmap;
37 use moz_task::{create_background_task_queue, is_main_thread, Task, TaskRunnable};
38 use nserror::{
39     nsresult, NS_ERROR_FAILURE, NS_ERROR_NOT_SAME_THREAD, NS_ERROR_NO_AGGREGATION,
40     NS_ERROR_NULL_POINTER, NS_ERROR_UNEXPECTED, NS_OK,
41 };
42 use nsstring::{nsACString, nsAString, nsCStr, nsCString, nsString};
43 use rkv::backend::{BackendEnvironmentBuilder, SafeMode, SafeModeDatabase, SafeModeEnvironment};
44 use rkv::{StoreError, StoreOptions, Value};
45 use rust_cascade::Cascade;
46 use sha2::{Digest, Sha256};
47 use std::collections::{HashMap, HashSet};
48 use std::ffi::{CStr, CString};
49 use std::fmt::Display;
50 use std::fs::{create_dir_all, remove_file, File, OpenOptions};
51 use std::io::{BufRead, BufReader, Read, Write};
52 use std::mem::size_of;
53 use std::os::raw::c_char;
54 use std::path::{Path, PathBuf};
55 use std::slice;
56 use std::str;
57 use std::sync::{Arc, RwLock};
58 use std::time::{Duration, SystemTime};
59 use storage_variant::VariantType;
60 use thin_vec::ThinVec;
61 use xpcom::interfaces::{
62     nsICRLiteCoverage, nsICRLiteTimestamp, nsICertInfo, nsICertStorage, nsICertStorageCallback,
63     nsIFile, nsIHandleReportCallback, nsIIssuerAndSerialRevocationState, nsIMemoryReporter,
64     nsIMemoryReporterManager, nsIObserver, nsIPrefBranch, nsIRevocationState, nsISerialEventTarget,
65     nsISubjectAndPubKeyRevocationState, nsISupports,
66 };
67 use xpcom::{nsIID, GetterAddrefs, RefPtr, ThreadBoundRefPtr, XpCom};
68 
69 const PREFIX_REV_IS: &str = "is";
70 const PREFIX_REV_SPK: &str = "spk";
71 const PREFIX_SUBJECT: &str = "subject";
72 const PREFIX_CERT: &str = "cert";
73 const PREFIX_DATA_TYPE: &str = "datatype";
74 
75 const COVERAGE_SERIALIZATION_VERSION: u8 = 1;
76 const COVERAGE_V1_ENTRY_BYTES: usize = 48;
77 
78 const ENROLLMENT_SERIALIZATION_VERSION: u8 = 1;
79 const ENROLLMENT_V1_ENTRY_BYTES: usize = 32;
80 
81 type Rkv = rkv::Rkv<SafeModeEnvironment>;
82 type SingleStore = rkv::SingleStore<SafeModeDatabase>;
83 
84 macro_rules! make_key {
85     ( $prefix:expr, $( $part:expr ),+ ) => {
86         {
87             let mut key = $prefix.as_bytes().to_owned();
88             $( key.extend_from_slice($part); )+
89             key
90         }
91     }
92 }
93 
94 #[allow(non_camel_case_types, non_snake_case)]
95 
96 /// `SecurityStateError` is a type to represent errors in accessing or
97 /// modifying security state.
98 #[derive(Debug)]
99 struct SecurityStateError {
100     message: String,
101 }
102 
103 impl<T: Display> From<T> for SecurityStateError {
104     /// Creates a new instance of `SecurityStateError` from something that
105     /// implements the `Display` trait.
from(err: T) -> SecurityStateError106     fn from(err: T) -> SecurityStateError {
107         SecurityStateError {
108             message: format!("{}", err),
109         }
110     }
111 }
112 
113 struct EnvAndStore {
114     env: Rkv,
115     store: SingleStore,
116 }
117 
118 impl MallocSizeOf for EnvAndStore {
size_of(&self, _ops: &mut MallocSizeOfOps) -> usize119     fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
120         self.env
121             .read()
122             .and_then(|reader| {
123                 let iter = self.store.iter_start(&reader)?.into_iter();
124                 Ok(iter
125                     .map(|r| {
126                         r.map(|(k, v)| k.len() + v.serialized_size().unwrap_or(0) as usize)
127                             .unwrap_or(0)
128                     })
129                     .sum())
130             })
131             .unwrap_or(0)
132     }
133 }
134 
135 // In Rust, structs cannot have self references (if a struct gets moved, the compiler has no
136 // guarantees that the references are still valid). In our case, since the memmapped data is at a
137 // particular place in memory (and that's what we're referencing), we can use the rental crate to
138 // create a struct that does reference itself.
139 rental! {
140     mod holding {
141         use super::{Cascade, Mmap};
142 
143         #[rental]
144         pub struct CRLiteFilter {
145             backing_file: Box<Mmap>,
146             cascade: Box<Cascade<'backing_file>>,
147         }
148     }
149 }
150 
151 /// `SecurityState`
152 #[derive(MallocSizeOf)]
153 struct SecurityState {
154     profile_path: PathBuf,
155     env_and_store: Option<EnvAndStore>,
156     int_prefs: HashMap<String, u32>,
157     #[ignore_malloc_size_of = "rental crate does not allow impls for rental structs"]
158     crlite_filter: Option<holding::CRLiteFilter>,
159     /// Maps issuer spki hashes to sets of serial numbers.
160     crlite_stash: Option<HashMap<Vec<u8>, HashSet<Vec<u8>>>>,
161     /// Maps an RFC 6962 LogID to a pair of 64 bit unix timestamps
162     crlite_coverage: Option<HashMap<Vec<u8>, (u64, u64)>>,
163     /// Set of `SHA256(subject || spki)` values for enrolled issuers
164     crlite_enrollment: Option<HashSet<Vec<u8>>>,
165     /// Tracks the number of asynchronous operations which have been dispatched but not completed.
166     remaining_ops: i32,
167 }
168 
169 impl SecurityState {
new(profile_path: PathBuf) -> Result<SecurityState, SecurityStateError>170     pub fn new(profile_path: PathBuf) -> Result<SecurityState, SecurityStateError> {
171         // Since this gets called on the main thread, we don't actually want to open the DB yet.
172         // We do this on-demand later, when we're probably on a certificate verification thread.
173         Ok(SecurityState {
174             profile_path,
175             env_and_store: None,
176             int_prefs: HashMap::new(),
177             crlite_filter: None,
178             crlite_stash: None,
179             crlite_coverage: None,
180             crlite_enrollment: None,
181             remaining_ops: 0,
182         })
183     }
184 
db_needs_opening(&self) -> bool185     pub fn db_needs_opening(&self) -> bool {
186         self.env_and_store.is_none()
187     }
188 
open_db(&mut self) -> Result<(), SecurityStateError>189     pub fn open_db(&mut self) -> Result<(), SecurityStateError> {
190         if self.env_and_store.is_some() {
191             return Ok(());
192         }
193 
194         let store_path = get_store_path(&self.profile_path)?;
195 
196         // Open the store in read-write mode to create it (if needed) and migrate data from the old
197         // store (if any).
198         // If opening initially fails, try to remove and recreate the database. Consumers will
199         // repopulate the database as necessary if this happens (see bug 1546361).
200         let env = make_env(store_path.as_path()).or_else(|_| {
201             remove_db(store_path.as_path())?;
202             make_env(store_path.as_path())
203         })?;
204         let store = env.open_single("cert_storage", StoreOptions::create())?;
205 
206         // if the profile has a revocations.txt, migrate it and remove the file
207         let mut revocations_path = self.profile_path.clone();
208         revocations_path.push("revocations.txt");
209         if revocations_path.exists() {
210             SecurityState::migrate(&revocations_path, &env, &store)?;
211             remove_file(revocations_path)?;
212         }
213 
214         // We already returned early if env_and_store was Some, so this should take the None branch.
215         match self.env_and_store.replace(EnvAndStore { env, store }) {
216             Some(_) => Err(SecurityStateError::from(
217                 "env and store already initialized? (did we mess up our threading model?)",
218             )),
219             None => Ok(()),
220         }?;
221         self.load_crlite_filter()?;
222         Ok(())
223     }
224 
migrate( revocations_path: &PathBuf, env: &Rkv, store: &SingleStore, ) -> Result<(), SecurityStateError>225     fn migrate(
226         revocations_path: &PathBuf,
227         env: &Rkv,
228         store: &SingleStore,
229     ) -> Result<(), SecurityStateError> {
230         let f = File::open(revocations_path)?;
231         let file = BufReader::new(f);
232         let value = Value::I64(nsICertStorage::STATE_ENFORCE as i64);
233         let mut writer = env.write()?;
234 
235         // Add the data from revocations.txt
236         let mut dn: Option<Vec<u8>> = None;
237         for line in file.lines() {
238             let l = match line.map_err(|_| SecurityStateError::from("io error reading line data")) {
239                 Ok(data) => data,
240                 Err(e) => return Err(e),
241             };
242             if l.len() == 0 || l.starts_with("#") {
243                 continue;
244             }
245             let leading_char = match l.chars().next() {
246                 Some(c) => c,
247                 None => {
248                     return Err(SecurityStateError::from(
249                         "couldn't get char from non-empty str?",
250                     ));
251                 }
252             };
253             // In future, we can maybe log migration failures. For now, ignore decoding and storage
254             // errors and attempt to continue.
255             // Check if we have a new DN
256             if leading_char != '\t' && leading_char != ' ' {
257                 if let Ok(decoded_dn) = base64::decode(&l) {
258                     dn = Some(decoded_dn);
259                 }
260                 continue;
261             }
262             let l_sans_prefix = match base64::decode(&l[1..]) {
263                 Ok(decoded) => decoded,
264                 Err(_) => continue,
265             };
266             if let Some(name) = &dn {
267                 if leading_char == '\t' {
268                     let _ = store.put(
269                         &mut writer,
270                         &make_key!(PREFIX_REV_SPK, name, &l_sans_prefix),
271                         &value,
272                     );
273                 } else {
274                     let _ = store.put(
275                         &mut writer,
276                         &make_key!(PREFIX_REV_IS, name, &l_sans_prefix),
277                         &value,
278                     );
279                 }
280             }
281         }
282 
283         writer.commit()?;
284         Ok(())
285     }
286 
read_entry(&self, key: &[u8]) -> Result<Option<i16>, SecurityStateError>287     fn read_entry(&self, key: &[u8]) -> Result<Option<i16>, SecurityStateError> {
288         let env_and_store = match self.env_and_store.as_ref() {
289             Some(env_and_store) => env_and_store,
290             None => return Err(SecurityStateError::from("env and store not initialized?")),
291         };
292         let reader = env_and_store.env.read()?;
293         match env_and_store.store.get(&reader, key) {
294             Ok(Some(Value::I64(i)))
295                 if i <= (std::i16::MAX as i64) && i >= (std::i16::MIN as i64) =>
296             {
297                 Ok(Some(i as i16))
298             }
299             Ok(None) => Ok(None),
300             Ok(_) => Err(SecurityStateError::from(
301                 "Unexpected type when trying to get a Value::I64",
302             )),
303             Err(_) => Err(SecurityStateError::from(
304                 "There was a problem getting the value",
305             )),
306         }
307     }
308 
get_has_prior_data(&self, data_type: u8) -> Result<bool, SecurityStateError>309     pub fn get_has_prior_data(&self, data_type: u8) -> Result<bool, SecurityStateError> {
310         if data_type == nsICertStorage::DATA_TYPE_CRLITE_FILTER_FULL {
311             return Ok(self.crlite_filter.is_some()
312                 && self.crlite_coverage.is_some()
313                 && self.crlite_enrollment.is_some());
314         }
315         if data_type == nsICertStorage::DATA_TYPE_CRLITE_FILTER_INCREMENTAL {
316             return Ok(self.crlite_stash.is_some());
317         }
318 
319         let env_and_store = match self.env_and_store.as_ref() {
320             Some(env_and_store) => env_and_store,
321             None => return Err(SecurityStateError::from("env and store not initialized?")),
322         };
323         let reader = env_and_store.env.read()?;
324         match env_and_store
325             .store
326             .get(&reader, &make_key!(PREFIX_DATA_TYPE, &[data_type]))
327         {
328             Ok(Some(Value::Bool(true))) => Ok(true),
329             Ok(None) => Ok(false),
330             Ok(_) => Err(SecurityStateError::from(
331                 "Unexpected type when trying to get a Value::Bool",
332             )),
333             Err(_) => Err(SecurityStateError::from(
334                 "There was a problem getting the value",
335             )),
336         }
337     }
338 
set_batch_state( &mut self, entries: &[EncodedSecurityState], typ: u8, ) -> Result<(), SecurityStateError>339     pub fn set_batch_state(
340         &mut self,
341         entries: &[EncodedSecurityState],
342         typ: u8,
343     ) -> Result<(), SecurityStateError> {
344         let env_and_store = match self.env_and_store.as_mut() {
345             Some(env_and_store) => env_and_store,
346             None => return Err(SecurityStateError::from("env and store not initialized?")),
347         };
348         let mut writer = env_and_store.env.write()?;
349         // Make a note that we have prior data of the given type now.
350         env_and_store.store.put(
351             &mut writer,
352             &make_key!(PREFIX_DATA_TYPE, &[typ]),
353             &Value::Bool(true),
354         )?;
355 
356         for entry in entries {
357             let key = match entry.key() {
358                 Ok(key) => key,
359                 Err(e) => {
360                     warn!("error base64-decoding key parts - ignoring: {}", e.message);
361                     continue;
362                 }
363             };
364             env_and_store
365                 .store
366                 .put(&mut writer, &key, &Value::I64(entry.state() as i64))?;
367         }
368 
369         writer.commit()?;
370         Ok(())
371     }
372 
get_revocation_state( &self, issuer: &[u8], serial: &[u8], subject: &[u8], pub_key: &[u8], ) -> Result<i16, SecurityStateError>373     pub fn get_revocation_state(
374         &self,
375         issuer: &[u8],
376         serial: &[u8],
377         subject: &[u8],
378         pub_key: &[u8],
379     ) -> Result<i16, SecurityStateError> {
380         let mut digest = Sha256::default();
381         digest.input(pub_key);
382         let pub_key_hash = digest.result();
383 
384         let subject_pubkey = make_key!(PREFIX_REV_SPK, subject, &pub_key_hash);
385         let issuer_serial = make_key!(PREFIX_REV_IS, issuer, serial);
386 
387         let st: i16 = match self.read_entry(&issuer_serial) {
388             Ok(Some(value)) => value,
389             Ok(None) => nsICertStorage::STATE_UNSET,
390             Err(_) => {
391                 return Err(SecurityStateError::from(
392                     "problem reading revocation state (from issuer / serial)",
393                 ));
394             }
395         };
396 
397         if st != nsICertStorage::STATE_UNSET {
398             return Ok(st);
399         }
400 
401         match self.read_entry(&subject_pubkey) {
402             Ok(Some(value)) => Ok(value),
403             Ok(None) => Ok(nsICertStorage::STATE_UNSET),
404             Err(_) => {
405                 return Err(SecurityStateError::from(
406                     "problem reading revocation state (from subject / pubkey)",
407                 ));
408             }
409         }
410     }
411 
issuer_is_enrolled(&self, subject: &[u8], pub_key: &[u8]) -> bool412     fn issuer_is_enrolled(&self, subject: &[u8], pub_key: &[u8]) -> bool {
413         if let Some(crlite_enrollment) = self.crlite_enrollment.as_ref() {
414             let mut digest = Sha256::default();
415             digest.input(subject);
416             digest.input(pub_key);
417             let issuer_id = digest.result();
418             return crlite_enrollment.contains(&issuer_id.to_vec());
419         }
420         return false;
421     }
422 
filter_covers_some_timestamp(&self, timestamps: &[CRLiteTimestamp]) -> bool423     fn filter_covers_some_timestamp(&self, timestamps: &[CRLiteTimestamp]) -> bool {
424         if let Some(crlite_coverage) = self.crlite_coverage.as_ref() {
425             for entry in timestamps {
426                 if let Some(&(low, high)) = crlite_coverage.get(entry.log_id.as_ref()) {
427                     if low <= entry.timestamp && entry.timestamp <= high {
428                         return true;
429                     }
430                 }
431             }
432         }
433         return false;
434     }
435 
set_full_crlite_filter( &mut self, filter: Vec<u8>, enrolled_issuers: Vec<nsCString>, coverage_entries: &[(nsCString, u64, u64)], ) -> Result<(), SecurityStateError>436     pub fn set_full_crlite_filter(
437         &mut self,
438         filter: Vec<u8>,
439         enrolled_issuers: Vec<nsCString>,
440         coverage_entries: &[(nsCString, u64, u64)],
441     ) -> Result<(), SecurityStateError> {
442         // First drop any existing crlite filter and clear the accumulated stash.
443         {
444             let _ = self.crlite_filter.take();
445             let _ = self.crlite_stash.take();
446             let _ = self.crlite_coverage.take();
447             let _ = self.crlite_enrollment.take();
448             let mut path = get_store_path(&self.profile_path)?;
449             path.push("crlite.stash");
450             // Truncate the stash file if it exists.
451             if path.exists() {
452                 File::create(path).map_err(|e| {
453                     SecurityStateError::from(format!("couldn't truncate stash file: {}", e))
454                 })?;
455             }
456         }
457         // Write the new full filter.
458         let mut path = get_store_path(&self.profile_path)?;
459         path.push("crlite.filter");
460         {
461             let mut filter_file = File::create(&path)?;
462             filter_file.write_all(&filter)?;
463         }
464 
465         // Serialize the coverage metadata as a 1 byte version number followed by any number of 48
466         // byte entries. Each entry is a 32 byte (opaque) log id, followed by two 8 byte
467         // timestamps. Each timestamp is an 8 byte unsigned integer in little endian.
468         let mut coverage_bytes =
469             Vec::with_capacity(size_of::<u8>() + coverage_entries.len() * COVERAGE_V1_ENTRY_BYTES);
470         coverage_bytes.push(COVERAGE_SERIALIZATION_VERSION);
471         for (b64_log_id, min_t, max_t) in coverage_entries {
472             let log_id = match base64::decode(&b64_log_id) {
473                 Ok(log_id) if log_id.len() == 32 => log_id,
474                 _ => {
475                     warn!("malformed log ID - skipping: {}", b64_log_id);
476                     continue;
477                 }
478             };
479             coverage_bytes.extend_from_slice(&log_id);
480             coverage_bytes.extend_from_slice(&min_t.to_le_bytes());
481             coverage_bytes.extend_from_slice(&max_t.to_le_bytes());
482         }
483         // Write the coverage file for the new filter
484         let mut path = get_store_path(&self.profile_path)?;
485         path.push("crlite.coverage");
486         {
487             let mut coverage_file = File::create(&path)?;
488             coverage_file.write_all(&coverage_bytes)?;
489         }
490 
491         // Serialize the enrollment list as a 1 byte version number followed by:
492         // Version 1: any number of 32 byte values of the form `SHA256(subject || spki)`.
493         let mut enrollment_bytes = Vec::with_capacity(
494             size_of::<u8>() + enrolled_issuers.len() * ENROLLMENT_V1_ENTRY_BYTES,
495         );
496         enrollment_bytes.push(ENROLLMENT_SERIALIZATION_VERSION);
497         for b64_issuer_id in enrolled_issuers {
498             let issuer_id = match base64::decode(&b64_issuer_id) {
499                 Ok(issuer_id) if issuer_id.len() == 32 => issuer_id,
500                 _ => {
501                     warn!("malformed issuer ID - skipping: {}", b64_issuer_id);
502                     continue;
503                 }
504             };
505             enrollment_bytes.extend_from_slice(&issuer_id);
506         }
507         // Write the enrollment file for the new filter
508         let mut path = get_store_path(&self.profile_path)?;
509         path.push("crlite.enrollment");
510         {
511             let mut enrollment_file = File::create(&path)?;
512             enrollment_file.write_all(&enrollment_bytes)?;
513         }
514 
515         self.load_crlite_filter()?;
516         Ok(())
517     }
518 
load_crlite_filter(&mut self) -> Result<(), SecurityStateError>519     fn load_crlite_filter(&mut self) -> Result<(), SecurityStateError> {
520         if self.crlite_filter.is_some() || self.crlite_coverage.is_some() {
521             return Err(SecurityStateError::from(
522                 "Both crlite_filter and crlite_coverage should be None here",
523             ));
524         }
525 
526         let mut path = get_store_path(&self.profile_path)?;
527         path.push("crlite.filter");
528         // Before we've downloaded any filters, this file won't exist.
529         if !path.exists() {
530             return Ok(());
531         }
532         let filter_file = File::open(path)?;
533         let mmap = unsafe { Mmap::map(&filter_file)? };
534         let crlite_filter = holding::CRLiteFilter::try_new(Box::new(mmap), |mmap| {
535             match Cascade::from_bytes(mmap)? {
536                 Some(cascade) => Ok(cascade),
537                 None => Err(SecurityStateError::from("invalid CRLite filter")),
538             }
539         })
540         .map_err(|_| SecurityStateError::from("unable to initialize CRLite filter"))?;
541 
542         let mut path = get_store_path(&self.profile_path)?;
543         path.push("crlite.coverage");
544         if !path.exists() {
545             return Ok(());
546         }
547 
548         // Deserialize the coverage metadata.
549         // The format is described in `set_full_crlite_filter`.
550         let coverage_file = File::open(path)?;
551         let coverage_file_len = coverage_file.metadata()?.len() as usize;
552         let mut coverage_reader = BufReader::new(coverage_file);
553         match coverage_reader.read_u8() {
554             Ok(COVERAGE_SERIALIZATION_VERSION) => (),
555             _ => return Err(SecurityStateError::from("unknown CRLite coverage version")),
556         }
557         if (coverage_file_len - 1) % COVERAGE_V1_ENTRY_BYTES != 0 {
558             return Err(SecurityStateError::from("truncated CRLite coverage file"));
559         }
560         let coverage_count = (coverage_file_len - 1) / COVERAGE_V1_ENTRY_BYTES;
561         let mut crlite_coverage: HashMap<Vec<u8>, (u64, u64)> = HashMap::new();
562         for _ in 0..coverage_count {
563             let mut coverage_entry = [0u8; COVERAGE_V1_ENTRY_BYTES];
564             match coverage_reader.read_exact(&mut coverage_entry) {
565                 Ok(()) => (),
566                 _ => return Err(SecurityStateError::from("truncated CRLite coverage file")),
567             };
568             let log_id = &coverage_entry[0..32];
569             let min_timestamp: u64;
570             let max_timestamp: u64;
571             match (&coverage_entry[32..40]).read_u64::<LittleEndian>() {
572                 Ok(value) => min_timestamp = value,
573                 _ => return Err(SecurityStateError::from("truncated CRLite coverage file")),
574             }
575             match (&coverage_entry[40..48]).read_u64::<LittleEndian>() {
576                 Ok(value) => max_timestamp = value,
577                 _ => return Err(SecurityStateError::from("truncated CRLite coverage file")),
578             }
579             crlite_coverage.insert(log_id.to_vec(), (min_timestamp, max_timestamp));
580         }
581 
582         let mut path = get_store_path(&self.profile_path)?;
583         path.push("crlite.enrollment");
584         if !path.exists() {
585             return Ok(());
586         }
587 
588         // Deserialize the enrollment metadata.
589         // The format is described in `set_full_crlite_filter`.
590         let enrollment_file = File::open(path)?;
591         let enrollment_file_len = enrollment_file.metadata()?.len() as usize;
592         let mut enrollment_reader = BufReader::new(enrollment_file);
593         match enrollment_reader.read_u8() {
594             Ok(ENROLLMENT_SERIALIZATION_VERSION) => (),
595             _ => {
596                 return Err(SecurityStateError::from(
597                     "unknown CRLite enrollment version",
598                 ))
599             }
600         }
601         if (enrollment_file_len - 1) % ENROLLMENT_V1_ENTRY_BYTES != 0 {
602             return Err(SecurityStateError::from("truncated CRLite enrollment file"));
603         }
604         let enrollment_count = (enrollment_file_len - 1) / ENROLLMENT_V1_ENTRY_BYTES;
605         let mut crlite_enrollment: HashSet<Vec<u8>> = HashSet::new();
606         for _ in 0..enrollment_count {
607             let mut enrollment_entry = [0u8; ENROLLMENT_V1_ENTRY_BYTES];
608             match enrollment_reader.read_exact(&mut enrollment_entry) {
609                 Ok(()) => (),
610                 _ => return Err(SecurityStateError::from("truncated CRLite enrollment file")),
611             };
612             let issuer_id = &enrollment_entry[..];
613             crlite_enrollment.insert(issuer_id.to_vec());
614         }
615 
616         let old_crlite_filter_should_be_none = self.crlite_filter.replace(crlite_filter);
617         assert!(old_crlite_filter_should_be_none.is_none());
618         let old_crlite_coverage_should_be_none = self.crlite_coverage.replace(crlite_coverage);
619         assert!(old_crlite_coverage_should_be_none.is_none());
620         let old_crlite_enrollment_should_be_none =
621             self.crlite_enrollment.replace(crlite_enrollment);
622         assert!(old_crlite_enrollment_should_be_none.is_none());
623         Ok(())
624     }
625 
add_crlite_stash(&mut self, stash: Vec<u8>) -> Result<(), SecurityStateError>626     pub fn add_crlite_stash(&mut self, stash: Vec<u8>) -> Result<(), SecurityStateError> {
627         // Append the update to the previously-seen stashes.
628         let mut path = get_store_path(&self.profile_path)?;
629         path.push("crlite.stash");
630         let mut stash_file = OpenOptions::new().append(true).create(true).open(path)?;
631         stash_file.write_all(&stash)?;
632         let crlite_stash = self.crlite_stash.get_or_insert(HashMap::new());
633         load_crlite_stash_from_reader_into_map(&mut stash.as_slice(), crlite_stash)?;
634         Ok(())
635     }
636 
is_cert_revoked_by_stash( &self, issuer_spki: &[u8], serial: &[u8], ) -> Result<bool, SecurityStateError>637     pub fn is_cert_revoked_by_stash(
638         &self,
639         issuer_spki: &[u8],
640         serial: &[u8],
641     ) -> Result<bool, SecurityStateError> {
642         let crlite_stash = match self.crlite_stash.as_ref() {
643             Some(crlite_stash) => crlite_stash,
644             None => return Ok(false),
645         };
646         let mut digest = Sha256::default();
647         digest.input(issuer_spki);
648         let lookup_key = digest.result().as_slice().to_vec();
649         let serials = match crlite_stash.get(&lookup_key) {
650             Some(serials) => serials,
651             None => return Ok(false),
652         };
653         Ok(serials.contains(&serial.to_vec()))
654     }
655 
get_crlite_revocation_state( &self, issuer: &[u8], issuer_spki: &[u8], serial_number: &[u8], timestamps: &[CRLiteTimestamp], ) -> i16656     pub fn get_crlite_revocation_state(
657         &self,
658         issuer: &[u8],
659         issuer_spki: &[u8],
660         serial_number: &[u8],
661         timestamps: &[CRLiteTimestamp],
662     ) -> i16 {
663         if !self.issuer_is_enrolled(issuer, issuer_spki) {
664             return nsICertStorage::STATE_NOT_ENROLLED;
665         }
666         if !self.filter_covers_some_timestamp(timestamps) {
667             return nsICertStorage::STATE_NOT_COVERED;
668         }
669         let mut digest = Sha256::default();
670         digest.input(issuer_spki);
671         let mut lookup_key = digest.result().as_slice().to_vec();
672         lookup_key.extend_from_slice(serial_number);
673         debug!("CRLite lookup key: {:?}", lookup_key);
674         let result = match &self.crlite_filter {
675             Some(crlite_filter) => crlite_filter.rent(|filter| filter.has(&lookup_key)),
676             // This can only happen if the backing file was deleted or if it or our database has
677             // become corrupted. In any case, we have no information.
678             None => return nsICertStorage::STATE_NOT_COVERED,
679         };
680         match result {
681             true => nsICertStorage::STATE_ENFORCE,
682             false => nsICertStorage::STATE_UNSET,
683         }
684     }
685 
is_data_fresh( &self, update_pref: &str, allowed_staleness: &str, ) -> Result<bool, SecurityStateError>686     pub fn is_data_fresh(
687         &self,
688         update_pref: &str,
689         allowed_staleness: &str,
690     ) -> Result<bool, SecurityStateError> {
691         let checked = match self.int_prefs.get(update_pref) {
692             Some(ch) => *ch,
693             None => 0,
694         };
695         let staleness_seconds = match self.int_prefs.get(allowed_staleness) {
696             Some(st) => *st,
697             None => 0,
698         };
699 
700         let update = SystemTime::UNIX_EPOCH + Duration::new(checked as u64, 0);
701         let staleness = Duration::new(staleness_seconds as u64, 0);
702 
703         Ok(match SystemTime::now().duration_since(update) {
704             Ok(duration) => duration <= staleness,
705             Err(_) => false,
706         })
707     }
708 
is_blocklist_fresh(&self) -> Result<bool, SecurityStateError>709     pub fn is_blocklist_fresh(&self) -> Result<bool, SecurityStateError> {
710         self.is_data_fresh(
711             "services.settings.security.onecrl.checked",
712             "security.onecrl.maximum_staleness_in_seconds",
713         )
714     }
715 
pref_seen(&mut self, name: &str, value: u32)716     pub fn pref_seen(&mut self, name: &str, value: u32) {
717         self.int_prefs.insert(name.to_owned(), value);
718     }
719 
720     // To store certificates, we create a Cert out of each given cert, subject, and trust tuple. We
721     // hash each certificate with sha-256 to obtain a unique* key for that certificate, and we store
722     // the Cert in the database. We also look up or create a CertHashList for the given subject and
723     // add the new certificate's hash if it isn't present in the list. If it wasn't present, we
724     // write out the updated CertHashList.
725     // *By the pigeon-hole principle, there exist collisions for sha-256, so this key is not
726     // actually unique. We rely on the assumption that sha-256 is a cryptographically strong hash.
727     // If an adversary can find two different certificates with the same sha-256 hash, they can
728     // probably forge a sha-256-based signature, so assuming the keys we create here are unique is
729     // not a security issue.
add_certs( &mut self, certs: &[(nsCString, nsCString, i16)], ) -> Result<(), SecurityStateError>730     pub fn add_certs(
731         &mut self,
732         certs: &[(nsCString, nsCString, i16)],
733     ) -> Result<(), SecurityStateError> {
734         let env_and_store = match self.env_and_store.as_mut() {
735             Some(env_and_store) => env_and_store,
736             None => return Err(SecurityStateError::from("env and store not initialized?")),
737         };
738         let mut writer = env_and_store.env.write()?;
739         // Make a note that we have prior cert data now.
740         env_and_store.store.put(
741             &mut writer,
742             &make_key!(PREFIX_DATA_TYPE, &[nsICertStorage::DATA_TYPE_CERTIFICATE]),
743             &Value::Bool(true),
744         )?;
745 
746         for (cert_der_base64, subject_base64, trust) in certs {
747             let cert_der = match base64::decode(&cert_der_base64) {
748                 Ok(cert_der) => cert_der,
749                 Err(e) => {
750                     warn!("error base64-decoding cert - skipping: {}", e);
751                     continue;
752                 }
753             };
754             let subject = match base64::decode(&subject_base64) {
755                 Ok(subject) => subject,
756                 Err(e) => {
757                     warn!("error base64-decoding subject - skipping: {}", e);
758                     continue;
759                 }
760             };
761             let mut digest = Sha256::default();
762             digest.input(&cert_der);
763             let cert_hash = digest.result();
764             let cert_key = make_key!(PREFIX_CERT, &cert_hash);
765             let cert = Cert::new(&cert_der, &subject, *trust)?;
766             env_and_store
767                 .store
768                 .put(&mut writer, &cert_key, &Value::Blob(&cert.to_bytes()?))?;
769             let subject_key = make_key!(PREFIX_SUBJECT, &subject);
770             let empty_vec = Vec::new();
771             let old_cert_hash_list = match env_and_store.store.get(&writer, &subject_key)? {
772                 Some(Value::Blob(hashes)) => hashes.to_owned(),
773                 Some(_) => empty_vec,
774                 None => empty_vec,
775             };
776             let new_cert_hash_list = CertHashList::add(&old_cert_hash_list, &cert_hash)?;
777             if new_cert_hash_list.len() != old_cert_hash_list.len() {
778                 env_and_store.store.put(
779                     &mut writer,
780                     &subject_key,
781                     &Value::Blob(&new_cert_hash_list),
782                 )?;
783             }
784         }
785 
786         writer.commit()?;
787         Ok(())
788     }
789 
790     // Given a list of certificate sha-256 hashes, we can look up each Cert entry in the database.
791     // We use this to find the corresponding subject so we can look up the CertHashList it should
792     // appear in. If that list contains the given hash, we remove it and update the CertHashList.
793     // Finally we delete the Cert entry.
remove_certs_by_hashes( &mut self, hashes_base64: &[nsCString], ) -> Result<(), SecurityStateError>794     pub fn remove_certs_by_hashes(
795         &mut self,
796         hashes_base64: &[nsCString],
797     ) -> Result<(), SecurityStateError> {
798         let env_and_store = match self.env_and_store.as_mut() {
799             Some(env_and_store) => env_and_store,
800             None => return Err(SecurityStateError::from("env and store not initialized?")),
801         };
802         let mut writer = env_and_store.env.write()?;
803         let reader = env_and_store.env.read()?;
804 
805         for hash in hashes_base64 {
806             let hash = match base64::decode(&hash) {
807                 Ok(hash) => hash,
808                 Err(e) => {
809                     warn!("error decoding hash - ignoring: {}", e);
810                     continue;
811                 }
812             };
813             let cert_key = make_key!(PREFIX_CERT, &hash);
814             if let Some(Value::Blob(cert_bytes)) = env_and_store.store.get(&reader, &cert_key)? {
815                 if let Ok(cert) = Cert::from_bytes(cert_bytes) {
816                     let subject_key = make_key!(PREFIX_SUBJECT, &cert.subject);
817                     let empty_vec = Vec::new();
818                     // We have to use the writer here to make sure we have an up-to-date view of
819                     // the cert hash list.
820                     let old_cert_hash_list = match env_and_store.store.get(&writer, &subject_key)? {
821                         Some(Value::Blob(hashes)) => hashes.to_owned(),
822                         Some(_) => empty_vec,
823                         None => empty_vec,
824                     };
825                     let new_cert_hash_list = CertHashList::remove(&old_cert_hash_list, &hash)?;
826                     if new_cert_hash_list.len() != old_cert_hash_list.len() {
827                         env_and_store.store.put(
828                             &mut writer,
829                             &subject_key,
830                             &Value::Blob(&new_cert_hash_list),
831                         )?;
832                     }
833                 }
834             }
835             match env_and_store.store.delete(&mut writer, &cert_key) {
836                 Ok(()) => {}
837                 Err(StoreError::KeyValuePairNotFound) => {}
838                 Err(e) => return Err(SecurityStateError::from(e)),
839             };
840         }
841         writer.commit()?;
842         Ok(())
843     }
844 
845     // Given a certificate's subject, we look up the corresponding CertHashList. In theory, each
846     // hash in that list corresponds to a certificate with the given subject, so we look up each of
847     // these (assuming the database is consistent and contains them) and add them to the given list.
848     // If we encounter an inconsistency, we continue looking as best we can.
find_certs_by_subject( &self, subject: &[u8], certs: &mut ThinVec<ThinVec<u8>>, ) -> Result<(), SecurityStateError>849     pub fn find_certs_by_subject(
850         &self,
851         subject: &[u8],
852         certs: &mut ThinVec<ThinVec<u8>>,
853     ) -> Result<(), SecurityStateError> {
854         let env_and_store = match self.env_and_store.as_ref() {
855             Some(env_and_store) => env_and_store,
856             None => return Err(SecurityStateError::from("env and store not initialized?")),
857         };
858         let reader = env_and_store.env.read()?;
859         certs.clear();
860         let subject_key = make_key!(PREFIX_SUBJECT, subject);
861         let empty_vec = Vec::new();
862         let cert_hash_list_bytes = match env_and_store.store.get(&reader, &subject_key)? {
863             Some(Value::Blob(hashes)) => hashes,
864             Some(_) => &empty_vec,
865             None => &empty_vec,
866         };
867         let cert_hash_list = CertHashList::new(cert_hash_list_bytes)?;
868         for cert_hash in cert_hash_list.into_iter() {
869             let cert_key = make_key!(PREFIX_CERT, cert_hash);
870             // If there's some inconsistency, we don't want to fail the whole operation - just go
871             // for best effort and find as many certificates as we can.
872             if let Some(Value::Blob(cert_bytes)) = env_and_store.store.get(&reader, &cert_key)? {
873                 if let Ok(cert) = Cert::from_bytes(cert_bytes) {
874                     let mut thin_vec_cert = ThinVec::with_capacity(cert.der.len());
875                     thin_vec_cert.extend_from_slice(&cert.der);
876                     certs.push(thin_vec_cert);
877                 }
878             }
879         }
880         Ok(())
881     }
882 }
883 
884 const CERT_SERIALIZATION_VERSION_1: u8 = 1;
885 
886 // A Cert consists of its DER encoding, its DER-encoded subject, and its trust (currently
887 // nsICertStorage::TRUST_INHERIT, but in the future nsICertStorage::TRUST_ANCHOR may also be used).
888 // The length of each encoding must be representable by a u16 (so 65535 bytes is the longest a
889 // certificate can be).
890 struct Cert<'a> {
891     der: &'a [u8],
892     subject: &'a [u8],
893     trust: i16,
894 }
895 
896 impl<'a> Cert<'a> {
new(der: &'a [u8], subject: &'a [u8], trust: i16) -> Result<Cert<'a>, SecurityStateError>897     fn new(der: &'a [u8], subject: &'a [u8], trust: i16) -> Result<Cert<'a>, SecurityStateError> {
898         if der.len() > u16::max as usize {
899             return Err(SecurityStateError::from("certificate is too long"));
900         }
901         if subject.len() > u16::max as usize {
902             return Err(SecurityStateError::from("subject is too long"));
903         }
904         Ok(Cert {
905             der,
906             subject,
907             trust,
908         })
909     }
910 
from_bytes(encoded: &'a [u8]) -> Result<Cert<'a>, SecurityStateError>911     fn from_bytes(encoded: &'a [u8]) -> Result<Cert<'a>, SecurityStateError> {
912         if encoded.len() < size_of::<u8>() {
913             return Err(SecurityStateError::from("invalid Cert: no version?"));
914         }
915         let (mut version, rest) = encoded.split_at(size_of::<u8>());
916         let version = version.read_u8()?;
917         if version != CERT_SERIALIZATION_VERSION_1 {
918             return Err(SecurityStateError::from("invalid Cert: unexpected version"));
919         }
920 
921         if rest.len() < size_of::<u16>() {
922             return Err(SecurityStateError::from("invalid Cert: no der len?"));
923         }
924         let (mut der_len, rest) = rest.split_at(size_of::<u16>());
925         let der_len = der_len.read_u16::<NetworkEndian>()? as usize;
926         if rest.len() < der_len {
927             return Err(SecurityStateError::from("invalid Cert: no der?"));
928         }
929         let (der, rest) = rest.split_at(der_len);
930 
931         if rest.len() < size_of::<u16>() {
932             return Err(SecurityStateError::from("invalid Cert: no subject len?"));
933         }
934         let (mut subject_len, rest) = rest.split_at(size_of::<u16>());
935         let subject_len = subject_len.read_u16::<NetworkEndian>()? as usize;
936         if rest.len() < subject_len {
937             return Err(SecurityStateError::from("invalid Cert: no subject?"));
938         }
939         let (subject, mut rest) = rest.split_at(subject_len);
940 
941         if rest.len() < size_of::<i16>() {
942             return Err(SecurityStateError::from("invalid Cert: no trust?"));
943         }
944         let trust = rest.read_i16::<NetworkEndian>()?;
945         if rest.len() > 0 {
946             return Err(SecurityStateError::from("invalid Cert: trailing data?"));
947         }
948 
949         Ok(Cert {
950             der,
951             subject,
952             trust,
953         })
954     }
955 
to_bytes(&self) -> Result<Vec<u8>, SecurityStateError>956     fn to_bytes(&self) -> Result<Vec<u8>, SecurityStateError> {
957         let mut bytes = Vec::with_capacity(
958             size_of::<u8>()
959                 + size_of::<u16>()
960                 + self.der.len()
961                 + size_of::<u16>()
962                 + self.subject.len()
963                 + size_of::<i16>(),
964         );
965         bytes.write_u8(CERT_SERIALIZATION_VERSION_1)?;
966         if self.der.len() > u16::max as usize {
967             return Err(SecurityStateError::from("certificate is too long"));
968         }
969         bytes.write_u16::<NetworkEndian>(self.der.len() as u16)?;
970         bytes.extend_from_slice(&self.der);
971         if self.subject.len() > u16::max as usize {
972             return Err(SecurityStateError::from("subject is too long"));
973         }
974         bytes.write_u16::<NetworkEndian>(self.subject.len() as u16)?;
975         bytes.extend_from_slice(&self.subject);
976         bytes.write_i16::<NetworkEndian>(self.trust)?;
977         Ok(bytes)
978     }
979 }
980 
981 // A CertHashList is a list of sha-256 hashes of DER-encoded certificates.
982 struct CertHashList<'a> {
983     hashes: Vec<&'a [u8]>,
984 }
985 
986 impl<'a> CertHashList<'a> {
new(hashes_bytes: &'a [u8]) -> Result<CertHashList<'a>, SecurityStateError>987     fn new(hashes_bytes: &'a [u8]) -> Result<CertHashList<'a>, SecurityStateError> {
988         if hashes_bytes.len() % Sha256::output_size() != 0 {
989             return Err(SecurityStateError::from(
990                 "unexpected length for cert hash list",
991             ));
992         }
993         let mut hashes = Vec::with_capacity(hashes_bytes.len() / Sha256::output_size());
994         for hash in hashes_bytes.chunks_exact(Sha256::output_size()) {
995             hashes.push(hash);
996         }
997         Ok(CertHashList { hashes })
998     }
999 
add(hashes_bytes: &[u8], new_hash: &[u8]) -> Result<Vec<u8>, SecurityStateError>1000     fn add(hashes_bytes: &[u8], new_hash: &[u8]) -> Result<Vec<u8>, SecurityStateError> {
1001         if hashes_bytes.len() % Sha256::output_size() != 0 {
1002             return Err(SecurityStateError::from(
1003                 "unexpected length for cert hash list",
1004             ));
1005         }
1006         if new_hash.len() != Sha256::output_size() {
1007             return Err(SecurityStateError::from("unexpected cert hash length"));
1008         }
1009         for hash in hashes_bytes.chunks_exact(Sha256::output_size()) {
1010             if hash == new_hash {
1011                 return Ok(hashes_bytes.to_owned());
1012             }
1013         }
1014         let mut combined = hashes_bytes.to_owned();
1015         combined.extend_from_slice(new_hash);
1016         Ok(combined)
1017     }
1018 
remove(hashes_bytes: &[u8], cert_hash: &[u8]) -> Result<Vec<u8>, SecurityStateError>1019     fn remove(hashes_bytes: &[u8], cert_hash: &[u8]) -> Result<Vec<u8>, SecurityStateError> {
1020         if hashes_bytes.len() % Sha256::output_size() != 0 {
1021             return Err(SecurityStateError::from(
1022                 "unexpected length for cert hash list",
1023             ));
1024         }
1025         if cert_hash.len() != Sha256::output_size() {
1026             return Err(SecurityStateError::from("unexpected cert hash length"));
1027         }
1028         let mut result = Vec::with_capacity(hashes_bytes.len());
1029         for hash in hashes_bytes.chunks_exact(Sha256::output_size()) {
1030             if hash != cert_hash {
1031                 result.extend_from_slice(hash);
1032             }
1033         }
1034         Ok(result)
1035     }
1036 }
1037 
1038 impl<'a> IntoIterator for CertHashList<'a> {
1039     type Item = &'a [u8];
1040     type IntoIter = std::vec::IntoIter<&'a [u8]>;
1041 
into_iter(self) -> Self::IntoIter1042     fn into_iter(self) -> Self::IntoIter {
1043         self.hashes.into_iter()
1044     }
1045 }
1046 
1047 // Helper struct for get_crlite_revocation_state.
1048 struct CRLiteTimestamp {
1049     log_id: ThinVec<u8>,
1050     timestamp: u64,
1051 }
1052 
1053 // Helper struct for set_batch_state. Takes a prefix, two base64-encoded key
1054 // parts, and a security state value.
1055 struct EncodedSecurityState {
1056     prefix: &'static str,
1057     key_part_1_base64: nsCString,
1058     key_part_2_base64: nsCString,
1059     state: i16,
1060 }
1061 
1062 impl EncodedSecurityState {
new( prefix: &'static str, key_part_1_base64: nsCString, key_part_2_base64: nsCString, state: i16, ) -> EncodedSecurityState1063     fn new(
1064         prefix: &'static str,
1065         key_part_1_base64: nsCString,
1066         key_part_2_base64: nsCString,
1067         state: i16,
1068     ) -> EncodedSecurityState {
1069         EncodedSecurityState {
1070             prefix,
1071             key_part_1_base64,
1072             key_part_2_base64,
1073             state,
1074         }
1075     }
1076 
key(&self) -> Result<Vec<u8>, SecurityStateError>1077     fn key(&self) -> Result<Vec<u8>, SecurityStateError> {
1078         let key_part_1 = base64::decode(&self.key_part_1_base64)?;
1079         let key_part_2 = base64::decode(&self.key_part_2_base64)?;
1080         Ok(make_key!(self.prefix, &key_part_1, &key_part_2))
1081     }
1082 
state(&self) -> i161083     fn state(&self) -> i16 {
1084         self.state
1085     }
1086 }
1087 
get_path_from_directory_service(key: &str) -> Result<PathBuf, SecurityStateError>1088 fn get_path_from_directory_service(key: &str) -> Result<PathBuf, SecurityStateError> {
1089     let directory_service = match xpcom::services::get_DirectoryService() {
1090         Some(ds) => ds,
1091         _ => return Err(SecurityStateError::from("None")),
1092     };
1093 
1094     let cs_key = CString::new(key)?;
1095     let mut requested_dir = GetterAddrefs::<nsIFile>::new();
1096 
1097     unsafe {
1098         (*directory_service)
1099             .Get(
1100                 (&cs_key).as_ptr(),
1101                 &nsIFile::IID as *const nsIID,
1102                 requested_dir.void_ptr(),
1103             )
1104             .to_result()
1105             .map_err(|res| SecurityStateError {
1106                 message: (*res.error_name()).as_str_unchecked().to_owned(),
1107             })
1108     }?;
1109 
1110     let dir_path = match requested_dir.refptr() {
1111         None => return Err(SecurityStateError::from("directory service failure")),
1112         Some(refptr) => refptr,
1113     };
1114 
1115     let mut path = nsString::new();
1116 
1117     unsafe {
1118         (*dir_path)
1119             .GetPath(&mut path as &mut nsAString)
1120             // For reasons that aren't clear to me, NsresultExt does not
1121             // implement std::error::Error (or Debug / Display). This map_err
1122             // hack is a way to get an error with a useful message.
1123             .to_result()
1124             .map_err(|res| SecurityStateError {
1125                 message: (*res.error_name()).as_str_unchecked().to_owned(),
1126             })?;
1127     }
1128 
1129     Ok(PathBuf::from(format!("{}", path)))
1130 }
1131 
get_profile_path() -> Result<PathBuf, SecurityStateError>1132 fn get_profile_path() -> Result<PathBuf, SecurityStateError> {
1133     Ok(get_path_from_directory_service("ProfD")
1134         .or_else(|_| get_path_from_directory_service("TmpD"))?)
1135 }
1136 
get_store_path(profile_path: &PathBuf) -> Result<PathBuf, SecurityStateError>1137 fn get_store_path(profile_path: &PathBuf) -> Result<PathBuf, SecurityStateError> {
1138     let mut store_path = profile_path.clone();
1139     store_path.push("security_state");
1140     create_dir_all(store_path.as_path())?;
1141     Ok(store_path)
1142 }
1143 
make_env(path: &Path) -> Result<Rkv, SecurityStateError>1144 fn make_env(path: &Path) -> Result<Rkv, SecurityStateError> {
1145     let mut builder = Rkv::environment_builder::<SafeMode>();
1146     builder.set_max_dbs(2);
1147 
1148     // 16MB is a little over twice the size of the current dataset. When we
1149     // eventually switch to the LMDB backend to create the builder above,
1150     // we should set this as the map size, since it cannot currently resize.
1151     // (The SafeMode backend warns when a map size is specified, so we skip it
1152     // for now to avoid console spam.)
1153 
1154     // builder.set_map_size(16777216);
1155 
1156     // Bug 1595004: Migrate databases between backends in the future,
1157     // and handle 32 and 64 bit architectures in case of LMDB.
1158     Rkv::from_builder(path, builder).map_err(SecurityStateError::from)
1159 }
1160 
unconditionally_remove_file(path: &Path) -> Result<(), SecurityStateError>1161 fn unconditionally_remove_file(path: &Path) -> Result<(), SecurityStateError> {
1162     match remove_file(path) {
1163         Ok(()) => Ok(()),
1164         Err(e) => match e.kind() {
1165             std::io::ErrorKind::NotFound => Ok(()),
1166             _ => Err(SecurityStateError::from(e)),
1167         },
1168     }
1169 }
1170 
remove_db(path: &Path) -> Result<(), SecurityStateError>1171 fn remove_db(path: &Path) -> Result<(), SecurityStateError> {
1172     // Remove LMDB-related files.
1173     let db = path.join("data.mdb");
1174     unconditionally_remove_file(&db)?;
1175     let lock = path.join("lock.mdb");
1176     unconditionally_remove_file(&lock)?;
1177 
1178     // Remove SafeMode-related files.
1179     let db = path.join("data.safe.bin");
1180     unconditionally_remove_file(&db)?;
1181 
1182     Ok(())
1183 }
1184 
1185 // Helper function to read stash information from the given reader and insert the results into the
1186 // given stash map.
load_crlite_stash_from_reader_into_map( reader: &mut dyn Read, dest: &mut HashMap<Vec<u8>, HashSet<Vec<u8>>>, ) -> Result<(), SecurityStateError>1187 fn load_crlite_stash_from_reader_into_map(
1188     reader: &mut dyn Read,
1189     dest: &mut HashMap<Vec<u8>, HashSet<Vec<u8>>>,
1190 ) -> Result<(), SecurityStateError> {
1191     // The basic unit of the stash file is an issuer subject public key info
1192     // hash (sha-256) followed by a number of serial numbers corresponding
1193     // to revoked certificates issued by that issuer. More specifically,
1194     // each unit consists of:
1195     //   4 bytes little-endian: the number of serial numbers following the issuer spki hash
1196     //   1 byte: the length of the issuer spki hash
1197     //   issuer spki hash length bytes: the issuer spki hash
1198     //   as many times as the indicated serial numbers:
1199     //     1 byte: the length of the serial number
1200     //     serial number length bytes: the serial number
1201     // The stash file consists of any number of these units concatenated
1202     // together.
1203     loop {
1204         let num_serials = match reader.read_u32::<LittleEndian>() {
1205             Ok(num_serials) => num_serials,
1206             Err(_) => break, // end of input, presumably
1207         };
1208         let issuer_spki_hash_len = reader.read_u8().map_err(|e| {
1209             SecurityStateError::from(format!("error reading stash issuer_spki_hash_len: {}", e))
1210         })?;
1211         let mut issuer_spki_hash = vec![0; issuer_spki_hash_len as usize];
1212         reader.read_exact(&mut issuer_spki_hash).map_err(|e| {
1213             SecurityStateError::from(format!("error reading stash issuer_spki_hash: {}", e))
1214         })?;
1215         let serials = dest.entry(issuer_spki_hash).or_insert(HashSet::new());
1216         for _ in 0..num_serials {
1217             let serial_len = reader.read_u8().map_err(|e| {
1218                 SecurityStateError::from(format!("error reading stash serial_len: {}", e))
1219             })?;
1220             let mut serial = vec![0; serial_len as usize];
1221             reader.read_exact(&mut serial).map_err(|e| {
1222                 SecurityStateError::from(format!("error reading stash serial: {}", e))
1223             })?;
1224             let _ = serials.insert(serial);
1225         }
1226     }
1227     Ok(())
1228 }
1229 
1230 // This is a helper struct that implements the task that asynchronously reads the CRLite stash on a
1231 // background thread.
1232 struct BackgroundReadStashTask {
1233     profile_path: PathBuf,
1234     security_state: Arc<RwLock<SecurityState>>,
1235 }
1236 
1237 impl BackgroundReadStashTask {
new( profile_path: PathBuf, security_state: &Arc<RwLock<SecurityState>>, ) -> BackgroundReadStashTask1238     fn new(
1239         profile_path: PathBuf,
1240         security_state: &Arc<RwLock<SecurityState>>,
1241     ) -> BackgroundReadStashTask {
1242         BackgroundReadStashTask {
1243             profile_path,
1244             security_state: Arc::clone(security_state),
1245         }
1246     }
1247 }
1248 
1249 impl Task for BackgroundReadStashTask {
run(&self)1250     fn run(&self) {
1251         let mut path = match get_store_path(&self.profile_path) {
1252             Ok(path) => path,
1253             Err(e) => {
1254                 error!("error getting security_state path: {}", e.message);
1255                 return;
1256             }
1257         };
1258         path.push("crlite.stash");
1259         // Before we've downloaded any stashes, this file won't exist.
1260         if !path.exists() {
1261             return;
1262         }
1263         let stash_file = match File::open(path) {
1264             Ok(file) => file,
1265             Err(e) => {
1266                 error!("error opening stash file: {}", e);
1267                 return;
1268             }
1269         };
1270         let mut stash_reader = BufReader::new(stash_file);
1271         let mut crlite_stash = HashMap::new();
1272         match load_crlite_stash_from_reader_into_map(&mut stash_reader, &mut crlite_stash) {
1273             Ok(()) => {}
1274             Err(e) => {
1275                 error!("error loading crlite stash: {}", e.message);
1276                 return;
1277             }
1278         }
1279         let mut ss = match self.security_state.write() {
1280             Ok(ss) => ss,
1281             Err(_) => return,
1282         };
1283         match ss.crlite_stash.replace(crlite_stash) {
1284             Some(_) => {
1285                 error!("replacing existing crlite stash when reading for the first time?");
1286                 return;
1287             }
1288             None => {}
1289         }
1290     }
1291 
done(&self) -> Result<(), nsresult>1292     fn done(&self) -> Result<(), nsresult> {
1293         Ok(())
1294     }
1295 }
1296 
do_construct_cert_storage( _outer: *const nsISupports, iid: *const xpcom::nsIID, result: *mut *mut xpcom::reexports::libc::c_void, ) -> Result<(), SecurityStateError>1297 fn do_construct_cert_storage(
1298     _outer: *const nsISupports,
1299     iid: *const xpcom::nsIID,
1300     result: *mut *mut xpcom::reexports::libc::c_void,
1301 ) -> Result<(), SecurityStateError> {
1302     let path_buf = get_profile_path()?;
1303 
1304     let security_state = Arc::new(RwLock::new(SecurityState::new(path_buf.clone())?));
1305     let cert_storage = CertStorage::allocate(InitCertStorage {
1306         security_state: security_state.clone(),
1307         queue: create_background_task_queue(cstr!("cert_storage"))?,
1308     });
1309     let memory_reporter = MemoryReporter::allocate(InitMemoryReporter { security_state });
1310 
1311     // Dispatch a task to the background task queue to asynchronously read the CRLite stash file (if
1312     // present) and load it into cert_storage. This task does not hold the
1313     // cert_storage.security_state mutex for the majority of its operation, which allows certificate
1314     // verification threads to query cert_storage without blocking. This is important for
1315     // performance, but it means that certificate verifications that happen before the task has
1316     // completed will not have stash information, and thus may not know of revocations that have
1317     // occurred since the last full CRLite filter was downloaded. As long as the last full filter
1318     // was downloaded no more than 10 days ago, this is no worse than relying on OCSP responses,
1319     // which have a maximum validity of 10 days.
1320     // NB: because the background task queue is serial, this task will complete before other tasks
1321     // later dispatched to the queue run. This means that other tasks that interact with the stash
1322     // will do so with the correct set of preconditions.
1323     let load_crlite_stash_task = Box::new(BackgroundReadStashTask::new(
1324         path_buf,
1325         &cert_storage.security_state,
1326     ));
1327     let runnable = TaskRunnable::new("LoadCrliteStash", load_crlite_stash_task)?;
1328     TaskRunnable::dispatch(runnable, cert_storage.queue.coerce())?;
1329 
1330     unsafe {
1331         cert_storage
1332             .QueryInterface(iid, result)
1333             // As above; greasy hack because NsresultExt
1334             .to_result()
1335             .map_err(|res| SecurityStateError {
1336                 message: (*res.error_name()).as_str_unchecked().to_owned(),
1337             })?;
1338 
1339         if let Some(reporter) = memory_reporter.query_interface::<nsIMemoryReporter>() {
1340             if let Some(reporter_manager) = xpcom::get_service::<nsIMemoryReporterManager>(cstr!(
1341                 "@mozilla.org/memory-reporter-manager;1"
1342             )) {
1343                 reporter_manager.RegisterStrongReporter(&*reporter);
1344             }
1345         }
1346 
1347         return cert_storage.setup_prefs();
1348     };
1349 }
1350 
read_int_pref(name: &str) -> Result<u32, SecurityStateError>1351 fn read_int_pref(name: &str) -> Result<u32, SecurityStateError> {
1352     let pref_service = match xpcom::services::get_PrefService() {
1353         Some(ps) => ps,
1354         _ => {
1355             return Err(SecurityStateError::from(
1356                 "could not get preferences service",
1357             ));
1358         }
1359     };
1360 
1361     let prefs: RefPtr<nsIPrefBranch> = match (*pref_service).query_interface() {
1362         Some(pb) => pb,
1363         _ => return Err(SecurityStateError::from("could not QI to nsIPrefBranch")),
1364     };
1365     let pref_name = match CString::new(name) {
1366         Ok(n) => n,
1367         _ => return Err(SecurityStateError::from("could not build pref name string")),
1368     };
1369 
1370     let mut pref_value: i32 = 0;
1371     // We can't use GetIntPrefWithDefault because optional_argc is not
1372     // supported. No matter, we can just check for failure and ignore
1373     // any NS_ERROR_UNEXPECTED result.
1374     let res = unsafe { (*prefs).GetIntPref((&pref_name).as_ptr(), (&mut pref_value) as *mut i32) };
1375     let pref_value = match res {
1376         NS_OK => pref_value,
1377         NS_ERROR_UNEXPECTED => 0,
1378         _ => return Err(SecurityStateError::from("could not read pref")),
1379     };
1380     if pref_value < 0 {
1381         Ok(0)
1382     } else {
1383         Ok(pref_value as u32)
1384     }
1385 }
1386 
1387 // This is a helper for creating a task that will perform a specific action on a background thread.
1388 struct SecurityStateTask<
1389     T: Default + VariantType,
1390     F: FnOnce(&mut SecurityState) -> Result<T, SecurityStateError>,
1391 > {
1392     callback: AtomicCell<Option<ThreadBoundRefPtr<nsICertStorageCallback>>>,
1393     security_state: Arc<RwLock<SecurityState>>,
1394     result: AtomicCell<(nserror::nsresult, T)>,
1395     task_action: AtomicCell<Option<F>>,
1396 }
1397 
1398 impl<T: Default + VariantType, F: FnOnce(&mut SecurityState) -> Result<T, SecurityStateError>>
1399     SecurityStateTask<T, F>
1400 {
new( callback: &nsICertStorageCallback, security_state: &Arc<RwLock<SecurityState>>, task_action: F, ) -> Result<SecurityStateTask<T, F>, nsresult>1401     fn new(
1402         callback: &nsICertStorageCallback,
1403         security_state: &Arc<RwLock<SecurityState>>,
1404         task_action: F,
1405     ) -> Result<SecurityStateTask<T, F>, nsresult> {
1406         let mut ss = security_state.write().or(Err(NS_ERROR_FAILURE))?;
1407         ss.remaining_ops = ss.remaining_ops.wrapping_add(1);
1408 
1409         Ok(SecurityStateTask {
1410             callback: AtomicCell::new(Some(ThreadBoundRefPtr::new(RefPtr::new(callback)))),
1411             security_state: Arc::clone(security_state),
1412             result: AtomicCell::new((NS_ERROR_FAILURE, T::default())),
1413             task_action: AtomicCell::new(Some(task_action)),
1414         })
1415     }
1416 }
1417 
1418 impl<T: Default + VariantType, F: FnOnce(&mut SecurityState) -> Result<T, SecurityStateError>> Task
1419     for SecurityStateTask<T, F>
1420 {
run(&self)1421     fn run(&self) {
1422         let mut ss = match self.security_state.write() {
1423             Ok(ss) => ss,
1424             Err(_) => return,
1425         };
1426         // this is a no-op if the DB is already open
1427         if ss.open_db().is_err() {
1428             return;
1429         }
1430         if let Some(task_action) = self.task_action.swap(None) {
1431             let rv = task_action(&mut ss)
1432                 .and_then(|v| Ok((NS_OK, v)))
1433                 .unwrap_or((NS_ERROR_FAILURE, T::default()));
1434             self.result.store(rv);
1435         }
1436     }
1437 
done(&self) -> Result<(), nsresult>1438     fn done(&self) -> Result<(), nsresult> {
1439         let threadbound = self.callback.swap(None).ok_or(NS_ERROR_FAILURE)?;
1440         let callback = threadbound.get_ref().ok_or(NS_ERROR_FAILURE)?;
1441         let result = self.result.swap((NS_ERROR_FAILURE, T::default()));
1442         let variant = result.1.into_variant();
1443         let nsrv = unsafe { callback.Done(result.0, &*variant) };
1444 
1445         let mut ss = self.security_state.write().or(Err(NS_ERROR_FAILURE))?;
1446         ss.remaining_ops = ss.remaining_ops.wrapping_sub(1);
1447 
1448         match nsrv {
1449             NS_OK => Ok(()),
1450             e => Err(e),
1451         }
1452     }
1453 }
1454 
1455 #[no_mangle]
cert_storage_constructor( outer: *const nsISupports, iid: *const xpcom::nsIID, result: *mut *mut xpcom::reexports::libc::c_void, ) -> nserror::nsresult1456 pub extern "C" fn cert_storage_constructor(
1457     outer: *const nsISupports,
1458     iid: *const xpcom::nsIID,
1459     result: *mut *mut xpcom::reexports::libc::c_void,
1460 ) -> nserror::nsresult {
1461     if !outer.is_null() {
1462         return NS_ERROR_NO_AGGREGATION;
1463     }
1464 
1465     if !is_main_thread() {
1466         return NS_ERROR_NOT_SAME_THREAD;
1467     }
1468 
1469     match do_construct_cert_storage(outer, iid, result) {
1470         Ok(_) => NS_OK,
1471         Err(_) => {
1472             // In future: log something so we know what went wrong?
1473             NS_ERROR_FAILURE
1474         }
1475     }
1476 }
1477 
1478 macro_rules! try_ns {
1479     ($e:expr) => {
1480         match $e {
1481             Ok(value) => value,
1482             Err(_) => return NS_ERROR_FAILURE,
1483         }
1484     };
1485     ($e:expr, or continue) => {
1486         match $e {
1487             Ok(value) => value,
1488             Err(err) => {
1489                 error!("{}", err);
1490                 continue;
1491             }
1492         }
1493     };
1494 }
1495 
1496 // This macro is a way to ensure the DB has been opened while minimizing lock acquisitions in the
1497 // common (read-only) case. First we acquire a read lock and see if we even need to open the DB. If
1498 // not, we can continue with the read lock we already have. Otherwise, we drop the read lock,
1499 // acquire the write lock, open the DB, drop the write lock, and re-acquire the read lock. While it
1500 // is possible for two or more threads to all come to the conclusion that they need to open the DB,
1501 // this isn't ultimately an issue - `open_db` will exit early if another thread has already done the
1502 // work.
1503 macro_rules! get_security_state {
1504     ($self:expr) => {{
1505         let ss_read_only = try_ns!($self.security_state.read());
1506         if !ss_read_only.db_needs_opening() {
1507             ss_read_only
1508         } else {
1509             drop(ss_read_only);
1510             {
1511                 let mut ss_write = try_ns!($self.security_state.write());
1512                 try_ns!(ss_write.open_db());
1513             }
1514             try_ns!($self.security_state.read())
1515         }
1516     }};
1517 }
1518 
1519 #[derive(xpcom)]
1520 #[xpimplements(nsICertStorage, nsIObserver)]
1521 #[refcnt = "atomic"]
1522 struct InitCertStorage {
1523     security_state: Arc<RwLock<SecurityState>>,
1524     queue: RefPtr<nsISerialEventTarget>,
1525 }
1526 
1527 /// CertStorage implements the nsICertStorage interface. The actual work is done by the
1528 /// SecurityState. To handle any threading issues, we have an atomic-refcounted read/write lock on
1529 /// the one and only SecurityState. So, only one thread can use SecurityState's &mut self functions
1530 /// at a time, while multiple threads can use &self functions simultaneously (as long as there are
1531 /// no threads using an &mut self function). The Arc is to allow for the creation of background
1532 /// tasks that use the SecurityState on the queue owned by CertStorage. This allows us to not block
1533 /// the main thread.
1534 #[allow(non_snake_case)]
1535 impl CertStorage {
setup_prefs(&self) -> Result<(), SecurityStateError>1536     unsafe fn setup_prefs(&self) -> Result<(), SecurityStateError> {
1537         let int_prefs = [
1538             "services.settings.security.onecrl.checked",
1539             "security.onecrl.maximum_staleness_in_seconds",
1540         ];
1541 
1542         // Fetch add observers for relevant prefs
1543         let pref_service = xpcom::services::get_PrefService().unwrap();
1544         let prefs: RefPtr<nsIPrefBranch> = match (*pref_service).query_interface() {
1545             Some(pb) => pb,
1546             _ => return Err(SecurityStateError::from("could not QI to nsIPrefBranch")),
1547         };
1548 
1549         for pref in int_prefs.iter() {
1550             let pref_nscstr = &nsCStr::from(pref.to_owned()) as &nsACString;
1551             let rv = (*prefs).AddObserverImpl(pref_nscstr, self.coerce::<nsIObserver>(), false);
1552             match read_int_pref(pref) {
1553                 Ok(up) => {
1554                     let mut ss = self.security_state.write()?;
1555                     // This doesn't use the DB, so no need to open it first. (Also since we do this
1556                     // upon initialization, it would defeat the purpose of lazily opening the DB.)
1557                     ss.pref_seen(pref, up);
1558                 }
1559                 Err(_) => return Err(SecurityStateError::from("could not read pref")),
1560             };
1561             assert!(rv.succeeded());
1562         }
1563 
1564         Ok(())
1565     }
1566 
HasPriorData( &self, data_type: u8, callback: *const nsICertStorageCallback, ) -> nserror::nsresult1567     unsafe fn HasPriorData(
1568         &self,
1569         data_type: u8,
1570         callback: *const nsICertStorageCallback,
1571     ) -> nserror::nsresult {
1572         if !is_main_thread() {
1573             return NS_ERROR_NOT_SAME_THREAD;
1574         }
1575         if callback.is_null() {
1576             return NS_ERROR_NULL_POINTER;
1577         }
1578         let task = Box::new(try_ns!(SecurityStateTask::new(
1579             &*callback,
1580             &self.security_state,
1581             move |ss| ss.get_has_prior_data(data_type),
1582         )));
1583         let runnable = try_ns!(TaskRunnable::new("HasPriorData", task));
1584         try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
1585         NS_OK
1586     }
1587 
GetRemainingOperationCount(&self, state: *mut i32) -> nserror::nsresult1588     unsafe fn GetRemainingOperationCount(&self, state: *mut i32) -> nserror::nsresult {
1589         if !is_main_thread() {
1590             return NS_ERROR_NOT_SAME_THREAD;
1591         }
1592         if state.is_null() {
1593             return NS_ERROR_NULL_POINTER;
1594         }
1595         let ss = try_ns!(self.security_state.read());
1596         *state = ss.remaining_ops;
1597         NS_OK
1598     }
1599 
SetRevocations( &self, revocations: *const ThinVec<RefPtr<nsIRevocationState>>, callback: *const nsICertStorageCallback, ) -> nserror::nsresult1600     unsafe fn SetRevocations(
1601         &self,
1602         revocations: *const ThinVec<RefPtr<nsIRevocationState>>,
1603         callback: *const nsICertStorageCallback,
1604     ) -> nserror::nsresult {
1605         if !is_main_thread() {
1606             return NS_ERROR_NOT_SAME_THREAD;
1607         }
1608         if revocations.is_null() || callback.is_null() {
1609             return NS_ERROR_NULL_POINTER;
1610         }
1611 
1612         let revocations = &*revocations;
1613         let mut entries = Vec::with_capacity(revocations.len());
1614 
1615         // By continuing when an nsIRevocationState attribute value is invalid,
1616         // we prevent errors relating to individual blocklist entries from
1617         // causing sync to fail. We will accumulate telemetry on these failures
1618         // in bug 1254099.
1619 
1620         for revocation in revocations {
1621             let mut state: i16 = 0;
1622             try_ns!(revocation.GetState(&mut state).to_result(), or continue);
1623 
1624             if let Some(revocation) =
1625                 (*revocation).query_interface::<nsIIssuerAndSerialRevocationState>()
1626             {
1627                 let mut issuer = nsCString::new();
1628                 try_ns!(revocation.GetIssuer(&mut *issuer).to_result(), or continue);
1629 
1630                 let mut serial = nsCString::new();
1631                 try_ns!(revocation.GetSerial(&mut *serial).to_result(), or continue);
1632 
1633                 entries.push(EncodedSecurityState::new(
1634                     PREFIX_REV_IS,
1635                     issuer,
1636                     serial,
1637                     state,
1638                 ));
1639             } else if let Some(revocation) =
1640                 (*revocation).query_interface::<nsISubjectAndPubKeyRevocationState>()
1641             {
1642                 let mut subject = nsCString::new();
1643                 try_ns!(revocation.GetSubject(&mut *subject).to_result(), or continue);
1644 
1645                 let mut pub_key_hash = nsCString::new();
1646                 try_ns!(revocation.GetPubKey(&mut *pub_key_hash).to_result(), or continue);
1647 
1648                 entries.push(EncodedSecurityState::new(
1649                     PREFIX_REV_SPK,
1650                     subject,
1651                     pub_key_hash,
1652                     state,
1653                 ));
1654             }
1655         }
1656 
1657         let task = Box::new(try_ns!(SecurityStateTask::new(
1658             &*callback,
1659             &self.security_state,
1660             move |ss| ss.set_batch_state(&entries, nsICertStorage::DATA_TYPE_REVOCATION),
1661         )));
1662         let runnable = try_ns!(TaskRunnable::new("SetRevocations", task));
1663         try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
1664         NS_OK
1665     }
1666 
GetRevocationState( &self, issuer: *const ThinVec<u8>, serial: *const ThinVec<u8>, subject: *const ThinVec<u8>, pub_key: *const ThinVec<u8>, state: *mut i16, ) -> nserror::nsresult1667     unsafe fn GetRevocationState(
1668         &self,
1669         issuer: *const ThinVec<u8>,
1670         serial: *const ThinVec<u8>,
1671         subject: *const ThinVec<u8>,
1672         pub_key: *const ThinVec<u8>,
1673         state: *mut i16,
1674     ) -> nserror::nsresult {
1675         // TODO (bug 1541212): We really want to restrict this to non-main-threads only, but we
1676         // can't do so until bug 1406854 is fixed.
1677         if issuer.is_null() || serial.is_null() || subject.is_null() || pub_key.is_null() {
1678             return NS_ERROR_NULL_POINTER;
1679         }
1680         *state = nsICertStorage::STATE_UNSET;
1681         let ss = get_security_state!(self);
1682         match ss.get_revocation_state(&*issuer, &*serial, &*subject, &*pub_key) {
1683             Ok(st) => {
1684                 *state = st;
1685                 NS_OK
1686             }
1687             _ => NS_ERROR_FAILURE,
1688         }
1689     }
1690 
IsBlocklistFresh(&self, fresh: *mut bool) -> nserror::nsresult1691     unsafe fn IsBlocklistFresh(&self, fresh: *mut bool) -> nserror::nsresult {
1692         *fresh = false;
1693         let ss = try_ns!(self.security_state.read());
1694         // This doesn't use the db -> don't need to make sure it's open.
1695         *fresh = match ss.is_blocklist_fresh() {
1696             Ok(is_fresh) => is_fresh,
1697             Err(_) => false,
1698         };
1699 
1700         NS_OK
1701     }
1702 
SetFullCRLiteFilter( &self, filter: *const ThinVec<u8>, enrolled_issuers: *const ThinVec<nsCString>, coverage: *const ThinVec<RefPtr<nsICRLiteCoverage>>, callback: *const nsICertStorageCallback, ) -> nserror::nsresult1703     unsafe fn SetFullCRLiteFilter(
1704         &self,
1705         filter: *const ThinVec<u8>,
1706         enrolled_issuers: *const ThinVec<nsCString>,
1707         coverage: *const ThinVec<RefPtr<nsICRLiteCoverage>>,
1708         callback: *const nsICertStorageCallback,
1709     ) -> nserror::nsresult {
1710         if !is_main_thread() {
1711             return NS_ERROR_NOT_SAME_THREAD;
1712         }
1713         if filter.is_null()
1714             || coverage.is_null()
1715             || callback.is_null()
1716             || enrolled_issuers.is_null()
1717         {
1718             return NS_ERROR_NULL_POINTER;
1719         }
1720 
1721         let filter_owned = (*filter).to_vec();
1722         let enrolled_issuers_owned = (*enrolled_issuers).to_vec();
1723 
1724         let coverage = &*coverage;
1725         let mut coverage_entries = Vec::with_capacity(coverage.len());
1726         for entry in coverage {
1727             let mut b64_log_id = nsCString::new();
1728             try_ns!((*entry).GetB64LogID(&mut *b64_log_id).to_result(), or continue);
1729             let mut min_timestamp: u64 = 0;
1730             try_ns!((*entry).GetMinTimestamp(&mut min_timestamp).to_result(), or continue);
1731             let mut max_timestamp: u64 = 0;
1732             try_ns!((*entry).GetMaxTimestamp(&mut max_timestamp).to_result(), or continue);
1733             coverage_entries.push((b64_log_id, min_timestamp, max_timestamp));
1734         }
1735 
1736         let task = Box::new(try_ns!(SecurityStateTask::new(
1737             &*callback,
1738             &self.security_state,
1739             move |ss| ss.set_full_crlite_filter(
1740                 filter_owned,
1741                 enrolled_issuers_owned,
1742                 &coverage_entries
1743             ),
1744         )));
1745         let runnable = try_ns!(TaskRunnable::new("SetFullCRLiteFilter", task));
1746         try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
1747         NS_OK
1748     }
1749 
AddCRLiteStash( &self, stash: *const ThinVec<u8>, callback: *const nsICertStorageCallback, ) -> nserror::nsresult1750     unsafe fn AddCRLiteStash(
1751         &self,
1752         stash: *const ThinVec<u8>,
1753         callback: *const nsICertStorageCallback,
1754     ) -> nserror::nsresult {
1755         if !is_main_thread() {
1756             return NS_ERROR_NOT_SAME_THREAD;
1757         }
1758         if stash.is_null() || callback.is_null() {
1759             return NS_ERROR_NULL_POINTER;
1760         }
1761         let stash_owned = (*stash).to_vec();
1762         let task = Box::new(try_ns!(SecurityStateTask::new(
1763             &*callback,
1764             &self.security_state,
1765             move |ss| ss.add_crlite_stash(stash_owned),
1766         )));
1767         let runnable = try_ns!(TaskRunnable::new("AddCRLiteStash", task));
1768         try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
1769         NS_OK
1770     }
1771 
IsCertRevokedByStash( &self, issuer_spki: *const ThinVec<u8>, serial_number: *const ThinVec<u8>, is_revoked: *mut bool, ) -> nserror::nsresult1772     unsafe fn IsCertRevokedByStash(
1773         &self,
1774         issuer_spki: *const ThinVec<u8>,
1775         serial_number: *const ThinVec<u8>,
1776         is_revoked: *mut bool,
1777     ) -> nserror::nsresult {
1778         if issuer_spki.is_null() || serial_number.is_null() || is_revoked.is_null() {
1779             return NS_ERROR_NULL_POINTER;
1780         }
1781         let ss = get_security_state!(self);
1782         *is_revoked = match ss.is_cert_revoked_by_stash(&*issuer_spki, &*serial_number) {
1783             Ok(is_revoked) => is_revoked,
1784             Err(_) => return NS_ERROR_FAILURE,
1785         };
1786         NS_OK
1787     }
1788 
GetCRLiteRevocationState( &self, issuer: *const ThinVec<u8>, issuerSPKI: *const ThinVec<u8>, serialNumber: *const ThinVec<u8>, timestamps: *const ThinVec<RefPtr<nsICRLiteTimestamp>>, state: *mut i16, ) -> nserror::nsresult1789     unsafe fn GetCRLiteRevocationState(
1790         &self,
1791         issuer: *const ThinVec<u8>,
1792         issuerSPKI: *const ThinVec<u8>,
1793         serialNumber: *const ThinVec<u8>,
1794         timestamps: *const ThinVec<RefPtr<nsICRLiteTimestamp>>,
1795         state: *mut i16,
1796     ) -> nserror::nsresult {
1797         // TODO (bug 1541212): We really want to restrict this to non-main-threads only, but we
1798         // can't do so until bug 1406854 is fixed.
1799         if issuer.is_null()
1800             || issuerSPKI.is_null()
1801             || serialNumber.is_null()
1802             || state.is_null()
1803             || timestamps.is_null()
1804         {
1805             return NS_ERROR_NULL_POINTER;
1806         }
1807         let timestamps = &*timestamps;
1808         let mut timestamp_entries = Vec::with_capacity(timestamps.len());
1809         for timestamp_entry in timestamps {
1810             let mut log_id = ThinVec::with_capacity(32);
1811             try_ns!(timestamp_entry.GetLogID(&mut log_id).to_result(), or continue);
1812             let mut timestamp: u64 = 0;
1813             try_ns!(timestamp_entry.GetTimestamp(&mut timestamp).to_result(), or continue);
1814             timestamp_entries.push(CRLiteTimestamp { log_id, timestamp });
1815         }
1816         let ss = get_security_state!(self);
1817         *state = ss.get_crlite_revocation_state(
1818             &*issuer,
1819             &*issuerSPKI,
1820             &*serialNumber,
1821             &timestamp_entries,
1822         );
1823         NS_OK
1824     }
1825 
AddCerts( &self, certs: *const ThinVec<RefPtr<nsICertInfo>>, callback: *const nsICertStorageCallback, ) -> nserror::nsresult1826     unsafe fn AddCerts(
1827         &self,
1828         certs: *const ThinVec<RefPtr<nsICertInfo>>,
1829         callback: *const nsICertStorageCallback,
1830     ) -> nserror::nsresult {
1831         if !is_main_thread() {
1832             return NS_ERROR_NOT_SAME_THREAD;
1833         }
1834         if certs.is_null() || callback.is_null() {
1835             return NS_ERROR_NULL_POINTER;
1836         }
1837         let certs = &*certs;
1838         let mut cert_entries = Vec::with_capacity(certs.len());
1839         for cert in certs {
1840             let mut der = nsCString::new();
1841             try_ns!((*cert).GetCert(&mut *der).to_result(), or continue);
1842             let mut subject = nsCString::new();
1843             try_ns!((*cert).GetSubject(&mut *subject).to_result(), or continue);
1844             let mut trust: i16 = 0;
1845             try_ns!((*cert).GetTrust(&mut trust).to_result(), or continue);
1846             cert_entries.push((der, subject, trust));
1847         }
1848         let task = Box::new(try_ns!(SecurityStateTask::new(
1849             &*callback,
1850             &self.security_state,
1851             move |ss| ss.add_certs(&cert_entries),
1852         )));
1853         let runnable = try_ns!(TaskRunnable::new("AddCerts", task));
1854         try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
1855         NS_OK
1856     }
1857 
RemoveCertsByHashes( &self, hashes: *const ThinVec<nsCString>, callback: *const nsICertStorageCallback, ) -> nserror::nsresult1858     unsafe fn RemoveCertsByHashes(
1859         &self,
1860         hashes: *const ThinVec<nsCString>,
1861         callback: *const nsICertStorageCallback,
1862     ) -> nserror::nsresult {
1863         if !is_main_thread() {
1864             return NS_ERROR_NOT_SAME_THREAD;
1865         }
1866         if hashes.is_null() || callback.is_null() {
1867             return NS_ERROR_NULL_POINTER;
1868         }
1869         let hashes = (*hashes).to_vec();
1870         let task = Box::new(try_ns!(SecurityStateTask::new(
1871             &*callback,
1872             &self.security_state,
1873             move |ss| ss.remove_certs_by_hashes(&hashes),
1874         )));
1875         let runnable = try_ns!(TaskRunnable::new("RemoveCertsByHashes", task));
1876         try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
1877         NS_OK
1878     }
1879 
FindCertsBySubject( &self, subject: *const ThinVec<u8>, certs: *mut ThinVec<ThinVec<u8>>, ) -> nserror::nsresult1880     unsafe fn FindCertsBySubject(
1881         &self,
1882         subject: *const ThinVec<u8>,
1883         certs: *mut ThinVec<ThinVec<u8>>,
1884     ) -> nserror::nsresult {
1885         // TODO (bug 1541212): We really want to restrict this to non-main-threads only, but we
1886         // can't do so until bug 1406854 is fixed.
1887         if subject.is_null() || certs.is_null() {
1888             return NS_ERROR_NULL_POINTER;
1889         }
1890         let ss = get_security_state!(self);
1891         match ss.find_certs_by_subject(&*subject, &mut *certs) {
1892             Ok(()) => NS_OK,
1893             Err(_) => NS_ERROR_FAILURE,
1894         }
1895     }
1896 
Observe( &self, subject: *const nsISupports, topic: *const c_char, pref_name: *const i16, ) -> nserror::nsresult1897     unsafe fn Observe(
1898         &self,
1899         subject: *const nsISupports,
1900         topic: *const c_char,
1901         pref_name: *const i16,
1902     ) -> nserror::nsresult {
1903         match CStr::from_ptr(topic).to_str() {
1904             Ok("nsPref:changed") => {
1905                 let prefs: RefPtr<nsIPrefBranch> = match (*subject).query_interface() {
1906                     Some(pb) => pb,
1907                     _ => return NS_ERROR_FAILURE,
1908                 };
1909 
1910                 // Convert our wstring pref_name to a cstring (via nsCString's
1911                 // utf16 to utf8 conversion)
1912                 let mut len: usize = 0;
1913                 while (*(pref_name.offset(len as isize))) != 0 {
1914                     len += 1;
1915                 }
1916                 let name_slice = slice::from_raw_parts(pref_name as *const u16, len);
1917                 let mut name_string = nsCString::new();
1918                 name_string.assign_utf16_to_utf8(name_slice);
1919 
1920                 let pref_name = match CString::new(name_string.as_str_unchecked()) {
1921                     Ok(n) => n,
1922                     _ => return NS_ERROR_FAILURE,
1923                 };
1924 
1925                 let mut pref_value: i32 = 0;
1926                 let res = prefs.GetIntPref((&pref_name).as_ptr(), (&mut pref_value) as *mut i32);
1927                 if !res.succeeded() {
1928                     return res;
1929                 }
1930                 let pref_value = if pref_value < 0 { 0 } else { pref_value as u32 };
1931 
1932                 let mut ss = try_ns!(self.security_state.write());
1933                 // This doesn't use the db -> don't need to make sure it's open.
1934                 ss.pref_seen(name_string.as_str_unchecked(), pref_value);
1935             }
1936             _ => (),
1937         }
1938         NS_OK
1939     }
1940 }
1941 
1942 extern "C" {
cert_storage_malloc_size_of(ptr: *const xpcom::reexports::libc::c_void) -> usize1943     fn cert_storage_malloc_size_of(ptr: *const xpcom::reexports::libc::c_void) -> usize;
1944 }
1945 
1946 #[derive(xpcom)]
1947 #[xpimplements(nsIMemoryReporter)]
1948 #[refcnt = "atomic"]
1949 struct InitMemoryReporter {
1950     security_state: Arc<RwLock<SecurityState>>,
1951 }
1952 
1953 #[allow(non_snake_case)]
1954 impl MemoryReporter {
CollectReports( &self, callback: *const nsIHandleReportCallback, data: *const nsISupports, _anonymize: bool, ) -> nserror::nsresult1955     unsafe fn CollectReports(
1956         &self,
1957         callback: *const nsIHandleReportCallback,
1958         data: *const nsISupports,
1959         _anonymize: bool,
1960     ) -> nserror::nsresult {
1961         let ss = try_ns!(self.security_state.read());
1962         let mut ops = MallocSizeOfOps::new(cert_storage_malloc_size_of, None);
1963         let size = ss.size_of(&mut ops);
1964         let callback = match RefPtr::from_raw(callback) {
1965             Some(ptr) => ptr,
1966             None => return NS_ERROR_UNEXPECTED,
1967         };
1968         // This does the same as MOZ_COLLECT_REPORT
1969         callback.Callback(
1970             &nsCStr::new() as &nsACString,
1971             &nsCStr::from("explicit/cert-storage/storage") as &nsACString,
1972             nsIMemoryReporter::KIND_HEAP,
1973             nsIMemoryReporter::UNITS_BYTES,
1974             size as i64,
1975             &nsCStr::from("Memory used by certificate storage") as &nsACString,
1976             data,
1977         );
1978         NS_OK
1979     }
1980 }
1981