sqlx_core/postgres/connection/
tls.rs

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