Improve error handling in client certificate handling

Yield errors instead of panicking. Avoid unnecessary allocations/cloning.
This commit is contained in:
Dirkjan Ochtman 2021-12-01 14:00:04 +01:00 committed by masalachai
parent af7911cbc3
commit cbd9fd84aa
2 changed files with 44 additions and 40 deletions

View File

@ -40,12 +40,15 @@
//! let tls = registry.tls_files().unwrap();
//! ```
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufReader, Seek, SeekFrom};
use rustls::{Certificate, PrivateKey};
use rustls_pemfile;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::io::{Seek, SeekFrom};
use std::{fs, io};
use crate::error::Error;
/// Paths to the client certificate and client key PEM files
#[derive(Serialize, Deserialize, Debug)]
@ -85,51 +88,52 @@ impl EppClientConnection {
self.ext_uris.as_ref()
}
/// Returns the parsed client certificate and private key for client TLS auth
pub fn tls_files(&self) -> Option<(Vec<Certificate>, PrivateKey)> {
let certificates = self.client_certificate();
let key = self.key();
if certificates == None || key == None {
None
} else {
Some((certificates.unwrap(), key.unwrap()))
pub fn tls_files(&self) -> Result<Option<(Vec<Certificate>, PrivateKey)>, Error> {
match (self.client_certificate()?, self.key()?) {
(Some(certificates), Some(key)) => Ok(Some((certificates, key))),
_ => Ok(None),
}
}
/// Parses the client certificate chain
fn client_certificate(&self) -> Option<Vec<Certificate>> {
self.tls_files.as_ref().map(|tls| {
rustls_pemfile::certs(&mut io::BufReader::new(
fs::File::open(tls.cert_chain.to_string()).unwrap(),
fn client_certificate(&self) -> Result<Option<Vec<Certificate>>, Error> {
let certs_file = match &self.tls_files {
Some(files) => &files.cert_chain,
None => return Ok(None),
};
Ok(Some(
rustls_pemfile::certs(&mut BufReader::new(File::open(certs_file)?))?
.into_iter()
.map(Certificate)
.collect::<Vec<_>>(),
))
.unwrap()
.iter()
.map(|v| Certificate(v.clone()))
.collect()
})
}
/// Parses the client private key
fn key(&self) -> Option<PrivateKey> {
self.tls_files.as_ref().map(|tls| {
let mut r = io::BufReader::new(fs::File::open(tls.key.to_string()).unwrap());
fn key(&self) -> Result<Option<PrivateKey>, Error> {
let key_file = match &self.tls_files {
Some(files) => &files.key,
None => return Ok(None),
};
let rsa_keys = rustls_pemfile::rsa_private_keys(&mut r).unwrap();
let mut r = BufReader::new(File::open(key_file).unwrap());
let mut rsa_keys = rustls_pemfile::rsa_private_keys(&mut r).unwrap();
if rsa_keys.len() > 1 {
warn!("Multiple RSA keys found in PEM file {}", tls.key);
} else if !rsa_keys.is_empty() {
return rustls::PrivateKey(rsa_keys[0].clone());
warn!("Multiple RSA keys found in PEM file {}", key_file);
} else if let Some(key) = rsa_keys.pop() {
return Ok(Some(rustls::PrivateKey(key)));
}
r.seek(SeekFrom::Start(0)).unwrap();
r.seek(SeekFrom::Start(0))?;
let pkcs8_keys = rustls_pemfile::pkcs8_private_keys(&mut r).unwrap();
let mut pkcs8_keys = rustls_pemfile::pkcs8_private_keys(&mut r).unwrap();
if pkcs8_keys.len() > 1 {
warn!("Multiple PKCS8 keys found in PEM file {}", tls.key);
} else if !pkcs8_keys.is_empty() {
return rustls::PrivateKey(pkcs8_keys[0].clone());
warn!("Multiple PKCS8 keys found in PEM file {}", key_file);
} else if let Some(key) = pkcs8_keys.pop() {
return Ok(Some(rustls::PrivateKey(key)));
}
panic!("No private key found in PEM file");
})
Err(Error::Other("No private key found in PEM file".to_owned()))
}
}

View File

@ -137,7 +137,7 @@ pub async fn epp_connect(
.with_safe_defaults()
.with_root_certificates(roots);
let config = match registry_creds.tls_files() {
let config = match registry_creds.tls_files()? {
Some((cert_chain, key)) => match builder.with_single_cert(cert_chain, key) {
Ok(config) => config,
Err(e) => return Err(format!("Failed to set client TLS credentials: {}", e).into()),