pub use boring::ssl::{ShutdownResult, SslVerifyMode};
use std::os::unix::prelude::AsRawFd;
use boring::ssl::{ErrorCode, Ssl, SslFiletype, SslMethod, SslStream};
use boring::x509::X509;
use crate::*;
#[derive(PartialEq)]
enum TlsState {
Handshaking,
Negotiated,
}
pub struct TlsTcpStream {
inner: SslStream<TcpStream>,
state: TlsState,
}
impl AsRawFd for TlsTcpStream {
fn as_raw_fd(&self) -> i32 {
self.inner.get_ref().as_raw_fd()
}
}
impl TlsTcpStream {
pub fn set_nodelay(&mut self, nodelay: bool) -> Result<()> {
self.inner.get_mut().set_nodelay(nodelay)
}
pub fn is_handshaking(&self) -> bool {
self.state == TlsState::Handshaking
}
pub fn interest(&self) -> Interest {
if self.is_handshaking() {
Interest::READABLE.add(Interest::WRITABLE)
} else {
Interest::READABLE
}
}
pub fn do_handshake(&mut self) -> Result<()> {
if self.is_handshaking() {
let ptr = self.inner.ssl().as_ptr();
let ret = unsafe { boring_sys::SSL_do_handshake(ptr) };
if ret > 0 {
STREAM_HANDSHAKE.increment();
self.state = TlsState::Negotiated;
Ok(())
} else {
let code = unsafe { ErrorCode::from_raw(boring_sys::SSL_get_error(ptr, ret)) };
match code {
ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => {
Err(Error::from(ErrorKind::WouldBlock))
}
_ => {
STREAM_HANDSHAKE.increment();
STREAM_HANDSHAKE_EX.increment();
Err(Error::new(ErrorKind::Other, "handshake failed"))
}
}
}
} else {
Ok(())
}
}
pub fn shutdown(&mut self) -> Result<ShutdownResult> {
self.inner
.shutdown()
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
}
}
impl Debug for TlsTcpStream {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(f, "{:?}", self.inner.get_ref())
}
}
impl Read for TlsTcpStream {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
if self.is_handshaking() {
Err(Error::new(
ErrorKind::WouldBlock,
"read on handshaking session would block",
))
} else {
self.inner.read(buf)
}
}
}
impl Write for TlsTcpStream {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
if self.is_handshaking() {
Err(Error::new(
ErrorKind::WouldBlock,
"write on handshaking session would block",
))
} else {
self.inner.write(buf)
}
}
fn flush(&mut self) -> Result<()> {
if self.is_handshaking() {
Err(Error::new(
ErrorKind::WouldBlock,
"flush on handshaking session would block",
))
} else {
self.inner.flush()
}
}
}
impl event::Source for TlsTcpStream {
fn register(&mut self, registry: &Registry, token: Token, interest: Interest) -> Result<()> {
self.inner.get_mut().register(registry, token, interest)
}
fn reregister(
&mut self,
registry: &mio::Registry,
token: mio::Token,
interest: mio::Interest,
) -> Result<()> {
self.inner.get_mut().reregister(registry, token, interest)
}
fn deregister(&mut self, registry: &mio::Registry) -> Result<()> {
self.inner.get_mut().deregister(registry)
}
}
pub struct TlsTcpAcceptor {
inner: boring::ssl::SslContext,
}
impl TlsTcpAcceptor {
pub fn mozilla_intermediate_v5() -> Result<TlsTcpAcceptorBuilder> {
let inner = boring::ssl::SslAcceptor::mozilla_intermediate_v5(SslMethod::tls_server())
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
Ok(TlsTcpAcceptorBuilder {
inner,
ca_file: None,
certificate_file: None,
certificate_chain_file: None,
private_key_file: None,
})
}
pub fn accept(&self, stream: TcpStream) -> Result<TlsTcpStream> {
let ssl = Ssl::new(&self.inner)?;
let stream = unsafe { SslStream::from_raw_parts(ssl.into_ptr(), stream) };
let ret = unsafe { boring_sys::SSL_accept(stream.ssl().as_ptr()) };
if ret > 0 {
Ok(TlsTcpStream {
inner: stream,
state: TlsState::Negotiated,
})
} else {
let code = unsafe {
ErrorCode::from_raw(boring_sys::SSL_get_error(stream.ssl().as_ptr(), ret))
};
match code {
ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => Ok(TlsTcpStream {
inner: stream,
state: TlsState::Handshaking,
}),
_ => Err(Error::new(ErrorKind::Other, "handshake failed")),
}
}
}
}
pub struct TlsTcpAcceptorBuilder {
inner: boring::ssl::SslAcceptorBuilder,
ca_file: Option<PathBuf>,
certificate_file: Option<PathBuf>,
certificate_chain_file: Option<PathBuf>,
private_key_file: Option<PathBuf>,
}
impl TlsTcpAcceptorBuilder {
pub fn build(mut self) -> Result<TlsTcpAcceptor> {
if let Some(f) = self.ca_file {
self.inner.set_ca_file(f.clone()).map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed to load CA file: {}\n{}", f.display(), e),
)
})?;
}
if let Some(f) = self.private_key_file {
self.inner
.set_private_key_file(f.clone(), SslFiletype::PEM)
.map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed to load private key file: {}\n{}", f.display(), e),
)
})?;
} else {
return Err(Error::new(ErrorKind::Other, "no private key file provided"));
}
match (self.certificate_chain_file, self.certificate_file) {
(Some(chain), Some(cert)) => {
self.inner
.set_certificate_file(cert.clone(), SslFiletype::PEM)
.map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed to load certificate file: {}\n{}", cert.display(), e),
)
})?;
let pem = std::fs::read(chain.clone()).map_err(|e| {
Error::new(
ErrorKind::Other,
format!(
"failed to load certificate chain file: {}\n{}",
chain.display(),
e
),
)
})?;
let cert_chain = X509::stack_from_pem(&pem).map_err(|e| {
Error::new(
ErrorKind::Other,
format!(
"failed to load certificate chain file: {}\n{}",
chain.display(),
e
),
)
})?;
for cert in cert_chain {
self.inner.add_extra_chain_cert(cert).map_err(|e| {
Error::new(
ErrorKind::Other,
format!(
"bad certificate in certificate chain file: {}\n{}",
chain.display(),
e
),
)
})?;
}
}
(Some(chain), None) => {
self.inner
.set_certificate_chain_file(chain.clone())
.map_err(|e| {
Error::new(
ErrorKind::Other,
format!(
"failed to load certificate chain file: {}\n{}",
chain.display(),
e
),
)
})?;
}
(None, Some(cert)) => {
self.inner
.set_certificate_file(cert.clone(), SslFiletype::PEM)
.map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed to load certificate file: {}\n{}", cert.display(), e),
)
})?;
}
(None, None) => {
return Err(Error::new(
ErrorKind::Other,
"no certificate file or certificate chain file provided",
));
}
}
let inner = self.inner.build().into_context();
Ok(TlsTcpAcceptor { inner })
}
pub fn verify(mut self, mode: SslVerifyMode) -> Self {
self.inner.set_verify(mode);
self
}
pub fn ca_file<P: AsRef<Path>>(mut self, file: P) -> Self {
self.ca_file = Some(file.as_ref().to_path_buf());
self
}
pub fn certificate_file<P: AsRef<Path>>(mut self, file: P) -> Self {
self.certificate_file = Some(file.as_ref().to_path_buf());
self
}
pub fn certificate_chain_file<P: AsRef<Path>>(mut self, file: P) -> Self {
self.certificate_chain_file = Some(file.as_ref().to_path_buf());
self
}
pub fn private_key_file<P: AsRef<Path>>(mut self, file: P) -> Self {
self.private_key_file = Some(file.as_ref().to_path_buf());
self
}
}
#[allow(dead_code)]
pub struct TlsTcpConnector {
inner: boring::ssl::SslContext,
}
impl TlsTcpConnector {
pub fn builder() -> Result<TlsTcpConnectorBuilder> {
let inner = boring::ssl::SslConnector::builder(SslMethod::tls_client())
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
Ok(TlsTcpConnectorBuilder {
inner,
ca_file: None,
certificate_file: None,
certificate_chain_file: None,
private_key_file: None,
})
}
pub fn connect<A: ToSocketAddrs>(&self, addr: A) -> Result<TlsTcpStream> {
let addrs: Vec<SocketAddr> = addr.to_socket_addrs()?.collect();
let mut s = Err(Error::new(ErrorKind::Other, "failed to resolve"));
for addr in addrs {
s = TcpStream::connect(addr);
if s.is_ok() {
break;
}
}
let ssl = Ssl::new(&self.inner)?;
let stream = unsafe { SslStream::from_raw_parts(ssl.into_ptr(), s?) };
let ret = unsafe { boring_sys::SSL_connect(stream.ssl().as_ptr()) };
if ret > 0 {
Ok(TlsTcpStream {
inner: stream,
state: TlsState::Negotiated,
})
} else {
let code = unsafe {
ErrorCode::from_raw(boring_sys::SSL_get_error(stream.ssl().as_ptr(), ret))
};
match code {
ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => Ok(TlsTcpStream {
inner: stream,
state: TlsState::Handshaking,
}),
_ => Err(Error::new(ErrorKind::Other, "handshake failed")),
}
}
}
}
pub struct TlsTcpConnectorBuilder {
inner: boring::ssl::SslConnectorBuilder,
ca_file: Option<PathBuf>,
certificate_file: Option<PathBuf>,
certificate_chain_file: Option<PathBuf>,
private_key_file: Option<PathBuf>,
}
impl TlsTcpConnectorBuilder {
pub fn build(mut self) -> Result<TlsTcpConnector> {
if let Some(f) = self.ca_file {
self.inner.set_ca_file(f).map_err(|e| {
Error::new(ErrorKind::Other, format!("failed to load CA file: {e}"))
})?;
}
if let Some(f) = self.private_key_file {
self.inner
.set_private_key_file(f, SslFiletype::PEM)
.map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed to load private key file: {e}"),
)
})?;
} else {
return Err(Error::new(ErrorKind::Other, "no private key file provided"));
}
match (self.certificate_chain_file, self.certificate_file) {
(Some(chain), Some(cert)) => {
self.inner
.set_certificate_file(cert, SslFiletype::PEM)
.map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed to load certificate file: {e}"),
)
})?;
let pem = std::fs::read(chain).map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed to load certificate chain file: {e}"),
)
})?;
let chain = X509::stack_from_pem(&pem).map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed to load certificate chain file: {e}"),
)
})?;
for cert in chain {
self.inner.add_extra_chain_cert(cert).map_err(|e| {
Error::new(
ErrorKind::Other,
format!("bad certificate in certificate chain file: {e}"),
)
})?;
}
}
(Some(chain), None) => {
self.inner.set_certificate_chain_file(chain).map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed to load certificate chain file: {e}"),
)
})?;
}
(None, Some(cert)) => {
self.inner
.set_certificate_file(cert, SslFiletype::PEM)
.map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed to load certificate file: {e}"),
)
})?;
}
(None, None) => {
return Err(Error::new(
ErrorKind::Other,
"no certificate file or certificate chain file provided",
));
}
}
let inner = self.inner.build().into_context();
Ok(TlsTcpConnector { inner })
}
pub fn verify(mut self, mode: SslVerifyMode) -> Self {
self.inner.set_verify(mode);
self
}
pub fn ca_file<P: AsRef<Path>>(mut self, file: P) -> Self {
self.ca_file = Some(file.as_ref().to_path_buf());
self
}
pub fn certificate_file<P: AsRef<Path>>(mut self, file: P) -> Self {
self.certificate_file = Some(file.as_ref().to_path_buf());
self
}
pub fn certificate_chain_file<P: AsRef<Path>>(mut self, file: P) -> Self {
self.certificate_chain_file = Some(file.as_ref().to_path_buf());
self
}
pub fn private_key_file<P: AsRef<Path>>(mut self, file: P) -> Self {
self.private_key_file = Some(file.as_ref().to_path_buf());
self
}
}