rustls_ffi/server.rs
1use std::ffi::c_void;
2use std::fmt::{Debug, Formatter};
3use std::slice;
4use std::sync::Arc;
5
6use libc::size_t;
7use rustls::crypto::CryptoProvider;
8use rustls::server::danger::ClientCertVerifier;
9use rustls::server::{
10 ClientHello, ResolvesServerCert, ServerConfig, ServerConnection, StoresServerSessions,
11 WebPkiClientVerifier,
12};
13use rustls::sign::CertifiedKey;
14use rustls::{KeyLog, KeyLogFile, ProtocolVersion, SignatureScheme, SupportedProtocolVersion};
15
16use crate::certificate::rustls_certified_key;
17use crate::connection::{Connection, rustls_connection};
18use crate::crypto_provider::{self, rustls_crypto_provider};
19use crate::error::{map_error, rustls_result};
20use crate::ffi::{
21 Castable, OwnershipRef, arc_castable, box_castable, free_arc, free_box, set_arc_mut_ptr,
22 set_boxed_mut_ptr, to_boxed_mut_ptr, try_box_from_ptr, try_clone_arc, try_mut_from_ptr,
23 try_mut_from_ptr_ptr, try_ref_from_ptr, try_ref_from_ptr_ptr, try_slice,
24};
25use crate::keylog::{CallbackKeyLog, rustls_keylog_log_callback, rustls_keylog_will_log_callback};
26use crate::panic::ffi_panic_boundary;
27use crate::rslice::{rustls_slice_bytes, rustls_slice_slice_bytes, rustls_slice_u16, rustls_str};
28use crate::session::{
29 SessionStoreBroker, rustls_session_store_get_callback, rustls_session_store_put_callback,
30};
31use crate::userdata::userdata_get;
32use crate::verifier::rustls_client_cert_verifier;
33
34box_castable! {
35 /// A server config being constructed.
36 ///
37 /// A builder can be modified by,
38 /// e.g. rustls_server_config_builder_load_native_roots. Once you're
39 /// done configuring settings, call rustls_server_config_builder_build
40 /// to turn it into a *const rustls_server_config.
41 ///
42 /// Alternatively, if an error occurs or, you don't wish to build a config,
43 /// call `rustls_server_config_builder_free` to free the builder directly.
44 ///
45 /// This object is not safe for concurrent mutation.
46 /// <https://docs.rs/rustls/latest/rustls/struct.ConfigBuilder.html>
47 pub struct rustls_server_config_builder(ServerConfigBuilder);
48}
49
50pub(crate) struct ServerConfigBuilder {
51 provider: Option<Arc<CryptoProvider>>,
52 versions: Vec<&'static SupportedProtocolVersion>,
53 verifier: Arc<dyn ClientCertVerifier>,
54 cert_resolver: Option<Arc<dyn ResolvesServerCert>>,
55 session_storage: Option<Arc<dyn StoresServerSessions + Send + Sync>>,
56 alpn_protocols: Vec<Vec<u8>>,
57 ignore_client_order: Option<bool>,
58 key_log: Option<Arc<dyn KeyLog>>,
59}
60
61arc_castable! {
62 /// A server config that is done being constructed and is now read-only.
63 ///
64 /// Under the hood, this object corresponds to an `Arc<ServerConfig>`.
65 /// <https://docs.rs/rustls/latest/rustls/struct.ServerConfig.html>
66 pub struct rustls_server_config(ServerConfig);
67}
68
69impl rustls_server_config_builder {
70 /// Create a rustls_server_config_builder using the process default crypto provider.
71 ///
72 /// Caller owns the memory and must eventually call rustls_server_config_builder_build,
73 /// then free the resulting rustls_server_config.
74 ///
75 /// Alternatively, if an error occurs or, you don't wish to build a config, call
76 /// `rustls_server_config_builder_free` to free the builder directly.
77 ///
78 /// This uses the process default provider's values for the cipher suites and key exchange
79 /// groups, as well as safe defaults for protocol versions.
80 #[no_mangle]
81 pub extern "C" fn rustls_server_config_builder_new() -> *mut rustls_server_config_builder {
82 ffi_panic_boundary! {
83 let builder = ServerConfigBuilder {
84 provider: crypto_provider::get_default_or_install_from_crate_features(),
85 versions: rustls::DEFAULT_VERSIONS.to_vec(),
86 verifier: WebPkiClientVerifier::no_client_auth(),
87 cert_resolver: None,
88 session_storage: None,
89 alpn_protocols: vec![],
90 ignore_client_order: None,
91 key_log: None,
92 };
93 to_boxed_mut_ptr(builder)
94 }
95 }
96
97 /// Create a rustls_server_config_builder using the specified crypto provider.
98 ///
99 /// Caller owns the memory and must eventually call rustls_server_config_builder_build,
100 /// then free the resulting rustls_server_config.
101 ///
102 /// Alternatively, if an error occurs or, you don't wish to build a config, call
103 /// `rustls_server_config_builder_free` to free the builder directly.
104 ///
105 /// `tls_versions` set the TLS protocol versions to use when negotiating a TLS session.
106 ///
107 /// `tls_versions` is the version of the protocol, as defined in rfc8446,
108 /// ch. 4.2.1 and end of ch. 5.1. Some values are defined in
109 /// `rustls_tls_version` for convenience.
110 ///
111 /// `tls_versions` will only be used during the call and the application retains
112 /// ownership. `tls_versions_len` is the number of consecutive `uint16_t` pointed
113 /// to by `tls_versions`.
114 ///
115 /// Ciphersuites are configured separately via the crypto provider. See
116 /// `rustls_crypto_provider_builder_set_cipher_suites` for more information.
117 #[no_mangle]
118 pub extern "C" fn rustls_server_config_builder_new_custom(
119 provider: *const rustls_crypto_provider,
120 tls_versions: *const u16,
121 tls_versions_len: size_t,
122 builder_out: *mut *mut rustls_server_config_builder,
123 ) -> rustls_result {
124 ffi_panic_boundary! {
125 let provider = try_clone_arc!(provider);
126 let tls_versions = try_slice!(tls_versions, tls_versions_len);
127 let mut versions = vec![];
128 for version_number in tls_versions {
129 let proto = ProtocolVersion::from(*version_number);
130 if proto == rustls::version::TLS12.version {
131 versions.push(&rustls::version::TLS12);
132 } else if proto == rustls::version::TLS13.version {
133 versions.push(&rustls::version::TLS13);
134 }
135 }
136 let builder_out = try_mut_from_ptr_ptr!(builder_out);
137
138 let builder = ServerConfigBuilder {
139 provider: Some(provider),
140 versions,
141 verifier: WebPkiClientVerifier::no_client_auth(),
142 cert_resolver: None,
143 session_storage: None,
144 alpn_protocols: vec![],
145 ignore_client_order: None,
146 key_log: None,
147 };
148 set_boxed_mut_ptr(builder_out, builder);
149 rustls_result::Ok
150 }
151 }
152
153 /// Create a rustls_server_config_builder for TLS sessions that may verify client
154 /// certificates.
155 ///
156 /// This increases the refcount of `verifier` and doesn't take ownership.
157 #[no_mangle]
158 pub extern "C" fn rustls_server_config_builder_set_client_verifier(
159 builder: *mut rustls_server_config_builder,
160 verifier: *const rustls_client_cert_verifier,
161 ) {
162 ffi_panic_boundary! {
163 let builder = try_mut_from_ptr!(builder);
164 let verifier = try_ref_from_ptr!(verifier);
165 builder.verifier = verifier.clone();
166 }
167 }
168
169 /// Log key material to the file specified by the `SSLKEYLOGFILE` environment variable.
170 ///
171 /// The key material will be logged in the NSS key log format,
172 /// <https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format> and is
173 /// compatible with tools like Wireshark.
174 ///
175 /// Secrets logged in this manner are **extremely sensitive** and can break the security
176 /// of past, present and future sessions.
177 ///
178 /// For more control over which secrets are logged, or to customize the format, prefer
179 /// `rustls_server_config_builder_set_key_log`.
180 #[no_mangle]
181 pub extern "C" fn rustls_server_config_builder_set_key_log_file(
182 builder: *mut rustls_server_config_builder,
183 ) -> rustls_result {
184 ffi_panic_boundary! {
185 let builder = try_mut_from_ptr!(builder);
186 builder.key_log = Some(Arc::new(KeyLogFile::new()));
187 rustls_result::Ok
188 }
189 }
190
191 /// Provide callbacks to manage logging key material.
192 ///
193 /// The `log_cb` argument is mandatory and must not be `NULL` or a `NullParameter` error is
194 /// returned. The `log_cb` will be invoked with a `client_random` to identify the relevant session,
195 /// a `label` to identify the purpose of the `secret`, and the `secret` itself. See the
196 /// Rustls documentation of the `KeyLog` trait for more information on possible labels:
197 /// <https://docs.rs/rustls/latest/rustls/trait.KeyLog.html#tymethod.log>
198 ///
199 /// The `will_log_cb` may be `NULL`, in which case all key material will be provided to
200 /// the `log_cb`. By providing a custom `will_log_cb` you may return `0` for labels you don't
201 /// wish to log, and non-zero for labels you _do_ wish to log as a performance optimization.
202 ///
203 /// Both callbacks **must** be thread-safe. Arguments provided to the callback live only for as
204 /// long as the callback is executing and are not valid after the callback returns. The
205 /// callbacks must not retain references to the provided data.
206 ///
207 /// Secrets provided to the `log_cb` are **extremely sensitive** and can break the security
208 /// of past, present and future sessions.
209 ///
210 /// See also `rustls_server_config_builder_set_key_log_file` for a simpler way to log
211 /// to a file specified by the `SSLKEYLOGFILE` environment variable.
212 #[no_mangle]
213 pub extern "C" fn rustls_server_config_builder_set_key_log(
214 builder: *mut rustls_server_config_builder,
215 log_cb: rustls_keylog_log_callback,
216 will_log_cb: rustls_keylog_will_log_callback,
217 ) -> rustls_result {
218 ffi_panic_boundary! {
219 let builder = try_mut_from_ptr!(builder);
220 let log_cb = match log_cb {
221 Some(cb) => cb,
222 None => return rustls_result::NullParameter,
223 };
224
225 builder.key_log = Some(Arc::new(CallbackKeyLog {
226 log_cb,
227 will_log_cb,
228 }));
229
230 rustls_result::Ok
231 }
232 }
233
234 /// "Free" a server_config_builder without building it into a rustls_server_config.
235 ///
236 /// Normally builders are built into rustls_server_configs via `rustls_server_config_builder_build`
237 /// and may not be free'd or otherwise used afterwards.
238 ///
239 /// Use free only when the building of a config has to be aborted before a config
240 /// was created.
241 #[no_mangle]
242 pub extern "C" fn rustls_server_config_builder_free(config: *mut rustls_server_config_builder) {
243 ffi_panic_boundary! {
244 free_box(config);
245 }
246 }
247
248 /// With `ignore` != 0, the server will ignore the client ordering of cipher
249 /// suites, aka preference, during handshake and respect its own ordering
250 /// as configured.
251 /// <https://docs.rs/rustls/latest/rustls/struct.ServerConfig.html#structfield.ignore_client_order>
252 #[no_mangle]
253 pub extern "C" fn rustls_server_config_builder_set_ignore_client_order(
254 builder: *mut rustls_server_config_builder,
255 ignore: bool,
256 ) -> rustls_result {
257 ffi_panic_boundary! {
258 let config = try_mut_from_ptr!(builder);
259 config.ignore_client_order = Some(ignore);
260 rustls_result::Ok
261 }
262 }
263
264 /// Set the ALPN protocol list to the given protocols.
265 ///
266 /// `protocols` must point to a buffer of `rustls_slice_bytes` (built by the caller)
267 /// with `len` elements. Each element of the buffer must point to a slice of bytes that
268 /// contains a single ALPN protocol from
269 /// <https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids>.
270 ///
271 /// This function makes a copy of the data in `protocols` and does not retain
272 /// any pointers, so the caller can free the pointed-to memory after calling.
273 ///
274 /// <https://docs.rs/rustls/latest/rustls/server/struct.ServerConfig.html#structfield.alpn_protocols>
275 #[no_mangle]
276 pub extern "C" fn rustls_server_config_builder_set_alpn_protocols(
277 builder: *mut rustls_server_config_builder,
278 protocols: *const rustls_slice_bytes,
279 len: size_t,
280 ) -> rustls_result {
281 ffi_panic_boundary! {
282 let config = try_mut_from_ptr!(builder);
283 let protocols = try_slice!(protocols, len);
284
285 let mut vv = Vec::new();
286 for p in protocols {
287 let v = try_slice!(p.data, p.len);
288 vv.push(v.to_vec());
289 }
290 config.alpn_protocols = vv;
291 rustls_result::Ok
292 }
293 }
294
295 /// Provide the configuration a list of certificates where the connection
296 /// will select the first one that is compatible with the client's signature
297 /// verification capabilities.
298 ///
299 /// Servers that want to support both ECDSA and RSA certificates will want
300 /// the ECSDA to go first in the list.
301 ///
302 /// The built configuration will keep a reference to all certified keys
303 /// provided. The client may `rustls_certified_key_free()` afterwards
304 /// without the configuration losing them. The same certified key may also
305 /// be used in multiple configs.
306 ///
307 /// EXPERIMENTAL: installing a client_hello callback will replace any
308 /// configured certified keys and vice versa.
309 #[no_mangle]
310 pub extern "C" fn rustls_server_config_builder_set_certified_keys(
311 builder: *mut rustls_server_config_builder,
312 certified_keys: *const *const rustls_certified_key,
313 certified_keys_len: size_t,
314 ) -> rustls_result {
315 ffi_panic_boundary! {
316 let builder = try_mut_from_ptr!(builder);
317 let keys_ptrs = try_slice!(certified_keys, certified_keys_len);
318 let mut keys = Vec::new();
319 for &key_ptr in keys_ptrs {
320 let certified_key = try_clone_arc!(key_ptr);
321 keys.push(certified_key);
322 }
323 builder.cert_resolver = Some(Arc::new(ResolvesServerCertFromChoices::new(&keys)));
324 rustls_result::Ok
325 }
326 }
327
328 /// Turn a *rustls_server_config_builder (mutable) into a const *rustls_server_config
329 /// (read-only). The constructed `rustls_server_config` will be written to the `config_out`
330 /// pointer when this function returns `rustls_result::Ok`.
331 ///
332 /// This function may return an error if no process default crypto provider has been set
333 /// and the builder was constructed using `rustls_server_config_builder_new`, or if no
334 /// certificate resolver was set.
335 #[no_mangle]
336 pub extern "C" fn rustls_server_config_builder_build(
337 builder: *mut rustls_server_config_builder,
338 config_out: *mut *const rustls_server_config,
339 ) -> rustls_result {
340 ffi_panic_boundary! {
341 let builder = try_box_from_ptr!(builder);
342 let config_out = try_ref_from_ptr_ptr!(config_out);
343
344 let provider = match builder.provider {
345 Some(provider) => provider,
346 None => return rustls_result::NoDefaultCryptoProvider,
347 };
348
349 let base = match ServerConfig::builder_with_provider(provider)
350 .with_protocol_versions(&builder.versions)
351 {
352 Ok(base) => base,
353 Err(err) => return map_error(err),
354 }
355 .with_client_cert_verifier(builder.verifier);
356
357 let mut config = if let Some(r) = builder.cert_resolver {
358 base.with_cert_resolver(r)
359 } else {
360 return rustls_result::NoCertResolver;
361 };
362 if let Some(ss) = builder.session_storage {
363 config.session_storage = ss;
364 }
365 config.alpn_protocols = builder.alpn_protocols;
366 if let Some(ignore_client_order) = builder.ignore_client_order {
367 config.ignore_client_order = ignore_client_order;
368 }
369
370 if let Some(key_log) = builder.key_log {
371 config.key_log = key_log;
372 }
373
374 set_arc_mut_ptr(config_out, config);
375 rustls_result::Ok
376 }
377 }
378}
379
380impl rustls_server_config {
381 /// Returns true if a `rustls_connection` created from the `rustls_server_config` will
382 /// operate in FIPS mode.
383 ///
384 /// This is different from `rustls_crypto_provider_fips` which is concerned
385 /// only with cryptography, whereas this also covers TLS-level configuration that NIST
386 /// recommends, as well as ECH HPKE suites if applicable.
387 #[no_mangle]
388 pub extern "C" fn rustls_server_config_fips(config: *const rustls_server_config) -> bool {
389 ffi_panic_boundary! {
390 try_ref_from_ptr!(config).fips()
391 }
392 }
393
394 /// "Free" a rustls_server_config previously returned from
395 /// rustls_server_config_builder_build.
396 ///
397 /// Since rustls_server_config is actually an
398 /// atomically reference-counted pointer, extant server connections may still
399 /// hold an internal reference to the Rust object. However, C code must
400 /// consider this pointer unusable after "free"ing it.
401 /// Calling with NULL is fine. Must not be called twice with the same value.
402 #[no_mangle]
403 pub extern "C" fn rustls_server_config_free(config: *const rustls_server_config) {
404 ffi_panic_boundary! {
405 free_arc(config);
406 }
407 }
408
409 /// Create a new rustls_connection containing a server connection, and return it.
410 ///
411 /// It is returned in the output parameter `conn_out`.
412 ///
413 /// If this returns an error code, the memory pointed to by `conn_out` remains unchanged.
414 ///
415 /// If this returns a non-error, the memory pointed to by `conn_out` is modified to point
416 /// at a valid rustls_connection
417 ///
418 /// The caller now owns the rustls_connection and must call `rustls_connection_free` when
419 /// done with it.
420 #[no_mangle]
421 pub extern "C" fn rustls_server_connection_new(
422 config: *const rustls_server_config,
423 conn_out: *mut *mut rustls_connection,
424 ) -> rustls_result {
425 ffi_panic_boundary! {
426 if conn_out.is_null() {
427 return rustls_result::NullParameter;
428 }
429 let config = try_clone_arc!(config);
430 let conn_out = try_mut_from_ptr_ptr!(conn_out);
431
432 let server_connection = match ServerConnection::new(config) {
433 Ok(sc) => sc,
434 Err(e) => return map_error(e),
435 };
436 // We've succeeded. Put the server on the heap, and transfer ownership
437 // to the caller. After this point, we must return rustls_result::Ok so the
438 // caller knows it is responsible for this memory.
439 let c = Connection::from_server(server_connection);
440 set_boxed_mut_ptr(conn_out, c);
441 rustls_result::Ok
442 }
443 }
444}
445
446/// Returns a `rustls_str` reference to the server name sent by the client in a server name
447/// indication (SNI) extension.
448///
449/// The returned `rustls_str` is valid until the next mutating function call affecting the
450/// connection. A mutating function call is one where the first argument has type
451/// `struct rustls_connection *` (as opposed to `const struct rustls_connection *`). The caller
452/// does not need to free the `rustls_str`.
453///
454/// Returns a zero-length `rustls_str` if:
455///
456/// - the connection is not a server connection.
457/// - the connection is a server connection but the SNI extension in the client hello has not
458/// been processed during the handshake yet. Check `rustls_connection_is_handshaking`.
459/// - the SNI value contains null bytes.
460#[no_mangle]
461pub extern "C" fn rustls_server_connection_get_server_name(
462 conn: *const rustls_connection,
463) -> rustls_str<'static> {
464 ffi_panic_boundary! {
465 let Some(server_connection) = try_ref_from_ptr!(conn).as_server() else {
466 return rustls_str::default();
467 };
468 let Some(sni_hostname) = server_connection.server_name() else {
469 return rustls_str::default();
470 };
471 let res = rustls_str::try_from(sni_hostname).unwrap_or_default();
472 unsafe { res.into_static() }
473 }
474}
475
476/// Choose the server certificate to be used for a connection based on certificate
477/// type. Will pick the first CertfiedKey available that is suitable for
478/// the SignatureSchemes supported by the client.
479#[derive(Debug)]
480struct ResolvesServerCertFromChoices {
481 choices: Vec<Arc<CertifiedKey>>,
482}
483
484impl ResolvesServerCertFromChoices {
485 pub fn new(choices: &[Arc<CertifiedKey>]) -> Self {
486 ResolvesServerCertFromChoices {
487 choices: Vec::from(choices),
488 }
489 }
490}
491
492impl ResolvesServerCert for ResolvesServerCertFromChoices {
493 fn resolve(&self, client_hello: ClientHello) -> Option<Arc<CertifiedKey>> {
494 for key in self.choices.iter() {
495 if key
496 .key
497 .choose_scheme(client_hello.signature_schemes())
498 .is_some()
499 {
500 return Some(key.clone());
501 }
502 }
503 None
504 }
505}
506
507/// The TLS Client Hello information provided to a ClientHelloCallback function.
508///
509/// `server_name` is the value of the ServerNameIndication extension provided
510/// by the client. If the client did not send an SNI, the length of this
511/// `rustls_string` will be 0.
512///
513/// `signature_schemes` carries the values supplied by the client or, if the
514/// client did not send this TLS extension, the default schemes in the rustls library. See:
515/// <https://docs.rs/rustls/latest/rustls/internal/msgs/enums/enum.SignatureScheme.html>.
516///
517/// `alpn` carries the list of ALPN protocol names that the client proposed to
518/// the server. Again, the length of this list will be 0 if none were supplied.
519///
520/// All this data, when passed to a callback function, is only accessible during
521/// the call and may not be modified. Users of this API must copy any values that
522/// they want to access when the callback returned.
523///
524/// EXPERIMENTAL: this feature of rustls-ffi is likely to change in the future, as
525/// the rustls library is re-evaluating their current approach to client hello handling.
526#[repr(C)]
527pub struct rustls_client_hello<'a> {
528 server_name: rustls_str<'a>,
529 signature_schemes: rustls_slice_u16<'a>,
530 alpn: *const rustls_slice_slice_bytes<'a>,
531}
532
533impl<'a> Castable for rustls_client_hello<'a> {
534 type Ownership = OwnershipRef;
535 type RustType = rustls_client_hello<'a>;
536}
537
538/// Any context information the callback will receive when invoked.
539pub type rustls_client_hello_userdata = *mut c_void;
540
541/// Prototype of a callback that can be installed by the application at the
542/// `rustls_server_config`.
543///
544/// This callback will be invoked by a `rustls_connection` once the TLS client
545/// hello message has been received.
546///
547/// `userdata` will be set based on rustls_connection_set_userdata.
548///
549/// `hello` gives the value of the available client announcements, as interpreted
550/// by rustls. See the definition of `rustls_client_hello` for details.
551///
552/// NOTE:
553/// - the passed in `hello` and all its values are only available during the
554/// callback invocations.
555/// - the passed callback function must be safe to call multiple times concurrently
556/// with the same userdata, unless there is only a single config and connection
557/// where it is installed.
558///
559/// EXPERIMENTAL: this feature of rustls-ffi is likely to change in the future, as
560/// the rustls library is re-evaluating their current approach to client hello handling.
561pub type rustls_client_hello_callback = Option<
562 unsafe extern "C" fn(
563 userdata: rustls_client_hello_userdata,
564 hello: *const rustls_client_hello,
565 ) -> *const rustls_certified_key,
566>;
567
568// This is the same as a rustls_verify_server_cert_callback after unwrapping
569// the Option (which is equivalent to checking for null).
570type ClientHelloCallback = unsafe extern "C" fn(
571 userdata: rustls_client_hello_userdata,
572 hello: *const rustls_client_hello,
573) -> *const rustls_certified_key;
574
575struct ClientHelloResolver {
576 /// Implementation of rustls::ResolvesServerCert that passes values
577 /// from the supplied ClientHello to the callback function.
578 pub callback: ClientHelloCallback,
579}
580
581impl ClientHelloResolver {
582 pub fn new(callback: ClientHelloCallback) -> ClientHelloResolver {
583 ClientHelloResolver { callback }
584 }
585}
586
587impl ResolvesServerCert for ClientHelloResolver {
588 fn resolve(&self, client_hello: ClientHello) -> Option<Arc<CertifiedKey>> {
589 let server_name = client_hello.server_name().unwrap_or_default();
590 let server_name = match server_name.try_into() {
591 Ok(r) => r,
592 Err(_) => return None,
593 };
594 let mapped_sigs: Vec<u16> = client_hello
595 .signature_schemes()
596 .iter()
597 .map(|s| u16::from(*s))
598 .collect();
599 // Unwrap the Option. None becomes an empty slice.
600 let alpn = match client_hello.alpn() {
601 Some(iter) => iter.collect(),
602 None => vec![],
603 };
604
605 let alpn = rustls_slice_slice_bytes { inner: &alpn };
606 let signature_schemes = (&*mapped_sigs).into();
607 let hello = rustls_client_hello {
608 server_name,
609 signature_schemes,
610 alpn: &alpn,
611 };
612
613 let cb = self.callback;
614 let userdata = match userdata_get() {
615 Ok(u) => u,
616 Err(_) => return None,
617 };
618 let key_ptr = unsafe { cb(userdata, &hello) };
619 let certified_key = try_ref_from_ptr!(key_ptr);
620 Some(Arc::new(certified_key.clone()))
621 }
622}
623
624/// This struct can be considered thread safe, as long
625/// as the registered callbacks are thread safe. This is
626/// documented as a requirement in the API.
627unsafe impl Sync for ClientHelloResolver {}
628
629unsafe impl Send for ClientHelloResolver {}
630
631impl Debug for ClientHelloResolver {
632 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
633 f.debug_struct("ClientHelloResolver").finish()
634 }
635}
636
637impl rustls_server_config_builder {
638 /// Register a callback to be invoked when a connection created from this config
639 /// sees a TLS ClientHello message. If `userdata` has been set with
640 /// rustls_connection_set_userdata, it will be passed to the callback.
641 /// Otherwise the userdata param passed to the callback will be NULL.
642 ///
643 /// Any existing `ResolvesServerCert` implementation currently installed in the
644 /// `rustls_server_config` will be replaced. This also means registering twice
645 /// will overwrite the first registration. It is not permitted to pass a NULL
646 /// value for `callback`.
647 ///
648 /// EXPERIMENTAL: this feature of rustls-ffi is likely to change in the future, as
649 /// the rustls library is re-evaluating their current approach to client hello handling.
650 /// Installing a client_hello callback will replace any configured certified keys
651 /// and vice versa. Same holds true for the set_certified_keys variant.
652 #[no_mangle]
653 pub extern "C" fn rustls_server_config_builder_set_hello_callback(
654 builder: *mut rustls_server_config_builder,
655 callback: rustls_client_hello_callback,
656 ) -> rustls_result {
657 ffi_panic_boundary! {
658 let callback = match callback {
659 Some(cb) => cb,
660 None => return rustls_result::NullParameter,
661 };
662 let builder = try_mut_from_ptr!(builder);
663 builder.cert_resolver = Some(Arc::new(ClientHelloResolver::new(callback)));
664 rustls_result::Ok
665 }
666 }
667}
668
669// Turn a slice of u16's into a vec of SignatureScheme as needed by rustls.
670fn sigschemes(input: &[u16]) -> Vec<SignatureScheme> {
671 input.iter().copied().map(Into::into).collect()
672}
673
674/// Select a `rustls_certified_key` from the list that matches the cryptographic
675/// parameters of a TLS client hello.
676///
677/// Note that this does not do any SNI matching. The input certificates should
678/// already have been filtered to ones matching the SNI from the client hello.
679///
680/// This is intended for servers that are configured with several keys for the
681/// same domain name(s), for example ECDSA and RSA types. The presented keys are
682/// inspected in the order given and keys first in the list are given preference,
683/// all else being equal. However rustls is free to choose whichever it considers
684/// to be the best key with its knowledge about security issues and possible future
685/// extensions of the protocol.
686///
687/// Return RUSTLS_RESULT_OK if a key was selected and RUSTLS_RESULT_NOT_FOUND
688/// if none was suitable.
689#[no_mangle]
690pub extern "C" fn rustls_client_hello_select_certified_key(
691 hello: *const rustls_client_hello,
692 certified_keys: *const *const rustls_certified_key,
693 certified_keys_len: size_t,
694 out_key: *mut *const rustls_certified_key,
695) -> rustls_result {
696 ffi_panic_boundary! {
697 let hello = try_ref_from_ptr!(hello);
698 let schemes = sigschemes(try_slice!(
699 hello.signature_schemes.data,
700 hello.signature_schemes.len
701 ));
702 if out_key.is_null() {
703 return rustls_result::NullParameter;
704 }
705 let keys_ptrs = try_slice!(certified_keys, certified_keys_len);
706 for &key_ptr in keys_ptrs {
707 let key_ref = try_ref_from_ptr!(key_ptr);
708 if key_ref.key.choose_scheme(&schemes).is_some() {
709 unsafe {
710 *out_key = key_ptr;
711 }
712 return rustls_result::Ok;
713 }
714 }
715 rustls_result::NotFound
716 }
717}
718
719impl rustls_server_config_builder {
720 /// Register callbacks for persistence of TLS session IDs and secrets. Both
721 /// keys and values are highly sensitive data, containing enough information
722 /// to break the security of the connections involved.
723 ///
724 /// If `builder`, `get_cb`, or `put_cb` are NULL, this function will return
725 /// immediately without doing anything.
726 ///
727 /// If `userdata` has been set with rustls_connection_set_userdata, it
728 /// will be passed to the callbacks. Otherwise the userdata param passed to
729 /// the callbacks will be NULL.
730 #[no_mangle]
731 pub extern "C" fn rustls_server_config_builder_set_persistence(
732 builder: *mut rustls_server_config_builder,
733 get_cb: rustls_session_store_get_callback,
734 put_cb: rustls_session_store_put_callback,
735 ) {
736 ffi_panic_boundary! {
737 let Some(get_cb) = get_cb else {
738 return;
739 };
740 let Some(put_cb) = put_cb else {
741 return;
742 };
743
744 try_mut_from_ptr!(builder).session_storage =
745 Some(Arc::new(SessionStoreBroker::new(get_cb, put_cb)));
746 }
747 }
748}
749
750#[cfg(all(test, any(feature = "ring", feature = "aws-lc-rs")))]
751mod tests {
752 use std::ptr::{null, null_mut};
753
754 use super::*;
755
756 #[test]
757 #[cfg_attr(miri, ignore)]
758 fn test_config_builder() {
759 let builder = rustls_server_config_builder::rustls_server_config_builder_new();
760 let h1 = "http/1.1".as_bytes();
761 let h2 = "h2".as_bytes();
762 let alpn = [h1.into(), h2.into()];
763 rustls_server_config_builder::rustls_server_config_builder_set_alpn_protocols(
764 builder,
765 alpn.as_ptr(),
766 alpn.len(),
767 );
768
769 let cert_pem = include_str!("../testdata/localhost/cert.pem").as_bytes();
770 let key_pem = include_str!("../testdata/localhost/key.pem").as_bytes();
771 let mut certified_key = null();
772 let result = rustls_certified_key::rustls_certified_key_build(
773 cert_pem.as_ptr(),
774 cert_pem.len(),
775 key_pem.as_ptr(),
776 key_pem.len(),
777 &mut certified_key,
778 );
779 if !matches!(result, rustls_result::Ok) {
780 panic!(
781 "expected RUSTLS_RESULT_OK from rustls_certified_key_build, got {:?}",
782 result
783 );
784 }
785 rustls_server_config_builder::rustls_server_config_builder_set_certified_keys(
786 builder,
787 &certified_key,
788 1,
789 );
790
791 let mut config = null();
792 let result =
793 rustls_server_config_builder::rustls_server_config_builder_build(builder, &mut config);
794 assert_eq!(result, rustls_result::Ok);
795 assert!(!config.is_null());
796 {
797 let config2 = try_ref_from_ptr!(config);
798 assert_eq!(config2.alpn_protocols, vec![h1, h2]);
799 }
800 rustls_server_config::rustls_server_config_free(config);
801 }
802
803 // Build a server connection and test the getters and initial values.
804 #[test]
805 fn test_server_config_builder_new_empty() {
806 let builder = rustls_server_config_builder::rustls_server_config_builder_new();
807 // Building a config with no certificate and key configured results in an error.
808 let mut config = null();
809 let result =
810 rustls_server_config_builder::rustls_server_config_builder_build(builder, &mut config);
811 assert_eq!(result, rustls_result::NoCertResolver);
812 assert!(config.is_null());
813 }
814
815 #[test]
816 #[cfg_attr(miri, ignore)]
817 fn test_server_connection_new() {
818 let builder = rustls_server_config_builder::rustls_server_config_builder_new();
819 let cert_pem = include_str!("../testdata/localhost/cert.pem").as_bytes();
820 let key_pem = include_str!("../testdata/localhost/key.pem").as_bytes();
821 let mut certified_key = null();
822 let result = rustls_certified_key::rustls_certified_key_build(
823 cert_pem.as_ptr(),
824 cert_pem.len(),
825 key_pem.as_ptr(),
826 key_pem.len(),
827 &mut certified_key,
828 );
829 if !matches!(result, rustls_result::Ok) {
830 panic!(
831 "expected RUSTLS_RESULT_OK from rustls_certified_key_build, got {:?}",
832 result
833 );
834 }
835 rustls_server_config_builder::rustls_server_config_builder_set_certified_keys(
836 builder,
837 &certified_key,
838 1,
839 );
840
841 let mut config = null();
842 let result =
843 rustls_server_config_builder::rustls_server_config_builder_build(builder, &mut config);
844 assert_eq!(result, rustls_result::Ok);
845 assert!(!config.is_null());
846
847 let mut conn = null_mut();
848 let result = rustls_server_config::rustls_server_connection_new(config, &mut conn);
849 if !matches!(result, rustls_result::Ok) {
850 panic!("expected RUSTLS_RESULT_OK, got {:?}", result);
851 }
852 assert!(rustls_connection::rustls_connection_wants_read(conn));
853 assert!(!rustls_connection::rustls_connection_wants_write(conn));
854 assert!(rustls_connection::rustls_connection_is_handshaking(conn));
855
856 let some_byte = 42u8;
857 let mut alpn_protocol: *const u8 = &some_byte;
858 let mut alpn_protocol_len = 1;
859 rustls_connection::rustls_connection_get_alpn_protocol(
860 conn,
861 &mut alpn_protocol,
862 &mut alpn_protocol_len,
863 );
864 assert_eq!(alpn_protocol, null());
865 assert_eq!(alpn_protocol_len, 0);
866
867 assert_eq!(
868 rustls_connection::rustls_connection_get_negotiated_ciphersuite(conn),
869 0
870 );
871 let cs_name = rustls_connection::rustls_connection_get_negotiated_ciphersuite_name(conn);
872 assert_eq!(unsafe { cs_name.to_str() }, "");
873 assert_eq!(
874 rustls_connection::rustls_connection_get_peer_certificate(conn, 0),
875 null()
876 );
877
878 assert_eq!(
879 rustls_connection::rustls_connection_get_protocol_version(conn),
880 0
881 );
882 rustls_connection::rustls_connection_free(conn);
883 }
884}