use std::{collections::HashMap, sync::Arc};
use openssl::{
pkey::{Id, PKey},
x509::X509,
};
use pem;
use super::{PkiBackend, PkiBackendInner};
use crate::{
context::Context,
errors::RvError,
logical::{Backend, Field, FieldType, Operation, Path, PathOperation, Request, Response},
new_fields, new_fields_internal, new_path, new_path_internal,
storage::StorageEntry,
utils::{cert, cert::CertBundle},
};
impl PkiBackend {
pub fn config_ca_path(&self) -> Path {
let pki_backend_ref = Arc::clone(&self.inner);
let path = new_path!({
pattern: "config/ca",
fields: {
"pem_bundle": {
field_type: FieldType::Str,
description: "PEM-format, concatenated unencrypted secret key and certificate"
}
},
operations: [
{op: Operation::Write, handler: pki_backend_ref.write_path_ca}
],
help: r#"
This configures the CA information used for credentials
generated by this backend. This must be a PEM-format, concatenated
unencrypted secret key and certificate.
For security reasons, you can only view the certificate when reading this endpoint
"#
});
path
}
}
impl PkiBackendInner {
pub fn write_path_ca(&self, _backend: &dyn Backend, req: &mut Request) -> Result<Option<Response>, RvError> {
let pem_bundle_value = req.get_data("pem_bundle")?;
let pem_bundle = pem_bundle_value.as_str().ok_or(RvError::ErrRequestFieldInvalid)?;
let items = pem::parse_many(pem_bundle)?;
let mut key_found = false;
let mut i = 0;
let mut cert_bundle = CertBundle::default();
for item in items {
if item.tag() == "CERTIFICATE" {
let cert = X509::from_der(item.contents())?;
if !cert::is_ca_cert(&cert) {
return Err(RvError::ErrPkiPemBundleInvalid);
}
if i == 0 {
cert_bundle.certificate = cert;
} else {
cert_bundle.ca_chain.push(cert);
}
i += 1;
}
if item.tag() == "PRIVATE KEY" {
if key_found {
return Err(RvError::ErrPkiPemBundleInvalid);
}
let key = PKey::private_key_from_der(item.contents())?;
match key.id() {
Id::RSA => {
cert_bundle.private_key_type = "rsa".to_string();
}
Id::EC => {
cert_bundle.private_key_type = "ec".to_string();
}
Id::SM2 => {
cert_bundle.private_key_type = "sm2".to_string();
}
Id::ED25519 => {
cert_bundle.private_key_type = "ed25519".to_string();
}
_ => {
cert_bundle.private_key_type = "other".to_string();
}
}
cert_bundle.private_key = key;
key_found = true;
}
}
cert_bundle.verify()?;
self.store_ca_bundle(req, &cert_bundle)?;
let entry = StorageEntry { key: "crl".to_string(), value: Vec::new() };
req.storage_put(&entry)?;
Ok(None)
}
pub fn fetch_ca_bundle(&self, req: &Request) -> Result<CertBundle, RvError> {
let entry = req.storage_get("config/ca_bundle")?;
if entry.is_none() {
return Err(RvError::ErrPkiCaNotConfig);
}
let ca_bundle: CertBundle = serde_json::from_slice(entry.unwrap().value.as_slice())?;
Ok(ca_bundle)
}
pub fn store_ca_bundle(&self, req: &mut Request, ca_bundle: &CertBundle) -> Result<(), RvError> {
let mut entry = StorageEntry::new("config/ca_bundle", ca_bundle)?;
req.storage_put(&entry)?;
entry.key = "ca".to_string();
entry.value = ca_bundle.certificate.to_pem().unwrap();
req.storage_put(&entry)?;
let serial_number_hex = ca_bundle.serial_number.replace(":", "-").to_lowercase();
self.store_cert(req, &serial_number_hex, &ca_bundle.certificate)?;
Ok(())
}
pub fn delete_ca_bundle(&self, req: &Request) -> Result<(), RvError> {
let ca_bundle = self.fetch_ca_bundle(req)?;
let serial_number_hex = ca_bundle.serial_number.replace(":", "-").to_lowercase();
self.delete_cert(req, &serial_number_hex)?;
req.storage_delete("config/ca_bundle")?;
Ok(())
}
}