diff --git a/.gitignore b/.gitignore index f9e44c1b..05161b08 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ target Cargo.lock /build +.idea +.venv diff --git a/Cargo.toml b/Cargo.toml index 6202b60d..2289b480 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,11 +23,11 @@ read_buf = ["rustls/read_buf"] [dependencies] # Keep in sync with RUSTLS_CRATE_VERSION in build.rs -rustls = { version = "=0.21.0", features = [ "dangerous_configuration" ] } -webpki = "0.22" +rustls = { version = "=0.21.5", features = [ "dangerous_configuration" ] } +rustls-webpki = "0.101.0" libc = "0.2" sct = "0.7" -rustls-pemfile = "0.2.1" +rustls-pemfile = "1.0.3" log = "0.4.17" [lib] diff --git a/build.rs b/build.rs index 5e0b3b79..6871afd5 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.0"; +const RUSTLS_CRATE_VERSION: &str = "0.21.5"; fn main() { let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); diff --git a/src/cipher.rs b/src/cipher.rs index 21409758..24aabecb 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -5,21 +5,23 @@ use std::ptr::null; use std::slice; use std::sync::Arc; -use rustls::server::{AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient}; +use rustls::server::{ + AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, UnparsedCertRevocationList, +}; use rustls::sign::CertifiedKey; use rustls::{ Certificate, PrivateKey, RootCertStore, SupportedCipherSuite, ALL_CIPHER_SUITES, DEFAULT_CIPHER_SUITES, }; -use rustls_pemfile::{certs, pkcs8_private_keys, rsa_private_keys}; +use rustls_pemfile::{certs, crls, pkcs8_private_keys, rsa_private_keys}; -use crate::error::rustls_result; +use crate::error::{map_error, 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, }; -use rustls_result::NullParameter; +use rustls_result::{AlreadyUsed, NullParameter}; use std::ops::Deref; /// An X.509 certificate, as used in rustls. @@ -509,102 +511,296 @@ 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 { + _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 BoxCastPtr for rustls_allow_any_authenticated_client_builder {} + +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: &RootCertStore = try_ref_from_ptr!(store); + let client_cert_verifier = Some(AllowAnyAuthenticatedClient::new(store.clone())); + 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: Vec = match crls(&mut Cursor::new(crl_pem)) { + Ok(vv) => vv.into_iter().map(UnparsedCertRevocationList).collect(), + 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. + #[no_mangle] + pub extern "C" fn rustls_allow_any_authenticated_client_builder_free( + builder: *mut rustls_allow_any_authenticated_client_builder, + ) { + ffi_panic_boundary! { + let store = try_box_from_ptr!(builder); + drop(store) + } + } +} + /// 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_client_cert_verifier { +pub struct rustls_allow_any_authenticated_client_verifier { _private: [u8; 0], } -impl CastConstPtr for rustls_client_cert_verifier { +impl CastConstPtr for rustls_allow_any_authenticated_client_verifier { type RustType = AllowAnyAuthenticatedClient; } -impl ArcCastPtr for rustls_client_cert_verifier {} +impl ArcCastPtr for rustls_allow_any_authenticated_client_verifier {} -impl rustls_client_cert_verifier { - /// Create a new client certificate verifier for the root store. The verifier - /// can be used in several rustls_server_config instances. Must be freed by +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_client_cert_verifier_free for details about lifetime. - /// This copies the contents of the rustls_root_cert_store. It does not take + /// `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_client_cert_verifier_new( - store: *const rustls_root_cert_store, - ) -> *const rustls_client_cert_verifier { + 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 store: &RootCertStore = try_ref_from_ptr!(store); - let client_cert_verifier = AllowAnyAuthenticatedClient::new(store.clone()); + 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, + }; return Arc::into_raw(client_cert_verifier.boxed()) as *const _; } } /// "Free" a verifier previously returned from - /// rustls_client_cert_verifier_new. Since rustls_client_cert_verifier is actually an + /// `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_client_cert_verifier_free( - verifier: *const rustls_client_cert_verifier, + pub extern "C" fn rustls_allow_any_authenticated_client_verifier_free( + verifier: *const rustls_allow_any_authenticated_client_verifier, ) { ffi_panic_boundary! { - rustls_client_cert_verifier::free(verifier); + rustls_allow_any_authenticated_client_verifier::free(verifier); } } } -/// Alternative to `rustls_client_cert_verifier` that allows connections +/// 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 BoxCastPtr for rustls_allow_any_anonymous_or_authenticated_client_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. + /// + /// 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_anonymous_or_authenticated_client_verifier` + /// by calling `rustls_allow_any_anonymous_or_authenticated_client_verifier_new()`. + #[no_mangle] + pub extern "C" fn rustls_client_cert_verifier_optional_builder_new( + store: *const rustls_root_cert_store, + ) -> *mut rustls_allow_any_anonymous_or_authenticated_client_builder { + ffi_panic_boundary! { + let store: &RootCertStore = try_ref_from_ptr!(store); + let client_cert_verifier = Some(AllowAnyAnonymousOrAuthenticatedClient::new(store.clone())); + 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_client_cert_verifier_optional_builder_add_crl( + builder: *mut rustls_allow_any_anonymous_or_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: Vec = match crls(&mut Cursor::new(crl_pem)) { + Ok(vv) => vv.into_iter().map(UnparsedCertRevocationList).collect(), + 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_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, + ) { + 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_client_cert_verifier_optional { +/// `rustls_connection_get_peer_certificate`. +pub struct rustls_allow_any_anonymous_or_authenticated_client_verifier { _private: [u8; 0], } -impl CastConstPtr for rustls_client_cert_verifier_optional { +impl CastConstPtr for rustls_allow_any_anonymous_or_authenticated_client_verifier { type RustType = AllowAnyAnonymousOrAuthenticatedClient; } -impl ArcCastPtr for rustls_client_cert_verifier_optional {} +impl ArcCastPtr for rustls_allow_any_anonymous_or_authenticated_client_verifier {} -impl rustls_client_cert_verifier_optional { - /// Create a new rustls_client_cert_verifier_optional for the root store. The - /// verifier can be used in several rustls_server_config instances. Must be +impl rustls_allow_any_anonymous_or_authenticated_client_verifier { + /// Create a new allow any anonymous or authenticated client certificate verifier builder + /// 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_client_cert_verifier_optional_free for details about lifetime. - /// This copies the contents of the rustls_root_cert_store. It does not take + /// `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. #[no_mangle] - pub extern "C" fn rustls_client_cert_verifier_optional_new( - store: *const rustls_root_cert_store, - ) -> *const rustls_client_cert_verifier_optional { + 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 { ffi_panic_boundary! { - let store: &RootCertStore = try_ref_from_ptr!(store); - let client_cert_verifier = AllowAnyAnonymousOrAuthenticatedClient::new(store.clone()); - return Arc::into_raw(client_cert_verifier.boxed()) - as *const _; + 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, + }; + return Arc::into_raw(client_cert_verifier.boxed()) as *const _; } } /// "Free" a verifier previously returned from - /// rustls_client_cert_verifier_optional_new. Since rustls_client_cert_verifier_optional - /// is actually an atomically reference-counted pointer, extant server_configs may still + /// `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. #[no_mangle] - pub extern "C" fn rustls_client_cert_verifier_optional_free( - verifier: *const rustls_client_cert_verifier_optional, + pub extern "C" fn rustls_allow_any_anonymous_or_authenticated_client_verifier_free( + verifier: *const rustls_allow_any_anonymous_or_authenticated_client_verifier, ) { ffi_panic_boundary! { - rustls_client_cert_verifier_optional::free(verifier); + rustls_allow_any_anonymous_or_authenticated_client_verifier::free(verifier); } } } diff --git a/src/error.rs b/src/error.rs index d238aa9b..97a750f6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use crate::ffi_panic_boundary; use libc::{c_char, c_uint, size_t}; -use rustls::{CertificateError, Error, InvalidMessage}; +use rustls::{CertRevocationListError, CertificateError, Error, InvalidMessage}; /// A return value for a function that may return either success (0) or a /// non-zero value representing an error. The values should match socket @@ -57,6 +57,7 @@ u32_enum_builder! { PlaintextEmpty => 7011, AcceptorNotReady => 7012, AlreadyUsed => 7013, + CertificateRevocationListParseError => 7014, // From https://docs.rs/rustls/latest/rustls/enum.Error.html NoCertificatesPresented => 7101, @@ -166,7 +167,21 @@ u32_enum_builder! { CertSCTInvalidSignature => 7320, CertSCTTimestampInFuture => 7321, CertSCTUnsupportedVersion => 7322, - CertSCTUnknownLog => 7323 + CertSCTUnknownLog => 7323, + + // From InvalidCertRevocationList, with fields that get flattened. + // https://docs.rs/rustls/0.21.6/rustls/enum.Error.html#variant.InvalidCertRevocationList + CertRevocationListBadSignature => 7400, + CertRevocationListInvalidCrlNumber => 7401, + CertRevocationListInvalidRevokedCertSerialNumber => 7402, + CertRevocationListIssuerInvalidForCrl => 7403, + CertRevocationListOtherError => 7404, + CertRevocationListParseError => 7405, + CertRevocationListUnsupportedCrlVersion => 7406, + CertRevocationListUnsupportedCriticalExtension => 7407, + CertRevocationListUnsupportedDeltaCrl => 7408, + CertRevocationListUnsupportedIndirectCrl => 7409, + CertRevocationListUnsupportedRevocationReason => 7410 } } @@ -394,6 +409,32 @@ pub(crate) fn map_error(input: rustls::Error) -> rustls_result { 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, + }, + _ => General, } } @@ -435,6 +476,9 @@ impl Display for rustls_result { f, "tried to use a rustls struct after it had been converted to another struct" ), + CertificateRevocationListParseError => { + write!(f, "error parsing certificate revocation list (CRL)",) + } CertEncodingBad => Error::InvalidCertificate(CertificateError::BadEncoding).fmt(f), CertExpired => Error::InvalidCertificate(CertificateError::Expired).fmt(f), @@ -584,6 +628,47 @@ impl Display for rustls_result { 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) + } + CertRevocationListInvalidCrlNumber => { + Error::InvalidCertRevocationList(CertRevocationListError::InvalidCrlNumber).fmt(f) + } + CertRevocationListInvalidRevokedCertSerialNumber => Error::InvalidCertRevocationList( + CertRevocationListError::InvalidRevokedCertSerialNumber, + ) + .fmt(f), + CertRevocationListIssuerInvalidForCrl => { + Error::InvalidCertRevocationList(CertRevocationListError::IssuerInvalidForCrl) + .fmt(f) + } + CertRevocationListOtherError => { + write!(f, "unknown certificate revocation list (CRL) error") + } + CertRevocationListParseError => { + Error::InvalidCertRevocationList(CertRevocationListError::ParseError).fmt(f) + } + CertRevocationListUnsupportedCrlVersion => { + Error::InvalidCertRevocationList(CertRevocationListError::UnsupportedCrlVersion) + .fmt(f) + } + CertRevocationListUnsupportedCriticalExtension => Error::InvalidCertRevocationList( + CertRevocationListError::UnsupportedCriticalExtension, + ) + .fmt(f), + CertRevocationListUnsupportedDeltaCrl => { + Error::InvalidCertRevocationList(CertRevocationListError::UnsupportedDeltaCrl) + .fmt(f) + } + CertRevocationListUnsupportedIndirectCrl => { + Error::InvalidCertRevocationList(CertRevocationListError::UnsupportedIndirectCrl) + .fmt(f) + } + CertRevocationListUnsupportedRevocationReason => Error::InvalidCertRevocationList( + CertRevocationListError::UnsupportedRevocationReason, + ) + .fmt(f), } } } diff --git a/src/rustls.h b/src/rustls.h index 5dc0c3eb..65e3ee3c 100644 --- a/src/rustls.h +++ b/src/rustls.h @@ -22,6 +22,7 @@ enum rustls_result { RUSTLS_RESULT_PLAINTEXT_EMPTY = 7011, RUSTLS_RESULT_ACCEPTOR_NOT_READY = 7012, RUSTLS_RESULT_ALREADY_USED = 7013, + RUSTLS_RESULT_CERTIFICATE_REVOCATION_LIST_PARSE_ERROR = 7014, RUSTLS_RESULT_NO_CERTIFICATES_PRESENTED = 7101, RUSTLS_RESULT_DECRYPT_ERROR = 7102, RUSTLS_RESULT_FAILED_TO_GET_CURRENT_TIME = 7103, @@ -109,6 +110,17 @@ enum rustls_result { 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, + RUSTLS_RESULT_CERT_REVOCATION_LIST_ISSUER_INVALID_FOR_CRL = 7403, + RUSTLS_RESULT_CERT_REVOCATION_LIST_OTHER_ERROR = 7404, + RUSTLS_RESULT_CERT_REVOCATION_LIST_PARSE_ERROR = 7405, + RUSTLS_RESULT_CERT_REVOCATION_LIST_UNSUPPORTED_CRL_VERSION = 7406, + RUSTLS_RESULT_CERT_REVOCATION_LIST_UNSUPPORTED_CRITICAL_EXTENSION = 7407, + 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, }; typedef uint32_t rustls_result; @@ -159,19 +171,29 @@ typedef struct rustls_accepted rustls_accepted; typedef struct rustls_acceptor rustls_acceptor; /** - * An X.509 certificate, as used in rustls. - * Corresponds to `Certificate` in the Rust API. - * + * 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_certificate rustls_certificate; +typedef struct rustls_allow_any_anonymous_or_authenticated_client_builder rustls_allow_any_anonymous_or_authenticated_client_builder; /** - * The complete chain of certificates to send during a TLS handshake, - * plus a private key that matches the end-entity (leaf) certificate. - * Corresponds to `CertifiedKey` in the Rust API. - * + * 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_certified_key rustls_certified_key; +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 @@ -179,18 +201,22 @@ typedef struct rustls_certified_key rustls_certified_key; * configurations. Connections without such a client certificate will not * be accepted. */ -typedef struct rustls_client_cert_verifier rustls_client_cert_verifier; +typedef struct rustls_allow_any_authenticated_client_verifier rustls_allow_any_authenticated_client_verifier; /** - * Alternative to `rustls_client_cert_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. + * An X.509 certificate, as used in rustls. + * Corresponds to `Certificate` in the Rust API. + * + */ +typedef struct rustls_certificate rustls_certificate; + +/** + * The complete chain of certificates to send during a TLS handshake, + * plus a private key that matches the end-entity (leaf) certificate. + * Corresponds to `CertifiedKey` in the Rust API. + * */ -typedef struct rustls_client_cert_verifier_optional rustls_client_cert_verifier_optional; +typedef struct rustls_certified_key rustls_certified_key; /** * A client config that is done being constructed and is now read-only. @@ -945,44 +971,117 @@ rustls_result rustls_root_cert_store_add_pem(struct rustls_root_cert_store *stor void rustls_root_cert_store_free(struct rustls_root_cert_store *store); /** - * Create a new client certificate verifier for the root store. 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_client_cert_verifier_free for details about lifetime. + * 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. */ -const struct rustls_client_cert_verifier *rustls_client_cert_verifier_new(const struct rustls_root_cert_store *store); +void rustls_allow_any_authenticated_client_builder_free(struct rustls_allow_any_authenticated_client_builder *builder); + +/** + * 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. + */ +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_client_cert_verifier_new. Since rustls_client_cert_verifier is actually an + * `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_client_cert_verifier_free(const struct rustls_client_cert_verifier *verifier); +void rustls_allow_any_authenticated_client_verifier_free(const struct rustls_allow_any_authenticated_client_verifier *verifier); /** - * Create a new rustls_client_cert_verifier_optional for the root store. 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_client_cert_verifier_optional_free for details about lifetime. + * 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. + * + * This object can then be used to load any CRLs. + * + * 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()`. + */ +struct rustls_allow_any_anonymous_or_authenticated_client_builder *rustls_client_cert_verifier_optional_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_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. + */ +void rustls_client_cert_verifier_optional_builder_free(struct rustls_allow_any_anonymous_or_authenticated_client_builder *builder); + +/** + * Create a new allow any anonymous or authenticated client certificate verifier builder + * 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_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. */ -const struct rustls_client_cert_verifier_optional *rustls_client_cert_verifier_optional_new(const struct rustls_root_cert_store *store); +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); /** * "Free" a verifier previously returned from - * rustls_client_cert_verifier_optional_new. Since rustls_client_cert_verifier_optional - * is actually an atomically reference-counted pointer, extant server_configs may still + * `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. */ -void rustls_client_cert_verifier_optional_free(const struct rustls_client_cert_verifier_optional *verifier); +void rustls_allow_any_anonymous_or_authenticated_client_verifier_free(const struct rustls_allow_any_anonymous_or_authenticated_client_verifier *verifier); /** * Create a rustls_client_config_builder. Caller owns the memory and must @@ -1440,7 +1539,7 @@ rustls_result rustls_server_config_builder_new_custom(const struct rustls_suppor * For memory lifetime, see rustls_server_config_builder_new. */ void rustls_server_config_builder_set_client_verifier(struct rustls_server_config_builder *builder, - const struct rustls_client_cert_verifier *verifier); + const struct rustls_allow_any_authenticated_client_verifier *verifier); /** * Create a rustls_server_config_builder for TLS sessions that accept @@ -1449,7 +1548,7 @@ void rustls_server_config_builder_set_client_verifier(struct rustls_server_confi * 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_client_cert_verifier_optional *verifier); + const struct rustls_allow_any_anonymous_or_authenticated_client_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 a0cd5aa9..afa042a3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -16,7 +16,8 @@ use rustls::{ }; use crate::cipher::{ - rustls_certified_key, rustls_client_cert_verifier, rustls_client_cert_verifier_optional, + rustls_allow_any_anonymous_or_authenticated_client_verifier, + rustls_allow_any_authenticated_client_verifier, rustls_certified_key, rustls_supported_ciphersuite, }; use crate::connection::{rustls_connection, Connection}; @@ -166,7 +167,7 @@ impl rustls_server_config_builder { #[no_mangle] pub extern "C" fn rustls_server_config_builder_set_client_verifier( builder: *mut rustls_server_config_builder, - verifier: *const rustls_client_cert_verifier, + verifier: *const rustls_allow_any_authenticated_client_verifier, ) { ffi_panic_boundary! { let builder: &mut ServerConfigBuilder = try_mut_from_ptr!(builder); @@ -182,7 +183,7 @@ impl rustls_server_config_builder { #[no_mangle] pub extern "C" fn rustls_server_config_builder_set_client_verifier_optional( builder: *mut rustls_server_config_builder, - verifier: *const rustls_client_cert_verifier_optional, + verifier: *const rustls_allow_any_anonymous_or_authenticated_client_verifier, ) { ffi_panic_boundary! { let builder: &mut ServerConfigBuilder = try_mut_from_ptr!(builder); diff --git a/testdata/test.crl.pem b/testdata/test.crl.pem new file mode 100644 index 00000000..ca069cd5 --- /dev/null +++ b/testdata/test.crl.pem @@ -0,0 +1,12 @@ +-----BEGIN X509 CRL----- +MIIBxjCBrwIBATANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDExVtaW5pY2Egcm9v +dCBjYSAxMGE3YTAXDTIzMDcwMzE3MTgxMloXDTIzMDcxMDE3MTgxMlowKTAnAghT +E+2CSHaYmBcNMjMwNzAzMTcxNzU5WjAMMAoGA1UdFQQDCgEBoDAwLjAfBgNVHSME +GDAWgBQ19H4hMuTID22xvBfISviOa+S+EDALBgNVHRQEBAICEAEwDQYJKoZIhvcN +AQELBQADggEBAF5fOpNZGLsHGAUasx5Il79My6EU66igE0YZWVzgX8EaCt1RMCFx +osumXkaPiohICSsczFlnJolpwacsHx/K/IMYvthna8lbAxhuWharRqoHUK+BdTDD +wtThMBC2dCNoLro/6cIpMov9OXjh8291ogIy0qIiSm20JiaWTB+0V7A6gA7riTXC +yzJTyGECLS9XP6rt+SYmcDn0D1jxfsIli0kYBJdKb3O0xF05oBaWadSLuXbcA41+ +Kcw07HACaUrR6BCrR3CjnnlTl6Pr25cQi3zPya7lNDQWqhLNx0sU2jviVZQe1nIA +Ie8Ha2syCv0aa33s0dUY6hOKDbLTGpI8f/E= +-----END X509 CRL----- diff --git a/tests/client-server.py b/tests/client-server.py index 4f0d10e4..c7f579cf 100755 --- a/tests/client-server.py +++ b/tests/client-server.py @@ -131,6 +131,38 @@ def run_mtls_client_tests(client, valgrind): ) +def run_mtls_client_crl_tests(client, valgrind): + run_with_maybe_valgrind( + [ + client, + HOST, + str(PORT), + "/" + ], + { + "CA_FILE": "testdata/minica.pem", + "AUTH_CERT": "testdata/example.com/cert.pem", + "AUTH_KEY": "testdata/example.com/key.pem", + }, + valgrind + ) + run_with_maybe_valgrind( + [ + client, + HOST, + str(PORT), + "/" + ], + { + "CA_FILE": "testdata/minica.pem", + "AUTH_CERT": "testdata/localhost/cert.pem", + "AUTH_KEY": "testdata/localhost/key.pem", + }, + valgrind, + expect_error=True # Client connecting w/ revoked cert should err. + ) + + def run_server(server, valgrind, env): args = [ server, @@ -181,10 +213,19 @@ def main(): server_popen.wait() # Client/server tests w/ mandatory client authentication. - run_server(server, valgrind, { + server_popen = run_server(server, valgrind, { "AUTH_CERT": "testdata/minica.pem", }) run_mtls_client_tests(client, valgrind) + server_popen.kill() + server_popen.wait() + + # Client/server tests w/ mandatory client authentication & CRL. + run_server(server, valgrind, { + "AUTH_CERT": "testdata/minica.pem", + "AUTH_CRL": "testdata/test.crl.pem", + }) + run_mtls_client_crl_tests(client, valgrind) if __name__ == "__main__": diff --git a/tests/server.c b/tests/server.c index e349b7e5..a1e164f7 100644 --- a/tests/server.c +++ b/tests/server.c @@ -250,8 +250,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; - const struct rustls_client_cert_verifier *client_cert_verifier = NULL; struct rustls_root_cert_store *client_cert_root_store = NULL; + struct rustls_allow_any_authenticated_client_builder + *client_cert_verifier_builder = NULL; + const struct rustls_allow_any_authenticated_client_verifier + *client_cert_verifier = NULL; alpn_http11.data = (unsigned char *)"http/1.1"; alpn_http11.len = 8; @@ -287,7 +290,7 @@ main(int argc, const char **argv) if(auth_cert) { char certbuf[10000]; size_t certbuf_len; - int result = + unsigned result = read_file(argv[0], auth_cert, certbuf, sizeof(certbuf), &certbuf_len); if(result != DEMO_OK) { goto cleanup; @@ -296,9 +299,29 @@ main(int argc, const char **argv) client_cert_root_store = rustls_root_cert_store_new(); rustls_root_cert_store_add_pem( client_cert_root_store, (uint8_t *)certbuf, certbuf_len, true); + client_cert_verifier_builder = + rustls_allow_any_authenticated_client_builder_new( + client_cert_root_store); + + char *auth_crl = getenv("AUTH_CRL"); + char crlbuf[10000]; + size_t crlbuf_len; + if(auth_crl) { + result = + read_file(argv[0], auth_crl, crlbuf, sizeof(crlbuf), &crlbuf_len); + if(result != DEMO_OK) { + goto cleanup; + } + + result = rustls_allow_any_authenticated_client_builder_add_crl( + client_cert_verifier_builder, (uint8_t *)crlbuf, certbuf_len); + if(result != RUSTLS_RESULT_OK) { + goto cleanup; + } + } - client_cert_verifier = - rustls_client_cert_verifier_new(client_cert_root_store); + client_cert_verifier = rustls_allow_any_authenticated_client_verifier_new( + client_cert_verifier_builder); rustls_server_config_builder_set_client_verifier(config_builder, client_cert_verifier); } @@ -379,7 +402,9 @@ main(int argc, const char **argv) cleanup: rustls_certified_key_free(certified_key); rustls_root_cert_store_free(client_cert_root_store); - rustls_client_cert_verifier_free(client_cert_verifier); + rustls_allow_any_authenticated_client_builder_free( + client_cert_verifier_builder); + rustls_allow_any_authenticated_client_verifier_free(client_cert_verifier); rustls_server_config_free(server_config); rustls_connection_free(rconn); if(sockfd > 0) {