1use crate::url::TlsPolicy;
4use pgsync_core::config::SafetyConfig;
5use pgsync_core::{Error, Result};
6use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
7use std::sync::Arc;
8
9pub fn client_config(
10 policy: TlsPolicy,
11 safety: &SafetyConfig,
12 url_ca_file: Option<&str>,
13) -> Result<Arc<rustls::ClientConfig>> {
14 let provider = Arc::new(rustls::crypto::ring::default_provider());
15 let builder = rustls::ClientConfig::builder_with_provider(provider.clone())
16 .with_safe_default_protocol_versions()
17 .map_err(|e| Error::config(format!("tls: {e}")))?;
18
19 let config = if policy.verify() {
20 let mut roots = rustls::RootCertStore::empty();
21 let native = rustls_native_certs::load_native_certs();
22 for cert in native.certs {
23 let _ = roots.add(cert);
24 }
25 for path in safety
26 .tls_ca_file
27 .iter()
28 .map(String::as_str)
29 .chain(url_ca_file)
30 {
31 let pem = std::fs::read(path)
32 .map_err(|e| Error::config(format!("cannot read CA file {path}: {e}")))?;
33 let mut added = 0;
34 for cert in rustls_pemfile::certs(&mut pem.as_slice()) {
35 let cert =
36 cert.map_err(|e| Error::config(format!("invalid certificate in {path}: {e}")))?;
37 roots
38 .add(cert)
39 .map_err(|e| Error::config(format!("invalid certificate in {path}: {e}")))?;
40 added += 1;
41 }
42 if added == 0 {
43 return Err(Error::config(format!(
44 "no certificates found in CA file {path}"
45 )));
46 }
47 }
48 if roots.is_empty() {
49 return Err(Error::config(
50 "no trusted root certificates available; set safety.tls_ca_file",
51 ));
52 }
53 builder.with_root_certificates(roots).with_no_client_auth()
54 } else {
55 builder
56 .dangerous()
57 .with_custom_certificate_verifier(Arc::new(NoVerify(provider)))
58 .with_no_client_auth()
59 };
60 Ok(Arc::new(config))
61}
62
63#[derive(Debug)]
66struct NoVerify(Arc<rustls::crypto::CryptoProvider>);
67
68impl rustls::client::danger::ServerCertVerifier for NoVerify {
69 fn verify_server_cert(
70 &self,
71 _end_entity: &CertificateDer<'_>,
72 _intermediates: &[CertificateDer<'_>],
73 _server_name: &ServerName<'_>,
74 _ocsp_response: &[u8],
75 _now: UnixTime,
76 ) -> std::result::Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
77 Ok(rustls::client::danger::ServerCertVerified::assertion())
78 }
79
80 fn verify_tls12_signature(
81 &self,
82 message: &[u8],
83 cert: &CertificateDer<'_>,
84 dss: &rustls::DigitallySignedStruct,
85 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
86 rustls::crypto::verify_tls12_signature(
87 message,
88 cert,
89 dss,
90 &self.0.signature_verification_algorithms,
91 )
92 }
93
94 fn verify_tls13_signature(
95 &self,
96 message: &[u8],
97 cert: &CertificateDer<'_>,
98 dss: &rustls::DigitallySignedStruct,
99 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
100 rustls::crypto::verify_tls13_signature(
101 message,
102 cert,
103 dss,
104 &self.0.signature_verification_algorithms,
105 )
106 }
107
108 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
109 self.0.signature_verification_algorithms.supported_schemes()
110 }
111}
112
113pub fn server_name(host: &str) -> Result<ServerName<'static>> {
114 ServerName::try_from(host.to_owned())
115 .map_err(|e| Error::config(format!("invalid TLS server name {host:?}: {e}")))
116}