diff --git a/Cargo.lock b/Cargo.lock index 99bd4b61..fdd51abe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,15 +106,17 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.9" +version = "0.22.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +checksum = "7c23376606de66c7b9249d091b59ee55b52df72063e1cae7bb44e0691c9e5150" dependencies = [ "log", "ring", + "rustls-pki-types", "rustls-webpki", "rustversion", - "sct", + "subtle", + "zeroize", ] [[package]] @@ -126,26 +128,35 @@ dependencies = [ "regex", "rustls", "rustls-pemfile", + "rustls-pki-types", "rustls-webpki", "sct", ] [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.0.0-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "4aaa4fe93b39faddb6a8f99568c3e5880680156da0d46818e884a071381f67fe" dependencies = [ "base64", + "rustls-pki-types", ] +[[package]] +name = "rustls-pki-types" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47003264dea418db67060fa420ad16d0d2f8f0a0360d825c00e177ac52cb5d8" + [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.102.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "34d9ed3a8267782ba32d257ff5b197b63eef19a467dbd1be011caaae35ee416e" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] @@ -171,6 +182,12 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "untrusted" version = "0.9.0" @@ -248,3 +265,9 @@ name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml index 17804270..84f96eb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,11 +23,12 @@ read_buf = ["rustls/read_buf"] [dependencies] # Keep in sync with RUSTLS_CRATE_VERSION in build.rs -rustls = { version = "=0.21.9", features = [ "dangerous_configuration" ] } -rustls-webpki = "0.101.0" +rustls = { version = "=0.22.0-alpha.4", features = [ "ring" ]} +rustls-webpki = "0.102.0-alpha.6" +pki-types = { package = "rustls-pki-types", version = "0.2.1", features = ["std"] } libc = "0.2" sct = "0.7" -rustls-pemfile = "1.0.3" +rustls-pemfile = { version = "2.0.0-alpha.1" } log = "0.4.17" [lib] diff --git a/build.rs b/build.rs index f259746e..a22bddc2 100644 --- a/build.rs +++ b/build.rs @@ -3,7 +3,7 @@ use std::io::Write; use std::{env, fs, path::PathBuf}; // Keep in sync with Cargo.toml. -const RUSTLS_CRATE_VERSION: &str = "0.21.9"; +const RUSTLS_CRATE_VERSION: &str = "0.22.0-alpha.4"; fn main() { let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); diff --git a/src/cipher.rs b/src/cipher.rs index 463f0726..e52da198 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -1,45 +1,50 @@ -use libc::size_t; +use libc::{c_char, size_t}; use std::convert::TryFrom; -use std::io::Cursor; +use std::ffi::{CStr, OsStr}; +use std::fs::File; +use std::io::{BufReader, Cursor}; +use std::marker::PhantomData; use std::ptr::null; use std::slice; use std::sync::Arc; -use rustls::server::{ - AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, UnparsedCertRevocationList, -}; +use pki_types::{CertificateDer, CertificateRevocationListDer, PrivateKeyDer}; +use rustls::client::danger::ServerCertVerifier; +use rustls::client::WebPkiServerVerifier; +use rustls::crypto::ring::{ALL_CIPHER_SUITES, DEFAULT_CIPHER_SUITES}; +use rustls::server::danger::ClientCertVerifier; +use rustls::server::WebPkiClientVerifier; use rustls::sign::CertifiedKey; -use rustls::{ - Certificate, PrivateKey, RootCertStore, SupportedCipherSuite, ALL_CIPHER_SUITES, - DEFAULT_CIPHER_SUITES, -}; +use rustls::{DistinguishedName, RootCertStore, SupportedCipherSuite}; use rustls_pemfile::{certs, crls, pkcs8_private_keys, rsa_private_keys}; +use webpki::{RevocationCheckDepth, UnknownStatusPolicy}; -use crate::error::{map_error, rustls_result}; +use crate::error::{self, rustls_result}; use crate::rslice::{rustls_slice_bytes, rustls_str}; use crate::{ - ffi_panic_boundary, free_arc, to_arc_const_ptr, to_boxed_mut_ptr, try_box_from_ptr, - try_mut_from_ptr, try_ref_from_ptr, try_slice, try_take, Castable, OwnershipArc, OwnershipBox, - OwnershipRef, + ffi_panic_boundary, free_arc, free_box, set_arc_mut_ptr, set_boxed_mut_ptr, to_arc_const_ptr, + to_boxed_mut_ptr, try_clone_arc, try_mut_from_ptr, try_ref_from_ptr, try_slice, try_take, + Castable, OwnershipArc, OwnershipBox, OwnershipRef, }; -use rustls_result::NullParameter; +use rustls_result::{AlreadyUsed, NullParameter}; /// An X.509 certificate, as used in rustls. -/// Corresponds to `Certificate` in the Rust API. -/// -pub struct rustls_certificate { +/// Corresponds to `CertificateDer` in the Rust pki-types API. +/// +pub struct rustls_certificate<'a> { // 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], + _marker: PhantomData<&'a ()>, } -impl Castable for rustls_certificate { +impl<'a> Castable for rustls_certificate<'a> { type Ownership = OwnershipRef; - type RustType = Certificate; + type RustType = CertificateDer<'a>; } -impl rustls_certificate { +impl<'a> rustls_certificate<'a> { /// Get the DER data of the certificate itself. /// The data is owned by the certificate and has the same lifetime. #[no_mangle] @@ -153,21 +158,23 @@ pub extern "C" fn rustls_default_ciphersuites_get_entry( /// releases. #[no_mangle] pub static mut RUSTLS_ALL_CIPHER_SUITES: [*const rustls_supported_ciphersuite; 9] = [ - &rustls::cipher_suite::TLS13_AES_256_GCM_SHA384 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS13_AES_128_GCM_SHA256 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256 as *const SupportedCipherSuite + &rustls::crypto::ring::cipher_suite::TLS13_AES_256_GCM_SHA384 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 as *const SupportedCipherSuite + &rustls::crypto::ring::cipher_suite::TLS13_AES_128_GCM_SHA256 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 as *const SupportedCipherSuite - as *const _, - &rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + &rustls::crypto::ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 as *const SupportedCipherSuite - as *const _, - &rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 as *const SupportedCipherSuite - as *const _, - &rustls::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 as *const SupportedCipherSuite as *const _, ]; @@ -181,21 +188,23 @@ pub static RUSTLS_ALL_CIPHER_SUITES_LEN: usize = unsafe { RUSTLS_ALL_CIPHER_SUIT /// between releases. #[no_mangle] pub static mut RUSTLS_DEFAULT_CIPHER_SUITES: [*const rustls_supported_ciphersuite; 9] = [ - &rustls::cipher_suite::TLS13_AES_256_GCM_SHA384 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS13_AES_128_GCM_SHA256 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256 as *const SupportedCipherSuite + &rustls::crypto::ring::cipher_suite::TLS13_AES_256_GCM_SHA384 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 as *const SupportedCipherSuite + &rustls::crypto::ring::cipher_suite::TLS13_AES_128_GCM_SHA256 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 as *const SupportedCipherSuite - as *const _, - &rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + &rustls::crypto::ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 as *const SupportedCipherSuite - as *const _, - &rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 as *const SupportedCipherSuite - as *const _, - &rustls::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 as *const SupportedCipherSuite as *const _, ]; @@ -329,14 +338,14 @@ impl rustls_certified_key { /// /// The returned certificate is valid until the rustls_certified_key is freed. #[no_mangle] - pub extern "C" fn rustls_certified_key_get_certificate( + pub extern "C" fn rustls_certified_key_get_certificate<'a>( certified_key: *const rustls_certified_key, i: size_t, - ) -> *const rustls_certificate { + ) -> *const rustls_certificate<'a> { ffi_panic_boundary! { let certified_key: &CertifiedKey = try_ref_from_ptr!(certified_key); match certified_key.cert.get(i) { - Some(cert) => cert as *const Certificate as *const _, + Some(cert) => cert as *const CertificateDer as *const _, None => null() } } @@ -398,37 +407,32 @@ impl rustls_certified_key { } slice::from_raw_parts(cert_chain, cert_chain_len) }; - let private_key: &[u8] = unsafe { + let private_key_der: &[u8] = unsafe { if private_key.is_null() { return Err(NullParameter); } slice::from_raw_parts(private_key, private_key_len) }; - let mut private_keys: Vec> = match pkcs8_private_keys(&mut Cursor::new(private_key)) - { - Ok(v) => v, - Err(_) => return Err(rustls_result::PrivateKeyParseError), - }; - let private_key: PrivateKey = match private_keys.pop() { - Some(p) => PrivateKey(p), - None => { - private_keys = match rsa_private_keys(&mut Cursor::new(private_key)) { - Ok(v) => v, - Err(_) => return Err(rustls_result::PrivateKeyParseError), - }; - let rsa_private_key: PrivateKey = match private_keys.pop() { - Some(p) => PrivateKey(p), - None => return Err(rustls_result::PrivateKeyParseError), - }; - rsa_private_key - } - }; - let signing_key = match rustls::sign::any_supported_type(&private_key) { + let private_key: PrivateKeyDer = + match pkcs8_private_keys(&mut Cursor::new(private_key_der)).next() { + Some(Ok(p)) => p.into(), + Some(Err(_)) => return Err(rustls_result::PrivateKeyParseError), + None => { + let rsa_private_key: PrivateKeyDer = + match rsa_private_keys(&mut Cursor::new(private_key_der)).next() { + Some(Ok(p)) => p.into(), + _ => return Err(rustls_result::PrivateKeyParseError), + }; + rsa_private_key + } + }; + let signing_key = match rustls::crypto::ring::sign::any_supported_type(&private_key) { Ok(key) => key, Err(_) => return Err(rustls_result::PrivateKeyParseError), }; - let parsed_chain: Vec = match certs(&mut cert_chain) { - Ok(v) => v.into_iter().map(Certificate).collect(), + let parsed_chain: Result, _> = certs(&mut cert_chain).collect(); + let parsed_chain = match parsed_chain { + Ok(v) => v, Err(_) => return Err(rustls_result::CertificateParseError), }; @@ -436,34 +440,46 @@ impl rustls_certified_key { } } -/// A root certificate store. -/// -pub struct rustls_root_cert_store { +/// A `rustls_root_cert_store` being constructed. +/// +/// A builder can be modified by adding trust anchor root certificates with +/// `rustls_root_cert_store_builder_add_pem`. Once you're done adding root certificates, +/// call `rustls_root_cert_store_builder_build` to turn it into a `rustls_root_cert_store`. +/// This object is not safe for concurrent mutation. +pub struct rustls_root_cert_store_builder { // 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 Castable for rustls_root_cert_store { +pub(crate) struct RootCertStoreBuilder { + roots: RootCertStore, +} + +impl Castable for rustls_root_cert_store_builder { type Ownership = OwnershipBox; - type RustType = RootCertStore; + type RustType = Option; } -impl 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. - /// +impl rustls_root_cert_store_builder { + /// Create a `rustls_root_cert_store_builder`. + /// + /// Caller owns the memory and may free it with `rustls_root_cert_store_free`, regardless of + /// whether `rustls_root_cert_store_builder_build` was called. + /// + /// If you wish to abandon the builder without calling `rustls_root_cert_store_builder_build`, + /// it must be freed with `rustls_root_cert_store_builder_free`. #[no_mangle] - pub extern "C" fn rustls_root_cert_store_new() -> *mut rustls_root_cert_store { + pub extern "C" fn rustls_root_cert_store_builder_new() -> *mut rustls_root_cert_store_builder { ffi_panic_boundary! { let store = rustls::RootCertStore::empty(); - to_boxed_mut_ptr(store) + to_boxed_mut_ptr(Some(RootCertStoreBuilder { roots: store })) } } - /// Add one or more certificates to the root cert store using PEM encoded data. + /// Add one or more certificates to the root cert store builder using PEM + /// encoded data. /// /// When `strict` is true an error will return a `CertificateParseError` /// result. So will an attempt to parse data that has zero certificates. @@ -472,17 +488,22 @@ impl rustls_root_cert_store { /// This may be useful on systems that have syntactically invalid root /// certificates. #[no_mangle] - pub extern "C" fn rustls_root_cert_store_add_pem( - store: *mut rustls_root_cert_store, + pub extern "C" fn rustls_root_cert_store_builder_add_pem( + builder: *mut rustls_root_cert_store_builder, pem: *const u8, pem_len: size_t, strict: bool, ) -> rustls_result { ffi_panic_boundary! { let certs_pem: &[u8] = try_slice!(pem, pem_len); - let store: &mut RootCertStore = try_mut_from_ptr!(store); + let builder: &mut Option = try_mut_from_ptr!(builder); + let builder = match builder { + None => return AlreadyUsed, + Some(b) => b, + }; - let certs_der = match rustls_pemfile::certs(&mut Cursor::new(certs_pem)) { + let certs_der: Result, _> = rustls_pemfile::certs(&mut Cursor::new(certs_pem)).collect(); + let certs_der = match certs_der { Ok(vv) => vv, Err(_) => return rustls_result::CertificateParseError, }; @@ -490,301 +511,645 @@ impl rustls_root_cert_store { // API guideline that there are no partial failures or partial // successes. let mut new_store = RootCertStore::empty(); - let (parsed, rejected) = new_store.add_parsable_certificates(&certs_der); + let (parsed, rejected) = new_store.add_parsable_certificates(certs_der); + if strict && (rejected > 0 || parsed == 0) { + return rustls_result::CertificateParseError; + } + + builder.roots.roots.append(&mut new_store.roots); + + rustls_result::Ok + } + } + + /// Add one or more certificates to the root cert store builder using PEM + /// encoded data read from the named file. + /// + /// When `strict` is true an error will return a `CertificateParseError` + /// result. So will an attempt to parse data that has zero certificates. + /// + /// When `strict` is false, unparseable root certificates will be ignored. + /// This may be useful on systems that have syntactically invalid root + /// certificates. + #[no_mangle] + pub extern "C" fn rustls_client_config_builder_load_roots_from_file( + builder: *mut rustls_root_cert_store_builder, + filename: *const c_char, + strict: bool, + ) -> rustls_result { + ffi_panic_boundary! { + let builder: &mut Option = try_mut_from_ptr!(builder); + let builder = match builder { + None => return AlreadyUsed, + Some(b) => b, + }; + + let filename: &CStr = unsafe { + if filename.is_null() { + return rustls_result::NullParameter; + } + CStr::from_ptr(filename) + }; + + let filename: &[u8] = filename.to_bytes(); + let filename: &str = match std::str::from_utf8(filename) { + Ok(s) => s, + Err(_) => return rustls_result::Io, + }; + let filename: &OsStr = OsStr::new(filename); + let mut cafile = match File::open(filename) { + Ok(f) => f, + Err(_) => return rustls_result::Io, + }; + + let mut bufreader = BufReader::new(&mut cafile); + let certs: Result, _> = rustls_pemfile::certs(&mut bufreader).collect(); + let certs = match certs { + Ok(certs) => certs, + Err(_) => return rustls_result::Io, + }; + + // 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 roots = RootCertStore::empty(); + let (parsed, rejected) = roots.add_parsable_certificates(certs); if strict && (rejected > 0 || parsed == 0) { return rustls_result::CertificateParseError; } - store.roots.append(&mut new_store.roots); + builder.roots.roots.append(&mut roots.roots); + rustls_result::Ok + } + } + + /// Create a new `rustls_root_cert_store` from the builder. + /// + /// The builder is consumed and cannot be used again, but must still be freed. + /// + /// The root cert store can be used in several `rustls_web_pki_client_cert_verifier_builder_new` + /// instances and must be freed by the application when no longer needed. See the documentation of + /// `rustls_root_cert_store_free` for details about lifetime. + #[no_mangle] + pub extern "C" fn rustls_root_cert_store_builder_build( + builder: *mut rustls_root_cert_store_builder, + root_cert_store_out: *mut *const rustls_root_cert_store, + ) -> rustls_result { + ffi_panic_boundary! { + let builder: &mut Option = try_mut_from_ptr!(builder); + let builder = try_take!(builder); + + set_arc_mut_ptr(root_cert_store_out, builder.roots); + rustls_result::Ok } } + /// Free a `rustls_root_cert_store_builder` previously returned from + /// `rustls_root_cert_store_builder_new`. Calling with NULL is fine. Must not be + /// called twice with the same value. + #[no_mangle] + pub extern "C" fn rustls_root_cert_store_builder_free( + builder: *mut rustls_root_cert_store_builder, + ) { + ffi_panic_boundary! { + free_box(builder); + } + } +} + +/// A root certificate store. +/// +pub struct rustls_root_cert_store { + // 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 Castable for rustls_root_cert_store { + type Ownership = OwnershipArc; + type RustType = RootCertStore; +} + +impl rustls_root_cert_store { /// Free a rustls_root_cert_store previously returned from rustls_root_cert_store_builder_build. /// Calling with NULL is fine. Must not be called twice with the same value. #[no_mangle] - pub extern "C" fn rustls_root_cert_store_free(store: *mut rustls_root_cert_store) { + pub extern "C" fn rustls_root_cert_store_free(store: *const rustls_root_cert_store) { ffi_panic_boundary! { - let store = try_box_from_ptr!(store); - drop(store) + free_arc(store); } } } -/// A builder for a `rustls_allow_any_authenticated_client_verifier`. This builder object can be -/// used to configure certificate revocation lists, and then turned into a -/// `rustls_allow_any_authenticated_client_verifier` once ready. -pub struct rustls_allow_any_authenticated_client_builder { +/// A built client certificate verifier that can be provided to a `rustls_server_config_builder` +/// with `rustls_server_config_builder_set_client_verifier`. +pub struct rustls_client_cert_verifier { _private: [u8; 0], } -impl Castable for rustls_allow_any_authenticated_client_builder { +/// Rustls' ConfigBuilder requires an `Arc` here, meaning we +/// must follow the pattern described in CONTRIBUTING.md[0] for handling dynamically sized +/// types (DSTs) across the FFI boundary. +/// [0] +impl Castable for rustls_client_cert_verifier { type Ownership = OwnershipBox; - // NOTE: contained value is consumed even on error, so this can contain None, but the caller - // still needs to free it - type RustType = Option; + type RustType = Arc; } -impl rustls_allow_any_authenticated_client_builder { - /// Create a new allow any authenticated client certificate verifier builder using the root store. +impl rustls_client_cert_verifier { + /// Free a `rustls_client_cert_verifier` previously returned from + /// `rustls_client_cert_verifier_builder_build`. Calling with NULL is fine. Must not be + /// called twice with the same value. + #[no_mangle] + pub extern "C" fn rustls_client_cert_verifier_free(verifier: *mut rustls_client_cert_verifier) { + ffi_panic_boundary! { + free_box(verifier); + } + } +} + +/// A client certificate verifier being constructed. A builder can be modified by, +/// e.g. `rustls_web_pki_client_cert_verifier_builder_add_crl`. Once you're +/// done configuring settings, call `rustls_web_pki_client_cert_verifier_builder_build` +/// to turn it into a `rustls_client_cert_verifier`. This object is not safe +/// for concurrent mutation. +// TODO(@cpu): Add rustdoc link once available. +pub struct rustls_web_pki_client_cert_verifier_builder { + // 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], +} + +pub(crate) struct ClientCertVerifierBuilder { + roots: Arc, + root_hint_subjects: Vec, + crls: Vec>, + revocation_depth: RevocationCheckDepth, + revocation_policy: UnknownStatusPolicy, + allow_unauthenticated: bool, +} + +impl Castable for rustls_web_pki_client_cert_verifier_builder { + type Ownership = OwnershipBox; + type RustType = Option; +} + +impl rustls_web_pki_client_cert_verifier_builder { + /// Create a `rustls_web_pki_client_cert_verifier_builder`. Caller owns the memory and may + /// eventually call `rustls_web_pki_client_cert_verifier_builder_free` to free it, whether or + /// not `rustls_web_pki_client_cert_verifier_builder_build` was called. + /// + /// Without further modification the builder will produce a client certificate verifier that + /// will require a client present a client certificate that chains to one of the trust anchors + /// in the provided `rustls_root_cert_store`. The root cert store must not be empty. /// - /// This copies the contents of the rustls_root_cert_store. It does not take - /// ownership of the pointed-to memory. + /// Revocation checking will not be performed unless + /// `rustls_web_pki_client_cert_verifier_builder_add_crl` is used to add certificate revocation + /// lists (CRLs) to the builder. If CRLs are added, revocation checking will be performed + /// for the entire certificate chain unless + /// `rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation` is used. Unknown + /// revocation status for certificates considered for revocation status will be treated as + /// an error unless `rustls_web_pki_client_cert_verifier_allow_unknown_revocation_status` is + /// used. /// - /// This object can then be used to load any CRLs. + /// Unauthenticated clients will not be permitted unless + /// `rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated` is used. /// - /// Once that is complete, convert it into a real `rustls_allow_any_authenticated_client_verifier` - /// by calling `rustls_allow_any_authenticated_client_verifier_new()`. + /// This copies the contents of the `rustls_root_cert_store`. It does not take + /// ownership of the pointed-to data. #[no_mangle] - pub extern "C" fn rustls_allow_any_authenticated_client_builder_new( + pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_new( store: *const rustls_root_cert_store, - ) -> *mut rustls_allow_any_authenticated_client_builder { + ) -> *mut rustls_web_pki_client_cert_verifier_builder { ffi_panic_boundary! { - let store: &RootCertStore = try_ref_from_ptr!(store); - let client_cert_verifier = Some(AllowAnyAuthenticatedClient::new(store.clone())); - to_boxed_mut_ptr(client_cert_verifier) + let store = try_clone_arc!(store); + let builder = ClientCertVerifierBuilder { + root_hint_subjects: store.subjects(), + roots: store, + crls: Vec::default(), + revocation_depth: RevocationCheckDepth::Chain, + revocation_policy: UnknownStatusPolicy::Deny, + allow_unauthenticated: false, + }; + to_boxed_mut_ptr(Some(builder)) } } - /// Add one or more certificate revocation lists (CRLs) to the client certificate verifier by - /// reading the CRL content from the provided buffer of PEM encoded content. + /// Add one or more certificate revocation lists (CRLs) to the client certificate verifier + /// builder by reading the CRL content from the provided buffer of PEM encoded content. /// - /// This function returns an error if the provided buffer is not valid PEM encoded content, - /// or if the CRL content is invalid or unsupported. + /// By default revocation checking will be performed on the entire certificate chain. To only + /// check the revocation status of the end entity certificate, use + /// `rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation`. + /// + /// This function returns an error if the provided buffer is not valid PEM encoded content. #[no_mangle] - pub extern "C" fn rustls_allow_any_authenticated_client_builder_add_crl( - builder: *mut rustls_allow_any_authenticated_client_builder, + pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_add_crl( + builder: *mut rustls_web_pki_client_cert_verifier_builder, crl_pem: *const u8, crl_pem_len: size_t, ) -> rustls_result { ffi_panic_boundary! { - let client_cert_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder = match client_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; let crl_pem: &[u8] = try_slice!(crl_pem, crl_pem_len); - let crls_der: Vec = match crls(&mut Cursor::new(crl_pem)) { - Ok(vv) => vv.into_iter().map(UnparsedCertRevocationList).collect(), + let crls_der: Result, _> = crls(&mut Cursor::new(crl_pem)).collect(); + let crls_der = match crls_der{ + Ok(vv) => vv, Err(_) => return rustls_result::CertificateRevocationListParseError, }; + if crls_der.is_empty() { + return rustls_result::CertificateRevocationListParseError; + } + + + client_verifier_builder.crls.extend(crls_der); + rustls_result::Ok + } + } - let client_cert_verifier = try_take!(client_cert_verifier_builder); - match client_cert_verifier.with_crls(crls_der) { - Ok(v) => client_cert_verifier_builder.replace(v), - Err(e) => return map_error(rustls::Error::InvalidCertRevocationList(e)), + /// When CRLs are provided with `rustls_web_pki_client_cert_verifier_builder_add_crl`, only + /// check the revocation status of end entity certificates, ignoring any intermediate certificates + /// in the chain. + #[no_mangle] + pub extern "C" fn rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation( + builder: *mut rustls_web_pki_client_cert_verifier_builder, + ) -> rustls_result { + ffi_panic_boundary! { + let client_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder = match client_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, }; + client_verifier_builder.revocation_depth = RevocationCheckDepth::EndEntity; rustls_result::Ok } } - /// Free a `rustls_allow_any_authenticated_client_builder` previously returned from - /// `rustls_allow_any_authenticated_client_builder_new`. - /// Calling with NULL is fine. Must not be called twice with the same value. + /// When CRLs are provided with `rustls_web_pki_client_cert_verifier_builder_add_crl`, and it + /// isn't possible to determine the revocation status of a considered certificate, do not treat + /// it as an error condition. + /// + /// Overrides the default behavior where unknown revocation status is considered an error. #[no_mangle] - pub extern "C" fn rustls_allow_any_authenticated_client_builder_free( - builder: *mut rustls_allow_any_authenticated_client_builder, - ) { + pub extern "C" fn rustls_web_pki_client_cert_verifier_allow_unknown_revocation_status( + builder: *mut rustls_web_pki_client_cert_verifier_builder, + ) -> rustls_result { ffi_panic_boundary! { - let store = try_box_from_ptr!(builder); - drop(store) + let client_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder = match client_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; + + client_verifier_builder.revocation_policy = UnknownStatusPolicy::Allow; + rustls_result::Ok } } -} -/// A verifier of client certificates that requires all certificates to be -/// trusted based on a given `rustls_root_cert_store`. Usable in building server -/// configurations. Connections without such a client certificate will not -/// be accepted. -pub struct rustls_allow_any_authenticated_client_verifier { - _private: [u8; 0], -} + /// Allow unauthenticated anonymous clients in addition to those that present a client + /// certificate that chains to one of the verifier's configured trust anchors. + #[no_mangle] + pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated( + builder: *mut rustls_web_pki_client_cert_verifier_builder, + ) -> rustls_result { + ffi_panic_boundary! { + let client_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder = match client_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; -impl Castable for rustls_allow_any_authenticated_client_verifier { - type Ownership = OwnershipArc; - type RustType = AllowAnyAuthenticatedClient; -} + client_verifier_builder.allow_unauthenticated = true; + rustls_result::Ok + } + } + + /// Clear the list of trust anchor hint subjects. + /// + /// By default, the client cert verifier will use the subjects provided by the root cert + /// store configured for client authentication. Calling this function will remove these + /// hint subjects, indicating the client should make a free choice of which certificate + /// to send. + #[no_mangle] + pub extern "C" fn rustls_web_pki_client_cert_verifier_clear_root_hint_subjects( + builder: *mut rustls_web_pki_client_cert_verifier_builder, + ) -> rustls_result { + ffi_panic_boundary! { + let client_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder = match client_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; + + client_verifier_builder.root_hint_subjects.clear(); + rustls_result::Ok + } + } -impl rustls_allow_any_authenticated_client_verifier { - /// Create a new allow any authenticated client certificate verifier from a builder. + /// Add additional distinguished names to the list of trust anchor hint subjects. + /// + /// By default, the client cert verifier will use the subjects provided by the root cert + /// store configured for client authentication. Calling this function will add to these + /// existing hint subjects. Calling this function with an empty `store` will have no + /// effect, use `rustls_web_pki_client_cert_verifier_clear_root_hint_subjects` to clear + /// the subject hints. + #[no_mangle] + pub extern "C" fn rustls_web_pki_client_cert_verifier_add_root_hint_subjects( + builder: *mut rustls_web_pki_client_cert_verifier_builder, + store: *const rustls_root_cert_store, + ) -> rustls_result { + let client_verifier_builder: &mut Option = + try_mut_from_ptr!(builder); + let client_verifier_builder = match client_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; + + let store = try_clone_arc!(store); + client_verifier_builder.root_hint_subjects = store.subjects(); + rustls_result::Ok + } + + /// Create a new client certificate verifier from the builder. /// /// The builder is consumed and cannot be used again, but must still be freed. /// - /// The verifier can be used in several `rustls_server_config` instances. Must be freed by - /// the application when no longer needed. See the documentation of - /// `rustls_allow_any_authenticated_client_verifier_free` for details about lifetime. - /// This copies the contents of the `rustls_root_cert_store`. It does not take - /// ownership of the pointed-to memory. + /// The verifier can be used in several `rustls_server_config` instances and must be + /// freed by the application when no longer needed. See the documentation of + /// `rustls_web_pki_client_cert_verifier_builder_free` for details about lifetime. #[no_mangle] - pub extern "C" fn rustls_allow_any_authenticated_client_verifier_new( - builder: *mut rustls_allow_any_authenticated_client_builder, - ) -> *const rustls_allow_any_authenticated_client_verifier { + pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_build( + builder: *mut rustls_web_pki_client_cert_verifier_builder, + verifier_out: *mut *mut rustls_client_cert_verifier, + ) -> rustls_result { ffi_panic_boundary! { - let client_cert_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder = try_take!(client_verifier_builder); - let client_cert_verifier = match client_cert_verifier_builder.take() { - None => { - return null() as *const _; - }, - Some(x) => x, + let mut builder = WebPkiClientVerifier::builder(client_verifier_builder.roots) + .with_crls(client_verifier_builder.crls); + match client_verifier_builder.revocation_depth { + RevocationCheckDepth::EndEntity => builder = builder.only_check_end_entity_revocation(), + RevocationCheckDepth::Chain => {}, + } + match client_verifier_builder.revocation_policy { + UnknownStatusPolicy::Allow => builder = builder.allow_unknown_revocation_status(), + UnknownStatusPolicy::Deny => {}, + } + if client_verifier_builder.allow_unauthenticated { + builder = builder.allow_unauthenticated(); + } + if client_verifier_builder.root_hint_subjects.is_empty() { + builder = builder.clear_root_hint_subjects(); + } else { + builder = builder.add_root_hint_subjects(client_verifier_builder.root_hint_subjects); + } + + let verifier = match builder.build() { + Ok(v) => v, + Err(e) => return error::map_verifier_builder_error(e), }; - Arc::into_raw(client_cert_verifier.boxed()) as *const _ + + set_boxed_mut_ptr(verifier_out, verifier); + + rustls_result::Ok } } - /// "Free" a verifier previously returned from - /// `rustls_allow_any_authenticated_client_verifier_new`. Since - /// `rustls_allow_any_authenticated_client_verifier` is actually an - /// atomically reference-counted pointer, extant server_configs may still - /// hold an internal reference to the Rust object. However, C code must - /// consider this pointer unusable after "free"ing it. - /// Calling with NULL is fine. Must not be called twice with the same value. + /// Free a `rustls_client_cert_verifier_builder` previously returned from + /// `rustls_client_cert_verifier_builder_new`. Calling with NULL is fine. Must not be + /// called twice with the same value. #[no_mangle] - pub extern "C" fn rustls_allow_any_authenticated_client_verifier_free( - verifier: *const rustls_allow_any_authenticated_client_verifier, + pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_free( + builder: *mut rustls_web_pki_client_cert_verifier_builder, ) { ffi_panic_boundary! { - free_arc(verifier); + free_box(builder); } } } -/// A builder for a `rustls_allow_any_anonymous_or_authenticated_client_verifier`. This builder -/// object can be used to configure certificate revocation lists, and then turned into a -/// `rustls_allow_any_anonymous_or_authenticated_client_verifier` once ready. -pub struct rustls_allow_any_anonymous_or_authenticated_client_builder { +/// A server certificate verifier being constructed. A builder can be modified by, +/// e.g. `rustls_web_pki_server_cert_verifier_builder_add_crl`. Once you're +/// done configuring settings, call `rustls_web_pki_server_cert_verifier_builder_build` +/// to turn it into a `rustls_server_cert_verifier`. This object is not safe +/// for concurrent mutation. +// TODO(@cpu): Add rustdoc link once available. +pub struct rustls_web_pki_server_cert_verifier_builder { + // 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 Castable for rustls_allow_any_anonymous_or_authenticated_client_builder { +pub(crate) struct ServerCertVerifierBuilder { + roots: Arc, + crls: Vec>, + revocation_depth: RevocationCheckDepth, + revocation_policy: UnknownStatusPolicy, +} + +impl Castable for rustls_web_pki_server_cert_verifier_builder { type Ownership = OwnershipBox; - // NOTE: contained value is consumed even on error, so this can contain None, but the caller - // still needs to free it - type RustType = Option; + type RustType = Option; } -impl rustls_allow_any_anonymous_or_authenticated_client_builder { - /// Create a new allow any anonymous or authenticated client certificate verifier builder - /// using the root store. +impl ServerCertVerifierBuilder { + /// Create a `rustls_web_pki_server_cert_verifier_builder`. Caller owns the memory and may + /// free it with `rustls_web_pki_server_cert_verifier_builder_free`, regardless of whether + /// `rustls_web_pki_server_cert_verifier_builder_build` was called. /// - /// This copies the contents of the rustls_root_cert_store. It does not take - /// ownership of the pointed-to memory. + /// Without further modification the builder will produce a server certificate verifier that + /// will require a server present a certificate that chains to one of the trust anchors + /// in the provided `rustls_root_cert_store`. The root cert store must not be empty. /// - /// This object can then be used to load any CRLs. + /// Revocation checking will not be performed unless + /// `rustls_web_pki_server_cert_verifier_builder_add_crl` is used to add certificate revocation + /// lists (CRLs) to the builder. If CRLs are added, revocation checking will be performed + /// for the entire certificate chain unless + /// `rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation` is used. Unknown + /// revocation status for certificates considered for revocation status will be treated as + /// an error unless `rustls_web_pki_server_cert_verifier_allow_unknown_revocation_status` is + /// used. /// - /// Once that is complete, convert it into a real - /// `rustls_allow_any_anonymous_or_authenticated_client_verifier` - /// by calling `rustls_allow_any_anonymous_or_authenticated_client_verifier_new()`. + /// This copies the contents of the `rustls_root_cert_store`. It does not take + /// ownership of the pointed-to data. #[no_mangle] - pub extern "C" fn rustls_client_cert_verifier_optional_builder_new( + pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_new( store: *const rustls_root_cert_store, - ) -> *mut rustls_allow_any_anonymous_or_authenticated_client_builder { + ) -> *mut rustls_web_pki_server_cert_verifier_builder { ffi_panic_boundary! { - let store: &RootCertStore = try_ref_from_ptr!(store); - let client_cert_verifier = Some(AllowAnyAnonymousOrAuthenticatedClient::new(store.clone())); - to_boxed_mut_ptr(client_cert_verifier) + let store = try_clone_arc!(store); + let builder = ServerCertVerifierBuilder { + roots: store, + crls: Vec::default(), + revocation_depth: RevocationCheckDepth::Chain, + revocation_policy: UnknownStatusPolicy::Deny + }; + to_boxed_mut_ptr(Some(builder)) } } - /// Add one or more certificate revocation lists (CRLs) to the client certificate verifier by - /// reading the CRL content from the provided buffer of PEM encoded content. + /// Add one or more certificate revocation lists (CRLs) to the server certificate verifier + /// builder by reading the CRL content from the provided buffer of PEM encoded content. /// - /// This function returns an error if the provided buffer is not valid PEM encoded content, - /// or if the CRL content is invalid or unsupported. + /// By default revocation checking will be performed on the entire certificate chain. To only + /// check the revocation status of the end entity certificate, use + /// `rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation`. + /// + /// This function returns an error if the provided buffer is not valid PEM encoded content. #[no_mangle] - pub extern "C" fn rustls_client_cert_verifier_optional_builder_add_crl( - builder: *mut rustls_allow_any_anonymous_or_authenticated_client_builder, + pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_add_crl( + builder: *mut rustls_web_pki_server_cert_verifier_builder, crl_pem: *const u8, crl_pem_len: size_t, ) -> rustls_result { ffi_panic_boundary! { - let client_cert_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let server_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let server_verifier_builder = match server_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; let crl_pem: &[u8] = try_slice!(crl_pem, crl_pem_len); - let crls_der: Vec = match crls(&mut Cursor::new(crl_pem)) { - Ok(vv) => vv.into_iter().map(UnparsedCertRevocationList).collect(), + let crls_der: Result, _> = crls(&mut Cursor::new(crl_pem)).collect(); + let crls_der = match crls_der{ + Ok(vv) => vv, Err(_) => return rustls_result::CertificateRevocationListParseError, }; + if crls_der.is_empty() { + return rustls_result::CertificateRevocationListParseError; + } - let client_cert_verifier = try_take!(client_cert_verifier_builder); - match client_cert_verifier.with_crls(crls_der) { - Ok(v) => client_cert_verifier_builder.replace(v), - Err(e) => return map_error(rustls::Error::InvalidCertRevocationList(e)), - }; + server_verifier_builder.crls.extend(crls_der); rustls_result::Ok } } - /// Free a `rustls_allow_any_anonymous_or_authenticated_client_builder` previously returned from - /// `rustls_client_cert_verifier_optional_builder_new`. - /// Calling with NULL is fine. Must not be called twice with the same value. + /// When CRLs are provided with `rustls_web_pki_server_cert_verifier_builder_add_crl`, only + /// check the revocation status of end entity certificates, ignoring any intermediate certificates + /// in the chain. #[no_mangle] - pub extern "C" fn rustls_client_cert_verifier_optional_builder_free( - builder: *mut rustls_allow_any_anonymous_or_authenticated_client_builder, - ) { + pub extern "C" fn rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation( + builder: *mut rustls_web_pki_server_cert_verifier_builder, + ) -> rustls_result { ffi_panic_boundary! { - let store = try_box_from_ptr!(builder); - drop(store) + let server_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let server_verifier_builder = match server_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; + + server_verifier_builder.revocation_depth = RevocationCheckDepth::EndEntity; + rustls_result::Ok } } -} -/// Alternative to `rustls_allow_any_authenticated_client_verifier` that allows connections -/// with or without a client certificate. If the client offers a certificate, -/// it will be verified (and rejected if it is not valid). If the client -/// does not offer a certificate, the connection will succeed. -/// -/// The application can retrieve the certificate, if any, with -/// `rustls_connection_get_peer_certificate`. -pub struct rustls_allow_any_anonymous_or_authenticated_client_verifier { - _private: [u8; 0], -} + /// When CRLs are provided with `rustls_web_pki_server_cert_verifier_builder_add_crl`, and it + /// isn't possible to determine the revocation status of a considered certificate, do not treat + /// it as an error condition. + /// + /// Overrides the default behavior where unknown revocation status is considered an error. + #[no_mangle] + pub extern "C" fn rustls_web_pki_server_cert_verifier_allow_unknown_revocation_status( + builder: *mut rustls_web_pki_server_cert_verifier_builder, + ) -> rustls_result { + ffi_panic_boundary! { + let server_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let server_verifier_builder = match server_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; -impl Castable for rustls_allow_any_anonymous_or_authenticated_client_verifier { - type Ownership = OwnershipArc; - type RustType = AllowAnyAnonymousOrAuthenticatedClient; -} + server_verifier_builder.revocation_policy = UnknownStatusPolicy::Allow; + rustls_result::Ok + } + } -impl rustls_allow_any_anonymous_or_authenticated_client_verifier { - /// Create a new allow any anonymous or authenticated client certificate verifier builder - /// from the builder. + /// Create a new server certificate verifier from the builder. /// /// The builder is consumed and cannot be used again, but must still be freed. /// - /// The verifier can be used in several `rustls_server_config` instances. Must be + /// The verifier can be used in several `rustls_client_config` instances and must be /// freed by the application when no longer needed. See the documentation of - /// `rustls_allow_any_anonymous_or_authenticated_client_verifier_free` for details about lifetime. - /// This copies the contents of the `rustls_root_cert_store`. It does not take - /// ownership of the pointed-to data. + /// `rustls_web_pki_server_cert_verifier_builder_free` for details about lifetime. #[no_mangle] - pub extern "C" fn rustls_allow_any_anonymous_or_authenticated_client_verifier_new( - builder: *mut rustls_allow_any_anonymous_or_authenticated_client_builder, - ) -> *const rustls_allow_any_anonymous_or_authenticated_client_verifier { + pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_build( + builder: *mut rustls_web_pki_server_cert_verifier_builder, + verifier_out: *mut *mut rustls_server_cert_verifier, + ) -> rustls_result { ffi_panic_boundary! { - let client_cert_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let server_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let server_verifier_builder = try_take!(server_verifier_builder); - let client_cert_verifier = match client_cert_verifier_builder.take() { - None => { - return null() as *const _; - }, - Some(x) => x, + let mut builder = WebPkiServerVerifier::builder(server_verifier_builder.roots) + .with_crls(server_verifier_builder.crls); + match server_verifier_builder.revocation_depth { + RevocationCheckDepth::EndEntity => builder = builder.only_check_end_entity_revocation(), + RevocationCheckDepth::Chain => {}, + } + match server_verifier_builder.revocation_policy { + UnknownStatusPolicy::Allow => builder = builder.allow_unknown_revocation_status(), + UnknownStatusPolicy::Deny => {}, + } + + let verifier = match builder.build() { + Ok(v) => v, + Err(e) => return error::map_verifier_builder_error(e), }; - Arc::into_raw(client_cert_verifier.boxed()) as *const _ + + set_boxed_mut_ptr(verifier_out, verifier); + + rustls_result::Ok } } - /// "Free" a verifier previously returned from - /// `rustls_allow_any_anonymous_or_authenticated_client_verifier_new`. Since - /// `rustls_allow_any_anonymous_or_authenticated_client_verifier` - /// is actually an atomically reference-counted pointer, extant `server_configs` may still - /// hold an internal reference to the Rust object. However, C code must - /// consider this pointer unusable after "free"ing it. - /// Calling with NULL is fine. Must not be called twice with the same value. + /// Free a `rustls_server_cert_verifier_builder` previously returned from + /// `rustls_server_cert_verifier_builder_new`. Calling with NULL is fine. Must not be + /// called twice with the same value. #[no_mangle] - pub extern "C" fn rustls_allow_any_anonymous_or_authenticated_client_verifier_free( - verifier: *const rustls_allow_any_anonymous_or_authenticated_client_verifier, + pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_free( + builder: *mut rustls_web_pki_server_cert_verifier_builder, ) { ffi_panic_boundary! { - free_arc(verifier); + free_box(builder); + } + } +} + +/// A built server certificate verifier that can be provided to a `rustls_client_config_builder` +/// with `rustls_client_config_builder_set_server_verifier`. +pub struct rustls_server_cert_verifier { + _private: [u8; 0], +} + +/// Rustls' ConfigBuilder requires an `Arc` here, meaning we +/// must follow the pattern described in CONTRIBUTING.md[0] for handling dynamically sized +/// types (DSTs) across the FFI boundary. +/// [0] +impl Castable for rustls_server_cert_verifier { + type Ownership = OwnershipBox; + type RustType = Arc; +} + +impl rustls_server_cert_verifier { + /// Free a `rustls_server_cert_verifier` previously returned from + /// `rustls_server_cert_verifier_builder_build`. Calling with NULL is fine. Must not be + /// called twice with the same value. + #[no_mangle] + pub extern "C" fn rustls_server_cert_verifier_free(verifier: *mut rustls_server_cert_verifier) { + ffi_panic_boundary! { + free_box(verifier); } } } diff --git a/src/client.rs b/src/client.rs index 6fef5af5..28075343 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,20 +1,23 @@ use std::borrow::Cow; use std::convert::TryInto; -use std::ffi::{CStr, OsStr}; -use std::fs::File; -use std::io::BufReader; +use std::ffi::CStr; +use std::fmt::{Debug, Formatter}; use std::slice; use std::sync::Arc; -use std::time::SystemTime; use libc::{c_char, size_t}; -use rustls::client::{ResolvesClientCert, ServerCertVerified, ServerCertVerifier}; +use pki_types::{CertificateDer, UnixTime}; +use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; +use rustls::client::{ResolvesClientCert, WebPkiServerVerifier}; +use rustls::crypto::ring::ALL_CIPHER_SUITES; use rustls::{ - sign::CertifiedKey, Certificate, CertificateError, ClientConfig, ClientConnection, - ProtocolVersion, RootCertStore, SupportedCipherSuite, WantsVerifier, ALL_CIPHER_SUITES, + sign::CertifiedKey, CertificateError, ClientConfig, ClientConnection, DigitallySignedStruct, + Error, ProtocolVersion, SignatureScheme, SupportedCipherSuite, WantsVerifier, }; -use crate::cipher::{rustls_certified_key, rustls_root_cert_store, rustls_supported_ciphersuite}; +use crate::cipher::{ + rustls_certified_key, rustls_server_cert_verifier, rustls_supported_ciphersuite, +}; use crate::connection::{rustls_connection, Connection}; use crate::error::rustls_result::{InvalidParameter, NullParameter}; use crate::error::{self, rustls_result}; @@ -68,22 +71,44 @@ impl Castable for rustls_client_config { type RustType = ClientConfig; } +#[derive(Debug)] struct NoneVerifier; impl ServerCertVerifier for NoneVerifier { fn verify_server_cert( &self, - _end_entity: &Certificate, - _intermediates: &[Certificate], + _end_entity: &CertificateDer, + _intermediates: &[CertificateDer], _server_name: &rustls::ServerName, - _scts: &mut dyn Iterator, _ocsp_response: &[u8], - _now: SystemTime, + _now: UnixTime, ) -> Result { Err(rustls::Error::InvalidCertificate( CertificateError::BadSignature, )) } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer, + _dss: &DigitallySignedStruct, + ) -> Result { + Err(Error::InvalidCertificate(CertificateError::BadSignature)) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer, + _dss: &DigitallySignedStruct, + ) -> Result { + Err(Error::InvalidCertificate(CertificateError::BadSignature)) + } + + fn supported_verify_schemes(&self) -> Vec { + WebPkiServerVerifier::default_supported_verify_schemes() + } } impl rustls_client_config_builder { @@ -225,15 +250,14 @@ unsafe impl Send for Verifier {} /// rustls_client_config_builder_dangerous_set_certificate_verifier. unsafe impl Sync for Verifier {} -impl rustls::client::ServerCertVerifier for Verifier { +impl ServerCertVerifier for Verifier { fn verify_server_cert( &self, - end_entity: &Certificate, - intermediates: &[Certificate], + end_entity: &CertificateDer, + intermediates: &[CertificateDer], server_name: &rustls::ServerName, - _scts: &mut dyn Iterator, ocsp_response: &[u8], - _now: SystemTime, + _now: UnixTime, ) -> Result { let cb = self.callback; let server_name: Cow<'_, str> = match server_name { @@ -267,6 +291,34 @@ impl rustls::client::ServerCertVerifier for Verifier { r => Err(error::cert_result_to_error(r)), } } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer, + dss: &DigitallySignedStruct, + ) -> Result { + WebPkiServerVerifier::default_verify_tls12_signature(message, cert, dss) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer, + dss: &DigitallySignedStruct, + ) -> Result { + WebPkiServerVerifier::default_verify_tls13_signature(message, cert, dss) + } + + fn supported_verify_schemes(&self) -> Vec { + WebPkiServerVerifier::default_supported_verify_schemes() + } +} + +impl Debug for Verifier { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Verifier").finish() + } } impl rustls_client_config_builder { @@ -312,66 +364,18 @@ impl rustls_client_config_builder { } } - /// Use the trusted root certificates from the provided store. + /// Configure the server certificate verifier. /// - /// This replaces any trusted roots already configured with copies - /// from `roots`. This adds 1 to the refcount for `roots`. When you - /// call rustls_client_config_free or rustls_client_config_builder_free, - /// those will subtract 1 from the refcount for `roots`. - #[no_mangle] - pub extern "C" fn rustls_client_config_builder_use_roots( - config_builder: *mut rustls_client_config_builder, - roots: *const rustls_root_cert_store, - ) -> rustls_result { - ffi_panic_boundary! { - let builder = try_mut_from_ptr!(config_builder); - let root_store: &RootCertStore = try_ref_from_ptr!(roots); - builder.verifier = Arc::new(rustls::client::WebPkiVerifier::new(root_store.clone(), None)); - rustls_result::Ok - } - } - - /// Add trusted root certificates from the named file, which should contain - /// PEM-formatted certificates. + /// This increases the reference count of `verifier` and does not take ownership. #[no_mangle] - pub extern "C" fn rustls_client_config_builder_load_roots_from_file( - config_builder: *mut rustls_client_config_builder, - filename: *const c_char, - ) -> rustls_result { + pub extern "C" fn rustls_client_config_builder_set_server_verifier( + builder: *mut rustls_client_config_builder, + verifier: *const rustls_server_cert_verifier, + ) { ffi_panic_boundary! { - let config_builder = try_mut_from_ptr!(config_builder); - let filename: &CStr = unsafe { - if filename.is_null() { - return rustls_result::NullParameter; - } - CStr::from_ptr(filename) - }; - - let filename: &[u8] = filename.to_bytes(); - let filename: &str = match std::str::from_utf8(filename) { - Ok(s) => s, - Err(_) => return rustls_result::Io, - }; - let filename: &OsStr = OsStr::new(filename); - let mut cafile = match File::open(filename) { - Ok(f) => f, - Err(_) => return rustls_result::Io, - }; - - let mut bufreader = BufReader::new(&mut cafile); - let certs = match rustls_pemfile::certs(&mut bufreader) { - Ok(certs) => certs, - Err(_) => return rustls_result::Io, - }; - - let mut roots = RootCertStore::empty(); - let (_, failed) = roots.add_parsable_certificates(&certs); - if failed > 0 { - return rustls_result::CertificateParseError; - } - - config_builder.verifier = Arc::new(rustls::client::WebPkiVerifier::new(roots, None)); - rustls_result::Ok + let builder: &mut ClientConfigBuilder = try_mut_from_ptr!(builder); + let verifier = try_ref_from_ptr!(verifier); + builder.verifier = verifier.clone(); } } @@ -452,6 +456,7 @@ impl rustls_client_config_builder { } /// Always send the same client certificate. +#[derive(Debug)] struct ResolvesClientCertFromChoices { keys: Vec>, } @@ -484,7 +489,7 @@ impl rustls_client_config_builder { ) -> *const rustls_client_config { ffi_panic_boundary! { let builder: Box = try_box_from_ptr!(builder); - let config = builder.base.with_custom_certificate_verifier(builder.verifier); + let config = builder.base.dangerous().with_custom_certificate_verifier(builder.verifier); let mut config = match builder.cert_resolver { Some(r) => config.with_client_cert_resolver(r), None => config.with_no_client_auth(), diff --git a/src/connection.rs b/src/connection.rs index 49e93b00..78d25f9f 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -3,9 +3,9 @@ use std::{ffi::c_void, ptr::null}; use std::{ptr::null_mut, slice}; use libc::{size_t, EINVAL, EIO}; -use rustls::{ - Certificate, ClientConnection, ServerConnection, SupportedCipherSuite, ALL_CIPHER_SUITES, -}; +use pki_types::CertificateDer; +use rustls::crypto::ring::ALL_CIPHER_SUITES; +use rustls::{ClientConnection, ServerConnection, SupportedCipherSuite}; use crate::io::{ rustls_write_vectored_callback, CallbackReader, CallbackWriter, ReadCallback, @@ -324,14 +324,14 @@ impl rustls_connection { /// `const struct rustls_connection *`). /// #[no_mangle] - pub extern "C" fn rustls_connection_get_peer_certificate( + pub extern "C" fn rustls_connection_get_peer_certificate<'a>( conn: *const rustls_connection, i: size_t, - ) -> *const rustls_certificate { + ) -> *const rustls_certificate<'a> { ffi_panic_boundary! { let conn: &Connection = try_ref_from_ptr!(conn); match conn.peer_certificates().and_then(|c| c.get(i)) { - Some(cert) => cert as *const Certificate as *const _, + Some(cert) => cert as *const CertificateDer as *const _, None => null() } } diff --git a/src/error.rs b/src/error.rs index 97a750f6..3cb6d344 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use crate::ffi_panic_boundary; use libc::{c_char, c_uint, size_t}; +use rustls::server::VerifierBuilderError; use rustls::{CertRevocationListError, CertificateError, Error, InvalidMessage}; /// A return value for a function that may return either success (0) or a @@ -162,12 +163,12 @@ u32_enum_builder! { AlertNoApplicationProtocol => 7233, AlertUnknown => 7234, - // https://docs.rs/sct/latest/sct/enum.Error.html - CertSCTMalformed => 7319, - CertSCTInvalidSignature => 7320, - CertSCTTimestampInFuture => 7321, - CertSCTUnsupportedVersion => 7322, - CertSCTUnknownLog => 7323, + // Reserved from previous use pre rustls-ffi <0.22.0 + // CertSCTMalformed => 7319, + // CertSCTInvalidSignature => 7320, + // CertSCTTimestampInFuture => 7321, + // CertSCTUnsupportedVersion => 7322, + // CertSCTUnknownLog => 7323, // From InvalidCertRevocationList, with fields that get flattened. // https://docs.rs/rustls/0.21.6/rustls/enum.Error.html#variant.InvalidCertRevocationList @@ -181,7 +182,10 @@ u32_enum_builder! { CertRevocationListUnsupportedCriticalExtension => 7407, CertRevocationListUnsupportedDeltaCrl => 7408, CertRevocationListUnsupportedIndirectCrl => 7409, - CertRevocationListUnsupportedRevocationReason => 7410 + CertRevocationListUnsupportedRevocationReason => 7410, + + // From ClientCertVerifierBuilderError, with fields that get flattened. + ClientCertVerifierBuilderNoRootAnchors => 7500 } } @@ -229,11 +233,6 @@ impl rustls_result { | CertInvalidPurpose | CertApplicationVerificationFailure | CertOtherError - | CertSCTMalformed - | CertSCTInvalidSignature - | CertSCTTimestampInFuture - | CertSCTUnsupportedVersion - | CertSCTUnknownLog ) } } @@ -259,11 +258,6 @@ pub(crate) fn cert_result_to_error(result: rustls_result) -> rustls::Error { InvalidCertificate(CertificateError::ApplicationVerificationFailure) } CertOtherError => InvalidCertificate(CertificateError::Other(Arc::from(Box::from("")))), - CertSCTMalformed => InvalidSct(sct::Error::MalformedSct), - CertSCTInvalidSignature => InvalidSct(sct::Error::InvalidSignature), - CertSCTTimestampInFuture => InvalidSct(sct::Error::TimestampInFuture), - CertSCTUnsupportedVersion => InvalidSct(sct::Error::UnsupportedSctVersion), - CertSCTUnknownLog => InvalidSct(sct::Error::UnknownLog), _ => rustls::Error::General("".into()), } } @@ -294,17 +288,11 @@ fn test_rustls_result_is_cert_error() { for id in 7121..=7131 { assert!(rustls_result::rustls_result_is_cert_error(id)); } - - // Test SCTError range - for id in 7319..=7323 { - assert!(rustls_result::rustls_result_is_cert_error(id)); - } } pub(crate) fn map_error(input: rustls::Error) -> rustls_result { use rustls::AlertDescription as alert; use rustls_result::*; - use sct::Error as sct; match input { Error::InappropriateMessage { .. } => InappropriateMessage, @@ -402,48 +390,52 @@ pub(crate) fn map_error(input: rustls::Error) -> rustls_result { alert::Unknown(_) => AlertUnknown, _ => AlertUnknown, }, - Error::InvalidSct(e) => match e { - sct::MalformedSct => CertSCTMalformed, - sct::InvalidSignature => CertSCTInvalidSignature, - sct::TimestampInFuture => CertSCTTimestampInFuture, - sct::UnsupportedSctVersion => CertSCTUnsupportedVersion, - sct::UnknownLog => CertSCTUnknownLog, - }, - Error::InvalidCertRevocationList(e) => match e { - CertRevocationListError::BadSignature => CertRevocationListBadSignature, - CertRevocationListError::InvalidCrlNumber => CertRevocationListInvalidCrlNumber, - CertRevocationListError::InvalidRevokedCertSerialNumber => { - CertRevocationListInvalidRevokedCertSerialNumber - } - CertRevocationListError::IssuerInvalidForCrl => CertRevocationListIssuerInvalidForCrl, - CertRevocationListError::Other(_) => CertRevocationListOtherError, - CertRevocationListError::ParseError => CertRevocationListParseError, - CertRevocationListError::UnsupportedCrlVersion => { - CertRevocationListUnsupportedCrlVersion - } - CertRevocationListError::UnsupportedCriticalExtension => { - CertRevocationListUnsupportedCriticalExtension - } - CertRevocationListError::UnsupportedDeltaCrl => CertRevocationListUnsupportedDeltaCrl, - CertRevocationListError::UnsupportedIndirectCrl => { - CertRevocationListUnsupportedIndirectCrl - } - CertRevocationListError::UnsupportedRevocationReason => { - CertRevocationListUnsupportedRevocationReason - } - _ => CertRevocationListOtherError, - }, + Error::InvalidCertRevocationList(e) => map_crl_error(e), _ => General, } } +pub(crate) fn map_crl_error(err: CertRevocationListError) -> rustls_result { + use rustls_result::*; + + match err { + CertRevocationListError::BadSignature => CertRevocationListBadSignature, + CertRevocationListError::InvalidCrlNumber => CertRevocationListInvalidCrlNumber, + CertRevocationListError::InvalidRevokedCertSerialNumber => { + CertRevocationListInvalidRevokedCertSerialNumber + } + CertRevocationListError::IssuerInvalidForCrl => CertRevocationListIssuerInvalidForCrl, + CertRevocationListError::Other(_) => CertRevocationListOtherError, + CertRevocationListError::ParseError => CertRevocationListParseError, + CertRevocationListError::UnsupportedCrlVersion => CertRevocationListUnsupportedCrlVersion, + CertRevocationListError::UnsupportedCriticalExtension => { + CertRevocationListUnsupportedCriticalExtension + } + CertRevocationListError::UnsupportedDeltaCrl => CertRevocationListUnsupportedDeltaCrl, + CertRevocationListError::UnsupportedIndirectCrl => CertRevocationListUnsupportedIndirectCrl, + CertRevocationListError::UnsupportedRevocationReason => { + CertRevocationListUnsupportedRevocationReason + } + _ => CertRevocationListOtherError, + } +} + +pub(crate) fn map_verifier_builder_error(err: VerifierBuilderError) -> rustls_result { + match err { + VerifierBuilderError::NoRootAnchors => { + rustls_result::ClientCertVerifierBuilderNoRootAnchors + } + VerifierBuilderError::InvalidCrl(crl_err) => map_crl_error(crl_err), + _ => rustls_result::General, + } +} + impl Display for rustls_result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use rustls::AlertDescription as alert; use rustls_result::*; - use sct::Error as sct; match self { // These variants are local to this glue layer. @@ -623,12 +615,6 @@ impl Display for rustls_result { AlertNoApplicationProtocol => Error::AlertReceived(alert::NoApplicationProtocol).fmt(f), AlertUnknown => Error::AlertReceived(alert::Unknown(0)).fmt(f), - CertSCTMalformed => Error::InvalidSct(sct::MalformedSct).fmt(f), - CertSCTInvalidSignature => Error::InvalidSct(sct::InvalidSignature).fmt(f), - CertSCTTimestampInFuture => Error::InvalidSct(sct::TimestampInFuture).fmt(f), - CertSCTUnsupportedVersion => Error::InvalidSct(sct::UnsupportedSctVersion).fmt(f), - CertSCTUnknownLog => Error::InvalidSct(sct::UnknownLog).fmt(f), - CertRevocationListBadSignature => { Error::InvalidCertRevocationList(CertRevocationListError::BadSignature).fmt(f) } @@ -669,6 +655,8 @@ impl Display for rustls_result { CertRevocationListError::UnsupportedRevocationReason, ) .fmt(f), + + ClientCertVerifierBuilderNoRootAnchors => write!(f, "no root trust anchors provided"), } } } diff --git a/src/lib.rs b/src/lib.rs index f090e912..b7e43106 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -496,6 +496,22 @@ where } } +/// Converts a [`Castable`]'s underlying [`Castable::RustType`] to a const pointer +/// to an `Arc` over the rust type and sets the `dst` out pointer to the resulting const `Arc` +/// pointer. See [`to_arc_const_ptr`] for more information. +/// +/// ## Unsafety: +/// +/// `dst` must not be `NULL`. +pub(crate) fn set_arc_mut_ptr(dst: *mut *const C, src: C::RustType) +where + C: Castable, +{ + unsafe { + *dst = to_arc_const_ptr(src); + } +} + /// Converts a mutable pointer to a [`Castable`] to an optional ref to the underlying /// [`Castable::RustType`]. See [`cast_mut_ptr`] for more information. /// @@ -511,12 +527,6 @@ where /// If the provided pointer to a [`Castable`] is non-null, convert it to a mutable reference using /// [`try_from_mut`]. Otherwise, return [`rustls_result::NullParameter`], or an appropriate default /// (`false`, `0`, `NULL`) based on the context. See [`try_from_mut`] for more information. -/// -/// ## Example: -/// -/// ```rust,ignore -/// let config: &mut ClientConfig = try_mut_from_ptr!(builder); -/// ``` macro_rules! try_mut_from_ptr { ( $var:ident ) => { match $crate::try_from_mut($var) { @@ -546,12 +556,6 @@ where /// (`false`, `0`, `NULL`) based on the context; /// /// See [`try_from`] for more information. -/// -/// ## Example: -/// -/// ```rust, ignore -/// let config: &ClientConfig = try_ref_from_ptr!(builder); -/// ``` macro_rules! try_ref_from_ptr { ( $var:ident ) => { match $crate::try_from($var) { diff --git a/src/rustls.h b/src/rustls.h index 65e3ee3c..aa870fbc 100644 --- a/src/rustls.h +++ b/src/rustls.h @@ -105,11 +105,6 @@ enum rustls_result { RUSTLS_RESULT_ALERT_CERTIFICATE_REQUIRED = 7232, RUSTLS_RESULT_ALERT_NO_APPLICATION_PROTOCOL = 7233, RUSTLS_RESULT_ALERT_UNKNOWN = 7234, - RUSTLS_RESULT_CERT_SCT_MALFORMED = 7319, - RUSTLS_RESULT_CERT_SCT_INVALID_SIGNATURE = 7320, - RUSTLS_RESULT_CERT_SCT_TIMESTAMP_IN_FUTURE = 7321, - RUSTLS_RESULT_CERT_SCT_UNSUPPORTED_VERSION = 7322, - RUSTLS_RESULT_CERT_SCT_UNKNOWN_LOG = 7323, RUSTLS_RESULT_CERT_REVOCATION_LIST_BAD_SIGNATURE = 7400, RUSTLS_RESULT_CERT_REVOCATION_LIST_INVALID_CRL_NUMBER = 7401, RUSTLS_RESULT_CERT_REVOCATION_LIST_INVALID_REVOKED_CERT_SERIAL_NUMBER = 7402, @@ -121,6 +116,7 @@ enum rustls_result { RUSTLS_RESULT_CERT_REVOCATION_LIST_UNSUPPORTED_DELTA_CRL = 7408, RUSTLS_RESULT_CERT_REVOCATION_LIST_UNSUPPORTED_INDIRECT_CRL = 7409, RUSTLS_RESULT_CERT_REVOCATION_LIST_UNSUPPORTED_REVOCATION_REASON = 7410, + RUSTLS_RESULT_CLIENT_CERT_VERIFIER_BUILDER_NO_ROOT_ANCHORS = 7500, }; typedef uint32_t rustls_result; @@ -170,43 +166,10 @@ typedef struct rustls_accepted rustls_accepted; */ typedef struct rustls_acceptor rustls_acceptor; -/** - * A builder for a `rustls_allow_any_anonymous_or_authenticated_client_verifier`. This builder - * object can be used to configure certificate revocation lists, and then turned into a - * `rustls_allow_any_anonymous_or_authenticated_client_verifier` once ready. - */ -typedef struct rustls_allow_any_anonymous_or_authenticated_client_builder rustls_allow_any_anonymous_or_authenticated_client_builder; - -/** - * Alternative to `rustls_allow_any_authenticated_client_verifier` that allows connections - * with or without a client certificate. If the client offers a certificate, - * it will be verified (and rejected if it is not valid). If the client - * does not offer a certificate, the connection will succeed. - * - * The application can retrieve the certificate, if any, with - * `rustls_connection_get_peer_certificate`. - */ -typedef struct rustls_allow_any_anonymous_or_authenticated_client_verifier rustls_allow_any_anonymous_or_authenticated_client_verifier; - -/** - * A builder for a `rustls_allow_any_authenticated_client_verifier`. This builder object can be - * used to configure certificate revocation lists, and then turned into a - * `rustls_allow_any_authenticated_client_verifier` once ready. - */ -typedef struct rustls_allow_any_authenticated_client_builder rustls_allow_any_authenticated_client_builder; - -/** - * A verifier of client certificates that requires all certificates to be - * trusted based on a given `rustls_root_cert_store`. Usable in building server - * configurations. Connections without such a client certificate will not - * be accepted. - */ -typedef struct rustls_allow_any_authenticated_client_verifier rustls_allow_any_authenticated_client_verifier; - /** * An X.509 certificate, as used in rustls. - * Corresponds to `Certificate` in the Rust API. - * + * Corresponds to `CertificateDer` in the Rust pki-types API. + * */ typedef struct rustls_certificate rustls_certificate; @@ -218,6 +181,12 @@ typedef struct rustls_certificate rustls_certificate; */ typedef struct rustls_certified_key rustls_certified_key; +/** + * A built client certificate verifier that can be provided to a `rustls_server_config_builder` + * with `rustls_server_config_builder_set_client_verifier`. + */ +typedef struct rustls_client_cert_verifier 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`. @@ -252,6 +221,22 @@ typedef struct rustls_iovec rustls_iovec; */ typedef struct rustls_root_cert_store rustls_root_cert_store; +/** + * A `rustls_root_cert_store` being constructed. + * + * A builder can be modified by adding trust anchor root certificates with + * `rustls_root_cert_store_builder_add_pem`. Once you're done adding root certificates, + * call `rustls_root_cert_store_builder_build` to turn it into a `rustls_root_cert_store`. + * This object is not safe for concurrent mutation. + */ +typedef struct rustls_root_cert_store_builder rustls_root_cert_store_builder; + +/** + * A built server certificate verifier that can be provided to a `rustls_client_config_builder` + * with `rustls_client_config_builder_set_server_verifier`. + */ +typedef struct rustls_server_cert_verifier rustls_server_cert_verifier; + /** * A server config that is done being constructed and is now read-only. * Under the hood, this object corresponds to an `Arc`. @@ -310,6 +295,24 @@ typedef struct rustls_slice_str rustls_slice_str; */ typedef struct rustls_supported_ciphersuite rustls_supported_ciphersuite; +/** + * A client certificate verifier being constructed. A builder can be modified by, + * e.g. `rustls_web_pki_client_cert_verifier_builder_add_crl`. Once you're + * done configuring settings, call `rustls_web_pki_client_cert_verifier_builder_build` + * to turn it into a `rustls_client_cert_verifier`. This object is not safe + * for concurrent mutation. + */ +typedef struct rustls_web_pki_client_cert_verifier_builder rustls_web_pki_client_cert_verifier_builder; + +/** + * A server certificate verifier being constructed. A builder can be modified by, + * e.g. `rustls_web_pki_server_cert_verifier_builder_add_crl`. Once you're + * done configuring settings, call `rustls_web_pki_server_cert_verifier_builder_build` + * to turn it into a `rustls_server_cert_verifier`. This object is not safe + * for concurrent mutation. + */ +typedef struct rustls_web_pki_server_cert_verifier_builder rustls_web_pki_server_cert_verifier_builder; + /** * A read-only view on a Rust `&str`. The contents are guaranteed to be valid * UTF-8. As an additional guarantee on top of Rust's normal UTF-8 guarantee, @@ -827,14 +830,6 @@ rustls_result rustls_accepted_into_connection(struct rustls_accepted *accepted, */ void rustls_accepted_free(struct rustls_accepted *accepted); -/** - * Get the DER data of the certificate itself. - * The data is owned by the certificate and has the same lifetime. - */ -rustls_result rustls_certificate_get_der(const struct rustls_certificate *cert, - const uint8_t **out_der_data, - size_t *out_der_len); - /** * Return a 16-bit unsigned integer corresponding to this cipher suite's assignment from * . @@ -942,15 +937,35 @@ rustls_result rustls_certified_key_clone_with_ocsp(const struct rustls_certified 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. - * + * Create a `rustls_root_cert_store_builder`. + * + * Caller owns the memory and may free it with `rustls_root_cert_store_free`, regardless of + * whether `rustls_root_cert_store_builder_build` was called. + * + * If you wish to abandon the builder without calling `rustls_root_cert_store_builder_build`, + * it must be freed with `rustls_root_cert_store_builder_free`. + */ +struct rustls_root_cert_store_builder *rustls_root_cert_store_builder_new(void); + +/** + * Add one or more certificates to the root cert store builder using PEM + * encoded data. + * + * When `strict` is true an error will return a `CertificateParseError` + * result. So will an attempt to parse data that has zero certificates. + * + * When `strict` is false, unparseable root certificates will be ignored. + * This may be useful on systems that have syntactically invalid root + * certificates. */ -struct rustls_root_cert_store *rustls_root_cert_store_new(void); +rustls_result rustls_root_cert_store_builder_add_pem(struct rustls_root_cert_store_builder *builder, + const uint8_t *pem, + size_t pem_len, + bool strict); /** - * Add one or more certificates to the root cert store using PEM encoded data. + * Add one or more certificates to the root cert store builder using PEM + * encoded data read from the named file. * * When `strict` is true an error will return a `CertificateParseError` * result. So will an attempt to parse data that has zero certificates. @@ -959,129 +974,223 @@ struct rustls_root_cert_store *rustls_root_cert_store_new(void); * This may be useful on systems that have syntactically invalid root * certificates. */ -rustls_result rustls_root_cert_store_add_pem(struct rustls_root_cert_store *store, - const uint8_t *pem, - size_t pem_len, - bool strict); +rustls_result rustls_client_config_builder_load_roots_from_file(struct rustls_root_cert_store_builder *builder, + const char *filename, + bool strict); + +/** + * Create a new `rustls_root_cert_store` from the builder. + * + * The builder is consumed and cannot be used again, but must still be freed. + * + * The root cert store can be used in several `rustls_web_pki_client_cert_verifier_builder_new` + * instances and must be freed by the application when no longer needed. See the documentation of + * `rustls_root_cert_store_free` for details about lifetime. + */ +rustls_result rustls_root_cert_store_builder_build(struct rustls_root_cert_store_builder *builder, + const struct rustls_root_cert_store **root_cert_store_out); + +/** + * Free a `rustls_root_cert_store_builder` previously returned from + * `rustls_root_cert_store_builder_new`. Calling with NULL is fine. Must not be + * called twice with the same value. + */ +void rustls_root_cert_store_builder_free(struct rustls_root_cert_store_builder *builder); /** * Free a rustls_root_cert_store previously returned from rustls_root_cert_store_builder_build. * Calling with NULL is fine. Must not be called twice with the same value. */ -void rustls_root_cert_store_free(struct rustls_root_cert_store *store); +void rustls_root_cert_store_free(const struct rustls_root_cert_store *store); /** - * Create a new allow any authenticated client certificate verifier builder using the root store. + * Free a `rustls_client_cert_verifier` previously returned from + * `rustls_client_cert_verifier_builder_build`. Calling with NULL is fine. Must not be + * called twice with the same value. + */ +void rustls_client_cert_verifier_free(struct rustls_client_cert_verifier *verifier); + +/** + * Create a `rustls_web_pki_client_cert_verifier_builder`. Caller owns the memory and may + * eventually call `rustls_web_pki_client_cert_verifier_builder_free` to free it, whether or + * not `rustls_web_pki_client_cert_verifier_builder_build` was called. + * + * Without further modification the builder will produce a client certificate verifier that + * will require a client present a client certificate that chains to one of the trust anchors + * in the provided `rustls_root_cert_store`. The root cert store must not be empty. * - * This copies the contents of the rustls_root_cert_store. It does not take - * ownership of the pointed-to memory. + * Revocation checking will not be performed unless + * `rustls_web_pki_client_cert_verifier_builder_add_crl` is used to add certificate revocation + * lists (CRLs) to the builder. If CRLs are added, revocation checking will be performed + * for the entire certificate chain unless + * `rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation` is used. Unknown + * revocation status for certificates considered for revocation status will be treated as + * an error unless `rustls_web_pki_client_cert_verifier_allow_unknown_revocation_status` is + * used. * - * This object can then be used to load any CRLs. + * Unauthenticated clients will not be permitted unless + * `rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated` is used. * - * Once that is complete, convert it into a real `rustls_allow_any_authenticated_client_verifier` - * by calling `rustls_allow_any_authenticated_client_verifier_new()`. + * This copies the contents of the `rustls_root_cert_store`. It does not take + * ownership of the pointed-to data. */ -struct rustls_allow_any_authenticated_client_builder *rustls_allow_any_authenticated_client_builder_new(const struct rustls_root_cert_store *store); +struct rustls_web_pki_client_cert_verifier_builder *rustls_web_pki_client_cert_verifier_builder_new(const struct rustls_root_cert_store *store); /** - * Add one or more certificate revocation lists (CRLs) to the client certificate verifier by - * reading the CRL content from the provided buffer of PEM encoded content. + * Add one or more certificate revocation lists (CRLs) to the client certificate verifier + * builder by reading the CRL content from the provided buffer of PEM encoded content. + * + * By default revocation checking will be performed on the entire certificate chain. To only + * check the revocation status of the end entity certificate, use + * `rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation`. * - * This function returns an error if the provided buffer is not valid PEM encoded content, - * or if the CRL content is invalid or unsupported. + * This function returns an error if the provided buffer is not valid PEM encoded content. */ -rustls_result rustls_allow_any_authenticated_client_builder_add_crl(struct rustls_allow_any_authenticated_client_builder *builder, - const uint8_t *crl_pem, - size_t crl_pem_len); +rustls_result rustls_web_pki_client_cert_verifier_builder_add_crl(struct rustls_web_pki_client_cert_verifier_builder *builder, + const uint8_t *crl_pem, + size_t crl_pem_len); /** - * Free a `rustls_allow_any_authenticated_client_builder` previously returned from - * `rustls_allow_any_authenticated_client_builder_new`. - * Calling with NULL is fine. Must not be called twice with the same value. + * When CRLs are provided with `rustls_web_pki_client_cert_verifier_builder_add_crl`, only + * check the revocation status of end entity certificates, ignoring any intermediate certificates + * in the chain. + */ +rustls_result rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation(struct rustls_web_pki_client_cert_verifier_builder *builder); + +/** + * When CRLs are provided with `rustls_web_pki_client_cert_verifier_builder_add_crl`, and it + * isn't possible to determine the revocation status of a considered certificate, do not treat + * it as an error condition. + * + * Overrides the default behavior where unknown revocation status is considered an error. + */ +rustls_result rustls_web_pki_client_cert_verifier_allow_unknown_revocation_status(struct rustls_web_pki_client_cert_verifier_builder *builder); + +/** + * Allow unauthenticated anonymous clients in addition to those that present a client + * certificate that chains to one of the verifier's configured trust anchors. */ -void rustls_allow_any_authenticated_client_builder_free(struct rustls_allow_any_authenticated_client_builder *builder); +rustls_result rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated(struct rustls_web_pki_client_cert_verifier_builder *builder); /** - * Create a new allow any authenticated client certificate verifier from a builder. + * Clear the list of trust anchor hint subjects. + * + * By default, the client cert verifier will use the subjects provided by the root cert + * store configured for client authentication. Calling this function will remove these + * hint subjects, indicating the client should make a free choice of which certificate + * to send. + */ +rustls_result rustls_web_pki_client_cert_verifier_clear_root_hint_subjects(struct rustls_web_pki_client_cert_verifier_builder *builder); + +/** + * Add additional distinguished names to the list of trust anchor hint subjects. + * + * By default, the client cert verifier will use the subjects provided by the root cert + * store configured for client authentication. Calling this function will add to these + * existing hint subjects. Calling this function with an empty `store` will have no + * effect, use `rustls_web_pki_client_cert_verifier_clear_root_hint_subjects` to clear + * the subject hints. + */ +rustls_result rustls_web_pki_client_cert_verifier_add_root_hint_subjects(struct rustls_web_pki_client_cert_verifier_builder *builder, + const struct rustls_root_cert_store *store); + +/** + * Create a new client certificate verifier from the builder. * * The builder is consumed and cannot be used again, but must still be freed. * - * The verifier can be used in several `rustls_server_config` instances. Must be freed by - * the application when no longer needed. See the documentation of - * `rustls_allow_any_authenticated_client_verifier_free` for details about lifetime. - * This copies the contents of the `rustls_root_cert_store`. It does not take - * ownership of the pointed-to memory. + * The verifier can be used in several `rustls_server_config` instances and must be + * freed by the application when no longer needed. See the documentation of + * `rustls_web_pki_client_cert_verifier_builder_free` for details about lifetime. */ -const struct rustls_allow_any_authenticated_client_verifier *rustls_allow_any_authenticated_client_verifier_new(struct rustls_allow_any_authenticated_client_builder *builder); +rustls_result rustls_web_pki_client_cert_verifier_builder_build(struct rustls_web_pki_client_cert_verifier_builder *builder, + struct rustls_client_cert_verifier **verifier_out); /** - * "Free" a verifier previously returned from - * `rustls_allow_any_authenticated_client_verifier_new`. Since - * `rustls_allow_any_authenticated_client_verifier` is actually an - * atomically reference-counted pointer, extant server_configs may still - * hold an internal reference to the Rust object. However, C code must - * consider this pointer unusable after "free"ing it. - * Calling with NULL is fine. Must not be called twice with the same value. + * Free a `rustls_client_cert_verifier_builder` previously returned from + * `rustls_client_cert_verifier_builder_new`. Calling with NULL is fine. Must not be + * called twice with the same value. */ -void rustls_allow_any_authenticated_client_verifier_free(const struct rustls_allow_any_authenticated_client_verifier *verifier); +void rustls_web_pki_client_cert_verifier_builder_free(struct rustls_web_pki_client_cert_verifier_builder *builder); /** - * Create a new allow any anonymous or authenticated client certificate verifier builder - * using the root store. + * Create a `rustls_web_pki_server_cert_verifier_builder`. Caller owns the memory and may + * free it with `rustls_web_pki_server_cert_verifier_builder_free`, regardless of whether + * `rustls_web_pki_server_cert_verifier_builder_build` was called. * - * This copies the contents of the rustls_root_cert_store. It does not take - * ownership of the pointed-to memory. + * Without further modification the builder will produce a server certificate verifier that + * will require a server present a certificate that chains to one of the trust anchors + * in the provided `rustls_root_cert_store`. The root cert store must not be empty. * - * This object can then be used to load any CRLs. + * Revocation checking will not be performed unless + * `rustls_web_pki_server_cert_verifier_builder_add_crl` is used to add certificate revocation + * lists (CRLs) to the builder. If CRLs are added, revocation checking will be performed + * for the entire certificate chain unless + * `rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation` is used. Unknown + * revocation status for certificates considered for revocation status will be treated as + * an error unless `rustls_web_pki_server_cert_verifier_allow_unknown_revocation_status` is + * used. * - * Once that is complete, convert it into a real - * `rustls_allow_any_anonymous_or_authenticated_client_verifier` - * by calling `rustls_allow_any_anonymous_or_authenticated_client_verifier_new()`. + * This copies the contents of the `rustls_root_cert_store`. It does not take + * ownership of the pointed-to data. */ -struct rustls_allow_any_anonymous_or_authenticated_client_builder *rustls_client_cert_verifier_optional_builder_new(const struct rustls_root_cert_store *store); +struct rustls_web_pki_server_cert_verifier_builder *rustls_web_pki_server_cert_verifier_builder_new(const struct rustls_root_cert_store *store); /** - * Add one or more certificate revocation lists (CRLs) to the client certificate verifier by - * reading the CRL content from the provided buffer of PEM encoded content. + * Add one or more certificate revocation lists (CRLs) to the server certificate verifier + * builder by reading the CRL content from the provided buffer of PEM encoded content. + * + * By default revocation checking will be performed on the entire certificate chain. To only + * check the revocation status of the end entity certificate, use + * `rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation`. * - * This function returns an error if the provided buffer is not valid PEM encoded content, - * or if the CRL content is invalid or unsupported. + * This function returns an error if the provided buffer is not valid PEM encoded content. */ -rustls_result rustls_client_cert_verifier_optional_builder_add_crl(struct rustls_allow_any_anonymous_or_authenticated_client_builder *builder, - const uint8_t *crl_pem, - size_t crl_pem_len); +rustls_result rustls_web_pki_server_cert_verifier_builder_add_crl(struct rustls_web_pki_server_cert_verifier_builder *builder, + const uint8_t *crl_pem, + size_t crl_pem_len); /** - * Free a `rustls_allow_any_anonymous_or_authenticated_client_builder` previously returned from - * `rustls_client_cert_verifier_optional_builder_new`. - * Calling with NULL is fine. Must not be called twice with the same value. + * When CRLs are provided with `rustls_web_pki_server_cert_verifier_builder_add_crl`, only + * check the revocation status of end entity certificates, ignoring any intermediate certificates + * in the chain. + */ +rustls_result rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation(struct rustls_web_pki_server_cert_verifier_builder *builder); + +/** + * When CRLs are provided with `rustls_web_pki_server_cert_verifier_builder_add_crl`, and it + * isn't possible to determine the revocation status of a considered certificate, do not treat + * it as an error condition. + * + * Overrides the default behavior where unknown revocation status is considered an error. */ -void rustls_client_cert_verifier_optional_builder_free(struct rustls_allow_any_anonymous_or_authenticated_client_builder *builder); +rustls_result rustls_web_pki_server_cert_verifier_allow_unknown_revocation_status(struct rustls_web_pki_server_cert_verifier_builder *builder); /** - * Create a new allow any anonymous or authenticated client certificate verifier builder - * from the builder. + * Create a new server certificate verifier from the builder. * * The builder is consumed and cannot be used again, but must still be freed. * - * The verifier can be used in several `rustls_server_config` instances. Must be + * The verifier can be used in several `rustls_client_config` instances and must be * freed by the application when no longer needed. See the documentation of - * `rustls_allow_any_anonymous_or_authenticated_client_verifier_free` for details about lifetime. - * This copies the contents of the `rustls_root_cert_store`. It does not take - * ownership of the pointed-to data. + * `rustls_web_pki_server_cert_verifier_builder_free` for details about lifetime. */ -const struct rustls_allow_any_anonymous_or_authenticated_client_verifier *rustls_allow_any_anonymous_or_authenticated_client_verifier_new(struct rustls_allow_any_anonymous_or_authenticated_client_builder *builder); +rustls_result rustls_web_pki_server_cert_verifier_builder_build(struct rustls_web_pki_server_cert_verifier_builder *builder, + struct rustls_server_cert_verifier **verifier_out); /** - * "Free" a verifier previously returned from - * `rustls_allow_any_anonymous_or_authenticated_client_verifier_new`. Since - * `rustls_allow_any_anonymous_or_authenticated_client_verifier` - * is actually an atomically reference-counted pointer, extant `server_configs` may still - * hold an internal reference to the Rust object. However, C code must - * consider this pointer unusable after "free"ing it. - * Calling with NULL is fine. Must not be called twice with the same value. + * Free a `rustls_server_cert_verifier_builder` previously returned from + * `rustls_server_cert_verifier_builder_new`. Calling with NULL is fine. Must not be + * called twice with the same value. + */ +void rustls_web_pki_server_cert_verifier_builder_free(struct rustls_web_pki_server_cert_verifier_builder *builder); + +/** + * Free a `rustls_server_cert_verifier` previously returned from + * `rustls_server_cert_verifier_builder_build`. Calling with NULL is fine. Must not be + * called twice with the same value. */ -void rustls_allow_any_anonymous_or_authenticated_client_verifier_free(const struct rustls_allow_any_anonymous_or_authenticated_client_verifier *verifier); +void rustls_server_cert_verifier_free(struct rustls_server_cert_verifier *verifier); /** * Create a rustls_client_config_builder. Caller owns the memory and must @@ -1149,22 +1258,12 @@ rustls_result rustls_client_config_builder_dangerous_set_certificate_verifier(st rustls_verify_server_cert_callback callback); /** - * Use the trusted root certificates from the provided store. + * Configure the server certificate verifier. * - * This replaces any trusted roots already configured with copies - * from `roots`. This adds 1 to the refcount for `roots`. When you - * call rustls_client_config_free or rustls_client_config_builder_free, - * those will subtract 1 from the refcount for `roots`. + * This increases the reference count of `verifier` and does not take ownership. */ -rustls_result rustls_client_config_builder_use_roots(struct rustls_client_config_builder *config_builder, - const struct rustls_root_cert_store *roots); - -/** - * Add trusted root certificates from the named file, which should contain - * PEM-formatted certificates. - */ -rustls_result rustls_client_config_builder_load_roots_from_file(struct rustls_client_config_builder *config_builder, - const char *filename); +void rustls_client_config_builder_set_server_verifier(struct rustls_client_config_builder *builder, + const struct rustls_server_cert_verifier *verifier); /** * Set the ALPN protocol list to the given protocols. `protocols` must point @@ -1533,22 +1632,11 @@ rustls_result rustls_server_config_builder_new_custom(const struct rustls_suppor struct rustls_server_config_builder **builder_out); /** - * Create a rustls_server_config_builder for TLS sessions that require - * valid client certificates. The passed rustls_client_cert_verifier may - * be used in several builders. - * For memory lifetime, see rustls_server_config_builder_new. + * Create a rustls_server_config_builder for TLS sessions that may verify client + * certificates. This increases the refcount of `verifier` and doesn't take ownership. */ void rustls_server_config_builder_set_client_verifier(struct rustls_server_config_builder *builder, - const struct rustls_allow_any_authenticated_client_verifier *verifier); - -/** - * Create a rustls_server_config_builder for TLS sessions that accept - * valid client certificates, but do not require them. The passed - * rustls_client_cert_verifier_optional may be used in several builders. - * For memory lifetime, see rustls_server_config_builder_new. - */ -void rustls_server_config_builder_set_client_verifier_optional(struct rustls_server_config_builder *builder, - const struct rustls_allow_any_anonymous_or_authenticated_client_verifier *verifier); + const struct rustls_client_cert_verifier *verifier); /** * "Free" a server_config_builder without building it into a rustls_server_config. diff --git a/src/server.rs b/src/server.rs index 2234af10..cd2d5507 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,24 +1,22 @@ use std::convert::TryInto; use std::ffi::c_void; +use std::fmt::{Debug, Formatter}; use std::ptr::null; use std::slice; use std::sync::Arc; use libc::size_t; +use rustls::crypto::ring::ALL_CIPHER_SUITES; +use rustls::server::danger::ClientCertVerifier; use rustls::server::{ - AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, ClientCertVerifier, - ClientHello, NoClientAuth, ResolvesServerCert, ServerConfig, ServerConnection, - StoresServerSessions, + ClientHello, ResolvesServerCert, ServerConfig, ServerConnection, StoresServerSessions, + WebPkiClientVerifier, }; use rustls::sign::CertifiedKey; -use rustls::{ - ProtocolVersion, SignatureScheme, SupportedCipherSuite, WantsVerifier, ALL_CIPHER_SUITES, -}; +use rustls::{ProtocolVersion, SignatureScheme, SupportedCipherSuite, WantsVerifier}; use crate::cipher::{ - rustls_allow_any_anonymous_or_authenticated_client_verifier, - rustls_allow_any_authenticated_client_verifier, rustls_certified_key, - rustls_supported_ciphersuite, + rustls_certified_key, rustls_client_cert_verifier, rustls_supported_ciphersuite, }; use crate::connection::{rustls_connection, Connection}; use crate::error::rustls_result::{InvalidParameter, NullParameter}; @@ -86,7 +84,7 @@ impl rustls_server_config_builder { ffi_panic_boundary! { let builder = ServerConfigBuilder { base: rustls::ServerConfig::builder().with_safe_defaults(), - verifier: NoClientAuth::boxed(), + verifier: WebPkiClientVerifier::no_client_auth(), cert_resolver: None, session_storage: None, alpn_protocols: vec![], @@ -148,7 +146,7 @@ impl rustls_server_config_builder { let builder = ServerConfigBuilder { base, - verifier: NoClientAuth::boxed(), + verifier: WebPkiClientVerifier::no_client_auth(), cert_resolver: None, session_storage: None, alpn_protocols: vec![], @@ -159,36 +157,17 @@ impl rustls_server_config_builder { } } - /// Create a rustls_server_config_builder for TLS sessions that require - /// valid client certificates. The passed rustls_client_cert_verifier may - /// be used in several builders. - /// For memory lifetime, see rustls_server_config_builder_new. + /// Create a rustls_server_config_builder for TLS sessions that may verify client + /// certificates. This increases the refcount of `verifier` and doesn't take ownership. #[no_mangle] pub extern "C" fn rustls_server_config_builder_set_client_verifier( builder: *mut rustls_server_config_builder, - verifier: *const rustls_allow_any_authenticated_client_verifier, - ) { - ffi_panic_boundary! { - let builder: &mut ServerConfigBuilder = try_mut_from_ptr!(builder); - let verifier: Arc = try_clone_arc!(verifier); - builder.verifier = verifier; - } - } - - /// Create a rustls_server_config_builder for TLS sessions that accept - /// valid client certificates, but do not require them. The passed - /// rustls_client_cert_verifier_optional may be used in several builders. - /// For memory lifetime, see rustls_server_config_builder_new. - #[no_mangle] - pub extern "C" fn rustls_server_config_builder_set_client_verifier_optional( - builder: *mut rustls_server_config_builder, - verifier: *const rustls_allow_any_anonymous_or_authenticated_client_verifier, + verifier: *const rustls_client_cert_verifier, ) { ffi_panic_boundary! { let builder: &mut ServerConfigBuilder = try_mut_from_ptr!(builder); - let verifier: Arc = try_clone_arc!(verifier); - - builder.verifier = verifier; + let verifier = try_ref_from_ptr!(verifier); + builder.verifier = verifier.clone(); } } @@ -402,6 +381,7 @@ pub extern "C" fn rustls_server_connection_get_server_name( /// Choose the server certificate to be used for a connection based on certificate /// type. Will pick the first CertfiedKey available that is suitable for /// the SignatureSchemes supported by the client. +#[derive(Debug)] struct ResolvesServerCertFromChoices { choices: Vec>, } @@ -550,6 +530,12 @@ impl ResolvesServerCert for ClientHelloResolver { unsafe impl Sync for ClientHelloResolver {} unsafe impl Send for ClientHelloResolver {} +impl Debug for ClientHelloResolver { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ClientHelloResolver").finish() + } +} + impl rustls_server_config_builder { /// Register a callback to be invoked when a connection created from this config /// sees a TLS ClientHello message. If `userdata` has been set with diff --git a/src/session.rs b/src/session.rs index 6f2594da..d8af719f 100644 --- a/src/session.rs +++ b/src/session.rs @@ -2,6 +2,7 @@ use crate::error::rustls_result; use crate::rslice::rustls_slice_bytes; use crate::userdata_get; use libc::{c_int, c_void, size_t}; +use std::fmt::{Debug, Formatter}; /// Any context information the callback will receive when invoked. pub type rustls_session_store_userdata = *mut c_void; @@ -148,6 +149,12 @@ impl rustls::server::StoresServerSessions for SessionStoreBroker { } } +impl Debug for SessionStoreBroker { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SessionStoreBroker").finish() + } +} + /// This struct can be considered thread safe, as long /// as the registered callbacks are thread safe. This is /// documented as a requirement in the API. diff --git a/tests/client.c b/tests/client.c index a406595e..5f29a3e8 100644 --- a/tests/client.c +++ b/tests/client.c @@ -413,7 +413,12 @@ main(int argc, const char **argv) struct rustls_client_config_builder *config_builder = rustls_client_config_builder_new(); + struct rustls_root_cert_store_builder *server_cert_root_store_builder = NULL; + const struct rustls_root_cert_store *server_cert_root_store = NULL; const struct rustls_client_config *client_config = NULL; + struct rustls_web_pki_server_cert_verifier_builder + *server_cert_verifier_builder = NULL; + struct rustls_server_cert_verifier *server_cert_verifier = NULL; struct rustls_slice_bytes alpn_http11; const struct rustls_certified_key *certified_key = NULL; @@ -427,12 +432,29 @@ main(int argc, const char **argv) #endif if(getenv("CA_FILE")) { + server_cert_root_store_builder = rustls_root_cert_store_builder_new(); result = rustls_client_config_builder_load_roots_from_file( - config_builder, getenv("CA_FILE")); + server_cert_root_store_builder, getenv("CA_FILE"), true); if(result != RUSTLS_RESULT_OK) { print_error("loading trusted certificates", result); goto cleanup; } + result = rustls_root_cert_store_builder_build( + server_cert_root_store_builder, &server_cert_root_store); + if(result != RUSTLS_RESULT_OK) { + goto cleanup; + } + + server_cert_verifier_builder = + rustls_web_pki_server_cert_verifier_builder_new(server_cert_root_store); + + result = rustls_web_pki_server_cert_verifier_builder_build( + server_cert_verifier_builder, &server_cert_verifier); + if(result != RUSTLS_RESULT_OK) { + goto cleanup; + } + rustls_client_config_builder_set_server_verifier(config_builder, + server_cert_verifier); } else if(getenv("NO_CHECK_CERTIFICATE")) { rustls_client_config_builder_dangerous_set_certificate_verifier( @@ -479,6 +501,11 @@ main(int argc, const char **argv) ret = 0; cleanup: + rustls_root_cert_store_builder_free(server_cert_root_store_builder); + rustls_root_cert_store_free(server_cert_root_store); + rustls_web_pki_server_cert_verifier_builder_free( + server_cert_verifier_builder); + rustls_server_cert_verifier_free(server_cert_verifier); rustls_certified_key_free(certified_key); rustls_client_config_free(client_config); diff --git a/tests/server.c b/tests/server.c index 86cb354a..013c624a 100644 --- a/tests/server.c +++ b/tests/server.c @@ -238,11 +238,11 @@ main(int argc, const char **argv) struct rustls_connection *rconn = NULL; const struct rustls_certified_key *certified_key = NULL; struct rustls_slice_bytes alpn_http11; - struct rustls_root_cert_store *client_cert_root_store = NULL; - struct rustls_allow_any_authenticated_client_builder + struct rustls_root_cert_store_builder *client_cert_root_store_builder = NULL; + const struct rustls_root_cert_store *client_cert_root_store = NULL; + struct rustls_web_pki_client_cert_verifier_builder *client_cert_verifier_builder = NULL; - const struct rustls_allow_any_authenticated_client_verifier - *client_cert_verifier = NULL; + struct rustls_client_cert_verifier *client_cert_verifier = NULL; /* Set this global variable for logging purposes. */ programname = "server"; @@ -288,15 +288,19 @@ main(int argc, const char **argv) goto cleanup; } - client_cert_root_store = rustls_root_cert_store_new(); - result = rustls_root_cert_store_add_pem( - client_cert_root_store, (uint8_t *)certbuf, certbuf_len, true); + client_cert_root_store_builder = rustls_root_cert_store_builder_new(); + result = rustls_root_cert_store_builder_add_pem( + client_cert_root_store_builder, (uint8_t *)certbuf, certbuf_len, true); + if(result != RUSTLS_RESULT_OK) { + goto cleanup; + } + result = rustls_root_cert_store_builder_build( + client_cert_root_store_builder, &client_cert_root_store); if(result != RUSTLS_RESULT_OK) { goto cleanup; } client_cert_verifier_builder = - rustls_allow_any_authenticated_client_builder_new( - client_cert_root_store); + rustls_web_pki_client_cert_verifier_builder_new(client_cert_root_store); char crlbuf[10000]; size_t crlbuf_len; @@ -306,15 +310,18 @@ main(int argc, const char **argv) goto cleanup; } - result = rustls_allow_any_authenticated_client_builder_add_crl( + result = rustls_web_pki_client_cert_verifier_builder_add_crl( client_cert_verifier_builder, (uint8_t *)crlbuf, certbuf_len); if(result != RUSTLS_RESULT_OK) { goto cleanup; } } - client_cert_verifier = rustls_allow_any_authenticated_client_verifier_new( - client_cert_verifier_builder); + result = rustls_web_pki_client_cert_verifier_builder_build( + client_cert_verifier_builder, &client_cert_verifier); + if(result != RUSTLS_RESULT_OK) { + goto cleanup; + } rustls_server_config_builder_set_client_verifier(config_builder, client_cert_verifier); } @@ -396,10 +403,11 @@ main(int argc, const char **argv) cleanup: rustls_certified_key_free(certified_key); + rustls_root_cert_store_builder_free(client_cert_root_store_builder); rustls_root_cert_store_free(client_cert_root_store); - rustls_allow_any_authenticated_client_builder_free( + rustls_web_pki_client_cert_verifier_builder_free( client_cert_verifier_builder); - rustls_allow_any_authenticated_client_verifier_free(client_cert_verifier); + rustls_client_cert_verifier_free(client_cert_verifier); rustls_server_config_free(server_config); rustls_connection_free(rconn); if(sockfd > 0) {