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