Line data Source code
1 : pub mod client_config;
2 : pub mod postgres_rustls;
3 : pub mod server_config;
4 :
5 : use anyhow::Context;
6 : use base64::Engine as _;
7 : use base64::prelude::BASE64_STANDARD;
8 : use rustls::pki_types::CertificateDer;
9 : use sha2::{Digest, Sha256};
10 : use tracing::{error, info};
11 : use x509_cert::der::{Reader, SliceReader, oid};
12 :
13 : /// <https://github.com/postgres/postgres/blob/ca481d3c9ab7bf69ff0c8d71ad3951d407f6a33c/src/include/libpq/pqcomm.h#L159>
14 : pub const PG_ALPN_PROTOCOL: &[u8] = b"postgresql";
15 :
16 : /// Channel binding parameter
17 : ///
18 : /// <https://www.rfc-editor.org/rfc/rfc5929#section-4>
19 : /// Description: The hash of the TLS server's certificate as it
20 : /// appears, octet for octet, in the server's Certificate message. Note
21 : /// that the Certificate message contains a certificate_list, in which
22 : /// the first element is the server's certificate.
23 : ///
24 : /// The hash function is to be selected as follows:
25 : ///
26 : /// * if the certificate's signatureAlgorithm uses a single hash
27 : /// function, and that hash function is either MD5 or SHA-1, then use SHA-256;
28 : ///
29 : /// * if the certificate's signatureAlgorithm uses a single hash
30 : /// function and that hash function neither MD5 nor SHA-1, then use
31 : /// the hash function associated with the certificate's
32 : /// signatureAlgorithm;
33 : ///
34 : /// * if the certificate's signatureAlgorithm uses no hash functions or
35 : /// uses multiple hash functions, then this channel binding type's
36 : /// channel bindings are undefined at this time (updates to is channel
37 : /// binding type may occur to address this issue if it ever arises).
38 : #[derive(Debug, Clone, Copy)]
39 : pub enum TlsServerEndPoint {
40 : Sha256([u8; 32]),
41 : Undefined,
42 : }
43 :
44 : impl TlsServerEndPoint {
45 33 : pub fn new(cert: &CertificateDer<'_>) -> anyhow::Result<Self> {
46 : const SHA256_OIDS: &[oid::ObjectIdentifier] = &[
47 : // I'm explicitly not adding MD5 or SHA1 here... They're bad.
48 : oid::db::rfc5912::ECDSA_WITH_SHA_256,
49 : oid::db::rfc5912::SHA_256_WITH_RSA_ENCRYPTION,
50 : ];
51 :
52 33 : let certificate = SliceReader::new(cert)
53 33 : .context("Failed to parse cerficiate")?
54 33 : .decode::<x509_cert::Certificate>()
55 33 : .context("Failed to parse cerficiate")?;
56 :
57 33 : let subject = certificate.tbs_certificate.subject;
58 33 : info!(%subject, "parsing TLS certificate");
59 :
60 33 : let oid = certificate.signature_algorithm.oid;
61 33 : if SHA256_OIDS.contains(&oid) {
62 33 : let tls_server_end_point: [u8; 32] = Sha256::new().chain_update(cert).finalize().into();
63 33 : info!(%subject, tls_server_end_point = %BASE64_STANDARD.encode(tls_server_end_point), "determined channel binding");
64 33 : Ok(Self::Sha256(tls_server_end_point))
65 : } else {
66 0 : error!(%subject, "unknown channel binding");
67 0 : Ok(Self::Undefined)
68 : }
69 33 : }
70 :
71 14 : pub fn supported(&self) -> bool {
72 14 : !matches!(self, TlsServerEndPoint::Undefined)
73 14 : }
74 : }
|