mirror of
https://github.com/wez/wezterm.git
synced 2024-12-29 16:42:13 +03:00
refactor: move listener and tls bits into own files
This commit is contained in:
parent
1d9678706c
commit
22622219c7
@ -29,6 +29,14 @@ use term::terminal::Clipboard;
|
|||||||
use term::StableRowIndex;
|
use term::StableRowIndex;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
mod not_ossl;
|
||||||
|
mod ossl;
|
||||||
|
|
||||||
|
#[cfg(not(any(feature = "openssl", unix)))]
|
||||||
|
use not_ossl as tls_impl;
|
||||||
|
#[cfg(any(feature = "openssl", unix))]
|
||||||
|
use ossl as tls_impl;
|
||||||
|
|
||||||
struct LocalListener {
|
struct LocalListener {
|
||||||
listener: UnixListener,
|
listener: UnixListener,
|
||||||
}
|
}
|
||||||
@ -78,61 +86,6 @@ pub fn read_bytes<T: AsRef<Path>>(path: T) -> anyhow::Result<Vec<u8>> {
|
|||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "openssl", unix))]
|
|
||||||
fn pem_files_to_identity(
|
|
||||||
key: PathBuf,
|
|
||||||
cert: Option<PathBuf>,
|
|
||||||
chain: Option<PathBuf>,
|
|
||||||
) -> anyhow::Result<Identity> {
|
|
||||||
// This is a bit of a redundant dance around;
|
|
||||||
// the native_tls interface only allows for pkcs12
|
|
||||||
// encoded identity information, but in my use case
|
|
||||||
// I only have pem encoded identity information.
|
|
||||||
// We can use openssl to convert the data to pkcs12
|
|
||||||
// so that we can then pass it on using the Identity
|
|
||||||
// type that native_tls requires.
|
|
||||||
use openssl::pkcs12::Pkcs12;
|
|
||||||
use openssl::pkey::PKey;
|
|
||||||
use openssl::x509::X509;
|
|
||||||
let key_bytes = read_bytes(&key)?;
|
|
||||||
let pkey = PKey::private_key_from_pem(&key_bytes)?;
|
|
||||||
|
|
||||||
let cert_bytes = read_bytes(cert.as_ref().unwrap_or(&key))?;
|
|
||||||
let x509_cert = X509::from_pem(&cert_bytes)?;
|
|
||||||
|
|
||||||
let chain_bytes = read_bytes(chain.as_ref().unwrap_or(&key))?;
|
|
||||||
let x509_chain = X509::stack_from_pem(&chain_bytes)?;
|
|
||||||
|
|
||||||
let password = "internal";
|
|
||||||
let mut ca_stack = openssl::stack::Stack::new()?;
|
|
||||||
for ca in x509_chain.into_iter() {
|
|
||||||
ca_stack.push(ca)?;
|
|
||||||
}
|
|
||||||
let mut builder = Pkcs12::builder();
|
|
||||||
builder.ca(ca_stack);
|
|
||||||
let pkcs12 = builder.build(password, "", &pkey, &x509_cert)?;
|
|
||||||
|
|
||||||
let der = pkcs12.to_der()?;
|
|
||||||
Identity::from_pkcs12(&der, password).with_context(|| {
|
|
||||||
format!(
|
|
||||||
"error creating identity from pkcs12 generated \
|
|
||||||
from PemFiles {}, {:?}, {:?}",
|
|
||||||
key.display(),
|
|
||||||
cert,
|
|
||||||
chain,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(feature = "openssl", unix)))]
|
|
||||||
fn pem_files_to_identity(
|
|
||||||
_key: PathBuf,
|
|
||||||
_cert: Option<PathBuf>,
|
|
||||||
_chain: Option<PathBuf>,
|
|
||||||
) -> anyhow::Result<Identity> {
|
|
||||||
bail!("recompile wezterm using --features openssl")
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<IdentitySource> for Identity {
|
impl TryFrom<IdentitySource> for Identity {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
@ -144,233 +97,12 @@ impl TryFrom<IdentitySource> for Identity {
|
|||||||
.with_context(|| format!("error loading pkcs12 file '{}'", path.display()))
|
.with_context(|| format!("error loading pkcs12 file '{}'", path.display()))
|
||||||
}
|
}
|
||||||
IdentitySource::PemFiles { key, cert, chain } => {
|
IdentitySource::PemFiles { key, cert, chain } => {
|
||||||
pem_files_to_identity(key, cert, chain)
|
tls_impl::pem_files_to_identity(key, cert, chain)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(feature = "openssl", unix)))]
|
|
||||||
mod not_ossl {
|
|
||||||
use super::*;
|
|
||||||
use native_tls::TlsAcceptor;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
struct NetListener {
|
|
||||||
acceptor: Arc<TlsAcceptor>,
|
|
||||||
listener: TcpListener,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetListener {
|
|
||||||
pub fn new(listener: TcpListener, acceptor: TlsAcceptor) -> Self {
|
|
||||||
Self {
|
|
||||||
listener,
|
|
||||||
acceptor: Arc::new(acceptor),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(&mut self) {
|
|
||||||
for stream in self.listener.incoming() {
|
|
||||||
match stream {
|
|
||||||
Ok(stream) => {
|
|
||||||
stream.set_nodelay(true).ok();
|
|
||||||
let acceptor = self.acceptor.clone();
|
|
||||||
|
|
||||||
match acceptor.accept(stream) {
|
|
||||||
Ok(stream) => {
|
|
||||||
spawn_into_main_thread(async move {
|
|
||||||
let mut session = ClientSession::new(stream);
|
|
||||||
thread::spawn(move || session.run());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("failed TlsAcceptor: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!("accept failed: {}", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spawn_tls_listener(tls_server: &TlsDomainServer) -> anyhow::Result<()> {
|
|
||||||
let identity = IdentitySource::PemFiles {
|
|
||||||
key: tls_server
|
|
||||||
.pem_private_key
|
|
||||||
.as_ref()
|
|
||||||
.ok_or_else(|| anyhow!("missing pem_private_key config value"))?
|
|
||||||
.into(),
|
|
||||||
cert: tls_server.pem_cert.clone(),
|
|
||||||
chain: tls_server.pem_ca.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut net_listener = NetListener::new(
|
|
||||||
TcpListener::bind(&tls_server.bind_address).with_context(|| {
|
|
||||||
format!("error binding to bind_address {}", tls_server.bind_address,)
|
|
||||||
})?,
|
|
||||||
TlsAcceptor::new(identity.try_into()?)?,
|
|
||||||
);
|
|
||||||
thread::spawn(move || {
|
|
||||||
net_listener.run();
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(feature = "openssl", unix))]
|
|
||||||
mod ossl {
|
|
||||||
use super::*;
|
|
||||||
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslStream, SslVerifyMode};
|
|
||||||
use openssl::x509::X509;
|
|
||||||
|
|
||||||
struct OpenSSLNetListener {
|
|
||||||
acceptor: Arc<SslAcceptor>,
|
|
||||||
listener: TcpListener,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OpenSSLNetListener {
|
|
||||||
pub fn new(listener: TcpListener, acceptor: SslAcceptor) -> Self {
|
|
||||||
Self {
|
|
||||||
listener,
|
|
||||||
acceptor: Arc::new(acceptor),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Authenticates the peer.
|
|
||||||
/// The requirements are:
|
|
||||||
/// * The peer must have a certificate
|
|
||||||
/// * The peer certificate must be trusted
|
|
||||||
/// * The peer certificate must include a CN string that is
|
|
||||||
/// either an exact match for the unix username of the
|
|
||||||
/// user running this mux server instance, or must match
|
|
||||||
/// a special encoded prefix set up by a proprietary PKI
|
|
||||||
/// infrastructure in an environment used by the author.
|
|
||||||
fn verify_peer_cert<T>(stream: &SslStream<T>) -> anyhow::Result<()> {
|
|
||||||
let cert = stream
|
|
||||||
.ssl()
|
|
||||||
.peer_certificate()
|
|
||||||
.ok_or_else(|| anyhow!("no peer cert"))?;
|
|
||||||
let subject = cert.subject_name();
|
|
||||||
let cn = subject
|
|
||||||
.entries_by_nid(openssl::nid::Nid::COMMONNAME)
|
|
||||||
.next()
|
|
||||||
.ok_or_else(|| anyhow!("cert has no CN"))?;
|
|
||||||
let cn_str = cn.data().as_utf8()?.to_string();
|
|
||||||
|
|
||||||
let wanted_unix_name = std::env::var("USER")?;
|
|
||||||
|
|
||||||
if wanted_unix_name == cn_str {
|
|
||||||
log::info!(
|
|
||||||
"Peer certificate CN `{}` == $USER `{}`",
|
|
||||||
cn_str,
|
|
||||||
wanted_unix_name
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
// Some environments that are used by the author of this
|
|
||||||
// program encode the CN in the form `user:unixname/DATA`
|
|
||||||
let maybe_encoded = format!("user:{}/", wanted_unix_name);
|
|
||||||
if cn_str.starts_with(&maybe_encoded) {
|
|
||||||
log::info!(
|
|
||||||
"Peer certificate CN `{}` matches $USER `{}`",
|
|
||||||
cn_str,
|
|
||||||
wanted_unix_name
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
bail!("CN `{}` did not match $USER `{}`", cn_str, wanted_unix_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(&mut self) {
|
|
||||||
for stream in self.listener.incoming() {
|
|
||||||
match stream {
|
|
||||||
Ok(stream) => {
|
|
||||||
stream.set_nodelay(true).ok();
|
|
||||||
let acceptor = self.acceptor.clone();
|
|
||||||
|
|
||||||
match acceptor.accept(stream) {
|
|
||||||
Ok(stream) => {
|
|
||||||
if let Err(err) = Self::verify_peer_cert(&stream) {
|
|
||||||
error!("problem with peer cert: {}", err);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
spawn_into_main_thread(async move {
|
|
||||||
let mut session = ClientSession::new(stream);
|
|
||||||
thread::spawn(move || session.run());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("failed TlsAcceptor: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!("accept failed: {}", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spawn_tls_listener(tls_server: &TlsDomainServer) -> Result<(), Error> {
|
|
||||||
openssl::init();
|
|
||||||
|
|
||||||
let mut acceptor = SslAcceptor::mozilla_modern(SslMethod::tls())?;
|
|
||||||
|
|
||||||
if let Some(cert_file) = tls_server.pem_cert.as_ref() {
|
|
||||||
acceptor.set_certificate_file(cert_file, SslFiletype::PEM)?;
|
|
||||||
}
|
|
||||||
if let Some(chain_file) = tls_server.pem_ca.as_ref() {
|
|
||||||
acceptor.set_certificate_chain_file(chain_file)?;
|
|
||||||
}
|
|
||||||
if let Some(key_file) = tls_server.pem_private_key.as_ref() {
|
|
||||||
acceptor.set_private_key_file(key_file, SslFiletype::PEM)?;
|
|
||||||
}
|
|
||||||
fn load_cert(name: &Path) -> anyhow::Result<X509> {
|
|
||||||
let cert_bytes = read_bytes(name)?;
|
|
||||||
log::trace!("loaded {}", name.display());
|
|
||||||
Ok(X509::from_pem(&cert_bytes)?)
|
|
||||||
}
|
|
||||||
for name in &tls_server.pem_root_certs {
|
|
||||||
if name.is_dir() {
|
|
||||||
for entry in std::fs::read_dir(name)? {
|
|
||||||
if let Ok(cert) = load_cert(&entry?.path()) {
|
|
||||||
acceptor.cert_store_mut().add_cert(cert).ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
acceptor.cert_store_mut().add_cert(load_cert(name)?)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
acceptor.set_verify(SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT);
|
|
||||||
|
|
||||||
let acceptor = acceptor.build();
|
|
||||||
|
|
||||||
let mut net_listener = OpenSSLNetListener::new(
|
|
||||||
TcpListener::bind(&tls_server.bind_address).with_context(|| {
|
|
||||||
format!(
|
|
||||||
"error binding to mux_server_bind_address {}",
|
|
||||||
tls_server.bind_address,
|
|
||||||
)
|
|
||||||
})?,
|
|
||||||
acceptor,
|
|
||||||
);
|
|
||||||
thread::spawn(move || {
|
|
||||||
net_listener.run();
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ClientSession<S: ReadAndWrite> {
|
pub struct ClientSession<S: ReadAndWrite> {
|
||||||
stream: S,
|
stream: S,
|
||||||
to_write_rx: PollableReceiver<DecodedPdu>,
|
to_write_rx: PollableReceiver<DecodedPdu>,
|
||||||
@ -980,16 +712,6 @@ fn safely_create_sock_path(unix_dom: &UnixDomain) -> Result<UnixListener, Error>
|
|||||||
.with_context(|| format!("Failed to bind to {}", sock_path.display()))
|
.with_context(|| format!("Failed to bind to {}", sock_path.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "openssl", unix))]
|
|
||||||
fn spawn_tls_listener(tls_server: &TlsDomainServer) -> anyhow::Result<()> {
|
|
||||||
ossl::spawn_tls_listener(tls_server)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(feature = "openssl", unix)))]
|
|
||||||
fn spawn_tls_listener(tls_server: &TlsDomainServer) -> anyhow::Result<()> {
|
|
||||||
not_ossl::spawn_tls_listener(tls_server)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spawn_listener() -> anyhow::Result<()> {
|
pub fn spawn_listener() -> anyhow::Result<()> {
|
||||||
let config = configuration();
|
let config = configuration();
|
||||||
for unix_dom in &config.unix_domains {
|
for unix_dom in &config.unix_domains {
|
||||||
@ -1000,7 +722,7 @@ pub fn spawn_listener() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for tls_server in &config.tls_servers {
|
for tls_server in &config.tls_servers {
|
||||||
spawn_tls_listener(tls_server)?;
|
tls_impl::spawn_tls_listener(tls_server)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
76
src/server/listener/not_ossl.rs
Normal file
76
src/server/listener/not_ossl.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#![cfg(not(any(feature = "openssl", unix)))]
|
||||||
|
use super::*;
|
||||||
|
use native_tls::TlsAcceptor;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
struct NetListener {
|
||||||
|
acceptor: Arc<TlsAcceptor>,
|
||||||
|
listener: TcpListener,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetListener {
|
||||||
|
pub fn new(listener: TcpListener, acceptor: TlsAcceptor) -> Self {
|
||||||
|
Self {
|
||||||
|
listener,
|
||||||
|
acceptor: Arc::new(acceptor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self) {
|
||||||
|
for stream in self.listener.incoming() {
|
||||||
|
match stream {
|
||||||
|
Ok(stream) => {
|
||||||
|
stream.set_nodelay(true).ok();
|
||||||
|
let acceptor = self.acceptor.clone();
|
||||||
|
|
||||||
|
match acceptor.accept(stream) {
|
||||||
|
Ok(stream) => {
|
||||||
|
spawn_into_main_thread(async move {
|
||||||
|
let mut session = ClientSession::new(stream);
|
||||||
|
thread::spawn(move || session.run());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed TlsAcceptor: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("accept failed: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn_tls_listener(tls_server: &TlsDomainServer) -> anyhow::Result<()> {
|
||||||
|
let identity = IdentitySource::PemFiles {
|
||||||
|
key: tls_server
|
||||||
|
.pem_private_key
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| anyhow!("missing pem_private_key config value"))?
|
||||||
|
.into(),
|
||||||
|
cert: tls_server.pem_cert.clone(),
|
||||||
|
chain: tls_server.pem_ca.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut net_listener = NetListener::new(
|
||||||
|
TcpListener::bind(&tls_server.bind_address).with_context(|| {
|
||||||
|
format!("error binding to bind_address {}", tls_server.bind_address,)
|
||||||
|
})?,
|
||||||
|
TlsAcceptor::new(identity.try_into()?)?,
|
||||||
|
);
|
||||||
|
thread::spawn(move || {
|
||||||
|
net_listener.run();
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pem_files_to_identity(
|
||||||
|
_key: PathBuf,
|
||||||
|
_cert: Option<PathBuf>,
|
||||||
|
_chain: Option<PathBuf>,
|
||||||
|
) -> anyhow::Result<Identity> {
|
||||||
|
bail!("recompile wezterm using --features openssl")
|
||||||
|
}
|
191
src/server/listener/ossl.rs
Normal file
191
src/server/listener/ossl.rs
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
#![cfg(any(feature = "openssl", unix))]
|
||||||
|
use super::*;
|
||||||
|
use openssl::pkcs12::Pkcs12;
|
||||||
|
use openssl::pkey::PKey;
|
||||||
|
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslStream, SslVerifyMode};
|
||||||
|
use openssl::x509::X509;
|
||||||
|
|
||||||
|
struct OpenSSLNetListener {
|
||||||
|
acceptor: Arc<SslAcceptor>,
|
||||||
|
listener: TcpListener,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OpenSSLNetListener {
|
||||||
|
pub fn new(listener: TcpListener, acceptor: SslAcceptor) -> Self {
|
||||||
|
Self {
|
||||||
|
listener,
|
||||||
|
acceptor: Arc::new(acceptor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Authenticates the peer.
|
||||||
|
/// The requirements are:
|
||||||
|
/// * The peer must have a certificate
|
||||||
|
/// * The peer certificate must be trusted
|
||||||
|
/// * The peer certificate must include a CN string that is
|
||||||
|
/// either an exact match for the unix username of the
|
||||||
|
/// user running this mux server instance, or must match
|
||||||
|
/// a special encoded prefix set up by a proprietary PKI
|
||||||
|
/// infrastructure in an environment used by the author.
|
||||||
|
fn verify_peer_cert<T>(stream: &SslStream<T>) -> anyhow::Result<()> {
|
||||||
|
let cert = stream
|
||||||
|
.ssl()
|
||||||
|
.peer_certificate()
|
||||||
|
.ok_or_else(|| anyhow!("no peer cert"))?;
|
||||||
|
let subject = cert.subject_name();
|
||||||
|
let cn = subject
|
||||||
|
.entries_by_nid(openssl::nid::Nid::COMMONNAME)
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| anyhow!("cert has no CN"))?;
|
||||||
|
let cn_str = cn.data().as_utf8()?.to_string();
|
||||||
|
|
||||||
|
let wanted_unix_name = std::env::var("USER")?;
|
||||||
|
|
||||||
|
if wanted_unix_name == cn_str {
|
||||||
|
log::info!(
|
||||||
|
"Peer certificate CN `{}` == $USER `{}`",
|
||||||
|
cn_str,
|
||||||
|
wanted_unix_name
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// Some environments that are used by the author of this
|
||||||
|
// program encode the CN in the form `user:unixname/DATA`
|
||||||
|
let maybe_encoded = format!("user:{}/", wanted_unix_name);
|
||||||
|
if cn_str.starts_with(&maybe_encoded) {
|
||||||
|
log::info!(
|
||||||
|
"Peer certificate CN `{}` matches $USER `{}`",
|
||||||
|
cn_str,
|
||||||
|
wanted_unix_name
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
bail!("CN `{}` did not match $USER `{}`", cn_str, wanted_unix_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self) {
|
||||||
|
for stream in self.listener.incoming() {
|
||||||
|
match stream {
|
||||||
|
Ok(stream) => {
|
||||||
|
stream.set_nodelay(true).ok();
|
||||||
|
let acceptor = self.acceptor.clone();
|
||||||
|
|
||||||
|
match acceptor.accept(stream) {
|
||||||
|
Ok(stream) => {
|
||||||
|
if let Err(err) = Self::verify_peer_cert(&stream) {
|
||||||
|
error!("problem with peer cert: {}", err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
spawn_into_main_thread(async move {
|
||||||
|
let mut session = ClientSession::new(stream);
|
||||||
|
thread::spawn(move || session.run());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed TlsAcceptor: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("accept failed: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn_tls_listener(tls_server: &TlsDomainServer) -> Result<(), Error> {
|
||||||
|
openssl::init();
|
||||||
|
|
||||||
|
let mut acceptor = SslAcceptor::mozilla_modern(SslMethod::tls())?;
|
||||||
|
|
||||||
|
if let Some(cert_file) = tls_server.pem_cert.as_ref() {
|
||||||
|
acceptor.set_certificate_file(cert_file, SslFiletype::PEM)?;
|
||||||
|
}
|
||||||
|
if let Some(chain_file) = tls_server.pem_ca.as_ref() {
|
||||||
|
acceptor.set_certificate_chain_file(chain_file)?;
|
||||||
|
}
|
||||||
|
if let Some(key_file) = tls_server.pem_private_key.as_ref() {
|
||||||
|
acceptor.set_private_key_file(key_file, SslFiletype::PEM)?;
|
||||||
|
}
|
||||||
|
fn load_cert(name: &Path) -> anyhow::Result<X509> {
|
||||||
|
let cert_bytes = read_bytes(name)?;
|
||||||
|
log::trace!("loaded {}", name.display());
|
||||||
|
Ok(X509::from_pem(&cert_bytes)?)
|
||||||
|
}
|
||||||
|
for name in &tls_server.pem_root_certs {
|
||||||
|
if name.is_dir() {
|
||||||
|
for entry in std::fs::read_dir(name)? {
|
||||||
|
if let Ok(cert) = load_cert(&entry?.path()) {
|
||||||
|
acceptor.cert_store_mut().add_cert(cert).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acceptor.cert_store_mut().add_cert(load_cert(name)?)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptor.set_verify(SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT);
|
||||||
|
|
||||||
|
let acceptor = acceptor.build();
|
||||||
|
|
||||||
|
let mut net_listener = OpenSSLNetListener::new(
|
||||||
|
TcpListener::bind(&tls_server.bind_address).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"error binding to mux_server_bind_address {}",
|
||||||
|
tls_server.bind_address,
|
||||||
|
)
|
||||||
|
})?,
|
||||||
|
acceptor,
|
||||||
|
);
|
||||||
|
thread::spawn(move || {
|
||||||
|
net_listener.run();
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pem_files_to_identity(
|
||||||
|
key: PathBuf,
|
||||||
|
cert: Option<PathBuf>,
|
||||||
|
chain: Option<PathBuf>,
|
||||||
|
) -> anyhow::Result<Identity> {
|
||||||
|
// This is a bit of a redundant dance around;
|
||||||
|
// the native_tls interface only allows for pkcs12
|
||||||
|
// encoded identity information, but in my use case
|
||||||
|
// I only have pem encoded identity information.
|
||||||
|
// We can use openssl to convert the data to pkcs12
|
||||||
|
// so that we can then pass it on using the Identity
|
||||||
|
// type that native_tls requires.
|
||||||
|
let key_bytes = read_bytes(&key)?;
|
||||||
|
let pkey = PKey::private_key_from_pem(&key_bytes)?;
|
||||||
|
|
||||||
|
let cert_bytes = read_bytes(cert.as_ref().unwrap_or(&key))?;
|
||||||
|
let x509_cert = X509::from_pem(&cert_bytes)?;
|
||||||
|
|
||||||
|
let chain_bytes = read_bytes(chain.as_ref().unwrap_or(&key))?;
|
||||||
|
let x509_chain = X509::stack_from_pem(&chain_bytes)?;
|
||||||
|
|
||||||
|
let password = "internal";
|
||||||
|
let mut ca_stack = openssl::stack::Stack::new()?;
|
||||||
|
for ca in x509_chain.into_iter() {
|
||||||
|
ca_stack.push(ca)?;
|
||||||
|
}
|
||||||
|
let mut builder = Pkcs12::builder();
|
||||||
|
builder.ca(ca_stack);
|
||||||
|
let pkcs12 = builder.build(password, "", &pkey, &x509_cert)?;
|
||||||
|
|
||||||
|
let der = pkcs12.to_der()?;
|
||||||
|
Identity::from_pkcs12(&der, password).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"error creating identity from pkcs12 generated \
|
||||||
|
from PemFiles {}, {:?}, {:?}",
|
||||||
|
key.display(),
|
||||||
|
cert,
|
||||||
|
chain,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user