From 48d9e819aebb71bf1ecc3040a08cea1791515b6a Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 6 Oct 2023 14:10:25 -0400 Subject: [PATCH] rework client cert verifiers w/ builder API. This commit reworks the rustls-ffi API for client certificate validation to track the new builder based API that landed in Rustls rustls/rustls#1368 --- src/cipher.rs | 344 +++++++++++++++++-------------------------------- src/error.rs | 68 ++++++---- src/lib.rs | 6 + src/rustls.h | 188 ++++++++------------------- src/server.rs | 41 ++---- tests/server.c | 21 +-- 6 files changed, 237 insertions(+), 431 deletions(-) diff --git a/src/cipher.rs b/src/cipher.rs index 8ed07362..f09bbab0 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -7,16 +7,17 @@ use std::sync::Arc; use pki_types::{CertificateDer, CertificateRevocationListDer, PrivateKeyDer}; use rustls::crypto::ring::{ALL_CIPHER_SUITES, DEFAULT_CIPHER_SUITES}; -use rustls::server::{AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient}; +use rustls::server::danger::ClientCertVerifier; +use rustls::server::WebPkiClientVerifier; use rustls::sign::CertifiedKey; use rustls::{RootCertStore, SupportedCipherSuite}; use rustls_pemfile::{certs, crls, pkcs8_private_keys, rsa_private_keys}; -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, try_box_from_ptr, try_mut_from_ptr, try_ref_from_ptr, try_slice, - ArcCastPtr, BoxCastPtr, CastConstPtr, CastPtr, + ffi_panic_boundary, try_arc_from_ptr, try_mut_from_ptr, try_ref_from_ptr, try_slice, + ArcCastPtr, BoxCastPtr, CastPtr, }; use rustls_result::{AlreadyUsed, NullParameter}; @@ -573,297 +574,182 @@ impl rustls_root_cert_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 CastPtr for rustls_allow_any_authenticated_client_builder { - // NOTE: contained value is consumed even on error, so this can contain None. but the caller - // still needs to free it - type RustType = Option; +impl CastPtr for rustls_client_cert_verifier { + type RustType = Arc; } -impl BoxCastPtr for rustls_allow_any_authenticated_client_builder {} +impl BoxCastPtr for rustls_client_cert_verifier {} -impl rustls_allow_any_authenticated_client_builder { - /// Create a new allow any authenticated client certificate verifier builder using the root store. - /// - /// This copies the contents of the rustls_root_cert_store. It does not take - /// ownership of the pointed-to memory. - /// - /// This object can then be used to load any CRLs. - /// - /// Once that is complete, convert it into a real `rustls_allow_any_authenticated_client_verifier` - /// by calling `rustls_allow_any_authenticated_client_verifier_new()`. - #[no_mangle] - pub extern "C" fn rustls_allow_any_authenticated_client_builder_new( - store: *const rustls_root_cert_store, - ) -> *mut rustls_allow_any_authenticated_client_builder { - ffi_panic_boundary! { - let store = try_arc_from_ptr!(store); - let client_cert_verifier = Some(AllowAnyAuthenticatedClient::new(store)); - BoxCastPtr::to_mut_ptr(client_cert_verifier) - } - } - - /// 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. - /// - /// This function returns an error if the provided buffer is not valid PEM encoded content, - /// or if the CRL content is invalid or unsupported. - #[no_mangle] - pub extern "C" fn rustls_allow_any_authenticated_client_builder_add_crl( - builder: *mut rustls_allow_any_authenticated_client_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 crl_pem: &[u8] = try_slice!(crl_pem, crl_pem_len); - 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, - }; - - let client_cert_verifier = match client_cert_verifier_builder.take() { - None => { - return AlreadyUsed; - }, - Some(x) => x, - }; - - 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)), - }; - - 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. +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_allow_any_authenticated_client_builder_free( - builder: *mut rustls_allow_any_authenticated_client_builder, - ) { + pub extern "C" fn rustls_client_cert_verifier_free(verifier: *mut rustls_client_cert_verifier) { ffi_panic_boundary! { - let store = try_box_from_ptr!(builder); - drop(store) + BoxCastPtr::to_box(verifier); } } } -/// 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 { +/// 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], } -impl CastConstPtr for rustls_allow_any_authenticated_client_verifier { - type RustType = AllowAnyAuthenticatedClient; +pub(crate) struct ClientCertVerifierBuilder { + roots: Arc, + crls: Vec>, + allow_anonymous: bool, } -impl ArcCastPtr for rustls_allow_any_authenticated_client_verifier {} - -impl rustls_allow_any_authenticated_client_verifier { - /// Create a new allow any authenticated client certificate verifier from a 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. - #[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 { - ffi_panic_boundary! { - let client_cert_verifier_builder: &mut Option = try_mut_from_ptr!(builder); - - let client_cert_verifier = match client_cert_verifier_builder.take() { - None => { - return null() as *const _; - }, - Some(x) => x, - }; - Arc::into_raw(client_cert_verifier.boxed()) as *const _ - } - } - - /// "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. - #[no_mangle] - pub extern "C" fn rustls_allow_any_authenticated_client_verifier_free( - verifier: *const rustls_allow_any_authenticated_client_verifier, - ) { - ffi_panic_boundary! { - rustls_allow_any_authenticated_client_verifier::free(verifier); - } - } -} - -/// 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 { - _private: [u8; 0], -} - -impl CastPtr for rustls_allow_any_anonymous_or_authenticated_client_builder { - // NOTE: contained value is consumed even on error, so this can contain None. but the caller - // still needs to free it - type RustType = Option; +impl CastPtr for rustls_web_pki_client_cert_verifier_builder { + type RustType = Option; } -impl BoxCastPtr for rustls_allow_any_anonymous_or_authenticated_client_builder {} +impl BoxCastPtr for rustls_web_pki_client_cert_verifier_builder {} -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 rustls_web_pki_client_cert_verifier_builder { + /// Create a `rustls_web_pki_client_cert_verifier_builder`. Caller owns the memory and must + /// eventually call `rustls_web_pki_client_cert_verifier_builder_build`, then free the + /// resulting `rustls_client_cert_verifier`. /// - /// 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 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 object can then be used to load any CRLs. + /// 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. /// - /// 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()`. + /// Anonymous unauthenticated clients will not be permitted unless + /// `rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated` is used. + /// + /// 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_client_cert_verifier_builder_new( store: *const rustls_root_cert_store, - ) -> *mut rustls_allow_any_anonymous_or_authenticated_client_builder { + ) -> *mut rustls_web_pki_client_cert_verifier_builder { ffi_panic_boundary! { let store = try_arc_from_ptr!(store); - let client_cert_verifier = Some(AllowAnyAnonymousOrAuthenticatedClient::new(store)); - BoxCastPtr::to_mut_ptr(client_cert_verifier) + let builder = ClientCertVerifierBuilder { + roots: store, + crls: Vec::default(), + allow_anonymous: false, + }; + BoxCastPtr::to_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. + /// 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_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 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, }; - let client_cert_verifier = match client_cert_verifier_builder.take() { - None => { - return AlreadyUsed; - }, - Some(x) => x, + let client_verifier_builder = match client_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, }; - 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)), - }; + client_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. - #[no_mangle] - pub extern "C" fn rustls_client_cert_verifier_optional_builder_free( - builder: *mut rustls_allow_any_anonymous_or_authenticated_client_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. + 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 store = try_box_from_ptr!(builder); - drop(store) - } - } -} - -/// 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], -} + 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 CastConstPtr for rustls_allow_any_anonymous_or_authenticated_client_verifier { - type RustType = AllowAnyAnonymousOrAuthenticatedClient; -} + client_verifier_builder.allow_anonymous = true; -impl ArcCastPtr for rustls_allow_any_anonymous_or_authenticated_client_verifier {} + 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 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 + /// 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_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_client_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_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 = match client_verifier_builder.take() { + None => return AlreadyUsed, + Some(v) => v, + }; - 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); + if client_verifier_builder.allow_anonymous { + builder = builder.allow_unauthenticated(); + } + + 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 _ + + BoxCastPtr::set_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_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_anonymous_or_authenticated_client_verifier_free( - verifier: *const rustls_allow_any_anonymous_or_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! { - rustls_allow_any_anonymous_or_authenticated_client_verifier::free(verifier); + BoxCastPtr::to_box(builder); } } } diff --git a/src/error.rs b/src/error.rs index 28075644..56d0f0e5 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::ClientCertVerifierBuilderError; use rustls::{CertRevocationListError, CertificateError, Error, InvalidMessage}; /// A return value for a function that may return either success (0) or a @@ -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 } } @@ -387,35 +391,47 @@ pub(crate) fn map_error(input: rustls::Error) -> rustls_result { _ => AlertUnknown, }, - 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: ClientCertVerifierBuilderError) -> rustls_result { + match err { + ClientCertVerifierBuilderError::NoRootAnchors => { + rustls_result::ClientCertVerifierBuilderNoRootAnchors + } + ClientCertVerifierBuilderError::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; @@ -639,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 b64f446a..2c31dd10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -399,6 +399,12 @@ pub(crate) trait ArcCastPtr: CastConstPtr + Sized { fn to_const_ptr(src: Self::RustType) -> *const Self { Arc::into_raw(Arc::new(src)) as *const _ } + + fn set_mut_ptr(dst: *mut *const Self, src: Self::RustType) { + unsafe { + *dst = Self::to_const_ptr(src); + } + } } #[doc(hidden)] diff --git a/src/rustls.h b/src/rustls.h index 160cea9c..dd200239 100644 --- a/src/rustls.h +++ b/src/rustls.h @@ -116,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; @@ -165,39 +166,6 @@ 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 `CertificateDer` in the Rust pki-types API. @@ -213,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`. @@ -314,6 +288,15 @@ 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 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, @@ -997,117 +980,61 @@ void rustls_root_cert_store_builder_free(struct rustls_root_cert_store_builder * 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. - * - * This copies the contents of the rustls_root_cert_store. It does not take - * ownership of the pointed-to memory. - * - * This object can then be used to load any CRLs. - * - * Once that is complete, convert it into a real `rustls_allow_any_authenticated_client_verifier` - * by calling `rustls_allow_any_authenticated_client_verifier_new()`. - */ -struct rustls_allow_any_authenticated_client_builder *rustls_allow_any_authenticated_client_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. - * - * This function returns an error if the provided buffer is not valid PEM encoded content, - * or if the CRL content is invalid or unsupported. - */ -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); - -/** - * 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. + * 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_allow_any_authenticated_client_builder_free(struct rustls_allow_any_authenticated_client_builder *builder); +void rustls_client_cert_verifier_free(struct rustls_client_cert_verifier *verifier); /** - * Create a new allow any authenticated client certificate verifier from a builder. + * Create a `rustls_web_pki_client_cert_verifier_builder`. Caller owns the memory and must + * eventually call `rustls_web_pki_client_cert_verifier_builder_build`, then free the + * resulting `rustls_client_cert_verifier`. * - * The builder is consumed and cannot be used again, but must still be freed. + * 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. * - * 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. - */ -const struct rustls_allow_any_authenticated_client_verifier *rustls_allow_any_authenticated_client_verifier_new(struct rustls_allow_any_authenticated_client_builder *builder); - -/** - * "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. - */ -void rustls_allow_any_authenticated_client_verifier_free(const struct rustls_allow_any_authenticated_client_verifier *verifier); - -/** - * Create a new allow any anonymous or authenticated client certificate verifier builder - * using the root store. - * - * 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. * - * This object can then be used to load any CRLs. + * Anonymous 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_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_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. * - * This function returns an error if the provided buffer is not valid PEM encoded content, - * or if the CRL content is invalid or unsupported. - */ -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); - -/** - * 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. + * This function returns an error if the provided buffer is not valid PEM encoded content. */ -void rustls_client_cert_verifier_optional_builder_free(struct rustls_allow_any_anonymous_or_authenticated_client_builder *builder); +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); /** - * Create a new allow any anonymous or authenticated client certificate verifier builder - * from the builder. + * 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 + * 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_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_client_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_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_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_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_anonymous_or_authenticated_client_verifier_free(const struct rustls_allow_any_anonymous_or_authenticated_client_verifier *verifier); +void rustls_web_pki_client_cert_verifier_builder_free(struct rustls_web_pki_client_cert_verifier_builder *builder); /** * Create a rustls_client_config_builder. Caller owns the memory and must @@ -1559,22 +1486,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. */ 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 a7e0f729..bd0c1064 100644 --- a/src/server.rs +++ b/src/server.rs @@ -8,16 +8,14 @@ use libc::size_t; use rustls::crypto::ring::ALL_CIPHER_SUITES; use rustls::server::danger::ClientCertVerifier; use rustls::server::{ - AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, ClientHello, NoClientAuth, - ResolvesServerCert, ServerConfig, ServerConnection, StoresServerSessions, + ClientHello, ResolvesServerCert, ServerConfig, ServerConnection, StoresServerSessions, + WebPkiClientVerifier, }; use rustls::sign::CertifiedKey; 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. #[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_arc_from_ptr!(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_arc_from_ptr!(verifier); - - builder.verifier = verifier; + let verifier = try_ref_from_ptr!(verifier); + builder.verifier = verifier.clone(); } } diff --git a/tests/server.c b/tests/server.c index dbed3eb6..013c624a 100644 --- a/tests/server.c +++ b/tests/server.c @@ -240,10 +240,9 @@ main(int argc, const char **argv) struct rustls_slice_bytes alpn_http11; 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_allow_any_authenticated_client_builder + 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"; @@ -301,8 +300,7 @@ main(int argc, const char **argv) 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; @@ -312,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); } @@ -404,9 +405,9 @@ main(int argc, const char **argv) 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) {