tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

lib.rs (62474B)


      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 clubcard;
      8 extern crate clubcard_crlite;
      9 extern crate crossbeam_utils;
     10 #[macro_use]
     11 extern crate cstr;
     12 #[macro_use]
     13 extern crate log;
     14 extern crate moz_task;
     15 extern crate nserror;
     16 extern crate nsstring;
     17 extern crate rkv;
     18 extern crate sha2;
     19 extern crate thin_vec;
     20 extern crate time;
     21 #[macro_use]
     22 extern crate xpcom;
     23 extern crate storage_variant;
     24 extern crate tempfile;
     25 
     26 extern crate wr_malloc_size_of;
     27 use wr_malloc_size_of as malloc_size_of;
     28 
     29 use base64::prelude::*;
     30 use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
     31 use clubcard::{ApproximateSizeOf, Queryable};
     32 use clubcard_crlite::{CRLiteClubcard, CRLiteKey, CRLiteQuery, CRLiteStatus};
     33 use crossbeam_utils::atomic::AtomicCell;
     34 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
     35 use moz_task::{create_background_task_queue, is_main_thread, Task, TaskRunnable};
     36 use nserror::{
     37    nsresult, NS_ERROR_FAILURE, NS_ERROR_NOT_SAME_THREAD, NS_ERROR_NULL_POINTER,
     38    NS_ERROR_UNEXPECTED, NS_OK,
     39 };
     40 use nsstring::{nsACString, nsCStr, nsCString, nsString};
     41 use rkv::backend::{BackendEnvironmentBuilder, SafeMode, SafeModeDatabase, SafeModeEnvironment};
     42 use rkv::{StoreError, StoreOptions, Value};
     43 use sha2::{Digest, Sha256};
     44 use std::convert::TryInto;
     45 use std::ffi::{CString, OsString};
     46 use std::fmt::Display;
     47 use std::fs::{create_dir_all, remove_file, File};
     48 use std::io::{BufRead, BufReader};
     49 use std::mem::size_of;
     50 use std::path::{Path, PathBuf};
     51 use std::str;
     52 use std::sync::{Arc, RwLock};
     53 use std::time::{SystemTime, UNIX_EPOCH};
     54 use storage_variant::VariantType;
     55 use thin_vec::ThinVec;
     56 use xpcom::interfaces::{
     57    nsICRLiteTimestamp, nsICertInfo, nsICertStorage, nsICertStorageCallback, nsIFile,
     58    nsIHandleReportCallback, nsIIssuerAndSerialRevocationState, nsIMemoryReporter,
     59    nsIMemoryReporterManager, nsIProperties, nsIRevocationState, nsISerialEventTarget,
     60    nsISubjectAndPubKeyRevocationState, nsISupports,
     61 };
     62 use xpcom::{nsIID, GetterAddrefs, RefPtr, ThreadBoundRefPtr, XpCom};
     63 
     64 const PREFIX_REV_IS: &str = "is";
     65 const PREFIX_REV_SPK: &str = "spk";
     66 const PREFIX_SUBJECT: &str = "subject";
     67 const PREFIX_CERT: &str = "cert";
     68 const PREFIX_DATA_TYPE: &str = "datatype";
     69 
     70 const LAST_CRLITE_UPDATE_KEY: &str = "last_crlite_update";
     71 
     72 type Rkv = rkv::Rkv<SafeModeEnvironment>;
     73 type SingleStore = rkv::SingleStore<SafeModeDatabase>;
     74 
     75 macro_rules! make_key {
     76    ( $prefix:expr, $( $part:expr ),+ ) => {
     77        {
     78            let mut key = $prefix.as_bytes().to_owned();
     79            $( key.extend_from_slice($part); )+
     80            key
     81        }
     82    }
     83 }
     84 
     85 #[allow(non_camel_case_types, non_snake_case)]
     86 
     87 /// `SecurityStateError` is a type to represent errors in accessing or
     88 /// modifying security state.
     89 #[derive(Debug)]
     90 struct SecurityStateError {
     91    message: String,
     92 }
     93 
     94 impl<T: Display> From<T> for SecurityStateError {
     95    /// Creates a new instance of `SecurityStateError` from something that
     96    /// implements the `Display` trait.
     97    fn from(err: T) -> SecurityStateError {
     98        SecurityStateError {
     99            message: format!("{}", err),
    100        }
    101    }
    102 }
    103 
    104 struct EnvAndStore {
    105    env: Rkv,
    106    store: SingleStore,
    107 }
    108 
    109 impl MallocSizeOf for EnvAndStore {
    110    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
    111        self.env
    112            .read()
    113            .and_then(|reader| {
    114                let iter = self.store.iter_start(&reader)?.into_iter();
    115                Ok(iter
    116                    .map(|r| {
    117                        r.map(|(k, v)| k.len() + v.serialized_size().unwrap_or(0) as usize)
    118                            .unwrap_or(0)
    119                    })
    120                    .sum())
    121            })
    122            .unwrap_or(0)
    123    }
    124 }
    125 
    126 enum Filter {
    127    Clubcard(CRLiteClubcard),
    128 }
    129 
    130 impl Filter {
    131    fn load(
    132        store_path: &PathBuf,
    133        filter_filename: &OsString,
    134    ) -> Result<Option<Filter>, SecurityStateError> {
    135        // Before we've downloaded any filters, this file won't exist.
    136        let filter_path = store_path.join(filter_filename);
    137        if !filter_path.exists() {
    138            return Ok(None);
    139        }
    140        let filter_bytes = std::fs::read(&filter_path)?;
    141 
    142        if let Ok(clubcard) = CRLiteClubcard::from_bytes(&filter_bytes) {
    143            Ok(Some(Filter::Clubcard(clubcard)))
    144        } else {
    145            Err(SecurityStateError::from("invalid CRLite filter"))
    146        }
    147    }
    148 
    149    fn has(&self, clubcard_crlite_key: &CRLiteKey, timestamps: &[CRLiteTimestamp]) -> i16 {
    150        match self {
    151            Filter::Clubcard(clubcard) => {
    152                let timestamp_iter = timestamps
    153                    .iter()
    154                    .map(|timestamp| {
    155                        (&*timestamp.log_id) /* ThinVec -> &[u8; 32] */
    156                            .try_into()
    157                            .ok()
    158                            .map(|log_id| (log_id, timestamp.timestamp))
    159                    })
    160                    .flatten();
    161                let mut covered_timestamp_count = 0;
    162                for timestamp in timestamp_iter.clone() {
    163                    if CRLiteQuery::new(&clubcard_crlite_key, Some(timestamp))
    164                        .in_universe(clubcard.universe())
    165                    {
    166                        covered_timestamp_count += 1;
    167                    }
    168                }
    169                if covered_timestamp_count
    170                    < static_prefs::pref!("security.pki.crlite_timestamps_for_coverage")
    171                {
    172                    return nsICertStorage::STATE_NOT_COVERED;
    173                }
    174                match clubcard.contains(&clubcard_crlite_key, timestamp_iter) {
    175                    CRLiteStatus::Good => nsICertStorage::STATE_UNSET,
    176                    CRLiteStatus::NotCovered => nsICertStorage::STATE_NOT_COVERED,
    177                    CRLiteStatus::NotEnrolled => nsICertStorage::STATE_NOT_ENROLLED,
    178                    CRLiteStatus::Revoked => nsICertStorage::STATE_ENFORCE,
    179                }
    180            }
    181        }
    182    }
    183 }
    184 
    185 impl MallocSizeOf for Filter {
    186    fn size_of(&self, _: &mut MallocSizeOfOps) -> usize {
    187        match self {
    188            Filter::Clubcard(clubcard) => clubcard.approximate_size_of(),
    189        }
    190    }
    191 }
    192 
    193 /// `SecurityState`
    194 struct SecurityState {
    195    profile_path: PathBuf,
    196    env_and_store: Option<EnvAndStore>,
    197    crlite_filters: Vec<Filter>,
    198    /// Tracks the number of asynchronous operations which have been dispatched but not completed.
    199    remaining_ops: i32,
    200 }
    201 
    202 impl SecurityState {
    203    pub fn new(profile_path: PathBuf) -> SecurityState {
    204        // Since this gets called on the main thread, we don't actually want to open the DB yet.
    205        // We do this on-demand later, when we're probably on a certificate verification thread.
    206        SecurityState {
    207            profile_path,
    208            env_and_store: None,
    209            crlite_filters: vec![],
    210            remaining_ops: 0,
    211        }
    212    }
    213 
    214    pub fn db_needs_opening(&self) -> bool {
    215        self.env_and_store.is_none()
    216    }
    217 
    218    pub fn open_db(&mut self) -> Result<(), SecurityStateError> {
    219        if self.env_and_store.is_some() {
    220            return Ok(());
    221        }
    222 
    223        let store_path = get_store_path(&self.profile_path)?;
    224 
    225        // Open the store in read-write mode to create it (if needed) and migrate data from the old
    226        // store (if any).
    227        // If opening initially fails, try to remove and recreate the database. Consumers will
    228        // repopulate the database as necessary if this happens (see bug 1546361).
    229        let env = make_env(store_path.as_path()).or_else(|_| {
    230            remove_db(store_path.as_path())?;
    231            make_env(store_path.as_path())
    232        })?;
    233        let store = env.open_single("cert_storage", StoreOptions::create())?;
    234 
    235        // if the profile has a revocations.txt, migrate it and remove the file
    236        let mut revocations_path = self.profile_path.clone();
    237        revocations_path.push("revocations.txt");
    238        if revocations_path.exists() {
    239            SecurityState::migrate(&revocations_path, &env, &store)?;
    240            remove_file(revocations_path)?;
    241        }
    242 
    243        // We already returned early if env_and_store was Some, so this should take the None branch.
    244        match self.env_and_store.replace(EnvAndStore { env, store }) {
    245            Some(_) => Err(SecurityStateError::from(
    246                "env and store already initialized? (did we mess up our threading model?)",
    247            )),
    248            None => Ok(()),
    249        }?;
    250        self.load_crlite_filter()?;
    251        Ok(())
    252    }
    253 
    254    fn migrate(
    255        revocations_path: &PathBuf,
    256        env: &Rkv,
    257        store: &SingleStore,
    258    ) -> Result<(), SecurityStateError> {
    259        let f = File::open(revocations_path)?;
    260        let file = BufReader::new(f);
    261        let value = Value::I64(nsICertStorage::STATE_ENFORCE as i64);
    262        let mut writer = env.write()?;
    263 
    264        // Add the data from revocations.txt
    265        let mut dn: Option<Vec<u8>> = None;
    266        for line in file.lines() {
    267            let l = match line.map_err(|_| SecurityStateError::from("io error reading line data")) {
    268                Ok(data) => data,
    269                Err(e) => return Err(e),
    270            };
    271            if l.len() == 0 || l.starts_with("#") {
    272                continue;
    273            }
    274            let leading_char = match l.chars().next() {
    275                Some(c) => c,
    276                None => {
    277                    return Err(SecurityStateError::from(
    278                        "couldn't get char from non-empty str?",
    279                    ));
    280                }
    281            };
    282            // In future, we can maybe log migration failures. For now, ignore decoding and storage
    283            // errors and attempt to continue.
    284            // Check if we have a new DN
    285            if leading_char != '\t' && leading_char != ' ' {
    286                if let Ok(decoded_dn) = BASE64_STANDARD.decode(&l) {
    287                    dn = Some(decoded_dn);
    288                }
    289                continue;
    290            }
    291            let l_sans_prefix = match BASE64_STANDARD.decode(&l[1..]) {
    292                Ok(decoded) => decoded,
    293                Err(_) => continue,
    294            };
    295            if let Some(name) = &dn {
    296                if leading_char == '\t' {
    297                    let _ = store.put(
    298                        &mut writer,
    299                        &make_key!(PREFIX_REV_SPK, name, &l_sans_prefix),
    300                        &value,
    301                    );
    302                } else {
    303                    let _ = store.put(
    304                        &mut writer,
    305                        &make_key!(PREFIX_REV_IS, name, &l_sans_prefix),
    306                        &value,
    307                    );
    308                }
    309            }
    310        }
    311 
    312        writer.commit()?;
    313        Ok(())
    314    }
    315 
    316    fn read_entry(&self, key: &[u8]) -> Result<Option<i16>, SecurityStateError> {
    317        let env_and_store = match self.env_and_store.as_ref() {
    318            Some(env_and_store) => env_and_store,
    319            None => return Err(SecurityStateError::from("env and store not initialized?")),
    320        };
    321        let reader = env_and_store.env.read()?;
    322        match env_and_store.store.get(&reader, key) {
    323            Ok(Some(Value::I64(i))) => {
    324                Ok(Some(i.try_into().map_err(|_| {
    325                    SecurityStateError::from("Stored value out of range for i16")
    326                })?))
    327            }
    328            Ok(None) => Ok(None),
    329            Ok(_) => Err(SecurityStateError::from(
    330                "Unexpected type when trying to get a Value::I64",
    331            )),
    332            Err(_) => Err(SecurityStateError::from(
    333                "There was a problem getting the value",
    334            )),
    335        }
    336    }
    337 
    338    pub fn get_has_prior_data(&self, data_type: u8) -> Result<bool, SecurityStateError> {
    339        if data_type == nsICertStorage::DATA_TYPE_CRLITE_FILTER_FULL {
    340            return Ok(!self.crlite_filters.is_empty());
    341        }
    342        if data_type == nsICertStorage::DATA_TYPE_CRLITE_FILTER_INCREMENTAL {
    343            return Ok(self.crlite_filters.len() > 1);
    344        }
    345 
    346        let env_and_store = match self.env_and_store.as_ref() {
    347            Some(env_and_store) => env_and_store,
    348            None => return Err(SecurityStateError::from("env and store not initialized?")),
    349        };
    350        let reader = env_and_store.env.read()?;
    351        match env_and_store
    352            .store
    353            .get(&reader, &make_key!(PREFIX_DATA_TYPE, &[data_type]))
    354        {
    355            Ok(Some(Value::Bool(true))) => Ok(true),
    356            Ok(None) => Ok(false),
    357            Ok(_) => Err(SecurityStateError::from(
    358                "Unexpected type when trying to get a Value::Bool",
    359            )),
    360            Err(_) => Err(SecurityStateError::from(
    361                "There was a problem getting the value",
    362            )),
    363        }
    364    }
    365 
    366    pub fn set_batch_state(
    367        &mut self,
    368        entries: &[EncodedSecurityState],
    369        typ: u8,
    370    ) -> Result<(), SecurityStateError> {
    371        let env_and_store = match self.env_and_store.as_mut() {
    372            Some(env_and_store) => env_and_store,
    373            None => return Err(SecurityStateError::from("env and store not initialized?")),
    374        };
    375        let mut writer = env_and_store.env.write()?;
    376        // Make a note that we have prior data of the given type now.
    377        env_and_store.store.put(
    378            &mut writer,
    379            &make_key!(PREFIX_DATA_TYPE, &[typ]),
    380            &Value::Bool(true),
    381        )?;
    382 
    383        for entry in entries {
    384            let key = match entry.key() {
    385                Ok(key) => key,
    386                Err(e) => {
    387                    warn!("error base64-decoding key parts - ignoring: {}", e.message);
    388                    continue;
    389                }
    390            };
    391            env_and_store
    392                .store
    393                .put(&mut writer, &key, &Value::I64(entry.state() as i64))?;
    394        }
    395 
    396        writer.commit()?;
    397        Ok(())
    398    }
    399 
    400    pub fn get_revocation_state(
    401        &self,
    402        issuer: &[u8],
    403        serial: &[u8],
    404        subject: &[u8],
    405        pub_key: &[u8],
    406    ) -> Result<i16, SecurityStateError> {
    407        let mut digest = Sha256::default();
    408        digest.update(pub_key);
    409        let pub_key_hash = digest.finalize();
    410 
    411        let subject_pubkey = make_key!(PREFIX_REV_SPK, subject, &pub_key_hash);
    412        let issuer_serial = make_key!(PREFIX_REV_IS, issuer, serial);
    413 
    414        let st: i16 = match self.read_entry(&issuer_serial) {
    415            Ok(Some(value)) => value,
    416            Ok(None) => nsICertStorage::STATE_UNSET,
    417            Err(_) => {
    418                return Err(SecurityStateError::from(
    419                    "problem reading revocation state (from issuer / serial)",
    420                ));
    421            }
    422        };
    423 
    424        if st != nsICertStorage::STATE_UNSET {
    425            return Ok(st);
    426        }
    427 
    428        match self.read_entry(&subject_pubkey) {
    429            Ok(Some(value)) => Ok(value),
    430            Ok(None) => Ok(nsICertStorage::STATE_UNSET),
    431            Err(_) => {
    432                return Err(SecurityStateError::from(
    433                    "problem reading revocation state (from subject / pubkey)",
    434                ));
    435            }
    436        }
    437    }
    438 
    439    fn note_crlite_update_time(&mut self) -> Result<(), SecurityStateError> {
    440        let seconds_since_epoch = Value::U64(
    441            SystemTime::now()
    442                .duration_since(UNIX_EPOCH)
    443                .map_err(|_| SecurityStateError::from("could not get current time"))?
    444                .as_secs(),
    445        );
    446        let env_and_store = match self.env_and_store.as_mut() {
    447            Some(env_and_store) => env_and_store,
    448            None => return Err(SecurityStateError::from("env and store not initialized?")),
    449        };
    450        let mut writer = env_and_store.env.write()?;
    451        env_and_store
    452            .store
    453            .put(&mut writer, LAST_CRLITE_UPDATE_KEY, &seconds_since_epoch)
    454            .map_err(|_| SecurityStateError::from("could not store timestamp"))?;
    455        writer.commit()?;
    456        Ok(())
    457    }
    458 
    459    fn is_crlite_fresh(&self) -> bool {
    460        let now = match SystemTime::now().duration_since(UNIX_EPOCH) {
    461            Ok(t) => t.as_secs(),
    462            _ => return false,
    463        };
    464        let env_and_store = match self.env_and_store.as_ref() {
    465            Some(env_and_store) => env_and_store,
    466            None => return false,
    467        };
    468        let reader = match env_and_store.env.read() {
    469            Ok(reader) => reader,
    470            _ => return false,
    471        };
    472        match env_and_store.store.get(&reader, LAST_CRLITE_UPDATE_KEY) {
    473            Ok(Some(Value::U64(last_update))) if last_update < u64::MAX / 2 => {
    474                now < last_update + 60 * 60 * 24 * 10
    475            }
    476            _ => false,
    477        }
    478    }
    479 
    480    pub fn set_full_crlite_filter(&mut self, filter: Vec<u8>) -> Result<(), SecurityStateError> {
    481        let store_path = get_store_path(&self.profile_path)?;
    482 
    483        // Drop any existing crlite filters
    484        self.crlite_filters.clear();
    485 
    486        // Delete the backing data for the previous collection of filters. We may be migrating
    487        // from a bloom filter cascade, so check for and delete "coverage", "enrollment", and
    488        // "stash" files in addition to "delta" and "filter" files.
    489        for entry in std::fs::read_dir(&store_path)? {
    490            let Ok(entry) = entry else {
    491                continue;
    492            };
    493            let entry_path = entry.path();
    494            let extension = entry_path
    495                .extension()
    496                .map(|os_str| os_str.to_str())
    497                .flatten();
    498            if extension == Some("coverage")
    499                || extension == Some("delta")
    500                || extension == Some("enrollment")
    501                || extension == Some("filter")
    502                || extension == Some("stash")
    503            {
    504                let _ = std::fs::remove_file(entry_path);
    505            }
    506        }
    507 
    508        // Write the new full filter.
    509        std::fs::write(store_path.join("crlite.filter"), &filter)?;
    510 
    511        self.note_crlite_update_time()?;
    512        self.load_crlite_filter()?;
    513        Ok(())
    514    }
    515 
    516    fn load_crlite_filter(&mut self) -> Result<(), SecurityStateError> {
    517        if !self.crlite_filters.is_empty() {
    518            return Err(SecurityStateError::from(
    519                "crlite_filters should be empty here",
    520            ));
    521        }
    522 
    523        let store_path = get_store_path(&self.profile_path)?;
    524        let filter_filename = OsString::from("crlite.filter");
    525        if let Ok(Some(filter)) = Filter::load(&store_path, &filter_filename) {
    526            self.crlite_filters.push(filter);
    527        }
    528        Ok(())
    529    }
    530 
    531    pub fn add_crlite_delta(
    532        &mut self,
    533        delta: Vec<u8>,
    534        filename: String,
    535    ) -> Result<(), SecurityStateError> {
    536        let store_path = get_store_path(&self.profile_path)?;
    537        let delta_path = store_path.join(&filename);
    538        std::fs::write(&delta_path, &delta)?;
    539        if let Ok(Some(filter)) = Filter::load(&store_path, &filename.into()) {
    540            self.crlite_filters.push(filter);
    541        }
    542        self.note_crlite_update_time()?;
    543        Ok(())
    544    }
    545 
    546    pub fn get_crlite_revocation_state(
    547        &self,
    548        issuer_spki: &[u8],
    549        serial_number: &[u8],
    550        timestamps: &[CRLiteTimestamp],
    551    ) -> i16 {
    552        if !self.is_crlite_fresh() {
    553            return nsICertStorage::STATE_NO_FILTER;
    554        }
    555        if self.crlite_filters.is_empty() {
    556            // This can only happen if the backing file was deleted or if it or our database has
    557            // become corrupted. In any case, we have no information.
    558            return nsICertStorage::STATE_NO_FILTER;
    559        }
    560        let mut maybe_good = false;
    561        let mut covered = false;
    562 
    563        let issuer_spki_hash = Sha256::digest(issuer_spki);
    564        let clubcard_crlite_key = CRLiteKey::new(issuer_spki_hash.as_ref(), serial_number);
    565        for filter in &self.crlite_filters {
    566            match filter.has(&clubcard_crlite_key, timestamps) {
    567                nsICertStorage::STATE_ENFORCE => return nsICertStorage::STATE_ENFORCE,
    568                nsICertStorage::STATE_UNSET => maybe_good = true,
    569                nsICertStorage::STATE_NOT_ENROLLED => covered = true,
    570                _ => (),
    571            }
    572        }
    573        if maybe_good {
    574            return nsICertStorage::STATE_UNSET;
    575        }
    576        if covered {
    577            return nsICertStorage::STATE_NOT_ENROLLED;
    578        }
    579        nsICertStorage::STATE_NOT_COVERED
    580    }
    581 
    582    // To store certificates, we create a Cert out of each given cert, subject, and trust tuple. We
    583    // hash each certificate with sha-256 to obtain a unique* key for that certificate, and we store
    584    // the Cert in the database. We also look up or create a CertHashList for the given subject and
    585    // add the new certificate's hash if it isn't present in the list. If it wasn't present, we
    586    // write out the updated CertHashList.
    587    // *By the pigeon-hole principle, there exist collisions for sha-256, so this key is not
    588    // actually unique. We rely on the assumption that sha-256 is a cryptographically strong hash.
    589    // If an adversary can find two different certificates with the same sha-256 hash, they can
    590    // probably forge a sha-256-based signature, so assuming the keys we create here are unique is
    591    // not a security issue.
    592    pub fn add_certs(
    593        &mut self,
    594        certs: &[(nsCString, nsCString, i16)],
    595    ) -> Result<(), SecurityStateError> {
    596        let env_and_store = match self.env_and_store.as_mut() {
    597            Some(env_and_store) => env_and_store,
    598            None => return Err(SecurityStateError::from("env and store not initialized?")),
    599        };
    600        let mut writer = env_and_store.env.write()?;
    601        // Make a note that we have prior cert data now.
    602        env_and_store.store.put(
    603            &mut writer,
    604            &make_key!(PREFIX_DATA_TYPE, &[nsICertStorage::DATA_TYPE_CERTIFICATE]),
    605            &Value::Bool(true),
    606        )?;
    607 
    608        for (cert_der_base64, subject_base64, trust) in certs {
    609            let cert_der = match BASE64_STANDARD.decode(&cert_der_base64) {
    610                Ok(cert_der) => cert_der,
    611                Err(e) => {
    612                    warn!("error base64-decoding cert - skipping: {}", e);
    613                    continue;
    614                }
    615            };
    616            let subject = match BASE64_STANDARD.decode(&subject_base64) {
    617                Ok(subject) => subject,
    618                Err(e) => {
    619                    warn!("error base64-decoding subject - skipping: {}", e);
    620                    continue;
    621                }
    622            };
    623            let mut digest = Sha256::default();
    624            digest.update(&cert_der);
    625            let cert_hash = digest.finalize();
    626            let cert_key = make_key!(PREFIX_CERT, &cert_hash);
    627            let cert = Cert::new(&cert_der, &subject, *trust)?;
    628            env_and_store
    629                .store
    630                .put(&mut writer, &cert_key, &Value::Blob(&cert.to_bytes()?))?;
    631            let subject_key = make_key!(PREFIX_SUBJECT, &subject);
    632            let empty_vec = Vec::new();
    633            let old_cert_hash_list = match env_and_store.store.get(&writer, &subject_key)? {
    634                Some(Value::Blob(hashes)) => hashes.to_owned(),
    635                Some(_) => empty_vec,
    636                None => empty_vec,
    637            };
    638            let new_cert_hash_list = CertHashList::add(&old_cert_hash_list, &cert_hash)?;
    639            if new_cert_hash_list.len() != old_cert_hash_list.len() {
    640                env_and_store.store.put(
    641                    &mut writer,
    642                    &subject_key,
    643                    &Value::Blob(&new_cert_hash_list),
    644                )?;
    645            }
    646        }
    647 
    648        writer.commit()?;
    649        Ok(())
    650    }
    651 
    652    // Given a list of certificate sha-256 hashes, we can look up each Cert entry in the database.
    653    // We use this to find the corresponding subject so we can look up the CertHashList it should
    654    // appear in. If that list contains the given hash, we remove it and update the CertHashList.
    655    // Finally we delete the Cert entry.
    656    pub fn remove_certs_by_hashes(
    657        &mut self,
    658        hashes_base64: &[nsCString],
    659    ) -> Result<(), SecurityStateError> {
    660        let env_and_store = match self.env_and_store.as_mut() {
    661            Some(env_and_store) => env_and_store,
    662            None => return Err(SecurityStateError::from("env and store not initialized?")),
    663        };
    664        let mut writer = env_and_store.env.write()?;
    665        let reader = env_and_store.env.read()?;
    666 
    667        for hash in hashes_base64 {
    668            let hash = match BASE64_STANDARD.decode(&hash) {
    669                Ok(hash) => hash,
    670                Err(e) => {
    671                    warn!("error decoding hash - ignoring: {}", e);
    672                    continue;
    673                }
    674            };
    675            let cert_key = make_key!(PREFIX_CERT, &hash);
    676            if let Some(Value::Blob(cert_bytes)) = env_and_store.store.get(&reader, &cert_key)? {
    677                if let Ok(cert) = Cert::from_bytes(cert_bytes) {
    678                    let subject_key = make_key!(PREFIX_SUBJECT, &cert.subject);
    679                    let empty_vec = Vec::new();
    680                    // We have to use the writer here to make sure we have an up-to-date view of
    681                    // the cert hash list.
    682                    let old_cert_hash_list = match env_and_store.store.get(&writer, &subject_key)? {
    683                        Some(Value::Blob(hashes)) => hashes.to_owned(),
    684                        Some(_) => empty_vec,
    685                        None => empty_vec,
    686                    };
    687                    let new_cert_hash_list = CertHashList::remove(&old_cert_hash_list, &hash)?;
    688                    if new_cert_hash_list.len() != old_cert_hash_list.len() {
    689                        env_and_store.store.put(
    690                            &mut writer,
    691                            &subject_key,
    692                            &Value::Blob(&new_cert_hash_list),
    693                        )?;
    694                    }
    695                }
    696            }
    697            match env_and_store.store.delete(&mut writer, &cert_key) {
    698                Ok(()) => {}
    699                Err(StoreError::KeyValuePairNotFound) => {}
    700                Err(e) => return Err(SecurityStateError::from(e)),
    701            };
    702        }
    703        writer.commit()?;
    704        Ok(())
    705    }
    706 
    707    // Given a certificate's subject, we look up the corresponding CertHashList. In theory, each
    708    // hash in that list corresponds to a certificate with the given subject, so we look up each of
    709    // these (assuming the database is consistent and contains them) and add them to the given list.
    710    // If we encounter an inconsistency, we continue looking as best we can.
    711    pub fn find_certs_by_subject(
    712        &self,
    713        subject: &[u8],
    714        certs: &mut ThinVec<ThinVec<u8>>,
    715    ) -> Result<(), SecurityStateError> {
    716        let env_and_store = match self.env_and_store.as_ref() {
    717            Some(env_and_store) => env_and_store,
    718            None => return Err(SecurityStateError::from("env and store not initialized?")),
    719        };
    720        let reader = env_and_store.env.read()?;
    721        certs.clear();
    722        let subject_key = make_key!(PREFIX_SUBJECT, subject);
    723        let empty_vec = Vec::new();
    724        let cert_hash_list_bytes = match env_and_store.store.get(&reader, &subject_key)? {
    725            Some(Value::Blob(hashes)) => hashes,
    726            Some(_) => &empty_vec,
    727            None => &empty_vec,
    728        };
    729        let cert_hash_list = CertHashList::new(cert_hash_list_bytes)?;
    730        for cert_hash in cert_hash_list.into_iter() {
    731            let cert_key = make_key!(PREFIX_CERT, cert_hash);
    732            // If there's some inconsistency, we don't want to fail the whole operation - just go
    733            // for best effort and find as many certificates as we can.
    734            if let Some(Value::Blob(cert_bytes)) = env_and_store.store.get(&reader, &cert_key)? {
    735                if let Ok(cert) = Cert::from_bytes(cert_bytes) {
    736                    let mut thin_vec_cert = ThinVec::with_capacity(cert.der.len());
    737                    thin_vec_cert.extend_from_slice(&cert.der);
    738                    certs.push(thin_vec_cert);
    739                }
    740            }
    741        }
    742        Ok(())
    743    }
    744 
    745    pub fn find_cert_by_hash(
    746        &self,
    747        cert_hash: &ThinVec<u8>,
    748        maybe_cert_bytes_out: Option<&mut ThinVec<u8>>,
    749    ) -> Result<bool, SecurityStateError> {
    750        let env_and_store = match self.env_and_store.as_ref() {
    751            Some(env_and_store) => env_and_store,
    752            None => return Err(SecurityStateError::from("env and store not initialized?")),
    753        };
    754        let reader = env_and_store.env.read()?;
    755        let cert_key = make_key!(PREFIX_CERT, &cert_hash);
    756        if let Some(Value::Blob(cert_bytes_stored)) = env_and_store.store.get(&reader, &cert_key)? {
    757            if let Some(maybe_cert_bytes_out) = maybe_cert_bytes_out {
    758                maybe_cert_bytes_out.clear();
    759                let cert = Cert::from_bytes(cert_bytes_stored)?;
    760                maybe_cert_bytes_out.extend_from_slice(cert.der);
    761            }
    762            return Ok(true);
    763        }
    764        Ok(false)
    765    }
    766 
    767    pub fn has_all_certs_by_hash(
    768        &self,
    769        cert_hashes: &ThinVec<ThinVec<u8>>,
    770    ) -> Result<bool, SecurityStateError> {
    771        // Bug 1950140 - Implement a cache for this function to improve performance,
    772        // based on a caller-supplied key identifiying the list of hashes.
    773        for cert_hash in cert_hashes {
    774            match self.find_cert_by_hash(&cert_hash, None) {
    775                Ok(true) => {}
    776                Ok(false) => return Ok(false),
    777                Err(err) => return Err(err),
    778            }
    779        }
    780        Ok(true)
    781    }
    782 }
    783 
    784 impl MallocSizeOf for SecurityState {
    785    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
    786        self.profile_path.size_of(ops)
    787            + self.env_and_store.size_of(ops)
    788            + self
    789                .crlite_filters
    790                .iter()
    791                .map(|filter| filter.size_of(ops))
    792                .sum::<usize>()
    793            + self.remaining_ops.size_of(ops)
    794    }
    795 }
    796 
    797 const CERT_SERIALIZATION_VERSION_1: u8 = 1;
    798 
    799 // A Cert consists of its DER encoding, its DER-encoded subject, and its trust (currently
    800 // nsICertStorage::TRUST_INHERIT, but in the future nsICertStorage::TRUST_ANCHOR may also be used).
    801 // The length of each encoding must be representable by a u16 (so 65535 bytes is the longest a
    802 // certificate can be).
    803 struct Cert<'a> {
    804    der: &'a [u8],
    805    subject: &'a [u8],
    806    trust: i16,
    807 }
    808 
    809 impl<'a> Cert<'a> {
    810    fn new(der: &'a [u8], subject: &'a [u8], trust: i16) -> Result<Cert<'a>, SecurityStateError> {
    811        if der.len() > u16::MAX.into() {
    812            return Err(SecurityStateError::from("certificate is too long"));
    813        }
    814        if subject.len() > u16::MAX.into() {
    815            return Err(SecurityStateError::from("subject is too long"));
    816        }
    817        Ok(Cert {
    818            der,
    819            subject,
    820            trust,
    821        })
    822    }
    823 
    824    fn from_bytes(encoded: &'a [u8]) -> Result<Cert<'a>, SecurityStateError> {
    825        if encoded.len() < size_of::<u8>() {
    826            return Err(SecurityStateError::from("invalid Cert: no version?"));
    827        }
    828        let (mut version, rest) = encoded.split_at(size_of::<u8>());
    829        let version = version.read_u8()?;
    830        if version != CERT_SERIALIZATION_VERSION_1 {
    831            return Err(SecurityStateError::from("invalid Cert: unexpected version"));
    832        }
    833 
    834        if rest.len() < size_of::<u16>() {
    835            return Err(SecurityStateError::from("invalid Cert: no der len?"));
    836        }
    837        let (mut der_len, rest) = rest.split_at(size_of::<u16>());
    838        let der_len = der_len.read_u16::<NetworkEndian>()?.into();
    839        if rest.len() < der_len {
    840            return Err(SecurityStateError::from("invalid Cert: no der?"));
    841        }
    842        let (der, rest) = rest.split_at(der_len);
    843 
    844        if rest.len() < size_of::<u16>() {
    845            return Err(SecurityStateError::from("invalid Cert: no subject len?"));
    846        }
    847        let (mut subject_len, rest) = rest.split_at(size_of::<u16>());
    848        let subject_len = subject_len.read_u16::<NetworkEndian>()?.into();
    849        if rest.len() < subject_len {
    850            return Err(SecurityStateError::from("invalid Cert: no subject?"));
    851        }
    852        let (subject, mut rest) = rest.split_at(subject_len);
    853 
    854        if rest.len() < size_of::<i16>() {
    855            return Err(SecurityStateError::from("invalid Cert: no trust?"));
    856        }
    857        let trust = rest.read_i16::<NetworkEndian>()?;
    858        if rest.len() > 0 {
    859            return Err(SecurityStateError::from("invalid Cert: trailing data?"));
    860        }
    861 
    862        Ok(Cert {
    863            der,
    864            subject,
    865            trust,
    866        })
    867    }
    868 
    869    fn to_bytes(&self) -> Result<Vec<u8>, SecurityStateError> {
    870        let mut bytes = Vec::with_capacity(
    871            size_of::<u8>()
    872                + size_of::<u16>()
    873                + self.der.len()
    874                + size_of::<u16>()
    875                + self.subject.len()
    876                + size_of::<i16>(),
    877        );
    878        bytes.write_u8(CERT_SERIALIZATION_VERSION_1)?;
    879        bytes.write_u16::<NetworkEndian>(
    880            self.der
    881                .len()
    882                .try_into()
    883                .map_err(|_| SecurityStateError::from("certificate is too long"))?,
    884        )?;
    885        bytes.extend_from_slice(&self.der);
    886        bytes.write_u16::<NetworkEndian>(
    887            self.subject
    888                .len()
    889                .try_into()
    890                .map_err(|_| SecurityStateError::from("subject is too long"))?,
    891        )?;
    892        bytes.extend_from_slice(&self.subject);
    893        bytes.write_i16::<NetworkEndian>(self.trust)?;
    894        Ok(bytes)
    895    }
    896 }
    897 
    898 // A CertHashList is a list of sha-256 hashes of DER-encoded certificates.
    899 struct CertHashList<'a> {
    900    hashes: Vec<&'a [u8]>,
    901 }
    902 
    903 impl<'a> CertHashList<'a> {
    904    fn new(hashes_bytes: &'a [u8]) -> Result<CertHashList<'a>, SecurityStateError> {
    905        if hashes_bytes.len() % Sha256::output_size() != 0 {
    906            return Err(SecurityStateError::from(
    907                "unexpected length for cert hash list",
    908            ));
    909        }
    910        let mut hashes = Vec::with_capacity(hashes_bytes.len() / Sha256::output_size());
    911        for hash in hashes_bytes.chunks_exact(Sha256::output_size()) {
    912            hashes.push(hash);
    913        }
    914        Ok(CertHashList { hashes })
    915    }
    916 
    917    fn add(hashes_bytes: &[u8], new_hash: &[u8]) -> Result<Vec<u8>, SecurityStateError> {
    918        if hashes_bytes.len() % Sha256::output_size() != 0 {
    919            return Err(SecurityStateError::from(
    920                "unexpected length for cert hash list",
    921            ));
    922        }
    923        if new_hash.len() != Sha256::output_size() {
    924            return Err(SecurityStateError::from("unexpected cert hash length"));
    925        }
    926        for hash in hashes_bytes.chunks_exact(Sha256::output_size()) {
    927            if hash == new_hash {
    928                return Ok(hashes_bytes.to_owned());
    929            }
    930        }
    931        let mut combined = hashes_bytes.to_owned();
    932        combined.extend_from_slice(new_hash);
    933        Ok(combined)
    934    }
    935 
    936    fn remove(hashes_bytes: &[u8], cert_hash: &[u8]) -> Result<Vec<u8>, SecurityStateError> {
    937        if hashes_bytes.len() % Sha256::output_size() != 0 {
    938            return Err(SecurityStateError::from(
    939                "unexpected length for cert hash list",
    940            ));
    941        }
    942        if cert_hash.len() != Sha256::output_size() {
    943            return Err(SecurityStateError::from("unexpected cert hash length"));
    944        }
    945        let mut result = Vec::with_capacity(hashes_bytes.len());
    946        for hash in hashes_bytes.chunks_exact(Sha256::output_size()) {
    947            if hash != cert_hash {
    948                result.extend_from_slice(hash);
    949            }
    950        }
    951        Ok(result)
    952    }
    953 }
    954 
    955 impl<'a> IntoIterator for CertHashList<'a> {
    956    type Item = &'a [u8];
    957    type IntoIter = std::vec::IntoIter<&'a [u8]>;
    958 
    959    fn into_iter(self) -> Self::IntoIter {
    960        self.hashes.into_iter()
    961    }
    962 }
    963 
    964 // Helper struct for get_crlite_revocation_state.
    965 struct CRLiteTimestamp {
    966    log_id: ThinVec<u8>,
    967    timestamp: u64,
    968 }
    969 
    970 // Helper struct for set_batch_state. Takes a prefix, two base64-encoded key
    971 // parts, and a security state value.
    972 struct EncodedSecurityState {
    973    prefix: &'static str,
    974    key_part_1_base64: nsCString,
    975    key_part_2_base64: nsCString,
    976    state: i16,
    977 }
    978 
    979 impl EncodedSecurityState {
    980    fn new(
    981        prefix: &'static str,
    982        key_part_1_base64: nsCString,
    983        key_part_2_base64: nsCString,
    984        state: i16,
    985    ) -> EncodedSecurityState {
    986        EncodedSecurityState {
    987            prefix,
    988            key_part_1_base64,
    989            key_part_2_base64,
    990            state,
    991        }
    992    }
    993 
    994    fn key(&self) -> Result<Vec<u8>, SecurityStateError> {
    995        let key_part_1 = BASE64_STANDARD.decode(&self.key_part_1_base64)?;
    996        let key_part_2 = BASE64_STANDARD.decode(&self.key_part_2_base64)?;
    997        Ok(make_key!(self.prefix, &key_part_1, &key_part_2))
    998    }
    999 
   1000    fn state(&self) -> i16 {
   1001        self.state
   1002    }
   1003 }
   1004 
   1005 fn get_path_from_directory_service(key: &str) -> Result<PathBuf, nserror::nsresult> {
   1006    let directory_service: RefPtr<nsIProperties> =
   1007        xpcom::components::Directory::service().map_err(|_| NS_ERROR_FAILURE)?;
   1008    let cs_key = CString::new(key).map_err(|_| NS_ERROR_FAILURE)?;
   1009 
   1010    let mut requested_dir = GetterAddrefs::<nsIFile>::new();
   1011    unsafe {
   1012        (*directory_service)
   1013            .Get(
   1014                (&cs_key).as_ptr(),
   1015                &nsIFile::IID as *const nsIID,
   1016                requested_dir.void_ptr(),
   1017            )
   1018            .to_result()
   1019    }?;
   1020 
   1021    let dir_path = requested_dir.refptr().ok_or(NS_ERROR_FAILURE)?;
   1022    let mut path = nsString::new();
   1023    unsafe { (*dir_path).GetPath(&mut *path).to_result() }?;
   1024    Ok(PathBuf::from(format!("{}", path)))
   1025 }
   1026 
   1027 fn get_profile_path() -> Result<PathBuf, nserror::nsresult> {
   1028    get_path_from_directory_service("ProfD").or_else(|_| get_path_from_directory_service("TmpD"))
   1029 }
   1030 
   1031 fn get_store_path(profile_path: &PathBuf) -> Result<PathBuf, SecurityStateError> {
   1032    let mut store_path = profile_path.clone();
   1033    store_path.push("security_state");
   1034    create_dir_all(store_path.as_path())?;
   1035    Ok(store_path)
   1036 }
   1037 
   1038 fn make_env(path: &Path) -> Result<Rkv, SecurityStateError> {
   1039    let mut builder = Rkv::environment_builder::<SafeMode>();
   1040    builder.set_max_dbs(2);
   1041 
   1042    // 16MB is a little over twice the size of the current dataset. When we
   1043    // eventually switch to the LMDB backend to create the builder above,
   1044    // we should set this as the map size, since it cannot currently resize.
   1045    // (The SafeMode backend warns when a map size is specified, so we skip it
   1046    // for now to avoid console spam.)
   1047 
   1048    // builder.set_map_size(16777216);
   1049 
   1050    // Bug 1595004: Migrate databases between backends in the future,
   1051    // and handle 32 and 64 bit architectures in case of LMDB.
   1052    Rkv::from_builder(path, builder).map_err(SecurityStateError::from)
   1053 }
   1054 
   1055 fn unconditionally_remove_file(path: &Path) -> Result<(), SecurityStateError> {
   1056    match remove_file(path) {
   1057        Ok(()) => Ok(()),
   1058        Err(e) => match e.kind() {
   1059            std::io::ErrorKind::NotFound => Ok(()),
   1060            _ => Err(SecurityStateError::from(e)),
   1061        },
   1062    }
   1063 }
   1064 
   1065 fn remove_db(path: &Path) -> Result<(), SecurityStateError> {
   1066    // Remove LMDB-related files.
   1067    let db = path.join("data.mdb");
   1068    unconditionally_remove_file(&db)?;
   1069    let lock = path.join("lock.mdb");
   1070    unconditionally_remove_file(&lock)?;
   1071 
   1072    // Remove SafeMode-related files.
   1073    let db = path.join("data.safe.bin");
   1074    unconditionally_remove_file(&db)?;
   1075 
   1076    Ok(())
   1077 }
   1078 
   1079 // This is a helper struct that implements the task that asynchronously reads CRLite deltas on
   1080 // a background thread.
   1081 struct BackgroundReadDeltasTask {
   1082    profile_path: PathBuf,
   1083    security_state: Arc<RwLock<SecurityState>>,
   1084 }
   1085 
   1086 impl BackgroundReadDeltasTask {
   1087    fn new(
   1088        profile_path: PathBuf,
   1089        security_state: &Arc<RwLock<SecurityState>>,
   1090    ) -> BackgroundReadDeltasTask {
   1091        BackgroundReadDeltasTask {
   1092            profile_path,
   1093            security_state: Arc::clone(security_state),
   1094        }
   1095    }
   1096 }
   1097 
   1098 impl Task for BackgroundReadDeltasTask {
   1099    fn run(&self) {
   1100        let Ok(store_path) = get_store_path(&self.profile_path) else {
   1101            error!("error getting security_state path");
   1102            return;
   1103        };
   1104 
   1105        let mut delta_filters = vec![];
   1106        if let Ok(mut entries) = std::fs::read_dir(&store_path) {
   1107            while let Some(Ok(entry)) = entries.next() {
   1108                let entry_path = entry.path();
   1109                let extension = entry_path
   1110                    .extension()
   1111                    .map(|os_str| os_str.to_str())
   1112                    .flatten();
   1113                if extension != Some("delta") {
   1114                    continue;
   1115                }
   1116                if let Ok(Some(filter)) = Filter::load(&store_path, &entry.file_name()) {
   1117                    delta_filters.push(filter);
   1118                }
   1119            }
   1120        }
   1121 
   1122        let Ok(mut ss) = self.security_state.write() else {
   1123            return;
   1124        };
   1125        if let Err(e) = ss.open_db() {
   1126            error!("error opening security state db: {}", e.message);
   1127        }
   1128        ss.crlite_filters.append(&mut delta_filters);
   1129    }
   1130 
   1131    fn done(&self) -> Result<(), nsresult> {
   1132        Ok(())
   1133    }
   1134 }
   1135 
   1136 fn do_construct_cert_storage(
   1137    iid: *const xpcom::nsIID,
   1138    result: *mut *mut xpcom::reexports::libc::c_void,
   1139 ) -> Result<(), nserror::nsresult> {
   1140    let path_buf = get_profile_path()?;
   1141    let security_state = Arc::new(RwLock::new(SecurityState::new(path_buf.clone())));
   1142    let cert_storage = CertStorage::allocate(InitCertStorage {
   1143        security_state: security_state.clone(),
   1144        queue: create_background_task_queue(cstr!("cert_storage"))?,
   1145    });
   1146    let memory_reporter = MemoryReporter::allocate(InitMemoryReporter { security_state });
   1147 
   1148    // Dispatch a task to the background task queue to asynchronously read CRLite delta files (if
   1149    // present) and load them into cert_storage. This task does not hold the
   1150    // cert_storage.security_state mutex for the majority of its operation, which allows certificate
   1151    // verification threads to query cert_storage without blocking. This is important for
   1152    // performance, but it means that certificate verifications that happen before the task has
   1153    // completed will not have delta information, and thus may not know of revocations that have
   1154    // occurred since the last full CRLite filter was downloaded.
   1155    // NB: because the background task queue is serial, this task will complete before other tasks
   1156    // later dispatched to the queue run. This means that other tasks that depend on deltas will do
   1157    // so with the correct set of preconditions.
   1158    let load_crlite_deltas_task = Box::new(BackgroundReadDeltasTask::new(
   1159        path_buf,
   1160        &cert_storage.security_state,
   1161    ));
   1162    let runnable = TaskRunnable::new("LoadCrliteDeltas", load_crlite_deltas_task)?;
   1163    TaskRunnable::dispatch(runnable, cert_storage.queue.coerce())?;
   1164 
   1165    if let Some(reporter) = memory_reporter.query_interface::<nsIMemoryReporter>() {
   1166        if let Some(reporter_manager) = xpcom::get_service::<nsIMemoryReporterManager>(cstr!(
   1167            "@mozilla.org/memory-reporter-manager;1"
   1168        )) {
   1169            unsafe { reporter_manager.RegisterStrongReporter(&*reporter) };
   1170        }
   1171    }
   1172 
   1173    unsafe { cert_storage.QueryInterface(iid, result).to_result() }
   1174 }
   1175 
   1176 // This is a helper for creating a task that will perform a specific action on a background thread.
   1177 struct SecurityStateTask<
   1178    T: Default + VariantType,
   1179    F: FnOnce(&mut SecurityState) -> Result<T, SecurityStateError>,
   1180 > {
   1181    callback: AtomicCell<Option<ThreadBoundRefPtr<nsICertStorageCallback>>>,
   1182    security_state: Arc<RwLock<SecurityState>>,
   1183    result: AtomicCell<(nserror::nsresult, T)>,
   1184    task_action: AtomicCell<Option<F>>,
   1185 }
   1186 
   1187 impl<T: Default + VariantType, F: FnOnce(&mut SecurityState) -> Result<T, SecurityStateError>>
   1188    SecurityStateTask<T, F>
   1189 {
   1190    fn new(
   1191        callback: &nsICertStorageCallback,
   1192        security_state: &Arc<RwLock<SecurityState>>,
   1193        task_action: F,
   1194    ) -> Result<SecurityStateTask<T, F>, nsresult> {
   1195        let mut ss = security_state.write().or(Err(NS_ERROR_FAILURE))?;
   1196        ss.remaining_ops = ss.remaining_ops.wrapping_add(1);
   1197 
   1198        Ok(SecurityStateTask {
   1199            callback: AtomicCell::new(Some(ThreadBoundRefPtr::new(RefPtr::new(callback)))),
   1200            security_state: Arc::clone(security_state),
   1201            result: AtomicCell::new((NS_ERROR_FAILURE, T::default())),
   1202            task_action: AtomicCell::new(Some(task_action)),
   1203        })
   1204    }
   1205 }
   1206 
   1207 impl<T: Default + VariantType, F: FnOnce(&mut SecurityState) -> Result<T, SecurityStateError>> Task
   1208    for SecurityStateTask<T, F>
   1209 {
   1210    fn run(&self) {
   1211        let mut ss = match self.security_state.write() {
   1212            Ok(ss) => ss,
   1213            Err(_) => return,
   1214        };
   1215        // this is a no-op if the DB is already open
   1216        if ss.open_db().is_err() {
   1217            return;
   1218        }
   1219        if let Some(task_action) = self.task_action.swap(None) {
   1220            let rv = task_action(&mut ss)
   1221                .and_then(|v| Ok((NS_OK, v)))
   1222                .unwrap_or((NS_ERROR_FAILURE, T::default()));
   1223            self.result.store(rv);
   1224        }
   1225    }
   1226 
   1227    fn done(&self) -> Result<(), nsresult> {
   1228        let threadbound = self.callback.swap(None).ok_or(NS_ERROR_FAILURE)?;
   1229        let callback = threadbound.get_ref().ok_or(NS_ERROR_FAILURE)?;
   1230        let result = self.result.swap((NS_ERROR_FAILURE, T::default()));
   1231        let variant = result.1.into_variant();
   1232        let nsrv = unsafe { callback.Done(result.0, &*variant) };
   1233 
   1234        let mut ss = self.security_state.write().or(Err(NS_ERROR_FAILURE))?;
   1235        ss.remaining_ops = ss.remaining_ops.wrapping_sub(1);
   1236 
   1237        match nsrv {
   1238            NS_OK => Ok(()),
   1239            e => Err(e),
   1240        }
   1241    }
   1242 }
   1243 
   1244 #[no_mangle]
   1245 pub extern "C" fn cert_storage_constructor(
   1246    iid: *const xpcom::nsIID,
   1247    result: *mut *mut xpcom::reexports::libc::c_void,
   1248 ) -> nserror::nsresult {
   1249    if !is_main_thread() {
   1250        return NS_ERROR_NOT_SAME_THREAD;
   1251    }
   1252    match do_construct_cert_storage(iid, result) {
   1253        Ok(()) => NS_OK,
   1254        Err(e) => e,
   1255    }
   1256 }
   1257 
   1258 macro_rules! try_ns {
   1259    ($e:expr) => {
   1260        match $e {
   1261            Ok(value) => value,
   1262            Err(_) => return NS_ERROR_FAILURE,
   1263        }
   1264    };
   1265    ($e:expr, or continue) => {
   1266        match $e {
   1267            Ok(value) => value,
   1268            Err(err) => {
   1269                error!("{}", err);
   1270                continue;
   1271            }
   1272        }
   1273    };
   1274 }
   1275 
   1276 // This macro is a way to ensure the DB has been opened while minimizing lock acquisitions in the
   1277 // common (read-only) case. First we acquire a read lock and see if we even need to open the DB. If
   1278 // not, we can continue with the read lock we already have. Otherwise, we drop the read lock,
   1279 // acquire the write lock, open the DB, drop the write lock, and re-acquire the read lock. While it
   1280 // is possible for two or more threads to all come to the conclusion that they need to open the DB,
   1281 // this isn't ultimately an issue - `open_db` will exit early if another thread has already done the
   1282 // work.
   1283 macro_rules! get_security_state {
   1284    ($self:expr) => {{
   1285        let ss_read_only = try_ns!($self.security_state.read());
   1286        if !ss_read_only.db_needs_opening() {
   1287            ss_read_only
   1288        } else {
   1289            drop(ss_read_only);
   1290            {
   1291                let mut ss_write = try_ns!($self.security_state.write());
   1292                try_ns!(ss_write.open_db());
   1293            }
   1294            try_ns!($self.security_state.read())
   1295        }
   1296    }};
   1297 }
   1298 
   1299 #[xpcom(implement(nsICertStorage), atomic)]
   1300 struct CertStorage {
   1301    security_state: Arc<RwLock<SecurityState>>,
   1302    queue: RefPtr<nsISerialEventTarget>,
   1303 }
   1304 
   1305 /// CertStorage implements the nsICertStorage interface. The actual work is done by the
   1306 /// SecurityState. To handle any threading issues, we have an atomic-refcounted read/write lock on
   1307 /// the one and only SecurityState. So, only one thread can use SecurityState's &mut self functions
   1308 /// at a time, while multiple threads can use &self functions simultaneously (as long as there are
   1309 /// no threads using an &mut self function). The Arc is to allow for the creation of background
   1310 /// tasks that use the SecurityState on the queue owned by CertStorage. This allows us to not block
   1311 /// the main thread.
   1312 #[allow(non_snake_case)]
   1313 impl CertStorage {
   1314    unsafe fn HasPriorData(
   1315        &self,
   1316        data_type: u8,
   1317        callback: *const nsICertStorageCallback,
   1318    ) -> nserror::nsresult {
   1319        if !is_main_thread() {
   1320            return NS_ERROR_NOT_SAME_THREAD;
   1321        }
   1322        if callback.is_null() {
   1323            return NS_ERROR_NULL_POINTER;
   1324        }
   1325        let task = Box::new(try_ns!(SecurityStateTask::new(
   1326            &*callback,
   1327            &self.security_state,
   1328            move |ss| ss.get_has_prior_data(data_type),
   1329        )));
   1330        let runnable = try_ns!(TaskRunnable::new("HasPriorData", task));
   1331        try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
   1332        NS_OK
   1333    }
   1334 
   1335    unsafe fn GetRemainingOperationCount(&self, state: *mut i32) -> nserror::nsresult {
   1336        if !is_main_thread() {
   1337            return NS_ERROR_NOT_SAME_THREAD;
   1338        }
   1339        if state.is_null() {
   1340            return NS_ERROR_NULL_POINTER;
   1341        }
   1342        let ss = try_ns!(self.security_state.read());
   1343        *state = ss.remaining_ops;
   1344        NS_OK
   1345    }
   1346 
   1347    unsafe fn SetRevocations(
   1348        &self,
   1349        revocations: *const ThinVec<Option<RefPtr<nsIRevocationState>>>,
   1350        callback: *const nsICertStorageCallback,
   1351    ) -> nserror::nsresult {
   1352        if !is_main_thread() {
   1353            return NS_ERROR_NOT_SAME_THREAD;
   1354        }
   1355        if revocations.is_null() || callback.is_null() {
   1356            return NS_ERROR_NULL_POINTER;
   1357        }
   1358 
   1359        let revocations = &*revocations;
   1360        let mut entries = Vec::with_capacity(revocations.len());
   1361 
   1362        // By continuing when an nsIRevocationState attribute value is invalid,
   1363        // we prevent errors relating to individual blocklist entries from
   1364        // causing sync to fail. We will accumulate telemetry on these failures
   1365        // in bug 1254099.
   1366 
   1367        for revocation in revocations.iter().flatten() {
   1368            let mut state: i16 = 0;
   1369            try_ns!(revocation.GetState(&mut state).to_result(), or continue);
   1370 
   1371            if let Some(revocation) =
   1372                (*revocation).query_interface::<nsIIssuerAndSerialRevocationState>()
   1373            {
   1374                let mut issuer = nsCString::new();
   1375                try_ns!(revocation.GetIssuer(&mut *issuer).to_result(), or continue);
   1376 
   1377                let mut serial = nsCString::new();
   1378                try_ns!(revocation.GetSerial(&mut *serial).to_result(), or continue);
   1379 
   1380                entries.push(EncodedSecurityState::new(
   1381                    PREFIX_REV_IS,
   1382                    issuer,
   1383                    serial,
   1384                    state,
   1385                ));
   1386            } else if let Some(revocation) =
   1387                (*revocation).query_interface::<nsISubjectAndPubKeyRevocationState>()
   1388            {
   1389                let mut subject = nsCString::new();
   1390                try_ns!(revocation.GetSubject(&mut *subject).to_result(), or continue);
   1391 
   1392                let mut pub_key_hash = nsCString::new();
   1393                try_ns!(revocation.GetPubKey(&mut *pub_key_hash).to_result(), or continue);
   1394 
   1395                entries.push(EncodedSecurityState::new(
   1396                    PREFIX_REV_SPK,
   1397                    subject,
   1398                    pub_key_hash,
   1399                    state,
   1400                ));
   1401            }
   1402        }
   1403 
   1404        let task = Box::new(try_ns!(SecurityStateTask::new(
   1405            &*callback,
   1406            &self.security_state,
   1407            move |ss| ss.set_batch_state(&entries, nsICertStorage::DATA_TYPE_REVOCATION),
   1408        )));
   1409        let runnable = try_ns!(TaskRunnable::new("SetRevocations", task));
   1410        try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
   1411        NS_OK
   1412    }
   1413 
   1414    unsafe fn GetRevocationState(
   1415        &self,
   1416        issuer: *const ThinVec<u8>,
   1417        serial: *const ThinVec<u8>,
   1418        subject: *const ThinVec<u8>,
   1419        pub_key: *const ThinVec<u8>,
   1420        state: *mut i16,
   1421    ) -> nserror::nsresult {
   1422        // TODO (bug 1541212): We really want to restrict this to non-main-threads only in non-test
   1423        // contexts, but we can't do so until bug 1406854 is fixed.
   1424        if issuer.is_null() || serial.is_null() || subject.is_null() || pub_key.is_null() {
   1425            return NS_ERROR_NULL_POINTER;
   1426        }
   1427        *state = nsICertStorage::STATE_UNSET;
   1428        let ss = get_security_state!(self);
   1429        match ss.get_revocation_state(&*issuer, &*serial, &*subject, &*pub_key) {
   1430            Ok(st) => {
   1431                *state = st;
   1432                NS_OK
   1433            }
   1434            _ => NS_ERROR_FAILURE,
   1435        }
   1436    }
   1437 
   1438    unsafe fn SetFullCRLiteFilter(
   1439        &self,
   1440        filter: *const ThinVec<u8>,
   1441        callback: *const nsICertStorageCallback,
   1442    ) -> nserror::nsresult {
   1443        if !is_main_thread() {
   1444            return NS_ERROR_NOT_SAME_THREAD;
   1445        }
   1446        if filter.is_null() || callback.is_null() {
   1447            return NS_ERROR_NULL_POINTER;
   1448        }
   1449 
   1450        let filter_owned = (*filter).to_vec();
   1451 
   1452        let task = Box::new(try_ns!(SecurityStateTask::new(
   1453            &*callback,
   1454            &self.security_state,
   1455            move |ss| ss.set_full_crlite_filter(filter_owned),
   1456        )));
   1457        let runnable = try_ns!(TaskRunnable::new("SetFullCRLiteFilter", task));
   1458        try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
   1459        NS_OK
   1460    }
   1461 
   1462    unsafe fn AddCRLiteDelta(
   1463        &self,
   1464        delta: *const ThinVec<u8>,
   1465        filename: *const nsACString,
   1466        callback: *const nsICertStorageCallback,
   1467    ) -> nserror::nsresult {
   1468        if !is_main_thread() {
   1469            return NS_ERROR_NOT_SAME_THREAD;
   1470        }
   1471        if delta.is_null() || filename.is_null() || callback.is_null() {
   1472            return NS_ERROR_NULL_POINTER;
   1473        }
   1474        let delta_owned = (*delta).to_vec();
   1475        let filename_owned = (*filename).to_string();
   1476        let task = Box::new(try_ns!(SecurityStateTask::new(
   1477            &*callback,
   1478            &self.security_state,
   1479            move |ss| ss.add_crlite_delta(delta_owned, filename_owned),
   1480        )));
   1481        let runnable = try_ns!(TaskRunnable::new("AddCRLiteDelta", task));
   1482        try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
   1483        NS_OK
   1484    }
   1485 
   1486    unsafe fn TestNoteCRLiteUpdateTime(
   1487        &self,
   1488        callback: *const nsICertStorageCallback,
   1489    ) -> nserror::nsresult {
   1490        if !is_main_thread() {
   1491            return NS_ERROR_NOT_SAME_THREAD;
   1492        }
   1493        let task = Box::new(try_ns!(SecurityStateTask::new(
   1494            &*callback,
   1495            &self.security_state,
   1496            move |ss| ss.note_crlite_update_time(),
   1497        )));
   1498        let runnable = try_ns!(TaskRunnable::new("TestNoteCRLiteUpdateTime", task));
   1499        try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
   1500        NS_OK
   1501    }
   1502 
   1503    unsafe fn GetCRLiteRevocationState(
   1504        &self,
   1505        issuerSPKI: *const ThinVec<u8>,
   1506        serialNumber: *const ThinVec<u8>,
   1507        timestamps: *const ThinVec<Option<RefPtr<nsICRLiteTimestamp>>>,
   1508        state: *mut i16,
   1509    ) -> nserror::nsresult {
   1510        // TODO (bug 1541212): We really want to restrict this to non-main-threads only, but we
   1511        // can't do so until bug 1406854 is fixed.
   1512        if issuerSPKI.is_null() || serialNumber.is_null() || state.is_null() || timestamps.is_null()
   1513        {
   1514            return NS_ERROR_NULL_POINTER;
   1515        }
   1516        let timestamps = &*timestamps;
   1517        let mut timestamp_entries = Vec::with_capacity(timestamps.len());
   1518        for timestamp_entry in timestamps.iter().flatten() {
   1519            let mut log_id = ThinVec::with_capacity(32);
   1520            try_ns!(timestamp_entry.GetLogID(&mut log_id).to_result(), or continue);
   1521            let mut timestamp: u64 = 0;
   1522            try_ns!(timestamp_entry.GetTimestamp(&mut timestamp).to_result(), or continue);
   1523            timestamp_entries.push(CRLiteTimestamp { log_id, timestamp });
   1524        }
   1525        let ss = get_security_state!(self);
   1526        *state = ss.get_crlite_revocation_state(&*issuerSPKI, &*serialNumber, &timestamp_entries);
   1527        NS_OK
   1528    }
   1529 
   1530    unsafe fn AddCerts(
   1531        &self,
   1532        certs: *const ThinVec<Option<RefPtr<nsICertInfo>>>,
   1533        callback: *const nsICertStorageCallback,
   1534    ) -> nserror::nsresult {
   1535        if !is_main_thread() {
   1536            return NS_ERROR_NOT_SAME_THREAD;
   1537        }
   1538        if certs.is_null() || callback.is_null() {
   1539            return NS_ERROR_NULL_POINTER;
   1540        }
   1541        let certs = &*certs;
   1542        let mut cert_entries = Vec::with_capacity(certs.len());
   1543        for cert in certs.iter().flatten() {
   1544            let mut der = nsCString::new();
   1545            try_ns!((*cert).GetCert(&mut *der).to_result(), or continue);
   1546            let mut subject = nsCString::new();
   1547            try_ns!((*cert).GetSubject(&mut *subject).to_result(), or continue);
   1548            let mut trust: i16 = 0;
   1549            try_ns!((*cert).GetTrust(&mut trust).to_result(), or continue);
   1550            cert_entries.push((der, subject, trust));
   1551        }
   1552        let task = Box::new(try_ns!(SecurityStateTask::new(
   1553            &*callback,
   1554            &self.security_state,
   1555            move |ss| ss.add_certs(&cert_entries),
   1556        )));
   1557        let runnable = try_ns!(TaskRunnable::new("AddCerts", task));
   1558        try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
   1559        NS_OK
   1560    }
   1561 
   1562    // Synchronous helper for testing purposes only
   1563    unsafe fn TestHelperAddCert(
   1564        &self,
   1565        cert: *const nsACString,
   1566        subject: *const nsACString,
   1567        trust: i16,
   1568    ) -> nserror::nsresult {
   1569        let cert = nsCString::from(&*cert);
   1570        let subject = nsCString::from(&*subject);
   1571        let mut ss = self.security_state.write().unwrap();
   1572        ss.open_db().unwrap();
   1573        ss.add_certs(&[(cert, subject, trust)]).unwrap();
   1574        NS_OK
   1575    }
   1576 
   1577    unsafe fn RemoveCertsByHashes(
   1578        &self,
   1579        hashes: *const ThinVec<nsCString>,
   1580        callback: *const nsICertStorageCallback,
   1581    ) -> nserror::nsresult {
   1582        if !is_main_thread() {
   1583            return NS_ERROR_NOT_SAME_THREAD;
   1584        }
   1585        if hashes.is_null() || callback.is_null() {
   1586            return NS_ERROR_NULL_POINTER;
   1587        }
   1588        let hashes = (*hashes).to_vec();
   1589        let task = Box::new(try_ns!(SecurityStateTask::new(
   1590            &*callback,
   1591            &self.security_state,
   1592            move |ss| ss.remove_certs_by_hashes(&hashes),
   1593        )));
   1594        let runnable = try_ns!(TaskRunnable::new("RemoveCertsByHashes", task));
   1595        try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
   1596        NS_OK
   1597    }
   1598 
   1599    unsafe fn FindCertsBySubject(
   1600        &self,
   1601        subject: *const ThinVec<u8>,
   1602        certs: *mut ThinVec<ThinVec<u8>>,
   1603    ) -> nserror::nsresult {
   1604        // TODO (bug 1541212): We really want to restrict this to non-main-threads only, but we
   1605        // can't do so until bug 1406854 is fixed.
   1606        if subject.is_null() || certs.is_null() {
   1607            return NS_ERROR_NULL_POINTER;
   1608        }
   1609        let ss = get_security_state!(self);
   1610        match ss.find_certs_by_subject(&*subject, &mut *certs) {
   1611            Ok(()) => NS_OK,
   1612            Err(_) => NS_ERROR_FAILURE,
   1613        }
   1614    }
   1615 
   1616    unsafe fn HasAllCertsByHash(
   1617        &self,
   1618        cert_hashes: *const ThinVec<ThinVec<u8>>,
   1619        found: *mut bool,
   1620    ) -> nserror::nsresult {
   1621        if cert_hashes.is_null() {
   1622            return NS_ERROR_NULL_POINTER;
   1623        }
   1624        let ss = get_security_state!(self);
   1625        match ss.has_all_certs_by_hash(&*cert_hashes) {
   1626            Ok(result) => {
   1627                *found = result;
   1628                NS_OK
   1629            }
   1630            Err(err) => {
   1631                log::error!("HasAllCertsByHash: {:?}", err);
   1632                NS_ERROR_FAILURE
   1633            }
   1634        }
   1635    }
   1636 
   1637    unsafe fn FindCertByHash(
   1638        &self,
   1639        cert_hash: *const ThinVec<u8>,
   1640        cert_bytes: *mut ThinVec<u8>,
   1641    ) -> nserror::nsresult {
   1642        if cert_hash.is_null() || cert_bytes.is_null() {
   1643            return NS_ERROR_NULL_POINTER;
   1644        }
   1645        let ss = get_security_state!(self);
   1646        match ss.find_cert_by_hash(&*cert_hash, Some(&mut *cert_bytes)) {
   1647            Ok(true) => NS_OK,
   1648            Ok(false) => NS_ERROR_FAILURE,
   1649            Err(err) => {
   1650                log::error!("FindCertByHash: {:?}", err);
   1651                NS_ERROR_FAILURE
   1652            }
   1653        }
   1654    }
   1655 }
   1656 
   1657 extern "C" {
   1658    fn cert_storage_malloc_size_of(ptr: *const xpcom::reexports::libc::c_void) -> usize;
   1659 }
   1660 
   1661 #[xpcom(implement(nsIMemoryReporter), atomic)]
   1662 struct MemoryReporter {
   1663    security_state: Arc<RwLock<SecurityState>>,
   1664 }
   1665 
   1666 #[allow(non_snake_case)]
   1667 impl MemoryReporter {
   1668    unsafe fn CollectReports(
   1669        &self,
   1670        callback: *const nsIHandleReportCallback,
   1671        data: *const nsISupports,
   1672        _anonymize: bool,
   1673    ) -> nserror::nsresult {
   1674        let ss = try_ns!(self.security_state.read());
   1675        let mut ops = MallocSizeOfOps::new(cert_storage_malloc_size_of, None);
   1676        let size = ss.size_of(&mut ops);
   1677        let callback = match RefPtr::from_raw(callback) {
   1678            Some(ptr) => ptr,
   1679            None => return NS_ERROR_UNEXPECTED,
   1680        };
   1681        // This does the same as MOZ_COLLECT_REPORT
   1682        callback.Callback(
   1683            &nsCStr::new() as &nsACString,
   1684            &nsCStr::from("explicit/cert-storage/storage") as &nsACString,
   1685            nsIMemoryReporter::KIND_HEAP,
   1686            nsIMemoryReporter::UNITS_BYTES,
   1687            size as i64,
   1688            &nsCStr::from("Memory used by certificate storage") as &nsACString,
   1689            data,
   1690        );
   1691        NS_OK
   1692    }
   1693 }