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