192f3ab15Sopenharmony_ciuse cfg_if::cfg_if; 292f3ab15Sopenharmony_ciuse std::io::{Read, Write}; 392f3ab15Sopenharmony_ciuse std::ops::{Deref, DerefMut}; 492f3ab15Sopenharmony_ci 592f3ab15Sopenharmony_ciuse crate::dh::Dh; 692f3ab15Sopenharmony_ciuse crate::error::ErrorStack; 792f3ab15Sopenharmony_ci#[cfg(any(ossl111, libressl340))] 892f3ab15Sopenharmony_ciuse crate::ssl::SslVersion; 992f3ab15Sopenharmony_ciuse crate::ssl::{ 1092f3ab15Sopenharmony_ci HandshakeError, Ssl, SslContext, SslContextBuilder, SslContextRef, SslMethod, SslMode, 1192f3ab15Sopenharmony_ci SslOptions, SslRef, SslStream, SslVerifyMode, 1292f3ab15Sopenharmony_ci}; 1392f3ab15Sopenharmony_ciuse crate::version; 1492f3ab15Sopenharmony_ciuse std::net::IpAddr; 1592f3ab15Sopenharmony_ci 1692f3ab15Sopenharmony_ciconst FFDHE_2048: &str = " 1792f3ab15Sopenharmony_ci-----BEGIN DH PARAMETERS----- 1892f3ab15Sopenharmony_ciMIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz 1992f3ab15Sopenharmony_ci+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a 2092f3ab15Sopenharmony_ci87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 2192f3ab15Sopenharmony_ciYdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi 2292f3ab15Sopenharmony_ci7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD 2392f3ab15Sopenharmony_cissbzSibBsu/6iGtCOGEoXJf//////////wIBAg== 2492f3ab15Sopenharmony_ci-----END DH PARAMETERS----- 2592f3ab15Sopenharmony_ci"; 2692f3ab15Sopenharmony_ci 2792f3ab15Sopenharmony_ci#[allow(clippy::inconsistent_digit_grouping, clippy::unusual_byte_groupings)] 2892f3ab15Sopenharmony_cifn ctx(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> { 2992f3ab15Sopenharmony_ci let mut ctx = SslContextBuilder::new(method)?; 3092f3ab15Sopenharmony_ci 3192f3ab15Sopenharmony_ci cfg_if! { 3292f3ab15Sopenharmony_ci if #[cfg(not(boringssl))] { 3392f3ab15Sopenharmony_ci let mut opts = SslOptions::ALL 3492f3ab15Sopenharmony_ci | SslOptions::NO_COMPRESSION 3592f3ab15Sopenharmony_ci | SslOptions::NO_SSLV2 3692f3ab15Sopenharmony_ci | SslOptions::NO_SSLV3 3792f3ab15Sopenharmony_ci | SslOptions::SINGLE_DH_USE 3892f3ab15Sopenharmony_ci | SslOptions::SINGLE_ECDH_USE; 3992f3ab15Sopenharmony_ci opts &= !SslOptions::DONT_INSERT_EMPTY_FRAGMENTS; 4092f3ab15Sopenharmony_ci 4192f3ab15Sopenharmony_ci ctx.set_options(opts); 4292f3ab15Sopenharmony_ci } 4392f3ab15Sopenharmony_ci } 4492f3ab15Sopenharmony_ci 4592f3ab15Sopenharmony_ci let mut mode = 4692f3ab15Sopenharmony_ci SslMode::AUTO_RETRY | SslMode::ACCEPT_MOVING_WRITE_BUFFER | SslMode::ENABLE_PARTIAL_WRITE; 4792f3ab15Sopenharmony_ci 4892f3ab15Sopenharmony_ci // This is quite a useful optimization for saving memory, but historically 4992f3ab15Sopenharmony_ci // caused CVEs in OpenSSL pre-1.0.1h, according to 5092f3ab15Sopenharmony_ci // https://bugs.python.org/issue25672 5192f3ab15Sopenharmony_ci if version::number() >= 0x1_00_01_08_0 { 5292f3ab15Sopenharmony_ci mode |= SslMode::RELEASE_BUFFERS; 5392f3ab15Sopenharmony_ci } 5492f3ab15Sopenharmony_ci 5592f3ab15Sopenharmony_ci ctx.set_mode(mode); 5692f3ab15Sopenharmony_ci 5792f3ab15Sopenharmony_ci Ok(ctx) 5892f3ab15Sopenharmony_ci} 5992f3ab15Sopenharmony_ci 6092f3ab15Sopenharmony_ci/// A type which wraps client-side streams in a TLS session. 6192f3ab15Sopenharmony_ci/// 6292f3ab15Sopenharmony_ci/// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL 6392f3ab15Sopenharmony_ci/// structures, configuring cipher suites, session options, hostname verification, and more. 6492f3ab15Sopenharmony_ci/// 6592f3ab15Sopenharmony_ci/// OpenSSL's built-in hostname verification is used when linking against OpenSSL 1.0.2 or 1.1.0, 6692f3ab15Sopenharmony_ci/// and a custom implementation is used when linking against OpenSSL 1.0.1. 6792f3ab15Sopenharmony_ci#[derive(Clone, Debug)] 6892f3ab15Sopenharmony_cipub struct SslConnector(SslContext); 6992f3ab15Sopenharmony_ci 7092f3ab15Sopenharmony_ciimpl SslConnector { 7192f3ab15Sopenharmony_ci /// Creates a new builder for TLS connections. 7292f3ab15Sopenharmony_ci /// 7392f3ab15Sopenharmony_ci /// The default configuration is subject to change, and is currently derived from Python. 7492f3ab15Sopenharmony_ci pub fn builder(method: SslMethod) -> Result<SslConnectorBuilder, ErrorStack> { 7592f3ab15Sopenharmony_ci let mut ctx = ctx(method)?; 7692f3ab15Sopenharmony_ci ctx.set_default_verify_paths()?; 7792f3ab15Sopenharmony_ci ctx.set_cipher_list( 7892f3ab15Sopenharmony_ci "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK", 7992f3ab15Sopenharmony_ci )?; 8092f3ab15Sopenharmony_ci setup_verify(&mut ctx); 8192f3ab15Sopenharmony_ci 8292f3ab15Sopenharmony_ci Ok(SslConnectorBuilder(ctx)) 8392f3ab15Sopenharmony_ci } 8492f3ab15Sopenharmony_ci 8592f3ab15Sopenharmony_ci /// Initiates a client-side TLS session on a stream. 8692f3ab15Sopenharmony_ci /// 8792f3ab15Sopenharmony_ci /// The domain is used for SNI and hostname verification. 8892f3ab15Sopenharmony_ci pub fn connect<S>(&self, domain: &str, stream: S) -> Result<SslStream<S>, HandshakeError<S>> 8992f3ab15Sopenharmony_ci where 9092f3ab15Sopenharmony_ci S: Read + Write, 9192f3ab15Sopenharmony_ci { 9292f3ab15Sopenharmony_ci self.configure()?.connect(domain, stream) 9392f3ab15Sopenharmony_ci } 9492f3ab15Sopenharmony_ci 9592f3ab15Sopenharmony_ci /// Returns a structure allowing for configuration of a single TLS session before connection. 9692f3ab15Sopenharmony_ci pub fn configure(&self) -> Result<ConnectConfiguration, ErrorStack> { 9792f3ab15Sopenharmony_ci Ssl::new(&self.0).map(|ssl| ConnectConfiguration { 9892f3ab15Sopenharmony_ci ssl, 9992f3ab15Sopenharmony_ci sni: true, 10092f3ab15Sopenharmony_ci verify_hostname: true, 10192f3ab15Sopenharmony_ci }) 10292f3ab15Sopenharmony_ci } 10392f3ab15Sopenharmony_ci 10492f3ab15Sopenharmony_ci /// Consumes the `SslConnector`, returning the inner raw `SslContext`. 10592f3ab15Sopenharmony_ci pub fn into_context(self) -> SslContext { 10692f3ab15Sopenharmony_ci self.0 10792f3ab15Sopenharmony_ci } 10892f3ab15Sopenharmony_ci 10992f3ab15Sopenharmony_ci /// Returns a shared reference to the inner raw `SslContext`. 11092f3ab15Sopenharmony_ci pub fn context(&self) -> &SslContextRef { 11192f3ab15Sopenharmony_ci &self.0 11292f3ab15Sopenharmony_ci } 11392f3ab15Sopenharmony_ci} 11492f3ab15Sopenharmony_ci 11592f3ab15Sopenharmony_ci/// A builder for `SslConnector`s. 11692f3ab15Sopenharmony_cipub struct SslConnectorBuilder(SslContextBuilder); 11792f3ab15Sopenharmony_ci 11892f3ab15Sopenharmony_ciimpl SslConnectorBuilder { 11992f3ab15Sopenharmony_ci /// Consumes the builder, returning an `SslConnector`. 12092f3ab15Sopenharmony_ci pub fn build(self) -> SslConnector { 12192f3ab15Sopenharmony_ci SslConnector(self.0.build()) 12292f3ab15Sopenharmony_ci } 12392f3ab15Sopenharmony_ci} 12492f3ab15Sopenharmony_ci 12592f3ab15Sopenharmony_ciimpl Deref for SslConnectorBuilder { 12692f3ab15Sopenharmony_ci type Target = SslContextBuilder; 12792f3ab15Sopenharmony_ci 12892f3ab15Sopenharmony_ci fn deref(&self) -> &SslContextBuilder { 12992f3ab15Sopenharmony_ci &self.0 13092f3ab15Sopenharmony_ci } 13192f3ab15Sopenharmony_ci} 13292f3ab15Sopenharmony_ci 13392f3ab15Sopenharmony_ciimpl DerefMut for SslConnectorBuilder { 13492f3ab15Sopenharmony_ci fn deref_mut(&mut self) -> &mut SslContextBuilder { 13592f3ab15Sopenharmony_ci &mut self.0 13692f3ab15Sopenharmony_ci } 13792f3ab15Sopenharmony_ci} 13892f3ab15Sopenharmony_ci 13992f3ab15Sopenharmony_ci/// A type which allows for configuration of a client-side TLS session before connection. 14092f3ab15Sopenharmony_cipub struct ConnectConfiguration { 14192f3ab15Sopenharmony_ci ssl: Ssl, 14292f3ab15Sopenharmony_ci sni: bool, 14392f3ab15Sopenharmony_ci verify_hostname: bool, 14492f3ab15Sopenharmony_ci} 14592f3ab15Sopenharmony_ci 14692f3ab15Sopenharmony_ciimpl ConnectConfiguration { 14792f3ab15Sopenharmony_ci /// A builder-style version of `set_use_server_name_indication`. 14892f3ab15Sopenharmony_ci pub fn use_server_name_indication(mut self, use_sni: bool) -> ConnectConfiguration { 14992f3ab15Sopenharmony_ci self.set_use_server_name_indication(use_sni); 15092f3ab15Sopenharmony_ci self 15192f3ab15Sopenharmony_ci } 15292f3ab15Sopenharmony_ci 15392f3ab15Sopenharmony_ci /// Configures the use of Server Name Indication (SNI) when connecting. 15492f3ab15Sopenharmony_ci /// 15592f3ab15Sopenharmony_ci /// Defaults to `true`. 15692f3ab15Sopenharmony_ci pub fn set_use_server_name_indication(&mut self, use_sni: bool) { 15792f3ab15Sopenharmony_ci self.sni = use_sni; 15892f3ab15Sopenharmony_ci } 15992f3ab15Sopenharmony_ci 16092f3ab15Sopenharmony_ci /// A builder-style version of `set_verify_hostname`. 16192f3ab15Sopenharmony_ci pub fn verify_hostname(mut self, verify_hostname: bool) -> ConnectConfiguration { 16292f3ab15Sopenharmony_ci self.set_verify_hostname(verify_hostname); 16392f3ab15Sopenharmony_ci self 16492f3ab15Sopenharmony_ci } 16592f3ab15Sopenharmony_ci 16692f3ab15Sopenharmony_ci /// Configures the use of hostname verification when connecting. 16792f3ab15Sopenharmony_ci /// 16892f3ab15Sopenharmony_ci /// Defaults to `true`. 16992f3ab15Sopenharmony_ci /// 17092f3ab15Sopenharmony_ci /// # Warning 17192f3ab15Sopenharmony_ci /// 17292f3ab15Sopenharmony_ci /// You should think very carefully before you use this method. If hostname verification is not 17392f3ab15Sopenharmony_ci /// used, *any* valid certificate for *any* site will be trusted for use from any other. This 17492f3ab15Sopenharmony_ci /// introduces a significant vulnerability to man-in-the-middle attacks. 17592f3ab15Sopenharmony_ci pub fn set_verify_hostname(&mut self, verify_hostname: bool) { 17692f3ab15Sopenharmony_ci self.verify_hostname = verify_hostname; 17792f3ab15Sopenharmony_ci } 17892f3ab15Sopenharmony_ci 17992f3ab15Sopenharmony_ci /// Returns an `Ssl` configured to connect to the provided domain. 18092f3ab15Sopenharmony_ci /// 18192f3ab15Sopenharmony_ci /// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled. 18292f3ab15Sopenharmony_ci pub fn into_ssl(mut self, domain: &str) -> Result<Ssl, ErrorStack> { 18392f3ab15Sopenharmony_ci if self.sni && domain.parse::<IpAddr>().is_err() { 18492f3ab15Sopenharmony_ci self.ssl.set_hostname(domain)?; 18592f3ab15Sopenharmony_ci } 18692f3ab15Sopenharmony_ci 18792f3ab15Sopenharmony_ci if self.verify_hostname { 18892f3ab15Sopenharmony_ci setup_verify_hostname(&mut self.ssl, domain)?; 18992f3ab15Sopenharmony_ci } 19092f3ab15Sopenharmony_ci 19192f3ab15Sopenharmony_ci Ok(self.ssl) 19292f3ab15Sopenharmony_ci } 19392f3ab15Sopenharmony_ci 19492f3ab15Sopenharmony_ci /// Initiates a client-side TLS session on a stream. 19592f3ab15Sopenharmony_ci /// 19692f3ab15Sopenharmony_ci /// The domain is used for SNI and hostname verification if enabled. 19792f3ab15Sopenharmony_ci pub fn connect<S>(self, domain: &str, stream: S) -> Result<SslStream<S>, HandshakeError<S>> 19892f3ab15Sopenharmony_ci where 19992f3ab15Sopenharmony_ci S: Read + Write, 20092f3ab15Sopenharmony_ci { 20192f3ab15Sopenharmony_ci self.into_ssl(domain)?.connect(stream) 20292f3ab15Sopenharmony_ci } 20392f3ab15Sopenharmony_ci} 20492f3ab15Sopenharmony_ci 20592f3ab15Sopenharmony_ciimpl Deref for ConnectConfiguration { 20692f3ab15Sopenharmony_ci type Target = SslRef; 20792f3ab15Sopenharmony_ci 20892f3ab15Sopenharmony_ci fn deref(&self) -> &SslRef { 20992f3ab15Sopenharmony_ci &self.ssl 21092f3ab15Sopenharmony_ci } 21192f3ab15Sopenharmony_ci} 21292f3ab15Sopenharmony_ci 21392f3ab15Sopenharmony_ciimpl DerefMut for ConnectConfiguration { 21492f3ab15Sopenharmony_ci fn deref_mut(&mut self) -> &mut SslRef { 21592f3ab15Sopenharmony_ci &mut self.ssl 21692f3ab15Sopenharmony_ci } 21792f3ab15Sopenharmony_ci} 21892f3ab15Sopenharmony_ci 21992f3ab15Sopenharmony_ci/// A type which wraps server-side streams in a TLS session. 22092f3ab15Sopenharmony_ci/// 22192f3ab15Sopenharmony_ci/// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL 22292f3ab15Sopenharmony_ci/// structures, configuring cipher suites, session options, and more. 22392f3ab15Sopenharmony_ci#[derive(Clone)] 22492f3ab15Sopenharmony_cipub struct SslAcceptor(SslContext); 22592f3ab15Sopenharmony_ci 22692f3ab15Sopenharmony_ciimpl SslAcceptor { 22792f3ab15Sopenharmony_ci /// Creates a new builder configured to connect to non-legacy clients. This should generally be 22892f3ab15Sopenharmony_ci /// considered a reasonable default choice. 22992f3ab15Sopenharmony_ci /// 23092f3ab15Sopenharmony_ci /// This corresponds to the intermediate configuration of version 5 of Mozilla's server side TLS 23192f3ab15Sopenharmony_ci /// recommendations. See its [documentation][docs] for more details on specifics. 23292f3ab15Sopenharmony_ci /// 23392f3ab15Sopenharmony_ci /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS 23492f3ab15Sopenharmony_ci pub fn mozilla_intermediate_v5(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> { 23592f3ab15Sopenharmony_ci let mut ctx = ctx(method)?; 23692f3ab15Sopenharmony_ci ctx.set_options(SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1); 23792f3ab15Sopenharmony_ci let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; 23892f3ab15Sopenharmony_ci ctx.set_tmp_dh(&dh)?; 23992f3ab15Sopenharmony_ci setup_curves(&mut ctx)?; 24092f3ab15Sopenharmony_ci ctx.set_cipher_list( 24192f3ab15Sopenharmony_ci "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:\ 24292f3ab15Sopenharmony_ci ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\ 24392f3ab15Sopenharmony_ci DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384" 24492f3ab15Sopenharmony_ci )?; 24592f3ab15Sopenharmony_ci #[cfg(any(ossl111, libressl340))] 24692f3ab15Sopenharmony_ci ctx.set_ciphersuites( 24792f3ab15Sopenharmony_ci "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256", 24892f3ab15Sopenharmony_ci )?; 24992f3ab15Sopenharmony_ci Ok(SslAcceptorBuilder(ctx)) 25092f3ab15Sopenharmony_ci } 25192f3ab15Sopenharmony_ci 25292f3ab15Sopenharmony_ci /// Creates a new builder configured to connect to modern clients. 25392f3ab15Sopenharmony_ci /// 25492f3ab15Sopenharmony_ci /// This corresponds to the modern configuration of version 5 of Mozilla's server side TLS recommendations. 25592f3ab15Sopenharmony_ci /// See its [documentation][docs] for more details on specifics. 25692f3ab15Sopenharmony_ci /// 25792f3ab15Sopenharmony_ci /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. 25892f3ab15Sopenharmony_ci /// 25992f3ab15Sopenharmony_ci /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS 26092f3ab15Sopenharmony_ci #[cfg(any(ossl111, libressl340))] 26192f3ab15Sopenharmony_ci pub fn mozilla_modern_v5(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> { 26292f3ab15Sopenharmony_ci let mut ctx = ctx(method)?; 26392f3ab15Sopenharmony_ci ctx.set_min_proto_version(Some(SslVersion::TLS1_3))?; 26492f3ab15Sopenharmony_ci ctx.set_ciphersuites( 26592f3ab15Sopenharmony_ci "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256", 26692f3ab15Sopenharmony_ci )?; 26792f3ab15Sopenharmony_ci Ok(SslAcceptorBuilder(ctx)) 26892f3ab15Sopenharmony_ci } 26992f3ab15Sopenharmony_ci 27092f3ab15Sopenharmony_ci /// Creates a new builder configured to connect to non-legacy clients. This should generally be 27192f3ab15Sopenharmony_ci /// considered a reasonable default choice. 27292f3ab15Sopenharmony_ci /// 27392f3ab15Sopenharmony_ci /// This corresponds to the intermediate configuration of version 4 of Mozilla's server side TLS 27492f3ab15Sopenharmony_ci /// recommendations. See its [documentation][docs] for more details on specifics. 27592f3ab15Sopenharmony_ci /// 27692f3ab15Sopenharmony_ci /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS 27792f3ab15Sopenharmony_ci // FIXME remove in next major version 27892f3ab15Sopenharmony_ci pub fn mozilla_intermediate(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> { 27992f3ab15Sopenharmony_ci let mut ctx = ctx(method)?; 28092f3ab15Sopenharmony_ci ctx.set_options(SslOptions::CIPHER_SERVER_PREFERENCE); 28192f3ab15Sopenharmony_ci #[cfg(any(ossl111, libressl340))] 28292f3ab15Sopenharmony_ci ctx.set_options(SslOptions::NO_TLSV1_3); 28392f3ab15Sopenharmony_ci let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; 28492f3ab15Sopenharmony_ci ctx.set_tmp_dh(&dh)?; 28592f3ab15Sopenharmony_ci setup_curves(&mut ctx)?; 28692f3ab15Sopenharmony_ci ctx.set_cipher_list( 28792f3ab15Sopenharmony_ci "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:\ 28892f3ab15Sopenharmony_ci ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\ 28992f3ab15Sopenharmony_ci DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:\ 29092f3ab15Sopenharmony_ci ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:\ 29192f3ab15Sopenharmony_ci ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:\ 29292f3ab15Sopenharmony_ci DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:\ 29392f3ab15Sopenharmony_ci EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:\ 29492f3ab15Sopenharmony_ci AES256-SHA:DES-CBC3-SHA:!DSS", 29592f3ab15Sopenharmony_ci )?; 29692f3ab15Sopenharmony_ci Ok(SslAcceptorBuilder(ctx)) 29792f3ab15Sopenharmony_ci } 29892f3ab15Sopenharmony_ci 29992f3ab15Sopenharmony_ci /// Creates a new builder configured to connect to modern clients. 30092f3ab15Sopenharmony_ci /// 30192f3ab15Sopenharmony_ci /// This corresponds to the modern configuration of version 4 of Mozilla's server side TLS recommendations. 30292f3ab15Sopenharmony_ci /// See its [documentation][docs] for more details on specifics. 30392f3ab15Sopenharmony_ci /// 30492f3ab15Sopenharmony_ci /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS 30592f3ab15Sopenharmony_ci // FIXME remove in next major version 30692f3ab15Sopenharmony_ci pub fn mozilla_modern(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> { 30792f3ab15Sopenharmony_ci let mut ctx = ctx(method)?; 30892f3ab15Sopenharmony_ci ctx.set_options( 30992f3ab15Sopenharmony_ci SslOptions::CIPHER_SERVER_PREFERENCE | SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1, 31092f3ab15Sopenharmony_ci ); 31192f3ab15Sopenharmony_ci #[cfg(any(ossl111, libressl340))] 31292f3ab15Sopenharmony_ci ctx.set_options(SslOptions::NO_TLSV1_3); 31392f3ab15Sopenharmony_ci setup_curves(&mut ctx)?; 31492f3ab15Sopenharmony_ci ctx.set_cipher_list( 31592f3ab15Sopenharmony_ci "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:\ 31692f3ab15Sopenharmony_ci ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\ 31792f3ab15Sopenharmony_ci ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256", 31892f3ab15Sopenharmony_ci )?; 31992f3ab15Sopenharmony_ci Ok(SslAcceptorBuilder(ctx)) 32092f3ab15Sopenharmony_ci } 32192f3ab15Sopenharmony_ci 32292f3ab15Sopenharmony_ci /// Initiates a server-side TLS session on a stream. 32392f3ab15Sopenharmony_ci pub fn accept<S>(&self, stream: S) -> Result<SslStream<S>, HandshakeError<S>> 32492f3ab15Sopenharmony_ci where 32592f3ab15Sopenharmony_ci S: Read + Write, 32692f3ab15Sopenharmony_ci { 32792f3ab15Sopenharmony_ci let ssl = Ssl::new(&self.0)?; 32892f3ab15Sopenharmony_ci ssl.accept(stream) 32992f3ab15Sopenharmony_ci } 33092f3ab15Sopenharmony_ci 33192f3ab15Sopenharmony_ci /// Consumes the `SslAcceptor`, returning the inner raw `SslContext`. 33292f3ab15Sopenharmony_ci pub fn into_context(self) -> SslContext { 33392f3ab15Sopenharmony_ci self.0 33492f3ab15Sopenharmony_ci } 33592f3ab15Sopenharmony_ci 33692f3ab15Sopenharmony_ci /// Returns a shared reference to the inner raw `SslContext`. 33792f3ab15Sopenharmony_ci pub fn context(&self) -> &SslContextRef { 33892f3ab15Sopenharmony_ci &self.0 33992f3ab15Sopenharmony_ci } 34092f3ab15Sopenharmony_ci} 34192f3ab15Sopenharmony_ci 34292f3ab15Sopenharmony_ci/// A builder for `SslAcceptor`s. 34392f3ab15Sopenharmony_cipub struct SslAcceptorBuilder(SslContextBuilder); 34492f3ab15Sopenharmony_ci 34592f3ab15Sopenharmony_ciimpl SslAcceptorBuilder { 34692f3ab15Sopenharmony_ci /// Consumes the builder, returning a `SslAcceptor`. 34792f3ab15Sopenharmony_ci pub fn build(self) -> SslAcceptor { 34892f3ab15Sopenharmony_ci SslAcceptor(self.0.build()) 34992f3ab15Sopenharmony_ci } 35092f3ab15Sopenharmony_ci} 35192f3ab15Sopenharmony_ci 35292f3ab15Sopenharmony_ciimpl Deref for SslAcceptorBuilder { 35392f3ab15Sopenharmony_ci type Target = SslContextBuilder; 35492f3ab15Sopenharmony_ci 35592f3ab15Sopenharmony_ci fn deref(&self) -> &SslContextBuilder { 35692f3ab15Sopenharmony_ci &self.0 35792f3ab15Sopenharmony_ci } 35892f3ab15Sopenharmony_ci} 35992f3ab15Sopenharmony_ci 36092f3ab15Sopenharmony_ciimpl DerefMut for SslAcceptorBuilder { 36192f3ab15Sopenharmony_ci fn deref_mut(&mut self) -> &mut SslContextBuilder { 36292f3ab15Sopenharmony_ci &mut self.0 36392f3ab15Sopenharmony_ci } 36492f3ab15Sopenharmony_ci} 36592f3ab15Sopenharmony_ci 36692f3ab15Sopenharmony_cicfg_if! { 36792f3ab15Sopenharmony_ci if #[cfg(ossl110)] { 36892f3ab15Sopenharmony_ci #[allow(clippy::unnecessary_wraps)] 36992f3ab15Sopenharmony_ci fn setup_curves(_: &mut SslContextBuilder) -> Result<(), ErrorStack> { 37092f3ab15Sopenharmony_ci Ok(()) 37192f3ab15Sopenharmony_ci } 37292f3ab15Sopenharmony_ci } else if #[cfg(any(ossl102, libressl))] { 37392f3ab15Sopenharmony_ci fn setup_curves(ctx: &mut SslContextBuilder) -> Result<(), ErrorStack> { 37492f3ab15Sopenharmony_ci ctx.set_ecdh_auto(true) 37592f3ab15Sopenharmony_ci } 37692f3ab15Sopenharmony_ci } else { 37792f3ab15Sopenharmony_ci fn setup_curves(ctx: &mut SslContextBuilder) -> Result<(), ErrorStack> { 37892f3ab15Sopenharmony_ci use crate::ec::EcKey; 37992f3ab15Sopenharmony_ci use crate::nid::Nid; 38092f3ab15Sopenharmony_ci 38192f3ab15Sopenharmony_ci let curve = EcKey::from_curve_name(Nid::X9_62_PRIME256V1)?; 38292f3ab15Sopenharmony_ci ctx.set_tmp_ecdh(&curve) 38392f3ab15Sopenharmony_ci } 38492f3ab15Sopenharmony_ci } 38592f3ab15Sopenharmony_ci} 38692f3ab15Sopenharmony_ci 38792f3ab15Sopenharmony_cicfg_if! { 38892f3ab15Sopenharmony_ci if #[cfg(any(ossl102, libressl261))] { 38992f3ab15Sopenharmony_ci fn setup_verify(ctx: &mut SslContextBuilder) { 39092f3ab15Sopenharmony_ci ctx.set_verify(SslVerifyMode::PEER); 39192f3ab15Sopenharmony_ci } 39292f3ab15Sopenharmony_ci 39392f3ab15Sopenharmony_ci fn setup_verify_hostname(ssl: &mut SslRef, domain: &str) -> Result<(), ErrorStack> { 39492f3ab15Sopenharmony_ci use crate::x509::verify::X509CheckFlags; 39592f3ab15Sopenharmony_ci 39692f3ab15Sopenharmony_ci let param = ssl.param_mut(); 39792f3ab15Sopenharmony_ci param.set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS); 39892f3ab15Sopenharmony_ci match domain.parse() { 39992f3ab15Sopenharmony_ci Ok(ip) => param.set_ip(ip), 40092f3ab15Sopenharmony_ci Err(_) => param.set_host(domain), 40192f3ab15Sopenharmony_ci } 40292f3ab15Sopenharmony_ci } 40392f3ab15Sopenharmony_ci } else { 40492f3ab15Sopenharmony_ci fn setup_verify(ctx: &mut SslContextBuilder) { 40592f3ab15Sopenharmony_ci ctx.set_verify_callback(SslVerifyMode::PEER, verify::verify_callback); 40692f3ab15Sopenharmony_ci } 40792f3ab15Sopenharmony_ci 40892f3ab15Sopenharmony_ci fn setup_verify_hostname(ssl: &mut Ssl, domain: &str) -> Result<(), ErrorStack> { 40992f3ab15Sopenharmony_ci let domain = domain.to_string(); 41092f3ab15Sopenharmony_ci let hostname_idx = verify::try_get_hostname_idx()?; 41192f3ab15Sopenharmony_ci ssl.set_ex_data(*hostname_idx, domain); 41292f3ab15Sopenharmony_ci Ok(()) 41392f3ab15Sopenharmony_ci } 41492f3ab15Sopenharmony_ci 41592f3ab15Sopenharmony_ci mod verify { 41692f3ab15Sopenharmony_ci use std::net::IpAddr; 41792f3ab15Sopenharmony_ci use std::str; 41892f3ab15Sopenharmony_ci use once_cell::sync::OnceCell; 41992f3ab15Sopenharmony_ci 42092f3ab15Sopenharmony_ci use crate::error::ErrorStack; 42192f3ab15Sopenharmony_ci use crate::ex_data::Index; 42292f3ab15Sopenharmony_ci use crate::nid::Nid; 42392f3ab15Sopenharmony_ci use crate::ssl::Ssl; 42492f3ab15Sopenharmony_ci use crate::stack::Stack; 42592f3ab15Sopenharmony_ci use crate::x509::{ 42692f3ab15Sopenharmony_ci GeneralName, X509NameRef, X509Ref, X509StoreContext, X509StoreContextRef, 42792f3ab15Sopenharmony_ci X509VerifyResult, 42892f3ab15Sopenharmony_ci }; 42992f3ab15Sopenharmony_ci 43092f3ab15Sopenharmony_ci static HOSTNAME_IDX: OnceCell<Index<Ssl, String>> = OnceCell::new(); 43192f3ab15Sopenharmony_ci 43292f3ab15Sopenharmony_ci pub fn try_get_hostname_idx() -> Result<&'static Index<Ssl, String>, ErrorStack> { 43392f3ab15Sopenharmony_ci HOSTNAME_IDX.get_or_try_init(Ssl::new_ex_index) 43492f3ab15Sopenharmony_ci } 43592f3ab15Sopenharmony_ci 43692f3ab15Sopenharmony_ci pub fn verify_callback(preverify_ok: bool, x509_ctx: &mut X509StoreContextRef) -> bool { 43792f3ab15Sopenharmony_ci if !preverify_ok || x509_ctx.error_depth() != 0 { 43892f3ab15Sopenharmony_ci return preverify_ok; 43992f3ab15Sopenharmony_ci } 44092f3ab15Sopenharmony_ci 44192f3ab15Sopenharmony_ci let hostname_idx = 44292f3ab15Sopenharmony_ci try_get_hostname_idx().expect("failed to initialize hostname index"); 44392f3ab15Sopenharmony_ci let ok = match ( 44492f3ab15Sopenharmony_ci x509_ctx.current_cert(), 44592f3ab15Sopenharmony_ci X509StoreContext::ssl_idx() 44692f3ab15Sopenharmony_ci .ok() 44792f3ab15Sopenharmony_ci .and_then(|idx| x509_ctx.ex_data(idx)) 44892f3ab15Sopenharmony_ci .and_then(|ssl| ssl.ex_data(*hostname_idx)), 44992f3ab15Sopenharmony_ci ) { 45092f3ab15Sopenharmony_ci (Some(x509), Some(domain)) => verify_hostname(domain, &x509), 45192f3ab15Sopenharmony_ci _ => true, 45292f3ab15Sopenharmony_ci }; 45392f3ab15Sopenharmony_ci 45492f3ab15Sopenharmony_ci if !ok { 45592f3ab15Sopenharmony_ci x509_ctx.set_error(X509VerifyResult::APPLICATION_VERIFICATION); 45692f3ab15Sopenharmony_ci } 45792f3ab15Sopenharmony_ci 45892f3ab15Sopenharmony_ci ok 45992f3ab15Sopenharmony_ci } 46092f3ab15Sopenharmony_ci 46192f3ab15Sopenharmony_ci fn verify_hostname(domain: &str, cert: &X509Ref) -> bool { 46292f3ab15Sopenharmony_ci match cert.subject_alt_names() { 46392f3ab15Sopenharmony_ci Some(names) => verify_subject_alt_names(domain, names), 46492f3ab15Sopenharmony_ci None => verify_subject_name(domain, &cert.subject_name()), 46592f3ab15Sopenharmony_ci } 46692f3ab15Sopenharmony_ci } 46792f3ab15Sopenharmony_ci 46892f3ab15Sopenharmony_ci fn verify_subject_alt_names(domain: &str, names: Stack<GeneralName>) -> bool { 46992f3ab15Sopenharmony_ci let ip = domain.parse(); 47092f3ab15Sopenharmony_ci 47192f3ab15Sopenharmony_ci for name in &names { 47292f3ab15Sopenharmony_ci match ip { 47392f3ab15Sopenharmony_ci Ok(ip) => { 47492f3ab15Sopenharmony_ci if let Some(actual) = name.ipaddress() { 47592f3ab15Sopenharmony_ci if matches_ip(&ip, actual) { 47692f3ab15Sopenharmony_ci return true; 47792f3ab15Sopenharmony_ci } 47892f3ab15Sopenharmony_ci } 47992f3ab15Sopenharmony_ci } 48092f3ab15Sopenharmony_ci Err(_) => { 48192f3ab15Sopenharmony_ci if let Some(pattern) = name.dnsname() { 48292f3ab15Sopenharmony_ci if matches_dns(pattern, domain) { 48392f3ab15Sopenharmony_ci return true; 48492f3ab15Sopenharmony_ci } 48592f3ab15Sopenharmony_ci } 48692f3ab15Sopenharmony_ci } 48792f3ab15Sopenharmony_ci } 48892f3ab15Sopenharmony_ci } 48992f3ab15Sopenharmony_ci 49092f3ab15Sopenharmony_ci false 49192f3ab15Sopenharmony_ci } 49292f3ab15Sopenharmony_ci 49392f3ab15Sopenharmony_ci fn verify_subject_name(domain: &str, subject_name: &X509NameRef) -> bool { 49492f3ab15Sopenharmony_ci match subject_name.entries_by_nid(Nid::COMMONNAME).next() { 49592f3ab15Sopenharmony_ci Some(pattern) => { 49692f3ab15Sopenharmony_ci let pattern = match str::from_utf8(pattern.data().as_slice()) { 49792f3ab15Sopenharmony_ci Ok(pattern) => pattern, 49892f3ab15Sopenharmony_ci Err(_) => return false, 49992f3ab15Sopenharmony_ci }; 50092f3ab15Sopenharmony_ci 50192f3ab15Sopenharmony_ci // Unlike SANs, IP addresses in the subject name don't have a 50292f3ab15Sopenharmony_ci // different encoding. 50392f3ab15Sopenharmony_ci match domain.parse::<IpAddr>() { 50492f3ab15Sopenharmony_ci Ok(ip) => pattern 50592f3ab15Sopenharmony_ci .parse::<IpAddr>() 50692f3ab15Sopenharmony_ci .ok() 50792f3ab15Sopenharmony_ci .map_or(false, |pattern| pattern == ip), 50892f3ab15Sopenharmony_ci Err(_) => matches_dns(pattern, domain), 50992f3ab15Sopenharmony_ci } 51092f3ab15Sopenharmony_ci } 51192f3ab15Sopenharmony_ci None => false, 51292f3ab15Sopenharmony_ci } 51392f3ab15Sopenharmony_ci } 51492f3ab15Sopenharmony_ci 51592f3ab15Sopenharmony_ci fn matches_dns(mut pattern: &str, mut hostname: &str) -> bool { 51692f3ab15Sopenharmony_ci // first strip trailing . off of pattern and hostname to normalize 51792f3ab15Sopenharmony_ci if pattern.ends_with('.') { 51892f3ab15Sopenharmony_ci pattern = &pattern[..pattern.len() - 1]; 51992f3ab15Sopenharmony_ci } 52092f3ab15Sopenharmony_ci if hostname.ends_with('.') { 52192f3ab15Sopenharmony_ci hostname = &hostname[..hostname.len() - 1]; 52292f3ab15Sopenharmony_ci } 52392f3ab15Sopenharmony_ci 52492f3ab15Sopenharmony_ci matches_wildcard(pattern, hostname).unwrap_or_else(|| pattern.eq_ignore_ascii_case(hostname)) 52592f3ab15Sopenharmony_ci } 52692f3ab15Sopenharmony_ci 52792f3ab15Sopenharmony_ci fn matches_wildcard(pattern: &str, hostname: &str) -> Option<bool> { 52892f3ab15Sopenharmony_ci let wildcard_location = match pattern.find('*') { 52992f3ab15Sopenharmony_ci Some(l) => l, 53092f3ab15Sopenharmony_ci None => return None, 53192f3ab15Sopenharmony_ci }; 53292f3ab15Sopenharmony_ci 53392f3ab15Sopenharmony_ci let mut dot_idxs = pattern.match_indices('.').map(|(l, _)| l); 53492f3ab15Sopenharmony_ci let wildcard_end = match dot_idxs.next() { 53592f3ab15Sopenharmony_ci Some(l) => l, 53692f3ab15Sopenharmony_ci None => return None, 53792f3ab15Sopenharmony_ci }; 53892f3ab15Sopenharmony_ci 53992f3ab15Sopenharmony_ci // Never match wildcards if the pattern has less than 2 '.'s (no *.com) 54092f3ab15Sopenharmony_ci // 54192f3ab15Sopenharmony_ci // This is a bit dubious, as it doesn't disallow other TLDs like *.co.uk. 54292f3ab15Sopenharmony_ci // Chrome has a black- and white-list for this, but Firefox (via NSS) does 54392f3ab15Sopenharmony_ci // the same thing we do here. 54492f3ab15Sopenharmony_ci // 54592f3ab15Sopenharmony_ci // The Public Suffix (https://www.publicsuffix.org/) list could 54692f3ab15Sopenharmony_ci // potentially be used here, but it's both huge and updated frequently 54792f3ab15Sopenharmony_ci // enough that management would be a PITA. 54892f3ab15Sopenharmony_ci if dot_idxs.next().is_none() { 54992f3ab15Sopenharmony_ci return None; 55092f3ab15Sopenharmony_ci } 55192f3ab15Sopenharmony_ci 55292f3ab15Sopenharmony_ci // Wildcards can only be in the first component, and must be the entire first label 55392f3ab15Sopenharmony_ci if wildcard_location != 0 || wildcard_end != wildcard_location + 1 { 55492f3ab15Sopenharmony_ci return None; 55592f3ab15Sopenharmony_ci } 55692f3ab15Sopenharmony_ci 55792f3ab15Sopenharmony_ci let hostname_label_end = match hostname.find('.') { 55892f3ab15Sopenharmony_ci Some(l) => l, 55992f3ab15Sopenharmony_ci None => return None, 56092f3ab15Sopenharmony_ci }; 56192f3ab15Sopenharmony_ci 56292f3ab15Sopenharmony_ci let pattern_after_wildcard = &pattern[wildcard_end..]; 56392f3ab15Sopenharmony_ci let hostname_after_wildcard = &hostname[hostname_label_end..]; 56492f3ab15Sopenharmony_ci 56592f3ab15Sopenharmony_ci Some(pattern_after_wildcard.eq_ignore_ascii_case(hostname_after_wildcard)) 56692f3ab15Sopenharmony_ci } 56792f3ab15Sopenharmony_ci 56892f3ab15Sopenharmony_ci fn matches_ip(expected: &IpAddr, actual: &[u8]) -> bool { 56992f3ab15Sopenharmony_ci match *expected { 57092f3ab15Sopenharmony_ci IpAddr::V4(ref addr) => actual == addr.octets(), 57192f3ab15Sopenharmony_ci IpAddr::V6(ref addr) => actual == addr.octets(), 57292f3ab15Sopenharmony_ci } 57392f3ab15Sopenharmony_ci } 57492f3ab15Sopenharmony_ci 57592f3ab15Sopenharmony_ci #[test] 57692f3ab15Sopenharmony_ci fn test_dns_match() { 57792f3ab15Sopenharmony_ci use crate::ssl::connector::verify::matches_dns; 57892f3ab15Sopenharmony_ci assert!(matches_dns("website.tld", "website.tld")); // A name should match itself. 57992f3ab15Sopenharmony_ci assert!(matches_dns("website.tld", "wEbSiTe.tLd")); // DNS name matching ignores case of hostname. 58092f3ab15Sopenharmony_ci assert!(matches_dns("wEbSiTe.TlD", "website.tld")); // DNS name matching ignores case of subject. 58192f3ab15Sopenharmony_ci 58292f3ab15Sopenharmony_ci assert!(matches_dns("xn--bcher-kva.tld", "xn--bcher-kva.tld")); // Likewise, nothing special to punycode names. 58392f3ab15Sopenharmony_ci assert!(matches_dns("xn--bcher-kva.tld", "xn--BcHer-Kva.tLd")); // And punycode must be compared similarly case-insensitively. 58492f3ab15Sopenharmony_ci 58592f3ab15Sopenharmony_ci assert!(matches_dns("*.example.com", "subdomain.example.com")); // Wildcard matching works. 58692f3ab15Sopenharmony_ci assert!(matches_dns("*.eXaMpLe.cOm", "subdomain.example.com")); // Wildcard matching ignores case of subject. 58792f3ab15Sopenharmony_ci assert!(matches_dns("*.example.com", "sUbDoMaIn.eXaMpLe.cOm")); // Wildcard matching ignores case of hostname. 58892f3ab15Sopenharmony_ci 58992f3ab15Sopenharmony_ci assert!(!matches_dns("prefix*.example.com", "p.example.com")); // Prefix longer than the label works and does not match. 59092f3ab15Sopenharmony_ci assert!(!matches_dns("*suffix.example.com", "s.example.com")); // Suffix longer than the label works and does not match. 59192f3ab15Sopenharmony_ci 59292f3ab15Sopenharmony_ci assert!(!matches_dns("prefix*.example.com", "prefix.example.com")); // Partial wildcards do not work. 59392f3ab15Sopenharmony_ci assert!(!matches_dns("*suffix.example.com", "suffix.example.com")); // Partial wildcards do not work. 59492f3ab15Sopenharmony_ci 59592f3ab15Sopenharmony_ci assert!(!matches_dns("prefix*.example.com", "prefixdomain.example.com")); // Partial wildcards do not work. 59692f3ab15Sopenharmony_ci assert!(!matches_dns("*suffix.example.com", "domainsuffix.example.com")); // Partial wildcards do not work. 59792f3ab15Sopenharmony_ci 59892f3ab15Sopenharmony_ci assert!(!matches_dns("xn--*.example.com", "subdomain.example.com")); // Punycode domains with wildcard parts do not match. 59992f3ab15Sopenharmony_ci assert!(!matches_dns("xN--*.example.com", "subdomain.example.com")); // And we can't bypass a punycode test with weird casing. 60092f3ab15Sopenharmony_ci assert!(!matches_dns("Xn--*.example.com", "subdomain.example.com")); // And we can't bypass a punycode test with weird casing. 60192f3ab15Sopenharmony_ci assert!(!matches_dns("XN--*.example.com", "subdomain.example.com")); // And we can't bypass a punycode test with weird casing. 60292f3ab15Sopenharmony_ci } 60392f3ab15Sopenharmony_ci } 60492f3ab15Sopenharmony_ci } 60592f3ab15Sopenharmony_ci} 606