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