Skip to content

Commit ce31cd5

Browse files
committed
builder for root_cert_store
1 parent df26cf9 commit ce31cd5

File tree

3 files changed

+144
-33
lines changed

3 files changed

+144
-33
lines changed

src/cipher.rs

+92-19
Original file line numberDiff line numberDiff line change
@@ -430,35 +430,47 @@ impl rustls_certified_key {
430430
}
431431
}
432432

433-
/// A root certificate store.
434-
/// <https://docs.rs/rustls/latest/rustls/struct.RootCertStore.html>
435-
pub struct rustls_root_cert_store {
433+
/// A `rustls_root_cert_store` being constructed. A builder can be modified by,
434+
/// adding trust anchor root certificates with `rustls_root_cert_store_builder_add_pem`.
435+
/// Once you're done adding root certificates, call `rustls_root_cert_store_builder_build`
436+
/// to turn it into a `rustls_root_cert_store`. This object is not safe
437+
/// for concurrent mutation.
438+
pub struct rustls_root_cert_store_builder {
436439
// We use the opaque struct pattern to tell C about our types without
437440
// telling them what's inside.
438441
// https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
439442
_private: [u8; 0],
440443
}
441444

442-
impl CastPtr for rustls_root_cert_store {
443-
type RustType = RootCertStore;
445+
pub(crate) struct RootCertStoreBuilder {
446+
roots: RootCertStore,
444447
}
445448

446-
impl ArcCastPtr for rustls_root_cert_store {}
449+
impl CastPtr for rustls_root_cert_store_builder {
450+
type RustType = Option<RootCertStoreBuilder>;
451+
}
447452

448-
impl rustls_root_cert_store {
449-
/// Create a rustls_root_cert_store. Caller owns the memory and must
450-
/// eventually call rustls_root_cert_store_free. The store starts out empty.
451-
/// Caller must add root certificates with rustls_root_cert_store_add_pem.
452-
/// <https://docs.rs/rustls/latest/rustls/struct.RootCertStore.html#method.empty>
453+
impl BoxCastPtr for rustls_root_cert_store_builder {}
454+
455+
impl rustls_root_cert_store_builder {
456+
/// Create a `rustls_root_cert_store_builder`. Caller owns the memory and must
457+
/// eventually call `rustls_root_cert_store_builder_build`, then free the
458+
/// resulting `rustls_root_cert_store`.
459+
///
460+
/// If you wish to abandon the builder without calling `rustls_root_cert_store_builder_build`,
461+
/// it must be freed with `rustls_root_cert_store_builder_free`.
453462
#[no_mangle]
454-
pub extern "C" fn rustls_root_cert_store_new() -> *const rustls_root_cert_store {
463+
pub extern "C" fn rustls_root_cert_store_builder_new() -> *mut rustls_root_cert_store_builder {
455464
ffi_panic_boundary! {
456-
let store = rustls::RootCertStore::empty();
457-
ArcCastPtr::to_const_ptr(store)
465+
let builder = RootCertStoreBuilder {
466+
roots: RootCertStore::empty(),
467+
};
468+
BoxCastPtr::to_mut_ptr(Some(builder))
458469
}
459470
}
460471

461-
/// Add one or more certificates to the root cert store using PEM encoded data.
472+
/// Add one or more certificates to the root cert store builder using PEM
473+
/// encoded data.
462474
///
463475
/// When `strict` is true an error will return a `CertificateParseError`
464476
/// result. So will an attempt to parse data that has zero certificates.
@@ -467,15 +479,19 @@ impl rustls_root_cert_store {
467479
/// This may be useful on systems that have syntactically invalid root
468480
/// certificates.
469481
#[no_mangle]
470-
pub extern "C" fn rustls_root_cert_store_add_pem(
471-
store: *mut rustls_root_cert_store,
482+
pub extern "C" fn rustls_root_cert_store_builder_add_pem(
483+
builder: *mut rustls_root_cert_store_builder,
472484
pem: *const u8,
473485
pem_len: size_t,
474486
strict: bool,
475487
) -> rustls_result {
476488
ffi_panic_boundary! {
477489
let certs_pem: &[u8] = try_slice!(pem, pem_len);
478-
let store: &mut RootCertStore = try_mut_from_ptr!(store);
490+
let builder: &mut Option<RootCertStoreBuilder> = try_mut_from_ptr!(builder);
491+
let builder = match builder {
492+
None => return AlreadyUsed,
493+
Some(b) => b,
494+
};
479495

480496
let certs_der: Result<Vec<CertificateDer>, _> = rustls_pemfile::certs(&mut Cursor::new(certs_pem)).collect();
481497
let certs_der = match certs_der {
@@ -491,11 +507,68 @@ impl rustls_root_cert_store {
491507
return rustls_result::CertificateParseError;
492508
}
493509

494-
store.roots.append(&mut new_store.roots);
510+
builder.roots.roots.append(&mut new_store.roots);
511+
495512
rustls_result::Ok
496513
}
497514
}
498515

516+
/// Create a new `rustls_root_cert_store` from the builder.
517+
///
518+
/// The builder is consumed and cannot be used again, but must still be freed.
519+
///
520+
/// The root cert store can be used in several `rustls_web_pki_client_cert_verifier_builder_new`
521+
/// instances and must be freed by the application when no longer needed. See the documentation of
522+
/// `rustls_root_cert_store_free` for details about lifetime.
523+
#[no_mangle]
524+
pub extern "C" fn rustls_root_cert_store_builder_build(
525+
builder: *mut rustls_root_cert_store_builder,
526+
root_cert_store_out: *mut *const rustls_root_cert_store,
527+
) -> rustls_result {
528+
ffi_panic_boundary! {
529+
let builder: &mut Option<RootCertStoreBuilder> = try_mut_from_ptr!(builder);
530+
let builder = match builder {
531+
None => return AlreadyUsed,
532+
Some(b) => b,
533+
};
534+
535+
unsafe {
536+
*root_cert_store_out = ArcCastPtr::to_const_ptr(builder.roots.clone());
537+
}
538+
539+
rustls_result::Ok
540+
}
541+
}
542+
543+
/// Free a `rustls_root_cert_store_builder` previously returned from
544+
/// `rustls_root_cert_store_builder_new`. Calling with NULL is fine. Must not be
545+
/// called twice with the same value.
546+
#[no_mangle]
547+
pub extern "C" fn rustls_root_cert_store_builder_free(
548+
builder: *mut rustls_root_cert_store_builder,
549+
) {
550+
ffi_panic_boundary! {
551+
BoxCastPtr::to_box(builder);
552+
}
553+
}
554+
}
555+
556+
/// A root certificate store.
557+
/// <https://docs.rs/rustls/latest/rustls/struct.RootCertStore.html>
558+
pub struct rustls_root_cert_store {
559+
// We use the opaque struct pattern to tell C about our types without
560+
// telling them what's inside.
561+
// https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
562+
_private: [u8; 0],
563+
}
564+
565+
impl CastPtr for rustls_root_cert_store {
566+
type RustType = RootCertStore;
567+
}
568+
569+
impl ArcCastPtr for rustls_root_cert_store {}
570+
571+
impl rustls_root_cert_store {
499572
/// Free a rustls_root_cert_store previously returned from rustls_root_cert_store_builder_build.
500573
/// Calling with NULL is fine. Must not be called twice with the same value.
501574
#[no_mangle]

src/rustls.h

+41-10
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,15 @@ typedef struct rustls_iovec rustls_iovec;
221221
*/
222222
typedef struct rustls_root_cert_store rustls_root_cert_store;
223223

224+
/**
225+
* A `rustls_root_cert_store` being constructed. A builder can be modified by,
226+
* adding trust anchor root certificates with `rustls_root_cert_store_builder_add_pem`.
227+
* Once you're done adding root certificates, call `rustls_root_cert_store_builder_build`
228+
* to turn it into a `rustls_root_cert_store`. This object is not safe
229+
* for concurrent mutation.
230+
*/
231+
typedef struct rustls_root_cert_store_builder rustls_root_cert_store_builder;
232+
224233
/**
225234
* A server config that is done being constructed and is now read-only.
226235
* Under the hood, this object corresponds to an `Arc<ServerConfig>`.
@@ -920,15 +929,18 @@ rustls_result rustls_certified_key_clone_with_ocsp(const struct rustls_certified
920929
void rustls_certified_key_free(const struct rustls_certified_key *key);
921930

922931
/**
923-
* Create a rustls_root_cert_store. Caller owns the memory and must
924-
* eventually call rustls_root_cert_store_free. The store starts out empty.
925-
* Caller must add root certificates with rustls_root_cert_store_add_pem.
926-
* <https://docs.rs/rustls/latest/rustls/struct.RootCertStore.html#method.empty>
932+
* Create a `rustls_root_cert_store_builder`. Caller owns the memory and must
933+
* eventually call `rustls_root_cert_store_builder_build`, then free the
934+
* resulting `rustls_root_cert_store`.
935+
*
936+
* If you wish to abandon the builder without calling `rustls_root_cert_store_builder_build`,
937+
* it must be freed with `rustls_root_cert_store_builder_free`.
927938
*/
928-
const struct rustls_root_cert_store *rustls_root_cert_store_new(void);
939+
struct rustls_root_cert_store_builder *rustls_root_cert_store_builder_new(void);
929940

930941
/**
931-
* Add one or more certificates to the root cert store using PEM encoded data.
942+
* Add one or more certificates to the root cert store builder using PEM
943+
* encoded data.
932944
*
933945
* When `strict` is true an error will return a `CertificateParseError`
934946
* result. So will an attempt to parse data that has zero certificates.
@@ -937,10 +949,29 @@ const struct rustls_root_cert_store *rustls_root_cert_store_new(void);
937949
* This may be useful on systems that have syntactically invalid root
938950
* certificates.
939951
*/
940-
rustls_result rustls_root_cert_store_add_pem(struct rustls_root_cert_store *store,
941-
const uint8_t *pem,
942-
size_t pem_len,
943-
bool strict);
952+
rustls_result rustls_root_cert_store_builder_add_pem(struct rustls_root_cert_store_builder *builder,
953+
const uint8_t *pem,
954+
size_t pem_len,
955+
bool strict);
956+
957+
/**
958+
* Create a new `rustls_root_cert_store` from the builder.
959+
*
960+
* The builder is consumed and cannot be used again, but must still be freed.
961+
*
962+
* The root cert store can be used in several `rustls_web_pki_client_cert_verifier_builder_new`
963+
* instances and must be freed by the application when no longer needed. See the documentation of
964+
* `rustls_root_cert_store_free` for details about lifetime.
965+
*/
966+
rustls_result rustls_root_cert_store_builder_build(struct rustls_root_cert_store_builder *builder,
967+
const struct rustls_root_cert_store **root_cert_store_out);
968+
969+
/**
970+
* Free a `rustls_root_cert_store_builder` previously returned from
971+
* `rustls_root_cert_store_builder_new`. Calling with NULL is fine. Must not be
972+
* called twice with the same value.
973+
*/
974+
void rustls_root_cert_store_builder_free(struct rustls_root_cert_store_builder *builder);
944975

945976
/**
946977
* Free a rustls_root_cert_store previously returned from rustls_root_cert_store_builder_build.

tests/server.c

+11-4
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,8 @@ main(int argc, const char **argv)
238238
struct rustls_connection *rconn = NULL;
239239
const struct rustls_certified_key *certified_key = NULL;
240240
struct rustls_slice_bytes alpn_http11;
241-
struct rustls_root_cert_store *client_cert_root_store = NULL;
241+
struct rustls_root_cert_store_builder *client_cert_root_store_builder = NULL;
242+
const struct rustls_root_cert_store *client_cert_root_store = NULL;
242243
struct rustls_web_pki_client_cert_verifier_builder
243244
*client_cert_verifier_builder = NULL;
244245
const struct rustls_client_cert_verifier *client_cert_verifier = NULL;
@@ -287,9 +288,14 @@ main(int argc, const char **argv)
287288
goto cleanup;
288289
}
289290

290-
client_cert_root_store = rustls_root_cert_store_new();
291-
result = rustls_root_cert_store_add_pem(
292-
client_cert_root_store, (uint8_t *)certbuf, certbuf_len, true);
291+
client_cert_root_store_builder = rustls_root_cert_store_builder_new();
292+
result = rustls_root_cert_store_builder_add_pem(
293+
client_cert_root_store_builder, (uint8_t *)certbuf, certbuf_len, true);
294+
if(result != RUSTLS_RESULT_OK) {
295+
goto cleanup;
296+
}
297+
result = rustls_root_cert_store_builder_build(
298+
client_cert_root_store_builder, &client_cert_root_store);
293299
if(result != RUSTLS_RESULT_OK) {
294300
goto cleanup;
295301
}
@@ -397,6 +403,7 @@ main(int argc, const char **argv)
397403

398404
cleanup:
399405
rustls_certified_key_free(certified_key);
406+
rustls_root_cert_store_builder_free(client_cert_root_store_builder);
400407
rustls_root_cert_store_free(client_cert_root_store);
401408
rustls_web_pki_client_cert_verifier_builder_free(
402409
client_cert_verifier_builder);

0 commit comments

Comments
 (0)