use std::path::Path;
use std::sync::Arc;
use anyhow::Context;
use sequoia_openpgp as openpgp;
use openpgp::cert::raw::RawCertParser;
use openpgp::Fingerprint;
use openpgp::KeyHandle;
use openpgp::packet::UserID;
use openpgp::parse::Parse;
use openpgp::Result;
use crate::LazyCert;
use crate::store;
use store::Certs;
use store::MergeCerts;
use store::Store;
use store::StoreError;
use store::StoreUpdate;
use store::UserIDQueryParams;
use crate::TRACE;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum AccessMode {
Always,
OnMiss,
}
pub struct CertStore<'a> {
certd: std::result::Result<store::CertD<'a>, store::Certs<'a>>,
backends: Vec<(Box<dyn store::Store<'a> + 'a>, AccessMode)>,
keyserver: Option<Box<store::KeyServer<'a>>>,
}
impl<'a> CertStore<'a> {
pub fn empty() -> Self {
CertStore {
certd: Err(store::Certs::empty()),
backends: Vec::new(),
keyserver: None,
}
}
pub fn new() -> Result<Self> {
Ok(CertStore {
certd: Ok(store::CertD::open_default()?),
backends: Vec::new(),
keyserver: None,
})
}
pub fn readonly() -> Result<Self> {
let mut cert_store = CertStore {
certd: Err(store::Certs::empty()),
backends: Vec::new(),
keyserver: None,
};
cert_store.add_default_certd()?;
Ok(cert_store)
}
pub fn open<P>(path: P) -> Result<Self>
where P: AsRef<Path>
{
let path = path.as_ref();
Ok(CertStore {
certd: Ok(store::CertD::open(path)?),
backends: Vec::new(),
keyserver: None,
})
}
pub fn open_readonly<P>(path: P) -> Result<Self>
where P: AsRef<Path>
{
let path = path.as_ref();
let mut cert_store = CertStore {
certd: Err(store::Certs::empty()),
backends: Vec::new(),
keyserver: None,
};
cert_store.add_certd(path)?;
Ok(cert_store)
}
pub fn add_backend(&mut self, backend: Box<dyn store::Store<'a> + 'a>,
mode: AccessMode)
-> &mut Self
{
self.backends.push((backend, mode));
self
}
pub fn add_certd<P>(&mut self, path: P) -> Result<&mut Self>
where P: AsRef<Path>
{
let path = path.as_ref();
self.add_backend(Box::new(store::CertD::open(path)?),
AccessMode::Always);
Ok(self)
}
pub fn add_default_certd(&mut self) -> Result<&mut Self>
{
self.add_backend(Box::new(store::CertD::open_default()?),
AccessMode::Always);
Ok(self)
}
pub fn add_keyring<P>(&mut self, path: P) -> Result<&mut Self>
where P: AsRef<Path>
{
self.add_keyrings(std::iter::once(path))?;
Ok(self)
}
pub fn add_keyrings<I, P>(&mut self, filenames: I) -> Result<&mut Self>
where P: AsRef<Path>,
I: IntoIterator<Item=P>,
{
let mut keyring = Certs::empty();
let mut error = None;
for filename in filenames {
let filename = filename.as_ref();
let f = std::fs::File::open(filename)
.with_context(|| format!("Open {:?}", filename))?;
let parser = RawCertParser::from_reader(f)
.with_context(|| format!("Parsing {:?}", filename))?;
for cert in parser {
match cert {
Ok(cert) => {
keyring.update(Arc::new(cert.into()))
.expect("implementation doesn't fail");
}
Err(err) => {
eprint!("Parsing certificate in {:?}: {}",
filename, err);
error = Some(err);
}
}
}
}
if let Some(err) = error {
return Err(err).context("Parsing keyrings");
}
self.add_backend(
Box::new(keyring),
AccessMode::Always);
Ok(self)
}
pub fn add_keyserver(&mut self, url: &str) -> Result<&mut Self>
{
self.keyserver = Some(Box::new(store::KeyServer::new(url)?));
Ok(self)
}
pub fn add_keyserver_backend(&mut self, ks: store::KeyServer<'a>)
-> Result<&mut Self>
{
self.keyserver = Some(Box::new(ks));
Ok(self)
}
pub fn certd(&self) -> Option<&store::CertD<'a>> {
self.certd.as_ref().ok()
}
pub fn certd_mut(&mut self) -> Option<&mut store::CertD<'a>> {
self.certd.as_mut().ok()
}
}
macro_rules! forward {
( $method:ident, append:$to_vec:expr, $self:expr, $term:expr, $($args:ident),* ) => {{
tracer!(TRACE, format!("{}({})", stringify!($method), $term));
let mut certs = Vec::new();
let mut err = None;
match &$self.certd {
Ok(certd) => {
match certd.$method($($args),*) {
Err(err2) => {
if let Some(StoreError::NotFound(_))
= err2.downcast_ref::<StoreError>()
{
t!("certd returned nothing");
} else {
t!("certd returned: {}", err2);
err = Some(err2)
}
}
Ok(c) => {
let mut c = $to_vec(c);
t!("certd returned {}",
c.iter()
.map(|cert| cert.fingerprint().to_string())
.collect::<Vec<String>>()
.join(", "));
certs.append(&mut c)
}
}
}
Err(in_memory) => {
match in_memory.$method($($args),*) {
Err(err2) => {
if let Some(StoreError::NotFound(_))
= err2.downcast_ref::<StoreError>()
{
t!("in-memory returned nothing");
} else {
t!("in-memory returned: {}", err2);
err = Some(err2)
}
}
Ok(c) => {
let mut c = $to_vec(c);
t!("in-memory returned {}",
c.iter()
.map(|cert| cert.fingerprint().to_string())
.collect::<Vec<String>>()
.join(", "));
certs.append(&mut c)
}
}
}
}
for mode in [AccessMode::Always, AccessMode::OnMiss] {
for (backend, _) in $self.backends.iter()
.filter(|(_, m)| &mode == m)
{
match backend.$method($($args),*) {
Err(err2) => {
if let Some(StoreError::NotFound(_))
= err2.downcast_ref::<StoreError>()
{
t!("backend returned nothing");
} else {
t!("backend returned: {}", err2);
err = Some(err2)
}
}
Ok(c) => {
let mut c = $to_vec(c);
t!("backend returned {}",
c.iter()
.map(|cert| cert.fingerprint().to_string())
.collect::<Vec<String>>()
.join(", "));
certs.append(&mut c)
}
}
}
if mode == AccessMode::Always && ! certs.is_empty() {
break;
}
}
if certs.is_empty() {
if let Some(ks) = $self.keyserver.as_ref() {
if let Ok(c) = ks.$method($($args),*) {
certs = $to_vec(c);
t!("keyserver returned {}",
certs.iter()
.map(|cert| cert.fingerprint().to_string())
.collect::<Vec<String>>()
.join(", "));
}
}
}
if certs.is_empty() {
if let Some(err) = err {
t!("query failed: {}", err);
Err(err)
} else {
t!("query returned nothing");
Ok(certs)
}
} else {
t!("query returned {}",
certs.iter()
.map(|cert| cert.fingerprint().to_string())
.collect::<Vec<String>>()
.join(", "));
Ok(certs)
}
}};
( $method:ident, $self:expr, $($args:ident),* ) => {{
forward!($method,
append:|c| c,
$self,
$($args),*)
}}
}
fn merge<'a, 'b>(mut certs: Vec<Arc<LazyCert<'a>>>)
-> Vec<Arc<LazyCert<'a>>>
{
certs.sort_by_key(|cert| cert.fingerprint());
certs.dedup_by(|a, b| {
if a.fingerprint() == b.fingerprint() {
if let Ok(a2) = a.to_cert() {
if let Ok(b2) = b.to_cert() {
*b = Arc::new(LazyCert::from(
b2.clone()
.merge_public(a2.clone())
.expect("Same certificate")));
} else {
*b = Arc::new(LazyCert::from(a2.clone()));
}
} else {
}
true
} else {
false
}
});
certs
}
impl<'a> store::Store<'a> for CertStore<'a> {
fn lookup_by_cert(&self, kh: &KeyHandle)
-> Result<Vec<Arc<LazyCert<'a>>>>
{
let certs = forward!(lookup_by_cert, self, kh, kh)?;
if certs.is_empty() {
Err(StoreError::NotFound(kh.clone()).into())
} else {
Ok(merge(certs))
}
}
fn lookup_by_cert_fpr(&self, fingerprint: &Fingerprint)
-> Result<Arc<LazyCert<'a>>>
{
let certs = forward!(lookup_by_cert_fpr,
append:|c| vec![c],
self, fingerprint, fingerprint)?;
let certs = merge(certs);
assert!(certs.len() <= 1);
if let Some(cert) = certs.into_iter().next() {
Ok(cert)
} else {
Err(StoreError::NotFound(
KeyHandle::from(fingerprint.clone())).into())
}
}
fn lookup_by_cert_or_subkey(&self, kh: &KeyHandle)
-> Result<Vec<Arc<LazyCert<'a>>>>
{
let certs = forward!(lookup_by_cert_or_subkey, self, kh, kh)?;
if certs.is_empty() {
Err(StoreError::NotFound(kh.clone()).into())
} else {
Ok(merge(certs))
}
}
fn select_userid(&self, query: &UserIDQueryParams, pattern: &str)
-> Result<Vec<Arc<LazyCert<'a>>>>
{
let certs = forward!(select_userid, self, pattern, query, pattern)?;
if certs.is_empty() {
Err(StoreError::NoMatches(pattern.to_string()).into())
} else {
Ok(merge(certs))
}
}
fn lookup_by_userid(&self, userid: &UserID)
-> Result<Vec<Arc<LazyCert<'a>>>>
{
let certs = forward!(lookup_by_userid, self, userid, userid)?;
if certs.is_empty() {
Err(StoreError::NoMatches(
String::from_utf8_lossy(userid.value()).to_string()).into())
} else {
Ok(merge(certs))
}
}
fn grep_userid(&self, pattern: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
let certs = forward!(grep_userid, self, pattern, pattern)?;
if certs.is_empty() {
Err(StoreError::NoMatches(pattern.to_string()).into())
} else {
Ok(merge(certs))
}
}
fn lookup_by_email(&self, email: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
let certs = forward!(lookup_by_email, self, email, email)?;
if certs.is_empty() {
Err(StoreError::NoMatches(email.to_string()).into())
} else {
Ok(merge(certs))
}
}
fn grep_email(&self, pattern: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
let certs = forward!(grep_email, self, pattern, pattern)?;
if certs.is_empty() {
Err(StoreError::NoMatches(pattern.to_string()).into())
} else {
Ok(merge(certs))
}
}
fn lookup_by_email_domain(&self, domain: &str)
-> Result<Vec<Arc<LazyCert<'a>>>>
{
let certs = forward!(lookup_by_email_domain, self, domain, domain)?;
if certs.is_empty() {
Err(StoreError::NoMatches(domain.to_string()).into())
} else {
Ok(merge(certs))
}
}
fn fingerprints(&self) -> Box<dyn Iterator<Item=Fingerprint> + 'a> {
let mut certs = Vec::new();
match self.certd.as_ref() {
Ok(certd) => certs.extend(certd.fingerprints()),
Err(in_memory) => certs.extend(in_memory.fingerprints()),
};
for (backend, mode) in self.backends.iter() {
if mode != &AccessMode::Always {
continue;
}
certs.extend(backend.fingerprints());
}
certs.sort();
certs.dedup();
Box::new(certs.into_iter())
}
fn certs<'b>(&'b self) -> Box<dyn Iterator<Item=Arc<LazyCert<'a>>> + 'b>
where 'a: 'b
{
let mut certs = Vec::new();
match self.certd {
Ok(ref certd) => certs.extend(certd.certs()),
Err(ref in_memory) => certs.extend(in_memory.certs()),
};
for (backend, mode) in self.backends.iter() {
if mode != &AccessMode::Always {
continue;
}
certs.extend(backend.certs());
}
let certs = merge(certs);
Box::new(certs.into_iter())
}
fn prefetch_all(&mut self) {
match self.certd.as_mut() {
Ok(certd) => certd.prefetch_all(),
Err(in_memory) => in_memory.prefetch_all(),
};
for (backend, _mode) in self.backends.iter_mut() {
backend.prefetch_all();
}
}
fn prefetch_some(&mut self, certs: &[KeyHandle]) {
match self.certd.as_mut() {
Ok(certd) => certd.prefetch_some(certs),
Err(in_memory) => in_memory.prefetch_some(certs),
};
for (backend, _mode) in self.backends.iter_mut() {
backend.prefetch_some(certs);
}
}
}
impl<'a> store::StoreUpdate<'a> for CertStore<'a> {
fn update_by(&mut self, cert: Arc<LazyCert<'a>>,
merge_strategy: &mut dyn MergeCerts<'a>)
-> Result<Arc<LazyCert<'a>>>
{
tracer!(TRACE, "CertStore::update_by");
match self.certd.as_mut() {
Ok(certd) => {
t!("Forwarding to underlying certd");
certd.update_by(cert, merge_strategy)
}
Err(in_memory) => {
t!("Forwarding to underlying in-memory DB");
in_memory.update_by(cert, merge_strategy)
}
}
}
}
impl<'a> CertStore<'a> {
pub fn flush(&mut self) -> Result<()> {
tracer!(TRACE, "CertStore::flush");
t!("flushing");
let certd = if let Ok(certd) = self.certd.as_mut() {
certd
} else {
t!("no certd, can't sync");
return Ok(());
};
let ks = if let Some(ks) = self.keyserver.as_mut() {
ks
} else {
t!("no keyserver, can't sync");
return Ok(());
};
let mut flushed_certs = Vec::new();
let mut result = Ok(());
for c in ks.certs() {
let keyid = c.keyid();
if let Err(err) = certd.update(c.clone()) {
t!("syncing {} to the cert-d: {}", keyid, err);
if result.is_ok() {
result = Err(err)
.with_context(|| {
format!("Flushing changes to {} to disk",
keyid)
})
}
} else {
flushed_certs.push(c);
}
}
flushed_certs.iter().for_each(|c| ks.delete_from_cache(c));
t!("Flushed {} certificates", flushed_certs.len());
result
}
}
impl<'a> Drop for CertStore<'a> {
fn drop(&mut self) {
let _ = self.flush();
}
}