summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel McCarney <daniel@binaryparadox.net>2024-07-05 13:06:32 -0400
committerDaniel McCarney <daniel@binaryparadox.net>2024-09-09 11:42:49 -0400
commit0b8bc1fbe4e37333b4a6e41b6827bc610b866d16 (patch)
tree6eeda489d8a8ca934bc6c01bfb55621146b2dc08
parentf82f2cc5baa54986e1d9f12c01cfbd4422e42ca1 (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.rs7
-rw-r--r--src/crypto_provider.rs2
-rw-r--r--src/rustls.h28
-rw-r--r--src/server.rs160
-rw-r--r--tests/server.c7
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;