vector_core/tls/
settings.rs

1use std::{
2    fmt,
3    fs::File,
4    io::Read,
5    path::{Path, PathBuf},
6};
7
8use lookup::lookup_v2::OptionalValuePath;
9use openssl::{
10    pkcs12::{ParsedPkcs12_2, Pkcs12},
11    pkey::{PKey, Private},
12    ssl::{select_next_proto, AlpnError, ConnectConfiguration, SslContextBuilder, SslVerifyMode},
13    stack::Stack,
14    x509::{store::X509StoreBuilder, X509},
15};
16use snafu::ResultExt;
17use vector_config::configurable_component;
18
19use super::{
20    AddCertToStoreSnafu, AddExtraChainCertSnafu, CaStackPushSnafu, DerExportSnafu,
21    EncodeAlpnProtocolsSnafu, FileOpenFailedSnafu, FileReadFailedSnafu, MaybeTls, NewCaStackSnafu,
22    NewStoreBuilderSnafu, ParsePkcs12Snafu, Pkcs12Snafu, PrivateKeyParseSnafu, Result,
23    SetAlpnProtocolsSnafu, SetCertificateSnafu, SetPrivateKeySnafu, SetVerifyCertSnafu, TlsError,
24    TlsIdentitySnafu, X509ParseSnafu,
25};
26
27pub const PEM_START_MARKER: &str = "-----BEGIN ";
28
29pub const TEST_PEM_CA_PATH: &str = "tests/data/ca/certs/ca.cert.pem";
30pub const TEST_PEM_INTERMEDIATE_CA_PATH: &str =
31    "tests/data/ca/intermediate_server/certs/ca-chain.cert.pem";
32pub const TEST_PEM_CRT_PATH: &str =
33    "tests/data/ca/intermediate_server/certs/localhost-chain.cert.pem";
34pub const TEST_PEM_KEY_PATH: &str = "tests/data/ca/intermediate_server/private/localhost.key.pem";
35pub const TEST_PEM_CLIENT_CRT_PATH: &str =
36    "tests/data/ca/intermediate_client/certs/localhost-chain.cert.pem";
37pub const TEST_PEM_CLIENT_KEY_PATH: &str =
38    "tests/data/ca/intermediate_client/private/localhost.key.pem";
39
40/// Configures the TLS options for incoming/outgoing connections.
41#[configurable_component]
42#[configurable(metadata(docs::advanced))]
43#[derive(Clone, Debug, Default)]
44#[serde(deny_unknown_fields)]
45pub struct TlsEnableableConfig {
46    /// Whether to require TLS for incoming or outgoing connections.
47    ///
48    /// When enabled and used for incoming connections, an identity certificate is also required. See `tls.crt_file` for
49    /// more information.
50    pub enabled: Option<bool>,
51
52    #[serde(flatten)]
53    pub options: TlsConfig,
54}
55
56impl TlsEnableableConfig {
57    pub fn enabled() -> Self {
58        Self {
59            enabled: Some(true),
60            ..Self::default()
61        }
62    }
63
64    pub fn test_config() -> Self {
65        Self {
66            enabled: Some(true),
67            options: TlsConfig::test_config(),
68        }
69    }
70}
71
72/// TlsEnableableConfig for `sources`, adding metadata from the client certificate.
73#[configurable_component]
74#[derive(Clone, Debug, Default)]
75pub struct TlsSourceConfig {
76    /// Event field for client certificate metadata.
77    pub client_metadata_key: Option<OptionalValuePath>,
78
79    #[serde(flatten)]
80    pub tls_config: TlsEnableableConfig,
81}
82
83/// TLS configuration.
84#[configurable_component]
85#[configurable(metadata(docs::advanced))]
86#[derive(Clone, Debug, Default)]
87#[serde(deny_unknown_fields)]
88pub struct TlsConfig {
89    /// Enables certificate verification. For components that create a server, this requires that the
90    /// client connections have a valid client certificate. For components that initiate requests,
91    /// this validates that the upstream has a valid certificate.
92    ///
93    /// If enabled, certificates must not be expired and must be issued by a trusted
94    /// issuer. This verification operates in a hierarchical manner, checking that the leaf certificate (the
95    /// certificate presented by the client/server) is not only valid, but that the issuer of that certificate is also valid, and
96    /// so on, until the verification process reaches a root certificate.
97    ///
98    /// Do NOT set this to `false` unless you understand the risks of not verifying the validity of certificates.
99    pub verify_certificate: Option<bool>,
100
101    /// Enables hostname verification.
102    ///
103    /// If enabled, the hostname used to connect to the remote host must be present in the TLS certificate presented by
104    /// the remote host, either as the Common Name or as an entry in the Subject Alternative Name extension.
105    ///
106    /// Only relevant for outgoing connections.
107    ///
108    /// Do NOT set this to `false` unless you understand the risks of not verifying the remote hostname.
109    pub verify_hostname: Option<bool>,
110
111    /// Sets the list of supported ALPN protocols.
112    ///
113    /// Declare the supported ALPN protocols, which are used during negotiation with a peer. They are prioritized in the order
114    /// that they are defined.
115    #[configurable(metadata(docs::examples = "h2"))]
116    pub alpn_protocols: Option<Vec<String>>,
117
118    /// Absolute path to an additional CA certificate file.
119    ///
120    /// The certificate must be in the DER or PEM (X.509) format. Additionally, the certificate can be provided as an inline string in PEM format.
121    #[serde(alias = "ca_path")]
122    #[configurable(metadata(docs::examples = "/path/to/certificate_authority.crt"))]
123    #[configurable(metadata(docs::human_name = "CA File Path"))]
124    pub ca_file: Option<PathBuf>,
125
126    /// Absolute path to a certificate file used to identify this server.
127    ///
128    /// The certificate must be in DER, PEM (X.509), or PKCS#12 format. Additionally, the certificate can be provided as
129    /// an inline string in PEM format.
130    ///
131    /// If this is set _and_ is not a PKCS#12 archive, `key_file` must also be set.
132    #[serde(alias = "crt_path")]
133    #[configurable(metadata(docs::examples = "/path/to/host_certificate.crt"))]
134    #[configurable(metadata(docs::human_name = "Certificate File Path"))]
135    pub crt_file: Option<PathBuf>,
136
137    /// Absolute path to a private key file used to identify this server.
138    ///
139    /// The key must be in DER or PEM (PKCS#8) format. Additionally, the key can be provided as an inline string in PEM format.
140    #[serde(alias = "key_path")]
141    #[configurable(metadata(docs::examples = "/path/to/host_certificate.key"))]
142    #[configurable(metadata(docs::human_name = "Key File Path"))]
143    pub key_file: Option<PathBuf>,
144
145    /// Passphrase used to unlock the encrypted key file.
146    ///
147    /// This has no effect unless `key_file` is set.
148    #[configurable(metadata(docs::examples = "${KEY_PASS_ENV_VAR}"))]
149    #[configurable(metadata(docs::examples = "PassWord1"))]
150    #[configurable(metadata(docs::human_name = "Key File Password"))]
151    pub key_pass: Option<String>,
152
153    /// Server name to use when using Server Name Indication (SNI).
154    ///
155    /// Only relevant for outgoing connections.
156    #[serde(alias = "server_name")]
157    #[configurable(metadata(docs::examples = "www.example.com"))]
158    #[configurable(metadata(docs::human_name = "Server Name"))]
159    pub server_name: Option<String>,
160}
161
162impl TlsConfig {
163    pub fn test_config() -> Self {
164        Self {
165            ca_file: Some(TEST_PEM_CA_PATH.into()),
166            crt_file: Some(TEST_PEM_CRT_PATH.into()),
167            key_file: Some(TEST_PEM_KEY_PATH.into()),
168            ..Self::default()
169        }
170    }
171}
172
173/// Directly usable settings for TLS connectors
174#[derive(Clone, Default)]
175pub struct TlsSettings {
176    verify_certificate: bool,
177    pub(super) verify_hostname: bool,
178    authorities: Vec<X509>,
179    pub(super) identity: Option<IdentityStore>, // openssl::pkcs12::ParsedPkcs12 doesn't impl Clone yet
180    alpn_protocols: Option<Vec<u8>>,
181    server_name: Option<String>,
182}
183
184#[derive(Clone)]
185pub(super) struct IdentityStore(Vec<u8>, String);
186
187impl TlsSettings {
188    /// Generate a filled out settings struct from the given optional
189    /// option set, interpreted as client options. If `options` is
190    /// `None`, the result is set to defaults (ie empty).
191    pub fn from_options(options: Option<&TlsConfig>) -> Result<Self> {
192        Self::from_options_base(options, false)
193    }
194
195    pub(super) fn from_options_base(options: Option<&TlsConfig>, for_server: bool) -> Result<Self> {
196        let default = TlsConfig::default();
197        let options = options.unwrap_or(&default);
198
199        if !for_server {
200            if options.verify_certificate == Some(false) {
201                warn!(
202                    "The `verify_certificate` option is DISABLED, this may lead to security vulnerabilities."
203                );
204            }
205            if options.verify_hostname == Some(false) {
206                warn!("The `verify_hostname` option is DISABLED, this may lead to security vulnerabilities.");
207            }
208        }
209
210        Ok(Self {
211            verify_certificate: options.verify_certificate.unwrap_or(!for_server),
212            verify_hostname: options.verify_hostname.unwrap_or(!for_server),
213            authorities: options.load_authorities()?,
214            identity: options.load_identity()?,
215            alpn_protocols: options.parse_alpn_protocols()?,
216            server_name: options.server_name.clone(),
217        })
218    }
219
220    /// Returns the identity as PKCS12
221    ///
222    /// # Panics
223    ///
224    /// Panics if the identity is invalid.
225    fn identity(&self) -> Option<ParsedPkcs12_2> {
226        // This data was test-built previously, so we can just use it
227        // here and expect the results will not fail. This can all be
228        // reworked when `openssl::pkcs12::ParsedPkcs12` gains the Clone
229        // impl.
230        self.identity.as_ref().map(|identity| {
231            Pkcs12::from_der(&identity.0)
232                .expect("Could not build PKCS#12 archive from parsed data")
233                .parse2(&identity.1)
234                .expect("Could not parse stored PKCS#12 archive")
235        })
236    }
237
238    /// Returns the identity as PEM data
239    ///
240    /// # Panics
241    ///
242    /// Panics if the identity is missing, invalid, or the authorities to chain are invalid.
243    pub fn identity_pem(&self) -> Option<(Vec<u8>, Vec<u8>)> {
244        self.identity().map(|identity| {
245            let mut cert = identity
246                .cert
247                .expect("Identity required")
248                .to_pem()
249                .expect("Invalid stored identity");
250            if let Some(chain) = identity.ca {
251                for authority in chain {
252                    cert.extend(
253                        authority
254                            .to_pem()
255                            .expect("Invalid stored identity chain certificate"),
256                    );
257                }
258            }
259            let key = identity
260                .pkey
261                .expect("Private key required")
262                .private_key_to_pem_pkcs8()
263                .expect("Invalid stored private key");
264            (cert, key)
265        })
266    }
267
268    /// Returns the authorities as PEM data
269    ///
270    /// # Panics
271    ///
272    /// Panics if the authority is invalid.
273    pub fn authorities_pem(&self) -> impl Iterator<Item = Vec<u8>> + '_ {
274        self.authorities.iter().map(|authority| {
275            authority
276                .to_pem()
277                .expect("Invalid stored authority certificate")
278        })
279    }
280
281    pub(super) fn apply_context(&self, context: &mut SslContextBuilder) -> Result<()> {
282        self.apply_context_base(context, false)
283    }
284
285    pub(super) fn apply_context_base(
286        &self,
287        context: &mut SslContextBuilder,
288        for_server: bool,
289    ) -> Result<()> {
290        context.set_verify(if self.verify_certificate {
291            SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT
292        } else {
293            SslVerifyMode::NONE
294        });
295        if let Some(identity) = self.identity() {
296            if let Some(cert) = &identity.cert {
297                context.set_certificate(cert).context(SetCertificateSnafu)?;
298            }
299            if let Some(pkey) = &identity.pkey {
300                context.set_private_key(pkey).context(SetPrivateKeySnafu)?;
301            }
302
303            if let Some(chain) = identity.ca {
304                for cert in chain {
305                    context
306                        .add_extra_chain_cert(cert)
307                        .context(AddExtraChainCertSnafu)?;
308                }
309            }
310        }
311        if self.authorities.is_empty() {
312            debug!("Fetching system root certs.");
313
314            #[cfg(windows)]
315            load_windows_certs(context).unwrap();
316
317            #[cfg(target_os = "macos")]
318            load_mac_certs(context).unwrap();
319        } else {
320            let mut store = X509StoreBuilder::new().context(NewStoreBuilderSnafu)?;
321            for authority in &self.authorities {
322                store
323                    .add_cert(authority.clone())
324                    .context(AddCertToStoreSnafu)?;
325            }
326            context
327                .set_verify_cert_store(store.build())
328                .context(SetVerifyCertSnafu)?;
329        }
330
331        if let Some(alpn) = &self.alpn_protocols {
332            if for_server {
333                let server_proto = alpn.clone();
334                // See https://github.com/sfackler/rust-openssl/pull/2360.
335                let server_proto_ref: &'static [u8] = Box::leak(server_proto.into_boxed_slice());
336                context.set_alpn_select_callback(move |_, client_proto| {
337                    select_next_proto(server_proto_ref, client_proto).ok_or(AlpnError::NOACK)
338                });
339            } else {
340                context
341                    .set_alpn_protos(alpn.as_slice())
342                    .context(SetAlpnProtocolsSnafu)?;
343            }
344        }
345
346        Ok(())
347    }
348
349    pub fn apply_connect_configuration(
350        &self,
351        connection: &mut ConnectConfiguration,
352    ) -> std::result::Result<(), openssl::error::ErrorStack> {
353        connection.set_verify_hostname(self.verify_hostname);
354        if let Some(server_name) = &self.server_name {
355            // Prevent native TLS lib from inferring default SNI using domain name from url.
356            connection.set_use_server_name_indication(false);
357            connection.set_hostname(server_name)?;
358        }
359        Ok(())
360    }
361}
362
363impl TlsConfig {
364    fn load_authorities(&self) -> Result<Vec<X509>> {
365        match &self.ca_file {
366            None => Ok(vec![]),
367            Some(filename) => {
368                let (data, filename) = open_read(filename, "certificate")?;
369                der_or_pem(
370                    data,
371                    |der| X509::from_der(&der).map(|x509| vec![x509]),
372                    |pem| {
373                        pem.match_indices(PEM_START_MARKER)
374                            .map(|(start, _)| X509::from_pem(&pem.as_bytes()[start..]))
375                            .collect()
376                    },
377                )
378                .with_context(|_| X509ParseSnafu { filename })
379            }
380        }
381    }
382
383    fn load_identity(&self) -> Result<Option<IdentityStore>> {
384        match (&self.crt_file, &self.key_file) {
385            (None, Some(_)) => Err(TlsError::MissingCrtKeyFile),
386            (None, None) => Ok(None),
387            (Some(filename), _) => {
388                let (data, filename) = open_read(filename, "certificate")?;
389                der_or_pem(
390                    data,
391                    |der| self.parse_pkcs12_identity(der),
392                    |pem| self.parse_pem_identity(&pem, &filename),
393                )
394            }
395        }
396    }
397
398    /// The input must be in ALPN "wire format".
399    ///
400    /// It consists of a sequence of supported protocol names prefixed by their byte length.
401    fn parse_alpn_protocols(&self) -> Result<Option<Vec<u8>>> {
402        match &self.alpn_protocols {
403            None => Ok(None),
404            Some(protocols) => {
405                let mut data: Vec<u8> = Vec::new();
406                for str in protocols {
407                    data.push(str.len().try_into().context(EncodeAlpnProtocolsSnafu)?);
408                    data.append(&mut str.clone().into_bytes());
409                }
410                Ok(Some(data))
411            }
412        }
413    }
414
415    /// Parse identity from a PEM encoded certificate + key pair of files
416    fn parse_pem_identity(&self, pem: &str, crt_file: &Path) -> Result<Option<IdentityStore>> {
417        match &self.key_file {
418            None => Err(TlsError::MissingKey),
419            Some(key_file) => {
420                let name = crt_file.to_string_lossy().to_string();
421                let mut crt_stack = X509::stack_from_pem(pem.as_bytes())
422                    .with_context(|_| X509ParseSnafu { filename: crt_file })?
423                    .into_iter();
424
425                let crt = crt_stack.next().ok_or(TlsError::MissingCertificate)?;
426                let key = load_key(key_file.as_path(), self.key_pass.as_ref())?;
427
428                let mut ca_stack = Stack::new().context(NewCaStackSnafu)?;
429                for intermediate in crt_stack {
430                    ca_stack.push(intermediate).context(CaStackPushSnafu)?;
431                }
432
433                let pkcs12 = Pkcs12::builder()
434                    .ca(ca_stack)
435                    .name(&name)
436                    .pkey(&key)
437                    .cert(&crt)
438                    .build2("")
439                    .context(Pkcs12Snafu)?;
440                let identity = pkcs12.to_der().context(DerExportSnafu)?;
441
442                // Build the resulting parsed PKCS#12 archive,
443                // but don't store it, as it cannot be cloned.
444                // This is just for error checking.
445                pkcs12.parse2("").context(TlsIdentitySnafu)?;
446
447                Ok(Some(IdentityStore(identity, String::new())))
448            }
449        }
450    }
451
452    /// Parse identity from a DER encoded PKCS#12 archive
453    fn parse_pkcs12_identity(&self, der: Vec<u8>) -> Result<Option<IdentityStore>> {
454        let pkcs12 = Pkcs12::from_der(&der).context(ParsePkcs12Snafu)?;
455        // Verify password
456        let key_pass = self.key_pass.as_deref().unwrap_or("");
457        pkcs12.parse2(key_pass).context(ParsePkcs12Snafu)?;
458        Ok(Some(IdentityStore(der, key_pass.to_string())))
459    }
460}
461
462/// === System Specific Root Cert ===
463///
464/// Most of this code is borrowed from https://github.com/ctz/rustls-native-certs
465
466/// Load the system default certs from `schannel` this should be in place
467/// of openssl-probe on linux.
468#[cfg(windows)]
469fn load_windows_certs(builder: &mut SslContextBuilder) -> Result<()> {
470    use super::SchannelSnafu;
471
472    let mut store = X509StoreBuilder::new().context(NewStoreBuilderSnafu)?;
473
474    let current_user_store =
475        schannel::cert_store::CertStore::open_current_user("ROOT").context(SchannelSnafu)?;
476
477    for cert in current_user_store.certs() {
478        let cert = cert.to_der().to_vec();
479        let cert = X509::from_der(&cert[..]).context(super::X509SystemParseSnafu)?;
480        store.add_cert(cert).context(AddCertToStoreSnafu)?;
481    }
482
483    builder
484        .set_verify_cert_store(store.build())
485        .context(SetVerifyCertSnafu)?;
486
487    Ok(())
488}
489
490#[cfg(target_os = "macos")]
491fn load_mac_certs(builder: &mut SslContextBuilder) -> Result<()> {
492    use std::collections::HashMap;
493
494    use security_framework::trust_settings::{Domain, TrustSettings, TrustSettingsForCertificate};
495
496    use super::SecurityFrameworkSnafu;
497
498    // The various domains are designed to interact like this:
499    //
500    // "Per-user Trust Settings override locally administered
501    //  Trust Settings, which in turn override the System Trust
502    //  Settings."
503    //
504    // So we collect the certificates in this order; as a map of
505    // their DER encoding to what we'll do with them.  We don't
506    // overwrite existing elements, which mean User settings
507    // trump Admin trump System, as desired.
508
509    let mut store = X509StoreBuilder::new().context(NewStoreBuilderSnafu)?;
510    let mut all_certs = HashMap::new();
511
512    for domain in &[Domain::User, Domain::Admin, Domain::System] {
513        let ts = TrustSettings::new(*domain);
514
515        for cert in ts.iter().context(SecurityFrameworkSnafu)? {
516            // If there are no specific trust settings, the default
517            // is to trust the certificate as a root cert.  Weird API but OK.
518            // The docs say:
519            //
520            // "Note that an empty Trust Settings array means "always trust this cert,
521            //  with a resulting kSecTrustSettingsResult of kSecTrustSettingsResultTrustRoot".
522            let trusted = ts
523                .tls_trust_settings_for_certificate(&cert)
524                .context(SecurityFrameworkSnafu)?
525                .unwrap_or(TrustSettingsForCertificate::TrustRoot);
526
527            all_certs.entry(cert.to_der()).or_insert(trusted);
528        }
529    }
530
531    for (cert, trusted) in all_certs {
532        if matches!(
533            trusted,
534            TrustSettingsForCertificate::TrustRoot | TrustSettingsForCertificate::TrustAsRoot
535        ) {
536            let cert = X509::from_der(&cert[..]).context(super::X509SystemParseSnafu)?;
537            store.add_cert(cert).context(AddCertToStoreSnafu)?;
538        }
539    }
540
541    builder
542        .set_verify_cert_store(store.build())
543        .context(SetVerifyCertSnafu)?;
544
545    Ok(())
546}
547
548impl fmt::Debug for TlsSettings {
549    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
550        f.debug_struct("TlsSettings")
551            .field("verify_certificate", &self.verify_certificate)
552            .field("verify_hostname", &self.verify_hostname)
553            .finish_non_exhaustive()
554    }
555}
556
557pub type MaybeTlsSettings = MaybeTls<(), TlsSettings>;
558
559impl MaybeTlsSettings {
560    pub fn enable_client() -> Result<Self> {
561        let tls = TlsSettings::from_options_base(None, false)?;
562        Ok(Self::Tls(tls))
563    }
564
565    pub fn tls_client(config: Option<&TlsConfig>) -> Result<Self> {
566        Ok(Self::Tls(TlsSettings::from_options_base(config, false)?))
567    }
568
569    /// Generate an optional settings struct from the given optional
570    /// configuration reference. If `config` is `None`, TLS is
571    /// disabled. The `for_server` parameter indicates the options
572    /// should be interpreted as being for a TLS server, which requires
573    /// an identity certificate and changes the certificate verification
574    /// default to false.
575    pub fn from_config(config: Option<&TlsEnableableConfig>, for_server: bool) -> Result<Self> {
576        match config {
577            None => Ok(Self::Raw(())), // No config, no TLS settings
578            Some(config) => {
579                if config.enabled.unwrap_or(false) {
580                    let tls = TlsSettings::from_options_base(Some(&config.options), for_server)?;
581                    match (for_server, &tls.identity) {
582                        // Servers require an identity certificate
583                        (true, None) => Err(TlsError::MissingRequiredIdentity),
584                        _ => Ok(Self::Tls(tls)),
585                    }
586                } else {
587                    Ok(Self::Raw(())) // Explicitly disabled, still no TLS settings
588                }
589            }
590        }
591    }
592
593    pub const fn http_protocol_name(&self) -> &'static str {
594        match self {
595            MaybeTls::Raw(()) => "http",
596            MaybeTls::Tls(_) => "https",
597        }
598    }
599}
600
601impl From<TlsSettings> for MaybeTlsSettings {
602    fn from(tls: TlsSettings) -> Self {
603        Self::Tls(tls)
604    }
605}
606
607/// Load a private key from a named file
608fn load_key(filename: &Path, pass_phrase: Option<&String>) -> Result<PKey<Private>> {
609    let (data, filename) = open_read(filename, "key")?;
610    match pass_phrase {
611        None => der_or_pem(
612            data,
613            |der| PKey::private_key_from_der(&der),
614            |pem| PKey::private_key_from_pem(pem.as_bytes()),
615        )
616        .with_context(|_| PrivateKeyParseSnafu { filename }),
617        Some(phrase) => der_or_pem(
618            data,
619            |der| PKey::private_key_from_pkcs8_passphrase(&der, phrase.as_bytes()),
620            |pem| PKey::private_key_from_pem_passphrase(pem.as_bytes(), phrase.as_bytes()),
621        )
622        .with_context(|_| PrivateKeyParseSnafu { filename }),
623    }
624}
625
626/// Parse the data one way if it looks like a DER file, and the other if
627/// it looks like a PEM file. For the content to be treated as PEM, it
628/// must parse as valid UTF-8 and contain a PEM start marker.
629fn der_or_pem<T>(data: Vec<u8>, der_fn: impl Fn(Vec<u8>) -> T, pem_fn: impl Fn(String) -> T) -> T {
630    // None of these steps cause (re)allocations,
631    // just parsing and type manipulation
632    match String::from_utf8(data) {
633        Ok(text) => match text.find(PEM_START_MARKER) {
634            Some(_) => pem_fn(text),
635            None => der_fn(text.into_bytes()),
636        },
637        Err(err) => der_fn(err.into_bytes()),
638    }
639}
640
641/// Open the named file and read its entire contents into memory. If the
642/// file "name" contains a PEM start marker, it is assumed to contain
643/// inline data and is used directly instead of opening a file.
644fn open_read(filename: &Path, note: &'static str) -> Result<(Vec<u8>, PathBuf)> {
645    if let Some(filename) = filename.to_str() {
646        if filename.contains(PEM_START_MARKER) {
647            return Ok((Vec::from(filename), "inline text".into()));
648        }
649    }
650
651    let mut text = Vec::<u8>::new();
652
653    File::open(filename)
654        .with_context(|_| FileOpenFailedSnafu { note, filename })?
655        .read_to_end(&mut text)
656        .with_context(|_| FileReadFailedSnafu { note, filename })?;
657
658    Ok((text, filename.into()))
659}
660
661#[cfg(test)]
662mod test {
663    use super::*;
664
665    const TEST_PKCS12_PATH: &str = "tests/data/ca/intermediate_client/private/localhost.p12";
666    const TEST_PEM_CRT_BYTES: &[u8] =
667        include_bytes!("../../../../tests/data/ca/intermediate_server/certs/localhost.cert.pem");
668    const TEST_PEM_KEY_BYTES: &[u8] =
669        include_bytes!("../../../../tests/data/ca/intermediate_server/private/localhost.key.pem");
670
671    #[test]
672    fn parse_alpn_protocols() {
673        let options = TlsConfig {
674            alpn_protocols: Some(vec![String::from("h2")]),
675            ..Default::default()
676        };
677        let settings =
678            TlsSettings::from_options(Some(&options)).expect("Failed to parse alpn_protocols");
679        assert_eq!(settings.alpn_protocols, Some(vec![2, 104, 50]));
680    }
681
682    #[test]
683    fn from_options_pkcs12() {
684        let _provider = openssl::provider::Provider::try_load(None, "legacy", true).unwrap();
685        let options = TlsConfig {
686            crt_file: Some(TEST_PKCS12_PATH.into()),
687            key_pass: Some("NOPASS".into()),
688            ..Default::default()
689        };
690        let settings =
691            TlsSettings::from_options(Some(&options)).expect("Failed to load PKCS#12 certificate");
692        assert!(settings.identity.is_some());
693        assert_eq!(settings.authorities.len(), 0);
694    }
695
696    #[test]
697    fn from_options_pem() {
698        let options = TlsConfig {
699            crt_file: Some(TEST_PEM_CRT_PATH.into()),
700            key_file: Some(TEST_PEM_KEY_PATH.into()),
701            ..Default::default()
702        };
703        let settings =
704            TlsSettings::from_options(Some(&options)).expect("Failed to load PEM certificate");
705        assert!(settings.identity.is_some());
706        assert_eq!(settings.authorities.len(), 0);
707    }
708
709    #[test]
710    fn from_options_inline_pem() {
711        let crt = String::from_utf8(TEST_PEM_CRT_BYTES.to_vec()).unwrap();
712        let key = String::from_utf8(TEST_PEM_KEY_BYTES.to_vec()).unwrap();
713        let options = TlsConfig {
714            crt_file: Some(crt.into()),
715            key_file: Some(key.into()),
716            ..Default::default()
717        };
718        let settings =
719            TlsSettings::from_options(Some(&options)).expect("Failed to load PEM certificate");
720        assert!(settings.identity.is_some());
721        assert_eq!(settings.authorities.len(), 0);
722    }
723
724    #[test]
725    fn from_options_ca() {
726        let options = TlsConfig {
727            ca_file: Some(TEST_PEM_CA_PATH.into()),
728            ..Default::default()
729        };
730        let settings = TlsSettings::from_options(Some(&options))
731            .expect("Failed to load authority certificate");
732        assert!(settings.identity.is_none());
733        assert_eq!(settings.authorities.len(), 1);
734    }
735
736    #[test]
737    fn from_options_inline_ca() {
738        let ca = String::from_utf8(
739            include_bytes!("../../../../tests/data/ca/certs/ca.cert.pem").to_vec(),
740        )
741        .unwrap();
742        let options = TlsConfig {
743            ca_file: Some(ca.into()),
744            ..Default::default()
745        };
746        let settings = TlsSettings::from_options(Some(&options))
747            .expect("Failed to load authority certificate");
748        assert!(settings.identity.is_none());
749        assert_eq!(settings.authorities.len(), 1);
750    }
751
752    #[test]
753    fn from_options_intermediate_ca() {
754        let options = TlsConfig {
755            ca_file: Some("tests/data/ca/intermediate_server/certs/ca-chain.cert.pem".into()),
756            ..Default::default()
757        };
758        let settings = TlsSettings::from_options(Some(&options))
759            .expect("Failed to load authority certificate");
760        assert!(settings.identity.is_none());
761        assert_eq!(settings.authorities.len(), 2);
762    }
763
764    #[test]
765    fn from_options_multi_ca() {
766        let options = TlsConfig {
767            ca_file: Some("tests/data/Multi_CA.crt".into()),
768            ..Default::default()
769        };
770        let settings = TlsSettings::from_options(Some(&options))
771            .expect("Failed to load authority certificate");
772        assert!(settings.identity.is_none());
773        assert_eq!(settings.authorities.len(), 2);
774    }
775
776    #[test]
777    fn from_options_none() {
778        let settings = TlsSettings::from_options(None).expect("Failed to generate null settings");
779        assert!(settings.identity.is_none());
780        assert_eq!(settings.authorities.len(), 0);
781    }
782
783    #[test]
784    fn from_options_bad_certificate() {
785        let options = TlsConfig {
786            key_file: Some(TEST_PEM_KEY_PATH.into()),
787            ..Default::default()
788        };
789        let error = TlsSettings::from_options(Some(&options))
790            .expect_err("from_options failed to check certificate");
791        assert!(matches!(error, TlsError::MissingCrtKeyFile));
792
793        let options = TlsConfig {
794            crt_file: Some(TEST_PEM_CRT_PATH.into()),
795            ..Default::default()
796        };
797        let _error = TlsSettings::from_options(Some(&options))
798            .expect_err("from_options failed to check certificate");
799        // Actual error is an ASN parse, doesn't really matter
800    }
801
802    #[test]
803    fn from_config_none() {
804        assert!(MaybeTlsSettings::from_config(None, true).unwrap().is_raw());
805        assert!(MaybeTlsSettings::from_config(None, false).unwrap().is_raw());
806    }
807
808    #[test]
809    fn from_config_not_enabled() {
810        assert!(settings_from_config(None, false, false, true).is_raw());
811        assert!(settings_from_config(None, false, false, false).is_raw());
812        assert!(settings_from_config(Some(false), false, false, true).is_raw());
813        assert!(settings_from_config(Some(false), false, false, false).is_raw());
814    }
815
816    #[test]
817    fn from_config_fails_without_certificate() {
818        let config = make_config(Some(true), false, false);
819        let error = MaybeTlsSettings::from_config(Some(&config), true)
820            .expect_err("from_config failed to check for a certificate");
821        assert!(matches!(error, TlsError::MissingRequiredIdentity));
822    }
823
824    #[test]
825    fn from_config_with_certificate() {
826        let config = settings_from_config(Some(true), true, true, true);
827        assert!(config.is_tls());
828    }
829
830    fn settings_from_config(
831        enabled: Option<bool>,
832        set_crt: bool,
833        set_key: bool,
834        for_server: bool,
835    ) -> MaybeTlsSettings {
836        let config = make_config(enabled, set_crt, set_key);
837        MaybeTlsSettings::from_config(Some(&config), for_server)
838            .expect("Failed to generate settings from config")
839    }
840
841    fn make_config(enabled: Option<bool>, set_crt: bool, set_key: bool) -> TlsEnableableConfig {
842        TlsEnableableConfig {
843            enabled,
844            options: TlsConfig {
845                crt_file: set_crt.then(|| TEST_PEM_CRT_PATH.into()),
846                key_file: set_key.then(|| TEST_PEM_KEY_PATH.into()),
847                ..Default::default()
848            },
849        }
850    }
851}