use crate::connector::ConnectorOptions;
use crate::tls;
use rustls_webpki::types::{CertificateDer, PrivateKeyDer};
use std::io::{self, BufReader, ErrorKind};
use std::path::PathBuf;
use tokio_rustls::rustls::{ClientConfig, RootCertStore};
pub(crate) async fn load_certs(path: PathBuf) -> io::Result<Vec<CertificateDer<'static>>> {
tokio::task::spawn_blocking(move || {
let file = std::fs::File::open(path)?;
let mut reader = BufReader::new(file);
rustls_pemfile::certs(&mut reader).collect::<io::Result<Vec<_>>>()
})
.await?
}
pub(crate) async fn load_key(path: PathBuf) -> io::Result<PrivateKeyDer<'static>> {
tokio::task::spawn_blocking(move || {
let file = std::fs::File::open(path)?;
let mut reader = BufReader::new(file);
rustls_pemfile::private_key(&mut reader)?.ok_or_else(|| {
io::Error::new(ErrorKind::NotFound, "could not find client key in the path")
})
})
.await?
}
pub(crate) async fn config_tls(options: &ConnectorOptions) -> io::Result<ClientConfig> {
let mut root_store = RootCertStore::empty();
if options.tls_client_config.is_some() || options.certificates.is_empty() {
let certs_iter = rustls_native_certs::load_native_certs().map_err(|err| {
io::Error::new(
ErrorKind::Other,
format!("could not load platform certs: {err}"),
)
})?;
root_store.add_parsable_certificates(certs_iter);
}
let tls_config = {
if let Some(config) = &options.tls_client_config {
Ok(config.to_owned())
} else {
for cafile in &options.certificates {
let trust_anchors = load_certs(cafile.to_owned())
.await?
.into_iter()
.map(|cert| {
rustls_webpki::anchor_from_trusted_cert(&cert).map(|ta| ta.to_owned())
})
.collect::<Result<Vec<_>, rustls_webpki::Error>>()
.map_err(|err| {
io::Error::new(
ErrorKind::InvalidInput,
format!("could not load certs: {err}"),
)
})?;
root_store.extend(trust_anchors);
}
let builder = ClientConfig::builder().with_root_certificates(root_store);
if let Some(cert) = options.client_cert.clone() {
if let Some(key) = options.client_key.clone() {
let key = tls::load_key(key).await?;
let cert = tls::load_certs(cert).await?;
builder.with_client_auth_cert(cert, key).map_err(|_| {
io::Error::new(ErrorKind::Other, "could not add certificate or key")
})
} else {
Err(io::Error::new(
ErrorKind::Other,
"found certificate, but no key",
))
}
} else {
Ok(builder.with_no_client_auth())
}
}
}?;
Ok(tls_config)
}