agate

Simple gemini server for static files
git clone https://github.com/mbrubeck/agate.git
Log | Files | Refs | README

commit b5a416fc3502f227c9f0a2090529701ef8c51e30
parent 824d38469351519cd1004852d10ea089c6173312
Author: Johann150 <johann.galle@protonmail.com>
Date:   Tue, 19 Oct 2021 00:30:38 +0200

update rustls and related dependencies (#72)

* chore(deps): bump rustls from 0.19.1 to 0.20.0
* chore(deps): bump webpki from 0.21.4 to 0.22.0
* chore(deps): bump tokio-rustls from 0.22.0 to 0.23.0
* update rustls calls
* update webpki naming
* update tests

Diffstat:
MCargo.lock | 65++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
MCargo.toml | 6+++---
Msrc/certificates.rs | 55++++++++++++++-----------------------------------------
Msrc/main.rs | 16+++++++++++-----
Mtests/tests.rs | 63+++++++++++++++++++++++++++++++++++++++++----------------------
5 files changed, 123 insertions(+), 82 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -18,11 +18,11 @@ dependencies = [ "once_cell", "percent-encoding", "rcgen", - "rustls", + "rustls 0.20.0", "tokio", - "tokio-rustls", + "tokio-rustls 0.23.0", "url", - "webpki", + "webpki 0.22.0", ] [[package]] @@ -166,12 +166,12 @@ checksum = "aa12dfaa57be769c6681b4d193398cae8db7f7b9af3e86d362d7f0a3c294a1a0" dependencies = [ "anyhow", "ring", - "rustls", + "rustls 0.19.1", "thiserror", "tokio", - "tokio-rustls", + "tokio-rustls 0.22.0", "url", - "webpki", + "webpki 0.21.4", "webpki-roots", "x509-signature", ] @@ -451,8 +451,20 @@ dependencies = [ "base64", "log", "ring", - "sct", - "webpki", + "sct 0.6.1", + "webpki 0.21.4", +] + +[[package]] +name = "rustls" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b5ac6078ca424dc1d3ae2328526a76787fecc7f8011f520e3276730e711fc95" +dependencies = [ + "log", + "ring", + "sct 0.7.0", + "webpki 0.22.0", ] [[package]] @@ -466,6 +478,16 @@ dependencies = [ ] [[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] name = "slab" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -566,9 +588,20 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" dependencies = [ - "rustls", + "rustls 0.19.1", "tokio", - "webpki", + "webpki 0.21.4", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49194a46b06a69f2498a34a595ab4a9c1babd2642ffa3dbccf6c6778d1426f2" +dependencies = [ + "rustls 0.20.0", + "tokio", + "webpki 0.22.0", ] [[package]] @@ -706,12 +739,22 @@ dependencies = [ ] [[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] name = "webpki-roots" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" dependencies = [ - "webpki", + "webpki 0.21.4", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml @@ -22,11 +22,11 @@ mime_guess = "2.0" once_cell = "1.5" percent-encoding = "2.1" rcgen = { version = "0.8.14" } -rustls = "0.19.0" -tokio-rustls = "0.22.0" +rustls = "0.20.0" +tokio-rustls = "0.23.0" tokio = { version = "1.2", features = ["fs", "io-util", "net", "rt-multi-thread", "sync"] } url = "2.2.1" -webpki = "0.21.4" +webpki = "0.22.0" [dev-dependencies] anyhow = "1.0" diff --git a/src/certificates.rs b/src/certificates.rs @@ -1,7 +1,7 @@ use { rustls::{ - sign::{any_supported_type, CertifiedKey}, - ResolvesServerCert, + server::{ClientHello, ResolvesServerCert}, + sign::{any_supported_type, CertifiedKey, SignError}, }, std::{ ffi::OsStr, @@ -9,7 +9,6 @@ use { path::Path, sync::Arc, }, - webpki::DNSNameRef, }; /// A struct that holds all loaded certificates and the respective domain @@ -17,7 +16,7 @@ use { pub(crate) struct CertStore { /// Stores the certificates and the domains they apply to, sorted by domain /// names, longest matches first - certs: Vec<(String, CertifiedKey)>, + certs: Vec<(String, Arc<CertifiedKey>)>, } pub static CERT_FILE_NAME: &str = "cert.der"; @@ -29,14 +28,9 @@ pub enum CertLoadError { NoReadCertDir, /// no certificates or keys were found Empty, - /// the specified domain name cannot be processed correctly - BadDomain(String), /// the key file for the specified domain is bad (e.g. does not contain a /// key or is invalid) - BadKey(String), - /// The certificate file for the specified domain is bad (e.g. invalid) - /// The second parameter is the error message. - BadCert(String, String), + BadKey(String, SignError), /// the key file for the specified domain is missing (but a certificate /// file was present) MissingKey(String), @@ -53,16 +47,7 @@ impl Display for CertLoadError { match self { Self::NoReadCertDir => write!(f, "Could not read from certificate directory."), Self::Empty => write!(f, "No keys or certificates were found in the given directory.\nSpecify the --hostname option to generate these automatically."), - Self::BadDomain(domain) if !domain.is_ascii() => write!( - f, - "The domain name {} cannot be processed, it must be punycoded.", - domain - ), - Self::BadDomain(domain) => write!(f, "The domain name {} cannot be processed.", domain), - Self::BadKey(domain) => write!(f, "The key file for {} is malformed.", domain), - Self::BadCert(domain, e) => { - write!(f, "The certificate file for {} is malformed: {}", domain, e) - } + Self::BadKey(domain, err) => write!(f, "The key file for {} is malformed: {:?}", domain, err), Self::MissingKey(domain) => write!(f, "The key file for {} is missing.", domain), Self::MissingCert(domain) => { write!(f, "The certificate file for {} is missing.", domain) @@ -106,9 +91,9 @@ fn load_domain(certs_dir: &Path, domain: String) -> Result<CertifiedKey, CertLoa // transform key to correct format let key = match any_supported_type(&key) { Ok(key) => key, - Err(()) => return Err(CertLoadError::BadKey(domain)), + Err(e) => return Err(CertLoadError::BadKey(domain, e)), }; - Ok(CertifiedKey::new(vec![cert], Arc::new(key))) + Ok(CertifiedKey::new(vec![cert], key)) } impl CertStore { @@ -127,14 +112,9 @@ impl CertStore { // certificate directory. match load_domain(certs_dir, String::new()) { Err(CertLoadError::EmptyDomain(_)) => { /* there are no fallback keys */ } - Err(CertLoadError::Empty) - | Err(CertLoadError::NoReadCertDir) - | Err(CertLoadError::BadDomain(_)) => unreachable!(), - Err(CertLoadError::BadKey(_)) => { - return Err(CertLoadError::BadKey("fallback".to_string())) - } - Err(CertLoadError::BadCert(_, e)) => { - return Err(CertLoadError::BadCert("fallback".to_string(), e)) + Err(CertLoadError::Empty) | Err(CertLoadError::NoReadCertDir) => unreachable!(), + Err(CertLoadError::BadKey(_, e)) => { + return Err(CertLoadError::BadKey("fallback".to_string(), e)) } Err(CertLoadError::MissingKey(_)) => { return Err(CertLoadError::MissingKey("fallback".to_string())) @@ -145,7 +125,7 @@ impl CertStore { // For the fallback keys there is no domain name to verify them // against, so we can skip that step and only have to do it for the // other keys below. - Ok(key) => certs.push((String::new(), key)), + Ok(key) => certs.push((String::new(), Arc::new(key))), } for file in certs_dir @@ -163,16 +143,9 @@ impl CertStore { .unwrap() .to_string(); - let dns_name = match DNSNameRef::try_from_ascii_str(&filename) { - Ok(name) => name, - Err(_) => return Err(CertLoadError::BadDomain(filename)), - }; - let key = load_domain(certs_dir, filename.clone())?; - key.cross_check_end_entity_cert(Some(dns_name)) - .map_err(|e| CertLoadError::BadCert(filename.clone(), e.to_string()))?; - certs.push((filename, key)); + certs.push((filename, Arc::new(key))); } if certs.is_empty() { @@ -211,9 +184,9 @@ impl CertStore { } impl ResolvesServerCert for CertStore { - fn resolve(&self, client_hello: rustls::ClientHello<'_>) -> Option<CertifiedKey> { + fn resolve(&self, client_hello: ClientHello<'_>) -> Option<Arc<CertifiedKey>> { if let Some(name) = client_hello.server_name() { - let name: &str = name.into(); + let name: &str = name; // The certificate list is sorted so the longest match will always // appear first. We have to find the first that is either this // domain or a parent domain of the current one. diff --git a/src/main.rs b/src/main.rs @@ -8,7 +8,7 @@ use { once_cell::sync::Lazy, percent_encoding::{percent_decode_str, percent_encode, AsciiSet, CONTROLS}, rcgen::{Certificate, CertificateParams, DnType}, - rustls::{NoClientAuth, ServerConfig}, + rustls::server::ServerConfig, std::{ borrow::Cow, error::Error, @@ -318,11 +318,17 @@ fn check_path(s: String) -> Result<PathBuf, String> { static TLS: Lazy<TlsAcceptor> = Lazy::new(acceptor); fn acceptor() -> TlsAcceptor { - let mut config = ServerConfig::new(NoClientAuth::new()); - if ARGS.only_tls13 { - config.versions = vec![rustls::ProtocolVersion::TLSv1_3]; + let config = if ARGS.only_tls13 { + ServerConfig::builder() + .with_safe_default_cipher_suites() + .with_safe_default_kx_groups() + .with_protocol_versions(&[&rustls::version::TLS13]) + .expect("could not build server config") + } else { + ServerConfig::builder().with_safe_defaults() } - config.cert_resolver = ARGS.certs.clone(); + .with_no_client_auth() + .with_cert_resolver(ARGS.certs.clone()); TlsAcceptor::from(Arc::new(config)) } diff --git a/tests/tests.rs b/tests/tests.rs @@ -1,5 +1,6 @@ use anyhow::anyhow; use gemini_fetch::{Header, Page, Status}; +use std::convert::TryInto; use std::io::{BufRead, BufReader, Read}; use std::net::{SocketAddr, ToSocketAddrs}; use std::path::PathBuf; @@ -469,18 +470,24 @@ fn directory_traversal_regression() { /// (lower versions do not have to be tested because rustls does not even /// support them, making agate incapable of accepting them) fn explicit_tls_version() { - use rustls::{ClientSession, ProtocolVersion, TLSError}; + use rustls::{ClientConnection, Error, RootCertStore}; use std::io::Read; use std::net::TcpStream; let _server = Server::new(&["--addr", "[::]:1976", "-3"]); - let mut config = rustls::ClientConfig::new(); - // try to connect using only TLS 1.2 - config.versions = vec![ProtocolVersion::TLSv1_2]; + let config = rustls::ClientConfig::builder() + .with_safe_default_cipher_suites() + .with_safe_default_kx_groups() + // try to connect using only TLS 1.2 + .with_protocol_versions(&[&rustls::version::TLS12]) + .unwrap() + .with_root_certificates(RootCertStore::empty()) + .with_no_client_auth(); - let dns_name = webpki::DNSNameRef::try_from_ascii_str("localhost").unwrap(); - let mut session = ClientSession::new(&std::sync::Arc::new(config), dns_name); + let mut session = + ClientConnection::new(std::sync::Arc::new(config), "localhost".try_into().unwrap()) + .unwrap(); let mut tcp = TcpStream::connect(addr(1976)).unwrap(); let mut tls = rustls::Stream::new(&mut session, &mut tcp); @@ -490,9 +497,9 @@ fn explicit_tls_version() { .unwrap_err() .into_inner() .unwrap() - .downcast::<TLSError>() + .downcast::<Error>() .unwrap(), - TLSError::AlertReceived(rustls::internal::msgs::enums::AlertDescription::ProtocolVersion) + Error::AlertReceived(rustls::internal::msgs::enums::AlertDescription::ProtocolVersion) ) } @@ -586,15 +593,14 @@ mod multicert { #[test] fn example_com() { - use rustls::{Certificate, ClientSession}; + use rustls::{Certificate, ClientConnection, RootCertStore}; use std::io::Write; use std::net::TcpStream; let mut server = Server::new(&["--addr", "[::]:1981", "--certs", "multicert"]); - let mut config = rustls::ClientConfig::new(); - config - .root_store + let mut certs = RootCertStore::empty(); + certs .add(&Certificate( include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), @@ -603,9 +609,16 @@ mod multicert { .to_vec(), )) .unwrap(); - - let dns_name = webpki::DNSNameRef::try_from_ascii_str("example.com").unwrap(); - let mut session = ClientSession::new(&std::sync::Arc::new(config), dns_name); + let config = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(certs) + .with_no_client_auth(); + + let mut session = ClientConnection::new( + std::sync::Arc::new(config), + "example.com".try_into().unwrap(), + ) + .unwrap(); let mut tcp = TcpStream::connect(addr(1981)).unwrap(); let mut tls = rustls::Stream::new(&mut session, &mut tcp); @@ -619,15 +632,14 @@ mod multicert { #[test] fn example_org() { - use rustls::{Certificate, ClientSession}; + use rustls::{Certificate, ClientConnection, RootCertStore}; use std::io::Write; use std::net::TcpStream; let mut server = Server::new(&["--addr", "[::]:1982", "--certs", "multicert"]); - let mut config = rustls::ClientConfig::new(); - config - .root_store + let mut certs = RootCertStore::empty(); + certs .add(&Certificate( include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), @@ -636,9 +648,16 @@ mod multicert { .to_vec(), )) .unwrap(); - - let dns_name = webpki::DNSNameRef::try_from_ascii_str("example.org").unwrap(); - let mut session = ClientSession::new(&std::sync::Arc::new(config), dns_name); + let config = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(certs) + .with_no_client_auth(); + + let mut session = ClientConnection::new( + std::sync::Arc::new(config), + "example.org".try_into().unwrap(), + ) + .unwrap(); let mut tcp = TcpStream::connect(addr(1982)).unwrap(); let mut tls = rustls::Stream::new(&mut session, &mut tcp);