diff --git a/src/imp/openssl.rs b/src/imp/openssl.rs index 8fc4362..4cac343 100644 --- a/src/imp/openssl.rs +++ b/src/imp/openssl.rs @@ -16,7 +16,8 @@ use std::fmt; use std::io; use std::sync::Once; -use {Protocol, TlsAcceptorBuilder, TlsConnectorBuilder}; + +use {Protocol, TlsAcceptorBuilder, TlsConnectorBuilder, TlsClientCertificateVerification}; #[cfg(have_min_max_version)] fn supported_protocols( @@ -365,6 +366,20 @@ impl TlsAcceptor { let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls())?; acceptor.set_private_key(&builder.identity.0.pkey)?; acceptor.set_certificate(&builder.identity.0.cert)?; + + if let Some(client_ca_cert) = &builder.client_cert_verification_ca_cert { + if builder.client_cert_verification_trust { + acceptor.cert_store_mut().add_cert((client_ca_cert.0).0.to_owned())?; + } + acceptor.add_client_ca((client_ca_cert.0).0.as_ref())?; + } + let verify_mode = match &builder.client_cert_verification { + TlsClientCertificateVerification::DoNotRequestCertificate => SslVerifyMode::NONE, + TlsClientCertificateVerification::RequestCertificate => SslVerifyMode::PEER, + TlsClientCertificateVerification::RequireCertificate => SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT, + }; + acceptor.set_verify(verify_mode); + for cert in builder.identity.0.chain.iter() { // https://www.openssl.org/docs/manmaster/man3/SSL_CTX_add_extra_chain_cert.html // specifies that "When sending a certificate chain, extra chain certificates are diff --git a/src/lib.rs b/src/lib.rs index eaad41f..30fb97f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -507,6 +507,21 @@ impl TlsConnector { } } +/// Client certificate verification modes +pub enum TlsClientCertificateVerification { + /// The server will not request certificates from the client. + /// + /// # Warning + /// The client will not be able to send any certificates with this setting. + DoNotRequestCertificate, + /// The server will request a certificate from the client, then will validate + /// any certificate it receives. The client may choose not to send any. + RequestCertificate, + /// The server will request a certificate from the client, then will validate + /// any certificate it receives or reject the connection none are provided. + RequireCertificate, +} + /// A builder for `TlsAcceptor`s. /// /// You can get one from [`TlsAcceptor::builder()`](TlsAcceptor::builder) @@ -514,6 +529,9 @@ pub struct TlsAcceptorBuilder { identity: Identity, min_protocol: Option, max_protocol: Option, + client_cert_verification: TlsClientCertificateVerification, + client_cert_verification_ca_cert: Option, + client_cert_verification_trust: bool } impl TlsAcceptorBuilder { @@ -537,6 +555,35 @@ impl TlsAcceptorBuilder { self } + /// Sets the verification mode for client certificates. + /// + /// Defaults to `TlsClientCertificateVerification::DoNotRequestCertificate`. + pub fn client_cert_verification(&mut self, client_cert_verification: TlsClientCertificateVerification) -> &mut TlsAcceptorBuilder { + self.client_cert_verification = client_cert_verification; + self + } + + /// Sets which ca to tell the client is acceptable to send to the server. + /// + /// A value of `None` will not tell the client it is acceptable to send certificates signed by any ca. + /// + /// Defaults `None`. + pub fn client_cert_verification_ca_cert(&mut self, client_cert_verification_ca_cert: Option) -> &mut TlsAcceptorBuilder { + self.client_cert_verification_ca_cert = client_cert_verification_ca_cert; + self + } + + /// Trust the ca certificate used for client verification + /// + /// Adds client ca to the list of trusted certificates. This is intended for + /// testing purposes only and must not be used in production code. + /// + /// Defaults `false` + pub fn trust_client_ca_cert(&mut self, should_trust: bool) -> &mut TlsAcceptorBuilder { + self.client_cert_verification_trust = should_trust; + self + } + /// Creates a new `TlsAcceptor`. pub fn build(&self) -> Result { let acceptor = imp::TlsAcceptor::new(self)?; @@ -601,6 +648,9 @@ impl TlsAcceptor { identity, min_protocol: Some(Protocol::Tlsv10), max_protocol: None, + client_cert_verification: TlsClientCertificateVerification::DoNotRequestCertificate, + client_cert_verification_ca_cert: None, + client_cert_verification_trust: false } }