rustls_ffi/
client.rs

1use std::ffi::CStr;
2use std::fmt::{Debug, Formatter};
3use std::slice;
4use std::sync::Arc;
5
6use libc::{c_char, size_t};
7use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
8use rustls::client::{EchConfig, EchGreaseConfig, EchMode, ResolvesClientCert};
9use rustls::crypto::{CryptoProvider, verify_tls12_signature, verify_tls13_signature};
10use rustls::pki_types::{CertificateDer, EchConfigListBytes, ServerName, UnixTime};
11use rustls::{
12    ClientConfig, ClientConnection, DigitallySignedStruct, Error, KeyLog, KeyLogFile,
13    ProtocolVersion, SignatureScheme, SupportedProtocolVersion, sign::CertifiedKey,
14};
15
16use crate::certificate::rustls_certified_key;
17use crate::connection::{Connection, rustls_connection};
18use crate::crypto_provider::{self, rustls_crypto_provider, rustls_hpke};
19use crate::error::{self, map_error, rustls_result};
20use crate::ffi::{
21    arc_castable, box_castable, free_arc, free_box, set_arc_mut_ptr, set_boxed_mut_ptr,
22    to_boxed_mut_ptr, try_box_from_ptr, try_clone_arc, try_mut_from_ptr, try_mut_from_ptr_ptr,
23    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::NulByte;
28use crate::rslice::{rustls_slice_bytes, rustls_slice_slice_bytes, rustls_str};
29use crate::userdata::userdata_get;
30use crate::verifier::rustls_server_cert_verifier;
31
32box_castable! {
33    /// A client config being constructed.
34    ///
35    /// A builder can be modified by, e.g. `rustls_client_config_builder_load_roots_from_file`.
36    /// Once you're done configuring settings, call `rustls_client_config_builder_build`
37    /// to turn it into a *rustls_client_config.
38    ///
39    /// Alternatively, if an error occurs or, you don't wish to build a config,
40    /// call `rustls_client_config_builder_free` to free the builder directly.
41    ///
42    /// This object is not safe for concurrent mutation. Under the hood,
43    /// it corresponds to a `Box<ClientConfig>`.
44    /// <https://docs.rs/rustls/latest/rustls/struct.ConfigBuilder.html>
45    pub struct rustls_client_config_builder(ClientConfigBuilder);
46}
47
48pub(crate) struct ClientConfigBuilder {
49    provider: Option<Arc<CryptoProvider>>,
50    versions: Vec<&'static SupportedProtocolVersion>,
51    verifier: Option<Arc<dyn ServerCertVerifier>>,
52    alpn_protocols: Vec<Vec<u8>>,
53    enable_sni: bool,
54    cert_resolver: Option<Arc<dyn ResolvesClientCert>>,
55    key_log: Option<Arc<dyn KeyLog>>,
56    ech_mode: Option<EchMode>,
57}
58
59impl Default for ClientConfigBuilder {
60    fn default() -> Self {
61        Self {
62            provider: None,
63            // Note: we populate default versions at build-time with the Rustls defaults.
64            // We leave it empty for the default builder to be able to distinguish when
65            // a caller has customized w/ `rustls_client_config_builder_new_custom`.
66            versions: Vec::new(),
67            verifier: None,
68            alpn_protocols: Vec::new(),
69            // Note: we don't derive Default for this struct because we want to enable SNI
70            // by default.
71            enable_sni: true,
72            cert_resolver: None,
73            key_log: None,
74            ech_mode: None,
75        }
76    }
77}
78
79arc_castable! {
80    /// A client config that is done being constructed and is now read-only.
81    ///
82    /// Under the hood, this object corresponds to an `Arc<ClientConfig>`.
83    /// <https://docs.rs/rustls/latest/rustls/struct.ClientConfig.html>
84    pub struct rustls_client_config(ClientConfig);
85}
86
87impl rustls_client_config_builder {
88    /// Create a rustls_client_config_builder using the process default crypto provider.
89    ///
90    /// Caller owns the memory and must eventually call `rustls_client_config_builder_build`,
91    /// then free the resulting `rustls_client_config`.
92    ///
93    /// Alternatively, if an error occurs or, you don't wish to build a config,
94    /// call `rustls_client_config_builder_free` to free the builder directly.
95    ///
96    /// This uses the process default provider's values for the cipher suites and key
97    /// exchange groups, as well as safe defaults for protocol versions.
98    ///
99    /// This starts out with no trusted roots. Caller must add roots with
100    /// rustls_client_config_builder_load_roots_from_file or provide a custom verifier.
101    #[no_mangle]
102    pub extern "C" fn rustls_client_config_builder_new() -> *mut rustls_client_config_builder {
103        ffi_panic_boundary! {
104            let builder = ClientConfigBuilder {
105                provider: crypto_provider::get_default_or_install_from_crate_features(),
106                ..ClientConfigBuilder::default()
107            };
108            to_boxed_mut_ptr(builder)
109        }
110    }
111
112    /// Create a rustls_client_config_builder using the specified crypto provider.
113    ///
114    /// Caller owns the memory and must eventually call `rustls_client_config_builder_build`,
115    /// then free the resulting `rustls_client_config`.
116    ///
117    /// Alternatively, if an error occurs or, you don't wish to build a config,
118    /// call `rustls_client_config_builder_free` to free the builder directly.
119    ///
120    /// `tls_version` sets the TLS protocol versions to use when negotiating a TLS session.
121    /// `tls_version` is the version of the protocol, as defined in rfc8446,
122    /// ch. 4.2.1 and end of ch. 5.1. Some values are defined in
123    /// `rustls_tls_version` for convenience, and the arrays
124    /// RUSTLS_DEFAULT_VERSIONS or RUSTLS_ALL_VERSIONS can be used directly.
125    ///
126    /// `tls_versions` will only be used during the call and the application retains
127    /// ownership. `tls_versions_len` is the number of consecutive `uint16_t`
128    /// pointed to by `tls_versions`.
129    ///
130    /// Ciphersuites are configured separately via the crypto provider. See
131    /// `rustls_crypto_provider_builder_set_cipher_suites` for more information.
132    #[no_mangle]
133    pub extern "C" fn rustls_client_config_builder_new_custom(
134        provider: *const rustls_crypto_provider,
135        tls_versions: *const u16,
136        tls_versions_len: size_t,
137        builder_out: *mut *mut rustls_client_config_builder,
138    ) -> rustls_result {
139        ffi_panic_boundary! {
140            let provider = try_clone_arc!(provider);
141            let tls_versions = try_slice!(tls_versions, tls_versions_len);
142            let mut versions = vec![];
143            for version_number in tls_versions {
144                let proto = ProtocolVersion::from(*version_number);
145                if proto == rustls::version::TLS12.version {
146                    versions.push(&rustls::version::TLS12);
147                } else if proto == rustls::version::TLS13.version {
148                    versions.push(&rustls::version::TLS13);
149                }
150            }
151            let builder_out = try_mut_from_ptr_ptr!(builder_out);
152
153            let config_builder = ClientConfigBuilder {
154                provider: Some(provider),
155                versions,
156                ..ClientConfigBuilder::default()
157            };
158
159            set_boxed_mut_ptr(builder_out, config_builder);
160            rustls_result::Ok
161        }
162    }
163
164    /// Set a custom server certificate verifier using the builder crypto provider.
165    /// Returns rustls_result::NoDefaultCryptoProvider if no process default crypto
166    /// provider has been set, and the builder was not constructed with an explicit
167    /// provider choice.
168    ///
169    /// The callback must not capture any of the pointers in its
170    /// rustls_verify_server_cert_params.
171    /// If `userdata` has been set with rustls_connection_set_userdata, it
172    /// will be passed to the callback. Otherwise the userdata param passed to
173    /// the callback will be NULL.
174    ///
175    /// The callback must be safe to call on any thread at any time, including
176    /// multiple concurrent calls. So, for instance, if the callback mutates
177    /// userdata (or other shared state), it must use synchronization primitives
178    /// to make such mutation safe.
179    ///
180    /// The callback receives certificate chain information as raw bytes.
181    /// Currently this library offers no functions to parse the certificates,
182    /// so you'll need to bring your own certificate parsing library
183    /// if you need to parse them.
184    ///
185    /// If the custom verifier accepts the certificate, it should return
186    /// RUSTLS_RESULT_OK. Otherwise, it may return any other rustls_result error.
187    /// Feel free to use an appropriate error from the RUSTLS_RESULT_CERT_*
188    /// section.
189    ///
190    /// <https://docs.rs/rustls/latest/rustls/client/struct.DangerousClientConfig.html#method.set_certificate_verifier>
191    #[no_mangle]
192    pub extern "C" fn rustls_client_config_builder_dangerous_set_certificate_verifier(
193        config_builder: *mut rustls_client_config_builder,
194        callback: rustls_verify_server_cert_callback,
195    ) -> rustls_result {
196        ffi_panic_boundary! {
197            let config_builder = try_mut_from_ptr!(config_builder);
198            let callback = match callback {
199                Some(cb) => cb,
200                None => return rustls_result::InvalidParameter,
201            };
202
203            let provider = match &config_builder.provider {
204                Some(provider) => provider.clone(),
205                None => return rustls_result::NoDefaultCryptoProvider,
206            };
207
208            config_builder.verifier = Some(Arc::new(Verifier { provider, callback }));
209            rustls_result::Ok
210        }
211    }
212
213    /// Configure the server certificate verifier.
214    ///
215    /// This increases the reference count of `verifier` and does not take ownership.
216    #[no_mangle]
217    pub extern "C" fn rustls_client_config_builder_set_server_verifier(
218        builder: *mut rustls_client_config_builder,
219        verifier: *const rustls_server_cert_verifier,
220    ) {
221        ffi_panic_boundary! {
222            let builder = try_mut_from_ptr!(builder);
223            builder.verifier = Some(try_ref_from_ptr!(verifier).clone());
224        }
225    }
226
227    /// Set the ALPN protocol list to the given protocols.
228    ///
229    /// `protocols` must point to a buffer of `rustls_slice_bytes` (built by the caller) with `len`
230    /// elements.
231    ///
232    /// Each element of the buffer must be a rustls_slice_bytes whose
233    /// data field points to a single ALPN protocol ID.
234    ///
235    /// Standard ALPN protocol IDs are defined at
236    /// <https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids>.
237    ///
238    /// This function makes a copy of the data in `protocols` and does not retain
239    /// any pointers, so the caller can free the pointed-to memory after calling.
240    ///
241    /// <https://docs.rs/rustls/latest/rustls/client/struct.ClientConfig.html#structfield.alpn_protocols>
242    #[no_mangle]
243    pub extern "C" fn rustls_client_config_builder_set_alpn_protocols(
244        builder: *mut rustls_client_config_builder,
245        protocols: *const rustls_slice_bytes,
246        len: size_t,
247    ) -> rustls_result {
248        ffi_panic_boundary! {
249            let config = try_mut_from_ptr!(builder);
250            let protocols = try_slice!(protocols, len);
251
252            let mut vv = Vec::with_capacity(protocols.len());
253            for p in protocols {
254                let v = try_slice!(p.data, p.len);
255                vv.push(v.to_vec());
256            }
257            config.alpn_protocols = vv;
258            rustls_result::Ok
259        }
260    }
261
262    /// Enable or disable SNI.
263    /// <https://docs.rs/rustls/latest/rustls/struct.ClientConfig.html#structfield.enable_sni>
264    #[no_mangle]
265    pub extern "C" fn rustls_client_config_builder_set_enable_sni(
266        config: *mut rustls_client_config_builder,
267        enable: bool,
268    ) {
269        ffi_panic_boundary! {
270            let config = try_mut_from_ptr!(config);
271            config.enable_sni = enable;
272        }
273    }
274
275    /// Provide the configuration a list of certificates where the connection
276    /// will select the first one that is compatible with the server's signature
277    /// verification capabilities.
278    ///
279    /// Clients that want to support both ECDSA and RSA certificates will want the
280    /// ECSDA to go first in the list.
281    ///
282    /// The built configuration will keep a reference to all certified keys
283    /// provided. The client may `rustls_certified_key_free()` afterwards
284    /// without the configuration losing them. The same certified key may also
285    /// be used in multiple configs.
286    ///
287    /// EXPERIMENTAL: installing a client authentication callback will replace any
288    /// configured certified keys and vice versa.
289    #[no_mangle]
290    pub extern "C" fn rustls_client_config_builder_set_certified_key(
291        builder: *mut rustls_client_config_builder,
292        certified_keys: *const *const rustls_certified_key,
293        certified_keys_len: size_t,
294    ) -> rustls_result {
295        ffi_panic_boundary! {
296            let config = try_mut_from_ptr!(builder);
297            let keys_ptrs = try_slice!(certified_keys, certified_keys_len);
298            let mut keys = Vec::new();
299            for &key_ptr in keys_ptrs {
300                let certified_key = try_clone_arc!(key_ptr);
301                keys.push(certified_key);
302            }
303            config.cert_resolver = Some(Arc::new(ResolvesClientCertFromChoices { keys }));
304            rustls_result::Ok
305        }
306    }
307
308    /// Log key material to the file specified by the `SSLKEYLOGFILE` environment variable.
309    ///
310    /// The key material will be logged in the NSS key log format,
311    /// <https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format> and is
312    /// compatible with tools like Wireshark.
313    ///
314    /// Secrets logged in this manner are **extremely sensitive** and can break the security
315    /// of past, present and future sessions.
316    ///
317    /// For more control over which secrets are logged, or to customize the format, prefer
318    /// `rustls_client_config_builder_set_key_log`.
319    #[no_mangle]
320    pub extern "C" fn rustls_client_config_builder_set_key_log_file(
321        builder: *mut rustls_client_config_builder,
322    ) -> rustls_result {
323        ffi_panic_boundary! {
324            let builder = try_mut_from_ptr!(builder);
325            builder.key_log = Some(Arc::new(KeyLogFile::new()));
326            rustls_result::Ok
327        }
328    }
329
330    /// Provide callbacks to manage logging key material.
331    ///
332    /// The `log_cb` argument is mandatory and must not be `NULL` or a `NullParameter` error is
333    /// returned. The `log_cb` will be invoked with a `client_random` to identify the relevant session,
334    /// a `label` to identify the purpose of the `secret`, and the `secret` itself. See the
335    /// Rustls documentation of the `KeyLog` trait for more information on possible labels:
336    /// <https://docs.rs/rustls/latest/rustls/trait.KeyLog.html#tymethod.log>
337    ///
338    /// The `will_log_cb` may be `NULL`, in which case all key material will be provided to
339    /// the `log_cb`. By providing a custom `will_log_cb` you may return `0` for labels you don't
340    /// wish to log, and non-zero for labels you _do_ wish to log as a performance optimization.
341    ///
342    /// Both callbacks **must** be thread-safe. Arguments provided to the callback live only for as
343    /// long as the callback is executing and are not valid after the callback returns. The
344    /// callbacks must not retain references to the provided data.
345    ///
346    /// Secrets provided to the `log_cb` are **extremely sensitive** and can break the security
347    /// of past, present and future sessions.
348    ///
349    /// See also `rustls_client_config_builder_set_key_log_file` for a simpler way to log
350    /// to a file specified by the `SSLKEYLOGFILE` environment variable.
351    #[no_mangle]
352    pub extern "C" fn rustls_client_config_builder_set_key_log(
353        builder: *mut rustls_client_config_builder,
354        log_cb: rustls_keylog_log_callback,
355        will_log_cb: rustls_keylog_will_log_callback,
356    ) -> rustls_result {
357        ffi_panic_boundary! {
358            let builder = try_mut_from_ptr!(builder);
359            let log_cb = match log_cb {
360                Some(cb) => cb,
361                None => return rustls_result::NullParameter,
362            };
363
364            builder.key_log = Some(Arc::new(CallbackKeyLog {
365                log_cb,
366                will_log_cb,
367            }));
368
369            rustls_result::Ok
370        }
371    }
372
373    /// Configure the client for Encrypted Client Hello (ECH).
374    ///
375    /// This requires providing a TLS encoded list of ECH configurations that should
376    /// have been retrieved from the DNS HTTPS record for the domain you intend to connect to.
377    /// This should be done using DNS-over-HTTPS to avoid leaking the domain name you are
378    /// connecting to ahead of the TLS handshake.
379    ///
380    /// At least one of the ECH configurations must be compatible with the provided `rustls_hpke`
381    /// instance. See `rustls_supported_hpke()` for more information.
382    ///
383    /// Calling this function will replace any existing ECH configuration set by
384    /// previous calls to `rustls_client_config_builder_enable_ech()` or
385    /// `rustls_client_config_builder_enable_ech_grease()`.
386    ///
387    /// The provided `ech_config_list_bytes` and `rustls_hpke` must not be NULL or an
388    /// error will be returned. The caller maintains ownership of the ECH config list TLS bytes
389    /// and `rustls_hpke` instance. This function does not retain any reference to
390    /// `ech_config_list_bytes`.
391    ///
392    /// A `RUSTLS_RESULT_BUILDER_INCOMPATIBLE_TLS_VERSIONS` error is returned if the builder's
393    /// TLS versions have been customized via `rustls_client_config_builder_new_custom()`
394    /// and the customization isn't "only TLS 1.3". ECH may only be used with TLS 1.3.
395    #[no_mangle]
396    pub extern "C" fn rustls_client_config_builder_enable_ech(
397        builder: *mut rustls_client_config_builder,
398        ech_config_list_bytes: *const u8,
399        ech_config_list_bytes_size: size_t,
400        hpke: *const rustls_hpke,
401    ) -> rustls_result {
402        ffi_panic_boundary! {
403            let builder = try_mut_from_ptr!(builder);
404            let ech_config_list_bytes =
405                try_slice!(ech_config_list_bytes, ech_config_list_bytes_size);
406            let hpke = try_ref_from_ptr!(hpke);
407
408            // If the builder's TLS versions have been customized, and the customization
409            // isn't "only TLS 1.3", return an error.
410            if !builder.versions.is_empty() && builder.versions != [&rustls::version::TLS13] {
411                return rustls_result::BuilderIncompatibleTlsVersions;
412            }
413
414            // Construct an ECH config given the config list DER and our supported suites, or an
415            // error result if the ECH config is no good, or we don't have an HPKE suite that's
416            // compatible with any of the ECH configs in the list.
417            builder.ech_mode = match EchConfig::new(
418                EchConfigListBytes::from(ech_config_list_bytes),
419                hpke.suites,
420            ) {
421                Ok(ech_config) => Some(ech_config.into()),
422                Err(err) => {
423                    return map_error(err);
424                }
425            };
426
427            rustls_result::Ok
428        }
429    }
430
431    /// Configure the client for GREASE Encrypted Client Hello (ECH).
432    ///
433    /// This is a feature to prevent ossification of the TLS handshake by acting as though
434    /// ECH were configured for an imaginary ECH config generated with one of the
435    /// `rustls_hpke` supported suites, chosen at random.
436    ///
437    /// The provided `rustls_client_config_builder` and `rustls_hpke` must not be NULL or an
438    /// error will be returned. The caller maintains ownership of both the
439    /// `rustls_client_config_builder` and the `rustls_hpke` instance.
440    ///
441    /// Calling this function will replace any existing ECH configuration set by
442    /// previous calls to `rustls_client_config_builder_enable_ech()` or
443    /// `rustls_client_config_builder_enable_ech_grease()`.
444    ///
445    /// A `RUSTLS_RESULT_BUILDER_INCOMPATIBLE_TLS_VERSIONS` error is returned if the builder's
446    /// TLS versions have been customized via `rustls_client_config_builder_new_custom()`
447    /// and the customization isn't "only TLS 1.3". ECH may only be used with TLS 1.3.
448    #[no_mangle]
449    pub extern "C" fn rustls_client_config_builder_enable_ech_grease(
450        builder: *mut rustls_client_config_builder,
451        hpke: *const rustls_hpke,
452    ) -> rustls_result {
453        ffi_panic_boundary! {
454            let builder = try_mut_from_ptr!(builder);
455            let hpke = try_ref_from_ptr!(hpke);
456
457            let provider = match &builder.provider {
458                Some(provider) => provider,
459                None => return rustls_result::NoDefaultCryptoProvider,
460            };
461
462            let Some((suite, placeholder_pk)) = hpke.grease_public_key(provider) else {
463                return rustls_result::HpkeError;
464            };
465
466            // If the builder's TLS versions have been customized, and the customization
467            // isn't "only TLS 1.3", return an error.
468            if !builder.versions.is_empty() && builder.versions != [&rustls::version::TLS13] {
469                return rustls_result::BuilderIncompatibleTlsVersions;
470            }
471            builder.ech_mode = Some(EchMode::Grease(EchGreaseConfig::new(suite, placeholder_pk)));
472
473            rustls_result::Ok
474        }
475    }
476
477    /// Turn a *rustls_client_config_builder (mutable) into a const *rustls_client_config
478    /// (read-only).
479    #[no_mangle]
480    pub extern "C" fn rustls_client_config_builder_build(
481        builder: *mut rustls_client_config_builder,
482        config_out: *mut *const rustls_client_config,
483    ) -> rustls_result {
484        ffi_panic_boundary! {
485            let builder = try_box_from_ptr!(builder);
486            let config_out = try_ref_from_ptr_ptr!(config_out);
487
488            let provider = match builder.provider {
489                Some(provider) => provider,
490                None => return rustls_result::NoDefaultCryptoProvider,
491            };
492
493            let verifier = match builder.verifier {
494                Some(v) => v,
495                None => return rustls_result::NoServerCertVerifier,
496            };
497
498            let config = ClientConfig::builder_with_provider(provider);
499
500            // ECH configuration is mutually exclusive with customizing protocol versions.
501            //
502            // The upstream builder API is written such that calling `with_ech()` transitions
503            // the builder directly to `WantsVerifier`, skipping protocol customization to
504            // ensure the protocols are compatible with the ECH mode. C makes it harder to
505            // express that, so we enforce this at the time of populating `builder.ech_mode`.
506            let wants_verifier;
507            if let Some(ech_mode) = builder.ech_mode {
508                wants_verifier = match config.with_ech(ech_mode) {
509                    Ok(config) => config,
510                    Err(err) => return map_error(err),
511                }
512            } else {
513                let versions = match builder.versions.is_empty() {
514                    true => rustls::DEFAULT_VERSIONS,
515                    false => builder.versions.as_slice(),
516                };
517                wants_verifier = match config.with_protocol_versions(versions) {
518                    Ok(config) => config,
519                    Err(err) => return map_error(err),
520                };
521            }
522
523            let config = wants_verifier
524                .dangerous()
525                .with_custom_certificate_verifier(verifier);
526            let mut config = match builder.cert_resolver {
527                Some(r) => config.with_client_cert_resolver(r),
528                None => config.with_no_client_auth(),
529            };
530            config.alpn_protocols = builder.alpn_protocols;
531            config.enable_sni = builder.enable_sni;
532
533            if let Some(key_log) = builder.key_log {
534                config.key_log = key_log;
535            }
536
537            set_arc_mut_ptr(config_out, config);
538            rustls_result::Ok
539        }
540    }
541
542    /// "Free" a client_config_builder without building it into a rustls_client_config.
543    ///
544    /// Normally builders are built into rustls_client_config via `rustls_client_config_builder_build`
545    /// and may not be free'd or otherwise used afterwards.
546    ///
547    /// Use free only when the building of a config has to be aborted before a config
548    /// was created.
549    #[no_mangle]
550    pub extern "C" fn rustls_client_config_builder_free(config: *mut rustls_client_config_builder) {
551        ffi_panic_boundary! {
552            free_box(config);
553        }
554    }
555}
556
557/// Input to a custom certificate verifier callback.
558///
559/// See `rustls_client_config_builder_dangerous_set_certificate_verifier()`.
560///
561/// server_name can contain a hostname, an IPv4 address in textual form, or an
562/// IPv6 address in textual form.
563#[allow(non_camel_case_types)]
564#[repr(C)]
565pub struct rustls_verify_server_cert_params<'a> {
566    pub end_entity_cert_der: rustls_slice_bytes<'a>,
567    pub intermediate_certs_der: &'a rustls_slice_slice_bytes<'a>,
568    pub server_name: rustls_str<'a>,
569    pub ocsp_response: rustls_slice_bytes<'a>,
570}
571
572/// User-provided input to a custom certificate verifier callback.
573///
574/// See `rustls_client_config_builder_dangerous_set_certificate_verifier()`.
575#[allow(non_camel_case_types)]
576pub type rustls_verify_server_cert_user_data = *mut libc::c_void;
577
578/// A callback that is invoked to verify a server certificate.
579// According to the nomicon https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization):
580// > Option<extern "C" fn(c_int) -> c_int> is a correct way to represent a
581// > nullable function pointer using the C ABI (corresponding to the C type int (*)(int)).
582// So we use Option<...> here. This is the type that is passed from C code.
583#[allow(non_camel_case_types)]
584pub type rustls_verify_server_cert_callback = Option<
585    unsafe extern "C" fn(
586        userdata: rustls_verify_server_cert_user_data,
587        params: *const rustls_verify_server_cert_params,
588    ) -> u32,
589>;
590
591// This is the same as a rustls_verify_server_cert_callback after unwrapping
592// the Option (which is equivalent to checking for null).
593type VerifyCallback = unsafe extern "C" fn(
594    userdata: rustls_verify_server_cert_user_data,
595    params: *const rustls_verify_server_cert_params,
596) -> u32;
597
598// An implementation of rustls::ServerCertVerifier based on a C callback.
599struct Verifier {
600    provider: Arc<CryptoProvider>,
601    callback: VerifyCallback,
602}
603
604/// Safety: Verifier is Send because we don't allocate or deallocate any of its
605/// fields.
606unsafe impl Send for Verifier {}
607
608/// Safety: Verifier is Sync if the C code that passes us a callback that
609/// obeys the concurrency safety requirements documented in
610/// rustls_client_config_builder_dangerous_set_certificate_verifier.
611unsafe impl Sync for Verifier {}
612
613impl ServerCertVerifier for Verifier {
614    fn verify_server_cert(
615        &self,
616        end_entity: &CertificateDer,
617        intermediates: &[CertificateDer],
618        server_name: &ServerName<'_>,
619        ocsp_response: &[u8],
620        _now: UnixTime,
621    ) -> Result<ServerCertVerified, Error> {
622        let cb = self.callback;
623        let server_name = server_name.to_str();
624        let server_name = match server_name.as_ref().try_into() {
625            Ok(r) => r,
626            Err(NulByte {}) => return Err(Error::General("NUL byte in SNI".to_string())),
627        };
628
629        let intermediates: Vec<_> = intermediates.iter().map(|cert| cert.as_ref()).collect();
630
631        let intermediates = rustls_slice_slice_bytes {
632            inner: &intermediates,
633        };
634
635        let params = rustls_verify_server_cert_params {
636            end_entity_cert_der: end_entity.as_ref().into(),
637            intermediate_certs_der: &intermediates,
638            server_name,
639            ocsp_response: ocsp_response.into(),
640        };
641        let userdata = userdata_get()
642            .map_err(|_| Error::General("internal error with thread-local storage".to_string()))?;
643        let result = unsafe { cb(userdata, &params) };
644        match rustls_result::from(result) {
645            rustls_result::Ok => Ok(ServerCertVerified::assertion()),
646            r => Err(error::cert_result_to_error(r)),
647        }
648    }
649
650    fn verify_tls12_signature(
651        &self,
652        message: &[u8],
653        cert: &CertificateDer,
654        dss: &DigitallySignedStruct,
655    ) -> Result<HandshakeSignatureValid, Error> {
656        verify_tls12_signature(
657            message,
658            cert,
659            dss,
660            &self.provider.signature_verification_algorithms,
661        )
662    }
663
664    fn verify_tls13_signature(
665        &self,
666        message: &[u8],
667        cert: &CertificateDer,
668        dss: &DigitallySignedStruct,
669    ) -> Result<HandshakeSignatureValid, Error> {
670        verify_tls13_signature(
671            message,
672            cert,
673            dss,
674            &self.provider.signature_verification_algorithms,
675        )
676    }
677
678    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
679        self.provider
680            .signature_verification_algorithms
681            .supported_schemes()
682    }
683}
684
685impl Debug for Verifier {
686    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
687        f.debug_struct("Verifier").finish()
688    }
689}
690
691/// Always send the same client certificate.
692#[derive(Debug)]
693struct ResolvesClientCertFromChoices {
694    keys: Vec<Arc<CertifiedKey>>,
695}
696
697impl ResolvesClientCert for ResolvesClientCertFromChoices {
698    fn resolve(
699        &self,
700        _acceptable_issuers: &[&[u8]],
701        sig_schemes: &[SignatureScheme],
702    ) -> Option<Arc<CertifiedKey>> {
703        for key in self.keys.iter() {
704            if key.key.choose_scheme(sig_schemes).is_some() {
705                return Some(key.clone());
706            }
707        }
708        None
709    }
710
711    fn has_certs(&self) -> bool {
712        !self.keys.is_empty()
713    }
714}
715
716impl rustls_client_config {
717    /// Returns true if a `rustls_connection` created from the `rustls_client_config` will
718    /// operate in FIPS mode.
719    ///
720    /// This is different from `rustls_crypto_provider_fips` which is concerned
721    /// only with cryptography, whereas this also covers TLS-level configuration that NIST
722    /// recommends, as well as ECH HPKE suites if applicable.
723    #[no_mangle]
724    pub extern "C" fn rustls_client_config_fips(config: *const rustls_client_config) -> bool {
725        ffi_panic_boundary! {
726            try_ref_from_ptr!(config).fips()
727        }
728    }
729
730    /// "Free" a `rustls_client_config` previously returned from
731    /// `rustls_client_config_builder_build`.
732    ///
733    /// Since `rustls_client_config` is actually an atomically reference-counted pointer,
734    /// extant client connections may still hold an internal reference to the Rust object.
735    ///
736    /// However, C code must consider this pointer unusable after "free"ing it.
737    ///
738    /// Calling with NULL is fine. Must not be called twice with the same value.
739    #[no_mangle]
740    pub extern "C" fn rustls_client_config_free(config: *const rustls_client_config) {
741        ffi_panic_boundary! {
742            free_arc(config);
743        }
744    }
745
746    /// Create a new rustls_connection containing a client connection and return
747    /// it in the output parameter `conn_out`.
748    ///
749    /// If this returns an error code, the memory pointed to by `conn_out` remains
750    /// unchanged.
751    ///
752    /// If this returns a non-error, the memory pointed to by `conn_out`
753    /// is modified to point at a valid `rustls_connection`.  The caller now owns
754    /// the `rustls_connection` and must call `rustls_connection_free` when done with it.
755    ///
756    /// The server_name parameter can contain a hostname or an IP address in
757    /// textual form (IPv4 or IPv6). This function will return an error if it
758    /// cannot be parsed as one of those types.
759    #[no_mangle]
760    pub extern "C" fn rustls_client_connection_new(
761        config: *const rustls_client_config,
762        server_name: *const c_char,
763        conn_out: *mut *mut rustls_connection,
764    ) -> rustls_result {
765        ffi_panic_boundary! {
766            if conn_out.is_null() {
767                return rustls_result::NullParameter;
768            }
769            let server_name = unsafe {
770                if server_name.is_null() {
771                    return rustls_result::NullParameter;
772                }
773                CStr::from_ptr(server_name)
774            };
775            let config = try_clone_arc!(config);
776            let conn_out = try_mut_from_ptr_ptr!(conn_out);
777            let server_name = match server_name.to_str() {
778                Ok(s) => s,
779                Err(std::str::Utf8Error { .. }) => return rustls_result::InvalidDnsNameError,
780            };
781            let server_name = match server_name.try_into() {
782                Ok(sn) => sn,
783                Err(_) => return rustls_result::InvalidDnsNameError,
784            };
785            let client = ClientConnection::new(config, server_name).unwrap();
786
787            // We've succeeded. Put the client on the heap, and transfer ownership
788            // to the caller. After this point, we must return rustls_result::Ok so the
789            // caller knows it is responsible for this memory.
790            let c = Connection::from_client(client);
791            set_boxed_mut_ptr(conn_out, c);
792            rustls_result::Ok
793        }
794    }
795}
796
797#[cfg(all(test, any(feature = "ring", feature = "aws-lc-rs")))]
798mod tests {
799    use std::ptr::{null, null_mut};
800
801    use super::*;
802
803    #[test]
804    fn test_config_builder() {
805        let builder = rustls_client_config_builder::rustls_client_config_builder_new();
806        let mut verifier = null_mut();
807        let result =
808            rustls_server_cert_verifier::rustls_platform_server_cert_verifier(&mut verifier);
809        assert_eq!(result, rustls_result::Ok);
810        assert!(!verifier.is_null());
811        rustls_client_config_builder::rustls_client_config_builder_set_server_verifier(
812            builder, verifier,
813        );
814        let h1 = "http/1.1".as_bytes();
815        let h2 = "h2".as_bytes();
816        let alpn = [h1.into(), h2.into()];
817        rustls_client_config_builder::rustls_client_config_builder_set_alpn_protocols(
818            builder,
819            alpn.as_ptr(),
820            alpn.len(),
821        );
822        rustls_client_config_builder::rustls_client_config_builder_set_enable_sni(builder, false);
823        let mut config = null();
824        let result =
825            rustls_client_config_builder::rustls_client_config_builder_build(builder, &mut config);
826        assert_eq!(result, rustls_result::Ok);
827        assert!(!config.is_null());
828        {
829            let config2 = try_ref_from_ptr!(config);
830            assert!(!config2.enable_sni);
831            assert_eq!(config2.alpn_protocols, vec![h1, h2]);
832        }
833        rustls_client_config::rustls_client_config_free(config);
834        rustls_server_cert_verifier::rustls_server_cert_verifier_free(verifier);
835    }
836
837    // Build a client connection and test the getters and initial values.
838    #[test]
839    #[cfg_attr(miri, ignore)]
840    fn test_client_connection_new() {
841        let builder = rustls_client_config_builder::rustls_client_config_builder_new();
842        let mut verifier = null_mut();
843        let result =
844            rustls_server_cert_verifier::rustls_platform_server_cert_verifier(&mut verifier);
845        assert_eq!(result, rustls_result::Ok);
846        assert!(!verifier.is_null());
847        rustls_client_config_builder::rustls_client_config_builder_set_server_verifier(
848            builder, verifier,
849        );
850        let mut config = null();
851        let result =
852            rustls_client_config_builder::rustls_client_config_builder_build(builder, &mut config);
853        assert_eq!(result, rustls_result::Ok);
854        assert!(!config.is_null());
855        let mut conn = null_mut();
856        let result = rustls_client_config::rustls_client_connection_new(
857            config,
858            "example.com\0".as_ptr() as *const c_char,
859            &mut conn,
860        );
861        if !matches!(result, rustls_result::Ok) {
862            panic!("expected RUSTLS_RESULT_OK, got {:?}", result);
863        }
864        assert!(!rustls_connection::rustls_connection_wants_read(conn));
865        assert!(rustls_connection::rustls_connection_wants_write(conn));
866        assert!(rustls_connection::rustls_connection_is_handshaking(conn));
867
868        let some_byte = 42u8;
869        let mut alpn_protocol: *const u8 = &some_byte;
870        let mut alpn_protocol_len = 1;
871        rustls_connection::rustls_connection_get_alpn_protocol(
872            conn,
873            &mut alpn_protocol,
874            &mut alpn_protocol_len,
875        );
876        assert_eq!(alpn_protocol, null());
877        assert_eq!(alpn_protocol_len, 0);
878
879        assert_eq!(
880            rustls_connection::rustls_connection_get_negotiated_ciphersuite(conn),
881            0
882        );
883        let cs_name = rustls_connection::rustls_connection_get_negotiated_ciphersuite_name(conn);
884        assert_eq!(unsafe { cs_name.to_str() }, "");
885        assert_eq!(
886            rustls_connection::rustls_connection_get_peer_certificate(conn, 0),
887            null()
888        );
889
890        assert_eq!(
891            rustls_connection::rustls_connection_get_protocol_version(conn),
892            0
893        );
894        rustls_connection::rustls_connection_free(conn);
895        rustls_server_cert_verifier::rustls_server_cert_verifier_free(verifier);
896    }
897
898    #[test]
899    #[cfg_attr(miri, ignore)]
900    fn test_client_connection_new_ipaddress() {
901        let builder = rustls_client_config_builder::rustls_client_config_builder_new();
902        let mut verifier = null_mut();
903        let result =
904            rustls_server_cert_verifier::rustls_platform_server_cert_verifier(&mut verifier);
905        assert_eq!(result, rustls_result::Ok);
906        assert!(!verifier.is_null());
907        rustls_client_config_builder::rustls_client_config_builder_set_server_verifier(
908            builder, verifier,
909        );
910        let mut config = null();
911        let result =
912            rustls_client_config_builder::rustls_client_config_builder_build(builder, &mut config);
913        assert_eq!(result, rustls_result::Ok);
914        assert!(!config.is_null());
915        let mut conn = null_mut();
916        let result = rustls_client_config::rustls_client_connection_new(
917            config,
918            "198.51.100.198\0".as_ptr() as *const c_char,
919            &mut conn,
920        );
921        if !matches!(result, rustls_result::Ok) {
922            panic!("expected RUSTLS_RESULT_OK, got {:?}", result);
923        }
924        rustls_server_cert_verifier::rustls_server_cert_verifier_free(verifier);
925    }
926
927    #[test]
928    fn test_client_builder_no_verifier_err() {
929        let builder = rustls_client_config_builder::rustls_client_config_builder_new();
930        let mut config = null();
931        let result =
932            rustls_client_config_builder::rustls_client_config_builder_build(builder, &mut config);
933        assert_eq!(result, rustls_result::NoServerCertVerifier);
934    }
935}