From d57211f881c103104f4423746079257907ae5d3f Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Sat, 16 Oct 2021 04:21:14 +0200 Subject: [PATCH] Upgrade rustls to 0.20.0 (#88) This results in breaking API changes, because rustls 0.20 involved a lot of API changes. In particular: - rustls_\*_config_builder is now a multi-step process, moving through the ConfigBuilder typestates introduced in rustls 0.20.0. - reading plaintext can result in RUSTLS_RESULT_PLAINTEXT_EMPTY (read more TLS bytes from the wire to get more plaintext) or RUSTLS_RESULT_UNEXPECTED_EOF (peer closed connection uncleanly). A clean connection shutdown is signaled by returning RUSTLS_RESULT_OK with n == 0 from rustls_connection_read() - The RUSTLS_CERT_\* errors have been simplified and consolidated. - rustls_connection_get_peer_certificate -> rustls_connection_peer_certificate - rustls_connection_get_negotiated_ciphersuite -> rustls_connection_negotiated_ciphersuite - rustls_server_config_builder_set_protocols -> rustls_server_config_builder_set_alpn_protocols - rustls_server_config_builder_set_ciphersuites -> removed --- Cargo.toml | 8 +- src/cipher.rs | 47 +++--- src/client.rs | 352 +++++++++++++++++++++--------------------- src/connection.rs | 154 +++++++++---------- src/crustls.h | 383 +++++++++++++++++++++++----------------------- src/enums.rs | 19 --- src/error.rs | 356 +++++++++++++++++++----------------------- src/lib.rs | 25 ++- src/server.rs | 272 +++++++++++++++++++------------- src/session.rs | 8 +- tests/client.c | 49 +++--- tests/common.c | 12 +- tests/common.h | 3 +- tests/server.c | 24 +-- 14 files changed, 847 insertions(+), 865 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 62d14edb..17b18f67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,11 +8,11 @@ links = "crustls" [dependencies] # Keep in sync with RUSTLS_CRATE_VERSION in lib.rs -rustls = { version = "^0.19.0", features = [ "dangerous_configuration" ] } -webpki = "0.21" +rustls = { version = "=0.20", features = [ "dangerous_configuration" ] } +webpki = "0.22" libc = "0.2" -sct = "0.6.0" -rustls-pemfile = "0.2.0" +sct = "0.7" +rustls-pemfile = "0.2.1" log = "0.4.14" [dev_dependencies] diff --git a/src/cipher.rs b/src/cipher.rs index 7af4095f..5b7d79e5 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -4,12 +4,9 @@ use std::ptr::null; use std::slice; use std::sync::Arc; +use rustls::server::{AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient}; use rustls::sign::CertifiedKey; -use rustls::{ - AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, RootCertStore, - SupportedCipherSuite, ALL_CIPHERSUITES, -}; -use rustls::{Certificate, PrivateKey}; +use rustls::{Certificate, PrivateKey, RootCertStore, SupportedCipherSuite, ALL_CIPHER_SUITES}; use rustls_pemfile::{certs, pkcs8_private_keys, rsa_private_keys}; use crate::error::rustls_result; @@ -20,7 +17,7 @@ use std::ops::Deref; /// An X.509 certificate, as used in rustls. /// Corresponds to `Certificate` in the Rust API. -/// https://docs.rs/rustls/0.19.0/rustls/struct.CertifiedKey.html +/// https://docs.rs/rustls/0.20.0/rustls/struct.Certificate.html pub struct rustls_certificate { // We use the opaque struct pattern to tell C about our types without // telling them what's inside. @@ -54,7 +51,7 @@ pub extern "C" fn rustls_certificate_get_der( /// The complete chain of certificates to send during a TLS handshake, /// plus a private key that matches the end-entity (leaf) certificate. /// Corresponds to `CertifiedKey` in the Rust API. -/// https://docs.rs/rustls/0.19.0/rustls/sign/struct.CertifiedKey.html +/// https://docs.rs/rustls/0.20.0/rustls/sign/struct.CertifiedKey.html pub struct rustls_certified_key { // We use the opaque struct pattern to tell C about our types without // telling them what's inside. @@ -83,13 +80,18 @@ pub extern "C" fn rustls_supported_ciphersuite_get_suite( supported_ciphersuite: *const rustls_supported_ciphersuite, ) -> u16 { let supported_ciphersuite = try_ref_from_ptr!(supported_ciphersuite); - supported_ciphersuite.suite.get_u16() + match supported_ciphersuite { + rustls::SupportedCipherSuite::Tls12(sc) => &sc.common, + rustls::SupportedCipherSuite::Tls13(sc) => &sc.common, + } + .suite + .get_u16() } /// Return the length of rustls' list of supported cipher suites. #[no_mangle] pub extern "C" fn rustls_all_ciphersuites_len() -> usize { - ALL_CIPHERSUITES.len() + ALL_CIPHER_SUITES.len() } /// Get a pointer to a member of rustls' list of supported cipher suites. This will return non-NULL @@ -100,8 +102,8 @@ pub extern "C" fn rustls_all_ciphersuites_len() -> usize { pub extern "C" fn rustls_all_ciphersuites_get_entry( i: size_t, ) -> *const rustls_supported_ciphersuite { - match ALL_CIPHERSUITES.get(i) { - Some(&cs) => cs as *const SupportedCipherSuite as *const _, + match ALL_CIPHER_SUITES.get(i) { + Some(cs) => cs as *const SupportedCipherSuite as *const _, None => null(), } } @@ -261,15 +263,12 @@ fn certified_key_build( Err(_) => return Err(rustls_result::CertificateParseError), }; - Ok(rustls::sign::CertifiedKey::new( - parsed_chain, - Arc::new(signing_key), - )) + Ok(rustls::sign::CertifiedKey::new(parsed_chain, signing_key)) } /// A root cert store that is done being constructed and is now read-only. /// Under the hood, this object corresponds to an Arc. -/// https://docs.rs/rustls/0.19.0/rustls/struct.RootCertStore.html +/// https://docs.rs/rustls/0.20.0/rustls/struct.RootCertStore.html pub struct rustls_root_cert_store { // We use the opaque struct pattern to tell C about our types without // telling them what's inside. @@ -284,7 +283,7 @@ impl CastPtr for rustls_root_cert_store { /// Create a rustls_root_cert_store. Caller owns the memory and must /// eventually call rustls_root_cert_store_free. The store starts out empty. /// Caller must add root certificates with rustls_root_cert_store_add_pem. -/// https://docs.rs/rustls/0.19.0/rustls/struct.RootCertStore.html#method.empty +/// https://docs.rs/rustls/0.20.0/rustls/struct.RootCertStore.html#method.empty #[no_mangle] pub extern "C" fn rustls_root_cert_store_new() -> *mut rustls_root_cert_store { ffi_panic_boundary! { @@ -313,17 +312,17 @@ pub extern "C" fn rustls_root_cert_store_add_pem( let certs_pem: &[u8] = try_slice!(pem, pem_len); let store: &mut RootCertStore = try_mut_from_ptr!(store); + let certs_der = match rustls_pemfile::certs(&mut Cursor::new(certs_pem)) { + Ok(vv) => vv, + Err(_) => return rustls_result::CertificateParseError, + }; // We first copy into a temporary root store so we can uphold our // API guideline that there are no partial failures or partial // successes. let mut new_store = RootCertStore::empty(); - match new_store.add_pem_file(&mut Cursor::new(certs_pem)) { - Ok((parsed, rejected)) => { - if strict && (rejected > 0 || parsed == 0) { - return rustls_result::CertificateParseError; - } - }, - Err(_) => return rustls_result::CertificateParseError, + let (parsed, rejected) = new_store.add_parsable_certificates(&certs_der); + if strict && (rejected > 0 || parsed == 0) { + return rustls_result::CertificateParseError; } store.roots.append(&mut new_store.roots); diff --git a/src/client.rs b/src/client.rs index bff223db..5f9d7da5 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,29 +4,24 @@ use std::fs::File; use std::io::BufReader; use std::slice; use std::sync::Arc; +use std::time::SystemTime; use libc::{c_char, size_t}; +use rustls::client::{ResolvesClientCert, ServerCertVerified}; use rustls::{ - sign::CertifiedKey, Certificate, ClientConfig, ClientSession, ResolvesClientCert, - RootCertStore, ServerCertVerified, SupportedCipherSuite, TLSError, ALL_CIPHERSUITES, + sign::CertifiedKey, Certificate, ClientConfig, ClientConnection, ConfigBuilder, + ProtocolVersion, RootCertStore, SupportedCipherSuite, WantsVerifier, ALL_CIPHER_SUITES, }; -use webpki::DNSNameRef; - use crate::cipher::{rustls_certified_key, rustls_root_cert_store, rustls_supported_ciphersuite}; use crate::connection::{rustls_connection, Connection}; -use crate::enums::rustls_tls_version_from_u16; use crate::error::rustls_result::{InvalidParameter, NullParameter}; -use crate::error::{self, result_to_tlserror, rustls_result}; +use crate::error::{self, result_to_error, rustls_result}; use crate::rslice::NulByte; use crate::rslice::{rustls_slice_bytes, rustls_slice_slice_bytes, rustls_str}; -use crate::session::{ - rustls_session_store_get_callback, rustls_session_store_put_callback, SessionStoreBroker, - SessionStoreGetCallback, SessionStorePutCallback, -}; use crate::{ arc_with_incref_from_raw, ffi_panic_boundary, try_mut_from_ptr, try_ref_from_ptr, try_slice, - userdata_get, CastPtr, + userdata_get, BoxCastPtr, CastPtr, }; /// A client config being constructed. A builder can be modified by, @@ -35,21 +30,33 @@ use crate::{ /// to turn it into a *rustls_client_config. This object is not safe /// for concurrent mutation. Under the hood, it corresponds to a /// Box. -/// https://docs.rs/rustls/0.19.0/rustls/struct.ClientConfig.html -pub struct rustls_client_config_builder { +/// https://docs.rs/rustls/0.20.0/rustls/struct.ClientConfig.html +pub struct rustls_client_config_builder_wants_verifier { // We use the opaque struct pattern to tell C about our types without // telling them what's inside. // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs _private: [u8; 0], } +impl CastPtr for rustls_client_config_builder_wants_verifier { + type RustType = ConfigBuilder; +} + +impl BoxCastPtr for rustls_client_config_builder_wants_verifier {} + +pub struct rustls_client_config_builder { + _private: [u8; 0], +} + impl CastPtr for rustls_client_config_builder { type RustType = ClientConfig; } +impl BoxCastPtr for rustls_client_config_builder {} + /// A client config that is done being constructed and is now read-only. /// Under the hood, this object corresponds to an Arc. -/// https://docs.rs/rustls/0.19.0/rustls/struct.ClientConfig.html +/// https://docs.rs/rustls/0.20.0/rustls/struct.ClientConfig.html pub struct rustls_client_config { // We use the opaque struct pattern to tell C about our types without // telling them what's inside. @@ -63,41 +70,73 @@ impl CastPtr for rustls_client_config { /// Create a rustls_client_config_builder. Caller owns the memory and must /// eventually call rustls_client_config_builder_build, then free the -/// resulting rustls_client_config. This starts out with no trusted roots. +/// resulting rustls_client_config. +/// This uses rustls safe default values +/// for the cipher suites, key exchange groups and protocol versions. +/// This starts out with no trusted roots. /// Caller must add roots with rustls_client_config_builder_load_roots_from_file /// or provide a custom verifier. #[no_mangle] -pub extern "C" fn rustls_client_config_builder_new() -> *mut rustls_client_config_builder { +pub extern "C" fn rustls_client_config_builder_new_with_safe_defaults( +) -> *mut rustls_client_config_builder_wants_verifier { ffi_panic_boundary! { - let config = rustls::ClientConfig::new(); - let b = Box::new(config); - Box::into_raw(b) as *mut _ + let builder = rustls::ClientConfig::builder().with_safe_defaults(); + BoxCastPtr::to_mut_ptr(builder) } } -/// Create a rustls_client_config_builder from an existing rustls_client_config. The -/// builder will be used to create a new, separate config that starts with the settings -/// from the supplied configuration. +/// Create a rustls_client_config_builder. Caller owns the memory and must +/// eventually call rustls_client_config_builder_build, then free the +/// resulting rustls_client_config. Specify cipher suites in preference order; +/// the `cipher_suites` parameter must point to an array containing `len` +/// pointers to `rustls_supported_ciphersuite` previously obtained from +/// `rustls_all_ciphersuites_get()`. Set the TLS protocol versions to use +/// when negotiating a TLS session. +/// +/// `tls_version` is the version of the protocol, as defined in rfc8446, +/// ch. 4.2.1 and end of ch. 5.1. Some values are defined in +/// `rustls_tls_version` for convenience. +/// +/// `versions` will only be used during the call and the application retains +/// ownership. `len` is the number of consecutive `ui16` pointed to by `versions`. #[no_mangle] -pub extern "C" fn rustls_client_config_builder_from_config( - config: *const rustls_client_config, -) -> *mut rustls_client_config_builder { +pub extern "C" fn rustls_client_config_builder_new( + cipher_suites: *const *const rustls_supported_ciphersuite, + cipher_suites_len: size_t, + tls_versions: *const u16, + tls_versions_len: size_t, + builder: *mut *mut rustls_client_config_builder_wants_verifier, +) -> rustls_result { ffi_panic_boundary! { - let config: &ClientConfig = try_ref_from_ptr!(config); - Box::into_raw(Box::new(config.clone())) as *mut _ - } -} + let cipher_suites: &[*const rustls_supported_ciphersuite] = try_slice!(cipher_suites, cipher_suites_len); + let mut cs_vec: Vec = Vec::new(); + for &cs in cipher_suites.into_iter() { + let cs = try_ref_from_ptr!(cs); + match ALL_CIPHER_SUITES.iter().find(|&acs| cs.eq(acs)) { + Some(scs) => cs_vec.push(scs.clone()), + None => return InvalidParameter, + } + } -/// Turn a *rustls_client_config_builder (mutable) into a *rustls_client_config -/// (read-only). -#[no_mangle] -pub extern "C" fn rustls_client_config_builder_build( - builder: *mut rustls_client_config_builder, -) -> *const rustls_client_config { - ffi_panic_boundary! { - let config: &mut ClientConfig = try_mut_from_ptr!(builder); - let b = unsafe { Box::from_raw(config) }; - Arc::into_raw(Arc::new(*b)) as *const _ + let tls_versions: &[u16] = try_slice!(tls_versions, tls_versions_len); + let mut versions = vec![]; + for version_number in tls_versions { + let proto = ProtocolVersion::from(*version_number); + if proto == rustls::version::TLS12.version { + versions.push(&rustls::version::TLS12); + } else if proto == rustls::version::TLS13.version { + versions.push(&rustls::version::TLS13); + } + } + + let result = rustls::ClientConfig::builder().with_cipher_suites(&cs_vec).with_safe_default_kx_groups().with_protocol_versions(&versions); + let new = match result { + Ok(new) => new, + Err(_) => return rustls_result::InvalidParameter, + }; + + BoxCastPtr::set_mut_ptr(builder, new); + rustls_result::Ok } } @@ -108,7 +147,6 @@ pub extern "C" fn rustls_client_config_builder_build( pub struct rustls_verify_server_cert_params<'a> { pub end_entity_cert_der: rustls_slice_bytes<'a>, pub intermediate_certs_der: &'a rustls_slice_slice_bytes<'a>, - pub roots: *const rustls_root_cert_store, pub dns_name: rustls_str<'a>, pub ocsp_response: rustls_slice_bytes<'a>, } @@ -150,55 +188,50 @@ unsafe impl Send for Verifier {} /// rustls_client_config_builder_dangerous_set_certificate_verifier. unsafe impl Sync for Verifier {} -impl rustls::ServerCertVerifier for Verifier { +impl rustls::client::ServerCertVerifier for Verifier { fn verify_server_cert( &self, - roots: &RootCertStore, - presented_certs: &[Certificate], - dns_name: DNSNameRef<'_>, + end_entity: &Certificate, + intermediates: &[Certificate], + server_name: &rustls::ServerName, + _scts: &mut dyn Iterator, ocsp_response: &[u8], - ) -> Result { + _now: SystemTime, + ) -> Result { let cb = self.callback; - let dns_name: &str = dns_name.into(); + let dns_name: &str = match server_name { + rustls::ServerName::DnsName(n) => n.as_ref().into(), + _ => return Err(rustls::Error::General("unknown name type".to_string())), + }; let dns_name: rustls_str = match dns_name.try_into() { Ok(r) => r, - Err(NulByte {}) => return Err(TLSError::General("NUL byte in SNI".to_string())), + Err(NulByte {}) => return Err(rustls::Error::General("NUL byte in SNI".to_string())), }; - let mut certificates: Vec<&[u8]> = presented_certs - .iter() - .map(|cert: &Certificate| cert.as_ref()) + + let intermediates: Vec<_> = intermediates + .into_iter() + .map(|cert| cert.as_ref()) .collect(); - // In https://github.com/ctz/rustls/pull/462 (unreleased as of 0.19.0), - // rustls changed the verifier API to separate the end entity and intermediates. - // We anticipate that API by doing it ourselves. - let end_entity = match certificates.pop() { - Some(c) => c, - None => { - return Err(TLSError::General( - "missing end-entity certificate".to_string(), - )) - } - }; + let intermediates = rustls_slice_slice_bytes { - inner: &*certificates, + inner: &*intermediates, }; let params = rustls_verify_server_cert_params { - roots: (roots as *const RootCertStore) as *const rustls_root_cert_store, - end_entity_cert_der: end_entity.into(), + end_entity_cert_der: end_entity.as_ref().into(), intermediate_certs_der: &intermediates, dns_name: dns_name.into(), ocsp_response: ocsp_response.into(), }; let userdata = userdata_get().map_err(|_| { - TLSError::General("internal error with thread-local storage".to_string()) + rustls::Error::General("internal error with thread-local storage".to_string()) })?; let result: rustls_result = unsafe { cb(userdata, ¶ms) }; match result { rustls_result::Ok => Ok(ServerCertVerified::assertion()), - r => match result_to_tlserror(&r) { - error::Either::TLSError(te) => Err(te), - error::Either::String(se) => Err(TLSError::General(se)), + r => match result_to_error(&r) { + error::Either::Error(te) => Err(te), + error::Either::String(se) => Err(rustls::Error::General(se)), }, } } @@ -224,8 +257,8 @@ impl rustls::ServerCertVerifier for Verifier { /// /// If you intend to write a verifier that accepts all certificates, be aware /// that special measures are required for IP addresses. Rustls currently -/// (0.19.0) doesn't support building a ClientSession with an IP address -/// (because it's not a valid DNSNameRef). One workaround is to detect IP +/// (0.20.0) doesn't support building a ClientSession with an IP address +/// (because it's not a valid DnsNameRef). One workaround is to detect IP /// addresses and rewrite them to `example.invalid`, and _also_ to disable /// SNI via rustls_client_config_builder_set_enable_sni (IP addresses don't /// need SNI). @@ -235,20 +268,25 @@ impl rustls::ServerCertVerifier for Verifier { /// Feel free to use an appropriate error from the RUSTLS_RESULT_CERT_* /// section. /// -/// https://docs.rs/rustls/0.19.0/rustls/struct.DangerousClientConfig.html#method.set_certificate_verifier +/// https://docs.rs/rustls/0.20.0/rustls/client/struct.DangerousClientConfig.html#method.set_certificate_verifier #[no_mangle] pub extern "C" fn rustls_client_config_builder_dangerous_set_certificate_verifier( - config: *mut rustls_client_config_builder, + wants_verifier: *mut rustls_client_config_builder_wants_verifier, callback: rustls_verify_server_cert_callback, -) { + builder: *mut *mut rustls_client_config_builder, +) -> rustls_result { ffi_panic_boundary! { let callback: VerifyCallback = match callback { Some(cb) => cb, - None => return, + None => return rustls_result::InvalidParameter, }; - let config: &mut ClientConfig = try_mut_from_ptr!(config); + + let new = *BoxCastPtr::to_box(wants_verifier); let verifier: Verifier = Verifier{callback: callback}; - config.dangerous().set_certificate_verifier(Arc::new(verifier)); + // TODO: no client authentication support for now + let config = new.with_custom_certificate_verifier(Arc::new(verifier)).with_no_client_auth(); + BoxCastPtr::set_mut_ptr(builder, config); + rustls_result::Ok } } @@ -260,13 +298,16 @@ pub extern "C" fn rustls_client_config_builder_dangerous_set_certificate_verifie /// those will subtract 1 from the refcount for `roots`. #[no_mangle] pub extern "C" fn rustls_client_config_builder_use_roots( - config: *mut rustls_client_config_builder, + wants_verifier: *mut rustls_client_config_builder_wants_verifier, roots: *const rustls_root_cert_store, -) { + builder: *mut *mut rustls_client_config_builder, +) -> rustls_result { ffi_panic_boundary! { - let config: &mut ClientConfig = try_mut_from_ptr!(config); let root_store: &RootCertStore = try_ref_from_ptr!(roots); - config.root_store = root_store.clone(); + let prev = *BoxCastPtr::to_box(wants_verifier); + let config = prev.with_root_certificates(root_store.clone()).with_no_client_auth(); + BoxCastPtr::set_mut_ptr(builder, config); + rustls_result::Ok } } @@ -274,8 +315,9 @@ pub extern "C" fn rustls_client_config_builder_use_roots( /// PEM-formatted certificates. #[no_mangle] pub extern "C" fn rustls_client_config_builder_load_roots_from_file( - config: *mut rustls_client_config_builder, + wants_verifier: *mut rustls_client_config_builder_wants_verifier, filename: *const c_char, + builder: *mut *mut rustls_client_config_builder, ) -> rustls_result { ffi_panic_boundary! { let filename: &CStr = unsafe { @@ -284,7 +326,7 @@ pub extern "C" fn rustls_client_config_builder_load_roots_from_file( } CStr::from_ptr(filename) }; - let config: &mut ClientConfig = try_mut_from_ptr!(config); + let filename: &[u8] = filename.to_bytes(); let filename: &str = match std::str::from_utf8(filename) { Ok(s) => s, @@ -295,40 +337,24 @@ pub extern "C" fn rustls_client_config_builder_load_roots_from_file( Ok(f) => f, Err(_) => return rustls_result::Io, }; + let mut bufreader = BufReader::new(&mut cafile); - match config.root_store.add_pem_file(&mut bufreader) { - Ok(_) => {} + let certs = match rustls_pemfile::certs(&mut bufreader) { + Ok(certs) => certs, Err(_) => return rustls_result::Io, }; - rustls_result::Ok - } -} -/// Set the TLS protocol versions to use when negotiating a TLS session. -/// -/// `tls_version` is the version of the protocol, as defined in rfc8446, -/// ch. 4.2.1 and end of ch. 5.1. Some values are defined in -/// `rustls_tls_version` for convenience. -/// -/// `versions` will only be used during the call and the application retains -/// ownership. `len` is the number of consecutive `ui16` pointed to by `versions`. -#[no_mangle] -pub extern "C" fn rustls_client_config_builder_set_versions( - builder: *mut rustls_client_config_builder, - tls_versions: *const u16, - len: size_t, -) -> rustls_result { - ffi_panic_boundary! { - let config: &mut ClientConfig = try_mut_from_ptr!(builder); - let tls_versions: &[u16] = try_slice!(tls_versions, len); - config.versions.clear(); - - // rustls does not support an `Unkown(u16)` protocol version, - // so we have to fail on any version numbers not implemented - // in rustls. - for i in tls_versions { - config.versions.push(rustls_tls_version_from_u16(*i)); + let mut roots = RootCertStore::empty(); + let (_, failed) = roots.add_parsable_certificates(&certs); + if failed > 0 { + return rustls_result::CertificateParseError; } + + let prev = *BoxCastPtr::to_box(wants_verifier); + // TODO: no client authentication support for now + let config = prev.with_root_certificates(roots).with_no_client_auth(); + BoxCastPtr::set_mut_ptr(builder, config); + rustls_result::Ok } } @@ -343,9 +369,9 @@ pub extern "C" fn rustls_client_config_builder_set_versions( /// This function makes a copy of the data in `protocols` and does not retain /// any pointers, so the caller can free the pointed-to memory after calling. /// -/// https://docs.rs/rustls/0.19.0/rustls/struct.ClientConfig.html#method.set_protocols +/// https://docs.rs/rustls/0.20.0/rustls/client/struct.ClientConfig.html#structfield.alpn_protocols #[no_mangle] -pub extern "C" fn rustls_client_config_builder_set_protocols( +pub extern "C" fn rustls_client_config_builder_set_alpn_protocols( builder: *mut rustls_client_config_builder, protocols: *const rustls_slice_bytes, len: size_t, @@ -359,13 +385,13 @@ pub extern "C" fn rustls_client_config_builder_set_protocols( let v: &[u8] = try_slice!(p.data, p.len); vv.push(v.to_vec()); } - config.set_protocols(&vv); + config.alpn_protocols = vv; rustls_result::Ok } } /// Enable or disable SNI. -/// https://docs.rs/rustls/0.19.0/rustls/struct.ClientConfig.html#structfield.enable_sni +/// https://docs.rs/rustls/0.20.0/rustls/struct.ClientConfig.html#structfield.enable_sni #[no_mangle] pub extern "C" fn rustls_client_config_builder_set_enable_sni( config: *mut rustls_client_config_builder, @@ -377,30 +403,17 @@ pub extern "C" fn rustls_client_config_builder_set_enable_sni( } } -/// Set the cipher suite list, in preference order. The `ciphersuites` -/// parameter must point to an array containing `len` pointers to -/// `rustls_supported_ciphersuite` previously obtained from -/// `rustls_all_ciphersuites_get()`. -/// https://docs.rs/rustls/0.19.0/rustls/struct.ServerConfig.html#structfield.ciphersuites +/// "Free" a client_config_builder_wants_verifier before transmogrifying it into a client_config. +/// Normally builders are consumed to client_configs via `rustls_client_config_builder_build` +/// and may not be free'd or otherwise used afterwards. +/// Use free only when the building of a config has to be aborted before a config +/// was created. #[no_mangle] -pub extern "C" fn rustls_client_config_builder_set_ciphersuites( - builder: *mut rustls_client_config_builder, - ciphersuites: *const *const rustls_supported_ciphersuite, - len: size_t, -) -> rustls_result { +pub extern "C" fn rustls_client_config_builder_wants_verifier_free( + builder: *mut rustls_client_config_builder_wants_verifier, +) { ffi_panic_boundary! { - let config: &mut ClientConfig = try_mut_from_ptr!(builder); - let ciphersuites: &[*const rustls_supported_ciphersuite] = try_slice!(ciphersuites, len); - let mut cs_vec: Vec<&'static SupportedCipherSuite> = Vec::new(); - for &cs in ciphersuites.into_iter() { - let cs = try_ref_from_ptr!(cs); - match ALL_CIPHERSUITES.iter().find(|&acs| cs.eq(acs)) { - Some(scs) => cs_vec.push(scs), - None => return InvalidParameter, - } - } - config.ciphersuites = cs_vec; - rustls_result::Ok + BoxCastPtr::to_box(builder); } } @@ -451,10 +464,10 @@ impl ResolvesClientCert for ResolvesClientCertFromChoices { &self, _acceptable_issuers: &[&[u8]], sig_schemes: &[rustls::SignatureScheme], - ) -> Option { + ) -> Option> { for key in self.keys.iter() { if key.key.choose_scheme(sig_schemes).is_some() { - return Some(key.as_ref().clone()); + return Some(key.clone()); } } None @@ -465,6 +478,18 @@ impl ResolvesClientCert for ResolvesClientCertFromChoices { } } +/// Turn a *rustls_client_config_builder (mutable) into a const *rustls_client_config +/// (read-only). +#[no_mangle] +pub extern "C" fn rustls_client_config_builder_build( + builder: *mut rustls_client_config_builder, +) -> *const rustls_client_config { + ffi_panic_boundary! { + let b = BoxCastPtr::to_box(builder); + Arc::into_raw(Arc::new(*b)) as *const _ + } +} + /// "Free" a client_config_builder before transmogrifying it into a client_config. /// Normally builders are consumed to client_configs via `rustls_client_config_builder_build` /// and may not be free'd or otherwise used afterwards. @@ -473,9 +498,7 @@ impl ResolvesClientCert for ResolvesClientCertFromChoices { #[no_mangle] pub extern "C" fn rustls_client_config_builder_free(config: *mut rustls_client_config_builder) { ffi_panic_boundary! { - let config: &mut ClientConfig = try_mut_from_ptr!(config); - // Convert the pointer to a Box and drop it. - unsafe { Box::from_raw(config); } + BoxCastPtr::to_box(config); } } @@ -525,50 +548,17 @@ pub extern "C" fn rustls_client_connection_new( Ok(s) => s, Err(std::str::Utf8Error { .. }) => return rustls_result::InvalidDnsNameError, }; - let name_ref = match webpki::DNSNameRef::try_from_ascii_str(hostname) { - Ok(nr) => nr, - Err(webpki::InvalidDNSNameError { .. }) => return rustls_result::InvalidDnsNameError, + let server_name: rustls::ServerName = match hostname.try_into() { + Ok(sn) => sn, + Err(_) => return rustls_result::InvalidDnsNameError, }; + let client = ClientConnection::new(config, server_name).unwrap(); // We've succeeded. Put the client on the heap, and transfer ownership // to the caller. After this point, we must return CRUSTLS_OK so the // caller knows it is responsible for this memory. - let c = Connection::from_client( ClientSession::new(&config, name_ref)); - unsafe { - *conn_out = Box::into_raw(Box::new(c)) as *mut _; - } - - return rustls_result::Ok; - } -} - -/// Register callbacks for persistence of TLS session data. This means either -/// session IDs (TLSv1.2) or . Both -/// keys and values are highly sensitive data, containing enough information -/// to break the security of the sessions involved. -/// -/// If `userdata` has been set with rustls_connection_set_userdata, it -/// will be passed to the callbacks. Otherwise the userdata param passed to -/// the callbacks will be NULL. -#[no_mangle] -pub extern "C" fn rustls_client_config_builder_set_persistence( - builder: *mut rustls_client_config_builder, - get_cb: rustls_session_store_get_callback, - put_cb: rustls_session_store_put_callback, -) -> rustls_result { - ffi_panic_boundary! { - let get_cb: SessionStoreGetCallback = match get_cb { - Some(cb) => cb, - None => return rustls_result::NullParameter, - }; - let put_cb: SessionStorePutCallback = match put_cb { - Some(cb) => cb, - None => return rustls_result::NullParameter, - }; - let config: &mut ClientConfig = try_mut_from_ptr!(builder); - config.set_persistence(Arc::new(SessionStoreBroker::new( - get_cb, put_cb - ))); + let c = Connection::from_client(client); + BoxCastPtr::set_mut_ptr(conn_out, c); rustls_result::Ok } } diff --git a/src/connection.rs b/src/connection.rs index 426c46d0..bb34b028 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,15 +1,19 @@ +use std::io::{ErrorKind, Read, Write}; use std::{ffi::c_void, ptr::null}; use std::{ptr::null_mut, slice}; use libc::{size_t, EIO}; -use rustls::{Certificate, ClientSession, ServerSession, Session, SupportedCipherSuite}; +use rustls::{ + Certificate, ClientConnection, ServerConnection, SupportedCipherSuite, ALL_CIPHER_SUITES, +}; use crate::io::{ rustls_write_vectored_callback, CallbackReader, CallbackWriter, ReadCallback, VectoredCallbackWriter, VectoredWriteCallback, WriteCallback, }; -use crate::is_close_notify; use crate::log::{ensure_log_registered, rustls_log_callback}; + +use crate::BoxCastPtr; use crate::{ cipher::{rustls_certificate, rustls_supported_ciphersuite}, error::{map_error, rustls_io_result, rustls_result}, @@ -21,83 +25,71 @@ use crate::{try_mut_from_ptr, try_slice, userdata_push, CastPtr}; use rustls_result::NullParameter; pub(crate) struct Connection { - conn: Inner, + conn: rustls::Connection, userdata: *mut c_void, log_callback: rustls_log_callback, - peer_certs: Option>, -} - -enum Inner { - Client(ClientSession), - Server(ServerSession), } impl Connection { - pub(crate) fn from_client(s: ClientSession) -> Self { + pub(crate) fn from_client(conn: ClientConnection) -> Self { Connection { - conn: Inner::Client(s), + conn: conn.into(), userdata: null_mut(), log_callback: None, - peer_certs: None, } } - pub(crate) fn from_server(s: ServerSession) -> Self { + pub(crate) fn from_server(conn: ServerConnection) -> Self { Connection { - conn: Inner::Server(s), + conn: conn.into(), userdata: null_mut(), log_callback: None, - peer_certs: None, } } #[allow(dead_code)] - pub(crate) fn as_client(&self) -> Option<&ClientSession> { + pub(crate) fn as_client(&self) -> Option<&ClientConnection> { match &self.conn { - Inner::Client(c) => Some(c), + rustls::Connection::Client(c) => Some(c), _ => None, } } - pub(crate) fn as_server(&self) -> Option<&ServerSession> { + pub(crate) fn as_server(&self) -> Option<&ServerConnection> { match &self.conn { - Inner::Server(s) => Some(s), + rustls::Connection::Server(s) => Some(s), _ => None, } } #[allow(dead_code)] - pub(crate) fn as_client_mut(&mut self) -> Option<&mut ClientSession> { + pub(crate) fn as_client_mut(&mut self) -> Option<&mut ClientConnection> { match &mut self.conn { - Inner::Client(c) => Some(c), + rustls::Connection::Client(c) => Some(c), _ => None, } } #[allow(dead_code)] - pub(crate) fn as_server_mut(&mut self) -> Option<&mut ServerSession> { + pub(crate) fn as_server_mut(&mut self) -> Option<&mut ServerConnection> { match &mut self.conn { - Inner::Server(s) => Some(s), + rustls::Connection::Server(s) => Some(s), _ => None, } } } -impl<'conn> AsRef for Connection { - fn as_ref(&self) -> &(dyn Session + 'conn) { - match &self.conn { - Inner::Client(c) => c, - Inner::Server(c) => c, - } +impl std::ops::Deref for Connection { + type Target = rustls::Connection; + + fn deref(&self) -> &Self::Target { + &self.conn } } -impl<'conn> AsMut for Connection { - fn as_mut(&mut self) -> &mut (dyn Session + 'conn) { - match &mut self.conn { - Inner::Client(c) => c, - Inner::Server(c) => c, - } +impl std::ops::DerefMut for Connection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.conn } } @@ -109,6 +101,8 @@ impl CastPtr for rustls_connection { type RustType = Connection; } +impl BoxCastPtr for rustls_connection {} + /// Set the userdata pointer associated with this connection. This will be passed /// to any callbacks invoked by the connection, if you've set up callbacks in the config. /// The pointed-to data must outlive the connection. @@ -143,7 +137,7 @@ pub extern "C" fn rustls_connection_set_log_callback( /// `rustls_connection_set_userdata`. /// Returns 0 for success, or an errno value on error. Passes through return values /// from callback. See rustls_read_callback for more details. -/// https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.read_tls +/// https://docs.rs/rustls/0.20.0/rustls/enum.Connection.html#method.read_tls #[no_mangle] pub extern "C" fn rustls_connection_read_tls( conn: *mut rustls_connection, @@ -157,7 +151,7 @@ pub extern "C" fn rustls_connection_read_tls( let callback: ReadCallback = try_callback!(callback); let mut reader = CallbackReader { callback, userdata }; - let n_read: usize = match conn.as_mut().read_tls(&mut reader) { + let n_read: usize = match conn.read_tls(&mut reader) { Ok(n) => n, Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), }; @@ -176,7 +170,7 @@ pub extern "C" fn rustls_connection_read_tls( /// `rustls_connection_set_userdata`. /// Returns 0 for success, or an errno value on error. Passes through return values /// from callback. See rustls_write_callback for more details. -/// https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.write_tls +/// https://docs.rs/rustls/0.20.0/rustls/enum.Connection.html#method.write_tls #[no_mangle] pub extern "C" fn rustls_connection_write_tls( conn: *mut rustls_connection, @@ -190,7 +184,7 @@ pub extern "C" fn rustls_connection_write_tls( let callback: WriteCallback = try_callback!(callback); let mut writer = CallbackWriter { callback, userdata }; - let n_written: usize = match conn.as_mut().write_tls(&mut writer) { + let n_written: usize = match conn.write_tls(&mut writer) { Ok(n) => n, Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), }; @@ -209,7 +203,7 @@ pub extern "C" fn rustls_connection_write_tls( /// `rustls_connection_set_userdata`. /// Returns 0 for success, or an errno value on error. Passes through return values /// from callback. See rustls_write_callback for more details. -/// https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.write_tls +/// https://docs.rs/rustls/0.20.0/rustls/trait.Session.html#tymethod.write_tls #[no_mangle] pub extern "C" fn rustls_connection_write_tls_vectored( conn: *mut rustls_connection, @@ -223,7 +217,7 @@ pub extern "C" fn rustls_connection_write_tls_vectored( let callback: VectoredWriteCallback = try_callback!(callback); let mut writer = VectoredCallbackWriter { callback, userdata }; - let n_written: usize = match conn.as_mut().write_tls(&mut writer) { + let n_written: usize = match conn.write_tls(&mut writer) { Ok(n) => n, Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), }; @@ -243,8 +237,8 @@ pub extern "C" fn rustls_connection_process_new_packets( Ok(g) => g, Err(_) => return rustls_result::Panic, }; - let result = match conn.as_mut().process_new_packets() { - Ok(()) => rustls_result::Ok, + let result = match conn.process_new_packets() { + Ok(_) => rustls_result::Ok, Err(e) => map_error(e), }; match guard.try_drop() { @@ -258,7 +252,7 @@ pub extern "C" fn rustls_connection_process_new_packets( pub extern "C" fn rustls_connection_wants_read(conn: *const rustls_connection) -> bool { ffi_panic_boundary! { let conn: &Connection = try_ref_from_ptr!(conn); - conn.as_ref().wants_read() + conn.wants_read() } } @@ -266,7 +260,7 @@ pub extern "C" fn rustls_connection_wants_read(conn: *const rustls_connection) - pub extern "C" fn rustls_connection_wants_write(conn: *const rustls_connection) -> bool { ffi_panic_boundary! { let conn: &Connection = try_ref_from_ptr!(conn); - conn.as_ref().wants_write() + conn.wants_write() } } @@ -274,7 +268,7 @@ pub extern "C" fn rustls_connection_wants_write(conn: *const rustls_connection) pub extern "C" fn rustls_connection_is_handshaking(conn: *const rustls_connection) -> bool { ffi_panic_boundary! { let conn: &Connection = try_ref_from_ptr!(conn); - conn.as_ref().is_handshaking() + conn.is_handshaking() } } @@ -282,22 +276,22 @@ pub extern "C" fn rustls_connection_is_handshaking(conn: *const rustls_connectio /// to completing the TLS handshake) and unsent TLS records. By default, there /// is no limit. The limit can be set at any time, even if the current buffer /// use is higher. -/// https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.set_buffer_limit +/// https://docs.rs/rustls/0.20.0/rustls/enum.Connection.html#method.set_buffer_limit #[no_mangle] pub extern "C" fn rustls_connection_set_buffer_limit(conn: *mut rustls_connection, n: usize) { ffi_panic_boundary! { let conn: &mut Connection = try_mut_from_ptr!(conn); - conn.as_mut().set_buffer_limit(n); + conn.set_buffer_limit(Some(n)); } } /// Queues a close_notify fatal alert to be sent in the next write_tls call. -/// https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.send_close_notify +/// https://docs.rs/rustls/0.20.0/rustls/enum.Connection.html#method.send_close_notify #[no_mangle] pub extern "C" fn rustls_connection_send_close_notify(conn: *mut rustls_connection) { ffi_panic_boundary! { let conn: &mut Connection = try_mut_from_ptr!(conn); - conn.as_mut().send_close_notify(); + conn.send_close_notify(); } } @@ -305,29 +299,15 @@ pub extern "C" fn rustls_connection_send_close_notify(conn: *mut rustls_connecti /// Index 0 is the end entity certificate. Higher indexes are certificates /// in the chain. Requesting an index higher than what is available returns /// NULL. +/// The returned pointer lives as long as the rustls_connection does. #[no_mangle] -pub extern "C" fn rustls_connection_get_peer_certificate( +pub extern "C" fn rustls_connection_peer_certificate( conn: *mut rustls_connection, i: size_t, ) -> *const rustls_certificate { - // TODO: this should be changed in the next rustls release where the - // API no longer returns copies but references to the certificates it - // keeps. We then no longer have to hold our own Vec. ffi_panic_boundary! { let conn: &mut Connection = try_mut_from_ptr!(conn); - let certs = match &conn.peer_certs { - Some(certs) => certs, - None => { - match conn.as_ref().get_peer_certificates() { - Some(certs) => { - conn.peer_certs = Some(certs); - conn.peer_certs.as_ref().unwrap() - }, - None => return null() - } - } - }; - match certs.get(i) { + match conn.peer_certificates().and_then(|c| c.get(i)) { Some(cert) => cert as *const Certificate as *const _, None => null() } @@ -340,9 +320,9 @@ pub extern "C" fn rustls_connection_get_peer_certificate( /// If the connection is still handshaking, or no ALPN protocol was negotiated, /// stores NULL and 0 in the output parameters. /// https://www.iana.org/assignments/tls-parameters/ -/// https://docs.rs/rustls/0.19.1/rustls/trait.Session.html#tymethod.get_alpn_protocol +/// https://docs.rs/rustls/0.19.1/rustls/trait.Connection.html#tymethod.get_alpn_protocol #[no_mangle] -pub extern "C" fn rustls_connection_get_alpn_protocol( +pub extern "C" fn rustls_connection_alpn_protocol( conn: *const rustls_connection, protocol_out: *mut *const u8, protocol_out_len: *mut usize, @@ -351,7 +331,7 @@ pub extern "C" fn rustls_connection_get_alpn_protocol( let conn: &Connection = try_ref_from_ptr!(conn); let protocol_out = try_mut_from_ptr!(protocol_out); let protocol_out_len = try_mut_from_ptr!(protocol_out_len); - match conn.as_ref().get_alpn_protocol() { + match conn.alpn_protocol() { Some(p) => { *protocol_out = p.as_ptr(); *protocol_out_len = p.len(); @@ -367,13 +347,13 @@ pub extern "C" fn rustls_connection_get_alpn_protocol( /// Return the TLS protocol version that has been negotiated. Before this /// has been decided during the handshake, this will return 0. Otherwise, /// the u16 version number as defined in the relevant RFC is returned. -/// https://docs.rs/rustls/0.19.1/rustls/trait.Session.html#tymethod.get_protocol_version +/// https://docs.rs/rustls/0.19.1/rustls/trait.Connection.html#tymethod.get_protocol_version /// https://docs.rs/rustls/0.19.1/rustls/internal/msgs/enums/enum.ProtocolVersion.html #[no_mangle] -pub extern "C" fn rustls_connection_get_protocol_version(conn: *const rustls_connection) -> u16 { +pub extern "C" fn rustls_connection_protocol_version(conn: *const rustls_connection) -> u16 { ffi_panic_boundary! { let conn: &Connection = try_ref_from_ptr!(conn); - match conn.as_ref().get_protocol_version() { + match conn.protocol_version() { Some(p) => p.get_u16(), _ => 0, } @@ -382,17 +362,24 @@ pub extern "C" fn rustls_connection_get_protocol_version(conn: *const rustls_con /// Retrieves the cipher suite agreed with the peer. /// This returns NULL until the ciphersuite is agreed. -/// https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.get_negotiated_ciphersuite +/// The returned pointer lives as long as the program. +/// https://docs.rs/rustls/0.20.0/rustls/enum.Connection.html#method.get_negotiated_ciphersuite #[no_mangle] -pub extern "C" fn rustls_connection_get_negotiated_ciphersuite( +pub extern "C" fn rustls_connection_negotiated_ciphersuite( conn: *const rustls_connection, ) -> *const rustls_supported_ciphersuite { ffi_panic_boundary! { let conn: &Connection = try_ref_from_ptr!(conn); - match conn.as_ref().get_negotiated_ciphersuite() { - Some(cs) => cs as *const SupportedCipherSuite as *const _, - None => null(), + let negotiated = match conn.negotiated_cipher_suite() { + Some(cs) => cs, + None => return null(), + }; + for &cs in ALL_CIPHER_SUITES { + if negotiated == cs { + return &cs as *const SupportedCipherSuite as *const _; + } } + null() } } @@ -417,7 +404,7 @@ pub extern "C" fn rustls_connection_write( None => return NullParameter, } }; - let n_written: usize = match conn.as_mut().write(write_buf) { + let n_written: usize = match conn.writer().write(write_buf) { Ok(n) => n, Err(_) => return rustls_result::Io, }; @@ -449,13 +436,10 @@ pub extern "C" fn rustls_connection_read( let read_buf: &mut [u8] = try_mut_slice!(buf, count); let out_n: &mut size_t = try_mut_from_ptr!(out_n); - let n_read: usize = match conn.as_mut().read(read_buf) { + let n_read: usize = match conn.reader().read(read_buf) { Ok(n) => n, - // Rustls turns close_notify alerts into `io::Error` of kind `ConnectionAborted`. - // https://docs.rs/rustls/0.19.0/rustls/struct.ClientSession.html#impl-Read. - Err(e) if is_close_notify(&e) => { - return rustls_result::AlertCloseNotify; - } + Err(e) if e.kind() == ErrorKind::UnexpectedEof => return rustls_result::UnexpectedEof, + Err(e) if e.kind() == ErrorKind::WouldBlock => return rustls_result::PlaintextEmpty, Err(_) => return rustls_result::Io, }; *out_n = n_read; diff --git a/src/crustls.h b/src/crustls.h index 7f87200c..dd23be45 100644 --- a/src/crustls.h +++ b/src/crustls.h @@ -18,13 +18,23 @@ typedef enum rustls_result { RUSTLS_RESULT_INSUFFICIENT_SIZE = 7007, RUSTLS_RESULT_NOT_FOUND = 7008, RUSTLS_RESULT_INVALID_PARAMETER = 7009, + RUSTLS_RESULT_UNEXPECTED_EOF = 7010, + RUSTLS_RESULT_PLAINTEXT_EMPTY = 7011, RUSTLS_RESULT_CORRUPT_MESSAGE = 7100, RUSTLS_RESULT_NO_CERTIFICATES_PRESENTED = 7101, RUSTLS_RESULT_DECRYPT_ERROR = 7102, RUSTLS_RESULT_FAILED_TO_GET_CURRENT_TIME = 7103, + RUSTLS_RESULT_FAILED_TO_GET_RANDOM_BYTES = 7113, RUSTLS_RESULT_HANDSHAKE_NOT_COMPLETE = 7104, RUSTLS_RESULT_PEER_SENT_OVERSIZED_RECORD = 7105, RUSTLS_RESULT_NO_APPLICATION_PROTOCOL = 7106, + RUSTLS_RESULT_BAD_MAX_FRAGMENT_SIZE = 7114, + RUSTLS_RESULT_UNSUPPORTED_NAME_TYPE = 7115, + RUSTLS_RESULT_ENCRYPT_ERROR = 7116, + RUSTLS_RESULT_CERT_INVALID_ENCODING = 7117, + RUSTLS_RESULT_CERT_INVALID_SIGNATURE_TYPE = 7118, + RUSTLS_RESULT_CERT_INVALID_SIGNATURE = 7119, + RUSTLS_RESULT_CERT_INVALID_DATA = 7120, RUSTLS_RESULT_PEER_INCOMPATIBLE_ERROR = 7107, RUSTLS_RESULT_PEER_MISBEHAVED_ERROR = 7108, RUSTLS_RESULT_INAPPROPRIATE_MESSAGE = 7109, @@ -66,25 +76,6 @@ typedef enum rustls_result { RUSTLS_RESULT_ALERT_CERTIFICATE_REQUIRED = 7232, RUSTLS_RESULT_ALERT_NO_APPLICATION_PROTOCOL = 7233, RUSTLS_RESULT_ALERT_UNKNOWN = 7234, - RUSTLS_RESULT_CERT_BAD_DER = 7300, - RUSTLS_RESULT_CERT_BAD_DER_TIME = 7301, - RUSTLS_RESULT_CERT_CA_USED_AS_END_ENTITY = 7302, - RUSTLS_RESULT_CERT_EXPIRED = 7303, - RUSTLS_RESULT_CERT_NOT_VALID_FOR_NAME = 7304, - RUSTLS_RESULT_CERT_NOT_VALID_YET = 7305, - RUSTLS_RESULT_CERT_END_ENTITY_USED_AS_CA = 7306, - RUSTLS_RESULT_CERT_EXTENSION_VALUE_INVALID = 7307, - RUSTLS_RESULT_CERT_INVALID_CERT_VALIDITY = 7308, - RUSTLS_RESULT_CERT_INVALID_SIGNATURE_FOR_PUBLIC_KEY = 7309, - RUSTLS_RESULT_CERT_NAME_CONSTRAINT_VIOLATION = 7310, - RUSTLS_RESULT_CERT_PATH_LEN_CONSTRAINT_VIOLATED = 7311, - RUSTLS_RESULT_CERT_SIGNATURE_ALGORITHM_MISMATCH = 7312, - RUSTLS_RESULT_CERT_REQUIRED_EKU_NOT_FOUND = 7313, - RUSTLS_RESULT_CERT_UNKNOWN_ISSUER = 7314, - RUSTLS_RESULT_CERT_UNSUPPORTED_CERT_VERSION = 7315, - RUSTLS_RESULT_CERT_UNSUPPORTED_CRITICAL_EXTENSION = 7316, - RUSTLS_RESULT_CERT_UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_PUBLIC_KEY = 7317, - RUSTLS_RESULT_CERT_UNSUPPORTED_SIGNATURE_ALGORITHM = 7318, RUSTLS_RESULT_CERT_SCT_MALFORMED = 7319, RUSTLS_RESULT_CERT_SCT_INVALID_SIGNATURE = 7320, RUSTLS_RESULT_CERT_SCT_TIMESTAMP_IN_FUTURE = 7321, @@ -107,7 +98,7 @@ typedef enum rustls_tls_version { /** * An X.509 certificate, as used in rustls. * Corresponds to `Certificate` in the Rust API. - * https://docs.rs/rustls/0.19.0/rustls/struct.CertifiedKey.html + * https://docs.rs/rustls/0.20.0/rustls/struct.Certificate.html */ typedef struct rustls_certificate rustls_certificate; @@ -115,13 +106,13 @@ typedef struct rustls_certificate rustls_certificate; * The complete chain of certificates to send during a TLS handshake, * plus a private key that matches the end-entity (leaf) certificate. * Corresponds to `CertifiedKey` in the Rust API. - * https://docs.rs/rustls/0.19.0/rustls/sign/struct.CertifiedKey.html + * https://docs.rs/rustls/0.20.0/rustls/sign/struct.CertifiedKey.html */ typedef struct rustls_certified_key rustls_certified_key; /** * A verifier of client certificates that requires all certificates to be - * trusted based on a given`rustls_root_cert_store`. Usable in building server + * trusted based on a given `rustls_root_cert_store`. Usable in building server * configurations. Connections without such a client certificate will not * be accepted. */ @@ -141,10 +132,12 @@ typedef struct rustls_client_cert_verifier_optional rustls_client_cert_verifier_ /** * A client config that is done being constructed and is now read-only. * Under the hood, this object corresponds to an Arc. - * https://docs.rs/rustls/0.19.0/rustls/struct.ClientConfig.html + * https://docs.rs/rustls/0.20.0/rustls/struct.ClientConfig.html */ typedef struct rustls_client_config rustls_client_config; +typedef struct rustls_client_config_builder rustls_client_config_builder; + /** * A client config being constructed. A builder can be modified by, * e.g. rustls_client_config_builder_load_roots_from_file. Once you're @@ -152,9 +145,9 @@ typedef struct rustls_client_config rustls_client_config; * to turn it into a *rustls_client_config. This object is not safe * for concurrent mutation. Under the hood, it corresponds to a * Box. - * https://docs.rs/rustls/0.19.0/rustls/struct.ClientConfig.html + * https://docs.rs/rustls/0.20.0/rustls/struct.ClientConfig.html */ -typedef struct rustls_client_config_builder rustls_client_config_builder; +typedef struct rustls_client_config_builder_wants_verifier rustls_client_config_builder_wants_verifier; typedef struct rustls_connection rustls_connection; @@ -169,14 +162,14 @@ typedef struct rustls_iovec rustls_iovec; /** * A root cert store that is done being constructed and is now read-only. * Under the hood, this object corresponds to an Arc. - * https://docs.rs/rustls/0.19.0/rustls/struct.RootCertStore.html + * https://docs.rs/rustls/0.20.0/rustls/struct.RootCertStore.html */ typedef struct rustls_root_cert_store rustls_root_cert_store; /** * A server config that is done being constructed and is now read-only. * Under the hood, this object corresponds to an Arc. - * https://docs.rs/rustls/0.19.0/rustls/struct.ServerConfig.html + * https://docs.rs/rustls/0.20.0/rustls/struct.ServerConfig.html */ typedef struct rustls_server_config rustls_server_config; @@ -186,11 +179,15 @@ typedef struct rustls_server_config rustls_server_config; * done configuring settings, call rustls_server_config_builder_build * to turn it into a *rustls_server_config. This object is not safe * for concurrent mutation. Under the hood, it corresponds to a - * Box. - * https://docs.rs/rustls/0.19.0/rustls/struct.ServerConfig.html + * Box. + * https://docs.rs/rustls/0.20.0/rustls/struct.ServerConfig.html */ typedef struct rustls_server_config_builder rustls_server_config_builder; +typedef struct rustls_server_config_builder_wants_server_cert rustls_server_config_builder_wants_server_cert; + +typedef struct rustls_server_config_builder_wants_verifier rustls_server_config_builder_wants_verifier; + /** * A read-only view of a slice of Rust byte slices. * @@ -281,63 +278,12 @@ typedef struct rustls_str { typedef struct rustls_verify_server_cert_params { struct rustls_slice_bytes end_entity_cert_der; const struct rustls_slice_slice_bytes *intermediate_certs_der; - const struct rustls_root_cert_store *roots; struct rustls_str dns_name; struct rustls_slice_bytes ocsp_response; } rustls_verify_server_cert_params; typedef enum rustls_result (*rustls_verify_server_cert_callback)(rustls_verify_server_cert_user_data userdata, const struct rustls_verify_server_cert_params *params); -/** - * Any context information the callback will receive when invoked. - */ -typedef void *rustls_session_store_userdata; - -/** - * Prototype of a callback that can be installed by the application at the - * `rustls_server_config` or `rustls_client_config`. This callback will be - * invoked by a TLS session when looking up the data for a TLS session id. - * `userdata` will be supplied based on rustls_{client,server}_session_set_userdata. - * - * The `buf` points to `count` consecutive bytes where the - * callback is expected to copy the result to. The number of copied bytes - * needs to be written to `out_n`. The callback should not read any - * data from `buf`. - * - * If the value to copy is larger than `count`, the callback should never - * do a partial copy but instead remove the value from its store and - * act as if it was never found. - * - * The callback should return != 0 to indicate that a value was retrieved - * and written in its entirety into `buf`. - * - * When `remove_after` is != 0, the returned data needs to be removed - * from the store. - * - * NOTE: the passed in `key` and `buf` are only available during the - * callback invocation. - * NOTE: callbacks used in several sessions via a common config - * must be implemented thread-safe. - */ -typedef enum rustls_result (*rustls_session_store_get_callback)(rustls_session_store_userdata userdata, const struct rustls_slice_bytes *key, int remove_after, uint8_t *buf, size_t count, size_t *out_n); - -/** - * Prototype of a callback that can be installed by the application at the - * `rustls_server_config` or `rustls_client_config`. This callback will be - * invoked by a TLS session when a TLS session has been created and an id - * for later use is handed to the client/has been received from the server. - * `userdata` will be supplied based on rustls_{client,server}_session_set_userdata. - * - * The callback should return != 0 to indicate that the value has been - * successfully persisted in its store. - * - * NOTE: the passed in `key` and `val` are only available during the - * callback invocation. - * NOTE: callbacks used in several sessions via a common config - * must be implemented thread-safe. - */ -typedef enum rustls_result (*rustls_session_store_put_callback)(rustls_session_store_userdata userdata, const struct rustls_slice_bytes *key, const struct rustls_slice_bytes *val); - typedef size_t rustls_log_level; typedef struct rustls_log_params { @@ -433,7 +379,7 @@ typedef struct rustls_slice_u16 { * The signature_schemes carries the values supplied by the client or, should * the client not use this TLS extension, the default schemes in the rustls * library. See: - * https://docs.rs/rustls/0.19.0/rustls/internal/msgs/enums/enum.SignatureScheme.html + * https://docs.rs/rustls/0.20.0/rustls/internal/msgs/enums/enum.SignatureScheme.html * `alpn` carries the list of ALPN protocol names that the client proposed to * the server. Again, the length of this list will be 0 if none were supplied. * @@ -469,6 +415,56 @@ typedef struct rustls_client_hello { */ typedef const struct rustls_certified_key *(*rustls_client_hello_callback)(rustls_client_hello_userdata userdata, const struct rustls_client_hello *hello); +/** + * Any context information the callback will receive when invoked. + */ +typedef void *rustls_session_store_userdata; + +/** + * Prototype of a callback that can be installed by the application at the + * `rustls_server_config` or `rustls_client_config`. This callback will be + * invoked by a TLS session when looking up the data for a TLS session id. + * `userdata` will be supplied based on rustls_{client,server}_session_set_userdata. + * + * The `buf` points to `count` consecutive bytes where the + * callback is expected to copy the result to. The number of copied bytes + * needs to be written to `out_n`. The callback should not read any + * data from `buf`. + * + * If the value to copy is larger than `count`, the callback should never + * do a partial copy but instead remove the value from its store and + * act as if it was never found. + * + * The callback should return != 0 to indicate that a value was retrieved + * and written in its entirety into `buf`. + * + * When `remove_after` is != 0, the returned data needs to be removed + * from the store. + * + * NOTE: the passed in `key` and `buf` are only available during the + * callback invocation. + * NOTE: callbacks used in several sessions via a common config + * must be implemented thread-safe. + */ +typedef enum rustls_result (*rustls_session_store_get_callback)(rustls_session_store_userdata userdata, const struct rustls_slice_bytes *key, int remove_after, uint8_t *buf, size_t count, size_t *out_n); + +/** + * Prototype of a callback that can be installed by the application at the + * `rustls_server_config` or `rustls_client_config`. This callback will be + * invoked by a TLS session when a TLS session has been created and an id + * for later use is handed to the client/has been received from the server. + * `userdata` will be supplied based on rustls_{client,server}_session_set_userdata. + * + * The callback should return != 0 to indicate that the value has been + * successfully persisted in its store. + * + * NOTE: the passed in `key` and `val` are only available during the + * callback invocation. + * NOTE: callbacks used in several sessions via a common config + * must be implemented thread-safe. + */ +typedef enum rustls_result (*rustls_session_store_put_callback)(rustls_session_store_userdata userdata, const struct rustls_slice_bytes *key, const struct rustls_slice_bytes *val); + /** * Write the version of the crustls C bindings and rustls itself into the * provided buffer, up to a max of `len` bytes. Output is UTF-8 encoded @@ -562,7 +558,7 @@ void rustls_certified_key_free(const struct rustls_certified_key *key); * Create a rustls_root_cert_store. Caller owns the memory and must * eventually call rustls_root_cert_store_free. The store starts out empty. * Caller must add root certificates with rustls_root_cert_store_add_pem. - * https://docs.rs/rustls/0.19.0/rustls/struct.RootCertStore.html#method.empty + * https://docs.rs/rustls/0.20.0/rustls/struct.RootCertStore.html#method.empty */ struct rustls_root_cert_store *rustls_root_cert_store_new(void); @@ -629,24 +625,36 @@ void rustls_client_cert_verifier_optional_free(const struct rustls_client_cert_v /** * Create a rustls_client_config_builder. Caller owns the memory and must * eventually call rustls_client_config_builder_build, then free the - * resulting rustls_client_config. This starts out with no trusted roots. + * resulting rustls_client_config. + * This uses rustls safe default values + * for the cipher suites, key exchange groups and protocol versions. + * This starts out with no trusted roots. * Caller must add roots with rustls_client_config_builder_load_roots_from_file * or provide a custom verifier. */ -struct rustls_client_config_builder *rustls_client_config_builder_new(void); +struct rustls_client_config_builder_wants_verifier *rustls_client_config_builder_new_with_safe_defaults(void); /** - * Create a rustls_client_config_builder from an existing rustls_client_config. The - * builder will be used to create a new, separate config that starts with the settings - * from the supplied configuration. - */ -struct rustls_client_config_builder *rustls_client_config_builder_from_config(const struct rustls_client_config *config); - -/** - * Turn a *rustls_client_config_builder (mutable) into a *rustls_client_config - * (read-only). + * Create a rustls_client_config_builder. Caller owns the memory and must + * eventually call rustls_client_config_builder_build, then free the + * resulting rustls_client_config. Specify cipher suites in preference order; + * the `cipher_suites` parameter must point to an array containing `len` + * pointers to `rustls_supported_ciphersuite` previously obtained from + * `rustls_all_ciphersuites_get()`. Set the TLS protocol versions to use + * when negotiating a TLS session. + * + * `tls_version` is the version of the protocol, as defined in rfc8446, + * ch. 4.2.1 and end of ch. 5.1. Some values are defined in + * `rustls_tls_version` for convenience. + * + * `versions` will only be used during the call and the application retains + * ownership. `len` is the number of consecutive `ui16` pointed to by `versions`. */ -const struct rustls_client_config *rustls_client_config_builder_build(struct rustls_client_config_builder *builder); +enum rustls_result rustls_client_config_builder_new(const struct rustls_supported_ciphersuite *const *cipher_suites, + size_t cipher_suites_len, + const uint16_t *tls_versions, + size_t tls_versions_len, + struct rustls_client_config_builder_wants_verifier **builder); /** * Set a custom server certificate verifier. @@ -669,8 +677,8 @@ const struct rustls_client_config *rustls_client_config_builder_build(struct rus * * If you intend to write a verifier that accepts all certificates, be aware * that special measures are required for IP addresses. Rustls currently - * (0.19.0) doesn't support building a ClientSession with an IP address - * (because it's not a valid DNSNameRef). One workaround is to detect IP + * (0.20.0) doesn't support building a ClientSession with an IP address + * (because it's not a valid DnsNameRef). One workaround is to detect IP * addresses and rewrite them to `example.invalid`, and _also_ to disable * SNI via rustls_client_config_builder_set_enable_sni (IP addresses don't * need SNI). @@ -680,10 +688,11 @@ const struct rustls_client_config *rustls_client_config_builder_build(struct rus * Feel free to use an appropriate error from the RUSTLS_RESULT_CERT_* * section. * - * https://docs.rs/rustls/0.19.0/rustls/struct.DangerousClientConfig.html#method.set_certificate_verifier + * https://docs.rs/rustls/0.20.0/rustls/client/struct.DangerousClientConfig.html#method.set_certificate_verifier */ -void rustls_client_config_builder_dangerous_set_certificate_verifier(struct rustls_client_config_builder *config, - rustls_verify_server_cert_callback callback); +enum rustls_result rustls_client_config_builder_dangerous_set_certificate_verifier(struct rustls_client_config_builder_wants_verifier *wants_verifier, + rustls_verify_server_cert_callback callback, + struct rustls_client_config_builder **builder); /** * Use the trusted root certificates from the provided store. @@ -693,29 +702,17 @@ void rustls_client_config_builder_dangerous_set_certificate_verifier(struct rust * call rustls_client_config_free or rustls_client_config_builder_free, * those will subtract 1 from the refcount for `roots`. */ -void rustls_client_config_builder_use_roots(struct rustls_client_config_builder *config, - const struct rustls_root_cert_store *roots); +enum rustls_result rustls_client_config_builder_use_roots(struct rustls_client_config_builder_wants_verifier *wants_verifier, + const struct rustls_root_cert_store *roots, + struct rustls_client_config_builder **builder); /** * Add trusted root certificates from the named file, which should contain * PEM-formatted certificates. */ -enum rustls_result rustls_client_config_builder_load_roots_from_file(struct rustls_client_config_builder *config, - const char *filename); - -/** - * Set the TLS protocol versions to use when negotiating a TLS session. - * - * `tls_version` is the version of the protocol, as defined in rfc8446, - * ch. 4.2.1 and end of ch. 5.1. Some values are defined in - * `rustls_tls_version` for convenience. - * - * `versions` will only be used during the call and the application retains - * ownership. `len` is the number of consecutive `ui16` pointed to by `versions`. - */ -enum rustls_result rustls_client_config_builder_set_versions(struct rustls_client_config_builder *builder, - const uint16_t *tls_versions, - size_t len); +enum rustls_result rustls_client_config_builder_load_roots_from_file(struct rustls_client_config_builder_wants_verifier *wants_verifier, + const char *filename, + struct rustls_client_config_builder **builder); /** * Set the ALPN protocol list to the given protocols. `protocols` must point @@ -728,29 +725,27 @@ enum rustls_result rustls_client_config_builder_set_versions(struct rustls_clien * This function makes a copy of the data in `protocols` and does not retain * any pointers, so the caller can free the pointed-to memory after calling. * - * https://docs.rs/rustls/0.19.0/rustls/struct.ClientConfig.html#method.set_protocols + * https://docs.rs/rustls/0.20.0/rustls/client/struct.ClientConfig.html#structfield.alpn_protocols */ -enum rustls_result rustls_client_config_builder_set_protocols(struct rustls_client_config_builder *builder, - const struct rustls_slice_bytes *protocols, - size_t len); +enum rustls_result rustls_client_config_builder_set_alpn_protocols(struct rustls_client_config_builder *builder, + const struct rustls_slice_bytes *protocols, + size_t len); /** * Enable or disable SNI. - * https://docs.rs/rustls/0.19.0/rustls/struct.ClientConfig.html#structfield.enable_sni + * https://docs.rs/rustls/0.20.0/rustls/struct.ClientConfig.html#structfield.enable_sni */ void rustls_client_config_builder_set_enable_sni(struct rustls_client_config_builder *config, bool enable); /** - * Set the cipher suite list, in preference order. The `ciphersuites` - * parameter must point to an array containing `len` pointers to - * `rustls_supported_ciphersuite` previously obtained from - * `rustls_all_ciphersuites_get()`. - * https://docs.rs/rustls/0.19.0/rustls/struct.ServerConfig.html#structfield.ciphersuites + * "Free" a client_config_builder_wants_verifier before transmogrifying it into a client_config. + * Normally builders are consumed to client_configs via `rustls_client_config_builder_build` + * and may not be free'd or otherwise used afterwards. + * Use free only when the building of a config has to be aborted before a config + * was created. */ -enum rustls_result rustls_client_config_builder_set_ciphersuites(struct rustls_client_config_builder *builder, - const struct rustls_supported_ciphersuite *const *ciphersuites, - size_t len); +void rustls_client_config_builder_wants_verifier_free(struct rustls_client_config_builder_wants_verifier *builder); /** * Provide the configuration a list of certificates where the session @@ -770,6 +765,12 @@ enum rustls_result rustls_client_config_builder_set_certified_key(struct rustls_ const struct rustls_certified_key *const *certified_keys, size_t certified_keys_len); +/** + * Turn a *rustls_client_config_builder (mutable) into a const *rustls_client_config + * (read-only). + */ +const struct rustls_client_config *rustls_client_config_builder_build(struct rustls_client_config_builder *builder); + /** * "Free" a client_config_builder before transmogrifying it into a client_config. * Normally builders are consumed to client_configs via `rustls_client_config_builder_build` @@ -801,20 +802,6 @@ enum rustls_result rustls_client_connection_new(const struct rustls_client_confi const char *hostname, struct rustls_connection **conn_out); -/** - * Register callbacks for persistence of TLS session data. This means either - * session IDs (TLSv1.2) or . Both - * keys and values are highly sensitive data, containing enough information - * to break the security of the sessions involved. - * - * If `userdata` has been set with rustls_connection_set_userdata, it - * will be passed to the callbacks. Otherwise the userdata param passed to - * the callbacks will be NULL. - */ -enum rustls_result rustls_client_config_builder_set_persistence(struct rustls_client_config_builder *builder, - rustls_session_store_get_callback get_cb, - rustls_session_store_put_callback put_cb); - /** * Set the userdata pointer associated with this connection. This will be passed * to any callbacks invoked by the connection, if you've set up callbacks in the config. @@ -839,7 +826,7 @@ void rustls_connection_set_log_callback(struct rustls_connection *conn, rustls_l * `rustls_connection_set_userdata`. * Returns 0 for success, or an errno value on error. Passes through return values * from callback. See rustls_read_callback for more details. - * https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.read_tls + * https://docs.rs/rustls/0.20.0/rustls/enum.Connection.html#method.read_tls */ rustls_io_result rustls_connection_read_tls(struct rustls_connection *conn, rustls_read_callback callback, @@ -856,7 +843,7 @@ rustls_io_result rustls_connection_read_tls(struct rustls_connection *conn, * `rustls_connection_set_userdata`. * Returns 0 for success, or an errno value on error. Passes through return values * from callback. See rustls_write_callback for more details. - * https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.write_tls + * https://docs.rs/rustls/0.20.0/rustls/enum.Connection.html#method.write_tls */ rustls_io_result rustls_connection_write_tls(struct rustls_connection *conn, rustls_write_callback callback, @@ -873,7 +860,7 @@ rustls_io_result rustls_connection_write_tls(struct rustls_connection *conn, * `rustls_connection_set_userdata`. * Returns 0 for success, or an errno value on error. Passes through return values * from callback. See rustls_write_callback for more details. - * https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.write_tls + * https://docs.rs/rustls/0.20.0/rustls/trait.Session.html#tymethod.write_tls */ rustls_io_result rustls_connection_write_tls_vectored(struct rustls_connection *conn, rustls_write_vectored_callback callback, @@ -893,13 +880,13 @@ bool rustls_connection_is_handshaking(const struct rustls_connection *conn); * to completing the TLS handshake) and unsent TLS records. By default, there * is no limit. The limit can be set at any time, even if the current buffer * use is higher. - * https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.set_buffer_limit + * https://docs.rs/rustls/0.20.0/rustls/enum.Connection.html#method.set_buffer_limit */ void rustls_connection_set_buffer_limit(struct rustls_connection *conn, size_t n); /** * Queues a close_notify fatal alert to be sent in the next write_tls call. - * https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.send_close_notify + * https://docs.rs/rustls/0.20.0/rustls/enum.Connection.html#method.send_close_notify */ void rustls_connection_send_close_notify(struct rustls_connection *conn); @@ -908,9 +895,10 @@ void rustls_connection_send_close_notify(struct rustls_connection *conn); * Index 0 is the end entity certificate. Higher indexes are certificates * in the chain. Requesting an index higher than what is available returns * NULL. + * The returned pointer lives as long as the rustls_connection does. */ -const struct rustls_certificate *rustls_connection_get_peer_certificate(struct rustls_connection *conn, - size_t i); +const struct rustls_certificate *rustls_connection_peer_certificate(struct rustls_connection *conn, + size_t i); /** * Get the ALPN protocol that was negotiated, if any. Stores a pointer to a @@ -919,27 +907,28 @@ const struct rustls_certificate *rustls_connection_get_peer_certificate(struct r * If the connection is still handshaking, or no ALPN protocol was negotiated, * stores NULL and 0 in the output parameters. * https://www.iana.org/assignments/tls-parameters/ - * https://docs.rs/rustls/0.19.1/rustls/trait.Session.html#tymethod.get_alpn_protocol + * https://docs.rs/rustls/0.19.1/rustls/trait.Connection.html#tymethod.get_alpn_protocol */ -void rustls_connection_get_alpn_protocol(const struct rustls_connection *conn, - const uint8_t **protocol_out, - size_t *protocol_out_len); +void rustls_connection_alpn_protocol(const struct rustls_connection *conn, + const uint8_t **protocol_out, + size_t *protocol_out_len); /** * Return the TLS protocol version that has been negotiated. Before this * has been decided during the handshake, this will return 0. Otherwise, * the u16 version number as defined in the relevant RFC is returned. - * https://docs.rs/rustls/0.19.1/rustls/trait.Session.html#tymethod.get_protocol_version + * https://docs.rs/rustls/0.19.1/rustls/trait.Connection.html#tymethod.get_protocol_version * https://docs.rs/rustls/0.19.1/rustls/internal/msgs/enums/enum.ProtocolVersion.html */ -uint16_t rustls_connection_get_protocol_version(const struct rustls_connection *conn); +uint16_t rustls_connection_protocol_version(const struct rustls_connection *conn); /** * Retrieves the cipher suite agreed with the peer. * This returns NULL until the ciphersuite is agreed. - * https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.get_negotiated_ciphersuite + * The returned pointer lives as long as the program. + * https://docs.rs/rustls/0.20.0/rustls/enum.Connection.html#method.get_negotiated_ciphersuite */ -const struct rustls_supported_ciphersuite *rustls_connection_get_negotiated_ciphersuite(const struct rustls_connection *conn); +const struct rustls_supported_ciphersuite *rustls_connection_negotiated_ciphersuite(const struct rustls_connection *conn); /** * Write up to `count` plaintext bytes from `buf` into the `rustls_connection`. @@ -1022,10 +1011,37 @@ struct rustls_str rustls_slice_str_get(const struct rustls_slice_str *input, siz /** * Create a rustls_server_config_builder. Caller owns the memory and must * eventually call rustls_server_config_builder_build, then free the - * resulting rustls_server_config. - * https://docs.rs/rustls/0.19.0/rustls/struct.ServerConfig.html#method.new + * resulting rustls_server_config. This uses rustls safe default values + * for the cipher suites, key exchange groups and protocol versions. */ -struct rustls_server_config_builder *rustls_server_config_builder_new(void); +struct rustls_server_config_builder_wants_verifier *rustls_server_config_builder_new_with_safe_defaults(void); + +/** + * Create a rustls_server_config_builder. Caller owns the memory and must + * eventually call rustls_server_config_builder_build, then free the + * resulting rustls_server_config. Specify cipher suites in preference order; + * the `cipher_suites` parameter must point to an array containing `len` + * pointers to `rustls_supported_ciphersuite` previously obtained from + * `rustls_all_ciphersuites_get()`. Set the TLS protocol versions to use + * when negotiating a TLS session. + * + * `tls_version` is the version of the protocol, as defined in rfc8446, + * ch. 4.2.1 and end of ch. 5.1. Some values are defined in + * `rustls_tls_version` for convenience. + * + * `versions` will only be used during the call and the application retains + * ownership. `len` is the number of consecutive `ui16` pointed to by `versions`. + */ +enum rustls_result rustls_server_config_builder_new(const struct rustls_supported_ciphersuite *const *cipher_suites, + size_t cipher_suites_len, + const uint16_t *tls_versions, + size_t tls_versions_len, + struct rustls_server_config_builder_wants_verifier **builder); + +/** + * For memory lifetime, see rustls_server_config_builder_new. + */ +struct rustls_server_config_builder *rustls_server_config_builder_with_no_client_auth(struct rustls_server_config_builder_wants_verifier *wants_verifier); /** * Create a rustls_server_config_builder for TLS sessions that require @@ -1034,7 +1050,9 @@ struct rustls_server_config_builder *rustls_server_config_builder_new(void); * If input is NULL, this will return NULL. * For memory lifetime, see rustls_server_config_builder_new. */ -struct rustls_server_config_builder *rustls_server_config_builder_with_client_verifier(const struct rustls_client_cert_verifier *verifier); +enum rustls_result rustls_server_config_builder_with_client_verifier(struct rustls_server_config_builder_wants_verifier *wants_verifier, + const struct rustls_client_cert_verifier *verifier, + struct rustls_server_config_builder_wants_server_cert **builder); /** * Create a rustls_server_config_builder for TLS sessions that accept @@ -1061,25 +1079,11 @@ void rustls_server_config_builder_free(struct rustls_server_config_builder *conf */ struct rustls_server_config_builder *rustls_server_config_builder_from_config(const struct rustls_server_config *config); -/** - * Set the TLS protocol versions to use when negotiating a TLS session. - * - * `tls_version` is the version of the protocol, as defined in rfc8446, - * ch. 4.2.1 and end of ch. 5.1. Some values are defined in - * `rustls_tls_version` for convenience. - * - * `versions` will only be used during the call and the application retains - * ownership. `len` is the number of consecutive `ui16` pointed to by `versions`. - */ -enum rustls_result rustls_server_config_builder_set_versions(struct rustls_server_config_builder *builder, - const uint16_t *tls_versions, - size_t len); - /** * With `ignore` != 0, the server will ignore the client ordering of cipher * suites, aka preference, during handshake and respect its own ordering * as configured. - * https://docs.rs/rustls/0.19.0/rustls/struct.ServerConfig.html#fields + * https://docs.rs/rustls/0.20.0/rustls/struct.ServerConfig.html#fields */ enum rustls_result rustls_server_config_builder_set_ignore_client_order(struct rustls_server_config_builder *builder, bool ignore); @@ -1094,22 +1098,11 @@ enum rustls_result rustls_server_config_builder_set_ignore_client_order(struct r * This function makes a copy of the data in `protocols` and does not retain * any pointers, so the caller can free the pointed-to memory after calling. * - * https://docs.rs/rustls/0.19.0/rustls/struct.ServerConfig.html#method.set_protocols - */ -enum rustls_result rustls_server_config_builder_set_protocols(struct rustls_server_config_builder *builder, - const struct rustls_slice_bytes *protocols, - size_t len); - -/** - * Set the cipher suite list, in preference order. The `ciphersuites` - * parameter must point to an array containing `len` pointers to - * `rustls_supported_ciphersuite` previously obtained from - * `rustls_all_ciphersuites_get()`. - * https://docs.rs/rustls/0.19.0/rustls/struct.ServerConfig.html#structfield.ciphersuites + * https://docs.rs/rustls/0.20.0/rustls/server/struct.ServerConfig.html#structfield.alpn_protocols */ -enum rustls_result rustls_server_config_builder_set_ciphersuites(struct rustls_server_config_builder *builder, - const struct rustls_supported_ciphersuite *const *ciphersuites, - size_t len); +enum rustls_result rustls_server_config_builder_set_alpn_protocols(struct rustls_server_config_builder *builder, + const struct rustls_slice_bytes *protocols, + size_t len); /** * Provide the configuration a list of certificates where the session @@ -1130,7 +1123,7 @@ enum rustls_result rustls_server_config_builder_set_certified_keys(struct rustls size_t certified_keys_len); /** - * Turn a *rustls_server_config_builder (mutable) into a *rustls_server_config + * Turn a *rustls_server_config_builder (mutable) into a const *rustls_server_config * (read-only). */ const struct rustls_server_config *rustls_server_config_builder_build(struct rustls_server_config_builder *builder); @@ -1163,7 +1156,7 @@ enum rustls_result rustls_server_connection_new(const struct rustls_server_confi * Returns RUSTLS_RESULT_INSUFFICIENT_SIZE if the SNI hostname is longer than `count`. * Returns Ok with *out_n == 0 if there is no SNI hostname available on this session * because it hasn't been processed yet, or because the client did not send SNI. - * https://docs.rs/rustls/0.19.0/rustls/struct.ServerSession.html#method.get_sni_hostname + * https://docs.rs/rustls/0.20.0/rustls/server/struct.ServerConnection.html#method.sni_hostname */ enum rustls_result rustls_server_connection_get_sni_hostname(const struct rustls_connection *conn, uint8_t *buf, diff --git a/src/enums.rs b/src/enums.rs index a40b6118..f83214a7 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -9,22 +9,3 @@ pub enum rustls_tls_version { Tlsv1_2 = 0x0303, Tlsv1_3 = 0x0304, } - -pub(crate) fn rustls_tls_version_from_u16(version_num: u16) -> rustls::ProtocolVersion { - match version_num { - // This is very bad and will be fixes in rustls by - // . - // - // In Rust: - // rustls::ProtocolVersion::SSLv2 - // does NOT match rustls::ProtocolVersion::Unknown(0x0200) - // - 0x0200 => rustls::ProtocolVersion::SSLv2, - 0x0300 => rustls::ProtocolVersion::SSLv3, - 0x0301 => rustls::ProtocolVersion::TLSv1_0, - 0x0302 => rustls::ProtocolVersion::TLSv1_1, - 0x0303 => rustls::ProtocolVersion::TLSv1_2, - 0x0304 => rustls::ProtocolVersion::TLSv1_3, - n => rustls::ProtocolVersion::Unknown(n), - } -} diff --git a/src/error.rs b/src/error.rs index f4251001..3608c347 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,7 +2,7 @@ use std::{cmp::min, fmt::Display, slice}; use crate::ffi_panic_boundary; use libc::{c_char, size_t}; -use rustls::TLSError; +use rustls::Error; /// A return value for a function that may return either success (0) or a /// non-zero value representing an error. The values should match socket @@ -45,9 +45,14 @@ pub extern "C" fn rustls_error( #[no_mangle] pub extern "C" fn rustls_result_is_cert_error(result: rustls_result) -> bool { - match result_to_tlserror(&result) { - Either::TLSError(TLSError::WebPKIError(_)) => true, - Either::TLSError(TLSError::InvalidSCT(_)) => true, + match result_to_error(&result) { + Either::Error( + Error::InvalidCertificateData(_) + | Error::InvalidCertificateEncoding + | Error::InvalidCertificateSignature + | Error::InvalidCertificateSignatureType + | Error::InvalidSct(_), + ) => true, _ => false, } } @@ -65,17 +70,27 @@ pub enum rustls_result { InsufficientSize = 7007, NotFound = 7008, InvalidParameter = 7009, + UnexpectedEof = 7010, + PlaintextEmpty = 7011, - // From https://docs.rs/rustls/0.19.0/rustls/enum.TLSError.html + // From https://docs.rs/rustls/0.20.0/rustls/enum.Error.html CorruptMessage = 7100, NoCertificatesPresented = 7101, DecryptError = 7102, FailedToGetCurrentTime = 7103, + FailedToGetRandomBytes = 7113, HandshakeNotComplete = 7104, PeerSentOversizedRecord = 7105, NoApplicationProtocol = 7106, + BadMaxFragmentSize = 7114, + UnsupportedNameType = 7115, + EncryptError = 7116, + CertInvalidEncoding = 7117, + CertInvalidSignatureType = 7118, + CertInvalidSignature = 7119, + CertInvalidData = 7120, // Last added - // From TLSError, with fields that get dropped. + // From Error, with fields that get dropped. PeerIncompatibleError = 7107, PeerMisbehavedError = 7108, InappropriateMessage = 7109, @@ -83,8 +98,8 @@ pub enum rustls_result { CorruptMessagePayload = 7111, General = 7112, - // From TLSError, with fields that get flattened. - // https://docs.rs/rustls/0.19.0/rustls/internal/msgs/enums/enum.AlertDescription.html + // From Error, with fields that get flattened. + // https://docs.rs/rustls/0.20.0/rustls/internal/msgs/enums/enum.AlertDescription.html AlertCloseNotify = 7200, AlertUnexpectedMessage = 7201, AlertBadRecordMac = 7202, @@ -121,27 +136,6 @@ pub enum rustls_result { AlertNoApplicationProtocol = 7233, AlertUnknown = 7234, - // https://docs.rs/webpki/0.21.4/webpki/enum.Error.html - CertBadDER = 7300, - CertBadDERTime = 7301, - CertCAUsedAsEndEntity = 7302, - CertExpired = 7303, - CertNotValidForName = 7304, - CertNotValidYet = 7305, - CertEndEntityUsedAsCA = 7306, - CertExtensionValueInvalid = 7307, - CertInvalidCertValidity = 7308, - CertInvalidSignatureForPublicKey = 7309, - CertNameConstraintViolation = 7310, - CertPathLenConstraintViolated = 7311, - CertSignatureAlgorithmMismatch = 7312, - CertRequiredEKUNotFound = 7313, - CertUnknownIssuer = 7314, - CertUnsupportedCertVersion = 7315, - CertUnsupportedCriticalExtension = 7316, - CertUnsupportedSignatureAlgorithmForPublicKey = 7317, - CertUnsupportedSignatureAlgorithm = 7318, - // https://docs.rs/sct/0.5.0/sct/enum.Error.html CertSCTMalformed = 7319, CertSCTInvalidSignature = 7320, @@ -150,29 +144,38 @@ pub enum rustls_result { CertSCTUnknownLog = 7323, } -pub(crate) fn map_error(input: rustls::TLSError) -> rustls_result { +pub(crate) fn map_error(input: rustls::Error) -> rustls_result { use rustls::internal::msgs::enums::AlertDescription as alert; use rustls_result::*; use sct::Error as sct; - use webpki::Error as webpki; match input { - TLSError::CorruptMessage => CorruptMessage, - TLSError::NoCertificatesPresented => NoCertificatesPresented, - TLSError::DecryptError => DecryptError, - TLSError::FailedToGetCurrentTime => FailedToGetCurrentTime, - TLSError::HandshakeNotComplete => HandshakeNotComplete, - TLSError::PeerSentOversizedRecord => PeerSentOversizedRecord, - TLSError::NoApplicationProtocol => NoApplicationProtocol, + Error::InappropriateMessage { .. } => InappropriateMessage, + Error::InappropriateHandshakeMessage { .. } => InappropriateHandshakeMessage, + Error::CorruptMessage => CorruptMessage, + Error::CorruptMessagePayload(_) => CorruptMessagePayload, + Error::NoCertificatesPresented => NoCertificatesPresented, + Error::DecryptError => DecryptError, + Error::PeerIncompatibleError(_) => PeerIncompatibleError, + Error::PeerMisbehavedError(_) => PeerMisbehavedError, + Error::UnsupportedNameType => UnsupportedNameType, + Error::EncryptError => EncryptError, + + Error::FailedToGetCurrentTime => FailedToGetCurrentTime, + Error::FailedToGetRandomBytes => FailedToGetRandomBytes, + Error::HandshakeNotComplete => HandshakeNotComplete, + Error::PeerSentOversizedRecord => PeerSentOversizedRecord, + Error::NoApplicationProtocol => NoApplicationProtocol, + Error::BadMaxFragmentSize => BadMaxFragmentSize, - TLSError::PeerIncompatibleError(_) => PeerIncompatibleError, - TLSError::PeerMisbehavedError(_) => PeerMisbehavedError, - TLSError::General(_) => General, - TLSError::InappropriateMessage { .. } => InappropriateMessage, - TLSError::InappropriateHandshakeMessage { .. } => InappropriateHandshakeMessage, - TLSError::CorruptMessagePayload(_) => CorruptMessagePayload, + Error::InvalidCertificateEncoding => CertInvalidEncoding, + Error::InvalidCertificateSignatureType => CertInvalidSignatureType, + Error::InvalidCertificateSignature => CertInvalidSignature, + Error::InvalidCertificateData(_) => CertInvalidData, - TLSError::AlertReceived(e) => match e { + Error::General(_) => General, + + Error::AlertReceived(e) => match e { alert::CloseNotify => AlertCloseNotify, alert::UnexpectedMessage => AlertUnexpectedMessage, alert::BadRecordMac => AlertBadRecordMac, @@ -209,34 +212,11 @@ pub(crate) fn map_error(input: rustls::TLSError) -> rustls_result { alert::NoApplicationProtocol => AlertNoApplicationProtocol, alert::Unknown(_) => AlertUnknown, }, - TLSError::WebPKIError(e) => match e { - webpki::BadDER => CertBadDER, - webpki::BadDERTime => CertBadDERTime, - webpki::CAUsedAsEndEntity => CertCAUsedAsEndEntity, - webpki::CertExpired => CertExpired, - webpki::CertNotValidForName => CertNotValidForName, - webpki::CertNotValidYet => CertNotValidYet, - webpki::EndEntityUsedAsCA => CertEndEntityUsedAsCA, - webpki::ExtensionValueInvalid => CertExtensionValueInvalid, - webpki::InvalidCertValidity => CertInvalidCertValidity, - webpki::InvalidSignatureForPublicKey => CertInvalidSignatureForPublicKey, - webpki::NameConstraintViolation => CertNameConstraintViolation, - webpki::PathLenConstraintViolated => CertPathLenConstraintViolated, - webpki::SignatureAlgorithmMismatch => CertSignatureAlgorithmMismatch, - webpki::RequiredEKUNotFound => CertRequiredEKUNotFound, - webpki::UnknownIssuer => CertUnknownIssuer, - webpki::UnsupportedCertVersion => CertUnsupportedCertVersion, - webpki::UnsupportedCriticalExtension => CertUnsupportedCriticalExtension, - webpki::UnsupportedSignatureAlgorithmForPublicKey => { - CertUnsupportedSignatureAlgorithmForPublicKey - } - webpki::UnsupportedSignatureAlgorithm => CertUnsupportedSignatureAlgorithm, - }, - TLSError::InvalidSCT(e) => match e { - sct::MalformedSCT => CertSCTMalformed, + Error::InvalidSct(e) => match e { + sct::MalformedSct => CertSCTMalformed, sct::InvalidSignature => CertSCTInvalidSignature, sct::TimestampInFuture => CertSCTTimestampInFuture, - sct::UnsupportedSCTVersion => CertSCTUnsupportedVersion, + sct::UnsupportedSctVersion => CertSCTUnsupportedVersion, sct::UnknownLog => CertSCTUnknownLog, }, } @@ -244,157 +224,139 @@ pub(crate) fn map_error(input: rustls::TLSError) -> rustls_result { impl Display for rustls_result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let msg: String = match result_to_tlserror(self) { + let msg: String = match result_to_error(self) { Either::String(s) => s, - Either::TLSError(e) => e.to_string(), + Either::Error(e) => e.to_string(), }; write!(f, "{}", msg) } } -// Either a String or a TLSError +// Either a String or a rustls::Error pub(crate) enum Either { String(String), - TLSError(TLSError), + Error(rustls::Error), +} + +impl Into for String { + fn into(self) -> Either { + Either::String(self) + } +} + +impl Into for &str { + fn into(self) -> Either { + Either::String(self.to_string()) + } +} + +impl Into for webpki::Error { + fn into(self) -> Either { + Either::String(self.to_string()) + } +} + +impl Into for rustls::Error { + fn into(self) -> Either { + Either::Error(self) + } } -// Turn a rustls_result into a TLSError on a best-effort basis. For -// variants that don't have a corresponding TLSError, or where we want to -// override TLSError's Display implementation, this returns a String. -// Otherwise, it returns a TLSError. This is used internally for determining +// Turn a rustls_result into a rustls::Error on a best-effort basis. For +// variants that don't have a corresponding rustls::Error, or where we want to +// override rustls::Error's Display implementation, this returns a String. +// Otherwise, it returns a rustls::Error. This is used internally for determining // whether a rustls_result is part of some top-level variant that maps to // several rustls_results. -pub(crate) fn result_to_tlserror(input: &rustls_result) -> Either { +pub(crate) fn result_to_error(input: &rustls_result) -> Either { use rustls::internal::msgs::enums::AlertDescription as alert; use rustls_result::*; use sct::Error as sct; - use webpki::Error as webpki; match input { // These variants are local to this glue layer. - rustls_result::Ok => return Either::String("OK".to_string()), - Io => return Either::String("I/O error".to_string()), - NullParameter => return Either::String("a parameter was NULL".to_string()), - InvalidDnsNameError => return Either::String( - "hostname was either malformed or an IP address (rustls does not support certificates for IP addresses)".to_string()), - Panic => return Either::String("a Rust component panicked".to_string()), - CertificateParseError => return Either::String("error parsing certificate".to_string()), - PrivateKeyParseError => return Either::String("error parsing private key".to_string()), - InsufficientSize => return Either::String("provided buffer is of insufficient size".to_string()), - NotFound => return Either::String("the item was not found".to_string()), - InvalidParameter => return Either::String("a parameter had an invalid value".to_string()), + rustls_result::Ok => "OK".into(), + Io => "I/O error".into(), + NullParameter => "a parameter was NULL".into(), + InvalidDnsNameError => "hostname was either malformed or an IP address (rustls does not support certificates for IP addresses)".into(), + Panic => "a Rust component panicked".into(), + CertificateParseError => "error parsing certificate".into(), + PrivateKeyParseError => "error parsing private key".into(), + InsufficientSize => "provided buffer is of insufficient size".into(), + NotFound => "the item was not found".into(), + InvalidParameter => "a parameter had an invalid value".into(), + CertInvalidData => "invalid certificate data found".into(), + UnexpectedEof => "unexpected EOF".into(), + PlaintextEmpty => "no plaintext available; call rustls_connection_read_tls again".into(), - // These variants correspond to a TLSError variant with a field, + // These variants correspond to a rustls::Error variant with a field, // where generating an arbitrary field would produce a confusing error // message. So we reproduce a simplified error string. - InappropriateMessage => { - return Either::String("received unexpected message".to_string()); - } - InappropriateHandshakeMessage => { - return Either::String("received unexpected handshake message".to_string()); - } - CorruptMessagePayload => return Either::String("received corrupt message".to_string()), - _ => {} - }; - - let e: TLSError = match input { - rustls_result::Ok => unreachable!(), - Io => unreachable!(), - NullParameter => unreachable!(), - InvalidDnsNameError => unreachable!(), - Panic => unreachable!(), - CertificateParseError => unreachable!(), - PrivateKeyParseError => unreachable!(), - InsufficientSize => unreachable!(), - NotFound => unreachable!(), - InvalidParameter => unreachable!(), + InappropriateMessage => "received unexpected message".into(), + InappropriateHandshakeMessage => "received unexpected handshake message".into(), + CorruptMessagePayload => "received corrupt message".into(), - InappropriateMessage => unreachable!(), - InappropriateHandshakeMessage => unreachable!(), - CorruptMessagePayload => unreachable!(), + CorruptMessage => Error::CorruptMessage.into(), + NoCertificatesPresented => Error::NoCertificatesPresented.into(), + DecryptError => Error::DecryptError.into(), + FailedToGetCurrentTime => Error::FailedToGetCurrentTime.into(), + FailedToGetRandomBytes => Error::FailedToGetRandomBytes.into(), + HandshakeNotComplete => Error::HandshakeNotComplete.into(), + PeerSentOversizedRecord => Error::PeerSentOversizedRecord.into(), + NoApplicationProtocol => Error::NoApplicationProtocol.into(), + PeerIncompatibleError => Error::PeerIncompatibleError("reason omitted".to_string()).into(), + PeerMisbehavedError => Error::PeerMisbehavedError("reason omitted".to_string()).into(), + BadMaxFragmentSize => Error::BadMaxFragmentSize.into(), + UnsupportedNameType => Error::UnsupportedNameType.into(), + EncryptError => Error::EncryptError.into(), + CertInvalidEncoding => Error::InvalidCertificateEncoding.into(), + CertInvalidSignatureType => Error::InvalidCertificateSignatureType.into(), + CertInvalidSignature => Error::InvalidCertificateSignature.into(), - CorruptMessage => TLSError::CorruptMessage, - NoCertificatesPresented => TLSError::NoCertificatesPresented, - DecryptError => TLSError::DecryptError, - FailedToGetCurrentTime => TLSError::FailedToGetCurrentTime, - HandshakeNotComplete => TLSError::HandshakeNotComplete, - PeerSentOversizedRecord => TLSError::PeerSentOversizedRecord, - NoApplicationProtocol => TLSError::NoApplicationProtocol, - PeerIncompatibleError => TLSError::PeerIncompatibleError("reason omitted".to_string()), - PeerMisbehavedError => TLSError::PeerMisbehavedError("reason omitted".to_string()), - General => TLSError::General("omitted".to_string()), + General => Error::General("omitted".to_string()).into(), - AlertCloseNotify => TLSError::AlertReceived(alert::CloseNotify), - AlertUnexpectedMessage => TLSError::AlertReceived(alert::UnexpectedMessage), - AlertBadRecordMac => TLSError::AlertReceived(alert::BadRecordMac), - AlertDecryptionFailed => TLSError::AlertReceived(alert::DecryptionFailed), - AlertRecordOverflow => TLSError::AlertReceived(alert::RecordOverflow), - AlertDecompressionFailure => TLSError::AlertReceived(alert::DecompressionFailure), - AlertHandshakeFailure => TLSError::AlertReceived(alert::HandshakeFailure), - AlertNoCertificate => TLSError::AlertReceived(alert::NoCertificate), - AlertBadCertificate => TLSError::AlertReceived(alert::BadCertificate), - AlertUnsupportedCertificate => TLSError::AlertReceived(alert::UnsupportedCertificate), - AlertCertificateRevoked => TLSError::AlertReceived(alert::CertificateRevoked), - AlertCertificateExpired => TLSError::AlertReceived(alert::CertificateExpired), - AlertCertificateUnknown => TLSError::AlertReceived(alert::CertificateUnknown), - AlertIllegalParameter => TLSError::AlertReceived(alert::IllegalParameter), - AlertUnknownCA => TLSError::AlertReceived(alert::UnknownCA), - AlertAccessDenied => TLSError::AlertReceived(alert::AccessDenied), - AlertDecodeError => TLSError::AlertReceived(alert::DecodeError), - AlertDecryptError => TLSError::AlertReceived(alert::DecryptError), - AlertExportRestriction => TLSError::AlertReceived(alert::ExportRestriction), - AlertProtocolVersion => TLSError::AlertReceived(alert::ProtocolVersion), - AlertInsufficientSecurity => TLSError::AlertReceived(alert::InsufficientSecurity), - AlertInternalError => TLSError::AlertReceived(alert::InternalError), - AlertInappropriateFallback => TLSError::AlertReceived(alert::InappropriateFallback), - AlertUserCanceled => TLSError::AlertReceived(alert::UserCanceled), - AlertNoRenegotiation => TLSError::AlertReceived(alert::NoRenegotiation), - AlertMissingExtension => TLSError::AlertReceived(alert::MissingExtension), - AlertUnsupportedExtension => TLSError::AlertReceived(alert::UnsupportedExtension), - AlertCertificateUnobtainable => TLSError::AlertReceived(alert::CertificateUnobtainable), - AlertUnrecognisedName => TLSError::AlertReceived(alert::UnrecognisedName), + AlertCloseNotify => Error::AlertReceived(alert::CloseNotify).into(), + AlertUnexpectedMessage => Error::AlertReceived(alert::UnexpectedMessage).into(), + AlertBadRecordMac => Error::AlertReceived(alert::BadRecordMac).into(), + AlertDecryptionFailed => Error::AlertReceived(alert::DecryptionFailed).into(), + AlertRecordOverflow => Error::AlertReceived(alert::RecordOverflow).into(), + AlertDecompressionFailure => Error::AlertReceived(alert::DecompressionFailure).into(), + AlertHandshakeFailure => Error::AlertReceived(alert::HandshakeFailure).into(), + AlertNoCertificate => Error::AlertReceived(alert::NoCertificate).into(), + AlertBadCertificate => Error::AlertReceived(alert::BadCertificate).into(), + AlertUnsupportedCertificate => Error::AlertReceived(alert::UnsupportedCertificate).into(), + AlertCertificateRevoked => Error::AlertReceived(alert::CertificateRevoked).into(), + AlertCertificateExpired => Error::AlertReceived(alert::CertificateExpired).into(), + AlertCertificateUnknown => Error::AlertReceived(alert::CertificateUnknown).into(), + AlertIllegalParameter => Error::AlertReceived(alert::IllegalParameter).into(), + AlertUnknownCA => Error::AlertReceived(alert::UnknownCA).into(), + AlertAccessDenied => Error::AlertReceived(alert::AccessDenied).into(), + AlertDecodeError => Error::AlertReceived(alert::DecodeError).into(), + AlertDecryptError => Error::AlertReceived(alert::DecryptError).into(), + AlertExportRestriction => Error::AlertReceived(alert::ExportRestriction).into(), + AlertProtocolVersion => Error::AlertReceived(alert::ProtocolVersion).into(), + AlertInsufficientSecurity => Error::AlertReceived(alert::InsufficientSecurity).into(), + AlertInternalError => Error::AlertReceived(alert::InternalError).into(), + AlertInappropriateFallback => Error::AlertReceived(alert::InappropriateFallback).into(), + AlertUserCanceled => Error::AlertReceived(alert::UserCanceled).into(), + AlertNoRenegotiation => Error::AlertReceived(alert::NoRenegotiation).into(), + AlertMissingExtension => Error::AlertReceived(alert::MissingExtension).into(), + AlertUnsupportedExtension => Error::AlertReceived(alert::UnsupportedExtension).into(), + AlertCertificateUnobtainable => Error::AlertReceived(alert::CertificateUnobtainable).into(), + AlertUnrecognisedName => Error::AlertReceived(alert::UnrecognisedName).into(), AlertBadCertificateStatusResponse => { - TLSError::AlertReceived(alert::BadCertificateStatusResponse) + Error::AlertReceived(alert::BadCertificateStatusResponse).into() } - AlertBadCertificateHashValue => TLSError::AlertReceived(alert::BadCertificateHashValue), - AlertUnknownPSKIdentity => TLSError::AlertReceived(alert::UnknownPSKIdentity), - AlertCertificateRequired => TLSError::AlertReceived(alert::CertificateRequired), - AlertNoApplicationProtocol => TLSError::AlertReceived(alert::NoApplicationProtocol), - AlertUnknown => TLSError::AlertReceived(alert::Unknown(0)), + AlertBadCertificateHashValue => Error::AlertReceived(alert::BadCertificateHashValue).into(), + AlertUnknownPSKIdentity => Error::AlertReceived(alert::UnknownPSKIdentity).into(), + AlertCertificateRequired => Error::AlertReceived(alert::CertificateRequired).into(), + AlertNoApplicationProtocol => Error::AlertReceived(alert::NoApplicationProtocol).into(), + AlertUnknown => Error::AlertReceived(alert::Unknown(0)).into(), - CertBadDER => TLSError::WebPKIError(webpki::BadDER), - CertBadDERTime => TLSError::WebPKIError(webpki::BadDERTime), - CertCAUsedAsEndEntity => TLSError::WebPKIError(webpki::CAUsedAsEndEntity), - CertExpired => TLSError::WebPKIError(webpki::CertExpired), - CertNotValidForName => TLSError::WebPKIError(webpki::CertNotValidForName), - CertNotValidYet => TLSError::WebPKIError(webpki::CertNotValidYet), - CertEndEntityUsedAsCA => TLSError::WebPKIError(webpki::EndEntityUsedAsCA), - CertExtensionValueInvalid => TLSError::WebPKIError(webpki::ExtensionValueInvalid), - CertInvalidCertValidity => TLSError::WebPKIError(webpki::InvalidCertValidity), - CertInvalidSignatureForPublicKey => { - TLSError::WebPKIError(webpki::InvalidSignatureForPublicKey) - } - CertNameConstraintViolation => TLSError::WebPKIError(webpki::NameConstraintViolation), - CertPathLenConstraintViolated => TLSError::WebPKIError(webpki::PathLenConstraintViolated), - CertSignatureAlgorithmMismatch => TLSError::WebPKIError(webpki::SignatureAlgorithmMismatch), - CertRequiredEKUNotFound => TLSError::WebPKIError(webpki::RequiredEKUNotFound), - CertUnknownIssuer => TLSError::WebPKIError(webpki::UnknownIssuer), - CertUnsupportedCertVersion => TLSError::WebPKIError(webpki::UnsupportedCertVersion), - CertUnsupportedCriticalExtension => { - TLSError::WebPKIError(webpki::UnsupportedCriticalExtension) - } - CertUnsupportedSignatureAlgorithmForPublicKey => { - TLSError::WebPKIError(webpki::UnsupportedSignatureAlgorithmForPublicKey) - } - CertUnsupportedSignatureAlgorithm => { - TLSError::WebPKIError(webpki::UnsupportedSignatureAlgorithm) - } - - CertSCTMalformed => TLSError::InvalidSCT(sct::MalformedSCT), - CertSCTInvalidSignature => TLSError::InvalidSCT(sct::InvalidSignature), - CertSCTTimestampInFuture => TLSError::InvalidSCT(sct::TimestampInFuture), - CertSCTUnsupportedVersion => TLSError::InvalidSCT(sct::UnsupportedSCTVersion), - CertSCTUnknownLog => TLSError::InvalidSCT(sct::UnknownLog), - }; - Either::TLSError(e) + CertSCTMalformed => Error::InvalidSct(sct::MalformedSct).into(), + CertSCTInvalidSignature => Error::InvalidSct(sct::InvalidSignature).into(), + CertSCTTimestampInFuture => Error::InvalidSct(sct::TimestampInFuture).into(), + CertSCTUnsupportedVersion => Error::InvalidSct(sct::UnsupportedSctVersion).into(), + CertSCTUnknownLog => Error::InvalidSct(sct::UnknownLog).into(), + } } diff --git a/src/lib.rs b/src/lib.rs index cfb1a933..0e7e6dad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,8 +2,6 @@ #![allow(non_camel_case_types)] use libc::{c_char, c_void, size_t}; use std::cell::RefCell; -use std::io::Error; -use std::io::ErrorKind::ConnectionAborted; use std::sync::Arc; use std::{cmp::min, thread::AccessError}; use std::{mem, slice}; @@ -274,7 +272,7 @@ mod tests { } // Keep in sync with Cargo.toml. -const RUSTLS_CRATE_VERSION: &str = "0.19.0"; +const RUSTLS_CRATE_VERSION: &str = "0.20.0"; /// CastPtr represents the relationship between a snake case type (like rustls_client_session) /// and the corresponding Rust type (like ClientSession). For each matched pair of types, there @@ -296,6 +294,23 @@ pub(crate) trait CastPtr { } } +pub(crate) trait BoxCastPtr: CastPtr + Sized { + fn to_box(ptr: *mut Self) -> Box { + let rs_typed = Self::cast_mut_ptr(ptr); + unsafe { Box::from_raw(rs_typed) } + } + + fn to_mut_ptr(src: Self::RustType) -> *mut Self { + Box::into_raw(Box::new(src)) as *mut _ + } + + fn set_mut_ptr(dst: *mut *mut Self, src: Self::RustType) { + unsafe { + *dst = Self::to_mut_ptr(src); + } + } +} + #[macro_export] macro_rules! try_slice { ( $ptr:expr, $count:expr ) => { @@ -424,7 +439,3 @@ unsafe fn arc_with_incref_from_raw(v: *const T) -> Arc { mem::forget(r); val } - -pub(crate) fn is_close_notify(e: &Error) -> bool { - e.kind() == ConnectionAborted && e.to_string().contains("CloseNotify") -} diff --git a/src/server.rs b/src/server.rs index 7506e682..02ae51fd 100644 --- a/src/server.rs +++ b/src/server.rs @@ -5,22 +5,23 @@ use std::slice; use std::sync::Arc; use libc::size_t; +use rustls::server::{ + AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, ClientHello, + ResolvesServerCert, ServerConfig, ServerConnection, WantsServerCert, +}; use rustls::sign::CertifiedKey; use rustls::{ - AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, ClientHello, NoClientAuth, - ServerConfig, ServerSession, + ConfigBuilder, ProtocolVersion, SignatureScheme, SupportedCipherSuite, WantsVerifier, + ALL_CIPHER_SUITES, }; -use rustls::{ResolvesServerCert, ALL_CIPHERSUITES}; -use rustls::{SignatureScheme, SupportedCipherSuite}; use crate::cipher::{ rustls_certified_key, rustls_client_cert_verifier, rustls_client_cert_verifier_optional, rustls_supported_ciphersuite, }; use crate::connection::{rustls_connection, Connection}; -use crate::enums::rustls_tls_version_from_u16; -use crate::error::rustls_result; use crate::error::rustls_result::{InvalidParameter, NullParameter}; +use crate::error::{map_error, rustls_result}; use crate::rslice::{rustls_slice_bytes, rustls_slice_slice_bytes, rustls_slice_u16, rustls_str}; use crate::session::{ rustls_session_store_get_callback, rustls_session_store_put_callback, SessionStoreBroker, @@ -28,7 +29,7 @@ use crate::session::{ }; use crate::{ arc_with_incref_from_raw, ffi_panic_boundary, try_mut_from_ptr, try_mut_slice, - try_ref_from_ptr, try_slice, userdata_get, CastPtr, + try_ref_from_ptr, try_slice, userdata_get, BoxCastPtr, CastPtr, }; /// A server config being constructed. A builder can be modified by, @@ -36,8 +37,8 @@ use crate::{ /// done configuring settings, call rustls_server_config_builder_build /// to turn it into a *rustls_server_config. This object is not safe /// for concurrent mutation. Under the hood, it corresponds to a -/// Box. -/// https://docs.rs/rustls/0.19.0/rustls/struct.ServerConfig.html +/// Box. +/// https://docs.rs/rustls/0.20.0/rustls/struct.ServerConfig.html pub struct rustls_server_config_builder { // We use the opaque struct pattern to tell C about our types without // telling them what's inside. @@ -49,9 +50,37 @@ impl CastPtr for rustls_server_config_builder { type RustType = ServerConfig; } +impl BoxCastPtr for rustls_server_config_builder {} + +pub struct rustls_server_config_builder_wants_verifier { + // We use the opaque struct pattern to tell C about our types without + // telling them what's inside. + // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs + _private: [u8; 0], +} + +impl CastPtr for rustls_server_config_builder_wants_verifier { + type RustType = ConfigBuilder; +} + +impl BoxCastPtr for rustls_server_config_builder_wants_verifier {} + +pub struct rustls_server_config_builder_wants_server_cert { + // We use the opaque struct pattern to tell C about our types without + // telling them what's inside. + // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs + _private: [u8; 0], +} + +impl CastPtr for rustls_server_config_builder_wants_server_cert { + type RustType = ConfigBuilder; +} + +impl BoxCastPtr for rustls_server_config_builder_wants_server_cert {} + /// A server config that is done being constructed and is now read-only. /// Under the hood, this object corresponds to an Arc. -/// https://docs.rs/rustls/0.19.0/rustls/struct.ServerConfig.html +/// https://docs.rs/rustls/0.20.0/rustls/struct.ServerConfig.html pub struct rustls_server_config { // We use the opaque struct pattern to tell C about our types without // telling them what's inside. @@ -65,14 +94,81 @@ impl CastPtr for rustls_server_config { /// Create a rustls_server_config_builder. Caller owns the memory and must /// eventually call rustls_server_config_builder_build, then free the -/// resulting rustls_server_config. -/// https://docs.rs/rustls/0.19.0/rustls/struct.ServerConfig.html#method.new +/// resulting rustls_server_config. This uses rustls safe default values +/// for the cipher suites, key exchange groups and protocol versions. +#[no_mangle] +pub extern "C" fn rustls_server_config_builder_new_with_safe_defaults( +) -> *mut rustls_server_config_builder_wants_verifier { + ffi_panic_boundary! { + let builder = rustls::ServerConfig::builder().with_safe_defaults(); + BoxCastPtr::to_mut_ptr(builder) + } +} + +/// Create a rustls_server_config_builder. Caller owns the memory and must +/// eventually call rustls_server_config_builder_build, then free the +/// resulting rustls_server_config. Specify cipher suites in preference order; +/// the `cipher_suites` parameter must point to an array containing `len` +/// pointers to `rustls_supported_ciphersuite` previously obtained from +/// `rustls_all_ciphersuites_get()`. Set the TLS protocol versions to use +/// when negotiating a TLS session. +/// +/// `tls_version` is the version of the protocol, as defined in rfc8446, +/// ch. 4.2.1 and end of ch. 5.1. Some values are defined in +/// `rustls_tls_version` for convenience. +/// +/// `versions` will only be used during the call and the application retains +/// ownership. `len` is the number of consecutive `ui16` pointed to by `versions`. +#[no_mangle] +pub extern "C" fn rustls_server_config_builder_new( + cipher_suites: *const *const rustls_supported_ciphersuite, + cipher_suites_len: size_t, + tls_versions: *const u16, + tls_versions_len: size_t, + builder: *mut *mut rustls_server_config_builder_wants_verifier, +) -> rustls_result { + ffi_panic_boundary! { + let cipher_suites: &[*const rustls_supported_ciphersuite] = try_slice!(cipher_suites, cipher_suites_len); + let mut cs_vec: Vec = Vec::new(); + for &cs in cipher_suites.into_iter() { + let cs = try_ref_from_ptr!(cs); + match ALL_CIPHER_SUITES.iter().find(|&acs| cs.eq(acs)) { + Some(scs) => cs_vec.push(scs.clone()), + None => return InvalidParameter, + } + } + + let tls_versions: &[u16] = try_slice!(tls_versions, tls_versions_len); + let mut versions = vec![]; + for version_number in tls_versions { + let proto = ProtocolVersion::from(*version_number); + if proto == rustls::version::TLS12.version { + versions.push(&rustls::version::TLS12); + } else if proto == rustls::version::TLS13.version { + versions.push(&rustls::version::TLS13); + } + } + + let result = rustls::ServerConfig::builder().with_cipher_suites(&cs_vec).with_safe_default_kx_groups().with_protocol_versions(&versions); + let new = match result { + Ok(new) => new, + Err(_) => return rustls_result::InvalidParameter, + }; + + BoxCastPtr::set_mut_ptr(builder, new); + rustls_result::Ok + } +} + +/// For memory lifetime, see rustls_server_config_builder_new. #[no_mangle] -pub extern "C" fn rustls_server_config_builder_new() -> *mut rustls_server_config_builder { +pub extern "C" fn rustls_server_config_builder_with_no_client_auth( + wants_verifier: *mut rustls_server_config_builder_wants_verifier, +) -> *mut rustls_server_config_builder { ffi_panic_boundary! { - let config = rustls::ServerConfig::new(Arc::new(NoClientAuth)); - let b = Box::new(config); - Box::into_raw(b) as *mut _ + let prev = *BoxCastPtr::to_box(wants_verifier); + let config: ServerConfig = prev.with_no_client_auth().with_cert_resolver(Arc::new(rustls::server::ResolvesServerCertUsingSni::new())); + BoxCastPtr::to_mut_ptr(config) } } @@ -83,18 +179,22 @@ pub extern "C" fn rustls_server_config_builder_new() -> *mut rustls_server_confi /// For memory lifetime, see rustls_server_config_builder_new. #[no_mangle] pub extern "C" fn rustls_server_config_builder_with_client_verifier( + wants_verifier: *mut rustls_server_config_builder_wants_verifier, verifier: *const rustls_client_cert_verifier, -) -> *mut rustls_server_config_builder { + builder: *mut *mut rustls_server_config_builder_wants_server_cert, +) -> rustls_result { ffi_panic_boundary! { let verifier: Arc = unsafe { match (verifier as *const AllowAnyAuthenticatedClient).as_ref() { Some(c) => arc_with_incref_from_raw(c), - None => return null_mut(), + None => return rustls_result::InvalidParameter, } }; - let config = rustls::ServerConfig::new(verifier); - let b = Box::new(config); - Box::into_raw(b) as *mut rustls_server_config_builder + + let prev = *BoxCastPtr::to_box(wants_verifier); + let new = prev.with_client_cert_verifier(verifier); + BoxCastPtr::set_mut_ptr(builder, new); + rustls_result::Ok } } @@ -114,9 +214,10 @@ pub extern "C" fn rustls_server_config_builder_with_client_verifier_optional( None => return null_mut(), } }; - let config = rustls::ServerConfig::new(verifier); - let b = Box::new(config); - Box::into_raw(b) as *mut rustls_server_config_builder + + let builder = rustls::ServerConfig::builder().with_safe_defaults(); + let config: ServerConfig = builder.with_client_cert_verifier(verifier).with_cert_resolver(Arc::new(rustls::server::ResolvesServerCertUsingSni::new())); + BoxCastPtr::to_mut_ptr(config) } } @@ -128,9 +229,7 @@ pub extern "C" fn rustls_server_config_builder_with_client_verifier_optional( #[no_mangle] pub extern "C" fn rustls_server_config_builder_free(config: *mut rustls_server_config_builder) { ffi_panic_boundary! { - let config: &mut ServerConfig = try_mut_from_ptr!(config); - // Convert the pointer to a Box and drop it. - unsafe { Box::from_raw(config); } + BoxCastPtr::to_box(config); } } @@ -143,43 +242,14 @@ pub extern "C" fn rustls_server_config_builder_from_config( ) -> *mut rustls_server_config_builder { ffi_panic_boundary! { let config: &ServerConfig = try_ref_from_ptr!(config); - Box::into_raw(Box::new(config.clone())) as *mut _ - } -} - -/// Set the TLS protocol versions to use when negotiating a TLS session. -/// -/// `tls_version` is the version of the protocol, as defined in rfc8446, -/// ch. 4.2.1 and end of ch. 5.1. Some values are defined in -/// `rustls_tls_version` for convenience. -/// -/// `versions` will only be used during the call and the application retains -/// ownership. `len` is the number of consecutive `ui16` pointed to by `versions`. -#[no_mangle] -pub extern "C" fn rustls_server_config_builder_set_versions( - builder: *mut rustls_server_config_builder, - tls_versions: *const u16, - len: size_t, -) -> rustls_result { - ffi_panic_boundary! { - let config: &mut ServerConfig = try_mut_from_ptr!(builder); - let tls_versions: &[u16] = try_slice!(tls_versions, len); - config.versions.clear(); - - // rustls does not support an `Unkown(u16)` protocol version, - // so we have to fail on any version numbers not implemented - // in rustls. - for i in tls_versions { - config.versions.push(rustls_tls_version_from_u16(*i)); - } - rustls_result::Ok + BoxCastPtr::to_mut_ptr(config.clone()) } } /// With `ignore` != 0, the server will ignore the client ordering of cipher /// suites, aka preference, during handshake and respect its own ordering /// as configured. -/// https://docs.rs/rustls/0.19.0/rustls/struct.ServerConfig.html#fields +/// https://docs.rs/rustls/0.20.0/rustls/struct.ServerConfig.html#fields #[no_mangle] pub extern "C" fn rustls_server_config_builder_set_ignore_client_order( builder: *mut rustls_server_config_builder, @@ -201,9 +271,9 @@ pub extern "C" fn rustls_server_config_builder_set_ignore_client_order( /// This function makes a copy of the data in `protocols` and does not retain /// any pointers, so the caller can free the pointed-to memory after calling. /// -/// https://docs.rs/rustls/0.19.0/rustls/struct.ServerConfig.html#method.set_protocols +/// https://docs.rs/rustls/0.20.0/rustls/server/struct.ServerConfig.html#structfield.alpn_protocols #[no_mangle] -pub extern "C" fn rustls_server_config_builder_set_protocols( +pub extern "C" fn rustls_server_config_builder_set_alpn_protocols( builder: *mut rustls_server_config_builder, protocols: *const rustls_slice_bytes, len: size_t, @@ -217,34 +287,7 @@ pub extern "C" fn rustls_server_config_builder_set_protocols( let v: &[u8] = try_slice!(p.data, p.len); vv.push(v.to_vec()); } - config.set_protocols(&vv); - rustls_result::Ok - } -} - -/// Set the cipher suite list, in preference order. The `ciphersuites` -/// parameter must point to an array containing `len` pointers to -/// `rustls_supported_ciphersuite` previously obtained from -/// `rustls_all_ciphersuites_get()`. -/// https://docs.rs/rustls/0.19.0/rustls/struct.ServerConfig.html#structfield.ciphersuites -#[no_mangle] -pub extern "C" fn rustls_server_config_builder_set_ciphersuites( - builder: *mut rustls_server_config_builder, - ciphersuites: *const *const rustls_supported_ciphersuite, - len: size_t, -) -> rustls_result { - ffi_panic_boundary! { - let config: &mut ServerConfig = try_mut_from_ptr!(builder); - let ciphersuites: &[*const rustls_supported_ciphersuite] = try_slice!(ciphersuites, len); - let mut cs_vec: Vec<&'static SupportedCipherSuite> = Vec::new(); - for &cs in ciphersuites.into_iter() { - let cs = try_ref_from_ptr!(cs); - match ALL_CIPHERSUITES.iter().find(|&acs| cs.eq(acs)) { - Some(scs) => cs_vec.push(scs), - None => return InvalidParameter, - } - } - config.ciphersuites = cs_vec; + config.alpn_protocols = vv; rustls_result::Ok } } @@ -286,15 +329,14 @@ pub extern "C" fn rustls_server_config_builder_set_certified_keys( } } -/// Turn a *rustls_server_config_builder (mutable) into a *rustls_server_config +/// Turn a *rustls_server_config_builder (mutable) into a const *rustls_server_config /// (read-only). #[no_mangle] pub extern "C" fn rustls_server_config_builder_build( builder: *mut rustls_server_config_builder, ) -> *const rustls_server_config { ffi_panic_boundary! { - let config: &mut ServerConfig = try_mut_from_ptr!(builder); - let b = unsafe { Box::from_raw(config) }; + let b = BoxCastPtr::to_box(builder); Arc::into_raw(Arc::new(*b)) as *const _ } } @@ -335,15 +377,16 @@ pub extern "C" fn rustls_server_connection_new( } }; + let server_connection = match ServerConnection::new(config) { + Ok(sc) => sc, + Err(e) => return map_error(e), + }; // We've succeeded. Put the server on the heap, and transfer ownership // to the caller. After this point, we must return CRUSTLS_OK so the // caller knows it is responsible for this memory. - let c = Connection::from_server(ServerSession::new(&config)); - unsafe { - *conn_out = Box::into_raw(Box::new(c)) as *mut _; - } - - return rustls_result::Ok; + let c = Connection::from_server(server_connection); + BoxCastPtr::set_mut_ptr(conn_out, c); + rustls_result::Ok } } @@ -353,7 +396,7 @@ pub extern "C" fn rustls_server_connection_new( /// Returns RUSTLS_RESULT_INSUFFICIENT_SIZE if the SNI hostname is longer than `count`. /// Returns Ok with *out_n == 0 if there is no SNI hostname available on this session /// because it hasn't been processed yet, or because the client did not send SNI. -/// https://docs.rs/rustls/0.19.0/rustls/struct.ServerSession.html#method.get_sni_hostname +/// https://docs.rs/rustls/0.20.0/rustls/server/struct.ServerConnection.html#method.sni_hostname #[no_mangle] pub extern "C" fn rustls_server_connection_get_sni_hostname( conn: *const rustls_connection, @@ -369,7 +412,7 @@ pub extern "C" fn rustls_server_connection_get_sni_hostname( Some(s) => s, _ => return rustls_result::InvalidParameter, }; - let sni_hostname = match server_session.get_sni_hostname() { + let sni_hostname = match server_session.sni_hostname() { Some(sni_hostname) => sni_hostname, None => { return rustls_result::Ok @@ -401,10 +444,14 @@ impl ResolvesServerCertFromChoices { } impl ResolvesServerCert for ResolvesServerCertFromChoices { - fn resolve(&self, client_hello: ClientHello) -> Option { + fn resolve(&self, client_hello: ClientHello) -> Option> { for key in self.choices.iter() { - if key.key.choose_scheme(client_hello.sigschemes()).is_some() { - return Some(key.as_ref().clone()); + if key + .key + .choose_scheme(client_hello.signature_schemes()) + .is_some() + { + return Some(key.clone()); } } None @@ -417,7 +464,7 @@ impl ResolvesServerCert for ResolvesServerCertFromChoices { /// The signature_schemes carries the values supplied by the client or, should /// the client not use this TLS extension, the default schemes in the rustls /// library. See: -/// https://docs.rs/rustls/0.19.0/rustls/internal/msgs/enums/enum.SignatureScheme.html +/// https://docs.rs/rustls/0.20.0/rustls/internal/msgs/enums/enum.SignatureScheme.html /// `alpn` carries the list of ALPN protocol names that the client proposed to /// the server. Again, the length of this list will be 0 if none were supplied. /// @@ -483,7 +530,7 @@ impl ClientHelloResolver { } impl ResolvesServerCert for ClientHelloResolver { - fn resolve(&self, client_hello: ClientHello) -> Option { + fn resolve(&self, client_hello: ClientHello) -> Option> { let sni_name: &str = { match client_hello.server_name() { Some(c) => c.into(), @@ -495,19 +542,24 @@ impl ResolvesServerCert for ClientHelloResolver { Err(_) => return None, }; let mapped_sigs: Vec = client_hello - .sigschemes() + .signature_schemes() .iter() .map(|s| s.get_u16()) .collect(); // Unwrap the Option. None becomes an empty slice. - let alpn: &[&[u8]] = client_hello.alpn().unwrap_or(&[]); - let alpn = rustls_slice_slice_bytes { inner: alpn }; + let alpn = match client_hello.alpn() { + Some(iter) => iter.collect(), + None => vec![], + }; + + let alpn = rustls_slice_slice_bytes { inner: &alpn }; let signature_schemes: rustls_slice_u16 = (&*mapped_sigs).into(); let hello = rustls_client_hello { sni_name, signature_schemes, alpn: &alpn, }; + let cb = self.callback; let userdata = match userdata_get() { Ok(u) => u, @@ -515,7 +567,7 @@ impl ResolvesServerCert for ClientHelloResolver { }; let key_ptr: *const rustls_certified_key = unsafe { cb(userdata, &hello) }; let certified_key: &CertifiedKey = try_ref_from_ptr!(key_ptr); - Some(certified_key.clone()) + Some(Arc::new(certified_key.clone())) } } @@ -647,9 +699,9 @@ pub extern "C" fn rustls_server_config_builder_set_persistence( None => return rustls_result::NullParameter, }; let config: &mut ServerConfig = try_mut_from_ptr!(builder); - config.set_persistence(Arc::new(SessionStoreBroker::new( + config.session_storage = Arc::new(SessionStoreBroker::new( get_cb, put_cb - ))); + )); rustls_result::Ok } } diff --git a/src/session.rs b/src/session.rs index 9756e957..6ddc98af 100644 --- a/src/session.rs +++ b/src/session.rs @@ -131,7 +131,7 @@ impl SessionStoreBroker { } } -impl rustls::StoresServerSessions for SessionStoreBroker { +impl rustls::server::StoresServerSessions for SessionStoreBroker { fn put(&self, key: Vec, value: Vec) -> bool { self.store(key, value) } @@ -143,9 +143,13 @@ impl rustls::StoresServerSessions for SessionStoreBroker { fn take(&self, key: &[u8]) -> Option> { return self.retrieve(key, true); } + + fn can_cache(&self) -> bool { + true + } } -impl rustls::StoresClientSessions for SessionStoreBroker { +impl rustls::client::StoresClientSessions for SessionStoreBroker { fn put(&self, key: Vec, value: Vec) -> bool { self.store(key, value) } diff --git a/tests/client.c b/tests/client.c index e68f6740..9033cee0 100644 --- a/tests/client.c +++ b/tests/client.c @@ -108,7 +108,7 @@ make_conn(const char *hostname, const char *port) * * Returns CRUSTLS_DEMO_OK for success, * CRUSTLS_DEMO_ERROR for error, - * CRUSTLS_DEMO_CLOSE_NOTIFY for "received close_notify" + * CRUSTLS_DEMO_EOF for "received close_notify" */ int copy_plaintext_to_stdout(struct rustls_connection *client_conn) @@ -121,17 +121,16 @@ copy_plaintext_to_stdout(struct rustls_connection *client_conn) bzero(buf, sizeof(buf)); result = rustls_connection_read(client_conn, (uint8_t *)buf, sizeof(buf), &n); - if(result == RUSTLS_RESULT_ALERT_CLOSE_NOTIFY) { - fprintf(stderr, "Received close_notify, cleanly ending connection\n"); - return CRUSTLS_DEMO_CLOSE_NOTIFY; - } - if(result != RUSTLS_RESULT_OK) { - fprintf(stderr, "Error in rustls_connection_read: %d\n", result); + if(result == RUSTLS_RESULT_PLAINTEXT_EMPTY) { + /* This is expected. It just means "no more bytes for now." */ + return CRUSTLS_DEMO_OK; + } else if(result != RUSTLS_RESULT_OK) { + print_error("Error in rustls_connection_read", result); return CRUSTLS_DEMO_ERROR; } if(n == 0) { - /* This is expected. It just means "no more bytes for now." */ - return CRUSTLS_DEMO_OK; + fprintf(stderr, "Received clean EOF, cleanly ending connection\n"); + return CRUSTLS_DEMO_EOF; } result = write_all(STDOUT_FILENO, buf, n); @@ -183,12 +182,12 @@ do_read(struct conndata *conn, struct rustls_connection *rconn) } result = copy_plaintext_to_buffer(conn); - if(result != CRUSTLS_DEMO_CLOSE_NOTIFY) { + if(result != CRUSTLS_DEMO_EOF) { return result; } - /* If we got a close_notify, verify that the sender then - * closed the TCP connection. */ + /* If we got an EOF on the plaintext stream (peer closed connection cleanly), + * verify that the sender then closed the TCP connection. */ signed_n = read(conn->fd, buf, sizeof(buf)); if(signed_n > 0) { fprintf(stderr, @@ -202,7 +201,7 @@ do_read(struct conndata *conn, struct rustls_connection *rconn) strerror(errno)); return CRUSTLS_DEMO_ERROR; } - return CRUSTLS_DEMO_CLOSE_NOTIFY; + return CRUSTLS_DEMO_EOF; } static const char *CONTENT_LENGTH = "Content-Length"; @@ -286,7 +285,7 @@ send_request_and_read_response(struct conndata *conn, if(result == CRUSTLS_DEMO_AGAIN) { break; } - else if(result == CRUSTLS_DEMO_CLOSE_NOTIFY) { + else if(result == CRUSTLS_DEMO_EOF) { ret = 0; goto cleanup; } @@ -472,8 +471,9 @@ main(int argc, const char **argv) const char *port = argv[2]; const char *path = argv[3]; - struct rustls_client_config_builder *config_builder = - rustls_client_config_builder_new(); + struct rustls_client_config_builder_wants_verifier *config_builder = + rustls_client_config_builder_new_with_safe_defaults(); + struct rustls_client_config_builder *config_builder2 = NULL; const struct rustls_client_config *client_config = NULL; struct rustls_slice_bytes alpn_http11; @@ -488,21 +488,22 @@ main(int argc, const char **argv) if(getenv("CA_FILE")) { result = rustls_client_config_builder_load_roots_from_file( - config_builder, getenv("CA_FILE")); + config_builder, getenv("CA_FILE"), &config_builder2); if(result != RUSTLS_RESULT_OK) { - print_error("loading trusted certificate", result); + print_error("loading trusted certificates", result); goto cleanup; } - } - - if(getenv("NO_CHECK_CERTIFICATE")) { + } else if(getenv("NO_CHECK_CERTIFICATE")) { rustls_client_config_builder_dangerous_set_certificate_verifier( - config_builder, verify); + config_builder, verify, &config_builder2); + } else { + fprintf(stderr, "must set either CA_FILE or NO_CHECK_CERTIFICATE env var\n"); + goto cleanup; } - rustls_client_config_builder_set_protocols(config_builder, &alpn_http11, 1); + rustls_client_config_builder_set_alpn_protocols(config_builder2, &alpn_http11, 1); - client_config = rustls_client_config_builder_build(config_builder); + client_config = rustls_client_config_builder_build(config_builder2); int i; for(i = 0; i < 3; i++) { diff --git a/tests/common.c b/tests/common.c index 9453d205..a0eb1d6b 100644 --- a/tests/common.c +++ b/tests/common.c @@ -221,17 +221,17 @@ copy_plaintext_to_buffer(struct conndata *conn) char *buf = bytevec_writeable(&conn->data); size_t avail = bytevec_available(&conn->data); result = rustls_connection_read(rconn, (uint8_t *)buf, avail, &n); - if(result == RUSTLS_RESULT_ALERT_CLOSE_NOTIFY) { - fprintf(stderr, "Received close_notify, cleanly ending connection\n"); - return CRUSTLS_DEMO_CLOSE_NOTIFY; + if(result == RUSTLS_RESULT_PLAINTEXT_EMPTY) { + /* This is expected. It just means "no more bytes for now." */ + return CRUSTLS_DEMO_OK; } if(result != RUSTLS_RESULT_OK) { - fprintf(stderr, "Error in rustls_connection_read: %d\n", result); + print_error("Error in rustls_connection_read", result); return CRUSTLS_DEMO_ERROR; } if(n == 0) { - /* This is expected. It just means "no more bytes for now." */ - return CRUSTLS_DEMO_OK; + fprintf(stderr, "got 0-byte read, cleanly ending connection\n"); + return CRUSTLS_DEMO_EOF; } bytevec_consume(&conn->data, n); } diff --git a/tests/common.h b/tests/common.h index ef449dc5..bf20e48a 100644 --- a/tests/common.h +++ b/tests/common.h @@ -7,7 +7,6 @@ enum crustls_demo_result CRUSTLS_DEMO_ERROR, CRUSTLS_DEMO_AGAIN, CRUSTLS_DEMO_EOF, - CRUSTLS_DEMO_CLOSE_NOTIFY, }; /* A growable vector of bytes. */ @@ -76,7 +75,7 @@ bytevec_ensure_available(struct bytevec *vec, size_t n); * * Returns CRUSTLS_DEMO_OK for success, * CRUSTLS_DEMO_ERROR for error, - * CRUSTLS_DEMO_CLOSE_NOTIFY for "received close_notify" + * CRUSTLS_DEMO_EOF for "connection cleanly terminated by peer" */ int copy_plaintext_to_buffer(struct conndata *conn); diff --git a/tests/server.c b/tests/server.c index 5cee5c4a..5a36aec8 100644 --- a/tests/server.c +++ b/tests/server.c @@ -115,13 +115,13 @@ do_read(struct conndata *conn, struct rustls_connection *rconn) } result = copy_plaintext_to_buffer(conn); - if(result != CRUSTLS_DEMO_CLOSE_NOTIFY) { + if(result != CRUSTLS_DEMO_EOF) { fprintf(stderr, "do_read returning %d\n", result); return result; } - /* If we got a close_notify, verify that the sender then - * closed the TCP connection. */ + /* If we got an EOF on the plaintext stream (peer closed connection cleanly), + * verify that the sender then closed the TCP connection. */ signed_n = read(conn->fd, buf, sizeof(buf)); if(signed_n > 0) { fprintf(stderr, @@ -135,7 +135,7 @@ do_read(struct conndata *conn, struct rustls_connection *rconn) strerror(errno)); return CRUSTLS_DEMO_ERROR; } - return CRUSTLS_DEMO_CLOSE_NOTIFY; + return CRUSTLS_DEMO_EOF; } enum crustls_demo_result @@ -202,7 +202,7 @@ handle_conn(struct conndata *conn) if(result == CRUSTLS_DEMO_AGAIN) { break; } - else if(result == CRUSTLS_DEMO_CLOSE_NOTIFY) { + else if(result == CRUSTLS_DEMO_EOF) { goto cleanup; } else if(result != CRUSTLS_DEMO_OK) { @@ -278,11 +278,17 @@ main(int argc, const char **argv) int ret = 1; int result = 1; int sockfd = 0; - struct rustls_server_config_builder *config_builder = - rustls_server_config_builder_new(); + struct rustls_server_config_builder_wants_verifier *config_builder = + rustls_server_config_builder_new_with_safe_defaults(); + struct rustls_server_config_builder *config_builder2 = NULL; const struct rustls_server_config *server_config = NULL; struct rustls_connection *rconn = NULL; + config_builder2 = rustls_server_config_builder_with_no_client_auth(config_builder); + if(config_builder2 == NULL) { + goto cleanup; + } + if(argc <= 2) { fprintf(stderr, "usage: %s cert.pem key.pem\n\n" @@ -298,8 +304,8 @@ main(int argc, const char **argv) } rustls_server_config_builder_set_certified_keys( - config_builder, &certified_key, 1); - server_config = rustls_server_config_builder_build(config_builder); + config_builder2, &certified_key, 1); + server_config = rustls_server_config_builder_build(config_builder2); #ifdef _WIN32 WSADATA wsa;