|
1 |
| -use libc::size_t; |
| 1 | +use libc::{c_char, size_t}; |
2 | 2 | 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}; |
4 | 6 | use std::marker::PhantomData;
|
5 | 7 | use std::ptr::null;
|
6 | 8 | use std::slice;
|
7 | 9 | use std::sync::Arc;
|
8 | 10 |
|
9 | 11 | use pki_types::{CertificateDer, CertificateRevocationListDer, PrivateKeyDer};
|
| 12 | +use rustls::client::danger::ServerCertVerifier; |
| 13 | +use rustls::client::WebPkiServerVerifier; |
10 | 14 | use rustls::crypto::ring::{ALL_CIPHER_SUITES, DEFAULT_CIPHER_SUITES};
|
11 | 15 | use rustls::server::danger::ClientCertVerifier;
|
12 | 16 | use rustls::server::WebPkiClientVerifier;
|
@@ -515,6 +519,67 @@ impl rustls_root_cert_store_builder {
|
515 | 519 | }
|
516 | 520 | }
|
517 | 521 |
|
| 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 | + |
518 | 583 | /// Create a new `rustls_root_cert_store` from the builder.
|
519 | 584 | ///
|
520 | 585 | /// 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 {
|
754 | 819 | }
|
755 | 820 | }
|
756 | 821 | }
|
| 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 | +} |
0 commit comments