Skip to main content

sequoia_cert_store/
cert_store.rs

1use std::path::Path;
2use std::sync::Arc;
3
4use anyhow::Context;
5
6use sequoia_openpgp as openpgp;
7use openpgp::cert::raw::RawCertParser;
8use openpgp::Fingerprint;
9use openpgp::KeyHandle;
10use openpgp::packet::UserID;
11use openpgp::parse::Parse;
12use openpgp::Result;
13
14use crate::LazyCert;
15use crate::store;
16use store::Certs;
17use store::MergeCerts;
18use store::Store;
19use store::StoreError;
20use store::StoreUpdate;
21use store::UserIDQueryParams;
22
23use crate::TRACE;
24
25#[derive(Debug, Clone, PartialEq, Eq)]
26#[non_exhaustive]
27pub enum AccessMode {
28    Always,
29    OnMiss,
30}
31
32/// Uninhabitable type used as the `Ok` variant of `CertStore::certd`
33/// on targets without cert-d support (e.g. wasm).
34#[cfg(not(certd))]
35pub(crate) enum NoCertD {}
36
37/// A unified interface to multiple certificate stores.
38///
39/// When a certificate is looked up, the certificate is looked up in
40/// the primary cert-d, if any, and all the backends whose access mode
41/// is `AccessMode::Always`.  The results are merged and returned.  If
42/// no certificate is found, then the look up is also tried on the
43/// backends whose access mode is `AccessMode::OnMiss`.  Finally, if a
44/// key server is configured, the key server is tried.
45///
46/// In general, results are preferred to errors.  That is, if a
47/// backend returns a positive result, and another backend returns an
48/// error, the error is ignored, even if it is something other than
49/// [`StoreError::NotFound`].
50///
51/// Results from the key server are either cached when
52/// [`CertStore::flush`] is called (or the [`CertStore`] is dropped)
53/// if there is a writable primary cert-d, or simply dropped
54/// otherwise.
55pub struct CertStore<'a> {
56    #[cfg(certd)]
57    certd: std::result::Result<store::CertD<'a>, store::Certs<'a>>,
58    #[cfg(not(certd))]
59    certd: std::result::Result<NoCertD, store::Certs<'a>>,
60
61    // Read-only backends.
62    backends: Vec<(Box<dyn store::Store<'a> + Send + Sync + 'a>, AccessMode)>,
63
64    keyserver: Option<Box<store::KeyServer<'a>>>,
65}
66assert_send_and_sync!(CertStore<'_>);
67
68impl<'a> CertStore<'a> {
69    /// Returns a CertStore, which does not have any configured backends.
70    pub fn empty() -> Self {
71        CertStore {
72            certd: Err(store::Certs::empty()),
73            backends: Vec::new(),
74            keyserver: None,
75        }
76    }
77
78    /// Returns a CertStore, which uses the default certificate
79    /// directory.
80    ///
81    /// When a certificate is added or updated, it will be added to or
82    /// updated in this certificate store.
83    #[cfg(certd)]
84    pub fn new() -> Result<Self> {
85        Ok(CertStore {
86            certd: Ok(store::CertD::open_default()?),
87            backends: Vec::new(),
88            keyserver: None,
89        })
90    }
91
92    /// Returns a CertStore, which uses the default certificate
93    /// directory in read-only mode.
94    #[cfg(certd)]
95    pub fn readonly() -> Result<Self> {
96        let mut cert_store = CertStore {
97            certd: Err(store::Certs::empty()),
98            backends: Vec::new(),
99            keyserver: None,
100        };
101        cert_store.add_default_certd()?;
102        Ok(cert_store)
103    }
104
105    /// Returns a CertStore, which uses the specified certificate
106    /// directory.
107    ///
108    /// When a certificate is added or updated, it will be added to or
109    /// updated in this certificate store.
110    #[cfg(certd)]
111    pub fn open<P>(path: P) -> Result<Self>
112        where P: AsRef<Path>
113    {
114        let path = path.as_ref();
115
116        Ok(CertStore {
117            certd: Ok(store::CertD::open(path)?),
118            backends: Vec::new(),
119            keyserver: None,
120        })
121    }
122
123    /// Returns a CertStore, which uses the specified certificate
124    /// directory in read-only mode.
125    #[cfg(certd)]
126    pub fn open_readonly<P>(path: P) -> Result<Self>
127        where P: AsRef<Path>
128    {
129        let path = path.as_ref();
130
131        let mut cert_store = CertStore {
132            certd: Err(store::Certs::empty()),
133            backends: Vec::new(),
134            keyserver: None,
135        };
136        cert_store.add_certd(path)?;
137        Ok(cert_store)
138    }
139
140    /// Add the specified backend to the CertStore.
141    ///
142    /// The backend is added to the collection of read-only backends.
143    pub fn add_backend(&mut self, backend: Box<dyn store::Store<'a> + Send + Sync + 'a>,
144                       mode: AccessMode)
145        -> &mut Self
146    {
147        self.backends.push((backend, mode));
148        self
149    }
150
151    /// Adds the specified cert-d to the CertStore.
152    ///
153    /// The cert-d is added in read-only mode, and its access mode is
154    /// set to `AccessMode::Always`.
155    #[cfg(certd)]
156    pub fn add_certd<P>(&mut self, path: P) -> Result<&mut Self>
157        where P: AsRef<Path>
158    {
159        let path = path.as_ref();
160        self.add_backend(Box::new(store::CertD::open(path)?),
161                         AccessMode::Always);
162        Ok(self)
163    }
164
165    /// Adds the default cert-d to the CertStore.
166    ///
167    /// The cert-d is added in read-only mode, and its access mode is
168    /// set to `AccessMode::Always`.
169    #[cfg(certd)]
170    pub fn add_default_certd(&mut self) -> Result<&mut Self>
171    {
172        self.add_backend(Box::new(store::CertD::open_default()?),
173                         AccessMode::Always);
174        Ok(self)
175    }
176
177    /// Adds the specified keyring to the CertStore.
178    ///
179    /// The keyring is added in read-only mode, and its access mode is
180    /// set to `AccessMode::Always`.
181    pub fn add_keyring<P>(&mut self, path: P) -> Result<&mut Self>
182        where P: AsRef<Path>
183    {
184        self.add_keyrings(std::iter::once(path))?;
185        Ok(self)
186    }
187
188    /// Adds the specified keyrings to the CertStore.
189    ///
190    /// The keyrings are added in read-only mode, and their access
191    /// mode is set to `AccessMode::Always`.
192    pub fn add_keyrings<I, P>(&mut self, filenames: I) -> Result<&mut Self>
193    where P: AsRef<Path>,
194          I: IntoIterator<Item=P>,
195    {
196        let keyring = Certs::empty();
197        let mut error = None;
198        for filename in filenames {
199            let filename = filename.as_ref();
200
201            let f = std::fs::File::open(filename)
202                .with_context(|| format!("Open {:?}", filename))?;
203            let parser = RawCertParser::from_reader(f)
204                .with_context(|| format!("Parsing {:?}", filename))?;
205
206            for cert in parser {
207                match cert {
208                    Ok(cert) => {
209                        keyring.update(Arc::new(cert.into()))
210                            .expect("implementation doesn't fail");
211                    }
212                    Err(err) => {
213                        eprint!("Parsing certificate in {:?}: {}",
214                                filename, err);
215                        error = Some(err);
216                    }
217                }
218            }
219        }
220
221        if let Some(err) = error {
222            return Err(err).context("Parsing keyrings");
223        }
224
225        self.add_backend(
226            Box::new(keyring),
227            AccessMode::Always);
228
229        Ok(self)
230    }
231
232    /// Adds the specified keyserver to the CertStore.
233    ///
234    /// The keyserver is added in read-only mode, and its access mode
235    /// is set to `AccessMode::OnMiss`.
236    #[cfg(feature = "keyserver")]
237    pub fn add_keyserver(&mut self, url: &str) -> Result<&mut Self>
238    {
239        self.keyserver = Some(Box::new(store::KeyServer::new(url)?));
240        Ok(self)
241    }
242
243    /// Adds the specified keyserver to the CertStore.
244    ///
245    /// The keyserver is added in read-only mode, and its access mode
246    /// is set to `AccessMode::OnMiss`.
247    ///
248    /// A key server is treated specially from other backends: any
249    /// results that it returns are written to the cert store (if it
250    /// is open in read-write mode).
251    #[cfg(feature = "keyserver")]
252    pub fn add_keyserver_backend(&mut self, ks: store::KeyServer<'a>)
253        -> Result<&mut Self>
254    {
255        self.keyserver = Some(Box::new(ks));
256        Ok(self)
257    }
258
259    /// Returns a reference to the certd store, if there is one.
260    #[cfg(certd)]
261    pub fn certd(&self) -> Option<&store::CertD<'a>> {
262        self.certd.as_ref().ok()
263    }
264
265    /// Returns a mutable reference to the certd store, if there
266    /// is one.
267    #[cfg(certd)]
268    pub fn certd_mut(&mut self) -> Option<&mut store::CertD<'a>> {
269        self.certd.as_mut().ok()
270    }
271}
272
273macro_rules! forward {
274    ( $method:ident, append:$to_vec:expr, $self:expr, $term:expr, $($args:ident),* ) => {{
275        tracer!(TRACE, format!("{}({})", stringify!($method), $term));
276
277        let mut certs = Vec::new();
278        let mut err = None;
279
280        match &$self.certd {
281            #[cfg(certd)]
282            Ok(certd) => {
283                match certd.$method($($args),*) {
284                    Err(err2) => {
285                        if let Some(StoreError::NotFound(_))
286                            = err2.downcast_ref::<StoreError>()
287                        {
288                            // Ignore NotFound.
289                            t!("certd returned nothing");
290                        } else {
291                            t!("certd returned: {}", err2);
292                            err = Some(err2)
293                        }
294                    }
295                    Ok(c) => {
296                        let mut c = $to_vec(c);
297                        t!("certd returned {}",
298                           c.iter()
299                               .map(|cert| cert.fingerprint().to_string())
300                               .collect::<Vec<String>>()
301                               .join(", "));
302                        certs.append(&mut c)
303                    }
304                }
305            }
306            #[cfg(not(certd))]
307            Ok(_) => unreachable!(),
308            Err(in_memory) => {
309                match in_memory.$method($($args),*) {
310                    Err(err2) => {
311                        if let Some(StoreError::NotFound(_))
312                            = err2.downcast_ref::<StoreError>()
313                        {
314                            // Ignore NotFound.
315                            t!("in-memory returned nothing");
316                        } else {
317                            t!("in-memory returned: {}", err2);
318                            err = Some(err2)
319                        }
320                    }
321                    Ok(c) => {
322                        let mut c = $to_vec(c);
323                        t!("in-memory returned {}",
324                           c.iter()
325                               .map(|cert| cert.fingerprint().to_string())
326                               .collect::<Vec<String>>()
327                               .join(", "));
328                        certs.append(&mut c)
329                    }
330                }
331            }
332        }
333
334        for mode in [AccessMode::Always, AccessMode::OnMiss] {
335            for (backend, _) in $self.backends.iter()
336                .filter(|(_, m)| &mode == m)
337            {
338                match backend.$method($($args),*) {
339                    Err(err2) => {
340                        if let Some(StoreError::NotFound(_))
341                            = err2.downcast_ref::<StoreError>()
342                        {
343                            // Ignore NotFound.
344                            t!("backend returned nothing");
345                        } else {
346                            t!("backend returned: {}", err2);
347                            err = Some(err2)
348                        }
349                    }
350                    Ok(c) => {
351                        let mut c = $to_vec(c);
352                        t!("backend returned {}",
353                           c.iter()
354                               .map(|cert| cert.fingerprint().to_string())
355                               .collect::<Vec<String>>()
356                               .join(", "));
357                        certs.append(&mut c)
358                    }
359                }
360            }
361
362            // If we found a cert after the AccessMode::Always round,
363            // don't consult the AccessMode::OnMiss backends.
364            if mode == AccessMode::Always && ! certs.is_empty() {
365                break;
366            }
367        }
368
369        if certs.is_empty() {
370            if let Some(ks) = $self.keyserver.as_ref() {
371                if let Ok(c) = ks.$method($($args),*) {
372                    certs = $to_vec(c);
373                    t!("keyserver returned {}",
374                       certs.iter()
375                           .map(|cert| cert.fingerprint().to_string())
376                           .collect::<Vec<String>>()
377                           .join(", "));
378                }
379            }
380        }
381
382        if certs.is_empty() {
383            if let Some(err) = err {
384                t!("query failed: {}", err);
385                Err(err)
386            } else {
387                t!("query returned nothing");
388                Ok(certs)
389            }
390        } else {
391            t!("query returned {}",
392               certs.iter()
393                   .map(|cert| cert.fingerprint().to_string())
394                   .collect::<Vec<String>>()
395                   .join(", "));
396            Ok(certs)
397        }
398    }};
399
400    ( $method:ident, $self:expr, $($args:ident),* ) => {{
401        forward!($method,
402                 append:|c| c,
403                 $self,
404                 $($args),*)
405    }}
406}
407
408fn merge<'a, 'b>(mut certs: Vec<Arc<LazyCert<'a>>>)
409    -> Vec<Arc<LazyCert<'a>>>
410{
411    certs.sort_by_key(|cert| cert.fingerprint());
412    certs.dedup_by(|a, b| {
413        // If this returns true, a is dropped.  So merge into b.
414        if a.fingerprint() == b.fingerprint() {
415            if let Ok(a2) = a.to_cert() {
416                if let Ok(b2) = b.to_cert() {
417                    *b = Arc::new(LazyCert::from(
418                        b2.clone()
419                            .merge_public(a2.clone())
420                            .expect("Same certificate")));
421                } else {
422                    // b is invalid, but a is valid.  Just keep a.
423                    *b = Arc::new(LazyCert::from(a2.clone()));
424                }
425            } else {
426                // a is invalid.  By returning true, we drop a.
427                // That's what we want.
428            }
429            true
430        } else {
431            false
432        }
433    });
434    certs
435}
436
437impl<'a> store::Store<'a> for CertStore<'a> {
438    fn lookup_by_cert(&self, kh: &KeyHandle)
439        -> Result<Vec<Arc<LazyCert<'a>>>>
440    {
441        let certs = forward!(lookup_by_cert, self, kh, kh)?;
442        if certs.is_empty() {
443            Err(StoreError::NotFound(kh.clone()).into())
444        } else {
445            Ok(merge(certs))
446        }
447    }
448
449    fn lookup_by_cert_fpr(&self, fingerprint: &Fingerprint)
450        -> Result<Arc<LazyCert<'a>>>
451    {
452        let certs = forward!(lookup_by_cert_fpr,
453                             append:|c| vec![c],
454                             self, fingerprint, fingerprint)?;
455        // There may be multiple variants.  Merge them.
456        let certs = merge(certs);
457        assert!(certs.len() <= 1);
458        if let Some(cert) = certs.into_iter().next() {
459            Ok(cert)
460        } else {
461            Err(StoreError::NotFound(
462                KeyHandle::from(fingerprint.clone())).into())
463        }
464    }
465
466    fn lookup_by_cert_or_subkey(&self, kh: &KeyHandle)
467        -> Result<Vec<Arc<LazyCert<'a>>>>
468    {
469        let certs = forward!(lookup_by_cert_or_subkey, self, kh, kh)?;
470        if certs.is_empty() {
471            Err(StoreError::NotFound(kh.clone()).into())
472        } else {
473            Ok(merge(certs))
474        }
475    }
476
477    fn select_userid(&self, query: &UserIDQueryParams, pattern: &str)
478        -> Result<Vec<Arc<LazyCert<'a>>>>
479    {
480        let certs = forward!(select_userid, self, pattern, query, pattern)?;
481        if certs.is_empty() {
482            Err(StoreError::NoMatches(pattern.to_string()).into())
483        } else {
484            Ok(merge(certs))
485        }
486    }
487
488    fn lookup_by_userid(&self, userid: &UserID)
489        -> Result<Vec<Arc<LazyCert<'a>>>>
490    {
491        let certs = forward!(lookup_by_userid, self, userid, userid)?;
492        if certs.is_empty() {
493            Err(StoreError::NoMatches(
494                String::from_utf8_lossy(userid.value()).to_string()).into())
495        } else {
496            Ok(merge(certs))
497        }
498    }
499
500    fn grep_userid(&self, pattern: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
501        let certs = forward!(grep_userid, self, pattern, pattern)?;
502        if certs.is_empty() {
503            Err(StoreError::NoMatches(pattern.to_string()).into())
504        } else {
505            Ok(merge(certs))
506        }
507    }
508
509    fn lookup_by_email(&self, email: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
510        let certs = forward!(lookup_by_email, self, email, email)?;
511        if certs.is_empty() {
512            Err(StoreError::NoMatches(email.to_string()).into())
513        } else {
514            Ok(merge(certs))
515        }
516    }
517
518    fn grep_email(&self, pattern: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
519        let certs = forward!(grep_email, self, pattern, pattern)?;
520        if certs.is_empty() {
521            Err(StoreError::NoMatches(pattern.to_string()).into())
522        } else {
523            Ok(merge(certs))
524        }
525    }
526
527    fn lookup_by_email_domain(&self, domain: &str)
528        -> Result<Vec<Arc<LazyCert<'a>>>>
529    {
530        let certs = forward!(lookup_by_email_domain, self, domain, domain)?;
531        if certs.is_empty() {
532            Err(StoreError::NoMatches(domain.to_string()).into())
533        } else {
534            Ok(merge(certs))
535        }
536    }
537
538    fn fingerprints(&self) -> Box<dyn Iterator<Item=Fingerprint> + 'a> {
539        let mut certs = Vec::new();
540
541        match self.certd.as_ref() {
542            #[cfg(certd)]
543            Ok(certd) => certs.extend(certd.fingerprints()),
544            #[cfg(not(certd))]
545            Ok(_) => unreachable!(),
546            Err(in_memory) => certs.extend(in_memory.fingerprints()),
547        };
548
549        for (backend, mode) in self.backends.iter() {
550            if mode != &AccessMode::Always {
551                continue;
552            }
553
554            certs.extend(backend.fingerprints());
555        }
556
557        certs.sort();
558        certs.dedup();
559
560        Box::new(certs.into_iter())
561    }
562
563    fn certs<'b>(&'b self) -> Box<dyn Iterator<Item=Arc<LazyCert<'a>>> + 'b>
564        where 'a: 'b
565    {
566        let mut certs = Vec::new();
567
568        match self.certd {
569            #[cfg(certd)]
570            Ok(ref certd) => certs.extend(certd.certs()),
571            #[cfg(not(certd))]
572            Ok(_) => unreachable!(),
573            Err(ref in_memory) => certs.extend(in_memory.certs()),
574        };
575
576        for (backend, mode) in self.backends.iter() {
577            if mode != &AccessMode::Always {
578                continue;
579            }
580
581            certs.extend(backend.certs());
582        }
583
584        let certs = merge(certs);
585
586        Box::new(certs.into_iter())
587    }
588
589    fn prefetch_all(&self) {
590        match self.certd.as_ref() {
591            #[cfg(certd)]
592            Ok(certd) => certd.prefetch_all(),
593            #[cfg(not(certd))]
594            Ok(_) => unreachable!(),
595            Err(in_memory) => in_memory.prefetch_all(),
596        };
597
598        for (backend, _mode) in self.backends.iter() {
599            backend.prefetch_all();
600        }
601    }
602
603    fn prefetch_some(&self, certs: &[KeyHandle]) {
604        match self.certd.as_ref() {
605            #[cfg(certd)]
606            Ok(certd) => certd.prefetch_some(certs),
607            #[cfg(not(certd))]
608            Ok(_) => unreachable!(),
609            Err(in_memory) => in_memory.prefetch_some(certs),
610        };
611
612        for (backend, _mode) in self.backends.iter() {
613            backend.prefetch_some(certs);
614        }
615    }
616}
617
618impl<'a> store::StoreUpdate<'a> for CertStore<'a> {
619    fn update_by(&self, cert: Arc<LazyCert<'a>>,
620                 merge_strategy: &dyn MergeCerts<'a>)
621        -> Result<Arc<LazyCert<'a>>>
622    {
623        tracer!(TRACE, "CertStore::update_by");
624        match self.certd.as_ref() {
625            #[cfg(certd)]
626            Ok(certd) => {
627                t!("Forwarding to underlying certd");
628                certd.update_by(cert, merge_strategy)
629            }
630            #[cfg(not(certd))]
631            Ok(_) => unreachable!(),
632            Err(in_memory) => {
633                t!("Forwarding to underlying in-memory DB");
634                in_memory.update_by(cert, merge_strategy)
635            }
636        }
637    }
638}
639
640impl<'a> CertStore<'a> {
641    /// Flushes any modified certificates to the backing store.
642    ///
643    /// Currently, this flushes the key server cache to the underlying
644    /// cert-d, if any.  All other backends are currently expected to
645    /// work in a write-through manner.
646    ///
647    /// Note: this is called automatically when the `CertStore` is
648    /// dropped, but calling it explicitly allows for reporting of
649    /// errors.
650    pub fn flush(&mut self) -> Result<()> {
651        self.flush_impl()
652    }
653
654    #[cfg(not(certd))]
655    fn flush_impl(&mut self) -> Result<()> {
656        Ok(())
657    }
658
659    #[cfg(certd)]
660    fn flush_impl(&mut self) -> Result<()> {
661        // Sync the key server's cache to the backing store.
662        tracer!(TRACE, "CertStore::flush");
663        t!("flushing");
664
665        let certd = if let Ok(certd) = self.certd.as_mut() {
666            certd
667        } else {
668            // We don't have a writable backing store so we can't sync
669            // anything to it.  We're done.
670            t!("no certd, can't sync");
671            return Ok(());
672        };
673
674        let ks = if let Some(ks) = self.keyserver.as_mut() {
675            ks
676        } else {
677            // We don't have a key server.  There is clearly nothing
678            // to sync.
679            t!("no keyserver, can't sync");
680            return Ok(());
681        };
682
683        let mut flushed_certs = Vec::new();
684        let mut result = Ok(());
685        for c in ks.certs() {
686            let keyid = c.keyid();
687            if let Err(err) = certd.update(c.clone()) {
688                t!("syncing {} to the cert-d: {}", keyid, err);
689                if result.is_ok() {
690                    result = Err(err)
691                        .with_context(|| {
692                            format!("Flushing changes to {} to disk",
693                                    keyid)
694                        })
695                }
696            } else {
697                flushed_certs.push(c);
698            }
699        }
700
701        // Now purge the successfully flushed certs from the keyserver
702        // cache, so that we will not try to flush them again next
703        // time we're called.
704        ks.delete_from_cache(flushed_certs.iter().map(Arc::clone));
705
706        t!("Flushed {} certificates", flushed_certs.len());
707        result
708    }
709}
710
711impl<'a> Drop for CertStore<'a> {
712    fn drop(&mut self) {
713        let _ = self.flush();
714    }
715}