sqlx_core_oldapi/postgres/connection/
tls.rs

1use bytes::Bytes;
2
3use crate::error::Error;
4use crate::net::TlsConfig;
5use crate::postgres::connection::stream::PgStream;
6use crate::postgres::message::SslRequest;
7use crate::postgres::{PgConnectOptions, PgSslMode};
8
9pub(super) async fn maybe_upgrade(
10    stream: &mut PgStream,
11    options: &PgConnectOptions,
12) -> Result<(), Error> {
13    // https://www.postgresql.org/docs/12/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS
14    match options.ssl_mode {
15        // FIXME: Implement ALLOW
16        PgSslMode::Allow | PgSslMode::Disable => {}
17
18        PgSslMode::Prefer => {
19            // try upgrade, but its okay if we fail
20            upgrade(stream, options).await?;
21        }
22
23        PgSslMode::Require | PgSslMode::VerifyFull | PgSslMode::VerifyCa => {
24            if !upgrade(stream, options).await? {
25                // upgrade failed, die
26                return Err(Error::Tls("server does not support TLS".into()));
27            }
28        }
29    }
30
31    Ok(())
32}
33
34async fn upgrade(stream: &mut PgStream, options: &PgConnectOptions) -> Result<bool, Error> {
35    // https://www.postgresql.org/docs/current/protocol-flow.html#id-1.10.5.7.11
36
37    // To initiate an SSL-encrypted connection, the frontend initially sends an
38    // SSLRequest message rather than a StartupMessage
39
40    stream.send(SslRequest).await?;
41
42    // The server then responds with a single byte containing S or N, indicating that
43    // it is willing or unwilling to perform SSL, respectively.
44
45    match stream.read::<Bytes>(1).await?[0] {
46        b'S' => {
47            // The server is ready and willing to accept an SSL connection
48        }
49
50        b'N' => {
51            // The server is _unwilling_ to perform SSL
52            return Ok(false);
53        }
54
55        other => {
56            return Err(err_protocol!(
57                "unexpected response from SSLRequest: 0x{:02x}",
58                other
59            ));
60        }
61    }
62
63    let accept_invalid_certs = !matches!(
64        options.ssl_mode,
65        PgSslMode::VerifyCa | PgSslMode::VerifyFull
66    );
67    let accept_invalid_hostnames = !matches!(options.ssl_mode, PgSslMode::VerifyFull);
68
69    let tls_config: TlsConfig<'_> = TlsConfig {
70        accept_invalid_certs,
71        accept_invalid_hostnames,
72        root_cert_path: options.ssl_root_cert.as_ref(),
73        hostname: &options.host,
74        client_cert_path: options.ssl_client_cert.as_ref(),
75        client_key_path: options.ssl_client_key.as_ref(),
76    };
77    stream.upgrade(tls_config).await?;
78
79    Ok(true)
80}