diff options
author | Daniel McCarney <daniel@binaryparadox.net> | 2024-07-05 13:06:32 -0400 |
---|---|---|
committer | Daniel McCarney <daniel@binaryparadox.net> | 2024-09-09 11:42:49 -0400 |
commit | 0b8bc1fbe4e37333b4a6e41b6827bc610b866d16 (patch) | |
tree | 6eeda489d8a8ca934bc6c01bfb55621146b2dc08 | |
parent | f82f2cc5baa54986e1d9f12c01cfbd4422e42ca1 (diff) |
server: convert server config/builder to provider
* `rustls_server_config_builder_new()` now uses the process default
crypto provider instead of being hardcoded to `*ring*`. We defer
constructing the `ServerConfig` to avoid a panic in the event the
process default isn't constructed. This will be surfaced as an error
at build time instead. Like the upstream `ServerConfig::builder()`
we make an attempt to install a process default provider from
`rustls_server_config_builder_new()` if one has not been set and
a clear choice is available based on crate features.
* `rustls_server_config_builder_new_custom()` now takes
a `rustls_crypto_provider` as an argument in place of the list of
custom ciphersuites. The ciphersuites can be customized when the
provider is constructed.
* `rustls_server_config_builder_build()` now uses an out param for the
`ServerConfig` so we can return a suitable error if there is no crypto
provider (e.g. because `rustls_server_config_builder_new()` was used
but the process default wasn't set and couldn't be guessed by crate
features).
* The `server.c` test code is updated to account for the breaking change
in the builder out param.
-rw-r--r-- | src/acceptor.rs | 7 | ||||
-rw-r--r-- | src/crypto_provider.rs | 2 | ||||
-rw-r--r-- | src/rustls.h | 28 | ||||
-rw-r--r-- | src/server.rs | 160 | ||||
-rw-r--r-- | tests/server.c | 7 |
5 files changed, 120 insertions, 84 deletions
diff --git a/src/acceptor.rs b/src/acceptor.rs index f779942..a57feed 100644 --- a/src/acceptor.rs +++ b/src/acceptor.rs @@ -696,8 +696,11 @@ mod tests { assert_eq!(result, rustls_result::Ok); rustls_certified_key::rustls_certified_key_free(certified_key); - let config = rustls_server_config_builder::rustls_server_config_builder_build(builder); - assert_ne!(config, null()); + let mut config = null(); + let res = + rustls_server_config_builder::rustls_server_config_builder_build(builder, &mut config); + assert_eq!(res, rustls_result::Ok); + assert!(!config.is_null()); config } diff --git a/src/crypto_provider.rs b/src/crypto_provider.rs index 6d68e44..d12a050 100644 --- a/src/crypto_provider.rs +++ b/src/crypto_provider.rs @@ -333,7 +333,7 @@ pub extern "C" fn rustls_default_crypto_provider_ciphersuites_get( } } -fn get_default_or_install_from_crate_features() -> Option<Arc<CryptoProvider>> { +pub(crate) fn get_default_or_install_from_crate_features() -> Option<Arc<CryptoProvider>> { // If a process-wide default has already been installed, return it. if let Some(provider) = CryptoProvider::get_default() { return Some(provider.clone()); diff --git a/src/rustls.h b/src/rustls.h index e3563e7..29d4420 100644 --- a/src/rustls.h +++ b/src/rustls.h @@ -2049,7 +2049,7 @@ size_t rustls_slice_str_len(const struct rustls_slice_str *input); struct rustls_str rustls_slice_str_get(const struct rustls_slice_str *input, size_t n); /** - * Create a rustls_server_config_builder. + * Create a rustls_server_config_builder using the process default crypto provider. * * Caller owns the memory and must eventually call rustls_server_config_builder_build, * then free the resulting rustls_server_config. @@ -2057,13 +2057,13 @@ struct rustls_str rustls_slice_str_get(const struct rustls_slice_str *input, siz * Alternatively, if an error occurs or, you don't wish to build a config, call * `rustls_server_config_builder_free` to free the builder directly. * - * This uses rustls safe default values for the cipher suites, key exchange groups - * and protocol versions. + * This uses the process default provider's values for the cipher suites and key exchange + * groups, as well as safe defaults for protocol versions. */ struct rustls_server_config_builder *rustls_server_config_builder_new(void); /** - * Create a rustls_server_config_builder. + * Create a rustls_server_config_builder using the specified crypto provider. * * Caller owns the memory and must eventually call rustls_server_config_builder_build, * then free the resulting rustls_server_config. @@ -2071,10 +2071,6 @@ struct rustls_server_config_builder *rustls_server_config_builder_new(void); * Alternatively, if an error occurs or, you don't wish to build a config, call * `rustls_server_config_builder_free` to free the builder directly. * - * Specify cipher suites in preference order; the `cipher_suites` parameter must - * point to an array containing `len` pointers to `rustls_supported_ciphersuite` - * previously obtained from `rustls_all_ciphersuites_get_entry()`. - * * `tls_versions` set the TLS protocol versions to use when negotiating a TLS session. * * `tls_versions` is the version of the protocol, as defined in rfc8446, @@ -2084,9 +2080,11 @@ struct rustls_server_config_builder *rustls_server_config_builder_new(void); * `tls_versions` will only be used during the call and the application retains * ownership. `tls_versions_len` is the number of consecutive `uint16_t` pointed * to by `tls_versions`. + * + * Ciphersuites are configured separately via the crypto provider. See + * `rustls_crypto_provider_builder_set_cipher_suites` for more information. */ -rustls_result rustls_server_config_builder_new_custom(const struct rustls_supported_ciphersuite *const *cipher_suites, - size_t cipher_suites_len, +rustls_result rustls_server_config_builder_new_custom(const struct rustls_crypto_provider *provider, const uint16_t *tls_versions, size_t tls_versions_len, struct rustls_server_config_builder **builder_out); @@ -2159,9 +2157,15 @@ rustls_result rustls_server_config_builder_set_certified_keys(struct rustls_serv /** * Turn a *rustls_server_config_builder (mutable) into a const *rustls_server_config - * (read-only). + * (read-only). The constructed `rustls_server_config` will be written to the `config_out` + * pointer when this function returns `rustls_result::Ok`. + * + * This function may return an error if no process default crypto provider has been set + * and the builder was constructed using `rustls_server_config_builder_new`, or if no + * certificate resolver was set. */ -const struct rustls_server_config *rustls_server_config_builder_build(struct rustls_server_config_builder *builder); +rustls_result rustls_server_config_builder_build(struct rustls_server_config_builder *builder, + const struct rustls_server_config **config_out); /** * "Free" a rustls_server_config previously returned from diff --git a/src/server.rs b/src/server.rs index 64090ce..0e94ac6 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,33 +1,32 @@ 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::crypto::CryptoProvider; use rustls::server::danger::ClientCertVerifier; use rustls::server::{ ClientHello, ResolvesServerCert, ServerConfig, ServerConnection, StoresServerSessions, WebPkiClientVerifier, }; use rustls::sign::CertifiedKey; -use rustls::{ProtocolVersion, SignatureScheme, WantsVerifier}; +use rustls::{ProtocolVersion, SignatureScheme, SupportedProtocolVersion}; -use crate::cipher::{ - rustls_certified_key, rustls_client_cert_verifier, rustls_supported_ciphersuite, -}; +use crate::cipher::{rustls_certified_key, rustls_client_cert_verifier}; use crate::connection::{rustls_connection, Connection}; -use crate::error::rustls_result::{InvalidParameter, NullParameter}; +use crate::crypto_provider::rustls_crypto_provider; +use crate::error::rustls_result::NullParameter; use crate::error::{map_error, rustls_result}; use crate::rslice::{rustls_slice_bytes, rustls_slice_slice_bytes, rustls_slice_u16, rustls_str}; use crate::session::{ rustls_session_store_get_callback, rustls_session_store_put_callback, SessionStoreBroker, }; use crate::{ - arc_castable, box_castable, ffi_panic_boundary, free_arc, free_box, set_boxed_mut_ptr, - to_arc_const_ptr, to_boxed_mut_ptr, try_box_from_ptr, try_clone_arc, try_mut_from_ptr, - try_mut_from_ptr_ptr, try_ref_from_ptr, try_slice, userdata_get, Castable, OwnershipRef, + arc_castable, box_castable, crypto_provider, ffi_panic_boundary, free_arc, free_box, + set_arc_mut_ptr, set_boxed_mut_ptr, to_boxed_mut_ptr, try_box_from_ptr, try_clone_arc, + try_mut_from_ptr, try_mut_from_ptr_ptr, try_ref_from_ptr, try_ref_from_ptr_ptr, try_slice, + userdata_get, Castable, OwnershipRef, }; box_castable! { @@ -47,7 +46,8 @@ box_castable! { } pub(crate) struct ServerConfigBuilder { - base: rustls::ConfigBuilder<ServerConfig, WantsVerifier>, + provider: Option<Arc<CryptoProvider>>, + versions: Vec<&'static SupportedProtocolVersion>, verifier: Arc<dyn ClientCertVerifier>, cert_resolver: Option<Arc<dyn ResolvesServerCert>>, session_storage: Option<Arc<dyn StoresServerSessions + Send + Sync>>, @@ -64,7 +64,7 @@ arc_castable! { } impl rustls_server_config_builder { - /// Create a rustls_server_config_builder. + /// Create a rustls_server_config_builder using the process default crypto provider. /// /// Caller owns the memory and must eventually call rustls_server_config_builder_build, /// then free the resulting rustls_server_config. @@ -72,20 +72,14 @@ impl rustls_server_config_builder { /// Alternatively, if an error occurs or, you don't wish to build a config, call /// `rustls_server_config_builder_free` to free the builder directly. /// - /// This uses rustls safe default values for the cipher suites, key exchange groups - /// and protocol versions. + /// This uses the process default provider's values for the cipher suites and key exchange + /// groups, as well as safe defaults for protocol versions. #[no_mangle] pub extern "C" fn rustls_server_config_builder_new() -> *mut rustls_server_config_builder { ffi_panic_boundary! { - // Unwrap safety: *ring* default provider always has ciphersuites compatible with the - // default protocol versions. - let base = ServerConfig::builder_with_provider( - rustls::crypto::ring::default_provider().into(), - ) - .with_safe_default_protocol_versions() - .unwrap(); let builder = ServerConfigBuilder { - base, + provider: crypto_provider::get_default_or_install_from_crate_features(), + versions: rustls::DEFAULT_VERSIONS.to_vec(), verifier: WebPkiClientVerifier::no_client_auth(), cert_resolver: None, session_storage: None, @@ -96,7 +90,7 @@ impl rustls_server_config_builder { } } - /// Create a rustls_server_config_builder. + /// Create a rustls_server_config_builder using the specified crypto provider. /// /// Caller owns the memory and must eventually call rustls_server_config_builder_build, /// then free the resulting rustls_server_config. @@ -104,10 +98,6 @@ impl rustls_server_config_builder { /// Alternatively, if an error occurs or, you don't wish to build a config, call /// `rustls_server_config_builder_free` to free the builder directly. /// - /// Specify cipher suites in preference order; the `cipher_suites` parameter must - /// point to an array containing `len` pointers to `rustls_supported_ciphersuite` - /// previously obtained from `rustls_all_ciphersuites_get_entry()`. - /// /// `tls_versions` set the TLS protocol versions to use when negotiating a TLS session. /// /// `tls_versions` is the version of the protocol, as defined in rfc8446, @@ -117,28 +107,18 @@ impl rustls_server_config_builder { /// `tls_versions` will only be used during the call and the application retains /// ownership. `tls_versions_len` is the number of consecutive `uint16_t` pointed /// to by `tls_versions`. + /// + /// Ciphersuites are configured separately via the crypto provider. See + /// `rustls_crypto_provider_builder_set_cipher_suites` for more information. #[no_mangle] pub extern "C" fn rustls_server_config_builder_new_custom( - cipher_suites: *const *const rustls_supported_ciphersuite, - cipher_suites_len: size_t, + provider: *const rustls_crypto_provider, tls_versions: *const u16, tls_versions_len: size_t, builder_out: *mut *mut rustls_server_config_builder, ) -> rustls_result { ffi_panic_boundary! { - if builder_out.is_null() { - return NullParameter; - } - let cipher_suites = try_slice!(cipher_suites, cipher_suites_len); - let mut cs_vec = Vec::new(); - for &cs in cipher_suites.iter() { - let cs = try_ref_from_ptr!(cs); - match ALL_CIPHER_SUITES.iter().find(|&acs| cs.eq(acs)) { - Some(scs) => cs_vec.push(*scs), - None => return InvalidParameter, - } - } - + let provider = try_clone_arc!(provider); let tls_versions = try_slice!(tls_versions, tls_versions_len); let mut versions = vec![]; for version_number in tls_versions { @@ -149,22 +129,11 @@ impl rustls_server_config_builder { versions.push(&rustls::version::TLS13); } } - let builder_out = try_mut_from_ptr_ptr!(builder_out); - let provider = rustls::crypto::CryptoProvider { - cipher_suites: cs_vec, - ..rustls::crypto::ring::default_provider() - }; - let result = rustls::ServerConfig::builder_with_provider(provider.into()) - .with_protocol_versions(&versions); - let base = match result { - Ok(new) => new, - Err(_) => return rustls_result::InvalidParameter, - }; - let builder = ServerConfigBuilder { - base, + provider: Some(provider), + versions, verifier: WebPkiClientVerifier::no_client_auth(), cert_resolver: None, session_storage: None, @@ -287,18 +256,38 @@ impl rustls_server_config_builder { } /// Turn a *rustls_server_config_builder (mutable) into a const *rustls_server_config - /// (read-only). + /// (read-only). The constructed `rustls_server_config` will be written to the `config_out` + /// pointer when this function returns `rustls_result::Ok`. + /// + /// This function may return an error if no process default crypto provider has been set + /// and the builder was constructed using `rustls_server_config_builder_new`, or if no + /// certificate resolver was set. #[no_mangle] pub extern "C" fn rustls_server_config_builder_build( builder: *mut rustls_server_config_builder, - ) -> *const rustls_server_config { + config_out: *mut *const rustls_server_config, + ) -> rustls_result { ffi_panic_boundary! { let builder = try_box_from_ptr!(builder); - let base = builder.base.with_client_cert_verifier(builder.verifier); + let config_out = try_ref_from_ptr_ptr!(config_out); + + let provider = match builder.provider { + Some(provider) => provider, + None => return rustls_result::NoDefaultCryptoProvider, + }; + + let base = match ServerConfig::builder_with_provider(provider) + .with_protocol_versions(&builder.versions) + { + Ok(base) => base, + Err(err) => return map_error(err), + } + .with_client_cert_verifier(builder.verifier); + let mut config = if let Some(r) = builder.cert_resolver { base.with_cert_resolver(r) } else { - return null(); + return rustls_result::General; }; if let Some(ss) = builder.session_storage { config.session_storage = ss; @@ -307,7 +296,9 @@ impl rustls_server_config_builder { if let Some(ignore_client_order) = builder.ignore_client_order { config.ignore_client_order = ignore_client_order; } - to_arc_const_ptr(config) + + set_arc_mut_ptr(config_out, config); + rustls_result::Ok } } } @@ -692,11 +683,13 @@ impl rustls_server_config_builder { #[cfg(test)] mod tests { + use std::ptr::null; use std::ptr::null_mut; use super::*; #[test] + #[cfg_attr(miri, ignore)] fn test_config_builder() { let builder = rustls_server_config_builder::rustls_server_config_builder_new(); let h1 = "http/1.1".as_bytes(); @@ -707,7 +700,34 @@ mod tests { alpn.as_ptr(), alpn.len(), ); - let config = rustls_server_config_builder::rustls_server_config_builder_build(builder); + + let cert_pem = include_str!("../testdata/localhost/cert.pem").as_bytes(); + let key_pem = include_str!("../testdata/localhost/key.pem").as_bytes(); + let mut certified_key = null(); + let result = rustls_certified_key::rustls_certified_key_build( + cert_pem.as_ptr(), + cert_pem.len(), + key_pem.as_ptr(), + key_pem.len(), + &mut certified_key, + ); + if !matches!(result, rustls_result::Ok) { + panic!( + "expected RUSTLS_RESULT_OK from rustls_certified_key_build, got {:?}", + result + ); + } + rustls_server_config_builder::rustls_server_config_builder_set_certified_keys( + builder, + &certified_key, + 1, + ); + + let mut config = null(); + let result = + rustls_server_config_builder::rustls_server_config_builder_build(builder, &mut config); + assert_eq!(result, rustls_result::Ok); + assert!(!config.is_null()); { let config2 = try_ref_from_ptr!(config); assert_eq!(config2.alpn_protocols, vec![h1, h2]); @@ -719,11 +739,12 @@ mod tests { #[test] fn test_server_config_builder_new_empty() { let builder = rustls_server_config_builder::rustls_server_config_builder_new(); - // Building a config with no certificate and key configured results in null. - assert_eq!( - rustls_server_config_builder::rustls_server_config_builder_build(builder), - null() - ); + // Building a config with no certificate and key configured results in an error. + let mut config = null(); + let result = + rustls_server_config_builder::rustls_server_config_builder_build(builder, &mut config); + assert_eq!(result, rustls_result::General); + assert!(config.is_null()); } #[test] @@ -752,8 +773,11 @@ mod tests { 1, ); - let config = rustls_server_config_builder::rustls_server_config_builder_build(builder); - assert_ne!(config, null()); + let mut config = null(); + let result = + rustls_server_config_builder::rustls_server_config_builder_build(builder, &mut config); + assert_eq!(result, rustls_result::Ok); + assert!(!config.is_null()); let mut conn = null_mut(); let result = rustls_server_config::rustls_server_connection_new(config, &mut conn); diff --git a/tests/server.c b/tests/server.c index b1a96c9..723f25f 100644 --- a/tests/server.c +++ b/tests/server.c @@ -336,7 +336,12 @@ main(int argc, const char **argv) client_cert_verifier); } - server_config = rustls_server_config_builder_build(config_builder); + rustls_result result = + rustls_server_config_builder_build(config_builder, &server_config); + if(result != RUSTLS_RESULT_OK) { + print_error("building server config", result); + goto cleanup; + } #ifdef _WIN32 WSADATA wsa; |