Add Certificate
struct and ClientCertificateStore
trait
This commit is contained in:
parent
680ca4e9b9
commit
54a099bb44
8 changed files with 139 additions and 54 deletions
103
src/certificate.rs
Normal file
103
src/certificate.rs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
use crate::mailuser::Mailuser;
|
||||||
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||||
|
pub struct Certificate {
|
||||||
|
pub der: Vec<u8>,
|
||||||
|
pub key: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An item which stores known certificates. For convenience, this trait is implemented for
|
||||||
|
/// `std::collections::HashMap<String, String>` and `std::collections::BTreeMap<String, String>`
|
||||||
|
pub trait CertificateStore: Send + Sync {
|
||||||
|
/// Retrieves a certificate fingerprint using the hostname as it's key if
|
||||||
|
/// it has previously been saved
|
||||||
|
fn get_certificate(&self, host: &str) -> Option<String>;
|
||||||
|
/// Inserts a certificate fingerprint with the hostname as the key
|
||||||
|
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String>;
|
||||||
|
/// Returns true if a fingerprint has been stored for this host
|
||||||
|
fn contains_certificate(&self, host: &str) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An item which stores client certificates. For convenience, this trait is implemented for
|
||||||
|
/// `std::collections::HashMap<String, Certificate>` and `std::collections::BTreeMap<String, Certificate>`
|
||||||
|
pub trait ClientCertificateStore: Send + Sync {
|
||||||
|
/// Retrieves a client certificate using the sender's address as the key
|
||||||
|
fn get_certificate(&self, user: &Mailuser) -> Option<Certificate>;
|
||||||
|
/// Inserts a client certificate with the sender's address as the key
|
||||||
|
fn insert_certificate(
|
||||||
|
&mut self,
|
||||||
|
user: &Mailuser,
|
||||||
|
certificate: Certificate,
|
||||||
|
) -> Option<Certificate>;
|
||||||
|
/// Returns true if the sender has a certificate in this store
|
||||||
|
fn contains_certificate(&self, user: &Mailuser) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CertificateStore for HashMap<String, String> {
|
||||||
|
fn get_certificate(&self, host: &str) -> Option<String> {
|
||||||
|
self.get(host).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String> {
|
||||||
|
self.insert(host.to_string(), fingerprint.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_certificate(&self, host: &str) -> bool {
|
||||||
|
self.contains_key(host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CertificateStore for BTreeMap<String, String> {
|
||||||
|
fn get_certificate(&self, host: &str) -> Option<String> {
|
||||||
|
self.get(host).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String> {
|
||||||
|
self.insert(host.to_string(), fingerprint.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_certificate(&self, host: &str) -> bool {
|
||||||
|
self.contains_key(host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientCertificateStore for HashMap<String, Certificate> {
|
||||||
|
fn get_certificate(&self, user: &Mailuser) -> Option<Certificate> {
|
||||||
|
self.get(&user.to_string()).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_certificate(
|
||||||
|
&mut self,
|
||||||
|
user: &Mailuser,
|
||||||
|
certificate: Certificate,
|
||||||
|
) -> Option<Certificate> {
|
||||||
|
self.insert(user.to_string(), certificate)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_certificate(&self, user: &Mailuser) -> bool {
|
||||||
|
self.contains_key(&user.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientCertificateStore for BTreeMap<String, Certificate> {
|
||||||
|
fn get_certificate(&self, user: &Mailuser) -> Option<Certificate> {
|
||||||
|
self.get(&user.to_string()).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_certificate(
|
||||||
|
&mut self,
|
||||||
|
user: &Mailuser,
|
||||||
|
certificate: Certificate,
|
||||||
|
) -> Option<Certificate> {
|
||||||
|
self.insert(user.to_string(), certificate)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_certificate(&self, user: &Mailuser) -> bool {
|
||||||
|
self.contains_key(&user.to_string())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,41 +0,0 @@
|
||||||
use std::collections::{BTreeMap, HashMap};
|
|
||||||
|
|
||||||
/// An item which stores known certificates. For convenience, this trait is implemented for
|
|
||||||
/// `std::collections::HashMap<String, String>` and `std::collections::BTreeMap<String, String>`
|
|
||||||
pub trait CertificateStore: Send + Sync {
|
|
||||||
/// Retrieves a certificate fingerprint using the hostname as it's key if
|
|
||||||
/// it has previously been saved
|
|
||||||
fn get_certificate(&self, host: &str) -> Option<String>;
|
|
||||||
/// Inserts a certificate fingerprint with the hostname as the key
|
|
||||||
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String>;
|
|
||||||
/// Returns true if a fingerprint has been stored for this host
|
|
||||||
fn contains_certificate(&self, host: &str) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CertificateStore for HashMap<String, String> {
|
|
||||||
fn get_certificate(&self, host: &str) -> Option<String> {
|
|
||||||
self.get(host).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String> {
|
|
||||||
self.insert(host.to_string(), fingerprint.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contains_certificate(&self, host: &str) -> bool {
|
|
||||||
self.contains_key(host)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CertificateStore for BTreeMap<String, String> {
|
|
||||||
fn get_certificate(&self, host: &str) -> Option<String> {
|
|
||||||
self.get(host).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String> {
|
|
||||||
self.insert(host.to_string(), fingerprint.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contains_certificate(&self, host: &str) -> bool {
|
|
||||||
self.contains_key(host)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
#![warn(clippy::all, clippy::pedantic)]
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
mod certificate_store;
|
mod certificate;
|
||||||
mod fingerprint;
|
mod fingerprint;
|
||||||
mod host;
|
mod host;
|
||||||
mod mailbox;
|
mod mailbox;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::prelude::Host;
|
use crate::{mailuser::Mailuser, prelude::Host};
|
||||||
use std::{fmt, str::FromStr};
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
|
@ -64,3 +64,13 @@ impl FromStr for Mailbox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Mailuser> for Mailbox {
|
||||||
|
fn from(value: Mailuser) -> Self {
|
||||||
|
Self {
|
||||||
|
username: value.username,
|
||||||
|
host: value.host,
|
||||||
|
blurb: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::prelude::{Host, ParseMailboxError as Error};
|
use crate::prelude::{Host, Mailbox, ParseMailboxError as Error};
|
||||||
use std::{fmt, str::FromStr};
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
|
@ -36,3 +36,12 @@ impl FromStr for Mailuser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Mailbox> for Mailuser {
|
||||||
|
fn from(value: Mailbox) -> Self {
|
||||||
|
Self {
|
||||||
|
username: value.username,
|
||||||
|
host: value.host,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
pub use super::{
|
pub use super::{
|
||||||
certificate_store::CertificateStore,
|
certificate::{Certificate, CertificateStore, ClientCertificateStore},
|
||||||
fingerprint::{Error as FingerprintError, Fingerprint, GetFingerprint},
|
fingerprint::{Error as FingerprintError, Fingerprint, GetFingerprint},
|
||||||
host::{Error as ParseHostError, Host},
|
host::{Error as ParseHostError, Host},
|
||||||
mailbox::{Error as ParseMailboxError, Mailbox},
|
mailbox::{Error as ParseMailboxError, Mailbox},
|
||||||
|
|
|
@ -32,11 +32,15 @@ where
|
||||||
|
|
||||||
impl<S> Sender<S>
|
impl<S> Sender<S>
|
||||||
where
|
where
|
||||||
S: CertificateStore + 'static
|
S: CertificateStore + 'static,
|
||||||
{
|
{
|
||||||
pub fn new(request_str: &str, store: S, client_store: S) -> Result<Self, Error> {
|
pub fn new(request_str: &str, store: S, client_store: S) -> Result<Self, Error> {
|
||||||
let request: Request = request_str.parse()?;
|
let request: Request = request_str.parse()?;
|
||||||
Ok(Self { request, store, client_store })
|
Ok(Self {
|
||||||
|
request,
|
||||||
|
store,
|
||||||
|
client_store,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn host_string(&self) -> String {
|
fn host_string(&self) -> String {
|
||||||
|
@ -50,7 +54,9 @@ where
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| Error::DnsError)?;
|
.map_err(|_| Error::DnsError)?;
|
||||||
let mut it = self.request.sender.host.to_socket_addrs()?;
|
let mut it = self.request.sender.host.to_socket_addrs()?;
|
||||||
let client_cert = self.client_store.get_certificate(&self.request.sender.to_string());
|
let client_cert = self
|
||||||
|
.client_store
|
||||||
|
.get_certificate(&self.request.sender.to_string());
|
||||||
let verifier = Arc::new(Verifier::new(self.store));
|
let verifier = Arc::new(Verifier::new(self.store));
|
||||||
let Some(socket_addrs) = it.next() else {
|
let Some(socket_addrs) = it.next() else {
|
||||||
return Err(io::Error::new(io::ErrorKind::Other, "no data retrieved").into());
|
return Err(io::Error::new(io::ErrorKind::Other, "no data retrieved").into());
|
||||||
|
@ -61,13 +67,15 @@ where
|
||||||
.with_custom_certificate_verifier(verifier);
|
.with_custom_certificate_verifier(verifier);
|
||||||
let cfg = match client_cert {
|
let cfg = match client_cert {
|
||||||
None => cfg.with_no_client_auth(),
|
None => cfg.with_no_client_auth(),
|
||||||
Some(_) => cfg.with_no_client_auth(),// todo: cfg.with_single_cert(cert_chain, key_der)
|
Some(_) => cfg.with_no_client_auth(), // todo: cfg.with_single_cert(cert_chain, key_der)
|
||||||
};
|
};
|
||||||
let client = ClientConnection::new(Arc::new(cfg), dnsname)?;
|
let client = ClientConnection::new(Arc::new(cfg), dnsname)?;
|
||||||
let mut stream = StreamOwned::new(client, tcp_stream);
|
let mut stream = StreamOwned::new(client, tcp_stream);
|
||||||
stream.write_all(self.request.to_string().as_bytes())?;
|
stream.write_all(self.request.to_string().as_bytes())?;
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
stream.read_to_end(&mut buf)?;
|
stream.read_to_end(&mut buf)?;
|
||||||
|
stream.conn.send_close_notify();
|
||||||
|
drop(stream);
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,7 @@ use {
|
||||||
client::{ServerCertVerified, ServerCertVerifier},
|
client::{ServerCertVerified, ServerCertVerifier},
|
||||||
Certificate,
|
Certificate,
|
||||||
},
|
},
|
||||||
std::{
|
std::{borrow::BorrowMut, sync::Mutex, time},
|
||||||
borrow::BorrowMut,
|
|
||||||
sync::Mutex,
|
|
||||||
time,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
Loading…
Add table
Reference in a new issue