From 638852a2ddcc4468c26f7f790aecc276a1c51ee1 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Thu, 9 Jan 2020 21:57:51 -0800 Subject: [PATCH 01/14] implement TLS support for Postgres --- .github/workflows/postgres.yml | 3 + Cargo.toml | 2 + sqlx-core/Cargo.toml | 2 + sqlx-core/src/error.rs | 32 +++++ sqlx-core/src/io/buf_stream.rs | 6 + sqlx-core/src/io/mod.rs | 3 + sqlx-core/src/io/tls.rs | 109 +++++++++++++++++ sqlx-core/src/postgres/connection.rs | 114 ++++++++++++++++-- sqlx-core/src/postgres/protocol/mod.rs | 4 + .../src/postgres/protocol/ssl_request.rs | 25 ++++ sqlx-core/src/url.rs | 11 ++ tests/postgres.rs | 2 + 12 files changed, 301 insertions(+), 12 deletions(-) create mode 100644 sqlx-core/src/io/tls.rs create mode 100644 sqlx-core/src/postgres/protocol/ssl_request.rs diff --git a/.github/workflows/postgres.yml b/.github/workflows/postgres.yml index 6eca7eb6..1e3dc038 100644 --- a/.github/workflows/postgres.yml +++ b/.github/workflows/postgres.yml @@ -49,6 +49,9 @@ jobs: # ----------------------------------------------------- + # Check that we build with TLS support (TODO: we need a postgres image with SSL certs to test) + - run: cargo build -p sqlx-core --no-default-features 'postgres macros uuid chrono tls' + - run: cargo test -p sqlx --no-default-features --features 'postgres macros uuid chrono' env: DATABASE_URL: postgres://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/postgres diff --git a/Cargo.toml b/Cargo.toml index bb7fa11f..541cde15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ all-features = true [features] default = [ "macros" ] macros = [ "sqlx-macros", "proc-macro-hack" ] +tls = ["sqlx-core/tls"] # database postgres = [ "sqlx-core/postgres", "sqlx-macros/postgres" ] @@ -48,6 +49,7 @@ hex = "0.4.0" [dev-dependencies] anyhow = "1.0.26" futures = "0.3.1" +env_logger = "0.7" async-std = { version = "1.4.0", features = [ "attributes" ] } dotenv = "0.15.0" diff --git a/sqlx-core/Cargo.toml b/sqlx-core/Cargo.toml index 2fff5f59..bb38bdf6 100644 --- a/sqlx-core/Cargo.toml +++ b/sqlx-core/Cargo.toml @@ -20,8 +20,10 @@ default = [] unstable = [] postgres = [ "md-5", "sha2", "base64", "sha-1", "rand", "hmac" ] mysql = [ "sha-1", "sha2", "generic-array", "num-bigint", "base64", "digest", "rand" ] +tls = ["async-native-tls"] [dependencies] +async-native-tls = { version = "0.3", optional = true } async-std = "1.4.0" async-stream = { version = "0.2.0", default-features = false } base64 = { version = "0.11.0", default-features = false, optional = true, features = [ "std" ] } diff --git a/sqlx-core/src/error.rs b/sqlx-core/src/error.rs index 2c16ec0a..960023d5 100644 --- a/sqlx-core/src/error.rs +++ b/sqlx-core/src/error.rs @@ -44,6 +44,9 @@ pub enum Error { /// [Pool::close] was called while we were waiting in [Pool::acquire]. PoolClosed, + /// An error occurred during a TLS upgrade. + TlsUpgrade(Box), + Decode(DecodeError), // TODO: Remove and replace with `#[non_exhaustive]` when possible @@ -62,6 +65,8 @@ impl StdError for Error { Error::Decode(DecodeError::Other(error)) => Some(&**error), + Error::TlsUpgrade(error) => Some(&**error), + _ => None, } } @@ -100,6 +105,8 @@ impl Display for Error { Error::PoolClosed => f.write_str("attempted to acquire a connection on a closed pool"), + Error::TlsUpgrade(ref err) => write!(f, "error during TLS upgrade: {}", err), + Error::__Nonexhaustive => unreachable!(), } } @@ -140,6 +147,21 @@ impl From> for Error { } } +#[cfg(feature = "tls")] +impl From for Error { + #[inline] + fn from(err: async_native_tls::Error) -> Self { + Error::TlsUpgrade(err.into()) + } +} + +impl From> for Error { + #[inline] + fn from(err: TlsError<'_>) -> Self { + Error::TlsUpgrade(err.args.to_string().into()) + } +} + impl From for Error where T: 'static + DatabaseError, @@ -189,6 +211,15 @@ macro_rules! protocol_err ( } ); +pub(crate) struct TlsError<'a> { + pub args: fmt::Arguments<'a>, +} + +#[allow(unused_macros)] +macro_rules! tls_err { + ($($args:tt)*) => { crate::error::TlsError { args: format_args!($($args)*)} }; +} + #[allow(unused_macros)] macro_rules! impl_fmt_error { ($err:ty) => { @@ -212,3 +243,4 @@ macro_rules! impl_fmt_error { } }; } + diff --git a/sqlx-core/src/io/buf_stream.rs b/sqlx-core/src/io/buf_stream.rs index 06e9f7d9..cb1e79c4 100644 --- a/sqlx-core/src/io/buf_stream.rs +++ b/sqlx-core/src/io/buf_stream.rs @@ -51,6 +51,12 @@ where Ok(()) } + pub fn clear_bufs(&mut self) { + self.rbuf_rindex = 0; + self.rbuf_windex = 0; + self.wbuf.clear(); + } + #[inline] pub fn consume(&mut self, cnt: usize) { self.rbuf_rindex += cnt; diff --git a/sqlx-core/src/io/mod.rs b/sqlx-core/src/io/mod.rs index 2995d857..b4c95430 100644 --- a/sqlx-core/src/io/mod.rs +++ b/sqlx-core/src/io/mod.rs @@ -5,11 +5,14 @@ mod buf; mod buf_mut; mod byte_str; +mod tls; + pub use self::{ buf::{Buf, ToBuf}, buf_mut::BufMut, buf_stream::BufStream, byte_str::ByteStr, + tls::MaybeTlsStream }; #[cfg(test)] diff --git a/sqlx-core/src/io/tls.rs b/sqlx-core/src/io/tls.rs new file mode 100644 index 00000000..19884564 --- /dev/null +++ b/sqlx-core/src/io/tls.rs @@ -0,0 +1,109 @@ +use std::io::{IoSlice, IoSliceMut}; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use async_std::io::{self, Read, Write}; +use async_std::net::{Shutdown, TcpStream}; + +use crate::url::Url; + +use self::Inner::*; + +pub struct MaybeTlsStream { + inner: Inner, +} + +enum Inner { + NotTls(TcpStream), + #[cfg(feature = "tls")] + Tls(async_native_tls::TlsStream), + #[cfg(feature = "tls")] + Upgrading, +} + +impl MaybeTlsStream { + pub async fn connect(url: &Url, default_port: u16) -> crate::Result { + let conn = TcpStream::connect((url.host(), url.port(default_port))).await?; + Ok(Self { inner: Inner::NotTls(conn) }) + } + + #[cfg(feature = "tls")] + pub async fn upgrade(&mut self, url: &Url, connector: async_native_tls::TlsConnector) -> crate::Result<()> { + let conn = match std::mem::replace(&mut self.inner, Upgrading) { + NotTls(conn) => conn, + Tls(_) => return Err(tls_err!("connection already upgraded").into()), + Upgrading => return Err(tls_err!("connection already failed to upgrade").into()), + }; + + self.inner = Tls(connector.connect(url.host(), conn).await?); + + Ok(()) + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + match self.inner { + NotTls(ref conn) => conn.shutdown(how), + #[cfg(feature = "tls")] + Tls(ref conn) => conn.get_ref().shutdown(how), + #[cfg(feature = "tls")] + // connection already closed + Upgrading => Ok(()), + } + } +} + +macro_rules! forward_pin ( + ($self:ident.$method:ident($($arg:ident),*)) => ( + match &mut $self.inner { + NotTls(ref mut conn) => Pin::new(conn).$method($($arg),*), + #[cfg(feature = "tls")] + Tls(ref mut conn) => Pin::new(conn).$method($($arg),*), + #[cfg(feature = "tls")] + Upgrading => Err(io::Error::new(io::ErrorKind::Other, "connection broken; TLS upgrade failed")).into(), + } + ) +); + +impl Read for MaybeTlsStream { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context, + buf: &mut [u8], + ) -> Poll> { + forward_pin!(self.poll_read(cx, buf)) + } + + fn poll_read_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context, + bufs: &mut [IoSliceMut], + ) -> Poll> { + forward_pin!(self.poll_read_vectored(cx, bufs)) + } +} + +impl Write for MaybeTlsStream { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context, + buf: &[u8], + ) -> Poll> { + forward_pin!(self.poll_write(cx, buf)) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + forward_pin!(self.poll_flush(cx)) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + forward_pin!(self.poll_close(cx)) + } + + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context, + bufs: &[IoSlice], + ) -> Poll> { + forward_pin!(self.poll_write_vectored(cx, bufs)) + } +} diff --git a/sqlx-core/src/postgres/connection.rs b/sqlx-core/src/postgres/connection.rs index dcb90e2b..d523cf6b 100644 --- a/sqlx-core/src/postgres/connection.rs +++ b/sqlx-core/src/postgres/connection.rs @@ -1,6 +1,8 @@ use std::convert::TryInto; +use std::path::Path; -use async_std::net::{Shutdown, TcpStream}; +use async_std::fs; +use async_std::net::Shutdown; use byteorder::NetworkEndian; use futures_core::future::BoxFuture; @@ -11,12 +13,15 @@ use crate::postgres::protocol::{ self, hi, Authentication, Decode, Encode, Message, SaslInitialResponse, SaslResponse, StatementId, }; +use crate::io::{Buf, BufStream, MaybeTlsStream}; use crate::postgres::PgError; +use crate::postgres::protocol::{self, Decode, Encode, Message, StatementId}; use crate::url::Url; use crate::Result; use hmac::{Hmac, Mac}; use rand::Rng; use sha2::{Digest, Sha256}; +use async_native_tls::Certificate; /// An asynchronous connection to a [Postgres] database. /// @@ -24,7 +29,7 @@ use sha2::{Digest, Sha256}; /// string, as documented at /// pub struct PgConnection { - pub(super) stream: BufStream, + pub(super) stream: BufStream, // Map of query to statement id pub(super) statement_cache: StatementCache, @@ -43,8 +48,43 @@ pub struct PgConnection { } impl PgConnection { + #[cfg(feature = "tls")] + async fn try_ssl(&mut self, url: &Url, invalid_certs: bool, invalid_hostnames: bool) -> crate::Result { + use async_native_tls::{TlsConnector, Certificate}; + use std::env; + + protocol::SslRequest::encode(self.stream.buffer_mut()); + + self.stream.flush().await?; + + match self.stream.peek(1).await? { + Some(b"N") => return Ok(false), + Some(b"S") => (), + Some(other) => return Err(tls_err!("unexpected single-byte response: 0x{:02X}", other[0]).into()), + None => return Err(tls_err!("server unexpectedly closed connection").into()) + } + + let mut connector = TlsConnector::new() + .danger_accept_invalid_certs(invalid_certs) + .danger_accept_invalid_hostnames(invalid_hostnames); + + if !invalid_certs { + match read_root_certificate(&url).await { + Ok(cert) => { + connector = connector.add_root_certificate(cert); + } + Err(e) => log::warn!("failed to read Postgres root certificate: {}", e) + } + } + + self.stream.clear_bufs(); + self.stream.stream.upgrade(url, connector).await?; + + Ok(true) + } + // https://www.postgresql.org/docs/12/protocol-flow.html#id-1.10.5.7.3 - async fn startup(&mut self, url: Url) -> Result<()> { + async fn startup(&mut self, url: &Url) -> Result<()> { // Defaults to postgres@.../postgres let username = url.username().unwrap_or("postgres"); let database = url.database().unwrap_or("postgres"); @@ -83,7 +123,7 @@ impl PgConnection { protocol::PasswordMessage::ClearText( url.password().unwrap_or_default(), ) - .encode(self.stream.buffer_mut()); + .encode(self.stream.buffer_mut()); self.stream.flush().await?; } @@ -94,7 +134,7 @@ impl PgConnection { user: username, salt, } - .encode(self.stream.buffer_mut()); + .encode(self.stream.buffer_mut()); self.stream.flush().await?; } @@ -137,7 +177,7 @@ impl PgConnection { "requires unimplemented authentication method: {:?}", auth ) - .into()); + .into()); } } } @@ -240,7 +280,8 @@ impl PgConnection { impl PgConnection { pub(super) async fn open(url: Result) -> Result { let url = url?; - let stream = TcpStream::connect((url.host(), url.port(5432))).await?; + + let stream = MaybeTlsStream::connect(&url, 5432).await?; let mut self_ = Self { stream: BufStream::new(stream), process_id: 0, @@ -251,7 +292,36 @@ impl PgConnection { ready: true, }; - self_.startup(url).await?; + let ssl_mode = url.get_param("sslmode").unwrap_or("prefer".into()); + + match &*ssl_mode { + // TODO: on "allow" retry with TLS if startup fails + "disable" | "allow" => (), + + #[cfg(feature = "tls")] + "prefer" => { self_.try_ssl(&url, true, true).await?; }, + + #[cfg(not(feature = "tls"))] + "prefer" => log::info!("compiled without TLS, skipping upgrade"), + + #[cfg(feature = "tls")] + "require" | "verify-ca" | "verify-full" => if !self_.try_ssl( + &url, + ssl_mode == "require", // false for both verify-ca and verify-full + ssl_mode != "verify-full" // false for only verify-full + ).await? { + return Err(tls_err!("Postgres server does not support TLS").into()) + } + + #[cfg(not(feature = "tls"))] + "require" | "verify-ca" | "verify-full" => return Err( + tls_err!("sslmode {:?} unsupported; SQLx was compiled without `tls` feature", + ssl_mode).into() + ), + _ => return Err(tls_err!("unknown `sslmode` value: {:?}", ssl_mode).into()), + } + + self_.startup(&url).await?; Ok(self_) } @@ -259,9 +329,9 @@ impl PgConnection { impl Connection for PgConnection { fn open(url: T) -> BoxFuture<'static, Result> - where - T: TryInto, - Self: Sized, + where + T: TryInto, + Self: Sized, { Box::pin(PgConnection::open(url.try_into())) } @@ -271,6 +341,26 @@ impl Connection for PgConnection { } } +#[cfg(feature = "tls")] +async fn read_root_certificate(url: &Url) -> crate::Result { + use std::env; + + let root_cert_path = if let Some(path) = url.get_param("sslrootcert") { + path.into() + } else if let Ok(cert_path) = env::var("PGSSLROOTCERT"){ + cert_path + } else if cfg!(windows) { + let appdata = env::var("APPDATA").map_err(|_| tls_err!("APPDATA not set"))?; + format!("{}\\postgresql\\root.crt", appdata) + } else { + let home = env::var("HOME").map_err(|_| tls_err!("HOME not set"))?; + format!("{}/.postgresql/root.crt", home) + }; + + let root_cert = async_std::fs::read(root_cert_path).await?; + Ok(async_native_tls::Certificate::from_pem(&root_cert)?) +} + static GS2_HEADER: &'static str = "n,,"; static CHANNEL_ATTR: &'static str = "c"; static USERNAME_ATTR: &'static str = "n"; @@ -354,7 +444,7 @@ async fn sasl_auth>(conn: &mut PgConnection, username: T, password ); // AuthMessage := client-first-message-bare + "," + server-first-message + "," + client-final-message-without-proof - let auth_message = format!("{client_first_message_bare},{server_first_message},{client_final_message_wo_proof}", + let auth_message = format!("{client_first_message_bare},{server_first_message},{client_final_message_wo_proof}", client_first_message_bare = client_first_message_bare, server_first_message = server_first_message, client_final_message_wo_proof = client_final_message_wo_proof); diff --git a/sqlx-core/src/postgres/protocol/mod.rs b/sqlx-core/src/postgres/protocol/mod.rs index 18446791..cd2fccc7 100644 --- a/sqlx-core/src/postgres/protocol/mod.rs +++ b/sqlx-core/src/postgres/protocol/mod.rs @@ -5,6 +5,7 @@ // the size of this module to exactly what is necessary. #![allow(unused)] +// REQUESTS mod bind; mod cancel_request; mod close; @@ -16,6 +17,7 @@ mod parse; mod password_message; mod query; mod sasl; +mod ssl_request; mod startup_message; mod statement; mod sync; @@ -32,11 +34,13 @@ pub use parse::Parse; pub use password_message::PasswordMessage; pub use query::Query; pub use sasl::{hi, SaslInitialResponse, SaslResponse}; +pub use ssl_request::SslRequest; pub use startup_message::StartupMessage; pub use statement::StatementId; pub use sync::Sync; pub use terminate::Terminate; +// RESPONSES mod authentication; mod backend_key_data; mod command_complete; diff --git a/sqlx-core/src/postgres/protocol/ssl_request.rs b/sqlx-core/src/postgres/protocol/ssl_request.rs new file mode 100644 index 00000000..4f7ae690 --- /dev/null +++ b/sqlx-core/src/postgres/protocol/ssl_request.rs @@ -0,0 +1,25 @@ +use crate::io::{Buf, BufMut}; +use byteorder::NetworkEndian; + +pub struct SslRequest; + +impl SslRequest { + pub fn encode(buf: &mut Vec) { + // packet length: 8 bytes including self + buf.put_u32::(8); + // 1234 in high 16 bits, 5679 in low 16 + buf.put_u32::( + (1234 << 16) | 5679, + ); + } +} + +#[test] +fn test_ssl_request() { + use crate::io::Buf; + + let mut buf = Vec::new(); + SslRequest::encode(&mut buf); + + assert_eq!((&buf[..]).get_u32::().unwrap(), 80877103); +} diff --git a/sqlx-core/src/url.rs b/sqlx-core/src/url.rs index d3d310c9..f7914dbb 100644 --- a/sqlx-core/src/url.rs +++ b/sqlx-core/src/url.rs @@ -1,4 +1,5 @@ use std::convert::{TryFrom, TryInto}; +use std::borrow::Cow; pub struct Url(url::Url); @@ -64,4 +65,14 @@ impl Url { Some(database) } } + + pub fn get_param(&self, key: &str) -> Option> { + self.0.query_pairs().find_map(|(key_, val)| { + if key == key_ { + Some(val) + } else { + None + } + }) + } } diff --git a/tests/postgres.rs b/tests/postgres.rs index 1bb9467c..68ee5340 100644 --- a/tests/postgres.rs +++ b/tests/postgres.rs @@ -66,5 +66,7 @@ async fn it_remains_stable_issue_30() -> anyhow::Result<()> { } async fn connect() -> anyhow::Result { + let _ = dotenv::dotenv(); + let _ = env_logger::try_init(); Ok(PgConnection::open(dotenv::var("DATABASE_URL")?).await?) } From ff219061b27ff7fc6c5dfb9f15719d4df46d4c01 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Fri, 10 Jan 2020 19:39:29 -0800 Subject: [PATCH 02/14] implement TLS for MySQL --- Cargo.lock | 181 ++++++++++++++++++++ sqlx-core/src/mysql/connection.rs | 94 ++++++++-- sqlx-core/src/mysql/protocol/mod.rs | 2 + sqlx-core/src/mysql/protocol/ssl_request.rs | 30 ++++ 4 files changed, 294 insertions(+), 13 deletions(-) create mode 100644 sqlx-core/src/mysql/protocol/ssl_request.rs diff --git a/Cargo.lock b/Cargo.lock index 4dccb1c3..b41d72a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,16 @@ dependencies = [ "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "async-native-tls" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "async-std" version = "1.4.0" @@ -247,6 +257,20 @@ name = "constant_time_eq" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "core-foundation" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "crossbeam-channel" version = "0.4.0" @@ -376,6 +400,19 @@ name = "fnv" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -831,6 +868,23 @@ dependencies = [ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "native-tls" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "net2" version = "0.2.33" @@ -887,6 +941,36 @@ name = "opaque-debug" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "openssl" +version = "0.10.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "openssl-sys" +version = "0.9.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot" version = "0.9.0" @@ -931,6 +1015,11 @@ name = "pin-utils" version = "0.1.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ppv-lite86" version = "0.2.6" @@ -1030,6 +1119,14 @@ name = "regex-syntax" version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "remove_dir_all" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ring" version = "0.14.6" @@ -1077,11 +1174,39 @@ name = "ryu" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "schannel" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "scopeguard" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "security-framework" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "semver" version = "0.9.0" @@ -1186,6 +1311,7 @@ dependencies = [ "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", "async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "dotenv 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1197,6 +1323,7 @@ dependencies = [ name = "sqlx-core" version = "0.1.4" dependencies = [ + "async-native-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "async-stream 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1278,6 +1405,19 @@ dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "termcolor" version = "1.0.5" @@ -1286,6 +1426,24 @@ dependencies = [ "wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thiserror" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thread_local" version = "1.0.0" @@ -1496,6 +1654,11 @@ name = "uuid" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "vcpkg" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "version_check" version = "0.1.5" @@ -1577,6 +1740,7 @@ dependencies = [ "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" "checksum async-attributes 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "efd3d156917d94862e779f356c5acae312b08fd3121e792c857d7928c8088423" +"checksum async-native-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d40a615e861c981117e15c28c577daf9918cabd2e2d588a5e06811ae79c9da1a" "checksum async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0bf6039b315300e057d198b9d3ab92ee029e31c759b7f1afae538145e6f18a3e" "checksum async-stream 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "58982858be7540a465c790b95aaea6710e5139bf8956b1d1344d014fa40100b0" "checksum async-stream-impl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393356ed99aa7bff0ac486dde592633b83ab02bd254d8c209d5b9f1d0f533480" @@ -1602,6 +1766,8 @@ dependencies = [ "checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" +"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" +"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" "checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" "checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" "checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" @@ -1618,6 +1784,8 @@ dependencies = [ "checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" @@ -1669,6 +1837,7 @@ dependencies = [ "checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum num-bigint 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f115de20ad793e857f76da2563ff4a09fbcfd6fe93cca0c5d996ab5f3ee38d" "checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" @@ -1676,12 +1845,16 @@ dependencies = [ "checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72" "checksum once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "891f486f630e5c5a4916c7e16c4b24a53e78c860b646e9f8e005e4f16847bfed" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3cc5799d98e1088141b8e01ff760112bbd9f19d850c124500566ca6901a585" +"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +"checksum openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)" = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f" "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum pin-project-lite 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e8822eb8bb72452f038ebf6048efa02c3fe22bf83f76519c9583e47fc194a422" "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" +"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" "checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" "checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" @@ -1695,13 +1868,17 @@ dependencies = [ "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87" "checksum regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90" +"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c" "checksum route-recognizer 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "ea509065eb0b3c446acdd0102f0d46567dc30902dc0be91d6552035d92b0f4f8" "checksum rust-argon2 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "416f5109bdd413cec4f04c029297838e7604c993f8d1483b1d438f23bdc3eb35" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +"checksum security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df" +"checksum security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e31493fc37615debb8c5090a7aeb4a9730bc61e77ab10b9af59f1a202284f895" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" @@ -1717,7 +1894,10 @@ dependencies = [ "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" "checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" "checksum syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8" +"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" +"checksum thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6f357d1814b33bc2dc221243f8424104bfe72dbe911d5b71b3816a2dff1c977e" +"checksum thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2e25d25307eb8436894f727aba8f65d07adf02e5b35a13cebed48bd282bfef" "checksum thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "88ddf1ad580c7e3d1efff877d972bcc93f995556b9087a5a259630985c88ceab" "checksum tide 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13c99b1991db81e611a2614cd1b07fec89ae33c5f755e1f8eb70826fb5af0eea" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" @@ -1739,6 +1919,7 @@ dependencies = [ "checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" "checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" "checksum uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" +"checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" diff --git a/sqlx-core/src/mysql/connection.rs b/sqlx-core/src/mysql/connection.rs index 9af8c9c2..e35cac09 100644 --- a/sqlx-core/src/mysql/connection.rs +++ b/sqlx-core/src/mysql/connection.rs @@ -8,15 +8,14 @@ use sha1::Sha1; use crate::cache::StatementCache; use crate::connection::Connection; -use crate::io::{Buf, BufMut, BufStream}; +use crate::io::{Buf, BufMut, BufStream, MaybeTlsStream}; use crate::mysql::error::MySqlError; -use crate::mysql::protocol::{ - AuthPlugin, AuthSwitch, Capabilities, Decode, Encode, EofPacket, ErrPacket, Handshake, - HandshakeResponse, OkPacket, -}; +use crate::mysql::protocol::{AuthPlugin, AuthSwitch, Capabilities, Decode, Encode, EofPacket, ErrPacket, Handshake, HandshakeResponse, OkPacket, SslRequest}; use crate::mysql::rsa; use crate::mysql::util::xor_eq; use crate::url::Url; +use std::borrow::Cow; +use std::path::Path; // Size before a packet is split const MAX_PACKET_SIZE: u32 = 1024; @@ -29,7 +28,7 @@ const COLLATE_UTF8MB4_UNICODE_CI: u8 = 224; /// string, as documented at /// pub struct MySqlConnection { - pub(super) stream: BufStream, + pub(super) stream: BufStream, // Active capabilities of the client _&_ the server pub(super) capabilities: Capabilities, @@ -197,9 +196,7 @@ impl MySqlConnection { } }) } -} -impl MySqlConnection { pub(crate) fn handle_ok(&mut self) -> crate::Result { let ok = OkPacket::decode(self.packet())?; @@ -301,8 +298,6 @@ impl MySqlConnection { ) -> crate::Result> { // https://mariadb.com/kb/en/caching_sha2_password-authentication-plugin/ - // TODO: Handle SSL - // client sends a public key request self.send(&[public_key_request_id][..]).await?; @@ -324,11 +319,17 @@ impl MySqlConnection { impl MySqlConnection { async fn new(url: &Url) -> crate::Result { - let stream = TcpStream::connect((url.host(), url.port(3306))).await?; + let stream = MaybeTlsStream::connect(url, 3306).await?; + + let mut capabilities = Capabilities::empty(); + + if cfg!(feature = "tls") { + capabilities |= Capabilities::SSL; + } Ok(Self { stream: BufStream::new(stream), - capabilities: Capabilities::empty(), + capabilities, packet: Vec::with_capacity(8192), packet_len: 0, next_seq_no: 0, @@ -372,6 +373,29 @@ impl MySqlConnection { Ok(()) } + + #[cfg(feature = "tls")] + async fn try_ssl(&mut self, url: &Url, ca_file: Option<&str>, invalid_hostnames: bool) -> crate::Result<()> { + use async_native_tls::{Certificate, TlsConnector}; + use async_std::fs; + + let mut connector = TlsConnector::new() + .danger_accept_invalid_certs(ca_file.is_none()) + .danger_accept_invalid_hostnames(invalid_hostnames); + + if let Some(ca_file) = ca_file { + let root_cert = fs::read(ca_file).await?; + connector = connector.add_root_certificate(Certificate::from_pem(&root_cert)?); + } + + // send upgrade request and then immediately try TLS handshake + self.send(SslRequest { + client_collation: COLLATE_UTF8MB4_UNICODE_CI, + max_packet_size: MAX_PACKET_SIZE + }).await?; + + self.stream.stream.upgrade(url, connector).await + } } impl MySqlConnection { @@ -383,7 +407,51 @@ impl MySqlConnection { // https://mariadb.com/kb/en/connection/ // On connect, server immediately sends the handshake - let handshake = self_.receive_handshake(&url).await?; + let mut handshake = self_.receive_handshake(&url).await?; + + let ca_file = url.get_param("ssl-ca"); + + let ssl_mode = url.get_param("ssl-mode") + .unwrap_or(if ca_file.is_some() { "VERIFY_CA" } else { "PREFERRED" }.into()); + + let supports_ssl = handshake.server_capabilities.contains(Capabilities::SSL); + + match &*ssl_mode { + "DISABLED" => (), + + // don't try upgrade + #[cfg(feature = "tls")] + "PREFERRED" if !supports_ssl => log::info!("server does not support TLS; using unencrypted connection"), + + // try to upgrade + #[cfg(feature = "tls")] + "PREFERRED" => if let Err(e) = self_.try_ssl(&url, None, true).await { + log::info!("server does not support TLS"); + // fallback, redo connection + self_ = Self::new(&url).await?; + handshake = self_.receive_handshake(&url).await?; + }, + + #[cfg(not(feature = "tls"))] + "PREFERRED" => log::info!("compiled without TLS, skipping upgrade"), + + #[cfg(feature = "tls")] + "REQUIRED" if !supports_ssl => return Err(tls_err!("server does not support TLS").into()), + + #[cfg(feature = "tls")] + "REQUIRED" => self_.try_ssl(&url, None, true).await?, + + #[cfg(feature = "tls")] + "VERIFY_CA" | "VERIFY_FULL" if ca_file.is_none() => + return Err(tls_err!("`ssl-mode` of {:?} requires `ssl-ca` to be set", ssl_mode).into()), + + #[cfg(feature = "tls")] + "VERIFY_CA" | "VERIFY_FULL" => self_.try_ssl(&url, ca_file.as_deref(), ssl_mode != "VERIFY_FULL").await?, + + #[cfg(not(feature = "tls"))] + "REQUIRED" | "VERIFY_CA" | "VERIFY_FULL" => return Err(tls_err!("compiled without TLS").into()), + _ => return Err(tls_err!("unknown `ssl-mode` value: {:?}", ssl_mode).into()), + } // Pre-generate an auth response by using the auth method in the [Handshake] let password = url.password().unwrap_or_default(); diff --git a/sqlx-core/src/mysql/protocol/mod.rs b/sqlx-core/src/mysql/protocol/mod.rs index dd974886..9da46ec6 100644 --- a/sqlx-core/src/mysql/protocol/mod.rs +++ b/sqlx-core/src/mysql/protocol/mod.rs @@ -39,6 +39,7 @@ mod com_stmt_prepare_ok; mod eof; mod err; mod handshake_response; +mod ssl_request; mod ok; mod row; @@ -50,4 +51,5 @@ pub use eof::EofPacket; pub use err::ErrPacket; pub use handshake_response::HandshakeResponse; pub use ok::OkPacket; +pub use ssl_request::SslRequest; pub use row::Row; diff --git a/sqlx-core/src/mysql/protocol/ssl_request.rs b/sqlx-core/src/mysql/protocol/ssl_request.rs new file mode 100644 index 00000000..088081ca --- /dev/null +++ b/sqlx-core/src/mysql/protocol/ssl_request.rs @@ -0,0 +1,30 @@ +use byteorder::LittleEndian; + +use crate::io::BufMut; +use crate::mysql::io::BufMutExt; +use crate::mysql::protocol::{AuthPlugin, Capabilities, Encode}; + +// https://dev.mysql.com/doc/dev/mysql-server/8.0.12/page_protocol_connection_phase_packets_protocol_handshake_response.html +// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest +#[derive(Debug)] +pub struct SslRequest { + pub max_packet_size: u32, + pub client_collation: u8, +} + +impl Encode for SslRequest { + fn encode(&self, buf: &mut Vec, capabilities: Capabilities) { + // client capabilities : int<4> + // SSL must be set or else it makes no sense to ask for an upgrade + buf.put_u32::((capabilities | Capabilities::SSL).bits() as u32); + + // max packet size : int<4> + buf.put_u32::(self.max_packet_size); + + // client character collation : int<1> + buf.put_u8(self.client_collation); + + // reserved : string<23> + buf.advance(23); + } +} From f04e4bdbeb6a35bd95eea2385c18951395379a7e Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Mon, 13 Jan 2020 14:43:36 -0800 Subject: [PATCH 03/14] document TLS options for Postgres --- sqlx-core/src/postgres/connection.rs | 71 ++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/sqlx-core/src/postgres/connection.rs b/sqlx-core/src/postgres/connection.rs index d523cf6b..689a3927 100644 --- a/sqlx-core/src/postgres/connection.rs +++ b/sqlx-core/src/postgres/connection.rs @@ -1,33 +1,72 @@ use std::convert::TryInto; use std::path::Path; +use async_native_tls::Certificate; use async_std::fs; use async_std::net::Shutdown; use byteorder::NetworkEndian; use futures_core::future::BoxFuture; - -use crate::cache::StatementCache; -use crate::connection::Connection; -use crate::io::{Buf, BufStream}; -use crate::postgres::protocol::{ - self, hi, Authentication, Decode, Encode, Message, SaslInitialResponse, SaslResponse, - StatementId, -}; -use crate::io::{Buf, BufStream, MaybeTlsStream}; -use crate::postgres::PgError; -use crate::postgres::protocol::{self, Decode, Encode, Message, StatementId}; -use crate::url::Url; -use crate::Result; use hmac::{Hmac, Mac}; use rand::Rng; use sha2::{Digest, Sha256}; -use async_native_tls::Certificate; + +use crate::cache::StatementCache; +use crate::connection::Connection; +use crate::io::{Buf, BufStream, MaybeTlsStream}; +use crate::postgres::PgError; +use crate::postgres::protocol::{ + self, Authentication, Decode, Encode, hi, Message, SaslInitialResponse, SaslResponse, + StatementId, +}; +use crate::Result; +use crate::url::Url; /// An asynchronous connection to a [Postgres] database. /// /// The connection string expected by [Connection::open] should be a PostgreSQL connection /// string, as documented at /// +/// +/// ### TLS Support (requires `tls` feature) +/// This connection type supports the same `sslmode` query parameter that `libpq` does in +/// connection strings: https://www.postgresql.org/docs/12/libpq-ssl.html +/// +/// If the `tls` feature is not enabled, `disable`, `allow` and `prefer` are no-ops and `require`, +/// `verify-ca` and `verify-full` are forbidden (attempting to connect with these will return +/// an error). +/// +/// If the `tls` feature is enabled, an upgrade to TLS is attempted on every connection by default +/// (equivalent to `sslmode=prefer`). If the server does not support TLS (because it was not +/// started with a valid certificate and key, see https://www.postgresql.org/docs/12/ssl-tcp.html) +/// then it falls back to an unsecured connection and logs a warning. +/// +/// Add `sslmode=require` to your connection string to emit an error if the TLS upgrade fails. +/// +/// However, like with `libpq` the server certificate is **not** checked for validity by default. +/// +/// Specifying `sslmode=verify-ca` will cause the TLS upgrade to verify the server's SSL +/// certificate against a local CA root certificate; this is not the system root certificate +/// but is instead expected to be specified in one of a few ways: +/// +/// * The path to the certificate can be specified by adding the `sslrootcert` query parameter +/// to the connection string. (Remember to percent-encode it!) +/// +/// * The path may also be specified via the `PGSSLROOTCERT` environment variable (which +/// should *not* be percent-encoded.) +/// +/// * Otherwise, the library will look for the Postgres global root CA certificate in the default +/// location: +/// +/// * `$HOME/.postgresql/root.crt` on POSIX systems +/// * `%APPDATA%\postgresql\root.crt` on Windows +/// +/// These locations are documented here: https://www.postgresql.org/docs/12/libpq-ssl.html#LIBQ-SSL-CERTIFICATES +/// If the root certificate cannot be found by any of these means then the TLS upgrade will fail. +/// +/// If `sslmode=verify-full` is specified, in addition to checking the certificate as with +/// `sslmode=verify-ca`, the hostname in the connection string will be verified +/// against the hostname in the server certificate, so they must be the same for the TLS +/// upgrade to succeed. pub struct PgConnection { pub(super) stream: BufStream, @@ -299,7 +338,9 @@ impl PgConnection { "disable" | "allow" => (), #[cfg(feature = "tls")] - "prefer" => { self_.try_ssl(&url, true, true).await?; }, + "prefer" => if !self_.try_ssl(&url, true, true).await? { + log::warn!("server does not support TLS, falling back to unsecured connection") + }, #[cfg(not(feature = "tls"))] "prefer" => log::info!("compiled without TLS, skipping upgrade"), From a014accf088b56f036ddec288fc1bb436b71f5c4 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Mon, 13 Jan 2020 14:58:37 -0800 Subject: [PATCH 04/14] document TLS options for MySQL --- sqlx-core/src/mysql/connection.rs | 38 ++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/sqlx-core/src/mysql/connection.rs b/sqlx-core/src/mysql/connection.rs index e35cac09..aa237ff1 100644 --- a/sqlx-core/src/mysql/connection.rs +++ b/sqlx-core/src/mysql/connection.rs @@ -27,6 +27,42 @@ const COLLATE_UTF8MB4_UNICODE_CI: u8 = 224; /// The connection string expected by [Connection::open] should be a MySQL connection /// string, as documented at /// +/// +/// ### TLS Support (requires `tls` feature) +/// This connection type supports some of the same flags as the `mysql` CLI application for SSL +/// connections, but they must be specified via the query segment of the connection string +/// rather than as program arguments. +/// +/// The same options for `--ssl-mode` are supported as the `ssl-mode` query parameter: +/// https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-mode +/// +/// If the `tls` feature is not enabled, `ssl-mode=DISABLED` and `ssl-mode=PREFERRED` are no-ops and +/// `ssl-mode=REQUIRED`, `ssl-mode=VERIFY_CA` and `ssl-mode=VERIFY_IDENTITY` are forbidden +/// (attempting to connect with these will return an error). +/// +/// If the `tls` feature is enabled, an upgrade to TLS is attempted on every connection by default +/// (equivalent to `ssl-mode=PREFERRED`). If the server does not support TLS (because `--ssl=0` was +/// passed or an invalid certificate or key was used, +/// https://dev.mysql.com/doc/refman/8.0/en/using-encrypted-connections.html) +/// then it falls back to an unsecured connection and logs a warning. +/// +/// Add `ssl-mode=REQUIRED` to your connection string to emit an error if the TLS upgrade fails. +/// +/// However, like with `mysql` the server certificate is **not** checked for validity by default. +/// +/// Specifying `ssl-mode=VERIFY_CA` will cause the TLS upgrade to verify the server's SSL +/// certificate against a local CA root certificate; this is not the system root certificate +/// but is instead expected to be specified as a local path with the `ssl-ca` query parameter +/// (percent-encoded so the URL remains valid). +/// +/// If `ssl-ca` is not specified or the file cannot be read, then an error is returned. +/// `ssl-ca` implies `ssl-mode=VERIFY_CA` so you only actually need to specify the former +/// but you may prefer having both to be more explicit. +/// +/// If `sslmode=VERIFY_IDENTITY` is specified, in addition to checking the certificate as with +/// `ssl-mode=VERIFY_CA`, the hostname in the connection string will be verified +/// against the hostname in the server certificate, so they must be the same for the TLS +/// upgrade to succeed. `ssl-ca` must still be specified. pub struct MySqlConnection { pub(super) stream: BufStream, @@ -426,7 +462,7 @@ impl MySqlConnection { // try to upgrade #[cfg(feature = "tls")] "PREFERRED" => if let Err(e) = self_.try_ssl(&url, None, true).await { - log::info!("server does not support TLS"); + log::warn!("server does not support TLS"); // fallback, redo connection self_ = Self::new(&url).await?; handshake = self_.receive_handshake(&url).await?; From d129dead815d2b54b073343488b81c1a9da4ec8b Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Mon, 13 Jan 2020 15:02:54 -0800 Subject: [PATCH 05/14] test TLS for MySQL on CI --- .github/workflows/mysql.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/mysql.yml b/.github/workflows/mysql.yml index 97cd9b09..842f1ed2 100644 --- a/.github/workflows/mysql.yml +++ b/.github/workflows/mysql.yml @@ -25,9 +25,14 @@ jobs: # will assign a random free host port - 3306/tcp # needed because the container does not provide a healthcheck - options: --health-cmd "mysqladmin ping --silent" --health-interval 30s --health-timeout 30s --health-retries 10 + options: >- + --health-cmd "mysqladmin ping --silent" --health-interval 30s --health-timeout 30s + --health-retries 10 -v /data/mysql:/var/lib/mysql + steps: + - run: docker logs ${{ job.services.mysql.id }} + - run: ls /data/mysql - uses: actions/checkout@v1 # Rust ------------------------------------------------ @@ -48,9 +53,11 @@ jobs: # ----------------------------------------------------- - - run: cargo test -p sqlx --no-default-features --features 'mysql macros chrono' + - run: cargo test -p sqlx --no-default-features --features 'mysql macros chrono tls' env: - DATABASE_URL: mysql://root:password@localhost:${{ job.services.mysql.ports[3306] }}/sqlx + # pass the path to the CA that the MySQL service generated + # Github Actions' YML parser doesn't handle multiline strings correctly + DATABASE_URL: mysql://root:password@localhost:${{ job.services.mysql.ports[3306] }}/sqlx?ssl-mode=VERIFY_CA&ssl-ca=%2Fdata%2Fmysql%2Fca.pem # Rust ------------------------------------------------ From c92ee619c3e5aaa0a815283f6ad4b57353d3ce82 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Mon, 13 Jan 2020 20:42:49 -0800 Subject: [PATCH 06/14] fix some behaviors with TLS in MySQL --- sqlx-core/src/mysql/connection.rs | 6 +++++- sqlx-core/src/mysql/protocol/ssl_request.rs | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/sqlx-core/src/mysql/connection.rs b/sqlx-core/src/mysql/connection.rs index aa237ff1..2c251049 100644 --- a/sqlx-core/src/mysql/connection.rs +++ b/sqlx-core/src/mysql/connection.rs @@ -206,6 +206,10 @@ impl MySqlConnection { client_capabilities |= Capabilities::CONNECT_WITH_DB; } + if cfg!(feature = "tls") { + client_capabilities |= Capabilities::SSL; + } + self.capabilities = (client_capabilities & handshake.server_capabilities) | Capabilities::PROTOCOL_41; @@ -462,7 +466,7 @@ impl MySqlConnection { // try to upgrade #[cfg(feature = "tls")] "PREFERRED" => if let Err(e) = self_.try_ssl(&url, None, true).await { - log::warn!("server does not support TLS"); + log::warn!("TLS handshake failed, falling back to insecure: {}", e); // fallback, redo connection self_ = Self::new(&url).await?; handshake = self_.receive_handshake(&url).await?; diff --git a/sqlx-core/src/mysql/protocol/ssl_request.rs b/sqlx-core/src/mysql/protocol/ssl_request.rs index 088081ca..34682763 100644 --- a/sqlx-core/src/mysql/protocol/ssl_request.rs +++ b/sqlx-core/src/mysql/protocol/ssl_request.rs @@ -14,9 +14,11 @@ pub struct SslRequest { impl Encode for SslRequest { fn encode(&self, buf: &mut Vec, capabilities: Capabilities) { - // client capabilities : int<4> // SSL must be set or else it makes no sense to ask for an upgrade - buf.put_u32::((capabilities | Capabilities::SSL).bits() as u32); + assert!(capabilities.contains(Capabilities::SSL), "SSL bit must be set for Capabilities"); + + // client capabilities : int<4> + buf.put_u32::(capabilities.bits() as u32); // max packet size : int<4> buf.put_u32::(self.max_packet_size); From 6682d4dce78e845194f05cc65ad062a5c3340f1a Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Mon, 13 Jan 2020 20:43:32 -0800 Subject: [PATCH 07/14] Pool::new(): forward to connection types for string syntax --- sqlx-core/src/pool/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sqlx-core/src/pool/mod.rs b/sqlx-core/src/pool/mod.rs index f68aefb5..fb03166c 100644 --- a/sqlx-core/src/pool/mod.rs +++ b/sqlx-core/src/pool/mod.rs @@ -43,6 +43,12 @@ where DB::Connection: crate::Connection, { /// Creates a connection pool with the default configuration. + /// + /// The connection URL syntax is documented on the connection type for the respective + /// database you're connecting to: + /// + /// * MySQL/MariaDB: [crate::MySqlConnection] + /// * PostgreSQL: [crate::PgConnection] pub async fn new(url: &str) -> crate::Result { Self::builder().build(url).await } From d99b87b5c58486d34b1cd1f2135bc57f20e9e5a3 Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Tue, 14 Jan 2020 10:25:38 -0800 Subject: [PATCH 08/14] Fix cargo-check command in postgres workflow --- .github/workflows/postgres.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/postgres.yml b/.github/workflows/postgres.yml index 1e3dc038..8beab186 100644 --- a/.github/workflows/postgres.yml +++ b/.github/workflows/postgres.yml @@ -50,7 +50,7 @@ jobs: # ----------------------------------------------------- # Check that we build with TLS support (TODO: we need a postgres image with SSL certs to test) - - run: cargo build -p sqlx-core --no-default-features 'postgres macros uuid chrono tls' + - run: cargo check -p sqlx-core --no-default-features --features 'postgres macros uuid chrono tls' - run: cargo test -p sqlx --no-default-features --features 'postgres macros uuid chrono' env: From cb1dbff5442fbbf62e8e385a6b085402209586a2 Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Tue, 14 Jan 2020 10:32:59 -0800 Subject: [PATCH 09/14] mysql: if in a TLS stream, sha2 auth is just "send a clear text password" --- sqlx-core/src/io/buf_stream.rs | 15 ++++++ sqlx-core/src/io/tls.rs | 20 +++++++- sqlx-core/src/mysql/connection.rs | 77 +++++++++++++++++++++++-------- 3 files changed, 91 insertions(+), 21 deletions(-) diff --git a/sqlx-core/src/io/buf_stream.rs b/sqlx-core/src/io/buf_stream.rs index cb1e79c4..b9ffda3c 100644 --- a/sqlx-core/src/io/buf_stream.rs +++ b/sqlx-core/src/io/buf_stream.rs @@ -3,6 +3,7 @@ use async_std::io::{ Read, Write, }; use std::io; +use std::ops::{Deref, DerefMut}; const RBUF_SIZE: usize = 8 * 1024; @@ -124,6 +125,20 @@ where } } +impl Deref for BufStream { + type Target = S; + + fn deref(&self) -> &Self::Target { + &self.stream + } +} + +impl DerefMut for BufStream { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.stream + } +} + // TODO: Find a nicer way to do this // Return `Ok(None)` immediately from a function if the wrapped value is `None` #[allow(unused)] diff --git a/sqlx-core/src/io/tls.rs b/sqlx-core/src/io/tls.rs index 19884564..798c2660 100644 --- a/sqlx-core/src/io/tls.rs +++ b/sqlx-core/src/io/tls.rs @@ -24,11 +24,27 @@ enum Inner { impl MaybeTlsStream { pub async fn connect(url: &Url, default_port: u16) -> crate::Result { let conn = TcpStream::connect((url.host(), url.port(default_port))).await?; - Ok(Self { inner: Inner::NotTls(conn) }) + Ok(Self { + inner: Inner::NotTls(conn), + }) + } + + pub fn is_tls(&self) -> bool { + match self.inner { + Inner::NotTls(_) => false, + #[cfg(feature = "tls")] + Inner::Tls(_) => true, + #[cfg(feature = "tls")] + Inner::Upgrading => false, + } } #[cfg(feature = "tls")] - pub async fn upgrade(&mut self, url: &Url, connector: async_native_tls::TlsConnector) -> crate::Result<()> { + pub async fn upgrade( + &mut self, + url: &Url, + connector: async_native_tls::TlsConnector, + ) -> crate::Result<()> { let conn = match std::mem::replace(&mut self.inner, Upgrading) { NotTls(conn) => conn, Tls(_) => return Err(tls_err!("connection already upgraded").into()), diff --git a/sqlx-core/src/mysql/connection.rs b/sqlx-core/src/mysql/connection.rs index 2c251049..d3b51ac0 100644 --- a/sqlx-core/src/mysql/connection.rs +++ b/sqlx-core/src/mysql/connection.rs @@ -10,7 +10,10 @@ use crate::cache::StatementCache; use crate::connection::Connection; use crate::io::{Buf, BufMut, BufStream, MaybeTlsStream}; use crate::mysql::error::MySqlError; -use crate::mysql::protocol::{AuthPlugin, AuthSwitch, Capabilities, Decode, Encode, EofPacket, ErrPacket, Handshake, HandshakeResponse, OkPacket, SslRequest}; +use crate::mysql::protocol::{ + AuthPlugin, AuthSwitch, Capabilities, Decode, Encode, EofPacket, ErrPacket, Handshake, + HandshakeResponse, OkPacket, SslRequest, +}; use crate::mysql::rsa; use crate::mysql::util::xor_eq; use crate::url::Url; @@ -59,7 +62,7 @@ const COLLATE_UTF8MB4_UNICODE_CI: u8 = 224; /// `ssl-ca` implies `ssl-mode=VERIFY_CA` so you only actually need to specify the former /// but you may prefer having both to be more explicit. /// -/// If `sslmode=VERIFY_IDENTITY` is specified, in addition to checking the certificate as with +/// If `ssl-mode=VERIFY_IDENTITY` is specified, in addition to checking the certificate as with /// `ssl-mode=VERIFY_CA`, the hostname in the connection string will be verified /// against the hostname in the server certificate, so they must be the same for the TLS /// upgrade to succeed. `ssl-ca` must still be specified. @@ -338,6 +341,15 @@ impl MySqlConnection { ) -> crate::Result> { // https://mariadb.com/kb/en/caching_sha2_password-authentication-plugin/ + if self.stream.is_tls() { + // If in a TLS stream, send the password directly in clear text + let mut clear_text = String::with_capacity(password.len() + 1); + clear_text.push_str(password); + clear_text.push('\0'); + + return Ok(clear_text.into_boxed_bytes()); + } + // client sends a public key request self.send(&[public_key_request_id][..]).await?; @@ -415,7 +427,12 @@ impl MySqlConnection { } #[cfg(feature = "tls")] - async fn try_ssl(&mut self, url: &Url, ca_file: Option<&str>, invalid_hostnames: bool) -> crate::Result<()> { + async fn try_ssl( + &mut self, + url: &Url, + ca_file: Option<&str>, + invalid_hostnames: bool, + ) -> crate::Result<()> { use async_native_tls::{Certificate, TlsConnector}; use async_std::fs; @@ -431,8 +448,9 @@ impl MySqlConnection { // send upgrade request and then immediately try TLS handshake self.send(SslRequest { client_collation: COLLATE_UTF8MB4_UNICODE_CI, - max_packet_size: MAX_PACKET_SIZE - }).await?; + max_packet_size: MAX_PACKET_SIZE, + }) + .await?; self.stream.stream.upgrade(url, connector).await } @@ -451,8 +469,14 @@ impl MySqlConnection { let ca_file = url.get_param("ssl-ca"); - let ssl_mode = url.get_param("ssl-mode") - .unwrap_or(if ca_file.is_some() { "VERIFY_CA" } else { "PREFERRED" }.into()); + let ssl_mode = url.get_param("ssl-mode").unwrap_or( + if ca_file.is_some() { + "VERIFY_CA" + } else { + "PREFERRED" + } + .into(), + ); let supports_ssl = handshake.server_capabilities.contains(Capabilities::SSL); @@ -461,35 +485,50 @@ impl MySqlConnection { // don't try upgrade #[cfg(feature = "tls")] - "PREFERRED" if !supports_ssl => log::info!("server does not support TLS; using unencrypted connection"), + "PREFERRED" if !supports_ssl => { + log::info!("server does not support TLS; using unencrypted connection") + } // try to upgrade #[cfg(feature = "tls")] - "PREFERRED" => if let Err(e) = self_.try_ssl(&url, None, true).await { - log::warn!("TLS handshake failed, falling back to insecure: {}", e); - // fallback, redo connection - self_ = Self::new(&url).await?; - handshake = self_.receive_handshake(&url).await?; - }, + "PREFERRED" => { + if let Err(e) = self_.try_ssl(&url, None, true).await { + log::warn!("TLS handshake failed, falling back to insecure: {}", e); + // fallback, redo connection + self_ = Self::new(&url).await?; + handshake = self_.receive_handshake(&url).await?; + } + } #[cfg(not(feature = "tls"))] "PREFERRED" => log::info!("compiled without TLS, skipping upgrade"), #[cfg(feature = "tls")] - "REQUIRED" if !supports_ssl => return Err(tls_err!("server does not support TLS").into()), + "REQUIRED" if !supports_ssl => { + return Err(tls_err!("server does not support TLS").into()) + } #[cfg(feature = "tls")] "REQUIRED" => self_.try_ssl(&url, None, true).await?, #[cfg(feature = "tls")] - "VERIFY_CA" | "VERIFY_FULL" if ca_file.is_none() => - return Err(tls_err!("`ssl-mode` of {:?} requires `ssl-ca` to be set", ssl_mode).into()), + "VERIFY_CA" | "VERIFY_FULL" if ca_file.is_none() => { + return Err( + tls_err!("`ssl-mode` of {:?} requires `ssl-ca` to be set", ssl_mode).into(), + ) + } #[cfg(feature = "tls")] - "VERIFY_CA" | "VERIFY_FULL" => self_.try_ssl(&url, ca_file.as_deref(), ssl_mode != "VERIFY_FULL").await?, + "VERIFY_CA" | "VERIFY_FULL" => { + self_ + .try_ssl(&url, ca_file.as_deref(), ssl_mode != "VERIFY_FULL") + .await? + } #[cfg(not(feature = "tls"))] - "REQUIRED" | "VERIFY_CA" | "VERIFY_FULL" => return Err(tls_err!("compiled without TLS").into()), + "REQUIRED" | "VERIFY_CA" | "VERIFY_FULL" => { + return Err(tls_err!("compiled without TLS").into()) + } _ => return Err(tls_err!("unknown `ssl-mode` value: {:?}", ssl_mode).into()), } From 0a5b527d79281da33f9dc41a03ff8f98b447802b Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Tue, 14 Jan 2020 10:35:50 -0800 Subject: [PATCH 10/14] fix import errors and run rustfmt --- sqlx-core/src/error.rs | 1 - sqlx-core/src/io/mod.rs | 2 +- sqlx-core/src/io/tls.rs | 1 + sqlx-core/src/mysql/connection.rs | 6 +- sqlx-core/src/mysql/protocol/mod.rs | 4 +- sqlx-core/src/mysql/protocol/ssl_request.rs | 5 +- sqlx-core/src/postgres/connection.rs | 77 +++++++++++-------- .../src/postgres/protocol/ssl_request.rs | 4 +- sqlx-core/src/url.rs | 12 +-- 9 files changed, 60 insertions(+), 52 deletions(-) diff --git a/sqlx-core/src/error.rs b/sqlx-core/src/error.rs index 960023d5..f3d0244a 100644 --- a/sqlx-core/src/error.rs +++ b/sqlx-core/src/error.rs @@ -243,4 +243,3 @@ macro_rules! impl_fmt_error { } }; } - diff --git a/sqlx-core/src/io/mod.rs b/sqlx-core/src/io/mod.rs index b4c95430..304c11b6 100644 --- a/sqlx-core/src/io/mod.rs +++ b/sqlx-core/src/io/mod.rs @@ -12,7 +12,7 @@ pub use self::{ buf_mut::BufMut, buf_stream::BufStream, byte_str::ByteStr, - tls::MaybeTlsStream + tls::MaybeTlsStream, }; #[cfg(test)] diff --git a/sqlx-core/src/io/tls.rs b/sqlx-core/src/io/tls.rs index 798c2660..7743ebdc 100644 --- a/sqlx-core/src/io/tls.rs +++ b/sqlx-core/src/io/tls.rs @@ -29,6 +29,7 @@ impl MaybeTlsStream { }) } + #[allow(dead_code)] pub fn is_tls(&self) -> bool { match self.inner { Inner::NotTls(_) => false, diff --git a/sqlx-core/src/mysql/connection.rs b/sqlx-core/src/mysql/connection.rs index d3b51ac0..df9537aa 100644 --- a/sqlx-core/src/mysql/connection.rs +++ b/sqlx-core/src/mysql/connection.rs @@ -1,7 +1,7 @@ use std::convert::TryInto; use std::io; -use async_std::net::{Shutdown, TcpStream}; +use async_std::net::Shutdown; use byteorder::{ByteOrder, LittleEndian}; use futures_core::future::BoxFuture; use sha1::Sha1; @@ -17,8 +17,6 @@ use crate::mysql::protocol::{ use crate::mysql::rsa; use crate::mysql::util::xor_eq; use crate::url::Url; -use std::borrow::Cow; -use std::path::Path; // Size before a packet is split const MAX_PACKET_SIZE: u32 = 1024; @@ -347,7 +345,7 @@ impl MySqlConnection { clear_text.push_str(password); clear_text.push('\0'); - return Ok(clear_text.into_boxed_bytes()); + return Ok(clear_text.into_bytes().into_boxed_slice()); } // client sends a public key request diff --git a/sqlx-core/src/mysql/protocol/mod.rs b/sqlx-core/src/mysql/protocol/mod.rs index 9da46ec6..3087d4e1 100644 --- a/sqlx-core/src/mysql/protocol/mod.rs +++ b/sqlx-core/src/mysql/protocol/mod.rs @@ -39,9 +39,9 @@ mod com_stmt_prepare_ok; mod eof; mod err; mod handshake_response; -mod ssl_request; mod ok; mod row; +mod ssl_request; pub use auth_switch::AuthSwitch; pub use column_count::ColumnCount; @@ -51,5 +51,5 @@ pub use eof::EofPacket; pub use err::ErrPacket; pub use handshake_response::HandshakeResponse; pub use ok::OkPacket; -pub use ssl_request::SslRequest; pub use row::Row; +pub use ssl_request::SslRequest; diff --git a/sqlx-core/src/mysql/protocol/ssl_request.rs b/sqlx-core/src/mysql/protocol/ssl_request.rs index 34682763..fea5ef8e 100644 --- a/sqlx-core/src/mysql/protocol/ssl_request.rs +++ b/sqlx-core/src/mysql/protocol/ssl_request.rs @@ -15,7 +15,10 @@ pub struct SslRequest { impl Encode for SslRequest { fn encode(&self, buf: &mut Vec, capabilities: Capabilities) { // SSL must be set or else it makes no sense to ask for an upgrade - assert!(capabilities.contains(Capabilities::SSL), "SSL bit must be set for Capabilities"); + assert!( + capabilities.contains(Capabilities::SSL), + "SSL bit must be set for Capabilities" + ); // client capabilities : int<4> buf.put_u32::(capabilities.bits() as u32); diff --git a/sqlx-core/src/postgres/connection.rs b/sqlx-core/src/postgres/connection.rs index 689a3927..d18b5cc5 100644 --- a/sqlx-core/src/postgres/connection.rs +++ b/sqlx-core/src/postgres/connection.rs @@ -1,8 +1,5 @@ use std::convert::TryInto; -use std::path::Path; -use async_native_tls::Certificate; -use async_std::fs; use async_std::net::Shutdown; use byteorder::NetworkEndian; use futures_core::future::BoxFuture; @@ -13,13 +10,13 @@ use sha2::{Digest, Sha256}; use crate::cache::StatementCache; use crate::connection::Connection; use crate::io::{Buf, BufStream, MaybeTlsStream}; -use crate::postgres::PgError; use crate::postgres::protocol::{ - self, Authentication, Decode, Encode, hi, Message, SaslInitialResponse, SaslResponse, + self, hi, Authentication, Decode, Encode, Message, SaslInitialResponse, SaslResponse, StatementId, }; -use crate::Result; +use crate::postgres::PgError; use crate::url::Url; +use crate::Result; /// An asynchronous connection to a [Postgres] database. /// @@ -88,9 +85,13 @@ pub struct PgConnection { impl PgConnection { #[cfg(feature = "tls")] - async fn try_ssl(&mut self, url: &Url, invalid_certs: bool, invalid_hostnames: bool) -> crate::Result { - use async_native_tls::{TlsConnector, Certificate}; - use std::env; + async fn try_ssl( + &mut self, + url: &Url, + invalid_certs: bool, + invalid_hostnames: bool, + ) -> crate::Result { + use async_native_tls::TlsConnector; protocol::SslRequest::encode(self.stream.buffer_mut()); @@ -99,8 +100,10 @@ impl PgConnection { match self.stream.peek(1).await? { Some(b"N") => return Ok(false), Some(b"S") => (), - Some(other) => return Err(tls_err!("unexpected single-byte response: 0x{:02X}", other[0]).into()), - None => return Err(tls_err!("server unexpectedly closed connection").into()) + Some(other) => { + return Err(tls_err!("unexpected single-byte response: 0x{:02X}", other[0]).into()) + } + None => return Err(tls_err!("server unexpectedly closed connection").into()), } let mut connector = TlsConnector::new() @@ -112,7 +115,7 @@ impl PgConnection { Ok(cert) => { connector = connector.add_root_certificate(cert); } - Err(e) => log::warn!("failed to read Postgres root certificate: {}", e) + Err(e) => log::warn!("failed to read Postgres root certificate: {}", e), } } @@ -162,7 +165,7 @@ impl PgConnection { protocol::PasswordMessage::ClearText( url.password().unwrap_or_default(), ) - .encode(self.stream.buffer_mut()); + .encode(self.stream.buffer_mut()); self.stream.flush().await?; } @@ -173,7 +176,7 @@ impl PgConnection { user: username, salt, } - .encode(self.stream.buffer_mut()); + .encode(self.stream.buffer_mut()); self.stream.flush().await?; } @@ -216,7 +219,7 @@ impl PgConnection { "requires unimplemented authentication method: {:?}", auth ) - .into()); + .into()); } } } @@ -338,27 +341,37 @@ impl PgConnection { "disable" | "allow" => (), #[cfg(feature = "tls")] - "prefer" => if !self_.try_ssl(&url, true, true).await? { - log::warn!("server does not support TLS, falling back to unsecured connection") - }, + "prefer" => { + if !self_.try_ssl(&url, true, true).await? { + log::warn!("server does not support TLS, falling back to unsecured connection") + } + } #[cfg(not(feature = "tls"))] "prefer" => log::info!("compiled without TLS, skipping upgrade"), #[cfg(feature = "tls")] - "require" | "verify-ca" | "verify-full" => if !self_.try_ssl( - &url, - ssl_mode == "require", // false for both verify-ca and verify-full - ssl_mode != "verify-full" // false for only verify-full - ).await? { - return Err(tls_err!("Postgres server does not support TLS").into()) + "require" | "verify-ca" | "verify-full" => { + if !self_ + .try_ssl( + &url, + ssl_mode == "require", // false for both verify-ca and verify-full + ssl_mode != "verify-full", // false for only verify-full + ) + .await? + { + return Err(tls_err!("Postgres server does not support TLS").into()); + } } #[cfg(not(feature = "tls"))] - "require" | "verify-ca" | "verify-full" => return Err( - tls_err!("sslmode {:?} unsupported; SQLx was compiled without `tls` feature", - ssl_mode).into() - ), + "require" | "verify-ca" | "verify-full" => { + return Err(tls_err!( + "sslmode {:?} unsupported; SQLx was compiled without `tls` feature", + ssl_mode + ) + .into()) + } _ => return Err(tls_err!("unknown `sslmode` value: {:?}", ssl_mode).into()), } @@ -370,9 +383,9 @@ impl PgConnection { impl Connection for PgConnection { fn open(url: T) -> BoxFuture<'static, Result> - where - T: TryInto, - Self: Sized, + where + T: TryInto, + Self: Sized, { Box::pin(PgConnection::open(url.try_into())) } @@ -388,7 +401,7 @@ async fn read_root_certificate(url: &Url) -> crate::Result(8); // 1234 in high 16 bits, 5679 in low 16 - buf.put_u32::( - (1234 << 16) | 5679, - ); + buf.put_u32::((1234 << 16) | 5679); } } diff --git a/sqlx-core/src/url.rs b/sqlx-core/src/url.rs index f7914dbb..62457122 100644 --- a/sqlx-core/src/url.rs +++ b/sqlx-core/src/url.rs @@ -1,5 +1,5 @@ -use std::convert::{TryFrom, TryInto}; use std::borrow::Cow; +use std::convert::{TryFrom, TryInto}; pub struct Url(url::Url); @@ -67,12 +67,8 @@ impl Url { } pub fn get_param(&self, key: &str) -> Option> { - self.0.query_pairs().find_map(|(key_, val)| { - if key == key_ { - Some(val) - } else { - None - } - }) + self.0 + .query_pairs() + .find_map(|(key_, val)| if key == key_ { Some(val) } else { None }) } } From 330b1e2b4ebd2d99e4c8cef6b79a0ce75852e1c6 Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Tue, 14 Jan 2020 10:43:54 -0800 Subject: [PATCH 11/14] Fix serialization test for postgres --- sqlx-core/src/mysql/rsa.rs | 3 +-- sqlx-core/src/postgres/protocol/ssl_request.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sqlx-core/src/mysql/rsa.rs b/sqlx-core/src/mysql/rsa.rs index 6be0bb2a..715a499b 100644 --- a/sqlx-core/src/mysql/rsa.rs +++ b/sqlx-core/src/mysql/rsa.rs @@ -178,7 +178,6 @@ mod tests { use super::{BigUint, PublicKey}; use rand::rngs::adapter::ReadRng; use sha1::Sha1; - use sha2::Sha256; const INPUT: &str = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv9E+l0oFIoGnZmu6bdil\nI3WK79iug/hukj5QrWRrJVVCHL8rRxNsQGYPvQfXgqEnJW0Rqy2BBebNrnSMduny\nCazz1KM1h57hSI1xHGhg/o82Us1j9fUucKo0Pt3vg7xjVVcN0j1bwr96gEbt6B4Q\nt4eKZBhtle1bgoBcqFBhGfU17cnedSzMUCutM+kXTzzOTplKoqXeJpEZDTX8AP9F\nQ9JkoA22yTn8H2GROIAffm1UQS7DXXjI5OnzBJNs72oNSeK8i72xLkoSdfVw3vCu\ni+mpt4LJgAZLvzc2O4nLzu4Bljb+Mrch34HSWyxOfWzt1v9vpJfEVQ2/VZaIng6U\nUQIDAQAB\n-----END PUBLIC KEY-----\n"; @@ -239,7 +238,7 @@ mod tests { 0x2c, 0x49, ]; - let mut seed = &[ + let seed = &[ 0xaa, 0xfd, 0x12, 0xf6, 0x59, 0xca, 0xe6, 0x34, 0x89, 0xb4, 0x79, 0xe5, 0x07, 0x6d, 0xde, 0xc2, 0xf0, 0x6c, 0xb5, 0x8f, ][..]; diff --git a/sqlx-core/src/postgres/protocol/ssl_request.rs b/sqlx-core/src/postgres/protocol/ssl_request.rs index bb7f50a6..ef6460c1 100644 --- a/sqlx-core/src/postgres/protocol/ssl_request.rs +++ b/sqlx-core/src/postgres/protocol/ssl_request.rs @@ -19,5 +19,5 @@ fn test_ssl_request() { let mut buf = Vec::new(); SslRequest::encode(&mut buf); - assert_eq!((&buf[..]).get_u32::().unwrap(), 80877103); + assert_eq!(&buf, b"\x00\x00\x00\x08\x04\xd2\x16/"); } From fc66c8fa3f723260cc25f2d00c25e4dd8181bfbb Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Tue, 14 Jan 2020 12:07:35 -0800 Subject: [PATCH 12/14] give examples of connection strings enabling SSL --- sqlx-core/src/mysql/connection.rs | 26 +++++++++++++++++++++++--- sqlx-core/src/postgres/connection.rs | 20 +++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/sqlx-core/src/mysql/connection.rs b/sqlx-core/src/mysql/connection.rs index df9537aa..fea2cb29 100644 --- a/sqlx-core/src/mysql/connection.rs +++ b/sqlx-core/src/mysql/connection.rs @@ -35,7 +35,16 @@ const COLLATE_UTF8MB4_UNICODE_CI: u8 = 224; /// rather than as program arguments. /// /// The same options for `--ssl-mode` are supported as the `ssl-mode` query parameter: -/// https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-mode +/// +/// +/// ```text +/// mysql://[:]@[:]/[?ssl-mode=[&ssl-ca=]] +/// ``` +/// where +/// ```text +/// ssl-mode = DISABLED | PREFERRED | REQUIRED | VERIFY_CA | VERIFY_IDENTITY +/// path = percent (URL) encoded path on the local machine +/// ``` /// /// If the `tls` feature is not enabled, `ssl-mode=DISABLED` and `ssl-mode=PREFERRED` are no-ops and /// `ssl-mode=REQUIRED`, `ssl-mode=VERIFY_CA` and `ssl-mode=VERIFY_IDENTITY` are forbidden @@ -43,8 +52,8 @@ const COLLATE_UTF8MB4_UNICODE_CI: u8 = 224; /// /// If the `tls` feature is enabled, an upgrade to TLS is attempted on every connection by default /// (equivalent to `ssl-mode=PREFERRED`). If the server does not support TLS (because `--ssl=0` was -/// passed or an invalid certificate or key was used, -/// https://dev.mysql.com/doc/refman/8.0/en/using-encrypted-connections.html) +/// passed to the server or an invalid certificate or key was used: +/// ) /// then it falls back to an unsecured connection and logs a warning. /// /// Add `ssl-mode=REQUIRED` to your connection string to emit an error if the TLS upgrade fails. @@ -56,6 +65,17 @@ const COLLATE_UTF8MB4_UNICODE_CI: u8 = 224; /// but is instead expected to be specified as a local path with the `ssl-ca` query parameter /// (percent-encoded so the URL remains valid). /// +/// If you're running MySQL locally it might look something like this (for `VERIFY_CA`): +/// ```text +/// mysql://root:password@localhost/my_database?ssl-mode=VERIFY_CA&ssl-ca=%2Fvar%2Flib%2Fmysql%2Fca.pem +/// ``` +/// +/// `%2F` is the percent-encoding for forward slash (`/`). In the example we give `/var/lib/mysql/ca.pem` +/// as the CA certificate path, which is generated by the MySQL server automatically if +/// no certificate is manually specified. Note that the path may vary based on the default `my.cnf` +/// packaged with MySQL for your Linux distribution. Also note that unlike MySQL, MariaDB does *not* +/// generate certificates automatically and they must always be passed in to enable TLS. +/// /// If `ssl-ca` is not specified or the file cannot be read, then an error is returned. /// `ssl-ca` implies `ssl-mode=VERIFY_CA` so you only actually need to specify the former /// but you may prefer having both to be more explicit. diff --git a/sqlx-core/src/postgres/connection.rs b/sqlx-core/src/postgres/connection.rs index d18b5cc5..133969ed 100644 --- a/sqlx-core/src/postgres/connection.rs +++ b/sqlx-core/src/postgres/connection.rs @@ -26,7 +26,16 @@ use crate::Result; /// /// ### TLS Support (requires `tls` feature) /// This connection type supports the same `sslmode` query parameter that `libpq` does in -/// connection strings: https://www.postgresql.org/docs/12/libpq-ssl.html +/// connection strings: +/// +/// ```text +/// postgresql://[:]@[:]/[?sslmode=[&sslcrootcert=]] +/// ``` +/// where +/// ```text +/// ssl-mode = disable | allow | prefer | require | verify-ca | verify-full +/// path = percent (URL) encoded path on the local machine +/// ``` /// /// If the `tls` feature is not enabled, `disable`, `allow` and `prefer` are no-ops and `require`, /// `verify-ca` and `verify-full` are forbidden (attempting to connect with these will return @@ -34,11 +43,16 @@ use crate::Result; /// /// If the `tls` feature is enabled, an upgrade to TLS is attempted on every connection by default /// (equivalent to `sslmode=prefer`). If the server does not support TLS (because it was not -/// started with a valid certificate and key, see https://www.postgresql.org/docs/12/ssl-tcp.html) +/// started with a valid certificate and key, see ) /// then it falls back to an unsecured connection and logs a warning. /// /// Add `sslmode=require` to your connection string to emit an error if the TLS upgrade fails. /// +/// If you're running Postgres locally, your connection string might look like this: +/// ```text +/// postgresql://root:password@localhost/my_database?sslmode=require +/// ``` +/// /// However, like with `libpq` the server certificate is **not** checked for validity by default. /// /// Specifying `sslmode=verify-ca` will cause the TLS upgrade to verify the server's SSL @@ -57,7 +71,7 @@ use crate::Result; /// * `$HOME/.postgresql/root.crt` on POSIX systems /// * `%APPDATA%\postgresql\root.crt` on Windows /// -/// These locations are documented here: https://www.postgresql.org/docs/12/libpq-ssl.html#LIBQ-SSL-CERTIFICATES +/// These locations are documented here: /// If the root certificate cannot be found by any of these means then the TLS upgrade will fail. /// /// If `sslmode=verify-full` is specified, in addition to checking the certificate as with From 367e6635cd58b1946eb0926719461428dfa7b99b Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Tue, 14 Jan 2020 12:09:34 -0800 Subject: [PATCH 13/14] remove debug lines from MySQL workflow --- .github/workflows/mysql.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/mysql.yml b/.github/workflows/mysql.yml index 842f1ed2..668a1937 100644 --- a/.github/workflows/mysql.yml +++ b/.github/workflows/mysql.yml @@ -31,8 +31,6 @@ jobs: steps: - - run: docker logs ${{ job.services.mysql.id }} - - run: ls /data/mysql - uses: actions/checkout@v1 # Rust ------------------------------------------------ From 114aaa5dfc7ba6c4b3a2ed523c0c88832f349432 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Tue, 14 Jan 2020 12:14:01 -0800 Subject: [PATCH 14/14] MySqlConnection: warn if server does not support SSL --- sqlx-core/src/mysql/connection.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlx-core/src/mysql/connection.rs b/sqlx-core/src/mysql/connection.rs index fea2cb29..934c573d 100644 --- a/sqlx-core/src/mysql/connection.rs +++ b/sqlx-core/src/mysql/connection.rs @@ -504,7 +504,7 @@ impl MySqlConnection { // don't try upgrade #[cfg(feature = "tls")] "PREFERRED" if !supports_ssl => { - log::info!("server does not support TLS; using unencrypted connection") + log::warn!("server does not support TLS; using unencrypted connection") } // try to upgrade