Skip to content

Commit 921e367

Browse files
committed
add builder for server cert verifier, root builder from file
* Implement a builder pattern and built representation for the webpki server cert verifier. * Update the client config builder to consume a built server cert verifier. * Update the roots builder to support loading roots from a file in addition to pem buffer.
1 parent 48141bc commit 921e367

File tree

2 files changed

+235
-66
lines changed

2 files changed

+235
-66
lines changed

Diff for: src/cipher.rs

+222-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
use libc::size_t;
1+
use libc::{c_char, size_t};
22
use std::convert::TryFrom;
3-
use std::io::Cursor;
3+
use std::ffi::{CStr, OsStr};
4+
use std::fs::File;
5+
use std::io::{BufReader, Cursor};
46
use std::marker::PhantomData;
57
use std::ptr::null;
68
use std::slice;
79
use std::sync::Arc;
810

911
use pki_types::{CertificateDer, CertificateRevocationListDer, PrivateKeyDer};
12+
use rustls::client::danger::ServerCertVerifier;
13+
use rustls::client::WebPkiServerVerifier;
1014
use rustls::crypto::ring::{ALL_CIPHER_SUITES, DEFAULT_CIPHER_SUITES};
1115
use rustls::server::danger::ClientCertVerifier;
1216
use rustls::server::WebPkiClientVerifier;
@@ -515,6 +519,67 @@ impl rustls_root_cert_store_builder {
515519
}
516520
}
517521

522+
/// Add one or more certificates to the root cert store builder using PEM
523+
/// encoded data read from the named file.
524+
///
525+
/// When `strict` is true an error will return a `CertificateParseError`
526+
/// result. So will an attempt to parse data that has zero certificates.
527+
///
528+
/// When `strict` is false, unparseable root certificates will be ignored.
529+
/// This may be useful on systems that have syntactically invalid root
530+
/// certificates.
531+
#[no_mangle]
532+
pub extern "C" fn rustls_client_config_builder_load_roots_from_file(
533+
builder: *mut rustls_root_cert_store_builder,
534+
filename: *const c_char,
535+
strict: bool,
536+
) -> rustls_result {
537+
ffi_panic_boundary! {
538+
let builder: &mut Option<RootCertStoreBuilder> = try_mut_from_ptr!(builder);
539+
let builder = match builder {
540+
None => return AlreadyUsed,
541+
Some(b) => b,
542+
};
543+
544+
let filename: &CStr = unsafe {
545+
if filename.is_null() {
546+
return rustls_result::NullParameter;
547+
}
548+
CStr::from_ptr(filename)
549+
};
550+
551+
let filename: &[u8] = filename.to_bytes();
552+
let filename: &str = match std::str::from_utf8(filename) {
553+
Ok(s) => s,
554+
Err(_) => return rustls_result::Io,
555+
};
556+
let filename: &OsStr = OsStr::new(filename);
557+
let mut cafile = match File::open(filename) {
558+
Ok(f) => f,
559+
Err(_) => return rustls_result::Io,
560+
};
561+
562+
let mut bufreader = BufReader::new(&mut cafile);
563+
let certs: Result<Vec<CertificateDer>, _> = rustls_pemfile::certs(&mut bufreader).collect();
564+
let certs = match certs {
565+
Ok(certs) => certs,
566+
Err(_) => return rustls_result::Io,
567+
};
568+
569+
// We first copy into a temporary root store so we can uphold our
570+
// API guideline that there are no partial failures or partial
571+
// successes.
572+
let mut roots = RootCertStore::empty();
573+
let (parsed, rejected) = roots.add_parsable_certificates(certs);
574+
if strict && (rejected > 0 || parsed == 0) {
575+
return rustls_result::CertificateParseError;
576+
}
577+
578+
builder.roots.roots.append(&mut roots.roots);
579+
rustls_result::Ok
580+
}
581+
}
582+
518583
/// Create a new `rustls_root_cert_store` from the builder.
519584
///
520585
/// The builder is consumed and cannot be used again, but must still be freed.
@@ -754,3 +819,158 @@ impl rustls_web_pki_client_cert_verifier_builder {
754819
}
755820
}
756821
}
822+
823+
/// A server certificate verifier being constructed. A builder can be modified by,
824+
/// e.g. `rustls_web_pki_server_cert_verifier_builder_add_crl`. Once you're
825+
/// done configuring settings, call `rustls_web_pki_server_cert_verifier_builder_build`
826+
/// to turn it into a `rustls_server_cert_verifier`. This object is not safe
827+
/// for concurrent mutation.
828+
// TODO(@cpu): Add rustdoc link once available.
829+
pub struct rustls_web_pki_server_cert_verifier_builder {
830+
// We use the opaque struct pattern to tell C about our types without
831+
// telling them what's inside.
832+
// https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
833+
_private: [u8; 0],
834+
}
835+
836+
pub(crate) struct ServerCertVerifierBuilder {
837+
roots: Arc<RootCertStore>,
838+
crls: Vec<CertificateRevocationListDer<'static>>,
839+
// TODO(@cpu): revocation checking depth
840+
// TODO(@cpu): unknown revocation status policy
841+
// TODO(@cpu): supported algs?
842+
}
843+
844+
impl Castable for rustls_web_pki_server_cert_verifier_builder {
845+
type Ownership = OwnershipBox;
846+
type RustType = Option<ServerCertVerifierBuilder>;
847+
}
848+
849+
impl ServerCertVerifierBuilder {
850+
/// Create a `rustls_web_pki_server_cert_verifier_builder`. Caller owns the memory and must
851+
/// eventually call `rustls_web_pki_server_cert_verifier_builder_build`, then free the
852+
/// resulting `rustls_server_cert_verifier`.
853+
///
854+
/// Without further modification the builder will produce a server certificate verifier that
855+
/// will require a server present a certificate that chains to one of the trust anchors
856+
/// in the provided `rustls_root_cert_store`. The root cert store must not be empty.
857+
///
858+
/// Revocation checking will not be performed unless
859+
/// `rustls_web_pki_server_cert_verifier_builder_add_crl` is used to add certificate revocation
860+
/// lists (CRLs) to the builder.
861+
///
862+
/// This copies the contents of the `rustls_root_cert_store`. It does not take
863+
/// ownership of the pointed-to data.
864+
#[no_mangle]
865+
pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_new(
866+
store: *const rustls_root_cert_store,
867+
) -> *mut rustls_web_pki_server_cert_verifier_builder {
868+
ffi_panic_boundary! {
869+
let store = try_arc_from_ptr!(store);
870+
let builder = ServerCertVerifierBuilder {
871+
roots: store,
872+
crls: Vec::default(),
873+
};
874+
to_boxed_mut_ptr(Some(builder))
875+
}
876+
}
877+
878+
/// Add one or more certificate revocation lists (CRLs) to the server certificate verifier
879+
/// builder by reading the CRL content from the provided buffer of PEM encoded content.
880+
///
881+
/// This function returns an error if the provided buffer is not valid PEM encoded content.
882+
#[no_mangle]
883+
pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_add_crl(
884+
builder: *mut rustls_web_pki_server_cert_verifier_builder,
885+
crl_pem: *const u8,
886+
crl_pem_len: size_t,
887+
) -> rustls_result {
888+
ffi_panic_boundary! {
889+
let server_verifier_builder: &mut Option<ServerCertVerifierBuilder> = try_mut_from_ptr!(builder);
890+
891+
let crl_pem: &[u8] = try_slice!(crl_pem, crl_pem_len);
892+
let crls_der: Result<Vec<CertificateRevocationListDer>, _> = crls(&mut Cursor::new(crl_pem)).collect();
893+
let crls_der = match crls_der{
894+
Ok(vv) => vv,
895+
Err(_) => return rustls_result::CertificateRevocationListParseError,
896+
};
897+
898+
let server_verifier_builder = match server_verifier_builder {
899+
None => return AlreadyUsed,
900+
Some(v) => v,
901+
};
902+
903+
server_verifier_builder.crls.extend(crls_der);
904+
905+
rustls_result::Ok
906+
}
907+
}
908+
909+
/// Create a new server certificate verifier from the builder.
910+
///
911+
/// The builder is consumed and cannot be used again, but must still be freed.
912+
///
913+
/// The verifier can be used in several `rustls_client_config` instances and must be
914+
/// freed by the application when no longer needed. See the documentation of
915+
/// `rustls_web_pki_server_cert_verifier_builder_free` for details about lifetime.
916+
#[no_mangle]
917+
pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_build(
918+
builder: *mut rustls_web_pki_server_cert_verifier_builder,
919+
verifier_out: *mut *mut rustls_server_cert_verifier,
920+
) -> rustls_result {
921+
ffi_panic_boundary! {
922+
let server_verifier_builder: &mut Option<ServerCertVerifierBuilder> = try_mut_from_ptr!(builder);
923+
let server_verifier_builder = match server_verifier_builder.take() {
924+
None => return AlreadyUsed,
925+
Some(v) => v,
926+
};
927+
928+
let builder = WebPkiServerVerifier::builder(server_verifier_builder.roots)
929+
.with_crls(server_verifier_builder.crls);
930+
931+
let verifier = match builder.build() {
932+
Ok(v) => v,
933+
Err(e) => return error::map_verifier_builder_error(e),
934+
};
935+
936+
set_boxed_mut_ptr(verifier_out, verifier);
937+
938+
rustls_result::Ok
939+
}
940+
}
941+
942+
/// Free a `rustls_server_cert_verifier_builder` previously returned from
943+
/// `rustls_server_cert_verifier_builder_new`. Calling with NULL is fine. Must not be
944+
/// called twice with the same value.
945+
#[no_mangle]
946+
pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_free(
947+
builder: *mut rustls_web_pki_server_cert_verifier_builder,
948+
) {
949+
ffi_panic_boundary! {
950+
free_box(builder);
951+
}
952+
}
953+
}
954+
955+
/// A built server certificate verifier that can be provided to a `rustls_client_config_builder`
956+
/// with `rustls_client_config_builder_set_server_verifier`.
957+
pub struct rustls_server_cert_verifier {
958+
_private: [u8; 0],
959+
}
960+
961+
impl Castable for rustls_server_cert_verifier {
962+
type Ownership = OwnershipBox;
963+
type RustType = Arc<dyn ServerCertVerifier>;
964+
}
965+
966+
impl rustls_server_cert_verifier {
967+
/// Free a `rustls_server_cert_verifier` previously returned from
968+
/// `rustls_server_cert_verifier_builder_build`. Calling with NULL is fine. Must not be
969+
/// called twice with the same value.
970+
#[no_mangle]
971+
pub extern "C" fn rustls_server_cert_verifier_free(verifier: *mut rustls_server_cert_verifier) {
972+
ffi_panic_boundary! {
973+
free_box(verifier);
974+
}
975+
}
976+
}

Diff for: src/client.rs

+13-64
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
use std::borrow::Cow;
22
use std::convert::TryInto;
3-
use std::ffi::{CStr, OsStr};
3+
use std::ffi::CStr;
44
use std::fmt::{Debug, Formatter};
5-
use std::fs::File;
6-
use std::io::BufReader;
75
use std::slice;
86
use std::sync::Arc;
97

@@ -14,10 +12,12 @@ use rustls::client::{ResolvesClientCert, WebPkiServerVerifier};
1412
use rustls::crypto::ring::ALL_CIPHER_SUITES;
1513
use rustls::{
1614
sign::CertifiedKey, CertificateError, ClientConfig, ClientConnection, DigitallySignedStruct,
17-
Error, ProtocolVersion, RootCertStore, SignatureScheme, SupportedCipherSuite, WantsVerifier,
15+
Error, ProtocolVersion, SignatureScheme, SupportedCipherSuite, WantsVerifier,
1816
};
1917

20-
use crate::cipher::{rustls_certified_key, rustls_root_cert_store, rustls_supported_ciphersuite};
18+
use crate::cipher::{
19+
rustls_certified_key, rustls_server_cert_verifier, rustls_supported_ciphersuite,
20+
};
2121
use crate::connection::{rustls_connection, Connection};
2222
use crate::error::rustls_result::{InvalidParameter, NullParameter};
2323
use crate::error::{self, rustls_result};
@@ -364,67 +364,16 @@ impl rustls_client_config_builder {
364364
}
365365
}
366366

367-
/// Use the trusted root certificates from the provided store.
368-
///
369-
/// This replaces any trusted roots already configured with copies
370-
/// from `roots`. This adds 1 to the refcount for `roots`. When you
371-
/// call rustls_client_config_free or rustls_client_config_builder_free,
372-
/// those will subtract 1 from the refcount for `roots`.
367+
/// Configure the server certificate verifier.
373368
#[no_mangle]
374-
pub extern "C" fn rustls_client_config_builder_use_roots(
375-
config_builder: *mut rustls_client_config_builder,
376-
roots: *const rustls_root_cert_store,
377-
) -> rustls_result {
378-
ffi_panic_boundary! {
379-
let builder = try_mut_from_ptr!(config_builder);
380-
let root_store: &RootCertStore = try_ref_from_ptr!(roots);
381-
builder.verifier = Arc::new(rustls::client::WebPkiServerVerifier::new(root_store.clone()));
382-
rustls_result::Ok
383-
}
384-
}
385-
386-
/// Add trusted root certificates from the named file, which should contain
387-
/// PEM-formatted certificates.
388-
#[no_mangle]
389-
pub extern "C" fn rustls_client_config_builder_load_roots_from_file(
390-
config_builder: *mut rustls_client_config_builder,
391-
filename: *const c_char,
392-
) -> rustls_result {
369+
pub extern "C" fn rustls_client_config_builder_set_server_verifier(
370+
builder: *mut rustls_client_config_builder,
371+
verifier: *const rustls_server_cert_verifier,
372+
) {
393373
ffi_panic_boundary! {
394-
let config_builder = try_mut_from_ptr!(config_builder);
395-
let filename: &CStr = unsafe {
396-
if filename.is_null() {
397-
return rustls_result::NullParameter;
398-
}
399-
CStr::from_ptr(filename)
400-
};
401-
402-
let filename: &[u8] = filename.to_bytes();
403-
let filename: &str = match std::str::from_utf8(filename) {
404-
Ok(s) => s,
405-
Err(_) => return rustls_result::Io,
406-
};
407-
let filename: &OsStr = OsStr::new(filename);
408-
let mut cafile = match File::open(filename) {
409-
Ok(f) => f,
410-
Err(_) => return rustls_result::Io,
411-
};
412-
413-
let mut bufreader = BufReader::new(&mut cafile);
414-
let certs: Result<Vec<CertificateDer>, _> = rustls_pemfile::certs(&mut bufreader).collect();
415-
let certs = match certs {
416-
Ok(certs) => certs,
417-
Err(_) => return rustls_result::Io,
418-
};
419-
420-
let mut roots = RootCertStore::empty();
421-
let (_, failed) = roots.add_parsable_certificates(certs);
422-
if failed > 0 {
423-
return rustls_result::CertificateParseError;
424-
}
425-
426-
config_builder.verifier = Arc::new(rustls::client::WebPkiServerVerifier::new(roots));
427-
rustls_result::Ok
374+
let builder: &mut ClientConfigBuilder = try_mut_from_ptr!(builder);
375+
let verifier = try_ref_from_ptr!(verifier);
376+
builder.verifier = verifier.clone();
428377
}
429378
}
430379

0 commit comments

Comments
 (0)