diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 9e169c5..4e4288e 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -255,7 +255,7 @@ | `SSL_SESSION_up_ref` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_SRP_CTX_free` [^deprecatedin_3_0] [^srp] | | | | | `SSL_SRP_CTX_init` [^deprecatedin_3_0] [^srp] | | | | -| `SSL_accept` | | | | +| `SSL_accept` | | | :white_check_mark: | | `SSL_add1_host` | | | | | `SSL_add1_to_CA_list` | | | | | `SSL_add_client_CA` | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 994adf9..9aa5635 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -46,6 +46,7 @@ const ENTRYPOINTS: &[&str] = &[ "d2i_SSL_SESSION", "i2d_SSL_SESSION", "OPENSSL_init_ssl", + "SSL_accept", "SSL_alert_desc_string", "SSL_alert_desc_string_long", "SSL_CIPHER_description", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index e9ea328..45c3caf 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -762,6 +762,22 @@ entry! { } } +entry! { + pub fn _SSL_accept(ssl: *mut SSL) -> c_int { + let ssl = try_clone_arc!(ssl); + + match ssl + .lock() + .map_err(|_| Error::cannot_lock()) + .and_then(|mut ssl| ssl.accept()) + .map_err(|err| err.raise()) + { + Err(e) => e.into(), + Ok(()) => C_INT_SUCCESS, + } + } +} + entry! { pub fn _SSL_write(ssl: *mut SSL, buf: *const c_void, num: c_int) -> c_int { const ERROR: c_int = -1; diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index bb30958..5e40fb4 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -1,5 +1,5 @@ use core::ffi::{c_int, c_uint, CStr}; -use core::ptr; +use core::{mem, ptr}; use std::fs; use std::io::{ErrorKind, Read, Write}; use std::path::PathBuf; @@ -12,7 +12,10 @@ use openssl_sys::{ }; use rustls::crypto::aws_lc_rs as provider; use rustls::pki_types::{CertificateDer, ServerName}; -use rustls::{CipherSuite, ClientConfig, ClientConnection, Connection, RootCertStore}; +use rustls::server::{Accepted, Acceptor}; +use rustls::{ + CipherSuite, ClientConfig, ClientConnection, Connection, RootCertStore, ServerConfig, +}; mod bio; #[macro_use] @@ -340,14 +343,22 @@ struct Ssl { alpn: Vec>, sni_server_name: Option>, bio: Option, - conn: Option, - verifier: Option>, + conn: ConnState, peer_cert: Option, peer_cert_chain: Option, shutdown_flags: ShutdownFlags, auth_keys: sign::CertifiedKeySet, } +#[allow(clippy::large_enum_variant)] +enum ConnState { + Nothing, + Client(Connection, Arc), + Accepting(Acceptor), + Accepted(Accepted), + Server(Connection, Arc), +} + impl Ssl { fn new(ctx: Arc>, inner: &SslContext) -> Result { Ok(Self { @@ -360,8 +371,7 @@ impl Ssl { alpn: inner.alpn.clone(), sni_server_name: None, bio: None, - conn: None, - verifier: None, + conn: ConnState::Nothing, peer_cert: None, peer_cert_chain: None, shutdown_flags: ShutdownFlags::default(), @@ -482,7 +492,7 @@ impl Ssl { fn connect(&mut self) -> Result<(), error::Error> { self.set_client_mode(); - if self.conn.is_none() { + if matches!(self.conn, ConnState::Nothing) { self.init_client_conn()?; } self.try_io() @@ -508,15 +518,14 @@ impl Ssl { self.verify_mode, &self.verify_server_name, )); - self.verifier = Some(verifier.clone()); let wants_resolver = ClientConfig::builder_with_provider(provider) .with_protocol_versions(method.client_versions) .map_err(error::Error::from_rustls)? .dangerous() - .with_custom_certificate_verifier(verifier); + .with_custom_certificate_verifier(verifier.clone()); - let mut config = if let Some(resolver) = self.auth_keys.resolver() { + let mut config = if let Some(resolver) = self.auth_keys.client_resolver() { wants_resolver.with_client_cert_resolver(resolver) } else { wants_resolver.with_no_client_auth() @@ -527,23 +536,99 @@ impl Ssl { let client_conn = ClientConnection::new(Arc::new(config), sni_server_name.clone()) .map_err(error::Error::from_rustls)?; - self.conn = Some(client_conn.into()); + self.conn = ConnState::Client(client_conn.into(), verifier); + Ok(()) + } + + fn accept(&mut self) -> Result<(), error::Error> { + self.set_server_mode(); + + if matches!(self.conn, ConnState::Nothing) { + self.conn = ConnState::Accepting(Acceptor::default()); + } + + self.try_io()?; + + if let ConnState::Accepted(_) = self.conn { + self.init_server_conn()?; + } + + self.try_io() + } + + fn init_server_conn(&mut self) -> Result<(), error::Error> { + let method = self + .ctx + .lock() + .map(|ctx| ctx.method) + .map_err(|_| error::Error::cannot_lock())?; + + let provider = Arc::new(provider::default_provider()); + let verifier = Arc::new( + verifier::ClientVerifier::new( + self.verify_roots.clone().into(), + provider.clone(), + self.verify_mode, + ) + .map_err(error::Error::from_rustls)?, + ); + + let resolver = self + .auth_keys + .server_resolver() + .ok_or_else(|| error::Error::bad_data("missing server keys"))?; + + let config = ServerConfig::builder_with_provider(provider) + .with_protocol_versions(method.server_versions) + .map_err(error::Error::from_rustls)? + .with_client_cert_verifier(verifier.clone()) + .with_cert_resolver(resolver); + + let accepted = match mem::replace(&mut self.conn, ConnState::Nothing) { + ConnState::Accepted(accepted) => accepted, + _ => unreachable!(), + }; + + // TODO: send alert + let server_conn = accepted + .into_connection(Arc::new(config)) + .map_err(|(err, _alert)| error::Error::from_rustls(err))?; + + self.conn = ConnState::Server(server_conn.into(), verifier); Ok(()) } + fn conn(&self) -> Option<&Connection> { + match &self.conn { + ConnState::Client(conn, _) | ConnState::Server(conn, _) => Some(conn), + _ => None, + } + } + + fn conn_mut(&mut self) -> Option<&mut Connection> { + match &mut self.conn { + ConnState::Client(conn, _) | ConnState::Server(conn, _) => Some(conn), + _ => None, + } + } + fn want(&self) -> Want { match &self.conn { - Some(conn) => Want { + ConnState::Client(conn, _) | ConnState::Server(conn, _) => Want { read: conn.wants_read(), write: conn.wants_write(), }, - None => Want::default(), + ConnState::Accepting(_) => Want { + read: true, + write: false, + }, + _ => Want::default(), } } fn write(&mut self, slice: &[u8]) -> Result { - let written = match &mut self.conn { - Some(ref mut conn) => conn.writer().write(slice).map_err(error::Error::from_io)?, + let written = match self.conn_mut() { + Some(conn) => conn.writer().write(slice).map_err(error::Error::from_io)?, None => 0, }; self.try_io()?; @@ -554,8 +639,8 @@ impl Ssl { let (late_err, read_count) = loop { let late_err = self.try_io(); - match &mut self.conn { - Some(ref mut conn) => match conn.reader().read(slice) { + match self.conn_mut() { + Some(conn) => match conn.reader().read(slice) { Ok(read) => break (late_err, read), Err(err) if err.kind() == ErrorKind::WouldBlock && late_err.is_ok() => { // no data available, go around again. @@ -586,7 +671,7 @@ impl Ssl { }; match &mut self.conn { - Some(ref mut conn) => { + ConnState::Client(conn, _) | ConnState::Server(conn, _) => { match conn.complete_io(bio) { Ok(_) => {} Err(e) => { @@ -606,15 +691,34 @@ impl Ssl { } Ok(()) } - None => Ok(()), + ConnState::Accepting(acceptor) => { + match acceptor.read_tls(bio) { + Ok(_) => {} + Err(e) => { + return Err(error::Error::from_io(e)); + } + }; + + match acceptor.accept() { + Ok(None) => Ok(()), + Ok(Some(accepted)) => { + self.conn = ConnState::Accepted(accepted); + Ok(()) + } + Err((error, mut alert)) => { + alert.write_all(bio).map_err(error::Error::from_io)?; + Err(error::Error::from_rustls(error)) + } + } + } + _ => Ok(()), } } fn try_shutdown(&mut self) -> Result { if !self.shutdown_flags.is_sent() { - match &mut self.conn { - Some(ref mut conn) => conn.send_close_notify(), - None => (), + if let Some(ref mut conn) = self.conn_mut() { + conn.send_close_notify(); }; self.shutdown_flags.set_sent(); @@ -637,7 +741,7 @@ impl Ssl { } fn get_pending_plaintext(&mut self) -> usize { - self.conn + self.conn_mut() .as_mut() .and_then(|conn| { let io_state = conn.process_new_packets().ok()?; @@ -647,11 +751,11 @@ impl Ssl { } fn get_agreed_alpn(&mut self) -> Option<&[u8]> { - self.conn.as_ref().and_then(|conn| conn.alpn_protocol()) + self.conn().and_then(|conn| conn.alpn_protocol()) } fn init_peer_cert(&mut self) { - let conn = match &self.conn { + let conn = match self.conn() { Some(conn) => conn, None => return, }; @@ -662,6 +766,8 @@ impl Ssl { }; let mut stack = x509::OwnedX509Stack::empty(); + let mut peer_cert = None; + for (i, cert) in certs.iter().enumerate() { let converted = match x509::OwnedX509::parse_der(cert.as_ref()) { Some(converted) => converted, @@ -676,12 +782,13 @@ impl Ssl { // certificate must be obtained separately" stack.push(&converted); } - self.peer_cert = Some(converted); + peer_cert = Some(converted); } else { stack.push(&converted); } } + self.peer_cert = peer_cert; self.peer_cert_chain = Some(stack); } @@ -700,22 +807,21 @@ impl Ssl { } fn get_negotiated_cipher_suite_id(&self) -> Option { - self.conn - .as_ref() + self.conn() .and_then(|conn| conn.negotiated_cipher_suite()) .map(|suite| suite.suite()) } fn get_last_verification_result(&self) -> i64 { - if let Some(verifier) = &self.verifier { - verifier.last_result() - } else { - X509_V_ERR_UNSPECIFIED as i64 + match &self.conn { + ConnState::Client(_, verifier) => verifier.last_result(), + ConnState::Server(_, verifier) => verifier.last_result(), + _ => X509_V_ERR_UNSPECIFIED as i64, } } fn get_error(&mut self) -> c_int { - match &mut self.conn { + match self.conn_mut() { Some(ref mut conn) => { if let Err(e) = conn.process_new_packets() { error::Error::from_rustls(e).raise(); @@ -775,13 +881,14 @@ impl Ssl { } fn handshake_state(&mut self) -> HandshakeState { - match &mut self.conn { + let mode = self.mode; + match self.conn_mut() { Some(ref mut conn) => { if conn.process_new_packets().is_err() { return HandshakeState::Error; } - match (&self.mode, conn.is_handshaking()) { + match (mode, conn.is_handshaking()) { (ConnMode::Server, true) => HandshakeState::ServerAwaitingClientHello, (ConnMode::Client, true) => HandshakeState::ClientAwaitingServerHello, (ConnMode::Unknown, true) => HandshakeState::Before, @@ -841,7 +948,7 @@ struct Want { write: bool, } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone, Copy)] enum ConnMode { Unknown, Client, @@ -895,6 +1002,10 @@ impl VerifyMode { self.0 & VerifyMode::PEER == VerifyMode::PEER } + pub fn server_must_attempt_client_auth(&self) -> bool { + self.0 & VerifyMode::PEER == VerifyMode::PEER + } + pub fn server_must_verify_client(&self) -> bool { let bitmap = VerifyMode::PEER | VerifyMode::FAIL_IF_NO_PEER_CERT; self.0 & bitmap == bitmap diff --git a/rustls-libssl/src/sign.rs b/rustls-libssl/src/sign.rs index c3f032e..c7390c7 100644 --- a/rustls-libssl/src/sign.rs +++ b/rustls-libssl/src/sign.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use openssl_sys::{EVP_PKEY, X509}; use rustls::client::ResolvesClientCert; use rustls::pki_types::CertificateDer; +use rustls::server::{ClientHello, ResolvesServerCert}; use rustls::sign; use rustls::{SignatureAlgorithm, SignatureScheme}; @@ -58,8 +59,12 @@ impl CertifiedKeySet { Ok(()) } - pub fn resolver(&self) -> Option> { - self.current_key.as_ref().map(|ck| ck.resolver()) + pub fn client_resolver(&self) -> Option> { + self.current_key.as_ref().map(|ck| ck.client_resolver()) + } + + pub fn server_resolver(&self) -> Option> { + self.current_key.as_ref().map(|ck| ck.server_resolver()) } /// For `SSL_get_certificate` @@ -104,12 +109,19 @@ impl OpenSslCertifiedKey { self.key.borrow_ref() } - fn resolver(&self) -> Arc { + fn client_resolver(&self) -> Arc { Arc::new(AlwaysResolvesClientCert(Arc::new(sign::CertifiedKey::new( self.rustls_chain.clone(), Arc::new(OpenSslKey(self.key.clone())), )))) } + + fn server_resolver(&self) -> Arc { + Arc::new(AlwaysResolvesServerCert(Arc::new(sign::CertifiedKey::new( + self.rustls_chain.clone(), + Arc::new(OpenSslKey(self.key.clone())), + )))) + } } #[derive(Debug)] @@ -129,6 +141,15 @@ impl ResolvesClientCert for AlwaysResolvesClientCert { } } +#[derive(Debug)] +struct AlwaysResolvesServerCert(Arc); + +impl ResolvesServerCert for AlwaysResolvesServerCert { + fn resolve(&self, _client_hello: ClientHello) -> Option> { + Some(Arc::clone(&self.0)) + } +} + #[derive(Debug)] struct OpenSslKey(EvpPkey); diff --git a/rustls-libssl/src/verifier.rs b/rustls-libssl/src/verifier.rs index 4d7a0a7..ccae494 100644 --- a/rustls-libssl/src/verifier.rs +++ b/rustls-libssl/src/verifier.rs @@ -14,8 +14,10 @@ use rustls::{ }, crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider}, pki_types::{CertificateDer, ServerName, UnixTime}, - server::ParsedCertificate, - CertificateError, DigitallySignedStruct, Error, RootCertStore, SignatureScheme, + server::danger::{ClientCertVerified, ClientCertVerifier}, + server::{ParsedCertificate, WebPkiClientVerifier}, + CertificateError, DigitallySignedStruct, DistinguishedName, Error, RootCertStore, + SignatureScheme, }; use crate::VerifyMode; @@ -96,27 +98,7 @@ impl ServerCertVerifier for ServerVerifier { ) -> Result { let result = self.verify_server_cert_inner(end_entity, intermediates, now); - let openssl_rv = match &result { - Ok(()) => X509_V_OK, - Err(Error::InvalidCertificate(CertificateError::UnknownIssuer)) => { - X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY - } - Err(Error::InvalidCertificate(CertificateError::NotValidYet)) => { - X509_V_ERR_CERT_NOT_YET_VALID - } - Err(Error::InvalidCertificate(CertificateError::Expired)) => { - X509_V_ERR_CERT_HAS_EXPIRED - } - Err(Error::InvalidCertificate(CertificateError::Revoked)) => X509_V_ERR_CERT_REVOKED, - Err(Error::InvalidCertificate(CertificateError::InvalidPurpose)) => { - X509_V_ERR_INVALID_PURPOSE - } - Err(Error::InvalidCertificate(CertificateError::NotValidForName)) => { - X509_V_ERR_HOSTNAME_MISMATCH - } - // TODO: more mappings can go here - Err(_) => X509_V_ERR_UNSPECIFIED, - }; + let openssl_rv = translate_verify_result(&result); self.last_result.store(openssl_rv as i64, Ordering::Release); // Call it success if it succeeded, or the `mode` says not to care. @@ -161,3 +143,126 @@ impl ServerCertVerifier for ServerVerifier { .supported_schemes() } } + +#[derive(Debug)] +pub struct ClientVerifier { + parent: Arc, + mode: VerifyMode, + last_result: AtomicI64, +} + +impl ClientVerifier { + pub fn new( + root_store: Arc, + provider: Arc, + mode: VerifyMode, + ) -> Result { + let (parent, initial_result) = if !mode.server_must_attempt_client_auth() { + (Ok(WebPkiClientVerifier::no_client_auth()), X509_V_OK) + } else { + let builder = WebPkiClientVerifier::builder_with_provider(root_store, provider) + .allow_unknown_revocation_status(); + + if mode.server_must_verify_client() { + (builder.build(), X509_V_ERR_UNSPECIFIED) + } else { + ( + builder.allow_unauthenticated().build(), + X509_V_ERR_UNSPECIFIED, + ) + } + }; + + let parent = parent.map_err(|err| rustls::Error::General(err.to_string()))?; + + Ok(Self { + parent, + mode, + last_result: AtomicI64::new(initial_result as i64), + }) + } + + pub fn last_result(&self) -> i64 { + self.last_result.load(Ordering::Acquire) + } +} + +impl ClientCertVerifier for ClientVerifier { + fn offer_client_auth(&self) -> bool { + self.mode.server_must_attempt_client_auth() + } + + fn client_auth_mandatory(&self) -> bool { + self.mode.server_must_verify_client() + } + + fn verify_client_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + now: UnixTime, + ) -> Result { + let result = self + .parent + .verify_client_cert(end_entity, intermediates, now) + .map(|_| ()); + + let openssl_rv = translate_verify_result(&result); + self.last_result.store(openssl_rv as i64, Ordering::Release); + + // Call it success if it succeeded, or the `mode` says not to care. + if openssl_rv == X509_V_OK || !self.mode.server_must_verify_client() { + Ok(ClientCertVerified::assertion()) + } else { + Err(result.unwrap_err()) + } + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + self.parent.verify_tls12_signature(message, cert, dss) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + self.parent.verify_tls13_signature(message, cert, dss) + } + + fn supported_verify_schemes(&self) -> Vec { + self.parent.supported_verify_schemes() + } + + fn root_hint_subjects(&self) -> &[DistinguishedName] { + self.parent.root_hint_subjects() + } +} + +fn translate_verify_result(result: &Result<(), Error>) -> i32 { + match result { + Ok(()) => X509_V_OK, + Err(Error::InvalidCertificate(CertificateError::UnknownIssuer)) => { + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY + } + Err(Error::InvalidCertificate(CertificateError::NotValidYet)) => { + X509_V_ERR_CERT_NOT_YET_VALID + } + Err(Error::InvalidCertificate(CertificateError::Expired)) => X509_V_ERR_CERT_HAS_EXPIRED, + Err(Error::InvalidCertificate(CertificateError::Revoked)) => X509_V_ERR_CERT_REVOKED, + Err(Error::InvalidCertificate(CertificateError::InvalidPurpose)) => { + X509_V_ERR_INVALID_PURPOSE + } + Err(Error::InvalidCertificate(CertificateError::NotValidForName)) => { + X509_V_ERR_HOSTNAME_MISMATCH + } + // TODO: more mappings can go here + Err(_) => X509_V_ERR_UNSPECIFIED, + } +}