s2n_tls/
config.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4#[cfg(feature = "unstable-renegotiate")]
5use crate::renegotiate::RenegotiateCallback;
6use crate::{
7    callbacks::*,
8    cert_chain::CertificateChain,
9    enums::*,
10    error::{Error, ErrorType, Fallible},
11    security,
12};
13use core::{convert::TryInto, ptr::NonNull};
14use s2n_tls_sys::*;
15use std::{
16    ffi::{c_void, CString},
17    path::Path,
18    pin::Pin,
19    sync::atomic::{AtomicUsize, Ordering},
20    task::Poll,
21    time::{Duration, SystemTime},
22};
23
24/// Corresponds to [s2n_config].
25#[derive(Debug, PartialEq)]
26pub struct Config(NonNull<s2n_config>);
27
28/// # Safety
29///
30/// Safety: s2n_config objects can be sent across threads
31unsafe impl Send for Config {}
32
33/// # Safety
34///
35/// Safety: All C methods that mutate the s2n_config are wrapped
36/// in Rust methods that require a mutable reference.
37unsafe impl Sync for Config {}
38
39impl Config {
40    /// Returns a Config object with pre-defined defaults.
41    ///
42    /// Use the [`Builder`] if custom configuration is desired.
43    ///
44    /// # Warning
45    ///
46    /// The newly created Config will use the default security policy.
47    /// Consider changing this depending on your security and compatibility requirements
48    /// by using [`Builder`] and [`Builder::set_security_policy`].
49    /// See the s2n-tls usage guide:
50    /// <https://aws.github.io/s2n-tls/usage-guide/ch06-security-policies.html>
51    pub fn new() -> Self {
52        Self::default()
53    }
54
55    /// Returns a Builder which can be used to configure the Config
56    pub fn builder() -> Builder {
57        Builder::default()
58    }
59
60    /// # Safety
61    ///
62    /// This config _MUST_ have been initialized with a [`Builder`].
63    /// Additionally, this does NOT increment the config reference count,
64    /// so consider cloning the result if the source pointer is still
65    /// valid and usable afterwards.
66    pub(crate) unsafe fn from_raw(config: NonNull<s2n_config>) -> Self {
67        let config = Self(config);
68
69        // Check if the context can be retrieved.
70        // If it can't, this is not a valid config.
71        config.context();
72
73        config
74    }
75
76    pub(crate) fn as_mut_ptr(&mut self) -> *mut s2n_config {
77        self.0.as_ptr()
78    }
79
80    /// Retrieve a reference to the [`Context`] stored on the config.
81    ///
82    /// Corresponds to [s2n_config_get_ctx].
83    pub(crate) fn context(&self) -> &Context {
84        let mut ctx = core::ptr::null_mut();
85        unsafe {
86            s2n_config_get_ctx(self.0.as_ptr(), &mut ctx)
87                .into_result()
88                .unwrap();
89            &*(ctx as *const Context)
90        }
91    }
92
93    /// Retrieve a mutable reference to the [`Context`] stored on the config.
94    ///
95    /// Corresponds to [s2n_config_get_ctx].
96    ///
97    /// SAFETY: There must only ever by mutable reference to `Context` alive at
98    ///         any time. Configs can be shared across threads, so this method is
99    ///         likely not correct for your usecase.
100    pub(crate) unsafe fn context_mut(&mut self) -> &mut Context {
101        let mut ctx = core::ptr::null_mut();
102        s2n_config_get_ctx(self.as_mut_ptr(), &mut ctx)
103            .into_result()
104            .unwrap();
105        &mut *(ctx as *mut Context)
106    }
107
108    #[cfg(test)]
109    /// Get the refcount associated with the config
110    pub fn test_get_refcount(&self) -> Result<usize, Error> {
111        let context = self.context();
112        Ok(context.refcount.load(Ordering::SeqCst))
113    }
114}
115
116impl Default for Config {
117    fn default() -> Self {
118        Builder::new().build().unwrap()
119    }
120}
121
122impl Clone for Config {
123    fn clone(&self) -> Self {
124        let context = self.context();
125
126        // Safety
127        //
128        // Using a relaxed ordering is alright here, as knowledge of the
129        // original reference prevents other threads from erroneously deleting
130        // the object.
131        // https://github.com/rust-lang/rust/blob/e012a191d768adeda1ee36a99ef8b92d51920154/library/alloc/src/sync.rs#L1329
132        let _count = context.refcount.fetch_add(1, Ordering::Relaxed);
133        Self(self.0)
134    }
135}
136
137impl Drop for Config {
138    /// Corresponds to [s2n_config_free].
139    fn drop(&mut self) {
140        let context = self.context();
141        let count = context.refcount.fetch_sub(1, Ordering::Release);
142        debug_assert!(count > 0, "refcount should not drop below 1 instance");
143
144        // only free the config if this is the last instance
145        if count != 1 {
146            return;
147        }
148
149        // Safety
150        //
151        // The use of Ordering and fence mirrors the `Arc` implementation in
152        // the standard library.
153        //
154        // This fence is needed to prevent reordering of use of the data and
155        // deletion of the data.  Because it is marked `Release`, the decreasing
156        // of the reference count synchronizes with this `Acquire` fence. This
157        // means that use of the data happens before decreasing the reference
158        // count, which happens before this fence, which happens before the
159        // deletion of the data.
160        // https://github.com/rust-lang/rust/blob/e012a191d768adeda1ee36a99ef8b92d51920154/library/alloc/src/sync.rs#L1637
161        std::sync::atomic::fence(Ordering::Acquire);
162
163        // This is the last instance so free the context.
164        let context = unsafe {
165            // SAFETY: The reference count is verified to be 1, so this is the
166            // last instance of the config, and the only reference to the context.
167            Box::from_raw(self.context_mut())
168        };
169        drop(context);
170
171        let _ = unsafe { s2n_config_free(self.0.as_ptr()).into_result() };
172    }
173}
174
175pub struct Builder {
176    pub(crate) config: Config,
177    load_system_certs: bool,
178    enable_ocsp: bool,
179}
180
181impl Builder {
182    /// # Warning
183    ///
184    /// The newly created Builder will create Configs that use the default security policy.
185    /// Consider changing this depending on your security and compatibility requirements
186    /// by calling [`Builder::set_security_policy`].
187    /// See the s2n-tls usage guide:
188    /// <https://aws.github.io/s2n-tls/usage-guide/ch06-security-policies.html>
189    ///
190    /// Corresponds to [s2n_config_new_minimal],
191    /// but also calls [s2n_config_set_client_hello_cb_mode] to set the client
192    /// hello callback to non-blocking mode.
193    pub fn new() -> Self {
194        crate::init::init();
195        let config = unsafe { s2n_config_new_minimal().into_result() }.unwrap();
196
197        let context = Box::<Context>::default();
198        let context = Box::into_raw(context) as *mut c_void;
199
200        unsafe {
201            s2n_config_set_ctx(config.as_ptr(), context)
202                .into_result()
203                .unwrap();
204
205            // The client hello callback originally did not support async operations,
206            // so defaults to blocking mode for backwards compatibility with old integrations.
207            // But these bindings use a polling model, so assume non-blocking mode.
208            s2n_config_set_client_hello_cb_mode(
209                config.as_ptr(),
210                s2n_client_hello_cb_mode::NONBLOCKING,
211            )
212            .into_result()
213            .unwrap();
214        }
215
216        Self {
217            config: Config(config),
218            load_system_certs: true,
219            enable_ocsp: false,
220        }
221    }
222
223    /// Corresponds to [s2n_config_set_alert_behavior].
224    pub fn set_alert_behavior(&mut self, value: AlertBehavior) -> Result<&mut Self, Error> {
225        unsafe { s2n_config_set_alert_behavior(self.as_mut_ptr(), value.into()).into_result() }?;
226        Ok(self)
227    }
228
229    /// Corresponds to [s2n_config_set_cipher_preferences].
230    pub fn set_security_policy(&mut self, policy: &security::Policy) -> Result<&mut Self, Error> {
231        unsafe {
232            s2n_config_set_cipher_preferences(self.as_mut_ptr(), policy.as_cstr().as_ptr())
233                .into_result()
234        }?;
235        Ok(self)
236    }
237
238    /// sets the application protocol preferences on an s2n_config object.
239    ///
240    /// protocols is a list in order of preference, with most preferred protocol first,
241    /// and of length protocol_count. When acting as a client the protocol list is
242    /// included in the Client Hello message as the ALPN extension. As a server, the
243    /// list is used to negotiate a mutual application protocol with the client. After
244    /// the negotiation for the connection has completed, the agreed upon protocol can
245    /// be retrieved with s2n_get_application_protocol
246    ///
247    /// Corresponds to [s2n_config_set_protocol_preferences].
248    pub fn set_application_protocol_preference<P: IntoIterator<Item = I>, I: AsRef<[u8]>>(
249        &mut self,
250        protocols: P,
251    ) -> Result<&mut Self, Error> {
252        // reset the list
253        unsafe {
254            s2n_config_set_protocol_preferences(self.as_mut_ptr(), core::ptr::null(), 0)
255                .into_result()
256        }?;
257
258        for protocol in protocols {
259            self.append_application_protocol_preference(protocol.as_ref())?;
260        }
261
262        Ok(self)
263    }
264
265    /// Corresponds to [s2n_config_append_protocol_preference].
266    pub fn append_application_protocol_preference(
267        &mut self,
268        protocol: &[u8],
269    ) -> Result<&mut Self, Error> {
270        unsafe {
271            s2n_config_append_protocol_preference(
272                self.as_mut_ptr(),
273                protocol.as_ptr(),
274                protocol
275                    .len()
276                    .try_into()
277                    .map_err(|_| Error::INVALID_INPUT)?,
278            )
279            .into_result()
280        }?;
281        Ok(self)
282    }
283
284    /// Turns off x509 verification
285    ///
286    /// # Safety
287    /// This functionality will weaken the security of the connections. As such, it should only
288    /// be used in development environments where obtaining a valid certificate would not be possible.
289    ///
290    /// Corresponds to [s2n_config_disable_x509_verification].
291    pub unsafe fn disable_x509_verification(&mut self) -> Result<&mut Self, Error> {
292        s2n_config_disable_x509_verification(self.as_mut_ptr()).into_result()?;
293        Ok(self)
294    }
295
296    /// Corresponds to [s2n_config_add_dhparams].
297    pub fn add_dhparams(&mut self, pem: &[u8]) -> Result<&mut Self, Error> {
298        let cstring = CString::new(pem).map_err(|_| Error::INVALID_INPUT)?;
299        unsafe { s2n_config_add_dhparams(self.as_mut_ptr(), cstring.as_ptr()).into_result() }?;
300        Ok(self)
301    }
302
303    /// Associate a `certificate` and corresponding `private_key` with a config.
304    /// Using this method, at most one certificate per auth type (ECDSA, RSA, RSA-PSS)
305    /// can be loaded.
306    ///
307    /// For more advanced cert use cases such as sharing certs across configs or
308    /// serving different certs based on the client SNI, see [Builder::load_chain].
309    ///
310    /// Corresponds to [s2n_config_add_cert_chain_and_key].
311    pub fn load_pem(&mut self, certificate: &[u8], private_key: &[u8]) -> Result<&mut Self, Error> {
312        let certificate = CString::new(certificate).map_err(|_| Error::INVALID_INPUT)?;
313        let private_key = CString::new(private_key).map_err(|_| Error::INVALID_INPUT)?;
314        unsafe {
315            s2n_config_add_cert_chain_and_key(
316                self.as_mut_ptr(),
317                certificate.as_ptr(),
318                private_key.as_ptr(),
319            )
320            .into_result()
321        }?;
322        Ok(self)
323    }
324
325    /// Corresponds to [s2n_config_add_cert_chain_and_key_to_store].
326    pub fn load_chain(&mut self, chain: CertificateChain<'static>) -> Result<&mut Self, Error> {
327        // Out of an abundance of caution, we hold a reference to the CertificateChain
328        // regardless of whether add_to_store fails or succeeds. We have limited
329        // visibility into the failure modes, so this behavior ensures that _if_
330        // the C library held the reference despite the failure, it would continue
331        // to be valid memory.
332        let result = unsafe {
333            s2n_config_add_cert_chain_and_key_to_store(
334                self.as_mut_ptr(),
335                // SAFETY: audit of add_to_store shows that the certificate chain
336                // is not mutated. https://github.com/aws/s2n-tls/issues/4140
337                chain.as_ptr() as *mut _,
338            )
339            .into_result()
340        };
341        let context = unsafe {
342            // SAFETY: usage of context_mut is safe in the builder, because the
343            // Builder owns the only reference to the config.
344            self.config.context_mut()
345        };
346        context.application_owned_certs.push(chain);
347        result?;
348
349        Ok(self)
350    }
351
352    /// Corresponds to [s2n_config_set_cert_chain_and_key_defaults].
353    pub fn set_default_chains<T: IntoIterator<Item = CertificateChain<'static>>>(
354        &mut self,
355        chains: T,
356    ) -> Result<&mut Self, Error> {
357        // Must be equal to S2N_CERT_TYPE_COUNT in s2n_certificate.h.
358        const CHAINS_MAX_COUNT: usize = 3;
359
360        let mut chain_arrays: [Option<CertificateChain<'static>>; CHAINS_MAX_COUNT] =
361            [None, None, None];
362        let mut pointer_array = [std::ptr::null_mut(); CHAINS_MAX_COUNT];
363        let mut cert_chain_count = 0;
364
365        for chain in chains.into_iter() {
366            if cert_chain_count >= CHAINS_MAX_COUNT {
367                return Err(Error::bindings(
368                    ErrorType::UsageError,
369                    "InvalidInput",
370                    "A single default can be specified for RSA, ECDSA, 
371                    and RSA-PSS auth types, but more than 3 certs were supplied",
372                ));
373            }
374
375            // SAFETY: manual inspection of set_defaults shows that certificates
376            // are not mutated. https://github.com/aws/s2n-tls/issues/4140
377            pointer_array[cert_chain_count] = chain.as_ptr() as *mut _;
378            chain_arrays[cert_chain_count] = Some(chain);
379
380            cert_chain_count += 1;
381        }
382
383        let collected_chains = chain_arrays.into_iter().take(cert_chain_count).flatten();
384
385        let context = unsafe {
386            // SAFETY: usage of context_mut is safe in the builder, because the
387            // Builder owns the only reference to the config.
388            self.config.context_mut()
389        };
390        context.application_owned_certs.extend(collected_chains);
391
392        unsafe {
393            s2n_config_set_cert_chain_and_key_defaults(
394                self.as_mut_ptr(),
395                pointer_array.as_mut_ptr(),
396                cert_chain_count as u32,
397            )
398            .into_result()
399        }?;
400
401        Ok(self)
402    }
403
404    /// Corresponds to [s2n_config_add_cert_chain].
405    pub fn load_public_pem(&mut self, certificate: &[u8]) -> Result<&mut Self, Error> {
406        let size: u32 = certificate
407            .len()
408            .try_into()
409            .map_err(|_| Error::INVALID_INPUT)?;
410        let certificate = certificate.as_ptr() as *mut u8;
411        unsafe { s2n_config_add_cert_chain(self.as_mut_ptr(), certificate, size) }.into_result()?;
412        Ok(self)
413    }
414
415    /// Corresponds to [s2n_config_add_pem_to_trust_store].
416    pub fn trust_pem(&mut self, certificate: &[u8]) -> Result<&mut Self, Error> {
417        let certificate = CString::new(certificate).map_err(|_| Error::INVALID_INPUT)?;
418        unsafe {
419            s2n_config_add_pem_to_trust_store(self.as_mut_ptr(), certificate.as_ptr()).into_result()
420        }?;
421        Ok(self)
422    }
423
424    /// Adds to the trust store from a CA file or directory containing trusted certificates.
425    ///
426    /// Corresponds to [s2n_config_set_verification_ca_location], except it
427    /// calls [s2n_config_set_status_request_type] with NONE to avoid
428    /// automatically enabling OCSP stapling.
429    pub fn trust_location(
430        &mut self,
431        file: Option<&Path>,
432        dir: Option<&Path>,
433    ) -> Result<&mut Self, Error> {
434        fn to_cstr(input: Option<&Path>) -> Result<Option<CString>, Error> {
435            Ok(match input {
436                Some(input) => {
437                    let string = input.to_str().ok_or(Error::INVALID_INPUT)?;
438                    let cstring = CString::new(string).map_err(|_| Error::INVALID_INPUT)?;
439                    Some(cstring)
440                }
441                None => None,
442            })
443        }
444
445        let file_cstr = to_cstr(file)?;
446        let file_ptr = file_cstr
447            .as_ref()
448            .map(|f| f.as_ptr())
449            .unwrap_or(core::ptr::null());
450
451        let dir_cstr = to_cstr(dir)?;
452        let dir_ptr = dir_cstr
453            .as_ref()
454            .map(|f| f.as_ptr())
455            .unwrap_or(core::ptr::null());
456
457        unsafe {
458            s2n_config_set_verification_ca_location(self.as_mut_ptr(), file_ptr, dir_ptr)
459                .into_result()
460        }?;
461
462        // If OCSP has not been explicitly requested, turn off OCSP. This is to prevent this function from
463        // automatically enabling `OCSP` due to the legacy behavior of `s2n_config_set_verification_ca_location`
464        if !self.enable_ocsp {
465            unsafe {
466                s2n_config_set_status_request_type(self.as_mut_ptr(), s2n_status_request_type::NONE)
467                    .into_result()?
468            };
469        }
470
471        Ok(self)
472    }
473
474    /// Sets whether or not default system certificates will be loaded into the trust store.
475    ///
476    /// Set to false for increased performance if system certificates are not needed during
477    /// certificate validation.
478    ///
479    /// Corresponds to [s2n_config_load_system_certs].
480    pub fn with_system_certs(&mut self, load_system_certs: bool) -> Result<&mut Self, Error> {
481        self.load_system_certs = load_system_certs;
482        Ok(self)
483    }
484
485    /// Corresponds to [s2n_config_wipe_trust_store].
486    pub fn wipe_trust_store(&mut self) -> Result<&mut Self, Error> {
487        unsafe { s2n_config_wipe_trust_store(self.as_mut_ptr()).into_result()? };
488        Ok(self)
489    }
490
491    /// Sets whether or not a client certificate should be required to complete the TLS connection.
492    ///
493    /// See the [Usage Guide](https://github.com/aws/s2n-tls/blob/main/docs/USAGE-GUIDE.md#client-auth-related-calls) for more details.
494    ///
495    /// Corresponds to [s2n_config_set_client_auth_type].
496    pub fn set_client_auth_type(&mut self, auth_type: ClientAuthType) -> Result<&mut Self, Error> {
497        unsafe {
498            s2n_config_set_client_auth_type(self.as_mut_ptr(), auth_type.into()).into_result()
499        }?;
500        Ok(self)
501    }
502
503    /// Clients will request OCSP stapling from the server.
504    ///
505    /// Corresponds to [s2n_config_set_status_request_type].
506    pub fn enable_ocsp(&mut self) -> Result<&mut Self, Error> {
507        unsafe {
508            s2n_config_set_status_request_type(self.as_mut_ptr(), s2n_status_request_type::OCSP)
509                .into_result()
510        }?;
511        self.enable_ocsp = true;
512        Ok(self)
513    }
514
515    /// Sets the OCSP data for the default certificate chain associated with the Config.
516    ///
517    /// Servers will send the data in response to OCSP stapling requests from clients.
518    ///
519    /// Corresponds to [s2n_config_set_extension_data] with OCSP_STAPLING.
520    //
521    // NOTE: this modifies a certificate chain, NOT the Config itself. This is currently safe
522    // because the certificate chain is set with s2n_config_add_cert_chain_and_key, which
523    // creates a new certificate chain only accessible by the given config. It will
524    // NOT be safe when we add support for the newer s2n_config_add_cert_chain_and_key_to_store API,
525    // which allows certificate chains to be shared across configs.
526    // In that case, we'll need additional guard rails either in these bindings or in the underlying C.
527    pub fn set_ocsp_data(&mut self, data: &[u8]) -> Result<&mut Self, Error> {
528        let size: u32 = data.len().try_into().map_err(|_| Error::INVALID_INPUT)?;
529        unsafe {
530            s2n_config_set_extension_data(
531                self.as_mut_ptr(),
532                s2n_tls_extension_type::OCSP_STAPLING,
533                data.as_ptr(),
534                size,
535            )
536            .into_result()
537        }?;
538        self.enable_ocsp()
539    }
540
541    /// Sets the callback to use for verifying that a hostname from an X.509 certificate is
542    /// trusted.
543    ///
544    /// The callback may be called more than once during certificate validation as each SAN on
545    /// the certificate will be checked.
546    ///
547    /// Corresponds to [s2n_config_set_verify_host_callback].
548    pub fn set_verify_host_callback<T: 'static + VerifyHostNameCallback>(
549        &mut self,
550        handler: T,
551    ) -> Result<&mut Self, Error> {
552        unsafe extern "C" fn verify_host_cb_fn(
553            host_name: *const ::libc::c_char,
554            host_name_len: usize,
555            context: *mut ::libc::c_void,
556        ) -> u8 {
557            let context = &mut *(context as *mut Context);
558            let handler = context.verify_host_callback.as_mut().unwrap();
559            verify_host(host_name, host_name_len, handler)
560        }
561
562        let context = unsafe {
563            // SAFETY: usage of context_mut is safe in the builder, because the
564            // Builder owns the only reference to the config.
565            self.config.context_mut()
566        };
567        context.verify_host_callback = Some(Box::new(handler));
568        unsafe {
569            s2n_config_set_verify_host_callback(
570                self.as_mut_ptr(),
571                Some(verify_host_cb_fn),
572                self.config.context() as *const Context as *mut c_void,
573            )
574            .into_result()?;
575        }
576        Ok(self)
577    }
578
579    /// # Safety
580    /// THIS SHOULD BE USED FOR DEBUGGING PURPOSES ONLY!
581    /// The `context` pointer must live at least as long as the config
582    ///
583    /// Corresponds to [s2n_config_set_key_log_cb].
584    pub unsafe fn set_key_log_callback(
585        &mut self,
586        callback: s2n_key_log_fn,
587        context: *mut core::ffi::c_void,
588    ) -> Result<&mut Self, Error> {
589        s2n_config_set_key_log_cb(self.as_mut_ptr(), callback, context).into_result()?;
590        Ok(self)
591    }
592
593    /// Corresponds to [s2n_config_set_max_cert_chain_depth].
594    pub fn set_max_cert_chain_depth(&mut self, depth: u16) -> Result<&mut Self, Error> {
595        unsafe { s2n_config_set_max_cert_chain_depth(self.as_mut_ptr(), depth).into_result() }?;
596        Ok(self)
597    }
598
599    /// Corresponds to [s2n_config_set_send_buffer_size].
600    pub fn set_send_buffer_size(&mut self, size: u32) -> Result<&mut Self, Error> {
601        unsafe { s2n_config_set_send_buffer_size(self.as_mut_ptr(), size).into_result() }?;
602        Ok(self)
603    }
604
605    /// Set a custom callback function which is run after parsing the client hello.
606    ///
607    /// Corresponds to [s2n_config_set_client_hello_cb].
608    pub fn set_client_hello_callback<T: 'static + ClientHelloCallback>(
609        &mut self,
610        handler: T,
611    ) -> Result<&mut Self, Error> {
612        unsafe extern "C" fn client_hello_cb(
613            connection_ptr: *mut s2n_connection,
614            _context: *mut core::ffi::c_void,
615        ) -> libc::c_int {
616            with_context(connection_ptr, |conn, context| {
617                let callback = context.client_hello_callback.as_ref();
618                let future = callback
619                    .map(|c| c.on_client_hello(conn))
620                    .unwrap_or(Ok(None));
621                AsyncCallback::trigger_client_hello_cb(future, conn)
622            })
623            .into()
624        }
625
626        let handler = Box::new(handler);
627        let context = unsafe {
628            // SAFETY: usage of context_mut is safe in the builder, because while
629            // it is being built, the Builder is the only reference to the config.
630            self.config.context_mut()
631        };
632        context.client_hello_callback = Some(handler);
633
634        unsafe {
635            s2n_config_set_client_hello_cb(
636                self.as_mut_ptr(),
637                Some(client_hello_cb),
638                core::ptr::null_mut(),
639            )
640            .into_result()?;
641        }
642
643        Ok(self)
644    }
645
646    pub fn set_connection_initializer<T: 'static + ConnectionInitializer>(
647        &mut self,
648        handler: T,
649    ) -> Result<&mut Self, Error> {
650        // Store callback in config context
651        let handler = Box::new(handler);
652        let context = unsafe {
653            // SAFETY: usage of context_mut is safe in the builder, because while
654            // it is being built, the Builder is the only reference to the config.
655            self.config.context_mut()
656        };
657        context.connection_initializer = Some(handler);
658        Ok(self)
659    }
660
661    /// Sets a custom callback which provides access to session tickets when they arrive
662    ///
663    /// Corresponds to [s2n_config_set_session_ticket_cb].
664    pub fn set_session_ticket_callback<T: 'static + SessionTicketCallback>(
665        &mut self,
666        handler: T,
667    ) -> Result<&mut Self, Error> {
668        // enable session tickets automatically
669        self.enable_session_tickets(true)?;
670
671        // Define C callback function that can be set on the s2n_config struct
672        unsafe extern "C" fn session_ticket_cb(
673            conn_ptr: *mut s2n_connection,
674            _context: *mut ::libc::c_void,
675            session_ticket: *mut s2n_session_ticket,
676        ) -> libc::c_int {
677            let session_ticket = SessionTicket::from_ptr(&*session_ticket);
678            with_context(conn_ptr, |conn, context| {
679                let callback = context.session_ticket_callback.as_ref();
680                callback.map(|c| c.on_session_ticket(conn, session_ticket))
681            });
682            CallbackResult::Success.into()
683        }
684
685        // Store callback in context
686        let handler = Box::new(handler);
687        let context = unsafe {
688            // SAFETY: usage of context_mut is safe in the builder, because while
689            // it is being built, the Builder is the only reference to the config.
690            self.config.context_mut()
691        };
692        context.session_ticket_callback = Some(handler);
693
694        unsafe {
695            s2n_config_set_session_ticket_cb(
696                self.as_mut_ptr(),
697                Some(session_ticket_cb),
698                self.config.context() as *const Context as *mut c_void,
699            )
700            .into_result()
701        }?;
702        Ok(self)
703    }
704
705    /// Set a callback function triggered by operations requiring the private key.
706    ///
707    /// See https://github.com/aws/s2n-tls/blob/main/docs/USAGE-GUIDE.md#private-key-operation-related-calls
708    ///
709    /// Corresponds to [s2n_config_set_async_pkey_callback].
710    pub fn set_private_key_callback<T: 'static + PrivateKeyCallback>(
711        &mut self,
712        handler: T,
713    ) -> Result<&mut Self, Error> {
714        unsafe extern "C" fn private_key_cb(
715            conn_ptr: *mut s2n_connection,
716            op_ptr: *mut s2n_async_pkey_op,
717        ) -> libc::c_int {
718            with_context(conn_ptr, |conn, context| {
719                let state = PrivateKeyOperation::try_from_cb(conn, op_ptr);
720                let callback = context.private_key_callback.as_ref();
721                let future_result = state.and_then(|state| {
722                    callback.map_or(Ok(None), |callback| callback.handle_operation(conn, state))
723                });
724                AsyncCallback::trigger(future_result, conn)
725            })
726            .into()
727        }
728
729        let handler = Box::new(handler);
730        let context = unsafe {
731            // SAFETY: usage of context_mut is safe in the builder, because while
732            // it is being built, the Builder is the only reference to the config.
733            self.config.context_mut()
734        };
735        context.private_key_callback = Some(handler);
736
737        unsafe {
738            s2n_config_set_async_pkey_callback(self.as_mut_ptr(), Some(private_key_cb))
739                .into_result()?;
740        }
741        Ok(self)
742    }
743
744    /// Set a callback function that will be used to get the system time.
745    ///
746    /// The wall clock time is the best-guess at the real time, measured since the epoch.
747    /// Unlike monotonic time, it CAN move backwards.
748    /// It is used by s2n-tls for timestamps.
749    ///
750    /// Corresponds to [s2n_config_set_wall_clock].
751    pub fn set_wall_clock<T: 'static + WallClock>(
752        &mut self,
753        handler: T,
754    ) -> Result<&mut Self, Error> {
755        unsafe extern "C" fn clock_cb(
756            context: *mut ::libc::c_void,
757            time_in_nanos: *mut u64,
758        ) -> libc::c_int {
759            let context = &mut *(context as *mut Context);
760            if let Some(handler) = context.wall_clock.as_mut() {
761                if let Ok(nanos) = handler.get_time_since_epoch().as_nanos().try_into() {
762                    *time_in_nanos = nanos;
763                    return CallbackResult::Success.into();
764                }
765            }
766            CallbackResult::Failure.into()
767        }
768
769        let handler = Box::new(handler);
770        let context = unsafe {
771            // SAFETY: usage of context_mut is safe in the builder, because while
772            // it is being built, the Builder is the only reference to the config.
773            self.config.context_mut()
774        };
775        context.wall_clock = Some(handler);
776        unsafe {
777            s2n_config_set_wall_clock(
778                self.as_mut_ptr(),
779                Some(clock_cb),
780                self.config.context() as *const _ as *mut c_void,
781            )
782            .into_result()?;
783        }
784        Ok(self)
785    }
786
787    /// Set a callback function that will be used to get the monotonic time.
788    ///
789    /// The monotonic time is the time since an arbitrary, unspecified point.
790    /// Unlike wall clock time, it MUST never move backwards.
791    /// It is used by s2n-tls for timers.
792    ///
793    /// Corresponds to [s2n_config_set_monotonic_clock].
794    pub fn set_monotonic_clock<T: 'static + MonotonicClock>(
795        &mut self,
796        handler: T,
797    ) -> Result<&mut Self, Error> {
798        unsafe extern "C" fn clock_cb(
799            context: *mut ::libc::c_void,
800            time_in_nanos: *mut u64,
801        ) -> libc::c_int {
802            let context = &mut *(context as *mut Context);
803            if let Some(handler) = context.monotonic_clock.as_mut() {
804                if let Ok(nanos) = handler.get_time().as_nanos().try_into() {
805                    *time_in_nanos = nanos;
806                    return CallbackResult::Success.into();
807                }
808            }
809            CallbackResult::Failure.into()
810        }
811
812        let handler = Box::new(handler);
813        let context = unsafe {
814            // SAFETY: usage of context_mut is safe in the builder, because while
815            // it is being built, the Builder is the only reference to the config.
816            self.config.context_mut()
817        };
818        context.monotonic_clock = Some(handler);
819        unsafe {
820            s2n_config_set_monotonic_clock(
821                self.as_mut_ptr(),
822                Some(clock_cb),
823                self.config.context() as *const _ as *mut c_void,
824            )
825            .into_result()?;
826        }
827        Ok(self)
828    }
829
830    /// Enable negotiating session tickets in a TLS connection
831    ///
832    /// Corresponds to [s2n_config_set_session_tickets_onoff].
833    pub fn enable_session_tickets(&mut self, enable: bool) -> Result<&mut Self, Error> {
834        unsafe {
835            s2n_config_set_session_tickets_onoff(self.as_mut_ptr(), enable.into()).into_result()
836        }?;
837        Ok(self)
838    }
839
840    /// Adds a key which will be used to encrypt and decrypt session tickets. The intro_time parameter is time since
841    /// the Unix epoch (Midnight, January 1st, 1970). The key must be at least 16 bytes.
842    ///
843    /// Corresponds to [s2n_config_add_ticket_crypto_key], but also
844    /// automatically calls [Builder::enable_session_tickets].
845    pub fn add_session_ticket_key(
846        &mut self,
847        key_name: &[u8],
848        key: &[u8],
849        intro_time: SystemTime,
850    ) -> Result<&mut Self, Error> {
851        let key_name_len: u32 = key_name
852            .len()
853            .try_into()
854            .map_err(|_| Error::INVALID_INPUT)?;
855        let key_len: u32 = key.len().try_into().map_err(|_| Error::INVALID_INPUT)?;
856        let intro_time = intro_time
857            .duration_since(std::time::UNIX_EPOCH)
858            .map_err(|_| Error::INVALID_INPUT)?;
859        // Ticket keys should be at least 128 bits in strength
860        // https://www.rfc-editor.org/rfc/rfc5077#section-5.5
861        if key_len < 16 {
862            return Err(Error::INVALID_INPUT);
863        }
864        self.enable_session_tickets(true)?;
865        unsafe {
866            s2n_config_add_ticket_crypto_key(
867                self.as_mut_ptr(),
868                key_name.as_ptr(),
869                key_name_len,
870                // s2n-tls doesn't mutate key, it's just mut for easier use with stuffers and blobs
871                key.as_ptr() as *mut u8,
872                key_len,
873                intro_time.as_secs(),
874            )
875            .into_result()
876        }?;
877        Ok(self)
878    }
879
880    // Sets how long a session ticket key will be able to be used for both encryption
881    // and decryption of tickets
882    ///
883    /// Corresponds to [s2n_config_set_ticket_encrypt_decrypt_key_lifetime].
884    pub fn set_ticket_key_encrypt_decrypt_lifetime(
885        &mut self,
886        lifetime: Duration,
887    ) -> Result<&mut Self, Error> {
888        unsafe {
889            s2n_config_set_ticket_encrypt_decrypt_key_lifetime(
890                self.as_mut_ptr(),
891                lifetime.as_secs(),
892            )
893            .into_result()
894        }?;
895        Ok(self)
896    }
897
898    // Sets how long a session ticket key will be able to be used for only decryption
899    ///
900    /// Corresponds to [s2n_config_set_ticket_decrypt_key_lifetime].
901    pub fn set_ticket_key_decrypt_lifetime(
902        &mut self,
903        lifetime: Duration,
904    ) -> Result<&mut Self, Error> {
905        unsafe {
906            s2n_config_set_ticket_decrypt_key_lifetime(self.as_mut_ptr(), lifetime.as_secs())
907                .into_result()
908        }?;
909        Ok(self)
910    }
911
912    /// Sets the expected connection serialization version. Must be set
913    /// before serializing the connection.
914    ///
915    /// Corresponds to [s2n_config_set_serialization_version].
916    pub fn set_serialization_version(
917        &mut self,
918        version: SerializationVersion,
919    ) -> Result<&mut Self, Error> {
920        unsafe {
921            s2n_config_set_serialization_version(self.as_mut_ptr(), version.into()).into_result()
922        }?;
923        Ok(self)
924    }
925
926    /// Sets a configurable blinding delay instead of the default
927    ///
928    /// Corresponds to [s2n_config_set_max_blinding_delay].
929    pub fn set_max_blinding_delay(&mut self, seconds: u32) -> Result<&mut Self, Error> {
930        unsafe { s2n_config_set_max_blinding_delay(self.as_mut_ptr(), seconds).into_result() }?;
931        Ok(self)
932    }
933
934    /// Requires that session tickets are only used when forward secrecy is possible
935    ///
936    /// Corresponds to [s2n_config_require_ticket_forward_secrecy].
937    pub fn require_ticket_forward_secrecy(&mut self, enable: bool) -> Result<&mut Self, Error> {
938        unsafe {
939            s2n_config_require_ticket_forward_secrecy(self.as_mut_ptr(), enable).into_result()
940        }?;
941        Ok(self)
942    }
943
944    pub fn build(mut self) -> Result<Config, Error> {
945        if self.load_system_certs {
946            unsafe {
947                s2n_config_load_system_certs(self.as_mut_ptr()).into_result()?;
948            }
949        }
950
951        Ok(self.config)
952    }
953
954    pub(crate) fn as_mut_ptr(&mut self) -> *mut s2n_config {
955        self.config.as_mut_ptr()
956    }
957}
958
959#[cfg(feature = "quic")]
960impl Builder {
961    /// Corresponds to [s2n_config_enable_quic].
962    pub fn enable_quic(&mut self) -> Result<&mut Self, Error> {
963        unsafe { s2n_tls_sys::s2n_config_enable_quic(self.as_mut_ptr()).into_result() }?;
964        Ok(self)
965    }
966}
967
968/// # Warning
969///
970/// The newly created Builder uses the default security policy.
971/// Consider changing this depending on your security and compatibility requirements
972/// by using [`Builder::new`] instead and calling [`Builder::set_security_policy`].
973/// See the s2n-tls usage guide:
974/// <https://aws.github.io/s2n-tls/usage-guide/ch06-security-policies.html>
975impl Default for Builder {
976    fn default() -> Self {
977        Self::new()
978    }
979}
980
981pub(crate) struct Context {
982    refcount: AtomicUsize,
983    /// This is a container for reference counts.
984    ///
985    /// In the bindings, application owned certificate chains are reference counted.
986    /// The C library is not aware of the reference counts, so a naive implementation
987    /// would result in certs being prematurely freed because the "reference"
988    /// held by the C library wouldn't be accounted for.
989    ///
990    /// Storing the CertificateChains in this Vec ensures that reference counts
991    /// behave as expected when stored in an s2n-tls config.
992    application_owned_certs: Vec<CertificateChain<'static>>,
993    pub(crate) client_hello_callback: Option<Box<dyn ClientHelloCallback>>,
994    pub(crate) private_key_callback: Option<Box<dyn PrivateKeyCallback>>,
995    pub(crate) verify_host_callback: Option<Box<dyn VerifyHostNameCallback>>,
996    pub(crate) session_ticket_callback: Option<Box<dyn SessionTicketCallback>>,
997    pub(crate) connection_initializer: Option<Box<dyn ConnectionInitializer>>,
998    pub(crate) wall_clock: Option<Box<dyn WallClock>>,
999    pub(crate) monotonic_clock: Option<Box<dyn MonotonicClock>>,
1000    #[cfg(feature = "unstable-renegotiate")]
1001    pub(crate) renegotiate: Option<Box<dyn RenegotiateCallback>>,
1002}
1003
1004impl Default for Context {
1005    fn default() -> Self {
1006        // The AtomicUsize is used to manually track the reference count of the Config.
1007        // This mechanism is used to track when the Config object should be freed.
1008        let refcount = AtomicUsize::new(1);
1009
1010        Self {
1011            refcount,
1012            application_owned_certs: Vec::new(),
1013            client_hello_callback: None,
1014            private_key_callback: None,
1015            verify_host_callback: None,
1016            session_ticket_callback: None,
1017            connection_initializer: None,
1018            wall_clock: None,
1019            monotonic_clock: None,
1020            #[cfg(feature = "unstable-renegotiate")]
1021            renegotiate: None,
1022        }
1023    }
1024}
1025
1026/// A trait executed asynchronously before a new connection negotiates TLS.
1027///
1028/// Used for dynamic configuration of a specific connection.
1029///
1030/// # Safety: This trait is polled to completion at the beginning of the
1031/// [connection::poll_negotiate](`crate::connection::poll_negotiate()`) function.
1032/// Therefore, negotiation of the TLS connection will not begin until the Future has completed.
1033pub trait ConnectionInitializer: 'static + Send + Sync {
1034    /// The application can return an `Ok(None)` to resolve the callback
1035    /// synchronously or return an `Ok(Some(ConnectionFuture))` if it wants to
1036    /// run some asynchronous task before resolving the callback.
1037    fn initialize_connection(
1038        &self,
1039        connection: &mut crate::connection::Connection,
1040    ) -> ConnectionFutureResult;
1041}
1042
1043impl<A: ConnectionInitializer, B: ConnectionInitializer> ConnectionInitializer for (A, B) {
1044    fn initialize_connection(
1045        &self,
1046        connection: &mut crate::connection::Connection,
1047    ) -> ConnectionFutureResult {
1048        let a = self.0.initialize_connection(connection)?;
1049        let b = self.1.initialize_connection(connection)?;
1050        match (a, b) {
1051            (None, None) => Ok(None),
1052            (None, Some(fut)) => Ok(Some(fut)),
1053            (Some(fut), None) => Ok(Some(fut)),
1054            (Some(fut_a), Some(fut_b)) => Ok(Some(Box::pin(ConcurrentConnectionFuture::new([
1055                fut_a, fut_b,
1056            ])))),
1057        }
1058    }
1059}
1060
1061struct ConcurrentConnectionFuture<const N: usize> {
1062    futures: [Option<Pin<Box<dyn ConnectionFuture>>>; N],
1063}
1064
1065impl<const N: usize> ConcurrentConnectionFuture<N> {
1066    fn new(futures: [Pin<Box<dyn ConnectionFuture>>; N]) -> Self {
1067        let futures = futures.map(Some);
1068        Self { futures }
1069    }
1070}
1071
1072impl<const N: usize> ConnectionFuture for ConcurrentConnectionFuture<N> {
1073    fn poll(
1074        mut self: std::pin::Pin<&mut Self>,
1075        connection: &mut crate::connection::Connection,
1076        ctx: &mut core::task::Context,
1077    ) -> std::task::Poll<Result<(), Error>> {
1078        let mut is_pending = false;
1079        for container in self.futures.iter_mut() {
1080            if let Some(future) = container.as_mut() {
1081                match future.as_mut().poll(connection, ctx) {
1082                    Poll::Ready(result) => {
1083                        result?;
1084                        *container = None;
1085                    }
1086                    Poll::Pending => is_pending = true,
1087                }
1088            }
1089        }
1090        if is_pending {
1091            Poll::Pending
1092        } else {
1093            Poll::Ready(Ok(()))
1094        }
1095    }
1096}
1097
1098#[cfg(test)]
1099mod tests {
1100    use super::*;
1101
1102    // ensure the config context is send and sync
1103    #[test]
1104    fn context_send_sync_test() {
1105        fn assert_send_sync<T: 'static + Send + Sync>() {}
1106        assert_send_sync::<Context>();
1107    }
1108}