Skip to content

Commit

Permalink
draft: handle certificate name override upon reception
Browse files Browse the repository at this point in the history
  • Loading branch information
Keksoj committed Feb 13, 2024
1 parent 76d4b7f commit 0de7f13
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 19 deletions.
24 changes: 24 additions & 0 deletions command/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,27 @@ pub fn load_full_certificate(
names,
})
}

impl CertificateAndKey {
pub fn fingerprint(&self) -> Result<Fingerprint, CertificateError> {
let pem = parse_pem(self.certificate.as_bytes())?;
let fingerprint = Fingerprint(Sha256::digest(pem.contents).iter().cloned().collect());
Ok(fingerprint)
}

pub fn get_overriding_names(&self) -> Result<Vec<String>, CertificateError> {
if self.names.is_empty() {
let pem = parse_pem(self.certificate.as_bytes())?;
let overriding_names = get_cn_and_san_attributes(&pem.contents)?;

Ok(overriding_names.into_iter().collect())
} else {
Ok(self.names.to_owned())
}
}

pub fn apply_overriding_names(&mut self) -> Result<(), CertificateError> {
self.names = self.get_overriding_names()?;
Ok(())
}
}
5 changes: 4 additions & 1 deletion command/src/command.proto
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,10 @@ message CertificateAndKey {
repeated string certificate_chain = 2;
required string key = 3;
repeated TlsVersion versions = 4;
// hostnames linked to the certificate
// this field overrides the certificate names
// TODO: find a proper way to document this, for instance:
// "if empty, there is no override"
// comment should be consistent with CertificateResolver::add_certificate
repeated string names = 5;
}

Expand Down
20 changes: 12 additions & 8 deletions command/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::{
use prost::{DecodeError, Message};

use crate::{
certificate::{self, calculate_fingerprint, Fingerprint},
certificate::{self, calculate_fingerprint, CertificateError, Fingerprint},
proto::{
command::{
request::RequestType, ActivateListener, AddBackend, AddCertificate, CertificateAndKey,
Expand Down Expand Up @@ -47,7 +47,7 @@ pub enum StateError {
#[error("Wrong request: {0}")]
WrongRequest(String),
#[error("Could not add certificate: {0}")]
AddCertificate(String),
AddCertificate(CertificateError),
#[error("Could not remove certificate: {0}")]
RemoveCertificate(String),
#[error("Could not replace certificate: {0}")]
Expand Down Expand Up @@ -374,11 +374,15 @@ impl ConfigState {
}

fn add_certificate(&mut self, add: &AddCertificate) -> Result<(), StateError> {
let fingerprint = Fingerprint(
calculate_fingerprint(add.certificate.certificate.as_bytes()).map_err(
|fingerprint_err| StateError::AddCertificate(fingerprint_err.to_string()),
)?,
);
let overriding_names = add
.certificate
.get_overriding_names()
.map_err(StateError::AddCertificate)?;

let fingerprint = add
.certificate
.fingerprint()
.map_err(StateError::AddCertificate)?;

let entry = self
.certificates
Expand All @@ -388,7 +392,7 @@ impl ConfigState {
if entry.contains_key(&fingerprint) {
info!(
"Skip loading of certificate '{}' for domain '{}' on listener '{}', the certificate is already present.",
fingerprint, add.certificate.names.join(", "), add.address
fingerprint, overriding_names.join(", "), add.address
);
return Ok(());
}
Expand Down
23 changes: 13 additions & 10 deletions lib/src/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@ impl From<&AddCertificate> for CertificateOverride {
#[derive(Clone, Debug)]
pub struct CertifiedKeyWrapper {
inner: Arc<CertifiedKey>,
/// domain names (can be overriden)
/// domain names, override what can be found in the cert
names: Vec<String>,
expiration: i64,
// TODO: add field fingerprint
}

impl CertifiedKeyWrapper {
Expand All @@ -100,6 +101,10 @@ impl CertifiedKeyWrapper {
fn names(&self) -> &[String] {
&self.names
}

fn fingerprint(&self) -> Fingerprint {
Fingerprint(Sha256::digest(self.pem_bytes()).iter().cloned().collect())
}
}

/// Convert an AddCertificate request into the Rustls format.
Expand Down Expand Up @@ -139,11 +144,13 @@ impl TryFrom<&AddCertificate> for CertifiedKeyWrapper {
_ => return Err(CertificateResolverError::EmptyKeys),
};

let overriding_names = cert.get_overriding_names()?;

match any_supported_type(&private_key) {
Ok(signing_key) => {
let stored_certificate = CertifiedKeyWrapper {
inner: Arc::new(CertifiedKey::new(chain, signing_key)),
names: cert.names.clone(),
names: overriding_names,
expiration,
};
Ok(stored_certificate)
Expand Down Expand Up @@ -214,7 +221,7 @@ impl CertificateResolver {
certificate_to_add
);

let fingerprint = fingerprint(certificate_to_add.pem_bytes());
let fingerprint = certificate_to_add.fingerprint();

let (should_insert, outdated_certs) = self.should_insert(&certificate_to_add)?;

Expand Down Expand Up @@ -444,6 +451,8 @@ impl CertificateResolver {

let mut related_certificates = HashSet::new();

// TODO: make sure the tests give names or should_insert will fail
// or recalculate names to add
for name in candidate_cert.names() {
match self.name_fingerprint_idx.get(name) {
None => should_insert = true,
Expand All @@ -452,10 +461,6 @@ impl CertificateResolver {
}
}

if related_certificates.is_empty() {
return Ok((true, Vec::new()));
}

let mut outdated_certificates = Vec::new();

for fingerprint in related_certificates {
Expand Down Expand Up @@ -848,9 +853,7 @@ mod tests {

let mut fingerprints = vec![];
for certificate in &certificates {
let pem = parse_pem(certificate.certificate.as_bytes())?;

fingerprints.push(fingerprint(&pem.contents));
fingerprints.push(certificate.fingerprint()?);
}

// randomize entries
Expand Down

0 comments on commit 0de7f13

Please sign in to comment.