diff --git a/Cargo.toml b/Cargo.toml index fd8bd09..e8d0c7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,9 @@ edition = "2018" [dependencies] tokio = { version = "0.2", features = [ "net", "io-util", "rt-threaded" ] } -tokio-rustls = "*" -rustls = "*" -futures-util = "*" +openssl = "0.10" +tokio-openssl = "0.4" +futures-util = "0.3" toml = "*" serde = "*" serde_derive = "*" diff --git a/src/cgi.rs b/src/cgi.rs index c27740a..304059c 100644 --- a/src/cgi.rs +++ b/src/cgi.rs @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::os::unix::fs::PermissionsExt; use tokio::net::TcpStream; -use tokio_rustls::server::TlsStream; +use tokio_openssl::SslStream; use url::Url; use crate::config; diff --git a/src/conn.rs b/src/conn.rs index 9ae1853..8c2b232 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -2,12 +2,12 @@ use std::io; use std::net::SocketAddr; use tokio::net::TcpStream; use tokio::prelude::*; -use tokio_rustls::server::TlsStream; +use tokio_openssl::SslStream; use crate::status; pub struct Connection { - pub stream: TlsStream, + pub stream: SslStream, pub peer_addr: SocketAddr, } diff --git a/src/main.rs b/src/main.rs index d7d94dd..3afc309 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,10 +22,8 @@ use tokio::net::TcpListener; use tokio::net::TcpStream; use tokio::prelude::*; use tokio::runtime; -use tokio_rustls::rustls::internal::pemfile::{certs, pkcs8_private_keys}; -use tokio_rustls::rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig}; -use tokio_rustls::server::TlsStream; -use tokio_rustls::TlsAcceptor; +use tokio_openssl::SslStream; +use openssl::ssl::NameType; use url::Url; use chrono::{DateTime, Utc}; use mime_guess; @@ -257,9 +255,8 @@ fn main() -> io::Result<()> { .build()?; let handle = runtime.handle().clone(); - let config = tls::get_tls_config(cfg.clone()); - let acceptor = TlsAcceptor::from(Arc::new(config)); + let acceptor = tls::acceptor_conf(cfg.clone())?; let fut = async { let mut listener = TcpListener::bind(&addr).await?; @@ -269,13 +266,12 @@ fn main() -> io::Result<()> { let cmap = cmap.clone(); let fut = async move { - let mut stream = acceptor.accept(stream).await?; - let (_, sni) = TlsStream::get_mut(&mut stream); - let sni = match sni.get_sni_hostname() { + let mut stream = tokio_openssl::accept(&acceptor, stream).await.expect("Couldn't accept"); + let sni = match stream.ssl().servername(NameType::HOST_NAME) { Some(s) => s, None => return Ok(()), }; - + let srv = match cmap.get(sni) { Some(h) => h, None => { diff --git a/src/tls.rs b/src/tls.rs index 9245799..a72f36e 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -1,72 +1,65 @@ +extern crate openssl; +extern crate tokio_openssl; use std::collections::HashMap; use std::fs::File; use std::io::{self, BufReader}; use std::sync::Arc; -use rustls::sign::CertifiedKey; -use rustls::sign::{RSASigningKey, SigningKey}; -use rustls::ClientHello; -use rustls::ResolvesServerCert; -use tokio_rustls::rustls::internal::pemfile::{certs, pkcs8_private_keys}; -use tokio_rustls::rustls::{Certificate, PrivateKey}; +use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; +use openssl::ssl::SslAcceptorBuilder; +use openssl::ssl::SniError; +use openssl::error::ErrorStack; +use openssl::ssl::SslContextBuilder; +use openssl::ssl::SslVersion; +use openssl::ssl::NameType; +use tokio_openssl::SslStream; use crate::config; -pub fn get_tls_config(cfg: config::Config) -> rustls::ServerConfig { - let store = rustls::RootCertStore::empty(); - let verifier = rustls::AllowAnyAnonymousOrAuthenticatedClient::new(store); - let mut tls_config = rustls::ServerConfig::new(verifier); - tls_config.cert_resolver = Arc::new(CertResolver::from_config(cfg).unwrap()); - - tls_config -} - -pub fn load_certs(path: &String) -> io::Result> { - certs(&mut BufReader::new(File::open(path)?)) - .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert")) -} - -pub fn load_key(path: &String) -> PrivateKey { - let keyfile = File::open(path).expect("cannot open private key file"); - let mut reader = BufReader::new(keyfile); - let key = pkcs8_private_keys(&mut reader).expect("file contains invalid pkcs8 private key"); - return key[0].clone(); -} - -// Rustls won't let self signed certs be used with sni which gemini requires. -// At 1.4.2 in https://gemini.circumlunar.space/docs/spec-spec.txt -pub struct CertResolver { - map: HashMap>, -} - -impl CertResolver { - pub fn from_config(cfg: config::Config) -> Result { - let mut map = HashMap::new(); - - for server in cfg.server.iter() { - let key = load_key(&server.key); - let certs = load_certs(&server.cert).unwrap(); - let signing_key = RSASigningKey::new(&key).unwrap(); - - let signing_key_boxed: Arc> = Arc::new(Box::new(signing_key)); - map.insert( - server.hostname.clone(), - Box::new(rustls::sign::CertifiedKey::new(certs, signing_key_boxed)), - ); +pub fn acceptor_conf(cfg: config::Config) -> Result { + let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls())?; + acceptor.set_min_proto_version(Some(SslVersion::TLS1_2))?; + let mut map = HashMap::new(); + let mut num = 1; + for server in cfg.server.iter() { + let mut ctx = SslContextBuilder::new(SslMethod::tls())?; + match ctx.set_private_key_file(&server.key, SslFiletype::PEM) { + Ok(c) => c, + Err(e) => { + eprintln!("Error: Can't load key file"); + return Err(e) + }, + }; + match ctx.set_certificate_chain_file(&server.cert) { + Ok(c) => c, + Err(e) => { + eprintln!("Error: Can't load cert file"); + return Err(e) + }, + }; + let ctx = ctx.build(); + map.insert(server.hostname.clone(), ctx.clone()); + if num == 1 { + map.insert("default".to_string(), ctx); + num += 1; } - - Ok(CertResolver { map }) } -} -impl ResolvesServerCert for CertResolver { - fn resolve(&self, client_hello: ClientHello) -> Option { - if let Some(hostname) = client_hello.server_name() { - if let Some(cert) = self.map.get(hostname.into()) { - return Some(*cert.clone()); + let ctx_builder = &mut *acceptor; + ctx_builder.set_servername_callback(move |ssl, _alert| -> Result<(), SniError> { + ssl.set_ssl_context({ + let hostname = ssl.servername(NameType::HOST_NAME); + if let Some(host) = hostname { + if let Some(ctx) = map.get(host) { + &ctx + } else { + &map.get(&"default".to_string()).expect("Can't get default") + } + } else { + &map.get(&"default".to_string()).expect("Can't get default") } - } - - None - } + }).expect("Can't get sni"); + Ok(()) + }); + Ok(acceptor.build()) }