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