diff --git a/Cargo.toml b/Cargo.toml index 7e6d1161e..50c5b233c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ open = "1.2" metrics = { version="0.12", features=["std"]} hdrhistogram = "6.3" native-tls = "0.2" +openssl = {version="0.10", optional=true} # file change notification notify = "4.0" palette = "0.5" @@ -98,15 +99,17 @@ winrt-notification = "0.2" [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies] fontconfig = { path = "deps/fontconfig" } -[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies] -openssl = "0.10" - [target.'cfg(target_os = "macos")'.dependencies] -openssl = { version = "0.10", features = ["vendored"] } core-foundation = "0.7" core-graphics = "0.19" core-text = "15.0" +[features] +default = ["enable_openssl", "vendor_openssl"] +enable_openssl = ["openssl"] +# FIXME: find a way to magically enable vendor_openssl only on macOS! +vendor_openssl = ["openssl/vendored"] + [workspace] [profile.release] diff --git a/src/server/client.rs b/src/server/client.rs index 02353df0d..7f0644301 100644 --- a/src/server/client.rs +++ b/src/server/client.rs @@ -448,7 +448,7 @@ impl Reconnectable { Ok(()) } - #[cfg(any(feature = "openssl", unix))] + #[cfg(feature = "enable_openssl")] pub fn tls_connect( &mut self, tls_client: TlsDomainClient, @@ -591,7 +591,7 @@ impl Reconnectable { Ok(()) } - #[cfg(not(any(feature = "openssl", unix)))] + #[cfg(not(feature = "enable_openssl"))] pub fn tls_connect( &mut self, tls_client: TlsDomainClient, @@ -611,13 +611,49 @@ impl Reconnectable { ) })?; + if let Some(Ok(ssh_params)) = tls_client.ssh_parameters() { + if self.tls_creds.is_none() { + // We need to bootstrap via an ssh session + let sess = + ssh_connect_with_ui(&ssh_params.host_and_port, &ssh_params.username, ui)?; + let mut chan = sess.channel_session()?; + + // The `tlscreds` command will start the server if needed and then + // obtain client credentials that we can use for tls. + let cmd = format!("{} cli tlscreds", Self::wezterm_bin_path()); + ui.output_str(&format!("Running: {}\n", cmd)); + chan.exec(&cmd)?; + let creds = match Pdu::decode(chan)?.pdu { + Pdu::GetTlsCredsResponse(creds) => creds, + _ => bail!("unexpected response to tlscreds"), + }; + + // Save the credentials to disk, as that is currently the easiest + // way to get them into the tls impl. Ideally we'd keep these entirely + // in memory. + std::fs::write(&self.tls_creds_ca_path()?, creds.ca_cert_pem.as_bytes())?; + std::fs::write( + &self.tls_creds_cert_path()?, + creds.client_cert_pem.as_bytes(), + )?; + self.tls_creds.replace(creds); + } + } + + let cert_file = match tls_client.pem_cert.clone() { + Some(cert) => cert, + None if self.tls_creds.is_some() => self.tls_creds_cert_path()?, + None => bail!("no pem_cert configured"), + }; + let key_file = match tls_client.pem_private_key.clone() { + Some(key) => key, + None if self.tls_creds.is_some() => self.tls_creds_cert_path()?, + None => bail!("no pem_private_key configured"), + }; + let identity = IdentitySource::PemFiles { - key: tls_client - .pem_private_key - .as_ref() - .ok_or_else(|| anyhow!("missing pem_private_key config value"))? - .into(), - cert: tls_client.pem_cert.clone(), + key: key_file.into(), + cert: Some(cert_file), chain: tls_client.pem_ca.clone(), }; diff --git a/src/server/listener/mod.rs b/src/server/listener/mod.rs index 7d4ce8faa..113a19316 100644 --- a/src/server/listener/mod.rs +++ b/src/server/listener/mod.rs @@ -20,9 +20,9 @@ lazy_static::lazy_static! { static ref PKI: pki::Pki = pki::Pki::init().expect("failed to initialize PKI"); } -#[cfg(not(any(feature = "openssl", unix)))] +#[cfg(not(feature = "enable_openssl"))] use not_ossl as tls_impl; -#[cfg(any(feature = "openssl", unix))] +#[cfg(feature = "enable_openssl")] use ossl as tls_impl; #[derive(Debug)] diff --git a/src/server/listener/not_ossl.rs b/src/server/listener/not_ossl.rs index 1bb22b47b..96d44cbf8 100644 --- a/src/server/listener/not_ossl.rs +++ b/src/server/listener/not_ossl.rs @@ -1,4 +1,5 @@ -#![cfg(not(any(feature = "openssl", unix)))] +#![allow(unused)] + use super::*; use native_tls::TlsAcceptor; use std::convert::TryInto; @@ -72,5 +73,8 @@ pub fn pem_files_to_identity( _cert: Option, _chain: Option, ) -> anyhow::Result { - bail!("recompile wezterm using --features openssl") + // This is a pain point in native_tls. + // Once https://github.com/sfackler/rust-native-tls/pull/147 + // is done this might be doable in terms of pem files + bail!("recompile wezterm using --features enable_openssl") } diff --git a/src/server/listener/ossl.rs b/src/server/listener/ossl.rs index 17b578882..113504f7a 100644 --- a/src/server/listener/ossl.rs +++ b/src/server/listener/ossl.rs @@ -1,4 +1,4 @@ -#![cfg(any(feature = "openssl", unix))] +#![cfg(feature = "enable_openssl")] use super::*; use openssl::pkcs12::Pkcs12; use openssl::pkey::PKey; diff --git a/src/server/pollable.rs b/src/server/pollable.rs index 56d7daf35..b2ef8d069 100644 --- a/src/server/pollable.rs +++ b/src/server/pollable.rs @@ -30,7 +30,7 @@ impl ReadAndWrite for native_tls::TlsStream { } } -#[cfg(any(feature = "openssl", unix))] +#[cfg(feature = "enable_openssl")] impl ReadAndWrite for openssl::ssl::SslStream { fn set_non_blocking(&self, non_blocking: bool) -> anyhow::Result<()> { self.get_ref().set_nonblocking(non_blocking)?; @@ -134,7 +134,7 @@ impl AsPollFd for native_tls::TlsStream { } } -#[cfg(any(feature = "openssl", unix))] +#[cfg(feature = "enable_openssl")] impl AsPollFd for openssl::ssl::SslStream { fn as_poll_fd(&self) -> pollfd { self.get_ref().as_socket_descriptor().as_poll_fd()