Skip to content

Commit

Permalink
client: consolidate builder impl blocks
Browse files Browse the repository at this point in the history
Previously there were three. Let's reduce to one.
  • Loading branch information
cpu committed Dec 12, 2024
1 parent 5dfdc6e commit 9582cc8
Showing 1 changed file with 158 additions and 162 deletions.
320 changes: 158 additions & 162 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,142 +157,7 @@ impl rustls_client_config_builder {
rustls_result::Ok
}
}
}

/// 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;

// According to the nomicon https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization):
// > Option<extern "C" fn(c_int) -> c_int> is a correct way to represent a
// > nullable function pointer using the C ABI (corresponding to the C type int (*)(int)).
// So we use Option<...> here. This is the type that is passed from C code.
#[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,
>;

// This is the same as a rustls_verify_server_cert_callback after unwrapping
// the Option (which is equivalent to checking for null).
type VerifyCallback = unsafe extern "C" fn(
userdata: rustls_verify_server_cert_user_data,
params: *const rustls_verify_server_cert_params,
) -> u32;

// An implementation of rustls::ServerCertVerifier based on a C callback.
struct Verifier {
provider: Arc<CryptoProvider>,
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, Error> {
let cb = self.callback;
let server_name = server_name.to_str();
let server_name = match server_name.as_ref().try_into() {
Ok(r) => r,
Err(NulByte {}) => return Err(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(|_| Error::General("internal error with thread-local storage".to_string()))?;
let result = unsafe { cb(userdata, &params) };
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> {
verify_tls12_signature(
message,
cert,
dss,
&self.provider.signature_verification_algorithms,
)
}

fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
verify_tls13_signature(
message,
cert,
dss,
&self.provider.signature_verification_algorithms,
)
}

fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
self.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 using the builder crypto provider.
/// Returns rustls_result::NoDefaultCryptoProvider if no process default crypto
/// provider has been set, and the builder was not constructed with an explicit
Expand Down Expand Up @@ -501,34 +366,7 @@ impl rustls_client_config_builder {
rustls_result::Ok
}
}
}

/// Always send the same client certificate.
#[derive(Debug)]
struct ResolvesClientCertFromChoices {
keys: Vec<Arc<CertifiedKey>>,
}

impl ResolvesClientCert for ResolvesClientCertFromChoices {
fn resolve(
&self,
_acceptable_issuers: &[&[u8]],
sig_schemes: &[SignatureScheme],
) -> Option<Arc<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]
Expand Down Expand Up @@ -596,6 +434,164 @@ impl rustls_client_config_builder {
}
}

/// 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;

// According to the nomicon https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization):
// > Option<extern "C" fn(c_int) -> c_int> is a correct way to represent a
// > nullable function pointer using the C ABI (corresponding to the C type int (*)(int)).
// So we use Option<...> here. This is the type that is passed from C code.
#[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,
>;

// This is the same as a rustls_verify_server_cert_callback after unwrapping
// the Option (which is equivalent to checking for null).
type VerifyCallback = unsafe extern "C" fn(
userdata: rustls_verify_server_cert_user_data,
params: *const rustls_verify_server_cert_params,
) -> u32;

// An implementation of rustls::ServerCertVerifier based on a C callback.
struct Verifier {
provider: Arc<CryptoProvider>,
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, Error> {
let cb = self.callback;
let server_name = server_name.to_str();
let server_name = match server_name.as_ref().try_into() {
Ok(r) => r,
Err(NulByte {}) => return Err(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(|_| Error::General("internal error with thread-local storage".to_string()))?;
let result = unsafe { cb(userdata, &params) };
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> {
verify_tls12_signature(
message,
cert,
dss,
&self.provider.signature_verification_algorithms,
)
}

fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
verify_tls13_signature(
message,
cert,
dss,
&self.provider.signature_verification_algorithms,
)
}

fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
self.provider
.signature_verification_algorithms
.supported_schemes()
}
}

impl Debug for Verifier {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Verifier").finish()
}
}

/// Always send the same client certificate.
#[derive(Debug)]
struct ResolvesClientCertFromChoices {
keys: Vec<Arc<CertifiedKey>>,
}

impl ResolvesClientCert for ResolvesClientCertFromChoices {
fn resolve(
&self,
_acceptable_issuers: &[&[u8]],
sig_schemes: &[SignatureScheme],
) -> Option<Arc<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 {
/// Returns true if a `rustls_connection` created from the `rustls_client_config` will
/// operate in FIPS mode.
Expand Down

0 comments on commit 9582cc8

Please sign in to comment.