-
-
Save cpu/888f13381ef86f084f447d7133791e69 to your computer and use it in GitHub Desktop.
cargo expand of rustls-ffi 10c5324a43a84afc4b13da0c148e6bdd74b4caa1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(prelude_import)] | |
#![crate_type = "staticlib"] | |
#![allow(non_camel_case_types)] | |
#![allow(clippy::not_unsafe_ptr_arg_deref)] | |
#![allow(clippy::arc_with_non_send_sync)] | |
//! This package contains bindings for using rustls via a C API. If | |
//! you're looking at this on docs.rs, [you may want the rustls docs | |
//! instead](https://docs.rs/rustls/latest/rustls/). | |
//! | |
//! Even though this is a C API, it is published on crates.io so other crates that | |
//! wrap a different C API (like curl) can depend on it. | |
//! | |
//! [You may also want to read the rustls-ffi README](https://github.com/rustls/rustls-ffi#rustls-ffi-bindings). | |
#[prelude_import] | |
use std::prelude::rust_2021::*; | |
#[macro_use] | |
extern crate std; | |
use crate::rslice::rustls_str; | |
use libc::c_void; | |
use std::cell::RefCell; | |
use std::mem; | |
use std::sync::Arc; | |
pub mod acceptor { | |
use std::sync::Arc; | |
use libc::{c_void, size_t, EINVAL, EIO}; | |
use rustls::server::{Accepted, AcceptedAlert, Acceptor}; | |
use rustls::ServerConfig; | |
use crate::connection::rustls_connection; | |
use crate::error::{map_error, rustls_io_result}; | |
use crate::io::{ | |
rustls_read_callback, rustls_write_callback, CallbackReader, CallbackWriter, | |
ReadCallback, | |
}; | |
use crate::rslice::{rustls_slice_bytes, rustls_str}; | |
use crate::server::rustls_server_config; | |
use crate::{ | |
box_castable, ffi_panic_boundary, free_box, rustls_result, set_boxed_mut_ptr, | |
to_box, to_boxed_mut_ptr, try_callback, try_clone_arc, try_mut_from_ptr, | |
try_ref_from_ptr, try_take, | |
}; | |
use rustls_result::NullParameter; | |
/// A buffer and parser for ClientHello bytes. This allows reading ClientHello | |
/// before choosing a rustls_server_config. It's useful when the server | |
/// config will be based on parameters in the ClientHello: server name | |
/// indication (SNI), ALPN protocols, signature schemes, and cipher suites. In | |
/// particular, if a server wants to do some potentially expensive work to load a | |
/// certificate for a given hostname, rustls_acceptor allows doing that asynchronously, | |
/// as opposed to rustls_server_config_builder_set_hello_callback(), which doesn't | |
/// work well for asynchronous I/O. | |
/// | |
/// The general flow is: | |
/// - rustls_acceptor_new() | |
/// - Loop: | |
/// - Read bytes from the network it with rustls_acceptor_read_tls(). | |
/// - If successful, parse those bytes with rustls_acceptor_accept(). | |
/// - If that returns RUSTLS_RESULT_ACCEPTOR_NOT_READY, continue. | |
/// - Otherwise, break. | |
/// - If rustls_acceptor_accept() returned RUSTLS_RESULT_OK: | |
/// - Examine the resulting rustls_accepted. | |
/// - Create or select a rustls_server_config. | |
/// - Call rustls_accepted_into_connection(). | |
/// - Otherwise, there was a problem with the ClientHello data and the | |
/// connection should be rejected. | |
pub struct rustls_acceptor { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_acceptor { | |
type Ownership = crate::OwnershipBox; | |
type RustType = Acceptor; | |
} | |
/// A parsed ClientHello produced by a rustls_acceptor. It is used to check | |
/// server name indication (SNI), ALPN protocols, signature schemes, and | |
/// cipher suites. It can be combined with a rustls_server_config to build a | |
/// rustls_connection. | |
pub struct rustls_accepted { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_accepted { | |
type Ownership = crate::OwnershipBox; | |
type RustType = Option<Accepted>; | |
} | |
impl rustls_acceptor { | |
/// Create and return a new rustls_acceptor. | |
/// | |
/// Caller owns the pointed-to memory and must eventually free it with | |
/// `rustls_acceptor_free()`. | |
#[no_mangle] | |
pub extern "C" fn rustls_acceptor_new() -> *mut rustls_acceptor { | |
match ::std::panic::catch_unwind(|| { | |
to_boxed_mut_ptr(Acceptor::default()) | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Free a rustls_acceptor. | |
/// | |
/// Parameters: | |
/// | |
/// acceptor: The rustls_acceptor to free. | |
/// | |
/// Calling with NULL is fine. Must not be called twice with the same value. | |
#[no_mangle] | |
pub extern "C" fn rustls_acceptor_free(acceptor: *mut rustls_acceptor) { | |
match ::std::panic::catch_unwind(|| { | |
to_box(acceptor); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Read some TLS bytes from the network into internal buffers. The actual network | |
/// I/O is performed by `callback`, which you provide. Rustls will invoke your | |
/// callback with a suitable buffer to store the read bytes into. You don't have | |
/// to fill it up, just fill with as many bytes as you get in one syscall. | |
/// | |
/// Parameters: | |
/// | |
/// acceptor: The rustls_acceptor to read bytes into. | |
/// callback: A function that will perform the actual network I/O. | |
/// Must be valid to call with the given userdata parameter until | |
/// this function call returns. | |
/// userdata: An opaque parameter to be passed directly to `callback`. | |
/// Note: this is distinct from the `userdata` parameter set with | |
/// `rustls_connection_set_userdata`. | |
/// out_n: An output parameter. This will be passed through to `callback`, | |
/// which should use it to store the number of bytes written. | |
/// | |
/// Returns: | |
/// | |
/// - 0: Success. You should call `rustls_acceptor_accept()` next. | |
/// - Any non-zero value: error. | |
/// | |
/// This function passes through return values from `callback`. Typically | |
/// `callback` should return an errno value. See `rustls_read_callback()` for | |
/// more details. | |
#[no_mangle] | |
pub extern "C" fn rustls_acceptor_read_tls( | |
acceptor: *mut rustls_acceptor, | |
callback: rustls_read_callback, | |
userdata: *mut c_void, | |
out_n: *mut size_t, | |
) -> rustls_io_result { | |
match ::std::panic::catch_unwind(|| { | |
let acceptor: &mut Acceptor = match crate::try_from_mut(acceptor) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
if out_n.is_null() { | |
return rustls_io_result(EINVAL); | |
} | |
let callback: ReadCallback = match callback { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let mut reader = CallbackReader { | |
callback, | |
userdata, | |
}; | |
let n_read: usize = match acceptor.read_tls(&mut reader) { | |
Ok(n) => n, | |
Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), | |
}; | |
unsafe { | |
*out_n = n_read; | |
} | |
rustls_io_result(0) | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Parse all TLS bytes read so far. If those bytes make up a ClientHello, | |
/// create a rustls_accepted from them. | |
/// | |
/// Parameters: | |
/// | |
/// acceptor: The rustls_acceptor to access. | |
/// out_accepted: An output parameter. The pointed-to pointer will be set | |
/// to a new rustls_accepted only when the function returns | |
/// RUSTLS_RESULT_OK. The memory is owned by the caller and must eventually | |
/// be freed | |
/// out_alert: An output parameter. The pointed-to pointer will be set | |
/// to a new rustls_accepted_alert only when the function returns | |
/// a non-OK result. The memory is owned by the caller and must eventually | |
/// be freed with rustls_accepted_alert_free. The caller should call | |
/// rustls_accepted_alert_write_tls to write the alert bytes to the TLS | |
/// connection before freeing the rustls_accepted_alert. | |
/// | |
/// At most one of out_accepted or out_alert will be set. | |
/// | |
/// Returns: | |
/// | |
/// - RUSTLS_RESULT_OK: a ClientHello has successfully been parsed. | |
/// A pointer to a newly allocated rustls_accepted has been written to | |
/// *out_accepted. | |
/// - RUSTLS_RESULT_ACCEPTOR_NOT_READY: a full ClientHello has not yet been read. | |
/// Read more TLS bytes to continue. | |
/// - Any other rustls_result: the TLS bytes read so far cannot be parsed | |
/// as a ClientHello, and reading additional bytes won't help. | |
/// | |
/// Memory and lifetimes: | |
/// | |
/// After this method returns RUSTLS_RESULT_OK, `acceptor` is | |
/// still allocated and valid. It needs to be freed regardless of success | |
/// or failure of this function. | |
/// | |
/// Calling `rustls_acceptor_accept()` multiple times on the same | |
/// `rustls_acceptor` is acceptable from a memory perspective but pointless | |
/// from a protocol perspective. | |
#[no_mangle] | |
pub extern "C" fn rustls_acceptor_accept( | |
acceptor: *mut rustls_acceptor, | |
out_accepted: *mut *mut rustls_accepted, | |
out_alert: *mut *mut rustls_accepted_alert, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let acceptor: &mut Acceptor = match crate::try_from_mut(acceptor) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
if out_accepted.is_null() { | |
return NullParameter; | |
} | |
if out_alert.is_null() { | |
return NullParameter; | |
} | |
match acceptor.accept() { | |
Ok(None) => rustls_result::AcceptorNotReady, | |
Ok(Some(accepted)) => { | |
set_boxed_mut_ptr(out_accepted, Some(accepted)); | |
rustls_result::Ok | |
} | |
Err((e, accepted_alert)) => { | |
set_boxed_mut_ptr(out_alert, accepted_alert); | |
map_error(e) | |
} | |
} | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
impl rustls_accepted { | |
/// Get the server name indication (SNI) from the ClientHello. | |
/// | |
/// Parameters: | |
/// | |
/// accepted: The rustls_accepted to access. | |
/// | |
/// Returns: | |
/// | |
/// A rustls_str containing the SNI field. | |
/// | |
/// The returned value is valid until rustls_accepted_into_connection or | |
/// rustls_accepted_free is called on the same `accepted`. It is not owned | |
/// by the caller and does not need to be freed. | |
/// | |
/// This will be a zero-length rustls_str in these error cases: | |
/// | |
/// - The SNI contains a NUL byte. | |
/// - The `accepted` parameter was NULL. | |
/// - The `accepted` parameter was already transformed into a connection | |
/// with rustls_accepted_into_connection. | |
#[no_mangle] | |
pub extern "C" fn rustls_accepted_server_name( | |
accepted: *const rustls_accepted, | |
) -> rustls_str<'static> { | |
match ::std::panic::catch_unwind(|| { | |
let accepted: &Option<Accepted> = match crate::try_from(accepted) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let accepted = match accepted { | |
Some(a) => a, | |
None => return Default::default(), | |
}; | |
let hello = accepted.client_hello(); | |
let sni = match hello.server_name() { | |
Some(s) => s, | |
None => return Default::default(), | |
}; | |
match rustls_str::try_from(sni) { | |
Ok(s) => unsafe { s.into_static() } | |
Err(_) => Default::default(), | |
} | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Get the i'th in the list of signature schemes offered in the ClientHello. | |
/// This is useful in selecting a server certificate when there are multiple | |
/// available for the same server name, for instance when selecting | |
/// between an RSA and an ECDSA certificate. | |
/// | |
/// Parameters: | |
/// | |
/// accepted: The rustls_accepted to access. | |
/// i: Fetch the signature scheme at this offset. | |
/// | |
/// Returns: | |
/// | |
/// A TLS Signature Scheme from <https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-signaturescheme> | |
/// | |
/// This will be 0 in these cases: | |
/// - i is greater than the number of available cipher suites. | |
/// - accepted is NULL. | |
/// - rustls_accepted_into_connection has already been called with `accepted`. | |
#[no_mangle] | |
pub extern "C" fn rustls_accepted_signature_scheme( | |
accepted: *const rustls_accepted, | |
i: usize, | |
) -> u16 { | |
match ::std::panic::catch_unwind(|| { | |
let accepted: &Option<Accepted> = match crate::try_from(accepted) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let accepted = match accepted { | |
Some(a) => a, | |
None => return 0, | |
}; | |
let hello = accepted.client_hello(); | |
let signature_schemes = hello.signature_schemes(); | |
match signature_schemes.get(i) { | |
Some(s) => u16::from(*s), | |
None => 0, | |
} | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Get the i'th in the list of cipher suites offered in the ClientHello. | |
/// | |
/// Parameters: | |
/// | |
/// accepted: The rustls_accepted to access. | |
/// i: Fetch the cipher suite at this offset. | |
/// | |
/// Returns: | |
/// | |
/// A cipher suite value from <https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4.> | |
/// | |
/// This will be 0 in these cases: | |
/// - i is greater than the number of available cipher suites. | |
/// - accepted is NULL. | |
/// - rustls_accepted_into_connection has already been called with `accepted`. | |
/// | |
/// Note that 0 is technically a valid cipher suite "TLS_NULL_WITH_NULL_NULL", | |
/// but this library will never support null ciphers. | |
#[no_mangle] | |
pub extern "C" fn rustls_accepted_cipher_suite( | |
accepted: *const rustls_accepted, | |
i: usize, | |
) -> u16 { | |
match ::std::panic::catch_unwind(|| { | |
let accepted: &Option<Accepted> = match crate::try_from(accepted) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let accepted = match accepted { | |
Some(a) => a, | |
None => return 0, | |
}; | |
let hello = accepted.client_hello(); | |
let cipher_suites = hello.cipher_suites(); | |
match cipher_suites.get(i) { | |
Some(cs) => u16::from(*cs), | |
None => 0, | |
} | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Get the i'th in the list of ALPN protocols requested in the ClientHello. | |
/// | |
/// accepted: The rustls_accepted to access. | |
/// i: Fetch the ALPN value at this offset. | |
/// | |
/// Returns: | |
/// | |
/// A rustls_slice_bytes containing the i'th ALPN protocol. This may | |
/// contain internal NUL bytes and is not guaranteed to contain valid | |
/// UTF-8. | |
/// | |
/// This will be a zero-length rustls_slice bytes in these cases: | |
/// - i is greater than the number of offered ALPN protocols. | |
/// - The client did not offer the ALPN extension. | |
/// - The `accepted` parameter was already transformed into a connection | |
/// with rustls_accepted_into_connection. | |
/// | |
/// The returned value is valid until rustls_accepted_into_connection or | |
/// rustls_accepted_free is called on the same `accepted`. It is not owned | |
/// by the caller and does not need to be freed. | |
/// | |
/// If you are calling this from Rust, note that the `'static` lifetime | |
/// in the return signature is fake and must not be relied upon. | |
#[no_mangle] | |
pub extern "C" fn rustls_accepted_alpn( | |
accepted: *const rustls_accepted, | |
i: usize, | |
) -> rustls_slice_bytes<'static> { | |
match ::std::panic::catch_unwind(|| { | |
let accepted: &Option<Accepted> = match crate::try_from(accepted) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let accepted = match accepted { | |
Some(a) => a, | |
None => return Default::default(), | |
}; | |
let mut alpn_iter = match accepted.client_hello().alpn() { | |
Some(iter) => iter, | |
None => return Default::default(), | |
}; | |
match alpn_iter.nth(i) { | |
Some(slice_bytes) => slice_bytes.into(), | |
None => rustls_slice_bytes::default(), | |
} | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Turn a rustls_accepted into a rustls_connection, given the provided | |
/// rustls_server_config. | |
/// | |
/// Parameters: | |
/// | |
/// accepted: The rustls_accepted to transform. | |
/// config: The configuration with which to create this connection. | |
/// out_conn: An output parameter. The pointed-to pointer will be set | |
/// to a new rustls_connection only when the function returns | |
/// RUSTLS_RESULT_OK. | |
/// out_alert: An output parameter. The pointed-to pointer will be set | |
/// to a new rustls_accepted_alert when, and only when, the function returns | |
/// a non-OK result. The memory is owned by the caller and must eventually | |
/// be freed with rustls_accepted_alert_free. The caller should call | |
/// rustls_accepted_alert_write_tls to write the alert bytes to | |
/// the TLS connection before freeing the rustls_accepted_alert. | |
/// | |
/// At most one of out_conn or out_alert will be set. | |
/// | |
/// Returns: | |
/// | |
/// - RUSTLS_RESULT_OK: The `accepted` parameter was successfully | |
/// transformed into a rustls_connection, and *out_conn was written to. | |
/// - RUSTLS_RESULT_ALREADY_USED: This function was called twice on the | |
/// same rustls_connection. | |
/// - RUSTLS_RESULT_NULL_PARAMETER: One of the input parameters was NULL. | |
/// | |
/// Memory and lifetimes: | |
/// | |
/// In both success and failure cases, this consumes the contents of | |
/// `accepted` but does not free its allocated memory. In either case, | |
/// call rustls_accepted_free to avoid a memory leak. | |
/// | |
/// Calling accessor methods on an `accepted` after consuming it will | |
/// return zero or default values. | |
/// | |
/// The rustls_connection emitted by this function in the success case | |
/// is owned by the caller and must eventually be freed. | |
/// | |
/// This function does not take ownership of `config`. It does increment | |
/// `config`'s internal reference count, indicating that the | |
/// rustls_connection may hold a reference to it until it is done. | |
/// See the documentation for rustls_connection for details. | |
#[no_mangle] | |
pub extern "C" fn rustls_accepted_into_connection( | |
accepted: *mut rustls_accepted, | |
config: *const rustls_server_config, | |
out_conn: *mut *mut rustls_connection, | |
out_alert: *mut *mut rustls_accepted_alert, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let accepted: &mut Option<Accepted> = match crate::try_from_mut( | |
accepted, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let accepted = match accepted.take() { | |
None => { | |
return crate::rustls_result::AlreadyUsed; | |
} | |
Some(x) => x, | |
}; | |
let config: Arc<ServerConfig> = match crate::clone_arc(config) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
if out_conn.is_null() { | |
return NullParameter; | |
} | |
if out_alert.is_null() { | |
return NullParameter; | |
} | |
match accepted.into_connection(config) { | |
Ok(built) => { | |
let wrapped = crate::connection::Connection::from_server(built); | |
set_boxed_mut_ptr(out_conn, wrapped); | |
rustls_result::Ok | |
} | |
Err((e, accepted_alert)) => { | |
set_boxed_mut_ptr(out_alert, accepted_alert); | |
map_error(e) | |
} | |
} | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Free a rustls_accepted. | |
/// | |
/// Parameters: | |
/// | |
/// accepted: The rustls_accepted to free. | |
/// | |
/// Calling with NULL is fine. Must not be called twice with the same value. | |
#[no_mangle] | |
pub extern "C" fn rustls_accepted_free(accepted: *mut rustls_accepted) { | |
match ::std::panic::catch_unwind(|| { | |
free_box(accepted); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
/// Represents a TLS alert resulting from accepting a client. | |
pub struct rustls_accepted_alert { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_accepted_alert { | |
type Ownership = crate::OwnershipBox; | |
type RustType = AcceptedAlert; | |
} | |
impl rustls_accepted_alert { | |
/// Write some TLS bytes (an alert) to the network. The actual network I/O is | |
/// performed by `callback`, which you provide. Rustls will invoke your callback with a | |
/// suitable buffer containing TLS bytes to send. You don't have to write them | |
/// all, just as many as you can in one syscall. | |
/// The `userdata` parameter is passed through directly to `callback`. Note that | |
/// this is distinct from the `userdata` parameter set with | |
/// `rustls_connection_set_userdata`. | |
/// Returns 0 for success, or an errno value on error. Passes through return values | |
/// from callback. See [`rustls_write_callback`] or [`AcceptedAlert`] for | |
/// more details. | |
#[no_mangle] | |
pub extern "C" fn rustls_accepted_alert_write_tls( | |
accepted_alert: *mut rustls_accepted_alert, | |
callback: rustls_write_callback, | |
userdata: *mut c_void, | |
out_n: *mut size_t, | |
) -> rustls_io_result { | |
match ::std::panic::catch_unwind(|| { | |
if out_n.is_null() { | |
return rustls_io_result(EINVAL); | |
} | |
let accepted_alert: &mut AcceptedAlert = match crate::try_from_mut( | |
accepted_alert, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let mut writer = CallbackWriter { | |
callback: match callback { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}, | |
userdata, | |
}; | |
let n_written: usize = match accepted_alert.write(&mut writer) { | |
Ok(n) => n, | |
Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), | |
}; | |
unsafe { | |
*out_n = n_written; | |
} | |
rustls_io_result(0) | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Free a rustls_accepted_alert. | |
/// | |
/// Parameters: | |
/// | |
/// accepted_alert: The rustls_accepted_alert to free. | |
/// | |
/// Calling with NULL is fine. Must not be called twice with the same value. | |
#[no_mangle] | |
pub extern "C" fn rustls_accepted_alert_free( | |
accepted_alert: *mut rustls_accepted_alert, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
free_box(accepted_alert); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
} | |
pub mod cipher { | |
use libc::{c_char, size_t}; | |
use std::ffi::{CStr, OsStr}; | |
use std::fs::File; | |
use std::io::{BufReader, Cursor}; | |
use std::marker::PhantomData; | |
use std::ptr::null; | |
use std::slice; | |
use std::sync::Arc; | |
use pki_types::{CertificateDer, CertificateRevocationListDer, PrivateKeyDer}; | |
use rustls::client::danger::ServerCertVerifier; | |
use rustls::client::WebPkiServerVerifier; | |
use rustls::crypto::ring::{ALL_CIPHER_SUITES, DEFAULT_CIPHER_SUITES}; | |
use rustls::server::danger::ClientCertVerifier; | |
use rustls::server::WebPkiClientVerifier; | |
use rustls::sign::CertifiedKey; | |
use rustls::{DistinguishedName, RootCertStore, SupportedCipherSuite}; | |
use rustls_pemfile::{certs, crls, pkcs8_private_keys, rsa_private_keys}; | |
use webpki::{RevocationCheckDepth, UnknownStatusPolicy}; | |
use crate::error::{self, rustls_result}; | |
use crate::rslice::{rustls_slice_bytes, rustls_str}; | |
use crate::{ | |
arc_castable, box_castable, ffi_panic_boundary, free_arc, free_box, ref_castable, | |
set_arc_mut_ptr, set_boxed_mut_ptr, to_arc_const_ptr, to_boxed_mut_ptr, | |
try_clone_arc, try_mut_from_ptr, try_ref_from_ptr, try_slice, try_take, | |
}; | |
use rustls_result::{AlreadyUsed, NullParameter}; | |
/// An X.509 certificate, as used in rustls. | |
/// Corresponds to `CertificateDer` in the Rust pki-types API. | |
/// <https://docs.rs/rustls-pki-types/latest/rustls_pki_types/struct.CertificateDer.html> | |
pub struct rustls_certificate<'a> { | |
_private: [u8; 0], | |
_marker: PhantomData<&'a ()>, | |
} | |
impl<'a> crate::Castable for rustls_certificate<'a> { | |
type Ownership = crate::OwnershipRef; | |
type RustType = CertificateDer<'a>; | |
} | |
/// Get the DER data of the certificate itself. | |
/// The data is owned by the certificate and has the same lifetime. | |
#[no_mangle] | |
pub extern "C" fn rustls_certificate_get_der( | |
cert: *const rustls_certificate, | |
out_der_data: *mut *const u8, | |
out_der_len: *mut size_t, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let cert = match crate::try_from(cert) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
if out_der_data.is_null() || out_der_len.is_null() { | |
return NullParameter; | |
} | |
let der = cert.as_ref(); | |
unsafe { | |
*out_der_data = der.as_ptr(); | |
*out_der_len = der.len(); | |
} | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// A cipher suite supported by rustls. | |
pub struct rustls_supported_ciphersuite { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_supported_ciphersuite { | |
type Ownership = crate::OwnershipRef; | |
type RustType = SupportedCipherSuite; | |
} | |
impl rustls_supported_ciphersuite { | |
/// Return a 16-bit unsigned integer corresponding to this cipher suite's assignment from | |
/// <https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4>. | |
/// The bytes from the assignment are interpreted in network order. | |
#[no_mangle] | |
pub extern "C" fn rustls_supported_ciphersuite_get_suite( | |
supported_ciphersuite: *const rustls_supported_ciphersuite, | |
) -> u16 { | |
let supported_ciphersuite = match crate::try_from(supported_ciphersuite) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
u16::from( | |
match supported_ciphersuite { | |
rustls::SupportedCipherSuite::Tls12(sc) => &sc.common, | |
rustls::SupportedCipherSuite::Tls13(sc) => &sc.common, | |
} | |
.suite, | |
) | |
} | |
} | |
/// Returns the name of the ciphersuite as a `rustls_str`. If the provided | |
/// ciphersuite is invalid, the rustls_str will contain the empty string. The | |
/// lifetime of the `rustls_str` is the lifetime of the program, it does not | |
/// need to be freed. | |
#[no_mangle] | |
pub extern "C" fn rustls_supported_ciphersuite_get_name( | |
supported_ciphersuite: *const rustls_supported_ciphersuite, | |
) -> rustls_str<'static> { | |
let supported_ciphersuite = match crate::try_from(supported_ciphersuite) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let s = supported_ciphersuite.suite().as_str().unwrap_or(""); | |
match rustls_str::try_from(s) { | |
Ok(s) => s, | |
Err(_) => rustls_str::from_str_unchecked(""), | |
} | |
} | |
/// Return the length of rustls' list of supported cipher suites. | |
#[no_mangle] | |
pub extern "C" fn rustls_all_ciphersuites_len() -> usize { | |
ALL_CIPHER_SUITES.len() | |
} | |
/// Get a pointer to a member of rustls' list of supported cipher suites. This will return non-NULL | |
/// for i < rustls_all_ciphersuites_len(). | |
/// The returned pointer is valid for the lifetime of the program and may be used directly when | |
/// building a ClientConfig or ServerConfig. | |
#[no_mangle] | |
pub extern "C" fn rustls_all_ciphersuites_get_entry( | |
i: size_t, | |
) -> *const rustls_supported_ciphersuite { | |
match ALL_CIPHER_SUITES.get(i) { | |
Some(cs) => cs as *const SupportedCipherSuite as *const _, | |
None => null(), | |
} | |
} | |
/// Return the length of rustls' list of default cipher suites. | |
#[no_mangle] | |
pub extern "C" fn rustls_default_ciphersuites_len() -> usize { | |
DEFAULT_CIPHER_SUITES.len() | |
} | |
/// Get a pointer to a member of rustls' list of supported cipher suites. This will return non-NULL | |
/// for i < rustls_default_ciphersuites_len(). | |
/// The returned pointer is valid for the lifetime of the program and may be used directly when | |
/// building a ClientConfig or ServerConfig. | |
#[no_mangle] | |
pub extern "C" fn rustls_default_ciphersuites_get_entry( | |
i: size_t, | |
) -> *const rustls_supported_ciphersuite { | |
match DEFAULT_CIPHER_SUITES.get(i) { | |
Some(cs) => cs as *const SupportedCipherSuite as *const _, | |
None => null(), | |
} | |
} | |
/// Rustls' list of supported cipher suites. This is an array of pointers, and | |
/// its length is given by `RUSTLS_ALL_CIPHER_SUITES_LEN`. The pointers will | |
/// always be valid. The contents and order of this array may change between | |
/// releases. | |
#[no_mangle] | |
pub static mut RUSTLS_ALL_CIPHER_SUITES: [*const rustls_supported_ciphersuite; 9] = [ | |
&rustls::crypto::ring::cipher_suite::TLS13_AES_256_GCM_SHA384 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS13_AES_128_GCM_SHA256 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 | |
as *const SupportedCipherSuite as *const _, | |
]; | |
/// The length of the array `RUSTLS_ALL_CIPHER_SUITES`. | |
#[no_mangle] | |
pub static RUSTLS_ALL_CIPHER_SUITES_LEN: usize = unsafe { | |
RUSTLS_ALL_CIPHER_SUITES.len() | |
}; | |
/// Rustls' list of default cipher suites. This is an array of pointers, and | |
/// its length is given by `RUSTLS_DEFAULT_CIPHER_SUITES_LEN`. The pointers | |
/// will always be valid. The contents and order of this array may change | |
/// between releases. | |
#[no_mangle] | |
pub static mut RUSTLS_DEFAULT_CIPHER_SUITES: [*const rustls_supported_ciphersuite; 9] = [ | |
&rustls::crypto::ring::cipher_suite::TLS13_AES_256_GCM_SHA384 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS13_AES_128_GCM_SHA256 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 | |
as *const SupportedCipherSuite as *const _, | |
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 | |
as *const SupportedCipherSuite as *const _, | |
]; | |
/// The length of the array `RUSTLS_DEFAULT_CIPHER_SUITES`. | |
#[no_mangle] | |
pub static RUSTLS_DEFAULT_CIPHER_SUITES_LEN: usize = unsafe { | |
RUSTLS_DEFAULT_CIPHER_SUITES.len() | |
}; | |
/// The complete chain of certificates to send during a TLS handshake, | |
/// plus a private key that matches the end-entity (leaf) certificate. | |
/// Corresponds to `CertifiedKey` in the Rust API. | |
/// <https://docs.rs/rustls/latest/rustls/sign/struct.CertifiedKey.html> | |
pub struct rustls_certified_key { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_certified_key { | |
type Ownership = crate::OwnershipArc; | |
type RustType = CertifiedKey; | |
} | |
impl rustls_certified_key { | |
/// Build a `rustls_certified_key` from a certificate chain and a private key. | |
/// `cert_chain` must point to a buffer of `cert_chain_len` bytes, containing | |
/// a series of PEM-encoded certificates, with the end-entity (leaf) | |
/// certificate first. | |
/// | |
/// `private_key` must point to a buffer of `private_key_len` bytes, containing | |
/// a PEM-encoded private key in either PKCS#1 or PKCS#8 format. | |
/// | |
/// On success, this writes a pointer to the newly created | |
/// `rustls_certified_key` in `certified_key_out`. That pointer must later | |
/// be freed with `rustls_certified_key_free` to avoid memory leaks. Note that | |
/// internally, this is an atomically reference-counted pointer, so even after | |
/// the original caller has called `rustls_certified_key_free`, other objects | |
/// may retain a pointer to the object. The memory will be freed when all | |
/// references are gone. | |
/// | |
/// This function does not take ownership of any of its input pointers. It | |
/// parses the pointed-to data and makes a copy of the result. You may | |
/// free the cert_chain and private_key pointers after calling it. | |
/// | |
/// Typically, you will build a `rustls_certified_key`, use it to create a | |
/// `rustls_server_config` (which increments the reference count), and then | |
/// immediately call `rustls_certified_key_free`. That leaves the | |
/// `rustls_server_config` in possession of the sole reference, so the | |
/// `rustls_certified_key`'s memory will automatically be released when | |
/// the `rustls_server_config` is freed. | |
#[no_mangle] | |
pub extern "C" fn rustls_certified_key_build( | |
cert_chain: *const u8, | |
cert_chain_len: size_t, | |
private_key: *const u8, | |
private_key_len: size_t, | |
certified_key_out: *mut *const rustls_certified_key, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let certified_key_out: &mut *const rustls_certified_key = unsafe { | |
match certified_key_out.as_mut() { | |
Some(c) => c, | |
None => return NullParameter, | |
} | |
}; | |
let certified_key = match rustls_certified_key::certified_key_build( | |
cert_chain, | |
cert_chain_len, | |
private_key, | |
private_key_len, | |
) { | |
Ok(key) => Box::new(key), | |
Err(rr) => return rr, | |
}; | |
let certified_key = Arc::into_raw(Arc::new(*certified_key)) as *const _; | |
*certified_key_out = certified_key; | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Return the i-th rustls_certificate in the rustls_certified_key. 0 gives the | |
/// end-entity certificate. 1 and higher give certificates from the chain. | |
/// Indexes higher than the last available certificate return NULL. | |
/// | |
/// The returned certificate is valid until the rustls_certified_key is freed. | |
#[no_mangle] | |
pub extern "C" fn rustls_certified_key_get_certificate<'a>( | |
certified_key: *const rustls_certified_key, | |
i: size_t, | |
) -> *const rustls_certificate<'a> { | |
match ::std::panic::catch_unwind(|| { | |
let certified_key: &CertifiedKey = match crate::try_from(certified_key) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
match certified_key.cert.get(i) { | |
Some(cert) => cert as *const CertificateDer as *const _, | |
None => null(), | |
} | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Create a copy of the rustls_certified_key with the given OCSP response data | |
/// as DER encoded bytes. The OCSP response may be given as NULL to clear any | |
/// possibly present OCSP data from the cloned key. | |
/// The cloned key is independent from its original and needs to be freed | |
/// by the application. | |
#[no_mangle] | |
pub extern "C" fn rustls_certified_key_clone_with_ocsp( | |
certified_key: *const rustls_certified_key, | |
ocsp_response: *const rustls_slice_bytes, | |
cloned_key_out: *mut *const rustls_certified_key, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let cloned_key_out: &mut *const rustls_certified_key = unsafe { | |
match cloned_key_out.as_mut() { | |
Some(c) => c, | |
None => return NullParameter, | |
} | |
}; | |
let certified_key: &CertifiedKey = match crate::try_from(certified_key) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let mut new_key = certified_key.clone(); | |
if !ocsp_response.is_null() { | |
let ocsp_slice = unsafe { &*ocsp_response }; | |
new_key | |
.ocsp = Some( | |
Vec::from( | |
if ocsp_slice.data.is_null() { | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { | |
slice::from_raw_parts( | |
ocsp_slice.data, | |
ocsp_slice.len as usize, | |
) | |
} | |
}, | |
), | |
); | |
} else { | |
new_key.ocsp = None; | |
} | |
*cloned_key_out = to_arc_const_ptr(new_key); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// "Free" a certified_key previously returned from | |
/// rustls_certified_key_build. Since certified_key is actually an | |
/// atomically reference-counted pointer, extant certified_key may still | |
/// hold an internal reference to the Rust object. However, C code must | |
/// consider this pointer unusable after "free"ing it. | |
/// Calling with NULL is fine. Must not be called twice with the same value. | |
#[no_mangle] | |
pub extern "C" fn rustls_certified_key_free(key: *const rustls_certified_key) { | |
match ::std::panic::catch_unwind(|| { | |
free_arc(key); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
fn certified_key_build( | |
cert_chain: *const u8, | |
cert_chain_len: size_t, | |
private_key: *const u8, | |
private_key_len: size_t, | |
) -> Result<CertifiedKey, rustls_result> { | |
let mut cert_chain: &[u8] = unsafe { | |
if cert_chain.is_null() { | |
return Err(NullParameter); | |
} | |
slice::from_raw_parts(cert_chain, cert_chain_len) | |
}; | |
let private_key_der: &[u8] = unsafe { | |
if private_key.is_null() { | |
return Err(NullParameter); | |
} | |
slice::from_raw_parts(private_key, private_key_len) | |
}; | |
let private_key: PrivateKeyDer = match pkcs8_private_keys( | |
&mut Cursor::new(private_key_der), | |
) | |
.next() | |
{ | |
Some(Ok(p)) => p.into(), | |
Some(Err(_)) => return Err(rustls_result::PrivateKeyParseError), | |
None => { | |
let rsa_private_key: PrivateKeyDer = match rsa_private_keys( | |
&mut Cursor::new(private_key_der), | |
) | |
.next() | |
{ | |
Some(Ok(p)) => p.into(), | |
_ => return Err(rustls_result::PrivateKeyParseError), | |
}; | |
rsa_private_key | |
} | |
}; | |
let signing_key = match rustls::crypto::ring::sign::any_supported_type( | |
&private_key, | |
) { | |
Ok(key) => key, | |
Err(_) => return Err(rustls_result::PrivateKeyParseError), | |
}; | |
let parsed_chain: Result<Vec<CertificateDer>, _> = certs(&mut cert_chain) | |
.collect(); | |
let parsed_chain = match parsed_chain { | |
Ok(v) => v, | |
Err(_) => return Err(rustls_result::CertificateParseError), | |
}; | |
Ok(rustls::sign::CertifiedKey::new(parsed_chain, signing_key)) | |
} | |
} | |
/// A `rustls_root_cert_store` being constructed. | |
/// | |
/// A builder can be modified by adding trust anchor root certificates with | |
/// `rustls_root_cert_store_builder_add_pem`. Once you're done adding root certificates, | |
/// call `rustls_root_cert_store_builder_build` to turn it into a `rustls_root_cert_store`. | |
/// This object is not safe for concurrent mutation. | |
pub struct rustls_root_cert_store_builder { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_root_cert_store_builder { | |
type Ownership = crate::OwnershipBox; | |
type RustType = Option<RootCertStoreBuilder>; | |
} | |
pub(crate) struct RootCertStoreBuilder { | |
roots: RootCertStore, | |
} | |
impl rustls_root_cert_store_builder { | |
/// Create a `rustls_root_cert_store_builder`. | |
/// | |
/// Caller owns the memory and may free it with `rustls_root_cert_store_free`, regardless of | |
/// whether `rustls_root_cert_store_builder_build` was called. | |
/// | |
/// If you wish to abandon the builder without calling `rustls_root_cert_store_builder_build`, | |
/// it must be freed with `rustls_root_cert_store_builder_free`. | |
#[no_mangle] | |
pub extern "C" fn rustls_root_cert_store_builder_new() -> *mut rustls_root_cert_store_builder { | |
match ::std::panic::catch_unwind(|| { | |
let store = rustls::RootCertStore::empty(); | |
to_boxed_mut_ptr( | |
Some(RootCertStoreBuilder { | |
roots: store, | |
}), | |
) | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Add one or more certificates to the root cert store builder using PEM | |
/// encoded data. | |
/// | |
/// When `strict` is true an error will return a `CertificateParseError` | |
/// result. So will an attempt to parse data that has zero certificates. | |
/// | |
/// When `strict` is false, unparseable root certificates will be ignored. | |
/// This may be useful on systems that have syntactically invalid root | |
/// certificates. | |
#[no_mangle] | |
pub extern "C" fn rustls_root_cert_store_builder_add_pem( | |
builder: *mut rustls_root_cert_store_builder, | |
pem: *const u8, | |
pem_len: size_t, | |
strict: bool, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let certs_pem: &[u8] = if pem.is_null() { | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { slice::from_raw_parts(pem, pem_len as usize) } | |
}; | |
let builder: &mut Option<RootCertStoreBuilder> = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let builder = match builder { | |
None => return AlreadyUsed, | |
Some(b) => b, | |
}; | |
let certs_der: Result<Vec<CertificateDer>, _> = rustls_pemfile::certs( | |
&mut Cursor::new(certs_pem), | |
) | |
.collect(); | |
let certs_der = match certs_der { | |
Ok(vv) => vv, | |
Err(_) => return rustls_result::CertificateParseError, | |
}; | |
let mut new_store = RootCertStore::empty(); | |
let (parsed, rejected) = new_store.add_parsable_certificates(certs_der); | |
if strict && (rejected > 0 || parsed == 0) { | |
return rustls_result::CertificateParseError; | |
} | |
builder.roots.roots.append(&mut new_store.roots); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Add one or more certificates to the root cert store builder using PEM | |
/// encoded data read from the named file. | |
/// | |
/// When `strict` is true an error will return a `CertificateParseError` | |
/// result. So will an attempt to parse data that has zero certificates. | |
/// | |
/// When `strict` is false, unparseable root certificates will be ignored. | |
/// This may be useful on systems that have syntactically invalid root | |
/// certificates. | |
#[no_mangle] | |
pub extern "C" fn rustls_root_cert_store_builder_load_roots_from_file( | |
builder: *mut rustls_root_cert_store_builder, | |
filename: *const c_char, | |
strict: bool, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let builder: &mut Option<RootCertStoreBuilder> = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let builder = match builder { | |
None => return AlreadyUsed, | |
Some(b) => b, | |
}; | |
let filename: &CStr = unsafe { | |
if filename.is_null() { | |
return rustls_result::NullParameter; | |
} | |
CStr::from_ptr(filename) | |
}; | |
let filename: &[u8] = filename.to_bytes(); | |
let filename: &str = match std::str::from_utf8(filename) { | |
Ok(s) => s, | |
Err(_) => return rustls_result::Io, | |
}; | |
let filename: &OsStr = OsStr::new(filename); | |
let mut cafile = match File::open(filename) { | |
Ok(f) => f, | |
Err(_) => return rustls_result::Io, | |
}; | |
let mut bufreader = BufReader::new(&mut cafile); | |
let certs: Result<Vec<CertificateDer>, _> = rustls_pemfile::certs( | |
&mut bufreader, | |
) | |
.collect(); | |
let certs = match certs { | |
Ok(certs) => certs, | |
Err(_) => return rustls_result::Io, | |
}; | |
let mut roots = RootCertStore::empty(); | |
let (parsed, rejected) = roots.add_parsable_certificates(certs); | |
if strict && (rejected > 0 || parsed == 0) { | |
return rustls_result::CertificateParseError; | |
} | |
builder.roots.roots.append(&mut roots.roots); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Create a new `rustls_root_cert_store` from the builder. | |
/// | |
/// The builder is consumed and cannot be used again, but must still be freed. | |
/// | |
/// The root cert store can be used in several `rustls_web_pki_client_cert_verifier_builder_new` | |
/// instances and must be freed by the application when no longer needed. See the documentation of | |
/// `rustls_root_cert_store_free` for details about lifetime. | |
#[no_mangle] | |
pub extern "C" fn rustls_root_cert_store_builder_build( | |
builder: *mut rustls_root_cert_store_builder, | |
root_cert_store_out: *mut *const rustls_root_cert_store, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let builder: &mut Option<RootCertStoreBuilder> = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let builder = match builder.take() { | |
None => { | |
return crate::rustls_result::AlreadyUsed; | |
} | |
Some(x) => x, | |
}; | |
set_arc_mut_ptr(root_cert_store_out, builder.roots); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Free a `rustls_root_cert_store_builder` previously returned from | |
/// `rustls_root_cert_store_builder_new`. Calling with NULL is fine. Must not be | |
/// called twice with the same value. | |
#[no_mangle] | |
pub extern "C" fn rustls_root_cert_store_builder_free( | |
builder: *mut rustls_root_cert_store_builder, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
free_box(builder); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
/// A root certificate store. | |
/// <https://docs.rs/rustls/latest/rustls/struct.RootCertStore.html> | |
pub struct rustls_root_cert_store { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_root_cert_store { | |
type Ownership = crate::OwnershipArc; | |
type RustType = RootCertStore; | |
} | |
impl rustls_root_cert_store { | |
/// Free a rustls_root_cert_store previously returned from rustls_root_cert_store_builder_build. | |
/// Calling with NULL is fine. Must not be called twice with the same value. | |
#[no_mangle] | |
pub extern "C" fn rustls_root_cert_store_free( | |
store: *const rustls_root_cert_store, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
free_arc(store); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
/// A built client certificate verifier that can be provided to a `rustls_server_config_builder` | |
/// with `rustls_server_config_builder_set_client_verifier`. | |
pub struct rustls_client_cert_verifier { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_client_cert_verifier { | |
type Ownership = crate::OwnershipBox; | |
type RustType = Arc<dyn ClientCertVerifier>; | |
} | |
impl rustls_client_cert_verifier { | |
/// Free a `rustls_client_cert_verifier` previously returned from | |
/// `rustls_client_cert_verifier_builder_build`. Calling with NULL is fine. Must not be | |
/// called twice with the same value. | |
#[no_mangle] | |
pub extern "C" fn rustls_client_cert_verifier_free( | |
verifier: *mut rustls_client_cert_verifier, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
free_box(verifier); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
pub(crate) struct ClientCertVerifierBuilder { | |
roots: Arc<RootCertStore>, | |
root_hint_subjects: Vec<DistinguishedName>, | |
crls: Vec<CertificateRevocationListDer<'static>>, | |
revocation_depth: RevocationCheckDepth, | |
revocation_policy: UnknownStatusPolicy, | |
allow_unauthenticated: bool, | |
} | |
/// A client certificate verifier being constructed. A builder can be modified by, | |
/// e.g. `rustls_web_pki_client_cert_verifier_builder_add_crl`. Once you're | |
/// done configuring settings, call `rustls_web_pki_client_cert_verifier_builder_build` | |
/// to turn it into a `rustls_client_cert_verifier`. This object is not safe | |
/// for concurrent mutation. | |
/// | |
/// See <https://docs.rs/rustls/latest/rustls/server/struct.ClientCertVerifierBuilder.html> | |
/// for more information. | |
pub struct rustls_web_pki_client_cert_verifier_builder { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_web_pki_client_cert_verifier_builder { | |
type Ownership = crate::OwnershipBox; | |
type RustType = Option<ClientCertVerifierBuilder>; | |
} | |
impl rustls_web_pki_client_cert_verifier_builder { | |
/// Create a `rustls_web_pki_client_cert_verifier_builder`. Caller owns the memory and may | |
/// eventually call `rustls_web_pki_client_cert_verifier_builder_free` to free it, whether or | |
/// not `rustls_web_pki_client_cert_verifier_builder_build` was called. | |
/// | |
/// Without further modification the builder will produce a client certificate verifier that | |
/// will require a client present a client certificate that chains to one of the trust anchors | |
/// in the provided `rustls_root_cert_store`. The root cert store must not be empty. | |
/// | |
/// Revocation checking will not be performed unless | |
/// `rustls_web_pki_client_cert_verifier_builder_add_crl` is used to add certificate revocation | |
/// lists (CRLs) to the builder. If CRLs are added, revocation checking will be performed | |
/// for the entire certificate chain unless | |
/// `rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation` is used. Unknown | |
/// revocation status for certificates considered for revocation status will be treated as | |
/// an error unless `rustls_web_pki_client_cert_verifier_allow_unknown_revocation_status` is | |
/// used. | |
/// | |
/// Unauthenticated clients will not be permitted unless | |
/// `rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated` is used. | |
/// | |
/// This copies the contents of the `rustls_root_cert_store`. It does not take | |
/// ownership of the pointed-to data. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_new( | |
store: *const rustls_root_cert_store, | |
) -> *mut rustls_web_pki_client_cert_verifier_builder { | |
match ::std::panic::catch_unwind(|| { | |
let store = match crate::clone_arc(store) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let builder = ClientCertVerifierBuilder { | |
root_hint_subjects: store.subjects(), | |
roots: store, | |
crls: Vec::default(), | |
revocation_depth: RevocationCheckDepth::Chain, | |
revocation_policy: UnknownStatusPolicy::Deny, | |
allow_unauthenticated: false, | |
}; | |
to_boxed_mut_ptr(Some(builder)) | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Add one or more certificate revocation lists (CRLs) to the client certificate verifier | |
/// builder by reading the CRL content from the provided buffer of PEM encoded content. | |
/// | |
/// By default revocation checking will be performed on the entire certificate chain. To only | |
/// check the revocation status of the end entity certificate, use | |
/// `rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation`. | |
/// | |
/// This function returns an error if the provided buffer is not valid PEM encoded content. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_add_crl( | |
builder: *mut rustls_web_pki_client_cert_verifier_builder, | |
crl_pem: *const u8, | |
crl_pem_len: size_t, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let client_verifier_builder: &mut Option<ClientCertVerifierBuilder> = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let client_verifier_builder = match client_verifier_builder { | |
None => return AlreadyUsed, | |
Some(v) => v, | |
}; | |
let crl_pem: &[u8] = if crl_pem.is_null() { | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { slice::from_raw_parts(crl_pem, crl_pem_len as usize) } | |
}; | |
let crls_der: Result<Vec<CertificateRevocationListDer>, _> = crls( | |
&mut Cursor::new(crl_pem), | |
) | |
.collect(); | |
let crls_der = match crls_der { | |
Ok(vv) => vv, | |
Err(_) => return rustls_result::CertificateRevocationListParseError, | |
}; | |
if crls_der.is_empty() { | |
return rustls_result::CertificateRevocationListParseError; | |
} | |
client_verifier_builder.crls.extend(crls_der); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// When CRLs are provided with `rustls_web_pki_client_cert_verifier_builder_add_crl`, only | |
/// check the revocation status of end entity certificates, ignoring any intermediate certificates | |
/// in the chain. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation( | |
builder: *mut rustls_web_pki_client_cert_verifier_builder, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let client_verifier_builder: &mut Option<ClientCertVerifierBuilder> = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let client_verifier_builder = match client_verifier_builder { | |
None => return AlreadyUsed, | |
Some(v) => v, | |
}; | |
client_verifier_builder | |
.revocation_depth = RevocationCheckDepth::EndEntity; | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// When CRLs are provided with `rustls_web_pki_client_cert_verifier_builder_add_crl`, and it | |
/// isn't possible to determine the revocation status of a considered certificate, do not treat | |
/// it as an error condition. | |
/// | |
/// Overrides the default behavior where unknown revocation status is considered an error. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_client_cert_verifier_allow_unknown_revocation_status( | |
builder: *mut rustls_web_pki_client_cert_verifier_builder, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let client_verifier_builder: &mut Option<ClientCertVerifierBuilder> = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let client_verifier_builder = match client_verifier_builder { | |
None => return AlreadyUsed, | |
Some(v) => v, | |
}; | |
client_verifier_builder.revocation_policy = UnknownStatusPolicy::Allow; | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Allow unauthenticated anonymous clients in addition to those that present a client | |
/// certificate that chains to one of the verifier's configured trust anchors. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated( | |
builder: *mut rustls_web_pki_client_cert_verifier_builder, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let client_verifier_builder: &mut Option<ClientCertVerifierBuilder> = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let client_verifier_builder = match client_verifier_builder { | |
None => return AlreadyUsed, | |
Some(v) => v, | |
}; | |
client_verifier_builder.allow_unauthenticated = true; | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Clear the list of trust anchor hint subjects. | |
/// | |
/// By default, the client cert verifier will use the subjects provided by the root cert | |
/// store configured for client authentication. Calling this function will remove these | |
/// hint subjects, indicating the client should make a free choice of which certificate | |
/// to send. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_client_cert_verifier_clear_root_hint_subjects( | |
builder: *mut rustls_web_pki_client_cert_verifier_builder, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let client_verifier_builder: &mut Option<ClientCertVerifierBuilder> = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let client_verifier_builder = match client_verifier_builder { | |
None => return AlreadyUsed, | |
Some(v) => v, | |
}; | |
client_verifier_builder.root_hint_subjects.clear(); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Add additional distinguished names to the list of trust anchor hint subjects. | |
/// | |
/// By default, the client cert verifier will use the subjects provided by the root cert | |
/// store configured for client authentication. Calling this function will add to these | |
/// existing hint subjects. Calling this function with an empty `store` will have no | |
/// effect, use `rustls_web_pki_client_cert_verifier_clear_root_hint_subjects` to clear | |
/// the subject hints. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_client_cert_verifier_add_root_hint_subjects( | |
builder: *mut rustls_web_pki_client_cert_verifier_builder, | |
store: *const rustls_root_cert_store, | |
) -> rustls_result { | |
let client_verifier_builder: &mut Option<ClientCertVerifierBuilder> = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let client_verifier_builder = match client_verifier_builder { | |
None => return AlreadyUsed, | |
Some(v) => v, | |
}; | |
let store = match crate::clone_arc(store) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
client_verifier_builder.root_hint_subjects = store.subjects(); | |
rustls_result::Ok | |
} | |
/// Create a new client certificate verifier from the builder. | |
/// | |
/// The builder is consumed and cannot be used again, but must still be freed. | |
/// | |
/// The verifier can be used in several `rustls_server_config` instances and must be | |
/// freed by the application when no longer needed. See the documentation of | |
/// `rustls_web_pki_client_cert_verifier_builder_free` for details about lifetime. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_build( | |
builder: *mut rustls_web_pki_client_cert_verifier_builder, | |
verifier_out: *mut *mut rustls_client_cert_verifier, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
if verifier_out.is_null() { | |
return NullParameter; | |
} | |
let client_verifier_builder: &mut Option<ClientCertVerifierBuilder> = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let client_verifier_builder = match client_verifier_builder.take() { | |
None => { | |
return crate::rustls_result::AlreadyUsed; | |
} | |
Some(x) => x, | |
}; | |
let mut builder = WebPkiClientVerifier::builder_with_provider( | |
client_verifier_builder.roots, | |
rustls::crypto::ring::default_provider().into(), | |
) | |
.with_crls(client_verifier_builder.crls); | |
match client_verifier_builder.revocation_depth { | |
RevocationCheckDepth::EndEntity => { | |
builder = builder.only_check_end_entity_revocation(); | |
} | |
RevocationCheckDepth::Chain => {} | |
} | |
match client_verifier_builder.revocation_policy { | |
UnknownStatusPolicy::Allow => { | |
builder = builder.allow_unknown_revocation_status(); | |
} | |
UnknownStatusPolicy::Deny => {} | |
} | |
if client_verifier_builder.allow_unauthenticated { | |
builder = builder.allow_unauthenticated(); | |
} | |
if client_verifier_builder.root_hint_subjects.is_empty() { | |
builder = builder.clear_root_hint_subjects(); | |
} else { | |
builder = builder | |
.add_root_hint_subjects( | |
client_verifier_builder.root_hint_subjects, | |
); | |
} | |
let verifier = match builder.build() { | |
Ok(v) => v, | |
Err(e) => return error::map_verifier_builder_error(e), | |
}; | |
set_boxed_mut_ptr(verifier_out, verifier); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Free a `rustls_client_cert_verifier_builder` previously returned from | |
/// `rustls_client_cert_verifier_builder_new`. Calling with NULL is fine. Must not be | |
/// called twice with the same value. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_free( | |
builder: *mut rustls_web_pki_client_cert_verifier_builder, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
free_box(builder); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
/// A server certificate verifier being constructed. A builder can be modified by, | |
/// e.g. `rustls_web_pki_server_cert_verifier_builder_add_crl`. Once you're | |
/// done configuring settings, call `rustls_web_pki_server_cert_verifier_builder_build` | |
/// to turn it into a `rustls_server_cert_verifier`. This object is not safe | |
/// for concurrent mutation. | |
/// | |
/// See <https://docs.rs/rustls/latest/rustls/client/struct.ServerCertVerifierBuilder.html> | |
/// for more information. | |
pub(crate) struct rustls_web_pki_server_cert_verifier_builder { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_web_pki_server_cert_verifier_builder { | |
type Ownership = crate::OwnershipBox; | |
type RustType = Option<ServerCertVerifierBuilder>; | |
} | |
pub(crate) struct ServerCertVerifierBuilder { | |
roots: Arc<RootCertStore>, | |
crls: Vec<CertificateRevocationListDer<'static>>, | |
revocation_depth: RevocationCheckDepth, | |
revocation_policy: UnknownStatusPolicy, | |
} | |
impl ServerCertVerifierBuilder { | |
/// Create a `rustls_web_pki_server_cert_verifier_builder`. Caller owns the memory and may | |
/// free it with `rustls_web_pki_server_cert_verifier_builder_free`, regardless of whether | |
/// `rustls_web_pki_server_cert_verifier_builder_build` was called. | |
/// | |
/// Without further modification the builder will produce a server certificate verifier that | |
/// will require a server present a certificate that chains to one of the trust anchors | |
/// in the provided `rustls_root_cert_store`. The root cert store must not be empty. | |
/// | |
/// Revocation checking will not be performed unless | |
/// `rustls_web_pki_server_cert_verifier_builder_add_crl` is used to add certificate revocation | |
/// lists (CRLs) to the builder. If CRLs are added, revocation checking will be performed | |
/// for the entire certificate chain unless | |
/// `rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation` is used. Unknown | |
/// revocation status for certificates considered for revocation status will be treated as | |
/// an error unless `rustls_web_pki_server_cert_verifier_allow_unknown_revocation_status` is | |
/// used. | |
/// | |
/// This copies the contents of the `rustls_root_cert_store`. It does not take | |
/// ownership of the pointed-to data. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_new( | |
store: *const rustls_root_cert_store, | |
) -> *mut rustls_web_pki_server_cert_verifier_builder { | |
match ::std::panic::catch_unwind(|| { | |
let store = match crate::clone_arc(store) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let builder = ServerCertVerifierBuilder { | |
roots: store, | |
crls: Vec::default(), | |
revocation_depth: RevocationCheckDepth::Chain, | |
revocation_policy: UnknownStatusPolicy::Deny, | |
}; | |
to_boxed_mut_ptr(Some(builder)) | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Add one or more certificate revocation lists (CRLs) to the server certificate verifier | |
/// builder by reading the CRL content from the provided buffer of PEM encoded content. | |
/// | |
/// By default revocation checking will be performed on the entire certificate chain. To only | |
/// check the revocation status of the end entity certificate, use | |
/// `rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation`. | |
/// | |
/// This function returns an error if the provided buffer is not valid PEM encoded content. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_add_crl( | |
builder: *mut rustls_web_pki_server_cert_verifier_builder, | |
crl_pem: *const u8, | |
crl_pem_len: size_t, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let server_verifier_builder: &mut Option<ServerCertVerifierBuilder> = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let server_verifier_builder = match server_verifier_builder { | |
None => return AlreadyUsed, | |
Some(v) => v, | |
}; | |
let crl_pem: &[u8] = if crl_pem.is_null() { | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { slice::from_raw_parts(crl_pem, crl_pem_len as usize) } | |
}; | |
let crls_der: Result<Vec<CertificateRevocationListDer>, _> = crls( | |
&mut Cursor::new(crl_pem), | |
) | |
.collect(); | |
let crls_der = match crls_der { | |
Ok(vv) => vv, | |
Err(_) => return rustls_result::CertificateRevocationListParseError, | |
}; | |
if crls_der.is_empty() { | |
return rustls_result::CertificateRevocationListParseError; | |
} | |
server_verifier_builder.crls.extend(crls_der); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// When CRLs are provided with `rustls_web_pki_server_cert_verifier_builder_add_crl`, only | |
/// check the revocation status of end entity certificates, ignoring any intermediate certificates | |
/// in the chain. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation( | |
builder: *mut rustls_web_pki_server_cert_verifier_builder, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let server_verifier_builder: &mut Option<ServerCertVerifierBuilder> = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let server_verifier_builder = match server_verifier_builder { | |
None => return AlreadyUsed, | |
Some(v) => v, | |
}; | |
server_verifier_builder | |
.revocation_depth = RevocationCheckDepth::EndEntity; | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// When CRLs are provided with `rustls_web_pki_server_cert_verifier_builder_add_crl`, and it | |
/// isn't possible to determine the revocation status of a considered certificate, do not treat | |
/// it as an error condition. | |
/// | |
/// Overrides the default behavior where unknown revocation status is considered an error. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_server_cert_verifier_allow_unknown_revocation_status( | |
builder: *mut rustls_web_pki_server_cert_verifier_builder, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let server_verifier_builder: &mut Option<ServerCertVerifierBuilder> = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let server_verifier_builder = match server_verifier_builder { | |
None => return AlreadyUsed, | |
Some(v) => v, | |
}; | |
server_verifier_builder.revocation_policy = UnknownStatusPolicy::Allow; | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Create a new server certificate verifier from the builder. | |
/// | |
/// The builder is consumed and cannot be used again, but must still be freed. | |
/// | |
/// The verifier can be used in several `rustls_client_config` instances and must be | |
/// freed by the application when no longer needed. See the documentation of | |
/// `rustls_web_pki_server_cert_verifier_builder_free` for details about lifetime. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_build( | |
builder: *mut rustls_web_pki_server_cert_verifier_builder, | |
verifier_out: *mut *mut rustls_server_cert_verifier, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
if verifier_out.is_null() { | |
return NullParameter; | |
} | |
let server_verifier_builder: &mut Option<ServerCertVerifierBuilder> = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let server_verifier_builder = match server_verifier_builder.take() { | |
None => { | |
return crate::rustls_result::AlreadyUsed; | |
} | |
Some(x) => x, | |
}; | |
let mut builder = WebPkiServerVerifier::builder_with_provider( | |
server_verifier_builder.roots, | |
rustls::crypto::ring::default_provider().into(), | |
) | |
.with_crls(server_verifier_builder.crls); | |
match server_verifier_builder.revocation_depth { | |
RevocationCheckDepth::EndEntity => { | |
builder = builder.only_check_end_entity_revocation(); | |
} | |
RevocationCheckDepth::Chain => {} | |
} | |
match server_verifier_builder.revocation_policy { | |
UnknownStatusPolicy::Allow => { | |
builder = builder.allow_unknown_revocation_status(); | |
} | |
UnknownStatusPolicy::Deny => {} | |
} | |
let verifier = match builder.build() { | |
Ok(v) => v, | |
Err(e) => return error::map_verifier_builder_error(e), | |
}; | |
set_boxed_mut_ptr(verifier_out, verifier); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Free a `rustls_server_cert_verifier_builder` previously returned from | |
/// `rustls_server_cert_verifier_builder_new`. Calling with NULL is fine. Must not be | |
/// called twice with the same value. | |
#[no_mangle] | |
pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_free( | |
builder: *mut rustls_web_pki_server_cert_verifier_builder, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
free_box(builder); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
/// A built server certificate verifier that can be provided to a `rustls_client_config_builder` | |
/// with `rustls_client_config_builder_set_server_verifier`. | |
pub struct rustls_server_cert_verifier { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_server_cert_verifier { | |
type Ownership = crate::OwnershipBox; | |
type RustType = Arc<dyn ServerCertVerifier>; | |
} | |
impl rustls_server_cert_verifier { | |
/// Free a `rustls_server_cert_verifier` previously returned from | |
/// `rustls_server_cert_verifier_builder_build`. Calling with NULL is fine. Must not be | |
/// called twice with the same value. | |
#[no_mangle] | |
pub extern "C" fn rustls_server_cert_verifier_free( | |
verifier: *mut rustls_server_cert_verifier, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
free_box(verifier); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
} | |
pub mod client { | |
use std::ffi::CStr; | |
use std::fmt::{Debug, Formatter}; | |
use std::slice; | |
use std::sync::Arc; | |
use libc::{c_char, size_t}; | |
use pki_types::{CertificateDer, UnixTime}; | |
use rustls::client::danger::{ | |
HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, | |
}; | |
use rustls::client::ResolvesClientCert; | |
use rustls::crypto::ring::ALL_CIPHER_SUITES; | |
use rustls::{ | |
sign::CertifiedKey, CertificateError, ClientConfig, ClientConnection, | |
DigitallySignedStruct, Error, ProtocolVersion, SignatureScheme, | |
SupportedCipherSuite, WantsVerifier, | |
}; | |
use crate::cipher::{ | |
rustls_certified_key, rustls_server_cert_verifier, rustls_supported_ciphersuite, | |
}; | |
use crate::connection::{rustls_connection, Connection}; | |
use crate::error::rustls_result::{InvalidParameter, NullParameter}; | |
use crate::error::{self, rustls_result}; | |
use crate::rslice::NulByte; | |
use crate::rslice::{rustls_slice_bytes, rustls_slice_slice_bytes, rustls_str}; | |
use crate::{ | |
arc_castable, box_castable, ffi_panic_boundary, free_arc, free_box, | |
set_boxed_mut_ptr, to_arc_const_ptr, to_boxed_mut_ptr, try_box_from_ptr, | |
try_clone_arc, try_mut_from_ptr, try_ref_from_ptr, try_slice, userdata_get, | |
}; | |
/// A client config being constructed. A builder can be modified by, | |
/// e.g. rustls_client_config_builder_load_roots_from_file. Once you're | |
/// done configuring settings, call rustls_client_config_builder_build | |
/// to turn it into a *rustls_client_config. This object is not safe | |
/// for concurrent mutation. Under the hood, it corresponds to a | |
/// `Box<ClientConfig>`. | |
/// <https://docs.rs/rustls/latest/rustls/struct.ConfigBuilder.html> | |
pub struct rustls_client_config_builder { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_client_config_builder { | |
type Ownership = crate::OwnershipBox; | |
type RustType = ClientConfigBuilder; | |
} | |
pub(crate) struct ClientConfigBuilder { | |
base: rustls::ConfigBuilder<ClientConfig, WantsVerifier>, | |
verifier: Arc<dyn ServerCertVerifier>, | |
alpn_protocols: Vec<Vec<u8>>, | |
enable_sni: bool, | |
cert_resolver: Option<Arc<dyn rustls::client::ResolvesClientCert>>, | |
} | |
/// A client config that is done being constructed and is now read-only. | |
/// Under the hood, this object corresponds to an `Arc<ClientConfig>`. | |
/// <https://docs.rs/rustls/latest/rustls/struct.ClientConfig.html> | |
pub struct rustls_client_config { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_client_config { | |
type Ownership = crate::OwnershipArc; | |
type RustType = ClientConfig; | |
} | |
struct NoneVerifier; | |
#[automatically_derived] | |
impl ::core::fmt::Debug for NoneVerifier { | |
#[inline] | |
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { | |
::core::fmt::Formatter::write_str(f, "NoneVerifier") | |
} | |
} | |
impl ServerCertVerifier for NoneVerifier { | |
fn verify_server_cert( | |
&self, | |
_end_entity: &CertificateDer, | |
_intermediates: &[CertificateDer], | |
_server_name: &pki_types::ServerName<'_>, | |
_ocsp_response: &[u8], | |
_now: UnixTime, | |
) -> Result<ServerCertVerified, rustls::Error> { | |
Err(rustls::Error::InvalidCertificate(CertificateError::BadSignature)) | |
} | |
fn verify_tls12_signature( | |
&self, | |
_message: &[u8], | |
_cert: &CertificateDer, | |
_dss: &DigitallySignedStruct, | |
) -> Result<HandshakeSignatureValid, Error> { | |
Err(Error::InvalidCertificate(CertificateError::BadSignature)) | |
} | |
fn verify_tls13_signature( | |
&self, | |
_message: &[u8], | |
_cert: &CertificateDer, | |
_dss: &DigitallySignedStruct, | |
) -> Result<HandshakeSignatureValid, Error> { | |
Err(Error::InvalidCertificate(CertificateError::BadSignature)) | |
} | |
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { | |
rustls::crypto::ring::default_provider() | |
.signature_verification_algorithms | |
.supported_schemes() | |
} | |
} | |
impl rustls_client_config_builder { | |
/// Create a rustls_client_config_builder. Caller owns the memory and must | |
/// eventually call rustls_client_config_builder_build, then free the | |
/// resulting rustls_client_config. | |
/// This uses rustls safe default values | |
/// for the cipher suites, key exchange groups and protocol versions. | |
/// This starts out with no trusted roots. | |
/// Caller must add roots with rustls_client_config_builder_load_roots_from_file | |
/// or provide a custom verifier. | |
#[no_mangle] | |
pub extern "C" fn rustls_client_config_builder_new() -> *mut rustls_client_config_builder { | |
match ::std::panic::catch_unwind(|| { | |
let base = ClientConfig::builder_with_provider( | |
rustls::crypto::ring::default_provider().into(), | |
) | |
.with_safe_default_protocol_versions() | |
.unwrap(); | |
let builder = ClientConfigBuilder { | |
base, | |
verifier: Arc::new(NoneVerifier), | |
cert_resolver: None, | |
alpn_protocols: ::alloc::vec::Vec::new(), | |
enable_sni: true, | |
}; | |
to_boxed_mut_ptr(builder) | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Create a rustls_client_config_builder. Caller owns the memory and must | |
/// eventually call rustls_client_config_builder_build, then free the | |
/// resulting rustls_client_config. Specify cipher suites in preference | |
/// order; the `cipher_suites` parameter must point to an array containing | |
/// `len` pointers to `rustls_supported_ciphersuite` previously obtained | |
/// from `rustls_all_ciphersuites_get_entry()`, or to a provided array, | |
/// RUSTLS_DEFAULT_CIPHER_SUITES or RUSTLS_ALL_CIPHER_SUITES. Set the TLS | |
/// protocol versions to use when negotiating a TLS session. | |
/// | |
/// `tls_version` is the version of the protocol, as defined in rfc8446, | |
/// ch. 4.2.1 and end of ch. 5.1. Some values are defined in | |
/// `rustls_tls_version` for convenience, and the arrays | |
/// RUSTLS_DEFAULT_VERSIONS or RUSTLS_ALL_VERSIONS can be used directly. | |
/// | |
/// `versions` will only be used during the call and the application retains | |
/// ownership. `len` is the number of consecutive `uint16_t` pointed to by `versions`. | |
#[no_mangle] | |
pub extern "C" fn rustls_client_config_builder_new_custom( | |
cipher_suites: *const *const rustls_supported_ciphersuite, | |
cipher_suites_len: size_t, | |
tls_versions: *const u16, | |
tls_versions_len: size_t, | |
builder_out: *mut *mut rustls_client_config_builder, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
if builder_out.is_null() { | |
return NullParameter; | |
} | |
let cipher_suites: &[*const rustls_supported_ciphersuite] = if cipher_suites | |
.is_null() | |
{ | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { | |
slice::from_raw_parts(cipher_suites, cipher_suites_len as usize) | |
} | |
}; | |
let mut cs_vec: Vec<SupportedCipherSuite> = Vec::new(); | |
for &cs in cipher_suites.iter() { | |
let cs = match crate::try_from(cs) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
match ALL_CIPHER_SUITES.iter().find(|&acs| cs.eq(acs)) { | |
Some(scs) => cs_vec.push(*scs), | |
None => return InvalidParameter, | |
} | |
} | |
let tls_versions: &[u16] = if tls_versions.is_null() { | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { | |
slice::from_raw_parts(tls_versions, tls_versions_len as usize) | |
} | |
}; | |
let mut versions = ::alloc::vec::Vec::new(); | |
for version_number in tls_versions { | |
let proto = ProtocolVersion::from(*version_number); | |
if proto == rustls::version::TLS12.version { | |
versions.push(&rustls::version::TLS12); | |
} else if proto == rustls::version::TLS13.version { | |
versions.push(&rustls::version::TLS13); | |
} | |
} | |
let provider = rustls::crypto::CryptoProvider { | |
cipher_suites: cs_vec, | |
..rustls::crypto::ring::default_provider() | |
}; | |
let result = rustls::ClientConfig::builder_with_provider(provider.into()) | |
.with_protocol_versions(&versions); | |
let base = match result { | |
Ok(new) => new, | |
Err(_) => return rustls_result::InvalidParameter, | |
}; | |
let config_builder = ClientConfigBuilder { | |
base, | |
verifier: Arc::new(NoneVerifier), | |
cert_resolver: None, | |
alpn_protocols: ::alloc::vec::Vec::new(), | |
enable_sni: true, | |
}; | |
set_boxed_mut_ptr(builder_out, config_builder); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
/// Input to a custom certificate verifier callback. See | |
/// rustls_client_config_builder_dangerous_set_certificate_verifier(). | |
/// | |
/// server_name can contain a hostname, an IPv4 address in textual form, or an | |
/// IPv6 address in textual form. | |
#[allow(non_camel_case_types)] | |
#[repr(C)] | |
pub struct rustls_verify_server_cert_params<'a> { | |
pub end_entity_cert_der: rustls_slice_bytes<'a>, | |
pub intermediate_certs_der: &'a rustls_slice_slice_bytes<'a>, | |
pub server_name: rustls_str<'a>, | |
pub ocsp_response: rustls_slice_bytes<'a>, | |
} | |
/// User-provided input to a custom certificate verifier callback. See | |
/// rustls_client_config_builder_dangerous_set_certificate_verifier(). | |
#[allow(non_camel_case_types)] | |
pub type rustls_verify_server_cert_user_data = *mut libc::c_void; | |
#[allow(non_camel_case_types)] | |
pub type rustls_verify_server_cert_callback = Option< | |
unsafe extern "C" fn( | |
userdata: rustls_verify_server_cert_user_data, | |
params: *const rustls_verify_server_cert_params, | |
) -> u32, | |
>; | |
type VerifyCallback = unsafe extern "C" fn( | |
userdata: rustls_verify_server_cert_user_data, | |
params: *const rustls_verify_server_cert_params, | |
) -> u32; | |
struct Verifier { | |
callback: VerifyCallback, | |
} | |
/// Safety: Verifier is Send because we don't allocate or deallocate any of its | |
/// fields. | |
unsafe impl Send for Verifier {} | |
/// Safety: Verifier is Sync if the C code that passes us a callback that | |
/// obeys the concurrency safety requirements documented in | |
/// rustls_client_config_builder_dangerous_set_certificate_verifier. | |
unsafe impl Sync for Verifier {} | |
impl ServerCertVerifier for Verifier { | |
fn verify_server_cert( | |
&self, | |
end_entity: &CertificateDer, | |
intermediates: &[CertificateDer], | |
server_name: &pki_types::ServerName<'_>, | |
ocsp_response: &[u8], | |
_now: UnixTime, | |
) -> Result<ServerCertVerified, rustls::Error> { | |
let cb = self.callback; | |
let server_name = server_name.to_str(); | |
let server_name: rustls_str = match server_name.as_ref().try_into() { | |
Ok(r) => r, | |
Err(NulByte {}) => { | |
return Err(rustls::Error::General("NUL byte in SNI".to_string())); | |
} | |
}; | |
let intermediates: Vec<_> = intermediates | |
.iter() | |
.map(|cert| cert.as_ref()) | |
.collect(); | |
let intermediates = rustls_slice_slice_bytes { | |
inner: &intermediates, | |
}; | |
let params = rustls_verify_server_cert_params { | |
end_entity_cert_der: end_entity.as_ref().into(), | |
intermediate_certs_der: &intermediates, | |
server_name, | |
ocsp_response: ocsp_response.into(), | |
}; | |
let userdata = userdata_get() | |
.map_err(|_| { | |
rustls::Error::General( | |
"internal error with thread-local storage".to_string(), | |
) | |
})?; | |
let result: u32 = unsafe { cb(userdata, ¶ms) }; | |
match rustls_result::from(result) { | |
rustls_result::Ok => Ok(ServerCertVerified::assertion()), | |
r => Err(error::cert_result_to_error(r)), | |
} | |
} | |
fn verify_tls12_signature( | |
&self, | |
message: &[u8], | |
cert: &CertificateDer, | |
dss: &DigitallySignedStruct, | |
) -> Result<HandshakeSignatureValid, Error> { | |
rustls::crypto::verify_tls12_signature( | |
message, | |
cert, | |
dss, | |
&rustls::crypto::ring::default_provider() | |
.signature_verification_algorithms, | |
) | |
} | |
fn verify_tls13_signature( | |
&self, | |
message: &[u8], | |
cert: &CertificateDer, | |
dss: &DigitallySignedStruct, | |
) -> Result<HandshakeSignatureValid, Error> { | |
rustls::crypto::verify_tls13_signature( | |
message, | |
cert, | |
dss, | |
&rustls::crypto::ring::default_provider() | |
.signature_verification_algorithms, | |
) | |
} | |
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { | |
rustls::crypto::ring::default_provider() | |
.signature_verification_algorithms | |
.supported_schemes() | |
} | |
} | |
impl Debug for Verifier { | |
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | |
f.debug_struct("Verifier").finish() | |
} | |
} | |
impl rustls_client_config_builder { | |
/// Set a custom server certificate verifier. | |
/// | |
/// The callback must not capture any of the pointers in its | |
/// rustls_verify_server_cert_params. | |
/// If `userdata` has been set with rustls_connection_set_userdata, it | |
/// will be passed to the callback. Otherwise the userdata param passed to | |
/// the callback will be NULL. | |
/// | |
/// The callback must be safe to call on any thread at any time, including | |
/// multiple concurrent calls. So, for instance, if the callback mutates | |
/// userdata (or other shared state), it must use synchronization primitives | |
/// to make such mutation safe. | |
/// | |
/// The callback receives certificate chain information as raw bytes. | |
/// Currently this library offers no functions to parse the certificates, | |
/// so you'll need to bring your own certificate parsing library | |
/// if you need to parse them. | |
/// | |
/// If the custom verifier accepts the certificate, it should return | |
/// RUSTLS_RESULT_OK. Otherwise, it may return any other rustls_result error. | |
/// Feel free to use an appropriate error from the RUSTLS_RESULT_CERT_* | |
/// section. | |
/// | |
/// <https://docs.rs/rustls/latest/rustls/client/struct.DangerousClientConfig.html#method.set_certificate_verifier> | |
#[no_mangle] | |
pub extern "C" fn rustls_client_config_builder_dangerous_set_certificate_verifier( | |
config_builder: *mut rustls_client_config_builder, | |
callback: rustls_verify_server_cert_callback, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let config_builder = match crate::try_from_mut(config_builder) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let callback: VerifyCallback = match callback { | |
Some(cb) => cb, | |
None => return rustls_result::InvalidParameter, | |
}; | |
let verifier: Verifier = Verifier { callback }; | |
config_builder.verifier = Arc::new(verifier); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Configure the server certificate verifier. | |
/// | |
/// This increases the reference count of `verifier` and does not take ownership. | |
#[no_mangle] | |
pub extern "C" fn rustls_client_config_builder_set_server_verifier( | |
builder: *mut rustls_client_config_builder, | |
verifier: *const rustls_server_cert_verifier, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
let builder: &mut ClientConfigBuilder = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let verifier = match crate::try_from(verifier) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
builder.verifier = verifier.clone(); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Set the ALPN protocol list to the given protocols. `protocols` must point | |
/// to a buffer of `rustls_slice_bytes` (built by the caller) with `len` | |
/// elements. Each element of the buffer must be a rustls_slice_bytes whose | |
/// data field points to a single ALPN protocol ID. Standard ALPN protocol | |
/// IDs are defined at | |
/// <https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids>. | |
/// | |
/// This function makes a copy of the data in `protocols` and does not retain | |
/// any pointers, so the caller can free the pointed-to memory after calling. | |
/// | |
/// <https://docs.rs/rustls/latest/rustls/client/struct.ClientConfig.html#structfield.alpn_protocols> | |
#[no_mangle] | |
pub extern "C" fn rustls_client_config_builder_set_alpn_protocols( | |
builder: *mut rustls_client_config_builder, | |
protocols: *const rustls_slice_bytes, | |
len: size_t, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let config: &mut ClientConfigBuilder = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let protocols: &[rustls_slice_bytes] = if protocols.is_null() { | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { slice::from_raw_parts(protocols, len as usize) } | |
}; | |
let mut vv: Vec<Vec<u8>> = Vec::with_capacity(protocols.len()); | |
for p in protocols { | |
let v: &[u8] = if p.data.is_null() { | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { slice::from_raw_parts(p.data, p.len as usize) } | |
}; | |
vv.push(v.to_vec()); | |
} | |
config.alpn_protocols = vv; | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Enable or disable SNI. | |
/// <https://docs.rs/rustls/latest/rustls/struct.ClientConfig.html#structfield.enable_sni> | |
#[no_mangle] | |
pub extern "C" fn rustls_client_config_builder_set_enable_sni( | |
config: *mut rustls_client_config_builder, | |
enable: bool, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
let config: &mut ClientConfigBuilder = match crate::try_from_mut( | |
config, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
config.enable_sni = enable; | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Provide the configuration a list of certificates where the connection | |
/// will select the first one that is compatible with the server's signature | |
/// verification capabilities. Clients that want to support both ECDSA and | |
/// RSA certificates will want the ECSDA to go first in the list. | |
/// | |
/// The built configuration will keep a reference to all certified keys | |
/// provided. The client may `rustls_certified_key_free()` afterwards | |
/// without the configuration losing them. The same certified key may also | |
/// be used in multiple configs. | |
/// | |
/// EXPERIMENTAL: installing a client authentication callback will replace any | |
/// configured certified keys and vice versa. | |
#[no_mangle] | |
pub extern "C" fn rustls_client_config_builder_set_certified_key( | |
builder: *mut rustls_client_config_builder, | |
certified_keys: *const *const rustls_certified_key, | |
certified_keys_len: size_t, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let config: &mut ClientConfigBuilder = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let keys_ptrs: &[*const rustls_certified_key] = if certified_keys | |
.is_null() | |
{ | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { | |
slice::from_raw_parts( | |
certified_keys, | |
certified_keys_len as usize, | |
) | |
} | |
}; | |
let mut keys: Vec<Arc<CertifiedKey>> = Vec::new(); | |
for &key_ptr in keys_ptrs { | |
let certified_key: Arc<CertifiedKey> = match crate::clone_arc( | |
key_ptr, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
keys.push(certified_key); | |
} | |
config | |
.cert_resolver = Some( | |
Arc::new(ResolvesClientCertFromChoices { | |
keys, | |
}), | |
); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
/// Always send the same client certificate. | |
struct ResolvesClientCertFromChoices { | |
keys: Vec<Arc<CertifiedKey>>, | |
} | |
#[automatically_derived] | |
impl ::core::fmt::Debug for ResolvesClientCertFromChoices { | |
#[inline] | |
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { | |
::core::fmt::Formatter::debug_struct_field1_finish( | |
f, | |
"ResolvesClientCertFromChoices", | |
"keys", | |
&&self.keys, | |
) | |
} | |
} | |
impl ResolvesClientCert for ResolvesClientCertFromChoices { | |
fn resolve( | |
&self, | |
_acceptable_issuers: &[&[u8]], | |
sig_schemes: &[rustls::SignatureScheme], | |
) -> Option<Arc<rustls::sign::CertifiedKey>> { | |
for key in self.keys.iter() { | |
if key.key.choose_scheme(sig_schemes).is_some() { | |
return Some(key.clone()); | |
} | |
} | |
None | |
} | |
fn has_certs(&self) -> bool { | |
!self.keys.is_empty() | |
} | |
} | |
impl rustls_client_config_builder { | |
/// Turn a *rustls_client_config_builder (mutable) into a const *rustls_client_config | |
/// (read-only). | |
#[no_mangle] | |
pub extern "C" fn rustls_client_config_builder_build( | |
builder: *mut rustls_client_config_builder, | |
) -> *const rustls_client_config { | |
match ::std::panic::catch_unwind(|| { | |
let builder: Box<ClientConfigBuilder> = match crate::try_box_from( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let config = builder | |
.base | |
.dangerous() | |
.with_custom_certificate_verifier(builder.verifier); | |
let mut config = match builder.cert_resolver { | |
Some(r) => config.with_client_cert_resolver(r), | |
None => config.with_no_client_auth(), | |
}; | |
config.alpn_protocols = builder.alpn_protocols; | |
config.enable_sni = builder.enable_sni; | |
to_arc_const_ptr(config) | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// "Free" a client_config_builder without building it into a rustls_client_config. | |
/// Normally builders are built into rustls_client_config via `rustls_client_config_builder_build` | |
/// and may not be free'd or otherwise used afterwards. | |
/// Use free only when the building of a config has to be aborted before a config | |
/// was created. | |
#[no_mangle] | |
pub extern "C" fn rustls_client_config_builder_free( | |
config: *mut rustls_client_config_builder, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
free_box(config); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
impl rustls_client_config { | |
/// "Free" a rustls_client_config previously returned from | |
/// rustls_client_config_builder_build. Since rustls_client_config is actually an | |
/// atomically reference-counted pointer, extant client connections may still | |
/// hold an internal reference to the Rust object. However, C code must | |
/// consider this pointer unusable after "free"ing it. | |
/// Calling with NULL is fine. Must not be called twice with the same value. | |
#[no_mangle] | |
pub extern "C" fn rustls_client_config_free( | |
config: *const rustls_client_config, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
free_arc(config); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Create a new rustls_connection containing a client connection and return | |
/// it in the output parameter `out`. If this returns an error code, the | |
/// memory pointed to by `conn_out` remains unchanged. If this returns a | |
/// non-error, the memory pointed to by `conn_out` is modified to point at a | |
/// valid rustls_connection. The caller now owns the rustls_connection and must | |
/// call `rustls_connection_free` when done with it. | |
/// | |
/// The server_name parameter can contain a hostname or an IP address in | |
/// textual form (IPv4 or IPv6). This function will return an error if it | |
/// cannot be parsed as one of those types. | |
#[no_mangle] | |
pub extern "C" fn rustls_client_connection_new( | |
config: *const rustls_client_config, | |
server_name: *const c_char, | |
conn_out: *mut *mut rustls_connection, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
if conn_out.is_null() { | |
return NullParameter; | |
} | |
let server_name: &CStr = unsafe { | |
if server_name.is_null() { | |
return NullParameter; | |
} | |
CStr::from_ptr(server_name) | |
}; | |
let config: Arc<ClientConfig> = match crate::clone_arc(config) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let server_name: &str = match server_name.to_str() { | |
Ok(s) => s, | |
Err(std::str::Utf8Error { .. }) => { | |
return rustls_result::InvalidDnsNameError; | |
} | |
}; | |
let server_name: pki_types::ServerName = match server_name.try_into() { | |
Ok(sn) => sn, | |
Err(_) => return rustls_result::InvalidDnsNameError, | |
}; | |
let client = ClientConnection::new(config, server_name).unwrap(); | |
let c = Connection::from_client(client); | |
set_boxed_mut_ptr(conn_out, c); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
} | |
pub mod connection { | |
use std::io::{ErrorKind, Read, Write}; | |
use std::{ffi::c_void, ptr::null}; | |
use std::{ptr::null_mut, slice}; | |
use libc::{size_t, EINVAL, EIO}; | |
use pki_types::CertificateDer; | |
use rustls::crypto::ring::ALL_CIPHER_SUITES; | |
use rustls::{ClientConnection, ServerConnection, SupportedCipherSuite}; | |
use crate::io::{ | |
rustls_write_vectored_callback, CallbackReader, CallbackWriter, ReadCallback, | |
VectoredCallbackWriter, VectoredWriteCallback, WriteCallback, | |
}; | |
use crate::log::{ensure_log_registered, rustls_log_callback}; | |
use crate::{ | |
box_castable, cipher::{rustls_certificate, rustls_supported_ciphersuite}, | |
error::{map_error, rustls_io_result, rustls_result}, | |
ffi_panic_boundary, free_box, io::{rustls_read_callback, rustls_write_callback}, | |
try_callback, try_mut_from_ptr, try_ref_from_ptr, try_slice, userdata_push, | |
}; | |
use rustls_result::NullParameter; | |
pub(crate) struct Connection { | |
conn: rustls::Connection, | |
userdata: *mut c_void, | |
log_callback: rustls_log_callback, | |
} | |
impl Connection { | |
pub(crate) fn from_client(conn: ClientConnection) -> Self { | |
Connection { | |
conn: conn.into(), | |
userdata: null_mut(), | |
log_callback: None, | |
} | |
} | |
pub(crate) fn from_server(conn: ServerConnection) -> Self { | |
Connection { | |
conn: conn.into(), | |
userdata: null_mut(), | |
log_callback: None, | |
} | |
} | |
#[allow(dead_code)] | |
pub(crate) fn as_client(&self) -> Option<&ClientConnection> { | |
match &self.conn { | |
rustls::Connection::Client(c) => Some(c), | |
_ => None, | |
} | |
} | |
pub(crate) fn as_server(&self) -> Option<&ServerConnection> { | |
match &self.conn { | |
rustls::Connection::Server(s) => Some(s), | |
_ => None, | |
} | |
} | |
#[allow(dead_code)] | |
pub(crate) fn as_client_mut(&mut self) -> Option<&mut ClientConnection> { | |
match &mut self.conn { | |
rustls::Connection::Client(c) => Some(c), | |
_ => None, | |
} | |
} | |
#[allow(dead_code)] | |
pub(crate) fn as_server_mut(&mut self) -> Option<&mut ServerConnection> { | |
match &mut self.conn { | |
rustls::Connection::Server(s) => Some(s), | |
_ => None, | |
} | |
} | |
} | |
impl std::ops::Deref for Connection { | |
type Target = rustls::Connection; | |
fn deref(&self) -> &Self::Target { | |
&self.conn | |
} | |
} | |
impl std::ops::DerefMut for Connection { | |
fn deref_mut(&mut self) -> &mut Self::Target { | |
&mut self.conn | |
} | |
} | |
pub struct rustls_connection { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_connection { | |
type Ownership = crate::OwnershipBox; | |
type RustType = Connection; | |
} | |
impl rustls_connection { | |
/// Set the userdata pointer associated with this connection. This will be passed | |
/// to any callbacks invoked by the connection, if you've set up callbacks in the config. | |
/// The pointed-to data must outlive the connection. | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_set_userdata( | |
conn: *mut rustls_connection, | |
userdata: *mut c_void, | |
) { | |
let conn: &mut Connection = match crate::try_from_mut(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
conn.userdata = userdata; | |
} | |
/// Set the logging callback for this connection. The log callback will be invoked | |
/// with the userdata parameter previously set by rustls_connection_set_userdata, or | |
/// NULL if no userdata was set. | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_set_log_callback( | |
conn: *mut rustls_connection, | |
cb: rustls_log_callback, | |
) { | |
let conn: &mut Connection = match crate::try_from_mut(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
ensure_log_registered(); | |
conn.log_callback = cb; | |
} | |
/// Read some TLS bytes from the network into internal buffers. The actual network | |
/// I/O is performed by `callback`, which you provide. Rustls will invoke your | |
/// callback with a suitable buffer to store the read bytes into. You don't have | |
/// to fill it up, just fill with as many bytes as you get in one syscall. | |
/// The `userdata` parameter is passed through directly to `callback`. Note that | |
/// this is distinct from the `userdata` parameter set with | |
/// `rustls_connection_set_userdata`. | |
/// Returns 0 for success, or an errno value on error. Passes through return values | |
/// from callback. See rustls_read_callback for more details. | |
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.read_tls> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_read_tls( | |
conn: *mut rustls_connection, | |
callback: rustls_read_callback, | |
userdata: *mut c_void, | |
out_n: *mut size_t, | |
) -> rustls_io_result { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &mut Connection = match crate::try_from_mut(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
if out_n.is_null() { | |
return rustls_io_result(EINVAL); | |
} | |
let callback: ReadCallback = match callback { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let mut reader = CallbackReader { | |
callback, | |
userdata, | |
}; | |
let n_read: usize = match conn.read_tls(&mut reader) { | |
Ok(n) => n, | |
Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), | |
}; | |
unsafe { | |
*out_n = n_read; | |
} | |
rustls_io_result(0) | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Write some TLS bytes to the network. The actual network I/O is performed by | |
/// `callback`, which you provide. Rustls will invoke your callback with a | |
/// suitable buffer containing TLS bytes to send. You don't have to write them | |
/// all, just as many as you can in one syscall. | |
/// The `userdata` parameter is passed through directly to `callback`. Note that | |
/// this is distinct from the `userdata` parameter set with | |
/// `rustls_connection_set_userdata`. | |
/// Returns 0 for success, or an errno value on error. Passes through return values | |
/// from callback. See rustls_write_callback for more details. | |
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.write_tls> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_write_tls( | |
conn: *mut rustls_connection, | |
callback: rustls_write_callback, | |
userdata: *mut c_void, | |
out_n: *mut size_t, | |
) -> rustls_io_result { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &mut Connection = match crate::try_from_mut(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
if out_n.is_null() { | |
return rustls_io_result(EINVAL); | |
} | |
let callback: WriteCallback = match callback { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let mut writer = CallbackWriter { | |
callback, | |
userdata, | |
}; | |
let n_written: usize = match conn.write_tls(&mut writer) { | |
Ok(n) => n, | |
Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), | |
}; | |
unsafe { | |
*out_n = n_written; | |
} | |
rustls_io_result(0) | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Write all available TLS bytes to the network. The actual network I/O is performed by | |
/// `callback`, which you provide. Rustls will invoke your callback with an array | |
/// of rustls_slice_bytes, each containing a buffer with TLS bytes to send. | |
/// You don't have to write them all, just as many as you are willing. | |
/// The `userdata` parameter is passed through directly to `callback`. Note that | |
/// this is distinct from the `userdata` parameter set with | |
/// `rustls_connection_set_userdata`. | |
/// Returns 0 for success, or an errno value on error. Passes through return values | |
/// from callback. See rustls_write_callback for more details. | |
/// <https://docs.rs/rustls/latest/rustls/struct.Writer.html#method.write_vectored> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_write_tls_vectored( | |
conn: *mut rustls_connection, | |
callback: rustls_write_vectored_callback, | |
userdata: *mut c_void, | |
out_n: *mut size_t, | |
) -> rustls_io_result { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &mut Connection = match crate::try_from_mut(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
if out_n.is_null() { | |
return rustls_io_result(EINVAL); | |
} | |
let callback: VectoredWriteCallback = match callback { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let mut writer = VectoredCallbackWriter { | |
callback, | |
userdata, | |
}; | |
let n_written: usize = match conn.write_tls(&mut writer) { | |
Ok(n) => n, | |
Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), | |
}; | |
unsafe { | |
*out_n = n_written; | |
} | |
rustls_io_result(0) | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Decrypt any available ciphertext from the internal buffer and put it | |
/// into the internal plaintext buffer, potentially making bytes available | |
/// for rustls_connection_read(). | |
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.process_new_packets> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_process_new_packets( | |
conn: *mut rustls_connection, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &mut Connection = match crate::try_from_mut(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let guard = match userdata_push(conn.userdata, conn.log_callback) { | |
Ok(g) => g, | |
Err(_) => return rustls_result::Panic, | |
}; | |
let result = match conn.process_new_packets() { | |
Ok(_) => rustls_result::Ok, | |
Err(e) => map_error(e), | |
}; | |
match guard.try_drop() { | |
Ok(()) => result, | |
Err(_) => rustls_result::Panic, | |
} | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// <https://docs.rs/rustls/latest/rustls/struct.CommonState.html#method.wants_read> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_wants_read( | |
conn: *const rustls_connection, | |
) -> bool { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &Connection = match crate::try_from(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
conn.wants_read() | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// <https://docs.rs/rustls/latest/rustls/struct.CommonState.html#method.wants_write> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_wants_write( | |
conn: *const rustls_connection, | |
) -> bool { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &Connection = match crate::try_from(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
conn.wants_write() | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// <https://docs.rs/rustls/latest/rustls/struct.CommonState.html#method.is_handshaking> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_is_handshaking( | |
conn: *const rustls_connection, | |
) -> bool { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &Connection = match crate::try_from(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
conn.is_handshaking() | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Sets a limit on the internal buffers used to buffer unsent plaintext (prior | |
/// to completing the TLS handshake) and unsent TLS records. By default, there | |
/// is no limit. The limit can be set at any time, even if the current buffer | |
/// use is higher. | |
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.set_buffer_limit> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_set_buffer_limit( | |
conn: *mut rustls_connection, | |
n: usize, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &mut Connection = match crate::try_from_mut(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
conn.set_buffer_limit(Some(n)); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Queues a close_notify fatal alert to be sent in the next write_tls call. | |
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.send_close_notify> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_send_close_notify( | |
conn: *mut rustls_connection, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &mut Connection = match crate::try_from_mut(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
conn.send_close_notify(); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Return the i-th certificate provided by the peer. | |
/// Index 0 is the end entity certificate. Higher indexes are certificates | |
/// in the chain. Requesting an index higher than what is available returns | |
/// NULL. | |
/// The returned pointer is valid until the next mutating function call | |
/// affecting the connection. A mutating function call is one where the | |
/// first argument has type `struct rustls_connection *` (as opposed to | |
/// `const struct rustls_connection *`). | |
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.peer_certificates> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_get_peer_certificate<'a>( | |
conn: *const rustls_connection, | |
i: size_t, | |
) -> *const rustls_certificate<'a> { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &Connection = match crate::try_from(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
match conn.peer_certificates().and_then(|c| c.get(i)) { | |
Some(cert) => cert as *const CertificateDer as *const _, | |
None => null(), | |
} | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Get the ALPN protocol that was negotiated, if any. Stores a pointer to a | |
/// borrowed buffer of bytes, and that buffer's len, in the output parameters. | |
/// The borrow lives as long as the connection. | |
/// If the connection is still handshaking, or no ALPN protocol was negotiated, | |
/// stores NULL and 0 in the output parameters. | |
/// The provided pointer is valid until the next mutating function call | |
/// affecting the connection. A mutating function call is one where the | |
/// first argument has type `struct rustls_connection *` (as opposed to | |
/// `const struct rustls_connection *`). | |
/// <https://www.iana.org/assignments/tls-parameters/> | |
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.alpn_protocol> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_get_alpn_protocol( | |
conn: *const rustls_connection, | |
protocol_out: *mut *const u8, | |
protocol_out_len: *mut usize, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &Connection = match crate::try_from(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
if protocol_out.is_null() || protocol_out_len.is_null() { | |
return; | |
} | |
match conn.alpn_protocol() { | |
Some(p) => { | |
unsafe { | |
*protocol_out = p.as_ptr(); | |
*protocol_out_len = p.len(); | |
} | |
} | |
None => { | |
unsafe { | |
*protocol_out = null(); | |
*protocol_out_len = 0; | |
} | |
} | |
} | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Return the TLS protocol version that has been negotiated. Before this | |
/// has been decided during the handshake, this will return 0. Otherwise, | |
/// the u16 version number as defined in the relevant RFC is returned. | |
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.protocol_version> | |
/// <https://docs.rs/rustls/latest/rustls/internal/msgs/enums/enum.ProtocolVersion.html> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_get_protocol_version( | |
conn: *const rustls_connection, | |
) -> u16 { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &Connection = match crate::try_from(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
match conn.protocol_version() { | |
Some(p) => u16::from(p), | |
_ => 0, | |
} | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Retrieves the cipher suite agreed with the peer. | |
/// This returns NULL until the ciphersuite is agreed. | |
/// The returned pointer lives as long as the program. | |
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.negotiated_cipher_suite> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_get_negotiated_ciphersuite( | |
conn: *const rustls_connection, | |
) -> *const rustls_supported_ciphersuite { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &Connection = match crate::try_from(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let negotiated = match conn.negotiated_cipher_suite() { | |
Some(cs) => cs, | |
None => return null(), | |
}; | |
for cs in ALL_CIPHER_SUITES { | |
let cs: &'static SupportedCipherSuite = cs; | |
if negotiated == *cs { | |
return cs as *const SupportedCipherSuite as *const _; | |
} | |
} | |
null() | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Write up to `count` plaintext bytes from `buf` into the `rustls_connection`. | |
/// This will increase the number of output bytes available to | |
/// `rustls_connection_write_tls`. | |
/// On success, store the number of bytes actually written in *out_n | |
/// (this may be less than `count`). | |
/// <https://docs.rs/rustls/latest/rustls/struct.Writer.html#method.write> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_write( | |
conn: *mut rustls_connection, | |
buf: *const u8, | |
count: size_t, | |
out_n: *mut size_t, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &mut Connection = match crate::try_from_mut(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let write_buf: &[u8] = if buf.is_null() { | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { slice::from_raw_parts(buf, count as usize) } | |
}; | |
if out_n.is_null() { | |
return NullParameter; | |
} | |
let n_written: usize = match conn.writer().write(write_buf) { | |
Ok(n) => n, | |
Err(_) => return rustls_result::Io, | |
}; | |
unsafe { | |
*out_n = n_written; | |
} | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Read up to `count` plaintext bytes from the `rustls_connection` into `buf`. | |
/// On success, store the number of bytes read in *out_n (this may be less | |
/// than `count`). A success with *out_n set to 0 means "all bytes currently | |
/// available have been read, but more bytes may become available after | |
/// subsequent calls to rustls_connection_read_tls and | |
/// rustls_connection_process_new_packets." | |
/// | |
/// Subtle note: Even though this function only writes to `buf` and does not | |
/// read from it, the memory in `buf` must be initialized before the call (for | |
/// Rust-internal reasons). Initializing a buffer once and then using it | |
/// multiple times without zeroizing before each call is fine. | |
/// <https://docs.rs/rustls/latest/rustls/struct.Reader.html#method.read> | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_read( | |
conn: *mut rustls_connection, | |
buf: *mut u8, | |
count: size_t, | |
out_n: *mut size_t, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &mut Connection = match crate::try_from_mut(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
if buf.is_null() { | |
return NullParameter; | |
} | |
if out_n.is_null() { | |
return NullParameter; | |
} | |
let read_buf: &mut [u8] = unsafe { | |
slice::from_raw_parts_mut(buf, count) | |
}; | |
let n_read: usize = match conn.reader().read(read_buf) { | |
Ok(n) => n, | |
Err(e) if e.kind() == ErrorKind::UnexpectedEof => { | |
return rustls_result::UnexpectedEof; | |
} | |
Err(e) if e.kind() == ErrorKind::WouldBlock => { | |
return rustls_result::PlaintextEmpty; | |
} | |
Err(_) => return rustls_result::Io, | |
}; | |
unsafe { | |
*out_n = n_read; | |
} | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Free a rustls_connection. Calling with NULL is fine. | |
/// Must not be called twice with the same value. | |
#[no_mangle] | |
pub extern "C" fn rustls_connection_free(conn: *mut rustls_connection) { | |
match ::std::panic::catch_unwind(|| { | |
free_box(conn); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
} | |
pub mod enums { | |
#[repr(C)] | |
#[allow(dead_code)] | |
/// Definitions of known TLS protocol versions. | |
pub enum rustls_tls_version { | |
Sslv2 = 0x0200, | |
Sslv3 = 0x0300, | |
Tlsv1_0 = 0x0301, | |
Tlsv1_1 = 0x0302, | |
Tlsv1_2 = 0x0303, | |
Tlsv1_3 = 0x0304, | |
} | |
/// Rustls' list of supported protocol versions. The length of the array is | |
/// given by `RUSTLS_ALL_VERSIONS_LEN`. | |
#[no_mangle] | |
pub static RUSTLS_ALL_VERSIONS: [u16; 2] = [ | |
rustls_tls_version::Tlsv1_3 as u16, | |
rustls_tls_version::Tlsv1_2 as u16, | |
]; | |
/// The length of the array `RUSTLS_ALL_VERSIONS`. | |
#[no_mangle] | |
pub static RUSTLS_ALL_VERSIONS_LEN: usize = RUSTLS_ALL_VERSIONS.len(); | |
/// Rustls' default list of protocol versions. The length of the array is | |
/// given by `RUSTLS_DEFAULT_VERSIONS_LEN`. | |
#[no_mangle] | |
pub static RUSTLS_DEFAULT_VERSIONS: [u16; 2] = [ | |
rustls_tls_version::Tlsv1_3 as u16, | |
rustls_tls_version::Tlsv1_2 as u16, | |
]; | |
/// The length of the array `RUSTLS_DEFAULT_VERSIONS`. | |
#[no_mangle] | |
pub static RUSTLS_DEFAULT_VERSIONS_LEN: usize = RUSTLS_DEFAULT_VERSIONS.len(); | |
} | |
mod error { | |
use std::cmp::min; | |
use std::fmt::Display; | |
use std::sync::Arc; | |
use crate::ffi_panic_boundary; | |
use libc::{c_char, c_uint, size_t}; | |
use rustls::server::VerifierBuilderError; | |
use rustls::{CertRevocationListError, CertificateError, Error, InvalidMessage}; | |
/// A return value for a function that may return either success (0) or a | |
/// non-zero value representing an error. The values should match socket | |
/// error numbers for your operating system - for example, the integers for | |
/// ETIMEDOUT, EAGAIN, or similar. | |
#[repr(transparent)] | |
pub struct rustls_io_result(pub libc::c_int); | |
#[automatically_derived] | |
impl ::core::fmt::Debug for rustls_io_result { | |
#[inline] | |
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { | |
::core::fmt::Formatter::debug_tuple_field1_finish( | |
f, | |
"rustls_io_result", | |
&&self.0, | |
) | |
} | |
} | |
#[automatically_derived] | |
impl ::core::clone::Clone for rustls_io_result { | |
#[inline] | |
fn clone(&self) -> rustls_io_result { | |
let _: ::core::clone::AssertParamIsClone<libc::c_int>; | |
*self | |
} | |
} | |
#[automatically_derived] | |
impl ::core::marker::Copy for rustls_io_result {} | |
#[automatically_derived] | |
impl ::core::marker::StructuralPartialEq for rustls_io_result {} | |
#[automatically_derived] | |
impl ::core::cmp::PartialEq for rustls_io_result { | |
#[inline] | |
fn eq(&self, other: &rustls_io_result) -> bool { | |
self.0 == other.0 | |
} | |
} | |
#[automatically_derived] | |
impl ::core::cmp::Eq for rustls_io_result { | |
#[inline] | |
#[doc(hidden)] | |
#[coverage(off)] | |
fn assert_receiver_is_total_eq(&self) -> () { | |
let _: ::core::cmp::AssertParamIsEq<libc::c_int>; | |
} | |
} | |
#[allow(dead_code)] | |
#[repr(u32)] | |
pub enum rustls_result { | |
Ok = 7000, | |
Io = 7001, | |
NullParameter = 7002, | |
InvalidDnsNameError = 7003, | |
Panic = 7004, | |
CertificateParseError = 7005, | |
PrivateKeyParseError = 7006, | |
InsufficientSize = 7007, | |
NotFound = 7008, | |
InvalidParameter = 7009, | |
UnexpectedEof = 7010, | |
PlaintextEmpty = 7011, | |
AcceptorNotReady = 7012, | |
AlreadyUsed = 7013, | |
CertificateRevocationListParseError = 7014, | |
NoCertificatesPresented = 7101, | |
DecryptError = 7102, | |
FailedToGetCurrentTime = 7103, | |
FailedToGetRandomBytes = 7113, | |
HandshakeNotComplete = 7104, | |
PeerSentOversizedRecord = 7105, | |
NoApplicationProtocol = 7106, | |
BadMaxFragmentSize = 7114, | |
UnsupportedNameType = 7115, | |
EncryptError = 7116, | |
CertEncodingBad = 7121, | |
CertExpired = 7122, | |
CertNotYetValid = 7123, | |
CertRevoked = 7124, | |
CertUnhandledCriticalExtension = 7125, | |
CertUnknownIssuer = 7126, | |
CertBadSignature = 7127, | |
CertNotValidForName = 7128, | |
CertInvalidPurpose = 7129, | |
CertApplicationVerificationFailure = 7130, | |
CertOtherError = 7131, | |
MessageHandshakePayloadTooLarge = 7133, | |
MessageInvalidCcs = 7134, | |
MessageInvalidContentType = 7135, | |
MessageInvalidCertStatusType = 7136, | |
MessageInvalidCertRequest = 7137, | |
MessageInvalidDhParams = 7138, | |
MessageInvalidEmptyPayload = 7139, | |
MessageInvalidKeyUpdate = 7140, | |
MessageInvalidServerName = 7141, | |
MessageTooLarge = 7142, | |
MessageTooShort = 7143, | |
MessageMissingData = 7144, | |
MessageMissingKeyExchange = 7145, | |
MessageNoSignatureSchemes = 7146, | |
MessageTrailingData = 7147, | |
MessageUnexpectedMessage = 7148, | |
MessageUnknownProtocolVersion = 7149, | |
MessageUnsupportedCompression = 7150, | |
MessageUnsupportedCurveType = 7151, | |
MessageUnsupportedKeyExchangeAlgorithm = 7152, | |
MessageInvalidOther = 7153, | |
PeerIncompatibleError = 7107, | |
PeerMisbehavedError = 7108, | |
InappropriateMessage = 7109, | |
InappropriateHandshakeMessage = 7110, | |
General = 7112, | |
AlertCloseNotify = 7200, | |
AlertUnexpectedMessage = 7201, | |
AlertBadRecordMac = 7202, | |
AlertDecryptionFailed = 7203, | |
AlertRecordOverflow = 7204, | |
AlertDecompressionFailure = 7205, | |
AlertHandshakeFailure = 7206, | |
AlertNoCertificate = 7207, | |
AlertBadCertificate = 7208, | |
AlertUnsupportedCertificate = 7209, | |
AlertCertificateRevoked = 7210, | |
AlertCertificateExpired = 7211, | |
AlertCertificateUnknown = 7212, | |
AlertIllegalParameter = 7213, | |
AlertUnknownCA = 7214, | |
AlertAccessDenied = 7215, | |
AlertDecodeError = 7216, | |
AlertDecryptError = 7217, | |
AlertExportRestriction = 7218, | |
AlertProtocolVersion = 7219, | |
AlertInsufficientSecurity = 7220, | |
AlertInternalError = 7221, | |
AlertInappropriateFallback = 7222, | |
AlertUserCanceled = 7223, | |
AlertNoRenegotiation = 7224, | |
AlertMissingExtension = 7225, | |
AlertUnsupportedExtension = 7226, | |
AlertCertificateUnobtainable = 7227, | |
AlertUnrecognisedName = 7228, | |
AlertBadCertificateStatusResponse = 7229, | |
AlertBadCertificateHashValue = 7230, | |
AlertUnknownPSKIdentity = 7231, | |
AlertCertificateRequired = 7232, | |
AlertNoApplicationProtocol = 7233, | |
AlertUnknown = 7234, | |
CertRevocationListBadSignature = 7400, | |
CertRevocationListInvalidCrlNumber = 7401, | |
CertRevocationListInvalidRevokedCertSerialNumber = 7402, | |
CertRevocationListIssuerInvalidForCrl = 7403, | |
CertRevocationListOtherError = 7404, | |
CertRevocationListParseError = 7405, | |
CertRevocationListUnsupportedCrlVersion = 7406, | |
CertRevocationListUnsupportedCriticalExtension = 7407, | |
CertRevocationListUnsupportedDeltaCrl = 7408, | |
CertRevocationListUnsupportedIndirectCrl = 7409, | |
CertRevocationListUnsupportedRevocationReason = 7410, | |
ClientCertVerifierBuilderNoRootAnchors = 7500, | |
} | |
#[automatically_derived] | |
#[allow(dead_code)] | |
impl ::core::fmt::Debug for rustls_result { | |
#[inline] | |
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { | |
::core::fmt::Formatter::write_str( | |
f, | |
match self { | |
rustls_result::Ok => "Ok", | |
rustls_result::Io => "Io", | |
rustls_result::NullParameter => "NullParameter", | |
rustls_result::InvalidDnsNameError => "InvalidDnsNameError", | |
rustls_result::Panic => "Panic", | |
rustls_result::CertificateParseError => "CertificateParseError", | |
rustls_result::PrivateKeyParseError => "PrivateKeyParseError", | |
rustls_result::InsufficientSize => "InsufficientSize", | |
rustls_result::NotFound => "NotFound", | |
rustls_result::InvalidParameter => "InvalidParameter", | |
rustls_result::UnexpectedEof => "UnexpectedEof", | |
rustls_result::PlaintextEmpty => "PlaintextEmpty", | |
rustls_result::AcceptorNotReady => "AcceptorNotReady", | |
rustls_result::AlreadyUsed => "AlreadyUsed", | |
rustls_result::CertificateRevocationListParseError => { | |
"CertificateRevocationListParseError" | |
} | |
rustls_result::NoCertificatesPresented => "NoCertificatesPresented", | |
rustls_result::DecryptError => "DecryptError", | |
rustls_result::FailedToGetCurrentTime => "FailedToGetCurrentTime", | |
rustls_result::FailedToGetRandomBytes => "FailedToGetRandomBytes", | |
rustls_result::HandshakeNotComplete => "HandshakeNotComplete", | |
rustls_result::PeerSentOversizedRecord => "PeerSentOversizedRecord", | |
rustls_result::NoApplicationProtocol => "NoApplicationProtocol", | |
rustls_result::BadMaxFragmentSize => "BadMaxFragmentSize", | |
rustls_result::UnsupportedNameType => "UnsupportedNameType", | |
rustls_result::EncryptError => "EncryptError", | |
rustls_result::CertEncodingBad => "CertEncodingBad", | |
rustls_result::CertExpired => "CertExpired", | |
rustls_result::CertNotYetValid => "CertNotYetValid", | |
rustls_result::CertRevoked => "CertRevoked", | |
rustls_result::CertUnhandledCriticalExtension => { | |
"CertUnhandledCriticalExtension" | |
} | |
rustls_result::CertUnknownIssuer => "CertUnknownIssuer", | |
rustls_result::CertBadSignature => "CertBadSignature", | |
rustls_result::CertNotValidForName => "CertNotValidForName", | |
rustls_result::CertInvalidPurpose => "CertInvalidPurpose", | |
rustls_result::CertApplicationVerificationFailure => { | |
"CertApplicationVerificationFailure" | |
} | |
rustls_result::CertOtherError => "CertOtherError", | |
rustls_result::MessageHandshakePayloadTooLarge => { | |
"MessageHandshakePayloadTooLarge" | |
} | |
rustls_result::MessageInvalidCcs => "MessageInvalidCcs", | |
rustls_result::MessageInvalidContentType => { | |
"MessageInvalidContentType" | |
} | |
rustls_result::MessageInvalidCertStatusType => { | |
"MessageInvalidCertStatusType" | |
} | |
rustls_result::MessageInvalidCertRequest => { | |
"MessageInvalidCertRequest" | |
} | |
rustls_result::MessageInvalidDhParams => "MessageInvalidDhParams", | |
rustls_result::MessageInvalidEmptyPayload => { | |
"MessageInvalidEmptyPayload" | |
} | |
rustls_result::MessageInvalidKeyUpdate => "MessageInvalidKeyUpdate", | |
rustls_result::MessageInvalidServerName => "MessageInvalidServerName", | |
rustls_result::MessageTooLarge => "MessageTooLarge", | |
rustls_result::MessageTooShort => "MessageTooShort", | |
rustls_result::MessageMissingData => "MessageMissingData", | |
rustls_result::MessageMissingKeyExchange => { | |
"MessageMissingKeyExchange" | |
} | |
rustls_result::MessageNoSignatureSchemes => { | |
"MessageNoSignatureSchemes" | |
} | |
rustls_result::MessageTrailingData => "MessageTrailingData", | |
rustls_result::MessageUnexpectedMessage => "MessageUnexpectedMessage", | |
rustls_result::MessageUnknownProtocolVersion => { | |
"MessageUnknownProtocolVersion" | |
} | |
rustls_result::MessageUnsupportedCompression => { | |
"MessageUnsupportedCompression" | |
} | |
rustls_result::MessageUnsupportedCurveType => { | |
"MessageUnsupportedCurveType" | |
} | |
rustls_result::MessageUnsupportedKeyExchangeAlgorithm => { | |
"MessageUnsupportedKeyExchangeAlgorithm" | |
} | |
rustls_result::MessageInvalidOther => "MessageInvalidOther", | |
rustls_result::PeerIncompatibleError => "PeerIncompatibleError", | |
rustls_result::PeerMisbehavedError => "PeerMisbehavedError", | |
rustls_result::InappropriateMessage => "InappropriateMessage", | |
rustls_result::InappropriateHandshakeMessage => { | |
"InappropriateHandshakeMessage" | |
} | |
rustls_result::General => "General", | |
rustls_result::AlertCloseNotify => "AlertCloseNotify", | |
rustls_result::AlertUnexpectedMessage => "AlertUnexpectedMessage", | |
rustls_result::AlertBadRecordMac => "AlertBadRecordMac", | |
rustls_result::AlertDecryptionFailed => "AlertDecryptionFailed", | |
rustls_result::AlertRecordOverflow => "AlertRecordOverflow", | |
rustls_result::AlertDecompressionFailure => { | |
"AlertDecompressionFailure" | |
} | |
rustls_result::AlertHandshakeFailure => "AlertHandshakeFailure", | |
rustls_result::AlertNoCertificate => "AlertNoCertificate", | |
rustls_result::AlertBadCertificate => "AlertBadCertificate", | |
rustls_result::AlertUnsupportedCertificate => { | |
"AlertUnsupportedCertificate" | |
} | |
rustls_result::AlertCertificateRevoked => "AlertCertificateRevoked", | |
rustls_result::AlertCertificateExpired => "AlertCertificateExpired", | |
rustls_result::AlertCertificateUnknown => "AlertCertificateUnknown", | |
rustls_result::AlertIllegalParameter => "AlertIllegalParameter", | |
rustls_result::AlertUnknownCA => "AlertUnknownCA", | |
rustls_result::AlertAccessDenied => "AlertAccessDenied", | |
rustls_result::AlertDecodeError => "AlertDecodeError", | |
rustls_result::AlertDecryptError => "AlertDecryptError", | |
rustls_result::AlertExportRestriction => "AlertExportRestriction", | |
rustls_result::AlertProtocolVersion => "AlertProtocolVersion", | |
rustls_result::AlertInsufficientSecurity => { | |
"AlertInsufficientSecurity" | |
} | |
rustls_result::AlertInternalError => "AlertInternalError", | |
rustls_result::AlertInappropriateFallback => { | |
"AlertInappropriateFallback" | |
} | |
rustls_result::AlertUserCanceled => "AlertUserCanceled", | |
rustls_result::AlertNoRenegotiation => "AlertNoRenegotiation", | |
rustls_result::AlertMissingExtension => "AlertMissingExtension", | |
rustls_result::AlertUnsupportedExtension => { | |
"AlertUnsupportedExtension" | |
} | |
rustls_result::AlertCertificateUnobtainable => { | |
"AlertCertificateUnobtainable" | |
} | |
rustls_result::AlertUnrecognisedName => "AlertUnrecognisedName", | |
rustls_result::AlertBadCertificateStatusResponse => { | |
"AlertBadCertificateStatusResponse" | |
} | |
rustls_result::AlertBadCertificateHashValue => { | |
"AlertBadCertificateHashValue" | |
} | |
rustls_result::AlertUnknownPSKIdentity => "AlertUnknownPSKIdentity", | |
rustls_result::AlertCertificateRequired => "AlertCertificateRequired", | |
rustls_result::AlertNoApplicationProtocol => { | |
"AlertNoApplicationProtocol" | |
} | |
rustls_result::AlertUnknown => "AlertUnknown", | |
rustls_result::CertRevocationListBadSignature => { | |
"CertRevocationListBadSignature" | |
} | |
rustls_result::CertRevocationListInvalidCrlNumber => { | |
"CertRevocationListInvalidCrlNumber" | |
} | |
rustls_result::CertRevocationListInvalidRevokedCertSerialNumber => { | |
"CertRevocationListInvalidRevokedCertSerialNumber" | |
} | |
rustls_result::CertRevocationListIssuerInvalidForCrl => { | |
"CertRevocationListIssuerInvalidForCrl" | |
} | |
rustls_result::CertRevocationListOtherError => { | |
"CertRevocationListOtherError" | |
} | |
rustls_result::CertRevocationListParseError => { | |
"CertRevocationListParseError" | |
} | |
rustls_result::CertRevocationListUnsupportedCrlVersion => { | |
"CertRevocationListUnsupportedCrlVersion" | |
} | |
rustls_result::CertRevocationListUnsupportedCriticalExtension => { | |
"CertRevocationListUnsupportedCriticalExtension" | |
} | |
rustls_result::CertRevocationListUnsupportedDeltaCrl => { | |
"CertRevocationListUnsupportedDeltaCrl" | |
} | |
rustls_result::CertRevocationListUnsupportedIndirectCrl => { | |
"CertRevocationListUnsupportedIndirectCrl" | |
} | |
rustls_result::CertRevocationListUnsupportedRevocationReason => { | |
"CertRevocationListUnsupportedRevocationReason" | |
} | |
rustls_result::ClientCertVerifierBuilderNoRootAnchors => { | |
"ClientCertVerifierBuilderNoRootAnchors" | |
} | |
}, | |
) | |
} | |
} | |
#[automatically_derived] | |
#[allow(dead_code)] | |
impl ::core::clone::Clone for rustls_result { | |
#[inline] | |
fn clone(&self) -> rustls_result { | |
*self | |
} | |
} | |
#[automatically_derived] | |
#[allow(dead_code)] | |
impl ::core::marker::Copy for rustls_result {} | |
#[automatically_derived] | |
#[allow(dead_code)] | |
impl ::core::marker::StructuralPartialEq for rustls_result {} | |
#[automatically_derived] | |
#[allow(dead_code)] | |
impl ::core::cmp::PartialEq for rustls_result { | |
#[inline] | |
fn eq(&self, other: &rustls_result) -> bool { | |
let __self_tag = ::core::intrinsics::discriminant_value(self); | |
let __arg1_tag = ::core::intrinsics::discriminant_value(other); | |
__self_tag == __arg1_tag | |
} | |
} | |
#[automatically_derived] | |
#[allow(dead_code)] | |
impl ::core::cmp::Eq for rustls_result { | |
#[inline] | |
#[doc(hidden)] | |
#[coverage(off)] | |
fn assert_receiver_is_total_eq(&self) -> () {} | |
} | |
impl From<u32> for rustls_result { | |
fn from(x: u32) -> Self { | |
match x { | |
7000 => rustls_result::Ok, | |
7001 => rustls_result::Io, | |
7002 => rustls_result::NullParameter, | |
7003 => rustls_result::InvalidDnsNameError, | |
7004 => rustls_result::Panic, | |
7005 => rustls_result::CertificateParseError, | |
7006 => rustls_result::PrivateKeyParseError, | |
7007 => rustls_result::InsufficientSize, | |
7008 => rustls_result::NotFound, | |
7009 => rustls_result::InvalidParameter, | |
7010 => rustls_result::UnexpectedEof, | |
7011 => rustls_result::PlaintextEmpty, | |
7012 => rustls_result::AcceptorNotReady, | |
7013 => rustls_result::AlreadyUsed, | |
7014 => rustls_result::CertificateRevocationListParseError, | |
7101 => rustls_result::NoCertificatesPresented, | |
7102 => rustls_result::DecryptError, | |
7103 => rustls_result::FailedToGetCurrentTime, | |
7113 => rustls_result::FailedToGetRandomBytes, | |
7104 => rustls_result::HandshakeNotComplete, | |
7105 => rustls_result::PeerSentOversizedRecord, | |
7106 => rustls_result::NoApplicationProtocol, | |
7114 => rustls_result::BadMaxFragmentSize, | |
7115 => rustls_result::UnsupportedNameType, | |
7116 => rustls_result::EncryptError, | |
7121 => rustls_result::CertEncodingBad, | |
7122 => rustls_result::CertExpired, | |
7123 => rustls_result::CertNotYetValid, | |
7124 => rustls_result::CertRevoked, | |
7125 => rustls_result::CertUnhandledCriticalExtension, | |
7126 => rustls_result::CertUnknownIssuer, | |
7127 => rustls_result::CertBadSignature, | |
7128 => rustls_result::CertNotValidForName, | |
7129 => rustls_result::CertInvalidPurpose, | |
7130 => rustls_result::CertApplicationVerificationFailure, | |
7131 => rustls_result::CertOtherError, | |
7133 => rustls_result::MessageHandshakePayloadTooLarge, | |
7134 => rustls_result::MessageInvalidCcs, | |
7135 => rustls_result::MessageInvalidContentType, | |
7136 => rustls_result::MessageInvalidCertStatusType, | |
7137 => rustls_result::MessageInvalidCertRequest, | |
7138 => rustls_result::MessageInvalidDhParams, | |
7139 => rustls_result::MessageInvalidEmptyPayload, | |
7140 => rustls_result::MessageInvalidKeyUpdate, | |
7141 => rustls_result::MessageInvalidServerName, | |
7142 => rustls_result::MessageTooLarge, | |
7143 => rustls_result::MessageTooShort, | |
7144 => rustls_result::MessageMissingData, | |
7145 => rustls_result::MessageMissingKeyExchange, | |
7146 => rustls_result::MessageNoSignatureSchemes, | |
7147 => rustls_result::MessageTrailingData, | |
7148 => rustls_result::MessageUnexpectedMessage, | |
7149 => rustls_result::MessageUnknownProtocolVersion, | |
7150 => rustls_result::MessageUnsupportedCompression, | |
7151 => rustls_result::MessageUnsupportedCurveType, | |
7152 => rustls_result::MessageUnsupportedKeyExchangeAlgorithm, | |
7153 => rustls_result::MessageInvalidOther, | |
7107 => rustls_result::PeerIncompatibleError, | |
7108 => rustls_result::PeerMisbehavedError, | |
7109 => rustls_result::InappropriateMessage, | |
7110 => rustls_result::InappropriateHandshakeMessage, | |
7112 => rustls_result::General, | |
7200 => rustls_result::AlertCloseNotify, | |
7201 => rustls_result::AlertUnexpectedMessage, | |
7202 => rustls_result::AlertBadRecordMac, | |
7203 => rustls_result::AlertDecryptionFailed, | |
7204 => rustls_result::AlertRecordOverflow, | |
7205 => rustls_result::AlertDecompressionFailure, | |
7206 => rustls_result::AlertHandshakeFailure, | |
7207 => rustls_result::AlertNoCertificate, | |
7208 => rustls_result::AlertBadCertificate, | |
7209 => rustls_result::AlertUnsupportedCertificate, | |
7210 => rustls_result::AlertCertificateRevoked, | |
7211 => rustls_result::AlertCertificateExpired, | |
7212 => rustls_result::AlertCertificateUnknown, | |
7213 => rustls_result::AlertIllegalParameter, | |
7214 => rustls_result::AlertUnknownCA, | |
7215 => rustls_result::AlertAccessDenied, | |
7216 => rustls_result::AlertDecodeError, | |
7217 => rustls_result::AlertDecryptError, | |
7218 => rustls_result::AlertExportRestriction, | |
7219 => rustls_result::AlertProtocolVersion, | |
7220 => rustls_result::AlertInsufficientSecurity, | |
7221 => rustls_result::AlertInternalError, | |
7222 => rustls_result::AlertInappropriateFallback, | |
7223 => rustls_result::AlertUserCanceled, | |
7224 => rustls_result::AlertNoRenegotiation, | |
7225 => rustls_result::AlertMissingExtension, | |
7226 => rustls_result::AlertUnsupportedExtension, | |
7227 => rustls_result::AlertCertificateUnobtainable, | |
7228 => rustls_result::AlertUnrecognisedName, | |
7229 => rustls_result::AlertBadCertificateStatusResponse, | |
7230 => rustls_result::AlertBadCertificateHashValue, | |
7231 => rustls_result::AlertUnknownPSKIdentity, | |
7232 => rustls_result::AlertCertificateRequired, | |
7233 => rustls_result::AlertNoApplicationProtocol, | |
7234 => rustls_result::AlertUnknown, | |
7400 => rustls_result::CertRevocationListBadSignature, | |
7401 => rustls_result::CertRevocationListInvalidCrlNumber, | |
7402 => rustls_result::CertRevocationListInvalidRevokedCertSerialNumber, | |
7403 => rustls_result::CertRevocationListIssuerInvalidForCrl, | |
7404 => rustls_result::CertRevocationListOtherError, | |
7405 => rustls_result::CertRevocationListParseError, | |
7406 => rustls_result::CertRevocationListUnsupportedCrlVersion, | |
7407 => rustls_result::CertRevocationListUnsupportedCriticalExtension, | |
7408 => rustls_result::CertRevocationListUnsupportedDeltaCrl, | |
7409 => rustls_result::CertRevocationListUnsupportedIndirectCrl, | |
7410 => rustls_result::CertRevocationListUnsupportedRevocationReason, | |
7500 => rustls_result::ClientCertVerifierBuilderNoRootAnchors, | |
_ => rustls_result::InvalidParameter, | |
} | |
} | |
} | |
impl rustls_result { | |
/// After a rustls function returns an error, you may call | |
/// this to get a pointer to a buffer containing a detailed error | |
/// message. The contents of the error buffer will be out_n bytes long, | |
/// UTF-8 encoded, and not NUL-terminated. | |
#[no_mangle] | |
pub extern "C" fn rustls_error( | |
result: c_uint, | |
buf: *mut c_char, | |
len: size_t, | |
out_n: *mut size_t, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
if buf.is_null() { | |
return; | |
} | |
if out_n.is_null() { | |
return; | |
} | |
let error_str = rustls_result::from(result).to_string(); | |
let out_len: usize = min(len - 1, error_str.len()); | |
unsafe { | |
std::ptr::copy_nonoverlapping( | |
error_str.as_ptr() as *mut c_char, | |
buf, | |
out_len, | |
); | |
*out_n = out_len; | |
} | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
#[no_mangle] | |
pub extern "C" fn rustls_result_is_cert_error(result: c_uint) -> bool { | |
use rustls_result::*; | |
match rustls_result::from(result) { | |
CertEncodingBad | |
| CertExpired | |
| CertNotYetValid | |
| CertRevoked | |
| CertUnhandledCriticalExtension | |
| CertUnknownIssuer | |
| CertBadSignature | |
| CertNotValidForName | |
| CertInvalidPurpose | |
| CertApplicationVerificationFailure | |
| CertOtherError => true, | |
_ => false, | |
} | |
} | |
} | |
/// For cert-related rustls_results, turn them into a rustls::Error. For other | |
/// inputs, including Ok, return rustls::Error::General. | |
pub(crate) fn cert_result_to_error(result: rustls_result) -> rustls::Error { | |
use rustls::Error::*; | |
use rustls::OtherError; | |
use rustls_result::*; | |
match result { | |
CertEncodingBad => InvalidCertificate(CertificateError::BadEncoding), | |
CertExpired => InvalidCertificate(CertificateError::Expired), | |
CertNotYetValid => InvalidCertificate(CertificateError::NotValidYet), | |
CertRevoked => InvalidCertificate(CertificateError::Revoked), | |
CertUnhandledCriticalExtension => { | |
InvalidCertificate(CertificateError::UnhandledCriticalExtension) | |
} | |
CertUnknownIssuer => InvalidCertificate(CertificateError::UnknownIssuer), | |
CertBadSignature => InvalidCertificate(CertificateError::BadSignature), | |
CertNotValidForName => InvalidCertificate(CertificateError::NotValidForName), | |
CertInvalidPurpose => InvalidCertificate(CertificateError::InvalidPurpose), | |
CertApplicationVerificationFailure => { | |
InvalidCertificate(CertificateError::ApplicationVerificationFailure) | |
} | |
CertOtherError => { | |
InvalidCertificate( | |
CertificateError::Other(OtherError(Arc::from(Box::from("")))), | |
) | |
} | |
_ => rustls::Error::General("".into()), | |
} | |
} | |
pub(crate) fn map_error(input: rustls::Error) -> rustls_result { | |
use rustls::AlertDescription as alert; | |
use rustls_result::*; | |
match input { | |
Error::InappropriateMessage { .. } => InappropriateMessage, | |
Error::InappropriateHandshakeMessage { .. } => InappropriateHandshakeMessage, | |
Error::NoCertificatesPresented => NoCertificatesPresented, | |
Error::DecryptError => DecryptError, | |
Error::PeerIncompatible(_) => PeerIncompatibleError, | |
Error::PeerMisbehaved(_) => PeerMisbehavedError, | |
Error::UnsupportedNameType => UnsupportedNameType, | |
Error::EncryptError => EncryptError, | |
Error::InvalidMessage(e) => { | |
match e { | |
InvalidMessage::HandshakePayloadTooLarge => { | |
MessageHandshakePayloadTooLarge | |
} | |
InvalidMessage::InvalidCcs => MessageInvalidCcs, | |
InvalidMessage::InvalidContentType => MessageInvalidContentType, | |
InvalidMessage::InvalidCertificateStatusType => { | |
MessageInvalidCertStatusType | |
} | |
InvalidMessage::InvalidCertRequest => MessageInvalidCertRequest, | |
InvalidMessage::InvalidDhParams => MessageInvalidDhParams, | |
InvalidMessage::InvalidEmptyPayload => MessageInvalidEmptyPayload, | |
InvalidMessage::InvalidKeyUpdate => MessageInvalidKeyUpdate, | |
InvalidMessage::InvalidServerName => MessageInvalidServerName, | |
InvalidMessage::MessageTooLarge => MessageTooLarge, | |
InvalidMessage::MessageTooShort => MessageTooShort, | |
InvalidMessage::MissingData(_) => MessageMissingData, | |
InvalidMessage::MissingKeyExchange => MessageMissingKeyExchange, | |
InvalidMessage::NoSignatureSchemes => MessageNoSignatureSchemes, | |
InvalidMessage::TrailingData(_) => MessageTrailingData, | |
InvalidMessage::UnexpectedMessage(_) => MessageUnexpectedMessage, | |
InvalidMessage::UnknownProtocolVersion => { | |
MessageUnknownProtocolVersion | |
} | |
InvalidMessage::UnsupportedCompression => { | |
MessageUnsupportedCompression | |
} | |
InvalidMessage::UnsupportedCurveType => MessageUnsupportedCurveType, | |
InvalidMessage::UnsupportedKeyExchangeAlgorithm(_) => { | |
MessageUnsupportedCompression | |
} | |
_ => MessageInvalidOther, | |
} | |
} | |
Error::FailedToGetCurrentTime => FailedToGetCurrentTime, | |
Error::FailedToGetRandomBytes => FailedToGetRandomBytes, | |
Error::HandshakeNotComplete => HandshakeNotComplete, | |
Error::PeerSentOversizedRecord => PeerSentOversizedRecord, | |
Error::NoApplicationProtocol => NoApplicationProtocol, | |
Error::BadMaxFragmentSize => BadMaxFragmentSize, | |
Error::InvalidCertificate(e) => { | |
match e { | |
CertificateError::BadEncoding => CertEncodingBad, | |
CertificateError::Expired => CertExpired, | |
CertificateError::NotValidYet => CertNotYetValid, | |
CertificateError::Revoked => CertRevoked, | |
CertificateError::UnhandledCriticalExtension => { | |
CertUnhandledCriticalExtension | |
} | |
CertificateError::UnknownIssuer => CertUnknownIssuer, | |
CertificateError::BadSignature => CertBadSignature, | |
CertificateError::NotValidForName => CertNotValidForName, | |
CertificateError::InvalidPurpose => CertInvalidPurpose, | |
CertificateError::ApplicationVerificationFailure => { | |
CertApplicationVerificationFailure | |
} | |
_ => CertOtherError, | |
} | |
} | |
Error::General(_) => General, | |
Error::AlertReceived(e) => { | |
match e { | |
alert::CloseNotify => AlertCloseNotify, | |
alert::UnexpectedMessage => AlertUnexpectedMessage, | |
alert::BadRecordMac => AlertBadRecordMac, | |
alert::DecryptionFailed => AlertDecryptionFailed, | |
alert::RecordOverflow => AlertRecordOverflow, | |
alert::DecompressionFailure => AlertDecompressionFailure, | |
alert::HandshakeFailure => AlertHandshakeFailure, | |
alert::NoCertificate => AlertNoCertificate, | |
alert::BadCertificate => AlertBadCertificate, | |
alert::UnsupportedCertificate => AlertUnsupportedCertificate, | |
alert::CertificateRevoked => AlertCertificateRevoked, | |
alert::CertificateExpired => AlertCertificateExpired, | |
alert::CertificateUnknown => AlertCertificateUnknown, | |
alert::IllegalParameter => AlertIllegalParameter, | |
alert::UnknownCA => AlertUnknownCA, | |
alert::AccessDenied => AlertAccessDenied, | |
alert::DecodeError => AlertDecodeError, | |
alert::DecryptError => AlertDecryptError, | |
alert::ExportRestriction => AlertExportRestriction, | |
alert::ProtocolVersion => AlertProtocolVersion, | |
alert::InsufficientSecurity => AlertInsufficientSecurity, | |
alert::InternalError => AlertInternalError, | |
alert::InappropriateFallback => AlertInappropriateFallback, | |
alert::UserCanceled => AlertUserCanceled, | |
alert::NoRenegotiation => AlertNoRenegotiation, | |
alert::MissingExtension => AlertMissingExtension, | |
alert::UnsupportedExtension => AlertUnsupportedExtension, | |
alert::CertificateUnobtainable => AlertCertificateUnobtainable, | |
alert::UnrecognisedName => AlertUnrecognisedName, | |
alert::BadCertificateStatusResponse => { | |
AlertBadCertificateStatusResponse | |
} | |
alert::BadCertificateHashValue => AlertBadCertificateHashValue, | |
alert::UnknownPSKIdentity => AlertUnknownPSKIdentity, | |
alert::CertificateRequired => AlertCertificateRequired, | |
alert::NoApplicationProtocol => AlertNoApplicationProtocol, | |
alert::Unknown(_) => AlertUnknown, | |
_ => AlertUnknown, | |
} | |
} | |
Error::InvalidCertRevocationList(e) => map_crl_error(e), | |
_ => General, | |
} | |
} | |
pub(crate) fn map_crl_error(err: CertRevocationListError) -> rustls_result { | |
use rustls_result::*; | |
match err { | |
CertRevocationListError::BadSignature => CertRevocationListBadSignature, | |
CertRevocationListError::InvalidCrlNumber => { | |
CertRevocationListInvalidCrlNumber | |
} | |
CertRevocationListError::InvalidRevokedCertSerialNumber => { | |
CertRevocationListInvalidRevokedCertSerialNumber | |
} | |
CertRevocationListError::IssuerInvalidForCrl => { | |
CertRevocationListIssuerInvalidForCrl | |
} | |
CertRevocationListError::Other(_) => CertRevocationListOtherError, | |
CertRevocationListError::ParseError => CertRevocationListParseError, | |
CertRevocationListError::UnsupportedCrlVersion => { | |
CertRevocationListUnsupportedCrlVersion | |
} | |
CertRevocationListError::UnsupportedCriticalExtension => { | |
CertRevocationListUnsupportedCriticalExtension | |
} | |
CertRevocationListError::UnsupportedDeltaCrl => { | |
CertRevocationListUnsupportedDeltaCrl | |
} | |
CertRevocationListError::UnsupportedIndirectCrl => { | |
CertRevocationListUnsupportedIndirectCrl | |
} | |
CertRevocationListError::UnsupportedRevocationReason => { | |
CertRevocationListUnsupportedRevocationReason | |
} | |
_ => CertRevocationListOtherError, | |
} | |
} | |
pub(crate) fn map_verifier_builder_error( | |
err: VerifierBuilderError, | |
) -> rustls_result { | |
match err { | |
VerifierBuilderError::NoRootAnchors => { | |
rustls_result::ClientCertVerifierBuilderNoRootAnchors | |
} | |
VerifierBuilderError::InvalidCrl(crl_err) => map_crl_error(crl_err), | |
_ => rustls_result::General, | |
} | |
} | |
impl Display for rustls_result { | |
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
use rustls::AlertDescription as alert; | |
use rustls_result::*; | |
match self { | |
rustls_result::Ok => f.write_fmt(format_args!("OK")), | |
Io => f.write_fmt(format_args!("I/O error")), | |
NullParameter => f.write_fmt(format_args!("a parameter was NULL")), | |
InvalidDnsNameError => { | |
f.write_fmt( | |
format_args!( | |
"server name was malformed (not a valid hostname or IP address)", | |
), | |
) | |
} | |
Panic => f.write_fmt(format_args!("a Rust component panicked")), | |
CertificateParseError => { | |
f.write_fmt(format_args!("error parsing certificate")) | |
} | |
PrivateKeyParseError => { | |
f.write_fmt(format_args!("error parsing private key")) | |
} | |
InsufficientSize => { | |
f.write_fmt(format_args!("provided buffer is of insufficient size")) | |
} | |
NotFound => f.write_fmt(format_args!("the item was not found")), | |
InvalidParameter => { | |
f.write_fmt(format_args!("a parameter had an invalid value")) | |
} | |
UnexpectedEof => { | |
f.write_fmt( | |
format_args!( | |
"peer closed TCP connection without first closing TLS connection", | |
), | |
) | |
} | |
PlaintextEmpty => { | |
f.write_fmt( | |
format_args!( | |
"no plaintext available; call rustls_connection_read_tls again", | |
), | |
) | |
} | |
AcceptorNotReady => { | |
f.write_fmt( | |
format_args!( | |
"rustls_acceptor not ready yet; read more TLS bytes into it", | |
), | |
) | |
} | |
AlreadyUsed => { | |
f.write_fmt( | |
format_args!( | |
"tried to use a rustls struct after it had been converted to another struct", | |
), | |
) | |
} | |
CertificateRevocationListParseError => { | |
f.write_fmt( | |
format_args!("error parsing certificate revocation list (CRL)"), | |
) | |
} | |
CertEncodingBad => { | |
Error::InvalidCertificate(CertificateError::BadEncoding).fmt(f) | |
} | |
CertExpired => { | |
Error::InvalidCertificate(CertificateError::Expired).fmt(f) | |
} | |
CertNotYetValid => { | |
Error::InvalidCertificate(CertificateError::NotValidYet).fmt(f) | |
} | |
CertRevoked => { | |
Error::InvalidCertificate(CertificateError::Revoked).fmt(f) | |
} | |
CertUnhandledCriticalExtension => { | |
Error::InvalidCertificate( | |
CertificateError::UnhandledCriticalExtension, | |
) | |
.fmt(f) | |
} | |
CertUnknownIssuer => { | |
Error::InvalidCertificate(CertificateError::UnknownIssuer).fmt(f) | |
} | |
CertBadSignature => { | |
Error::InvalidCertificate(CertificateError::BadSignature).fmt(f) | |
} | |
CertNotValidForName => { | |
Error::InvalidCertificate(CertificateError::NotValidForName).fmt(f) | |
} | |
CertInvalidPurpose => { | |
Error::InvalidCertificate(CertificateError::InvalidPurpose).fmt(f) | |
} | |
CertApplicationVerificationFailure => { | |
Error::InvalidCertificate( | |
CertificateError::ApplicationVerificationFailure, | |
) | |
.fmt(f) | |
} | |
CertOtherError => f.write_fmt(format_args!("unknown certificate error")), | |
InappropriateMessage => { | |
f.write_fmt(format_args!("received unexpected message")) | |
} | |
InappropriateHandshakeMessage => { | |
f.write_fmt(format_args!("received unexpected handshake message")) | |
} | |
MessageHandshakePayloadTooLarge => { | |
Error::InvalidMessage(InvalidMessage::HandshakePayloadTooLarge) | |
.fmt(f) | |
} | |
MessageInvalidContentType => { | |
Error::InvalidMessage(InvalidMessage::InvalidContentType).fmt(f) | |
} | |
MessageInvalidServerName => { | |
Error::InvalidMessage(InvalidMessage::InvalidServerName).fmt(f) | |
} | |
MessageTooLarge => { | |
Error::InvalidMessage(InvalidMessage::MessageTooLarge).fmt(f) | |
} | |
MessageTooShort => { | |
Error::InvalidMessage(InvalidMessage::MessageTooShort).fmt(f) | |
} | |
MessageUnknownProtocolVersion => { | |
Error::InvalidMessage(InvalidMessage::UnknownProtocolVersion).fmt(f) | |
} | |
MessageUnsupportedCompression => { | |
Error::InvalidMessage(InvalidMessage::UnsupportedCompression).fmt(f) | |
} | |
MessageInvalidEmptyPayload => { | |
Error::InvalidMessage(InvalidMessage::InvalidEmptyPayload).fmt(f) | |
} | |
MessageInvalidCertStatusType => { | |
Error::InvalidMessage(InvalidMessage::InvalidCertificateStatusType) | |
.fmt(f) | |
} | |
MessageInvalidKeyUpdate => { | |
Error::InvalidMessage(InvalidMessage::InvalidKeyUpdate).fmt(f) | |
} | |
MessageUnsupportedCurveType => { | |
Error::InvalidMessage(InvalidMessage::UnsupportedCurveType).fmt(f) | |
} | |
MessageMissingData => { | |
f.write_fmt( | |
format_args!( | |
"missing data for the named handshake payload value", | |
), | |
) | |
} | |
MessageTrailingData => { | |
f.write_fmt( | |
format_args!( | |
"trailing data found for the named handshake payload value", | |
), | |
) | |
} | |
MessageUnexpectedMessage => { | |
f.write_fmt(format_args!("peer sent unexpected message type")) | |
} | |
MessageUnsupportedKeyExchangeAlgorithm => { | |
f.write_fmt( | |
format_args!("peer sent an unsupported key exchange algorithm"), | |
) | |
} | |
MessageMissingKeyExchange => { | |
f.write_fmt( | |
format_args!( | |
"peer did not advertise supported key exchange groups", | |
), | |
) | |
} | |
MessageNoSignatureSchemes => { | |
f.write_fmt( | |
format_args!("peer sent an empty list of signature schemes"), | |
) | |
} | |
MessageInvalidDhParams => { | |
f.write_fmt( | |
format_args!( | |
"peer\'s Diffie-Hellman (DH) parameters could not be decoded", | |
), | |
) | |
} | |
MessageInvalidCertRequest => { | |
f.write_fmt(format_args!("invalid certificate request context")) | |
} | |
MessageInvalidCcs => { | |
f.write_fmt(format_args!("invalid change cipher spec (CCS) payload")) | |
} | |
MessageInvalidOther => f.write_fmt(format_args!("invalid message")), | |
PeerIncompatibleError => { | |
f.write_fmt(format_args!("peer is incompatible")) | |
} | |
PeerMisbehavedError => f.write_fmt(format_args!("peer misbehaved")), | |
General => f.write_fmt(format_args!("general error")), | |
NoCertificatesPresented => Error::NoCertificatesPresented.fmt(f), | |
DecryptError => Error::DecryptError.fmt(f), | |
FailedToGetCurrentTime => Error::FailedToGetCurrentTime.fmt(f), | |
FailedToGetRandomBytes => Error::FailedToGetRandomBytes.fmt(f), | |
HandshakeNotComplete => Error::HandshakeNotComplete.fmt(f), | |
PeerSentOversizedRecord => Error::PeerSentOversizedRecord.fmt(f), | |
NoApplicationProtocol => Error::NoApplicationProtocol.fmt(f), | |
BadMaxFragmentSize => Error::BadMaxFragmentSize.fmt(f), | |
UnsupportedNameType => Error::UnsupportedNameType.fmt(f), | |
EncryptError => Error::EncryptError.fmt(f), | |
AlertCloseNotify => Error::AlertReceived(alert::CloseNotify).fmt(f), | |
AlertUnexpectedMessage => { | |
Error::AlertReceived(alert::UnexpectedMessage).fmt(f) | |
} | |
AlertBadRecordMac => Error::AlertReceived(alert::BadRecordMac).fmt(f), | |
AlertDecryptionFailed => { | |
Error::AlertReceived(alert::DecryptionFailed).fmt(f) | |
} | |
AlertRecordOverflow => Error::AlertReceived(alert::RecordOverflow).fmt(f), | |
AlertDecompressionFailure => { | |
Error::AlertReceived(alert::DecompressionFailure).fmt(f) | |
} | |
AlertHandshakeFailure => { | |
Error::AlertReceived(alert::HandshakeFailure).fmt(f) | |
} | |
AlertNoCertificate => Error::AlertReceived(alert::NoCertificate).fmt(f), | |
AlertBadCertificate => Error::AlertReceived(alert::BadCertificate).fmt(f), | |
AlertUnsupportedCertificate => { | |
Error::AlertReceived(alert::UnsupportedCertificate).fmt(f) | |
} | |
AlertCertificateRevoked => { | |
Error::AlertReceived(alert::CertificateRevoked).fmt(f) | |
} | |
AlertCertificateExpired => { | |
Error::AlertReceived(alert::CertificateExpired).fmt(f) | |
} | |
AlertCertificateUnknown => { | |
Error::AlertReceived(alert::CertificateUnknown).fmt(f) | |
} | |
AlertIllegalParameter => { | |
Error::AlertReceived(alert::IllegalParameter).fmt(f) | |
} | |
AlertUnknownCA => Error::AlertReceived(alert::UnknownCA).fmt(f), | |
AlertAccessDenied => Error::AlertReceived(alert::AccessDenied).fmt(f), | |
AlertDecodeError => Error::AlertReceived(alert::DecodeError).fmt(f), | |
AlertDecryptError => Error::AlertReceived(alert::DecryptError).fmt(f), | |
AlertExportRestriction => { | |
Error::AlertReceived(alert::ExportRestriction).fmt(f) | |
} | |
AlertProtocolVersion => { | |
Error::AlertReceived(alert::ProtocolVersion).fmt(f) | |
} | |
AlertInsufficientSecurity => { | |
Error::AlertReceived(alert::InsufficientSecurity).fmt(f) | |
} | |
AlertInternalError => Error::AlertReceived(alert::InternalError).fmt(f), | |
AlertInappropriateFallback => { | |
Error::AlertReceived(alert::InappropriateFallback).fmt(f) | |
} | |
AlertUserCanceled => Error::AlertReceived(alert::UserCanceled).fmt(f), | |
AlertNoRenegotiation => { | |
Error::AlertReceived(alert::NoRenegotiation).fmt(f) | |
} | |
AlertMissingExtension => { | |
Error::AlertReceived(alert::MissingExtension).fmt(f) | |
} | |
AlertUnsupportedExtension => { | |
Error::AlertReceived(alert::UnsupportedExtension).fmt(f) | |
} | |
AlertCertificateUnobtainable => { | |
Error::AlertReceived(alert::CertificateUnobtainable).fmt(f) | |
} | |
AlertUnrecognisedName => { | |
Error::AlertReceived(alert::UnrecognisedName).fmt(f) | |
} | |
AlertBadCertificateStatusResponse => { | |
Error::AlertReceived(alert::BadCertificateStatusResponse).fmt(f) | |
} | |
AlertBadCertificateHashValue => { | |
Error::AlertReceived(alert::BadCertificateHashValue).fmt(f) | |
} | |
AlertUnknownPSKIdentity => { | |
Error::AlertReceived(alert::UnknownPSKIdentity).fmt(f) | |
} | |
AlertCertificateRequired => { | |
Error::AlertReceived(alert::CertificateRequired).fmt(f) | |
} | |
AlertNoApplicationProtocol => { | |
Error::AlertReceived(alert::NoApplicationProtocol).fmt(f) | |
} | |
AlertUnknown => Error::AlertReceived(alert::Unknown(0)).fmt(f), | |
CertRevocationListBadSignature => { | |
Error::InvalidCertRevocationList( | |
CertRevocationListError::BadSignature, | |
) | |
.fmt(f) | |
} | |
CertRevocationListInvalidCrlNumber => { | |
Error::InvalidCertRevocationList( | |
CertRevocationListError::InvalidCrlNumber, | |
) | |
.fmt(f) | |
} | |
CertRevocationListInvalidRevokedCertSerialNumber => { | |
Error::InvalidCertRevocationList( | |
CertRevocationListError::InvalidRevokedCertSerialNumber, | |
) | |
.fmt(f) | |
} | |
CertRevocationListIssuerInvalidForCrl => { | |
Error::InvalidCertRevocationList( | |
CertRevocationListError::IssuerInvalidForCrl, | |
) | |
.fmt(f) | |
} | |
CertRevocationListOtherError => { | |
f.write_fmt( | |
format_args!("unknown certificate revocation list (CRL) error"), | |
) | |
} | |
CertRevocationListParseError => { | |
Error::InvalidCertRevocationList(CertRevocationListError::ParseError) | |
.fmt(f) | |
} | |
CertRevocationListUnsupportedCrlVersion => { | |
Error::InvalidCertRevocationList( | |
CertRevocationListError::UnsupportedCrlVersion, | |
) | |
.fmt(f) | |
} | |
CertRevocationListUnsupportedCriticalExtension => { | |
Error::InvalidCertRevocationList( | |
CertRevocationListError::UnsupportedCriticalExtension, | |
) | |
.fmt(f) | |
} | |
CertRevocationListUnsupportedDeltaCrl => { | |
Error::InvalidCertRevocationList( | |
CertRevocationListError::UnsupportedDeltaCrl, | |
) | |
.fmt(f) | |
} | |
CertRevocationListUnsupportedIndirectCrl => { | |
Error::InvalidCertRevocationList( | |
CertRevocationListError::UnsupportedIndirectCrl, | |
) | |
.fmt(f) | |
} | |
CertRevocationListUnsupportedRevocationReason => { | |
Error::InvalidCertRevocationList( | |
CertRevocationListError::UnsupportedRevocationReason, | |
) | |
.fmt(f) | |
} | |
ClientCertVerifierBuilderNoRootAnchors => { | |
f.write_fmt(format_args!("no root trust anchors provided")) | |
} | |
} | |
} | |
} | |
} | |
pub mod io { | |
use std::io::{Error, IoSlice, Read, Result, Write}; | |
use libc::{c_void, size_t}; | |
use crate::error::rustls_io_result; | |
/// A callback for rustls_connection_read_tls. | |
/// An implementation of this callback should attempt to read up to n bytes from the | |
/// network, storing them in `buf`. If any bytes were stored, the implementation should | |
/// set out_n to the number of bytes stored and return 0. If there was an error, | |
/// the implementation should return a nonzero rustls_io_result, which will be | |
/// passed through to the caller. On POSIX systems, returning `errno` is convenient. | |
/// On other systems, any appropriate error code works. | |
/// It's best to make one read attempt to the network per call. Additional reads will | |
/// be triggered by subsequent calls to one of the `_read_tls` methods. | |
/// `userdata` is set to the value provided to `rustls_connection_set_userdata`. In most | |
/// cases that should be a struct that contains, at a minimum, a file descriptor. | |
/// The buf and out_n pointers are borrowed and should not be retained across calls. | |
pub type rustls_read_callback = Option< | |
unsafe extern "C" fn( | |
userdata: *mut c_void, | |
buf: *mut u8, | |
n: size_t, | |
out_n: *mut size_t, | |
) -> rustls_io_result, | |
>; | |
pub(crate) type ReadCallback = unsafe extern "C" fn( | |
userdata: *mut c_void, | |
buf: *mut u8, | |
n: size_t, | |
out_n: *mut size_t, | |
) -> rustls_io_result; | |
pub(crate) struct CallbackReader { | |
pub callback: ReadCallback, | |
pub userdata: *mut c_void, | |
} | |
impl Read for CallbackReader { | |
fn read(&mut self, buf: &mut [u8]) -> Result<usize> { | |
let mut out_n: usize = 0; | |
let cb = self.callback; | |
let result = unsafe { | |
cb(self.userdata, buf.as_mut_ptr(), buf.len(), &mut out_n) | |
}; | |
match result.0 { | |
0 => Ok(out_n), | |
e => Err(Error::from_raw_os_error(e)), | |
} | |
} | |
} | |
/// A callback for rustls_connection_write_tls or rustls_accepted_alert_write_tls. | |
/// An implementation of this callback should attempt to write the `n` bytes in buf | |
/// to the network. If any bytes were written, the implementation should | |
/// set out_n to the number of bytes stored and return 0. If there was an error, | |
/// the implementation should return a nonzero rustls_io_result, which will be | |
/// passed through to the caller. On POSIX systems, returning `errno` is convenient. | |
/// On other systems, any appropriate error code works. | |
/// It's best to make one write attempt to the network per call. Additional writes will | |
/// be triggered by subsequent calls to rustls_connection_write_tls. | |
/// `userdata` is set to the value provided to `rustls_connection_set_userdata`. In most | |
/// cases that should be a struct that contains, at a minimum, a file descriptor. | |
/// The buf and out_n pointers are borrowed and should not be retained across calls. | |
pub type rustls_write_callback = Option< | |
unsafe extern "C" fn( | |
userdata: *mut c_void, | |
buf: *const u8, | |
n: size_t, | |
out_n: *mut size_t, | |
) -> rustls_io_result, | |
>; | |
pub(crate) type WriteCallback = unsafe extern "C" fn( | |
userdata: *mut c_void, | |
buf: *const u8, | |
n: size_t, | |
out_n: *mut size_t, | |
) -> rustls_io_result; | |
pub(crate) struct CallbackWriter { | |
pub callback: WriteCallback, | |
pub userdata: *mut c_void, | |
} | |
impl Write for CallbackWriter { | |
fn write(&mut self, buf: &[u8]) -> Result<usize> { | |
let mut out_n: usize = 0; | |
let cb = self.callback; | |
let result = unsafe { | |
cb(self.userdata, buf.as_ptr(), buf.len(), &mut out_n) | |
}; | |
match result.0 { | |
0 => Ok(out_n), | |
e => Err(Error::from_raw_os_error(e)), | |
} | |
} | |
fn flush(&mut self) -> Result<()> { | |
Ok(()) | |
} | |
} | |
/// An alias for `struct iovec` from uio.h (on Unix) or `WSABUF` on Windows. You should cast | |
/// `const struct rustls_iovec *` to `const struct iovec *` on Unix, or `const *LPWSABUF` | |
/// on Windows. See [`std::io::IoSlice`] for details on interoperability with platform | |
/// specific vectored IO. | |
pub struct rustls_iovec { | |
_private: [u8; 0], | |
} | |
/// A callback for rustls_connection_write_tls_vectored. | |
/// An implementation of this callback should attempt to write the bytes in | |
/// the given `count` iovecs to the network. If any bytes were written, | |
/// the implementation should set out_n to the number of bytes written and return 0. | |
/// If there was an error, the implementation should return a nonzero rustls_io_result, | |
/// which will be passed through to the caller. On POSIX systems, returning `errno` is convenient. | |
/// On other systems, any appropriate error code works. | |
/// It's best to make one write attempt to the network per call. Additional write will | |
/// be triggered by subsequent calls to one of the `_write_tls` methods. | |
/// `userdata` is set to the value provided to `rustls_*_session_set_userdata`. In most | |
/// cases that should be a struct that contains, at a minimum, a file descriptor. | |
/// The buf and out_n pointers are borrowed and should not be retained across calls. | |
pub type rustls_write_vectored_callback = Option< | |
unsafe extern "C" fn( | |
userdata: *mut c_void, | |
iov: *const rustls_iovec, | |
count: size_t, | |
out_n: *mut size_t, | |
) -> rustls_io_result, | |
>; | |
pub(crate) type VectoredWriteCallback = unsafe extern "C" fn( | |
userdata: *mut c_void, | |
iov: *const rustls_iovec, | |
count: size_t, | |
out_n: *mut size_t, | |
) -> rustls_io_result; | |
pub(crate) struct VectoredCallbackWriter { | |
pub callback: VectoredWriteCallback, | |
pub userdata: *mut c_void, | |
} | |
impl Write for VectoredCallbackWriter { | |
fn write(&mut self, buf: &[u8]) -> Result<usize> { | |
self.write_vectored(&[IoSlice::new(buf)]) | |
} | |
fn flush(&mut self) -> Result<()> { | |
Ok(()) | |
} | |
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> { | |
let mut out_n: usize = 0; | |
let cb = self.callback; | |
let result = unsafe { | |
cb( | |
self.userdata, | |
bufs.as_ptr() as *const rustls_iovec, | |
bufs.len(), | |
&mut out_n, | |
) | |
}; | |
match result.0 { | |
0 => Ok(out_n), | |
e => Err(Error::from_raw_os_error(e)), | |
} | |
} | |
} | |
} | |
pub mod log { | |
use libc::c_void; | |
use log::Level; | |
use crate::{log_callback_get, rslice::rustls_str}; | |
struct Logger {} | |
impl log::Log for Logger { | |
fn enabled(&self, _metadata: &log::Metadata<'_>) -> bool { | |
true | |
} | |
fn log(&self, record: &log::Record<'_>) { | |
if let Ok((Some(cb), userdata)) = log_callback_get() { | |
let message = { | |
let res = ::alloc::fmt::format( | |
format_args!("{0} {1}", record.target(), record.args()), | |
); | |
res | |
}; | |
if let Ok(message) = message.as_str().try_into() { | |
unsafe { | |
cb( | |
userdata, | |
&rustls_log_params { | |
level: record.level() as rustls_log_level, | |
message, | |
}, | |
); | |
} | |
} | |
} | |
} | |
fn flush(&self) {} | |
} | |
#[cfg(not(feature = "no_log_capture"))] | |
pub(crate) fn ensure_log_registered() { | |
log::set_logger(&Logger {}).ok(); | |
log::set_max_level(log::LevelFilter::Debug) | |
} | |
pub type rustls_log_level = usize; | |
/// Return a rustls_str containing the stringified version of a log level. | |
#[no_mangle] | |
pub extern "C" fn rustls_log_level_str( | |
level: rustls_log_level, | |
) -> rustls_str<'static> { | |
let s = match level { | |
1 => Level::Error.as_str(), | |
2 => Level::Warn.as_str(), | |
3 => Level::Info.as_str(), | |
4 => Level::Debug.as_str(), | |
5 => Level::Trace.as_str(), | |
_ => "INVALID", | |
}; | |
rustls_str::from_str_unchecked(s) | |
} | |
#[repr(C)] | |
pub struct rustls_log_params<'a> { | |
pub level: rustls_log_level, | |
pub message: rustls_str<'a>, | |
} | |
#[allow(non_camel_case_types)] | |
pub type rustls_log_callback = Option< | |
unsafe extern "C" fn(userdata: *mut c_void, params: *const rustls_log_params), | |
>; | |
} | |
mod panic { | |
use libc::EINVAL; | |
use crate::error::{rustls_io_result, rustls_result}; | |
use crate::rslice::{rustls_slice_bytes, rustls_str}; | |
use std::ptr::{null, null_mut}; | |
pub(crate) trait PanicOrDefault { | |
fn value() -> Self; | |
} | |
pub(crate) trait NullParameterOrDefault { | |
fn value() -> Self; | |
} | |
pub(crate) trait Defaultable: Default {} | |
impl Defaultable for u16 {} | |
impl Defaultable for usize {} | |
impl Defaultable for bool {} | |
impl Defaultable for () {} | |
impl<T> Defaultable for Option<T> {} | |
impl<'a> Defaultable for rustls_slice_bytes<'a> {} | |
impl<T: Defaultable> PanicOrDefault for T { | |
fn value() -> Self { | |
Default::default() | |
} | |
} | |
impl<T> PanicOrDefault for *mut T { | |
fn value() -> Self { | |
null_mut() | |
} | |
} | |
impl<T> PanicOrDefault for *const T { | |
fn value() -> Self { | |
null() | |
} | |
} | |
impl PanicOrDefault for rustls_result { | |
fn value() -> Self { | |
rustls_result::Panic | |
} | |
} | |
impl<'a> PanicOrDefault for rustls_str<'a> { | |
fn value() -> Self { | |
rustls_str::from_str_unchecked("") | |
} | |
} | |
impl PanicOrDefault for rustls_io_result { | |
fn value() -> Self { | |
rustls_io_result(EINVAL) | |
} | |
} | |
impl<T: Defaultable> NullParameterOrDefault for T { | |
fn value() -> Self { | |
Default::default() | |
} | |
} | |
impl<T> NullParameterOrDefault for *mut T { | |
fn value() -> Self { | |
null_mut() | |
} | |
} | |
impl<T> NullParameterOrDefault for *const T { | |
fn value() -> Self { | |
null() | |
} | |
} | |
impl NullParameterOrDefault for rustls_result { | |
fn value() -> Self { | |
rustls_result::NullParameter | |
} | |
} | |
impl NullParameterOrDefault for rustls_io_result { | |
fn value() -> Self { | |
rustls_io_result(EINVAL) | |
} | |
} | |
impl<'a> NullParameterOrDefault for rustls_str<'a> { | |
fn value() -> Self { | |
rustls_str::from_str_unchecked("") | |
} | |
} | |
} | |
pub mod rslice { | |
use libc::{c_char, size_t}; | |
use std::fmt; | |
use std::marker::PhantomData; | |
use std::ptr::null; | |
use std::slice; | |
use std::str; | |
/// A read-only view on a Rust byte slice. | |
/// | |
/// This is used to pass data from rustls-ffi to callback functions provided | |
/// by the user of the API. | |
/// `len` indicates the number of bytes than can be safely read. | |
/// | |
/// The memory exposed is available as specified by the function | |
/// using this in its signature. For instance, when this is a parameter to a | |
/// callback, the lifetime will usually be the duration of the callback. | |
/// Functions that receive one of these must not dereference the data pointer | |
/// beyond the allowed lifetime. | |
#[repr(C)] | |
pub struct rustls_slice_bytes<'a> { | |
pub data: *const u8, | |
pub len: size_t, | |
phantom: PhantomData<&'a [u8]>, | |
} | |
impl<'a> Default for rustls_slice_bytes<'a> { | |
fn default() -> rustls_slice_bytes<'a> { | |
Self { | |
data: &[0u8; 0] as *const u8, | |
len: 0, | |
phantom: PhantomData, | |
} | |
} | |
} | |
impl<'a> From<&'a [u8]> for rustls_slice_bytes<'a> { | |
fn from(s: &[u8]) -> Self { | |
rustls_slice_bytes { | |
data: s.as_ptr(), | |
len: s.len(), | |
phantom: PhantomData, | |
} | |
} | |
} | |
/// A read-only view of a slice of Rust byte slices. | |
/// | |
/// This is used to pass data from rustls-ffi to callback functions provided | |
/// by the user of the API. Because Vec and slice are not `#[repr(C)]`, we | |
/// provide access via a pointer to an opaque struct and an accessor method | |
/// that acts on that struct to get entries of type `rustls_slice_bytes`. | |
/// Internally, the pointee is a `&[&[u8]]`. | |
/// | |
/// The memory exposed is available as specified by the function | |
/// using this in its signature. For instance, when this is a parameter to a | |
/// callback, the lifetime will usually be the duration of the callback. | |
/// Functions that receive one of these must not call its methods beyond the | |
/// allowed lifetime. | |
pub struct rustls_slice_slice_bytes<'a> { | |
pub(crate) inner: &'a [&'a [u8]], | |
} | |
/// Return the length of the outer slice. If the input pointer is NULL, | |
/// returns 0. | |
#[no_mangle] | |
pub extern "C" fn rustls_slice_slice_bytes_len( | |
input: *const rustls_slice_slice_bytes, | |
) -> size_t { | |
match unsafe { input.as_ref() } { | |
Some(c) => c.inner.len(), | |
None => 0, | |
} | |
} | |
/// Retrieve the nth element from the input slice of slices. If the input | |
/// pointer is NULL, or n is greater than the length of the | |
/// rustls_slice_slice_bytes, returns rustls_slice_bytes{NULL, 0}. | |
#[no_mangle] | |
pub extern "C" fn rustls_slice_slice_bytes_get( | |
input: *const rustls_slice_slice_bytes, | |
n: size_t, | |
) -> rustls_slice_bytes { | |
let input: &rustls_slice_slice_bytes = { | |
match unsafe { input.as_ref() } { | |
Some(c) => c, | |
None => { | |
return rustls_slice_bytes { | |
data: null(), | |
len: 0, | |
phantom: PhantomData, | |
}; | |
} | |
} | |
}; | |
match input.inner.get(n) { | |
Some(rsb) => (*rsb).into(), | |
None => { | |
rustls_slice_bytes { | |
data: null(), | |
len: 0, | |
phantom: PhantomData, | |
} | |
} | |
} | |
} | |
/// A read-only view on a Rust `&str`. The contents are guaranteed to be valid | |
/// UTF-8. As an additional guarantee on top of Rust's normal UTF-8 guarantee, | |
/// a `rustls_str` is guaranteed not to contain internal NUL bytes, so it is | |
/// safe to interpolate into a C string or compare using strncmp. Keep in mind | |
/// that it is not NUL-terminated. | |
/// | |
/// The memory exposed is available as specified by the function | |
/// using this in its signature. For instance, when this is a parameter to a | |
/// callback, the lifetime will usually be the duration of the callback. | |
/// Functions that receive one of these must not dereference the data pointer | |
/// beyond the allowed lifetime. | |
#[repr(C)] | |
pub struct rustls_str<'a> { | |
pub data: *const c_char, | |
pub len: size_t, | |
phantom: PhantomData<&'a str>, | |
} | |
/// NulByte represents an error converting `&str` to `rustls_str` when the &str | |
/// contains a NUL. | |
pub struct NulByte {} | |
#[automatically_derived] | |
impl ::core::fmt::Debug for NulByte { | |
#[inline] | |
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { | |
::core::fmt::Formatter::write_str(f, "NulByte") | |
} | |
} | |
impl<'a> TryFrom<&'a str> for rustls_str<'a> { | |
type Error = NulByte; | |
fn try_from(s: &str) -> Result<Self, Self::Error> { | |
if s.contains('\0') { | |
return Err(NulByte {}); | |
} | |
Ok(rustls_str { | |
data: s.as_ptr() as *const c_char, | |
len: s.len(), | |
phantom: PhantomData, | |
}) | |
} | |
} | |
impl<'a> Default for rustls_str<'a> { | |
fn default() -> rustls_str<'static> { | |
Self::from_str_unchecked("") | |
} | |
} | |
/// rustls_str represents a string that can be passed to C code. The string | |
/// should not have any internal NUL bytes and is not NUL terminated. C code | |
/// should not create rustls_str objects, they should only be created in Rust | |
/// code. | |
impl<'a> rustls_str<'a> { | |
pub fn from_str_unchecked(s: &'static str) -> rustls_str<'static> { | |
rustls_str { | |
data: s.as_ptr() as *const _, | |
len: s.len(), | |
phantom: PhantomData, | |
} | |
} | |
/// Change a rustls_str's lifetime to 'static. This doesn't actually change | |
/// how long the pointed-to data lives, but is necessary when returning a | |
/// rustls_str (as opposed to passing it into a callback), because Rust | |
/// can't figure out the "real" lifetime. | |
/// | |
/// # Safety | |
/// | |
/// The caller is responsible for requiring (usually via | |
/// documentation) that nothing uses the resulting rustls_str past its | |
/// actual validity period. The validity period is somewhat ill-defined | |
/// at present, but the Stacked Borrows experiment provides one definition, | |
/// by which a shared reference is valid until a mutable reference (to | |
/// the object or a parent object) is created. | |
pub unsafe fn into_static(self) -> rustls_str<'static> { | |
std::mem::transmute(self) | |
} | |
} | |
impl<'a> fmt::Debug for rustls_str<'a> { | |
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
let raw = unsafe { slice::from_raw_parts(self.data as *const u8, self.len) }; | |
let s = str::from_utf8(raw).unwrap_or("%!(ERROR)"); | |
f.debug_struct("rustls_str") | |
.field("data", &s) | |
.field("len", &self.len) | |
.finish() | |
} | |
} | |
/// A read-only view of a slice of multiple Rust `&str`'s (that is, multiple | |
/// strings). Like `rustls_str`, this guarantees that each string contains | |
/// UTF-8 and no NUL bytes. Strings are not NUL-terminated. | |
/// | |
/// This is used to pass data from rustls-ffi to callback functions provided | |
/// by the user of the API. Because Vec and slice are not `#[repr(C)]`, we | |
/// can't provide a straightforward `data` and `len` structure. Instead, we | |
/// provide access via a pointer to an opaque struct and accessor methods. | |
/// Internally, the pointee is a `&[&str]`. | |
/// | |
/// The memory exposed is available as specified by the function | |
/// using this in its signature. For instance, when this is a parameter to a | |
/// callback, the lifetime will usually be the duration of the callback. | |
/// Functions that receive one of these must not call its methods beyond the | |
/// allowed lifetime. | |
pub struct rustls_slice_str<'a> { | |
pub(crate) inner: &'a [&'a str], | |
} | |
/// Return the length of the outer slice. If the input pointer is NULL, | |
/// returns 0. | |
#[no_mangle] | |
pub extern "C" fn rustls_slice_str_len(input: *const rustls_slice_str) -> size_t { | |
unsafe { | |
match input.as_ref() { | |
Some(c) => c.inner.len(), | |
None => 0, | |
} | |
} | |
} | |
/// Retrieve the nth element from the input slice of `&str`s. If the input | |
/// pointer is NULL, or n is greater than the length of the | |
/// rustls_slice_str, returns rustls_str{NULL, 0}. | |
#[no_mangle] | |
pub extern "C" fn rustls_slice_str_get( | |
input: *const rustls_slice_str, | |
n: size_t, | |
) -> rustls_str { | |
let input: &rustls_slice_str = unsafe { | |
match input.as_ref() { | |
Some(c) => c, | |
None => { | |
return rustls_str { | |
data: null(), | |
len: 0, | |
phantom: PhantomData, | |
}; | |
} | |
} | |
}; | |
input | |
.inner | |
.get(n) | |
.and_then(|&s| s.try_into().ok()) | |
.unwrap_or(rustls_str { | |
data: null(), | |
len: 0, | |
phantom: PhantomData, | |
}) | |
} | |
/// A read-only view on a Rust slice of 16-bit integers in platform endianness. | |
/// | |
/// This is used to pass data from rustls-ffi to callback functions provided | |
/// by the user of the API. | |
/// `len` indicates the number of bytes than can be safely read. | |
/// | |
/// The memory exposed is available as specified by the function | |
/// using this in its signature. For instance, when this is a parameter to a | |
/// callback, the lifetime will usually be the duration of the callback. | |
/// Functions that receive one of these must not dereference the data pointer | |
/// beyond the allowed lifetime. | |
#[repr(C)] | |
pub struct rustls_slice_u16<'a> { | |
pub data: *const u16, | |
pub len: size_t, | |
phantom: PhantomData<&'a [u16]>, | |
} | |
impl<'a> From<&'a [u16]> for rustls_slice_u16<'a> { | |
fn from(s: &[u16]) -> Self { | |
rustls_slice_u16 { | |
data: s.as_ptr(), | |
len: s.len(), | |
phantom: PhantomData, | |
} | |
} | |
} | |
} | |
pub mod server { | |
use std::ffi::c_void; | |
use std::fmt::{Debug, Formatter}; | |
use std::ptr::null; | |
use std::slice; | |
use std::sync::Arc; | |
use libc::size_t; | |
use rustls::crypto::ring::ALL_CIPHER_SUITES; | |
use rustls::server::danger::ClientCertVerifier; | |
use rustls::server::{ | |
ClientHello, ResolvesServerCert, ServerConfig, ServerConnection, | |
StoresServerSessions, WebPkiClientVerifier, | |
}; | |
use rustls::sign::CertifiedKey; | |
use rustls::{ProtocolVersion, SignatureScheme, SupportedCipherSuite, WantsVerifier}; | |
use crate::cipher::{ | |
rustls_certified_key, rustls_client_cert_verifier, rustls_supported_ciphersuite, | |
}; | |
use crate::connection::{rustls_connection, Connection}; | |
use crate::error::rustls_result::{InvalidParameter, NullParameter}; | |
use crate::error::{map_error, rustls_result}; | |
use crate::rslice::{ | |
rustls_slice_bytes, rustls_slice_slice_bytes, rustls_slice_u16, rustls_str, | |
}; | |
use crate::session::{ | |
rustls_session_store_get_callback, rustls_session_store_put_callback, | |
SessionStoreBroker, SessionStoreGetCallback, SessionStorePutCallback, | |
}; | |
use crate::{ | |
arc_castable, box_castable, ffi_panic_boundary, free_arc, free_box, | |
set_boxed_mut_ptr, to_arc_const_ptr, to_boxed_mut_ptr, try_box_from_ptr, | |
try_clone_arc, try_mut_from_ptr, try_ref_from_ptr, try_slice, userdata_get, | |
Castable, OwnershipRef, | |
}; | |
/// A server config being constructed. A builder can be modified by, | |
/// e.g. rustls_server_config_builder_load_native_roots. Once you're | |
/// done configuring settings, call rustls_server_config_builder_build | |
/// to turn it into a *const rustls_server_config. This object is not safe | |
/// for concurrent mutation. | |
/// <https://docs.rs/rustls/latest/rustls/struct.ConfigBuilder.html> | |
pub struct rustls_server_config_builder { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_server_config_builder { | |
type Ownership = crate::OwnershipBox; | |
type RustType = ServerConfigBuilder; | |
} | |
pub(crate) struct ServerConfigBuilder { | |
base: rustls::ConfigBuilder<ServerConfig, WantsVerifier>, | |
verifier: Arc<dyn ClientCertVerifier>, | |
cert_resolver: Option<Arc<dyn ResolvesServerCert>>, | |
session_storage: Option<Arc<dyn StoresServerSessions + Send + Sync>>, | |
alpn_protocols: Vec<Vec<u8>>, | |
ignore_client_order: Option<bool>, | |
} | |
/// A server config that is done being constructed and is now read-only. | |
/// Under the hood, this object corresponds to an `Arc<ServerConfig>`. | |
/// <https://docs.rs/rustls/latest/rustls/struct.ServerConfig.html> | |
pub struct rustls_server_config { | |
_private: [u8; 0], | |
} | |
impl crate::Castable for rustls_server_config { | |
type Ownership = crate::OwnershipArc; | |
type RustType = ServerConfig; | |
} | |
impl rustls_server_config_builder { | |
/// Create a rustls_server_config_builder. Caller owns the memory and must | |
/// eventually call rustls_server_config_builder_build, then free the | |
/// resulting rustls_server_config. This uses rustls safe default values | |
/// for the cipher suites, key exchange groups and protocol versions. | |
#[no_mangle] | |
pub extern "C" fn rustls_server_config_builder_new() -> *mut rustls_server_config_builder { | |
match ::std::panic::catch_unwind(|| { | |
let base = ServerConfig::builder_with_provider( | |
rustls::crypto::ring::default_provider().into(), | |
) | |
.with_safe_default_protocol_versions() | |
.unwrap(); | |
let builder = ServerConfigBuilder { | |
base, | |
verifier: WebPkiClientVerifier::no_client_auth(), | |
cert_resolver: None, | |
session_storage: None, | |
alpn_protocols: ::alloc::vec::Vec::new(), | |
ignore_client_order: None, | |
}; | |
to_boxed_mut_ptr(builder) | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Create a rustls_server_config_builder. Caller owns the memory and must | |
/// eventually call rustls_server_config_builder_build, then free the | |
/// resulting rustls_server_config. Specify cipher suites in preference | |
/// order; the `cipher_suites` parameter must point to an array containing | |
/// `len` pointers to `rustls_supported_ciphersuite` previously obtained | |
/// from `rustls_all_ciphersuites_get_entry()`. Set the TLS protocol | |
/// versions to use when negotiating a TLS session. | |
/// | |
/// `tls_version` is the version of the protocol, as defined in rfc8446, | |
/// ch. 4.2.1 and end of ch. 5.1. Some values are defined in | |
/// `rustls_tls_version` for convenience. | |
/// | |
/// `versions` will only be used during the call and the application retains | |
/// ownership. `len` is the number of consecutive `uint16_t` pointed to by `versions`. | |
#[no_mangle] | |
pub extern "C" fn rustls_server_config_builder_new_custom( | |
cipher_suites: *const *const rustls_supported_ciphersuite, | |
cipher_suites_len: size_t, | |
tls_versions: *const u16, | |
tls_versions_len: size_t, | |
builder_out: *mut *mut rustls_server_config_builder, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
if builder_out.is_null() { | |
return NullParameter; | |
} | |
let cipher_suites: &[*const rustls_supported_ciphersuite] = if cipher_suites | |
.is_null() | |
{ | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { | |
slice::from_raw_parts(cipher_suites, cipher_suites_len as usize) | |
} | |
}; | |
let mut cs_vec: Vec<SupportedCipherSuite> = Vec::new(); | |
for &cs in cipher_suites.iter() { | |
let cs = match crate::try_from(cs) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
match ALL_CIPHER_SUITES.iter().find(|&acs| cs.eq(acs)) { | |
Some(scs) => cs_vec.push(*scs), | |
None => return InvalidParameter, | |
} | |
} | |
let tls_versions: &[u16] = if tls_versions.is_null() { | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { | |
slice::from_raw_parts(tls_versions, tls_versions_len as usize) | |
} | |
}; | |
let mut versions = ::alloc::vec::Vec::new(); | |
for version_number in tls_versions { | |
let proto = ProtocolVersion::from(*version_number); | |
if proto == rustls::version::TLS12.version { | |
versions.push(&rustls::version::TLS12); | |
} else if proto == rustls::version::TLS13.version { | |
versions.push(&rustls::version::TLS13); | |
} | |
} | |
let provider = rustls::crypto::CryptoProvider { | |
cipher_suites: cs_vec, | |
..rustls::crypto::ring::default_provider() | |
}; | |
let result = rustls::ServerConfig::builder_with_provider(provider.into()) | |
.with_protocol_versions(&versions); | |
let base = match result { | |
Ok(new) => new, | |
Err(_) => return rustls_result::InvalidParameter, | |
}; | |
let builder = ServerConfigBuilder { | |
base, | |
verifier: WebPkiClientVerifier::no_client_auth(), | |
cert_resolver: None, | |
session_storage: None, | |
alpn_protocols: ::alloc::vec::Vec::new(), | |
ignore_client_order: None, | |
}; | |
set_boxed_mut_ptr(builder_out, builder); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Create a rustls_server_config_builder for TLS sessions that may verify client | |
/// certificates. This increases the refcount of `verifier` and doesn't take ownership. | |
#[no_mangle] | |
pub extern "C" fn rustls_server_config_builder_set_client_verifier( | |
builder: *mut rustls_server_config_builder, | |
verifier: *const rustls_client_cert_verifier, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
let builder: &mut ServerConfigBuilder = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let verifier = match crate::try_from(verifier) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
builder.verifier = verifier.clone(); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// "Free" a server_config_builder without building it into a rustls_server_config. | |
/// Normally builders are built into rustls_server_configs via `rustls_server_config_builder_build` | |
/// and may not be free'd or otherwise used afterwards. | |
/// Use free only when the building of a config has to be aborted before a config | |
/// was created. | |
#[no_mangle] | |
pub extern "C" fn rustls_server_config_builder_free( | |
config: *mut rustls_server_config_builder, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
free_box(config); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// With `ignore` != 0, the server will ignore the client ordering of cipher | |
/// suites, aka preference, during handshake and respect its own ordering | |
/// as configured. | |
/// <https://docs.rs/rustls/latest/rustls/struct.ServerConfig.html#structfield.ignore_client_order> | |
#[no_mangle] | |
pub extern "C" fn rustls_server_config_builder_set_ignore_client_order( | |
builder: *mut rustls_server_config_builder, | |
ignore: bool, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let config: &mut ServerConfigBuilder = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
config.ignore_client_order = Some(ignore); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Set the ALPN protocol list to the given protocols. `protocols` must point | |
/// to a buffer of `rustls_slice_bytes` (built by the caller) with `len` | |
/// elements. Each element of the buffer must point to a slice of bytes that | |
/// contains a single ALPN protocol from | |
/// <https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids>. | |
/// | |
/// This function makes a copy of the data in `protocols` and does not retain | |
/// any pointers, so the caller can free the pointed-to memory after calling. | |
/// | |
/// <https://docs.rs/rustls/latest/rustls/server/struct.ServerConfig.html#structfield.alpn_protocols> | |
#[no_mangle] | |
pub extern "C" fn rustls_server_config_builder_set_alpn_protocols( | |
builder: *mut rustls_server_config_builder, | |
protocols: *const rustls_slice_bytes, | |
len: size_t, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let config: &mut ServerConfigBuilder = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let protocols: &[rustls_slice_bytes] = if protocols.is_null() { | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { slice::from_raw_parts(protocols, len as usize) } | |
}; | |
let mut vv: Vec<Vec<u8>> = Vec::new(); | |
for p in protocols { | |
let v: &[u8] = if p.data.is_null() { | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { slice::from_raw_parts(p.data, p.len as usize) } | |
}; | |
vv.push(v.to_vec()); | |
} | |
config.alpn_protocols = vv; | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Provide the configuration a list of certificates where the connection | |
/// will select the first one that is compatible with the client's signature | |
/// verification capabilities. Servers that want to support both ECDSA and | |
/// RSA certificates will want the ECSDA to go first in the list. | |
/// | |
/// The built configuration will keep a reference to all certified keys | |
/// provided. The client may `rustls_certified_key_free()` afterwards | |
/// without the configuration losing them. The same certified key may also | |
/// be used in multiple configs. | |
/// | |
/// EXPERIMENTAL: installing a client_hello callback will replace any | |
/// configured certified keys and vice versa. | |
#[no_mangle] | |
pub extern "C" fn rustls_server_config_builder_set_certified_keys( | |
builder: *mut rustls_server_config_builder, | |
certified_keys: *const *const rustls_certified_key, | |
certified_keys_len: size_t, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let builder: &mut ServerConfigBuilder = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let keys_ptrs: &[*const rustls_certified_key] = if certified_keys | |
.is_null() | |
{ | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { | |
slice::from_raw_parts( | |
certified_keys, | |
certified_keys_len as usize, | |
) | |
} | |
}; | |
let mut keys: Vec<Arc<CertifiedKey>> = Vec::new(); | |
for &key_ptr in keys_ptrs { | |
let certified_key: Arc<CertifiedKey> = match crate::clone_arc( | |
key_ptr, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
keys.push(certified_key); | |
} | |
builder | |
.cert_resolver = Some( | |
Arc::new(ResolvesServerCertFromChoices::new(&keys)), | |
); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Turn a *rustls_server_config_builder (mutable) into a const *rustls_server_config | |
/// (read-only). | |
#[no_mangle] | |
pub extern "C" fn rustls_server_config_builder_build( | |
builder: *mut rustls_server_config_builder, | |
) -> *const rustls_server_config { | |
match ::std::panic::catch_unwind(|| { | |
let builder = match crate::try_box_from(builder) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let base = builder.base.with_client_cert_verifier(builder.verifier); | |
let mut config = if let Some(r) = builder.cert_resolver { | |
base.with_cert_resolver(r) | |
} else { | |
return null(); | |
}; | |
if let Some(ss) = builder.session_storage { | |
config.session_storage = ss; | |
} | |
config.alpn_protocols = builder.alpn_protocols; | |
if let Some(ignore_client_order) = builder.ignore_client_order { | |
config.ignore_client_order = ignore_client_order; | |
} | |
to_arc_const_ptr(config) | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
impl rustls_server_config { | |
/// "Free" a rustls_server_config previously returned from | |
/// rustls_server_config_builder_build. Since rustls_server_config is actually an | |
/// atomically reference-counted pointer, extant server connections may still | |
/// hold an internal reference to the Rust object. However, C code must | |
/// consider this pointer unusable after "free"ing it. | |
/// Calling with NULL is fine. Must not be called twice with the same value. | |
#[no_mangle] | |
pub extern "C" fn rustls_server_config_free( | |
config: *const rustls_server_config, | |
) { | |
match ::std::panic::catch_unwind(|| { | |
free_arc(config); | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Create a new rustls_connection containing a server connection, and return it | |
/// in the output parameter `out`. If this returns an error code, the memory | |
/// pointed to by `conn_out` remains unchanged. If this returns a non-error, | |
/// the memory pointed to by `conn_out` is modified to point | |
/// at a valid rustls_connection. The caller now owns the rustls_connection | |
/// and must call `rustls_connection_free` when done with it. | |
#[no_mangle] | |
pub extern "C" fn rustls_server_connection_new( | |
config: *const rustls_server_config, | |
conn_out: *mut *mut rustls_connection, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
if conn_out.is_null() { | |
return NullParameter; | |
} | |
let config: Arc<ServerConfig> = match crate::clone_arc(config) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let server_connection = match ServerConnection::new(config) { | |
Ok(sc) => sc, | |
Err(e) => return map_error(e), | |
}; | |
let c = Connection::from_server(server_connection); | |
set_boxed_mut_ptr(conn_out, c); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
/// Copy the server name from the server name indication (SNI) extension to `buf` which can | |
/// hold up to `count` bytes, and the length of that server name in `out_n`. The string is | |
/// stored in UTF-8 with no terminating NUL byte. | |
/// Returns RUSTLS_RESULT_INSUFFICIENT_SIZE if the SNI hostname is longer than `count`. | |
/// Returns Ok with *out_n == 0 if there is no SNI hostname available on this connection | |
/// because it hasn't been processed yet, or because the client did not send SNI. | |
/// <https://docs.rs/rustls/latest/rustls/server/struct.ServerConnection.html#method.server_name> | |
#[no_mangle] | |
pub extern "C" fn rustls_server_connection_get_server_name( | |
conn: *const rustls_connection, | |
buf: *mut u8, | |
count: size_t, | |
out_n: *mut size_t, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let conn: &Connection = match crate::try_from(conn) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
if buf.is_null() { | |
return NullParameter; | |
} | |
if out_n.is_null() { | |
return NullParameter; | |
} | |
let server_connection = match conn.as_server() { | |
Some(s) => s, | |
_ => return rustls_result::InvalidParameter, | |
}; | |
let sni_hostname = match server_connection.server_name() { | |
Some(sni_hostname) => sni_hostname, | |
None => { | |
unsafe { | |
*out_n = 0; | |
} | |
return rustls_result::Ok; | |
} | |
}; | |
let len: usize = sni_hostname.len(); | |
if len > count { | |
unsafe { *out_n = 0 } | |
return rustls_result::InsufficientSize; | |
} | |
unsafe { | |
std::ptr::copy_nonoverlapping(sni_hostname.as_ptr(), buf, len); | |
*out_n = len; | |
} | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
/// Choose the server certificate to be used for a connection based on certificate | |
/// type. Will pick the first CertfiedKey available that is suitable for | |
/// the SignatureSchemes supported by the client. | |
struct ResolvesServerCertFromChoices { | |
choices: Vec<Arc<CertifiedKey>>, | |
} | |
#[automatically_derived] | |
impl ::core::fmt::Debug for ResolvesServerCertFromChoices { | |
#[inline] | |
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { | |
::core::fmt::Formatter::debug_struct_field1_finish( | |
f, | |
"ResolvesServerCertFromChoices", | |
"choices", | |
&&self.choices, | |
) | |
} | |
} | |
impl ResolvesServerCertFromChoices { | |
pub fn new(choices: &[Arc<CertifiedKey>]) -> Self { | |
ResolvesServerCertFromChoices { | |
choices: Vec::from(choices), | |
} | |
} | |
} | |
impl ResolvesServerCert for ResolvesServerCertFromChoices { | |
fn resolve(&self, client_hello: ClientHello) -> Option<Arc<CertifiedKey>> { | |
for key in self.choices.iter() { | |
if key.key.choose_scheme(client_hello.signature_schemes()).is_some() { | |
return Some(key.clone()); | |
} | |
} | |
None | |
} | |
} | |
/// The TLS Client Hello information provided to a ClientHelloCallback function. | |
/// `server_name` is the value of the ServerNameIndication extension provided | |
/// by the client. If the client did not send an SNI, the length of this | |
/// `rustls_string` will be 0. The signature_schemes field carries the values | |
/// supplied by the client or, if the client did not send this TLS extension, | |
/// the default schemes in the rustls library. See: | |
/// <https://docs.rs/rustls/latest/rustls/internal/msgs/enums/enum.SignatureScheme.html>. | |
/// `alpn` carries the list of ALPN protocol names that the client proposed to | |
/// the server. Again, the length of this list will be 0 if none were supplied. | |
/// | |
/// All this data, when passed to a callback function, is only accessible during | |
/// the call and may not be modified. Users of this API must copy any values that | |
/// they want to access when the callback returned. | |
/// | |
/// EXPERIMENTAL: this feature of rustls-ffi is likely to change in the future, as | |
/// the rustls library is re-evaluating their current approach to client hello handling. | |
#[repr(C)] | |
pub struct rustls_client_hello<'a> { | |
server_name: rustls_str<'a>, | |
signature_schemes: rustls_slice_u16<'a>, | |
alpn: *const rustls_slice_slice_bytes<'a>, | |
} | |
impl<'a> Castable for rustls_client_hello<'a> { | |
type Ownership = OwnershipRef; | |
type RustType = rustls_client_hello<'a>; | |
} | |
/// Any context information the callback will receive when invoked. | |
pub type rustls_client_hello_userdata = *mut c_void; | |
/// Prototype of a callback that can be installed by the application at the | |
/// `rustls_server_config`. This callback will be invoked by a `rustls_connection` | |
/// once the TLS client hello message has been received. | |
/// `userdata` will be set based on rustls_connection_set_userdata. | |
/// `hello` gives the value of the available client announcements, as interpreted | |
/// by rustls. See the definition of `rustls_client_hello` for details. | |
/// | |
/// NOTE: | |
/// - the passed in `hello` and all its values are only available during the | |
/// callback invocations. | |
/// - the passed callback function must be safe to call multiple times concurrently | |
/// with the same userdata, unless there is only a single config and connection | |
/// where it is installed. | |
/// | |
/// EXPERIMENTAL: this feature of rustls-ffi is likely to change in the future, as | |
/// the rustls library is re-evaluating their current approach to client hello handling. | |
pub type rustls_client_hello_callback = Option< | |
unsafe extern "C" fn( | |
userdata: rustls_client_hello_userdata, | |
hello: *const rustls_client_hello, | |
) -> *const rustls_certified_key, | |
>; | |
type ClientHelloCallback = unsafe extern "C" fn( | |
userdata: rustls_client_hello_userdata, | |
hello: *const rustls_client_hello, | |
) -> *const rustls_certified_key; | |
struct ClientHelloResolver { | |
/// Implementation of rustls::ResolvesServerCert that passes values | |
/// from the supplied ClientHello to the callback function. | |
pub callback: ClientHelloCallback, | |
} | |
impl ClientHelloResolver { | |
pub fn new(callback: ClientHelloCallback) -> ClientHelloResolver { | |
ClientHelloResolver { callback } | |
} | |
} | |
impl ResolvesServerCert for ClientHelloResolver { | |
fn resolve(&self, client_hello: ClientHello) -> Option<Arc<CertifiedKey>> { | |
let server_name = client_hello.server_name().unwrap_or_default(); | |
let server_name: rustls_str = match server_name.try_into() { | |
Ok(r) => r, | |
Err(_) => return None, | |
}; | |
let mapped_sigs: Vec<u16> = client_hello | |
.signature_schemes() | |
.iter() | |
.map(|s| u16::from(*s)) | |
.collect(); | |
let alpn = match client_hello.alpn() { | |
Some(iter) => iter.collect(), | |
None => ::alloc::vec::Vec::new(), | |
}; | |
let alpn = rustls_slice_slice_bytes { | |
inner: &alpn, | |
}; | |
let signature_schemes: rustls_slice_u16 = (&*mapped_sigs).into(); | |
let hello = rustls_client_hello { | |
server_name, | |
signature_schemes, | |
alpn: &alpn, | |
}; | |
let cb = self.callback; | |
let userdata = match userdata_get() { | |
Ok(u) => u, | |
Err(_) => return None, | |
}; | |
let key_ptr: *const rustls_certified_key = unsafe { cb(userdata, &hello) }; | |
let certified_key: &CertifiedKey = match crate::try_from(key_ptr) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
Some(Arc::new(certified_key.clone())) | |
} | |
} | |
/// This struct can be considered thread safe, as long | |
/// as the registered callbacks are thread safe. This is | |
/// documented as a requirement in the API. | |
unsafe impl Sync for ClientHelloResolver {} | |
unsafe impl Send for ClientHelloResolver {} | |
impl Debug for ClientHelloResolver { | |
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | |
f.debug_struct("ClientHelloResolver").finish() | |
} | |
} | |
impl rustls_server_config_builder { | |
/// Register a callback to be invoked when a connection created from this config | |
/// sees a TLS ClientHello message. If `userdata` has been set with | |
/// rustls_connection_set_userdata, it will be passed to the callback. | |
/// Otherwise the userdata param passed to the callback will be NULL. | |
/// | |
/// Any existing `ResolvesServerCert` implementation currently installed in the | |
/// `rustls_server_config` will be replaced. This also means registering twice | |
/// will overwrite the first registration. It is not permitted to pass a NULL | |
/// value for `callback`. | |
/// | |
/// EXPERIMENTAL: this feature of rustls-ffi is likely to change in the future, as | |
/// the rustls library is re-evaluating their current approach to client hello handling. | |
/// Installing a client_hello callback will replace any configured certified keys | |
/// and vice versa. Same holds true for the set_certified_keys variant. | |
#[no_mangle] | |
pub extern "C" fn rustls_server_config_builder_set_hello_callback( | |
builder: *mut rustls_server_config_builder, | |
callback: rustls_client_hello_callback, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let callback: ClientHelloCallback = match callback { | |
Some(cb) => cb, | |
None => return rustls_result::NullParameter, | |
}; | |
let builder: &mut ServerConfigBuilder = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
builder | |
.cert_resolver = Some(Arc::new(ClientHelloResolver::new(callback))); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
fn sigschemes(input: &[u16]) -> Vec<SignatureScheme> { | |
input.iter().copied().map(Into::into).collect() | |
} | |
/// Select a `rustls_certified_key` from the list that matches the cryptographic | |
/// parameters of a TLS client hello. Note that this does not do any SNI matching. | |
/// The input certificates should already have been filtered to ones matching the | |
/// SNI from the client hello. | |
/// | |
/// This is intended for servers that are configured with several keys for the | |
/// same domain name(s), for example ECDSA and RSA types. The presented keys are | |
/// inspected in the order given and keys first in the list are given preference, | |
/// all else being equal. However rustls is free to choose whichever it considers | |
/// to be the best key with its knowledge about security issues and possible future | |
/// extensions of the protocol. | |
/// | |
/// Return RUSTLS_RESULT_OK if a key was selected and RUSTLS_RESULT_NOT_FOUND | |
/// if none was suitable. | |
#[no_mangle] | |
pub extern "C" fn rustls_client_hello_select_certified_key( | |
hello: *const rustls_client_hello, | |
certified_keys: *const *const rustls_certified_key, | |
certified_keys_len: size_t, | |
out_key: *mut *const rustls_certified_key, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let hello = match crate::try_from(hello) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
let schemes: Vec<SignatureScheme> = sigschemes( | |
if hello.signature_schemes.data.is_null() { | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { | |
slice::from_raw_parts( | |
hello.signature_schemes.data, | |
hello.signature_schemes.len as usize, | |
) | |
} | |
}, | |
); | |
if out_key.is_null() { | |
return NullParameter; | |
} | |
let keys_ptrs: &[*const rustls_certified_key] = if certified_keys.is_null() { | |
return crate::panic::NullParameterOrDefault::value(); | |
} else { | |
unsafe { | |
slice::from_raw_parts(certified_keys, certified_keys_len as usize) | |
} | |
}; | |
for &key_ptr in keys_ptrs { | |
let key_ref: &CertifiedKey = match crate::try_from(key_ptr) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
if key_ref.key.choose_scheme(&schemes).is_some() { | |
unsafe { | |
*out_key = key_ptr; | |
} | |
return rustls_result::Ok; | |
} | |
} | |
rustls_result::NotFound | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
impl rustls_server_config_builder { | |
/// Register callbacks for persistence of TLS session IDs and secrets. Both | |
/// keys and values are highly sensitive data, containing enough information | |
/// to break the security of the connections involved. | |
/// | |
/// If `userdata` has been set with rustls_connection_set_userdata, it | |
/// will be passed to the callbacks. Otherwise the userdata param passed to | |
/// the callbacks will be NULL. | |
#[no_mangle] | |
pub extern "C" fn rustls_server_config_builder_set_persistence( | |
builder: *mut rustls_server_config_builder, | |
get_cb: rustls_session_store_get_callback, | |
put_cb: rustls_session_store_put_callback, | |
) -> rustls_result { | |
match ::std::panic::catch_unwind(|| { | |
let get_cb: SessionStoreGetCallback = match get_cb { | |
Some(cb) => cb, | |
None => return rustls_result::NullParameter, | |
}; | |
let put_cb: SessionStorePutCallback = match put_cb { | |
Some(cb) => cb, | |
None => return rustls_result::NullParameter, | |
}; | |
let builder: &mut ServerConfigBuilder = match crate::try_from_mut( | |
builder, | |
) { | |
Some(c) => c, | |
None => return crate::panic::NullParameterOrDefault::value(), | |
}; | |
builder | |
.session_storage = Some( | |
Arc::new(SessionStoreBroker::new(get_cb, put_cb)), | |
); | |
rustls_result::Ok | |
}) { | |
Ok(ret) => ret, | |
Err(_) => return crate::PanicOrDefault::value(), | |
} | |
} | |
} | |
} | |
pub mod session { | |
use crate::error::rustls_result; | |
use crate::rslice::rustls_slice_bytes; | |
use crate::userdata_get; | |
use libc::{c_int, c_void, size_t}; | |
use std::fmt::{Debug, Formatter}; | |
/// Any context information the callback will receive when invoked. | |
pub type rustls_session_store_userdata = *mut c_void; | |
/// Prototype of a callback that can be installed by the application at the | |
/// `rustls_server_config` or `rustls_client_config`. This callback will be | |
/// invoked by a TLS session when looking up the data for a TLS session id. | |
/// `userdata` will be supplied based on rustls_{client,server}_session_set_userdata. | |
/// | |
/// The `buf` points to `count` consecutive bytes where the | |
/// callback is expected to copy the result to. The number of copied bytes | |
/// needs to be written to `out_n`. The callback should not read any | |
/// data from `buf`. | |
/// | |
/// If the value to copy is larger than `count`, the callback should never | |
/// do a partial copy but instead remove the value from its store and | |
/// act as if it was never found. | |
/// | |
/// The callback should return RUSTLS_RESULT_OK to indicate that a value was | |
/// retrieved and written in its entirety into `buf`, or RUSTLS_RESULT_NOT_FOUND | |
/// if no session was retrieved. | |
/// | |
/// When `remove_after` is != 0, the returned data needs to be removed | |
/// from the store. | |
/// | |
/// NOTE: the passed in `key` and `buf` are only available during the | |
/// callback invocation. | |
/// NOTE: callbacks used in several sessions via a common config | |
/// must be implemented thread-safe. | |
pub type rustls_session_store_get_callback = Option< | |
unsafe extern "C" fn( | |
userdata: rustls_session_store_userdata, | |
key: *const rustls_slice_bytes, | |
remove_after: c_int, | |
buf: *mut u8, | |
count: size_t, | |
out_n: *mut size_t, | |
) -> u32, | |
>; | |
pub(crate) type SessionStoreGetCallback = unsafe extern "C" fn( | |
userdata: rustls_session_store_userdata, | |
key: *const rustls_slice_bytes, | |
remove_after: c_int, | |
buf: *mut u8, | |
count: size_t, | |
out_n: *mut size_t, | |
) -> u32; | |
/// Prototype of a callback that can be installed by the application at the | |
/// `rustls_server_config` or `rustls_client_config`. This callback will be | |
/// invoked by a TLS session when a TLS session has been created and an id | |
/// for later use is handed to the client/has been received from the server. | |
/// `userdata` will be supplied based on rustls_{client,server}_session_set_userdata. | |
/// | |
/// The callback should return RUSTLS_RESULT_OK to indicate that a value was | |
/// successfully stored, or RUSTLS_RESULT_IO on failure. | |
/// | |
/// NOTE: the passed in `key` and `val` are only available during the | |
/// callback invocation. | |
/// NOTE: callbacks used in several sessions via a common config | |
/// must be implemented thread-safe. | |
pub type rustls_session_store_put_callback = Option< | |
unsafe extern "C" fn( | |
userdata: rustls_session_store_userdata, | |
key: *const rustls_slice_bytes, | |
val: *const rustls_slice_bytes, | |
) -> u32, | |
>; | |
pub(crate) type SessionStorePutCallback = unsafe extern "C" fn( | |
userdata: rustls_session_store_userdata, | |
key: *const rustls_slice_bytes, | |
val: *const rustls_slice_bytes, | |
) -> u32; | |
pub(crate) struct SessionStoreBroker { | |
pub get_cb: SessionStoreGetCallback, | |
pub put_cb: SessionStorePutCallback, | |
} | |
impl SessionStoreBroker { | |
pub fn new( | |
get_cb: SessionStoreGetCallback, | |
put_cb: SessionStorePutCallback, | |
) -> Self { | |
SessionStoreBroker { | |
get_cb, | |
put_cb, | |
} | |
} | |
fn retrieve(&self, key: &[u8], remove: bool) -> Option<Vec<u8>> { | |
let key: rustls_slice_bytes = key.into(); | |
let userdata = userdata_get().ok()?; | |
let mut data: Vec<u8> = ::alloc::vec::from_elem(0, 65 * 1024); | |
let mut out_n: size_t = 0; | |
let cb = self.get_cb; | |
let result = unsafe { | |
cb( | |
userdata, | |
&key, | |
remove as c_int, | |
data.as_mut_ptr(), | |
data.len(), | |
&mut out_n, | |
) | |
}; | |
match rustls_result::from(result) { | |
rustls_result::Ok => { | |
unsafe { data.set_len(out_n) }; | |
Some(data) | |
} | |
_ => None, | |
} | |
} | |
fn store(&self, key: Vec<u8>, value: Vec<u8>) -> bool { | |
let key: rustls_slice_bytes = key.as_slice().into(); | |
let value: rustls_slice_bytes = value.as_slice().into(); | |
let cb = self.put_cb; | |
let userdata = match userdata_get() { | |
Ok(u) => u, | |
Err(_) => return false, | |
}; | |
let result = unsafe { cb(userdata, &key, &value) }; | |
result == rustls_result::Ok as u32 | |
} | |
} | |
impl rustls::server::StoresServerSessions for SessionStoreBroker { | |
fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool { | |
self.store(key, value) | |
} | |
fn get(&self, key: &[u8]) -> Option<Vec<u8>> { | |
self.retrieve(key, false) | |
} | |
fn take(&self, key: &[u8]) -> Option<Vec<u8>> { | |
self.retrieve(key, true) | |
} | |
fn can_cache(&self) -> bool { | |
true | |
} | |
} | |
impl Debug for SessionStoreBroker { | |
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | |
f.debug_struct("SessionStoreBroker").finish() | |
} | |
} | |
/// This struct can be considered thread safe, as long | |
/// as the registered callbacks are thread safe. This is | |
/// documented as a requirement in the API. | |
unsafe impl Sync for SessionStoreBroker {} | |
unsafe impl Send for SessionStoreBroker {} | |
} | |
pub use error::rustls_result; | |
pub use error::*; | |
use crate::log::rustls_log_callback; | |
use crate::panic::PanicOrDefault; | |
const RUSTLS_FFI_VERSION: &str = "rustls-ffi/0.13.0/rustls/0.23.4"; | |
#[allow(clippy::thread_local_initializer_can_be_made_const)] | |
pub(crate) const USERDATA: ::std::thread::LocalKey<RefCell<Vec<Userdata>>> = { | |
#[inline] | |
fn __init() -> RefCell<Vec<Userdata>> { | |
RefCell::new(Vec::new()) | |
} | |
#[inline] | |
unsafe fn __getit( | |
init: ::std::option::Option<&mut ::std::option::Option<RefCell<Vec<Userdata>>>>, | |
) -> ::std::option::Option<&'static RefCell<Vec<Userdata>>> { | |
#[thread_local] | |
static __KEY: ::std::thread::local_impl::Key<RefCell<Vec<Userdata>>> = ::std::thread::local_impl::Key::< | |
RefCell<Vec<Userdata>>, | |
>::new(); | |
unsafe { | |
__KEY | |
.get(move || { | |
if let ::std::option::Option::Some(init) = init { | |
if let ::std::option::Option::Some(value) = init.take() { | |
return value; | |
} | |
if true { | |
{ | |
::core::panicking::panic_fmt( | |
format_args!( | |
"internal error: entered unreachable code: {0}", | |
format_args!("missing default value"), | |
), | |
); | |
}; | |
} | |
} | |
__init() | |
}) | |
} | |
} | |
unsafe { ::std::thread::LocalKey::new(__getit) } | |
}; | |
pub(crate) struct Userdata { | |
userdata: *mut c_void, | |
log_callback: rustls_log_callback, | |
} | |
/// UserdataGuard pops an entry off the USERDATA stack, restoring the | |
/// thread-local state to its value previous to the creation of the UserdataGuard. | |
/// Invariants: As long as a UserdataGuard is live: | |
/// - The stack of userdata items for this thread must have at least one item. | |
/// - The top item on that stack must be the one this guard was built with. | |
/// - The `data` field must not be None. | |
/// If any of these invariants fails, try_drop will return an error. | |
pub(crate) struct UserdataGuard { | |
data: Option<Userdata>, | |
} | |
impl UserdataGuard { | |
fn new(u: *mut c_void) -> Self { | |
UserdataGuard { | |
data: Some(Userdata { | |
userdata: u, | |
log_callback: None, | |
}), | |
} | |
} | |
/// Even though we have a Drop impl on this guard, when possible it's | |
/// best to call try_drop explicitly. That way any failures of internal | |
/// variants can be signaled to the user immediately by returning | |
/// rustls_result::Panic. | |
fn try_drop(mut self) -> Result<(), UserdataError> { | |
self.try_pop() | |
} | |
fn try_pop(&mut self) -> Result<(), UserdataError> { | |
let expected_data = self | |
.data | |
.as_ref() | |
.ok_or(UserdataError::AlreadyPopped)? | |
.userdata; | |
USERDATA | |
.try_with(|userdata| { | |
userdata | |
.try_borrow_mut() | |
.map_or_else( | |
|_| Err(UserdataError::AlreadyBorrowed), | |
|mut v| { | |
let u = v.pop().ok_or(UserdataError::EmptyStack)?; | |
self.data = None; | |
if u.userdata == expected_data { | |
Ok(()) | |
} else { | |
Err(UserdataError::WrongData) | |
} | |
}, | |
) | |
}) | |
.unwrap_or(Err(UserdataError::AccessError)) | |
} | |
} | |
impl Drop for UserdataGuard { | |
fn drop(&mut self) { | |
self.try_pop().ok(); | |
} | |
} | |
pub(crate) enum UserdataError { | |
/// try_pop was called twice. | |
AlreadyPopped, | |
/// The RefCell is borrowed somewhere else. | |
AlreadyBorrowed, | |
/// The stack of userdata items was already empty. | |
EmptyStack, | |
/// The LocalKey was destroyed before this call. | |
/// See <https://doc.rust-lang.org/std/thread/struct.LocalKey.html#method.try_with> | |
AccessError, | |
/// Unexpected pointer when popping. | |
WrongData, | |
} | |
#[automatically_derived] | |
impl ::core::clone::Clone for UserdataError { | |
#[inline] | |
fn clone(&self) -> UserdataError { | |
match self { | |
UserdataError::AlreadyPopped => UserdataError::AlreadyPopped, | |
UserdataError::AlreadyBorrowed => UserdataError::AlreadyBorrowed, | |
UserdataError::EmptyStack => UserdataError::EmptyStack, | |
UserdataError::AccessError => UserdataError::AccessError, | |
UserdataError::WrongData => UserdataError::WrongData, | |
} | |
} | |
} | |
#[automatically_derived] | |
impl ::core::fmt::Debug for UserdataError { | |
#[inline] | |
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { | |
::core::fmt::Formatter::write_str( | |
f, | |
match self { | |
UserdataError::AlreadyPopped => "AlreadyPopped", | |
UserdataError::AlreadyBorrowed => "AlreadyBorrowed", | |
UserdataError::EmptyStack => "EmptyStack", | |
UserdataError::AccessError => "AccessError", | |
UserdataError::WrongData => "WrongData", | |
}, | |
) | |
} | |
} | |
#[must_use = "If you drop the guard, userdata will be immediately cleared"] | |
pub(crate) fn userdata_push( | |
u: *mut c_void, | |
cb: rustls_log_callback, | |
) -> Result<UserdataGuard, UserdataError> { | |
USERDATA | |
.try_with(|userdata| { | |
userdata | |
.try_borrow_mut() | |
.map_or_else( | |
|_| Err(UserdataError::AlreadyBorrowed), | |
|mut v| { | |
v.push(Userdata { | |
userdata: u, | |
log_callback: cb, | |
}); | |
Ok(()) | |
}, | |
) | |
}) | |
.unwrap_or(Err(UserdataError::AccessError))?; | |
Ok(UserdataGuard::new(u)) | |
} | |
pub(crate) fn userdata_get() -> Result<*mut c_void, UserdataError> { | |
USERDATA | |
.try_with(|userdata| { | |
userdata | |
.try_borrow_mut() | |
.map_or_else( | |
|_| Err(UserdataError::AlreadyBorrowed), | |
|v| match v.last() { | |
Some(u) => Ok(u.userdata), | |
None => Err(UserdataError::EmptyStack), | |
}, | |
) | |
}) | |
.unwrap_or(Err(UserdataError::AccessError)) | |
} | |
pub(crate) fn log_callback_get() -> Result< | |
(rustls_log_callback, *mut c_void), | |
UserdataError, | |
> { | |
USERDATA | |
.try_with(|userdata| { | |
userdata | |
.try_borrow_mut() | |
.map_or_else( | |
|_| Err(UserdataError::AlreadyBorrowed), | |
|v| match v.last() { | |
Some(u) => Ok((u.log_callback, u.userdata)), | |
None => Err(UserdataError::EmptyStack), | |
}, | |
) | |
}) | |
.unwrap_or(Err(UserdataError::AccessError)) | |
} | |
/// Used to mark that pointer to a [`Castable`]'s underlying `Castable::RustType` is provided | |
/// to C code as a pointer to a `Box<Castable::RustType>`. | |
pub(crate) struct OwnershipBox; | |
/// Used to mark that a pointer to a [`Castable`]'s underlying `Castable::RustType` is provided | |
/// to C code as a pointer to an `Arc<Castable::RustType>`. | |
pub(crate) struct OwnershipArc; | |
/// Used to mark that a pointer to a [`Castable`]'s underlying `Castable::RustType` is provided | |
/// to C code as a pointer to a reference, `&Castable::RustType`. | |
pub(crate) struct OwnershipRef; | |
/// A trait for marking the type of a pointer to a [`Castable`]'s underlying `Castable::RustType` | |
/// that is provided to C code, either a [`OwnershipBox`] when it is a pointer to a `Box<_>`, | |
/// a [`OwnershipArc`] when it is a pointer to an `Arc<_>`, or a [`OwnershipRef`] when it is a | |
/// pointer to a `&_`. | |
#[allow(dead_code)] | |
trait OwnershipMarker {} | |
impl OwnershipMarker for OwnershipBox {} | |
impl OwnershipMarker for OwnershipArc {} | |
impl OwnershipMarker for OwnershipRef {} | |
/// `Castable` represents the relationship between a snake case type (like [`client::rustls_client_config`]) | |
/// and the corresponding Rust type (like [`rustls::ClientConfig`]), specified as the associated type | |
/// `RustType`. Each `Castable` also has an associated type `Ownership` specifying one of the | |
/// [`OwnershipMarker`] types, [`OwnershipBox`], [`OwnershipArc`] or [`OwnershipRef`]. | |
/// | |
/// An implementation of `Castable` that uses [`OwnershipBox`] indicates that when we give C code | |
/// a pointer to the relevant `RustType` `T`, that it is actually a `Box<T>`. An | |
/// implementation of `Castable` that uses [`OwnershipArc`] means that when we give C code a | |
/// pointer to the relevant type, that it is actually an `Arc<T>`. Lastly an implementation of | |
/// `Castable` that uses [`OwnershipRef`] means that when we give C code a pointer to the relevant | |
/// type, that it is actually a `&T`. | |
/// | |
/// By using an associated type on `Castable` to communicate this we can use the type system to | |
/// guarantee that a single type can't implement `Castable` for more than one [`OwnershipMarker`], | |
/// since this would be a conflicting trait implementation and rejected by the compiler. | |
/// | |
/// This trait allows us to avoid using `as` in most places, and ensures that when we cast, we're | |
/// preserving const-ness, and casting between the correct types. Implementing this is required in | |
/// order to use `try_ref_from_ptr!` or `try_mut_from_ptr!` and several other helpful cast-related | |
/// conversion helpers. | |
/// | |
/// You can define a new `Castable` type using the `box_castable!`, `arc_castable!` or | |
/// `ref_castable!` macros depending on the ownership marker you desire. See each macro's | |
/// documentation for more information. | |
pub(crate) trait Castable { | |
/// Indicates whether to use `Box` or `Arc` when giving a pointer to C code for the underlying | |
/// `RustType`. | |
type Ownership: OwnershipMarker; | |
/// The underlying Rust type that we are casting to and from. | |
type RustType; | |
} | |
pub(crate) use box_castable; | |
pub(crate) use arc_castable; | |
pub(crate) use ref_castable; | |
pub(crate) use castable; | |
/// Convert a const pointer to a [`Castable`] to a const pointer to its underlying | |
/// [`Castable::RustType`]. | |
/// | |
/// This can be used regardless of the [`Castable::Ownership`] as we can make const pointers for | |
/// `Box`, `Arc` and ref types. | |
pub(crate) fn cast_const_ptr<C>(ptr: *const C) -> *const C::RustType | |
where | |
C: Castable, | |
{ | |
ptr as *const _ | |
} | |
/// Convert a [`Castable`]'s underlying [`Castable::RustType`] to a constant pointer | |
/// to an `Arc` over the rust type. Can only be used when the `Castable` has specified a cast type | |
/// equal to [`OwnershipArc`]. | |
pub(crate) fn to_arc_const_ptr<C>(src: C::RustType) -> *const C | |
where | |
C: Castable<Ownership = OwnershipArc>, | |
{ | |
Arc::into_raw(Arc::new(src)) as *const _ | |
} | |
/// Given a const pointer to a [`Castable`] representing an `Arc`, clone the `Arc` and return | |
/// the corresponding Rust type. | |
/// | |
/// The caller still owns its copy of the `Arc`. In other words, the reference count of the | |
/// `Arc` will be incremented by 1 by the end of this function. | |
/// | |
/// To achieve that, we need to `mem::forget` the `Arc` we get back from `into_raw`, because | |
/// `into_raw` _does_ take back ownership. If we called `into_raw` without `mem::forget`, at the | |
/// end of the function that Arc would be dropped and the reference count would be decremented, | |
/// potentially to 0, causing memory to be freed. | |
/// | |
/// Does nothing, returning `None`, when passed a `NULL` pointer. Can only be used when the | |
/// `Castable` has specified a cast type equal to [`OwnershipArc`]. | |
/// | |
/// ## Unsafety: | |
/// | |
/// If non-null, `ptr` must be a pointer that resulted from previously calling `Arc::into_raw`, | |
/// e.g. from using [`to_arc_const_ptr`]. | |
pub(crate) fn clone_arc<C>(ptr: *const C) -> Option<Arc<C::RustType>> | |
where | |
C: Castable<Ownership = OwnershipArc>, | |
{ | |
if ptr.is_null() { | |
return None; | |
} | |
let rs_typed = cast_const_ptr::<C>(ptr); | |
let r = unsafe { Arc::from_raw(rs_typed) }; | |
let val = Arc::clone(&r); | |
mem::forget(r); | |
Some(val) | |
} | |
/// Convert a mutable pointer to a [`Castable`] to an optional `Box` over the underlying rust type. | |
/// | |
/// Does nothing, returning `None`, when passed `NULL`. Can only be used when the `Castable` has | |
/// specified a cast type equal to [`OwnershipBox`]. | |
/// | |
/// ## Unsafety: | |
/// | |
/// If non-null, `ptr` must be a pointer that resulted from previously calling `Box::into_raw`, | |
/// e.g. from using [`to_boxed_mut_ptr`]. | |
pub(crate) fn to_box<C>(ptr: *mut C) -> Option<Box<C::RustType>> | |
where | |
C: Castable<Ownership = OwnershipBox>, | |
{ | |
if ptr.is_null() { | |
return None; | |
} | |
let rs_typed = cast_mut_ptr(ptr); | |
unsafe { Some(Box::from_raw(rs_typed)) } | |
} | |
/// Free a constant pointer to a [`Castable`]'s underlying [`Castable::RustType`] by | |
/// reconstituting an `Arc` from the raw pointer and dropping it. | |
/// | |
/// For types represented with an `Arc` on the Rust side, we offer a `_free()` | |
/// method to the C side that decrements the refcount and ultimately drops | |
/// the `Arc` if the refcount reaches 0. By contrast with `to_arc`, we call | |
/// `Arc::from_raw` on the input pointer, but we _don't_ clone it, because we | |
/// want the refcount to be lower by one when we reach the end of the function. | |
/// | |
/// Does nothing, returning `None`, when passed `NULL`. Can only be used when the `Castable` has | |
/// specified a cast type equal to [`OwnershipArc`]. | |
pub(crate) fn free_arc<C>(ptr: *const C) | |
where | |
C: Castable<Ownership = OwnershipArc>, | |
{ | |
if ptr.is_null() { | |
return; | |
} | |
let rs_typed = cast_const_ptr(ptr); | |
drop(unsafe { Arc::from_raw(rs_typed) }); | |
} | |
/// Convert a mutable pointer to a [`Castable`] to an optional `Box` over the underlying | |
/// [`Castable::RustType`], and immediately let it fall out of scope to be freed. | |
/// | |
/// Can only be used when the `Castable` has specified a cast type equal to [`OwnershipBox`]. | |
/// | |
/// ## Unsafety: | |
/// | |
/// If non-null, `ptr` must be a pointer that resulted from previously calling `Box::into_raw`, | |
/// e.g. from using [`to_boxed_mut_ptr`]. | |
pub(crate) fn free_box<C>(ptr: *mut C) | |
where | |
C: Castable<Ownership = OwnershipBox>, | |
{ | |
to_box(ptr); | |
} | |
/// Convert a mutable pointer to a [`Castable`] to a mutable pointer to its underlying | |
/// [`Castable::RustType`]. | |
/// | |
/// Can only be used when the `Castable` has specified a cast source equal to `BoxCastPtrMarker`. | |
pub(crate) fn cast_mut_ptr<C>(ptr: *mut C) -> *mut C::RustType | |
where | |
C: Castable<Ownership = OwnershipBox>, | |
{ | |
ptr as *mut _ | |
} | |
/// Converts a [`Castable`]'s underlying [`Castable::RustType`] to a mutable pointer | |
/// to a `Box` over the rust type. | |
/// | |
/// Can only be used when the `Castable` has specified a cast type equal to [`OwnershipBox`]. | |
pub(crate) fn to_boxed_mut_ptr<C>(src: C::RustType) -> *mut C | |
where | |
C: Castable<Ownership = OwnershipBox>, | |
{ | |
Box::into_raw(Box::new(src)) as *mut _ | |
} | |
/// Converts a [`Castable`]'s underlying [`Castable::RustType`] to a mutable pointer | |
/// to a `Box` over the rust type and sets the `dst` out pointer to the resulting mutable `Box` | |
/// pointer. See [`to_boxed_mut_ptr`] for more information. | |
/// | |
/// ## Unsafety: | |
/// | |
/// `dst` must not be `NULL`. | |
pub(crate) fn set_boxed_mut_ptr<C>(dst: *mut *mut C, src: C::RustType) | |
where | |
C: Castable<Ownership = OwnershipBox>, | |
{ | |
unsafe { | |
*dst = to_boxed_mut_ptr(src); | |
} | |
} | |
/// Converts a [`Castable`]'s underlying [`Castable::RustType`] to a const pointer | |
/// to an `Arc` over the rust type and sets the `dst` out pointer to the resulting const `Arc` | |
/// pointer. See [`to_arc_const_ptr`] for more information. | |
/// | |
/// ## Unsafety: | |
/// | |
/// `dst` must not be `NULL`. | |
pub(crate) fn set_arc_mut_ptr<C>(dst: *mut *const C, src: C::RustType) | |
where | |
C: Castable<Ownership = OwnershipArc>, | |
{ | |
unsafe { | |
*dst = to_arc_const_ptr(src); | |
} | |
} | |
/// Converts a mutable pointer to a [`Castable`] to an optional ref to the underlying | |
/// [`Castable::RustType`]. See [`cast_mut_ptr`] for more information. | |
/// | |
/// Does nothing, returning `None`, when passed `NULL`. Can only be used when the `Castable` has | |
/// specified a cast type equal to [`OwnershipBox`]. | |
pub(crate) fn try_from_mut<'a, C>(from: *mut C) -> Option<&'a mut C::RustType> | |
where | |
C: Castable<Ownership = OwnershipBox>, | |
{ | |
unsafe { cast_mut_ptr(from).as_mut() } | |
} | |
pub(crate) use try_mut_from_ptr; | |
/// Converts a const pointer to a [`Castable`] to an optional ref to the underlying | |
/// [`Castable::RustType`]. See [`cast_const_ptr`] for more information. | |
/// | |
/// Does nothing, returning `None` when passed `NULL`. Can be used with `Castable`'s that | |
/// specify a cast type of [`OwnershipArc`] as well as `Castable`'s that specify | |
/// a cast type of [`OwnershipBox`]. | |
pub(crate) fn try_from<'a, C, O>(from: *const C) -> Option<&'a C::RustType> | |
where | |
C: Castable<Ownership = O>, | |
{ | |
unsafe { cast_const_ptr(from).as_ref() } | |
} | |
pub(crate) use try_ref_from_ptr; | |
pub(crate) use try_clone_arc; | |
/// Convert a mutable pointer to a [`Castable`] to an optional `Box` over the underlying | |
/// [`Castable::RustType`]. | |
/// | |
/// Does nothing, returning `None`, when passed `NULL`. Can only be used with `Castable`'s that | |
/// specify a cast type of [`OwnershipBox`]. | |
pub(crate) fn try_box_from<C>(from: *mut C) -> Option<Box<C::RustType>> | |
where | |
C: Castable<Ownership = OwnershipBox>, | |
{ | |
to_box(from) | |
} | |
pub(crate) use try_box_from_ptr; | |
pub(crate) use try_slice; | |
pub(crate) use try_callback; | |
pub(crate) use try_take; | |
/// Returns a static string containing the rustls-ffi version as well as the | |
/// rustls version. The string is alive for the lifetime of the program and does | |
/// not need to be freed. | |
#[no_mangle] | |
pub extern "C" fn rustls_version() -> rustls_str<'static> { | |
rustls_str::from_str_unchecked(RUSTLS_FFI_VERSION) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment