Skip to main content

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