use openssl::x509::X509;
use super::{
CertBackend, PgpCertBackend, PkiBackend, PkiBackendInner, SshCertBackend, TlsCertBackend, types,
};
use crate::{
errors::RvError,
logical::{Backend, Field, FieldType, Operation, Path, Request, Response},
modules::ResponseExt,
utils::cert::CertBundle,
};
impl PkiBackend {
pub fn fetch_ca_path(&self) -> Path {
let backend = self.inner.clone();
Path::builder()
.pattern(r"ca/(?P<cert_type>tls|ssh)(/pem)?")
.field(
"cert_type",
Field::builder()
.field_type(FieldType::Str)
.required(true)
.description("Certificate type: tls or ssh"),
)
.operation(Operation::Read, {
let handler = backend.clone();
move |backend, req| {
let handler = handler.clone();
Box::pin(async move { handler.dispatch_fetch_ca(backend, req).await })
}
})
.help("Fetch the CA certificate (TLS) or public key (SSH).")
.build()
}
pub fn fetch_crl_path(&self) -> Path {
let backend = self.inner.clone();
Path::builder()
.pattern("crl(/pem)?")
.operation(Operation::Read, {
let handler = backend.clone();
move |backend, req| {
let handler = handler.clone();
Box::pin(async move { handler.read_path_fetch_crl(backend, req).await })
}
})
.help("Fetch the CRL.")
.build()
}
pub fn fetch_cert_path(&self) -> Path {
let backend = self.inner.clone();
Path::builder()
.pattern(r"cert/(?P<cert_type>tls|ssh)/(?P<serial>[0-9A-Fa-f-:]+)")
.field(
"cert_type",
Field::builder()
.field_type(FieldType::Str)
.required(true)
.description("Certificate type: tls or ssh"),
)
.field(
"serial",
Field::builder()
.field_type(FieldType::Str)
.description("Certificate serial number"),
)
.operation(Operation::Read, {
let handler = backend.clone();
move |backend, req| {
let handler = handler.clone();
Box::pin(async move { handler.dispatch_fetch_cert(backend, req).await })
}
})
.help("Fetch a certificate by serial number.")
.build()
}
pub fn fetch_cert_crl_path(&self) -> Path {
let backend = self.inner.clone();
Path::builder()
.pattern("cert/crl")
.operation(Operation::Read, {
let handler = backend.clone();
move |backend, req| {
let handler = handler.clone();
Box::pin(async move { handler.read_path_fetch_cert_crl(backend, req).await })
}
})
.help("Fetch the CRL certificate.")
.build()
}
pub fn list_certs_path(&self) -> Path {
let backend = self.inner.clone();
Path::builder()
.pattern(r"certs/(?P<cert_type>tls|ssh|pgp)/?$")
.field(
"cert_type",
Field::builder()
.field_type(FieldType::Str)
.required(true)
.description("Certificate type: tls, ssh, or pgp"),
)
.operation(Operation::List, {
let handler = backend.clone();
move |backend, req| {
let handler = handler.clone();
Box::pin(async move { handler.dispatch_list_certs(backend, req).await })
}
})
.help("List certificates.")
.build()
}
}
impl PkiBackendInner {
pub async fn dispatch_fetch_ca(
&self,
_backend: &dyn Backend,
req: &mut Request,
) -> Result<Option<Response>, RvError> {
let ct = req.get_data("cert_type")?;
let ct = ct.as_str().ok_or(RvError::ErrRequestFieldInvalid)?;
match ct {
"tls" => self.read_path_fetch_ca(_backend, req).await,
"ssh" => self.read_path_fetch_ssh_ca(_backend, req).await,
_ => Err(RvError::ErrRequestFieldInvalid),
}
}
pub async fn dispatch_fetch_cert(
&self,
_backend: &dyn Backend,
req: &mut Request,
) -> Result<Option<Response>, RvError> {
let ct = req.get_data("cert_type")?;
let ct = ct.as_str().ok_or(RvError::ErrRequestFieldInvalid)?;
match ct {
"tls" => self.read_path_fetch_cert(_backend, req).await,
"ssh" => self.read_path_fetch_ssh_cert(_backend, req).await,
_ => Err(RvError::ErrRequestFieldInvalid),
}
}
pub async fn dispatch_list_certs(
&self,
_backend: &dyn Backend,
req: &mut Request,
) -> Result<Option<Response>, RvError> {
let ct = req.get_data("cert_type")?;
let ct = ct.as_str().ok_or(RvError::ErrRequestFieldInvalid)?;
match ct {
"tls" => self.list_certs(_backend, req).await,
"ssh" => self.list_ssh_certs(_backend, req).await,
"pgp" => self.list_pgp_keys(_backend, req).await,
_ => Err(RvError::ErrRequestFieldInvalid),
}
}
pub async fn handle_fetch_cert_bundle(
&self,
cert_bundle: &CertBundle,
) -> Result<Option<Response>, RvError> {
let ca_chain_pem: String = cert_bundle
.ca_chain
.iter()
.rev()
.map(|x509| x509.to_pem().unwrap())
.map(|pem| String::from_utf8_lossy(&pem).to_string())
.collect::<Vec<String>>()
.join("");
let response = types::FetchCaResponse {
certificate: String::from_utf8_lossy(&cert_bundle.certificate.to_pem()?).to_string(),
ca_chain: Some(ca_chain_pem),
issuing_ca: None,
serial_number: Some(cert_bundle.serial_number.clone()),
};
Ok(Some(Response::data_response(response.to_map()?)))
}
pub async fn read_path_fetch_ca(
&self,
_backend: &dyn Backend,
req: &mut Request,
) -> Result<Option<Response>, RvError> {
let ca_bundle = self.fetch_ca_bundle(req).await?;
self.handle_fetch_cert_bundle(&ca_bundle).await
}
pub async fn read_path_fetch_ssh_ca(
&self,
_backend: &dyn Backend,
req: &mut Request,
) -> Result<Option<Response>, RvError> {
let bundle = self.fetch_ssh_ca_bundle(req).await?;
let response = types::SshConfigCaResponse {
public_key: bundle.public_key_openssh,
};
Ok(Some(Response::data_response(response.to_map()?)))
}
pub async fn read_path_fetch_crl(
&self,
_backend: &dyn Backend,
_req: &mut Request,
) -> Result<Option<Response>, RvError> {
Ok(None)
}
pub async fn read_path_fetch_cert(
&self,
_backend: &dyn Backend,
req: &mut Request,
) -> Result<Option<Response>, RvError> {
let serial_number_value = req.get_data("serial")?;
let serial_number = serial_number_value
.as_str()
.ok_or(RvError::ErrRequestFieldInvalid)?;
let serial_number_hex = serial_number.replace(':', "-").to_lowercase();
let cert = self.fetch_cert(req, &serial_number_hex).await?;
let ca_bundle = self.fetch_ca_bundle(req).await?;
let mut ca_chain_pem: String = ca_bundle
.ca_chain
.iter()
.rev()
.map(|x509| x509.to_pem().unwrap())
.map(|pem| String::from_utf8_lossy(&pem).to_string())
.collect::<Vec<String>>()
.join("");
ca_chain_pem =
ca_chain_pem + &String::from_utf8_lossy(&ca_bundle.certificate.to_pem().unwrap());
let response = types::FetchCertificateResponse {
ca_chain: ca_chain_pem,
certificate: String::from_utf8_lossy(&cert.to_pem()?).to_string(),
serial_number: serial_number.to_string(),
};
Ok(Some(Response::data_response(response.to_map()?)))
}
pub async fn read_path_fetch_ssh_cert(
&self,
_backend: &dyn Backend,
req: &mut Request,
) -> Result<Option<Response>, RvError> {
let serial_value = req.get_data("serial")?;
let serial = serial_value
.as_str()
.ok_or(RvError::ErrRequestFieldInvalid)?;
let signed_key = self.fetch_ssh_cert(req, serial).await?;
let response = types::SshSignKeyResponse {
signed_key,
serial_number: serial.to_string(),
expiration: 0,
};
Ok(Some(Response::data_response(response.to_map()?)))
}
pub async fn read_path_fetch_cert_crl(
&self,
_backend: &dyn Backend,
_req: &mut Request,
) -> Result<Option<Response>, RvError> {
Ok(None)
}
pub async fn list_certs(
&self,
_backend: &dyn Backend,
req: &mut Request,
) -> Result<Option<Response>, RvError> {
let keys = req.storage_list("certs/tls/").await?;
Ok(Some(Response::list_response(&keys)))
}
pub async fn list_ssh_certs(
&self,
_backend: &dyn Backend,
req: &mut Request,
) -> Result<Option<Response>, RvError> {
let keys = req.storage_list("certs/ssh/").await?;
Ok(Some(Response::list_response(&keys)))
}
pub async fn list_pgp_keys(
&self,
_backend: &dyn Backend,
req: &mut Request,
) -> Result<Option<Response>, RvError> {
let keys = req.storage_list("certs/pgp/").await?;
Ok(Some(Response::list_response(&keys)))
}
pub async fn fetch_cert(&self, req: &Request, serial_number: &str) -> Result<X509, RvError> {
TlsCertBackend.fetch_cert(req, serial_number).await
}
pub async fn store_cert(
&self,
req: &Request,
serial_number: &str,
cert: &X509,
) -> Result<(), RvError> {
TlsCertBackend.store_cert(req, serial_number, cert).await
}
pub async fn delete_cert(&self, req: &Request, serial_number: &str) -> Result<(), RvError> {
TlsCertBackend.delete_cert(req, serial_number).await
}
pub async fn store_ssh_cert(
&self,
req: &Request,
serial_hex: &str,
signed_key: &String,
) -> Result<(), RvError> {
SshCertBackend.store_cert(req, serial_hex, signed_key).await
}
pub async fn fetch_ssh_cert(&self, req: &Request, serial_hex: &str) -> Result<String, RvError> {
SshCertBackend.fetch_cert(req, serial_hex).await
}
pub async fn fetch_pgp_key(
&self,
req: &Request,
key_name: &str,
) -> Result<types::PgpKeyBundle, RvError> {
PgpCertBackend.fetch_cert(req, key_name).await
}
}