Skip to content

Commit 1af450e

Browse files
committed
cipher: client cert verifier w/ CRLs support.
This commit reworks both the `rustls_client_cert_verifier_optional` and `rustls_client_cert_verifier` structs to use a separate builder pattern. This enables optionally providing the builder CRLs from PEM files. Afterwards the intermediate builder can be turned into the standard `const *` verifiers to provide to the client configuration.
1 parent e0f65c2 commit 1af450e

File tree

4 files changed

+422
-36
lines changed

4 files changed

+422
-36
lines changed

src/cipher.rs

Lines changed: 230 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@ use std::ptr::null;
55
use std::slice;
66
use std::sync::Arc;
77

8-
use rustls::server::{AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient};
8+
use rustls::server::{
9+
AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, UnparsedCertRevocationList,
10+
};
911
use rustls::sign::CertifiedKey;
1012
use rustls::{
1113
Certificate, PrivateKey, RootCertStore, SupportedCipherSuite, ALL_CIPHER_SUITES,
1214
DEFAULT_CIPHER_SUITES,
1315
};
14-
use rustls_pemfile::{certs, pkcs8_private_keys, rsa_private_keys};
16+
use rustls_pemfile::{certs, crls, pkcs8_private_keys, rsa_private_keys};
1517

16-
use crate::error::rustls_result;
18+
use crate::error::{map_error, rustls_result};
1719
use crate::rslice::{rustls_slice_bytes, rustls_str};
1820
use crate::{
1921
ffi_panic_boundary, try_box_from_ptr, try_mut_from_ptr, try_ref_from_ptr, try_slice,
@@ -509,6 +511,97 @@ impl rustls_root_cert_store {
509511
}
510512
}
511513

514+
/// A builder for a `rustls_client_cert_verifier`. This builder object can be used to configure
515+
/// certificate revocation lists, and then turned into a `rustls_client_cert_verifier` once ready.
516+
pub struct rustls_client_cert_verifier_builder {
517+
_private: [u8; 0],
518+
}
519+
520+
// XXX: contained value is consumed even on error, so this can contain None. but the caller
521+
// still needs to free it
522+
pub(crate) struct AllowAnyAuthenticatedClientHolder(Option<AllowAnyAuthenticatedClient>);
523+
524+
impl CastPtr for rustls_client_cert_verifier_builder {
525+
type RustType = AllowAnyAuthenticatedClientHolder;
526+
}
527+
528+
impl BoxCastPtr for rustls_client_cert_verifier_builder {}
529+
530+
impl rustls_client_cert_verifier_builder {
531+
/// Create a new client certificate verifier builder for the root store.
532+
///
533+
/// This copies the contents of the rustls_root_cert_store. It does not take
534+
/// ownership of the pointed-to memory.
535+
///
536+
/// This object can then be used to load any CRLs.
537+
///
538+
/// Once that is complete, convert it into a real `rustls_client_cert_verifier`
539+
/// by calling `rustls_client_cert_verifier_new()`.
540+
#[no_mangle]
541+
pub extern "C" fn rustls_client_cert_verifier_builder_new(
542+
store: *const rustls_root_cert_store,
543+
) -> *mut rustls_client_cert_verifier_builder {
544+
ffi_panic_boundary! {
545+
let store: &RootCertStore = try_ref_from_ptr!(store);
546+
let client_cert_verifier = AllowAnyAuthenticatedClientHolder(Some(AllowAnyAuthenticatedClient::new(store.clone())));
547+
BoxCastPtr::to_mut_ptr(client_cert_verifier)
548+
}
549+
}
550+
551+
/// Add one or more certificate revocation lists (CRLs) to the client certificate verifier by
552+
/// reading the CRL content from the provided buffer of PEM encoded content.
553+
///
554+
/// This function may return an error if the provided buffer is not valid PEM encoded content,
555+
/// or if the CRL content is invalid or unsupported.
556+
#[no_mangle]
557+
pub extern "C" fn rustls_client_cert_verifier_builder_add_crl(
558+
verifier: *mut rustls_client_cert_verifier_builder,
559+
crl_pem: *const u8,
560+
crl_pem_len: size_t,
561+
) -> rustls_result {
562+
ffi_panic_boundary! {
563+
let client_cert_verifier_holder = match BoxCastPtr::to_box(verifier) {
564+
None => {
565+
return NullParameter;
566+
},
567+
Some(x) => Box::leak(x), // nb. does not take ownership of the box.
568+
};
569+
570+
let crl_pem: &[u8] = try_slice!(crl_pem, crl_pem_len);
571+
let crls_der = match crls(&mut Cursor::new(crl_pem)) {
572+
Ok(vv) => vv.into_iter().map(UnparsedCertRevocationList).collect::<Vec<UnparsedCertRevocationList>>(),
573+
Err(_) => return rustls_result::CertificateRevocationListParseError,
574+
};
575+
576+
let client_cert_verifier = match client_cert_verifier_holder.0.take() {
577+
None => {
578+
return NullParameter;
579+
},
580+
Some(x) => x,
581+
};
582+
583+
match client_cert_verifier.with_crls(crls_der) {
584+
Ok(v) => client_cert_verifier_holder.0.replace(v),
585+
Err(e) => return map_error(rustls::Error::InvalidCertRevocationList(e)),
586+
};
587+
588+
rustls_result::Ok
589+
}
590+
}
591+
592+
/// Free a rustls_client_cert_verifier_builder previously returned from rustls_client_cert_verifier_builder_new.
593+
/// Calling with NULL is fine. Must not be called twice with the same value.
594+
#[no_mangle]
595+
pub extern "C" fn rustls_client_cert_verifier_builder_free(
596+
builder: *mut rustls_client_cert_verifier_builder,
597+
) {
598+
ffi_panic_boundary! {
599+
let store = try_box_from_ptr!(builder);
600+
drop(store)
601+
}
602+
}
603+
}
604+
512605
/// A verifier of client certificates that requires all certificates to be
513606
/// trusted based on a given `rustls_root_cert_store`. Usable in building server
514607
/// configurations. Connections without such a client certificate will not
@@ -517,26 +610,37 @@ pub struct rustls_client_cert_verifier {
517610
_private: [u8; 0],
518611
}
519612

520-
impl CastConstPtr for rustls_client_cert_verifier {
613+
impl CastPtr for rustls_client_cert_verifier {
521614
type RustType = AllowAnyAuthenticatedClient;
522615
}
523616

524617
impl ArcCastPtr for rustls_client_cert_verifier {}
525618

526619
impl rustls_client_cert_verifier {
527-
/// Create a new client certificate verifier for the root store. The verifier
528-
/// can be used in several rustls_server_config instances. Must be freed by
620+
/// Create a new client certificate verifier from a builder.
621+
///
622+
/// The builder is consumed and is no longer valid.
623+
///
624+
/// The verifier can be used in several rustls_server_config instances. Must be freed by
529625
/// the application when no longer needed. See the documentation of
530626
/// rustls_client_cert_verifier_free for details about lifetime.
531-
/// This copies the contents of the rustls_root_cert_store. It does not take
532-
/// ownership of the pointed-to memory.
533627
#[no_mangle]
534628
pub extern "C" fn rustls_client_cert_verifier_new(
535-
store: *const rustls_root_cert_store,
629+
builder: *mut rustls_client_cert_verifier_builder,
536630
) -> *const rustls_client_cert_verifier {
537631
ffi_panic_boundary! {
538-
let store: &RootCertStore = try_ref_from_ptr!(store);
539-
let client_cert_verifier = AllowAnyAuthenticatedClient::new(store.clone());
632+
let mut client_cert_verifier_holder = match BoxCastPtr::to_box(builder) {
633+
None => {
634+
return null() as *const _;
635+
},
636+
Some(x) => x,
637+
};
638+
let client_cert_verifier = match client_cert_verifier_holder.0.take() {
639+
None => {
640+
return null() as *const _;
641+
},
642+
Some(x) => x,
643+
};
540644
return Arc::into_raw(client_cert_verifier.boxed()) as *const _;
541645
}
542646
}
@@ -557,6 +661,101 @@ impl rustls_client_cert_verifier {
557661
}
558662
}
559663

664+
/// A builder for a `rustls_client_cert_verifier_optional`. This builder object can be used to configure
665+
/// certificate revocation lists, and then turned into a `rustls_client_cert_verifier_optional` once ready.
666+
pub struct rustls_client_cert_verifier_optional_builder {
667+
_private: [u8; 0],
668+
}
669+
670+
// XXX: contained value is consumed even on error, so this can contain None. but the caller
671+
// still needs to free it
672+
pub(crate) struct AllowAnyAnonymousOrAuthenticatedClientHolder(
673+
Option<AllowAnyAnonymousOrAuthenticatedClient>,
674+
);
675+
676+
impl CastPtr for rustls_client_cert_verifier_optional_builder {
677+
type RustType = AllowAnyAnonymousOrAuthenticatedClientHolder;
678+
}
679+
680+
impl BoxCastPtr for rustls_client_cert_verifier_optional_builder {}
681+
682+
impl rustls_client_cert_verifier_optional_builder {
683+
/// Create a new optional client certificate verifier builder for the root store.
684+
///
685+
/// This copies the contents of the rustls_root_cert_store. It does not take
686+
/// ownership of the pointed-to memory.
687+
///
688+
/// This object can then be used to load any CRLs.
689+
///
690+
/// Once that is complete, convert it into a real `rustls_client_cert_verifier_optional`
691+
/// by calling `rustls_client_cert_verifier_optional_new()`.
692+
#[no_mangle]
693+
pub extern "C" fn rustls_client_cert_verifier_optional_builder_new(
694+
store: *const rustls_root_cert_store,
695+
) -> *mut rustls_client_cert_verifier_optional_builder {
696+
ffi_panic_boundary! {
697+
let store: &RootCertStore = try_ref_from_ptr!(store);
698+
let client_cert_verifier = AllowAnyAnonymousOrAuthenticatedClientHolder(
699+
Some(AllowAnyAnonymousOrAuthenticatedClient::new(store.clone())));
700+
BoxCastPtr::to_mut_ptr(client_cert_verifier)
701+
}
702+
}
703+
704+
/// Add one or more certificate revocation lists (CRLs) to the client certificate verifier by
705+
/// reading the CRL content from the provided buffer of PEM encoded content.
706+
///
707+
/// This function may return an error if the provided buffer is not valid PEM encoded content,
708+
/// or if the CRL content is invalid or unsupported.
709+
#[no_mangle]
710+
pub extern "C" fn rustls_client_cert_verifier_optional_builder_add_crl(
711+
verifier: *mut rustls_client_cert_verifier_optional_builder,
712+
crl_pem: *const u8,
713+
crl_pem_len: size_t,
714+
) -> rustls_result {
715+
ffi_panic_boundary! {
716+
let client_cert_verifier_holder = match BoxCastPtr::to_box(verifier) {
717+
None => {
718+
return NullParameter;
719+
},
720+
Some(x) => Box::leak(x), // nb. does not take ownership of the box.
721+
};
722+
723+
let crl_pem: &[u8] = try_slice!(crl_pem, crl_pem_len);
724+
let crls_der = match crls(&mut Cursor::new(crl_pem)) {
725+
Ok(vv) => vv.into_iter().map(UnparsedCertRevocationList).collect::<Vec<UnparsedCertRevocationList>>(),
726+
Err(_) => return rustls_result::CertificateRevocationListParseError,
727+
};
728+
729+
let client_cert_verifier = match client_cert_verifier_holder.0.take() {
730+
None => {
731+
return NullParameter;
732+
},
733+
Some(x) => x,
734+
};
735+
736+
match client_cert_verifier.with_crls(crls_der) {
737+
Ok(v) => client_cert_verifier_holder.0.replace(v),
738+
Err(e) => return map_error(rustls::Error::InvalidCertRevocationList(e)),
739+
};
740+
741+
rustls_result::Ok
742+
}
743+
}
744+
745+
/// Free a rustls_client_cert_verifier_optional_builder previously returned from
746+
/// rustls_client_cert_verifier_optional_builder_new.
747+
/// Calling with NULL is fine. Must not be called twice with the same value.
748+
#[no_mangle]
749+
pub extern "C" fn rustls_client_cert_verifier_optional_builder_free(
750+
builder: *mut rustls_client_cert_verifier_optional_builder,
751+
) {
752+
ffi_panic_boundary! {
753+
let store = try_box_from_ptr!(builder);
754+
drop(store)
755+
}
756+
}
757+
}
758+
560759
/// Alternative to `rustls_client_cert_verifier` that allows connections
561760
/// with or without a client certificate. If the client offers a certificate,
562761
/// it will be verified (and rejected if it is not valid). If the client
@@ -575,21 +774,31 @@ impl CastConstPtr for rustls_client_cert_verifier_optional {
575774
impl ArcCastPtr for rustls_client_cert_verifier_optional {}
576775

577776
impl rustls_client_cert_verifier_optional {
578-
/// Create a new rustls_client_cert_verifier_optional for the root store. The
579-
/// verifier can be used in several rustls_server_config instances. Must be
580-
/// freed by the application when no longer needed. See the documentation of
777+
/// Create a new optional client certificate verifier from a builder.
778+
///
779+
/// The builder is consumed and is no longer valid.
780+
///
781+
/// The verifier can be used in several rustls_server_config instances. Must be freed by
782+
/// the application when no longer needed. See the documentation of
581783
/// rustls_client_cert_verifier_optional_free for details about lifetime.
582-
/// This copies the contents of the rustls_root_cert_store. It does not take
583-
/// ownership of the pointed-to data.
584784
#[no_mangle]
585785
pub extern "C" fn rustls_client_cert_verifier_optional_new(
586-
store: *const rustls_root_cert_store,
786+
builder: *mut rustls_client_cert_verifier_optional_builder,
587787
) -> *const rustls_client_cert_verifier_optional {
588788
ffi_panic_boundary! {
589-
let store: &RootCertStore = try_ref_from_ptr!(store);
590-
let client_cert_verifier = AllowAnyAnonymousOrAuthenticatedClient::new(store.clone());
591-
return Arc::into_raw(client_cert_verifier.boxed())
592-
as *const _;
789+
let mut client_cert_verifier_holder = match BoxCastPtr::to_box(builder) {
790+
None => {
791+
return null() as *const _;
792+
},
793+
Some(x) => x,
794+
};
795+
let client_cert_verifier = match client_cert_verifier_holder.0.take() {
796+
None => {
797+
return null() as *const _;
798+
},
799+
Some(x) => x,
800+
};
801+
return Arc::into_raw(client_cert_verifier.boxed()) as *const _;
593802
}
594803
}
595804

0 commit comments

Comments
 (0)