From 7c02aecc27ded7458bf32fc62193841919729ec1 Mon Sep 17 00:00:00 2001 From: Toshit Chawda Date: Sat, 7 Sep 2024 13:34:30 -0700 Subject: [PATCH] fix certificate tampered --- client/src/lib.rs | 20 ++++++----- client/src/stream_provider.rs | 52 +++++++++++++++++----------- client/src/utils.rs | 65 ++++++++++++++++++++++++++++++++++- 3 files changed, 106 insertions(+), 31 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 5de45b5..f1fbc58 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -201,6 +201,7 @@ cfg_if! { pub user_agent: String, #[wasm_bindgen(getter_with_clone)] pub pem_files: Vec, + pub disable_certificate_validation: bool, } } else { #[wasm_bindgen] @@ -212,6 +213,7 @@ cfg_if! { pub redirect_limit: usize, #[wasm_bindgen(getter_with_clone)] pub user_agent: String, + pub disable_certificate_validation: bool, } } } @@ -234,6 +236,7 @@ impl Default for EpoxyClientOptions { user_agent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36".to_string(), #[cfg(feature = "full")] pem_files: Vec::new(), + disable_certificate_validation: false, } } } @@ -366,9 +369,9 @@ impl EpoxyClient { redirect_limit: options.redirect_limit, user_agent: options.user_agent, #[cfg(feature = "full")] - certs_tampered: !options.pem_files.is_empty(), + certs_tampered: options.disable_certificate_validation || !options.pem_files.is_empty(), #[cfg(not(feature = "full"))] - certs_tampered: false, + certs_tampered: options.disable_certificate_validation, }) } @@ -605,10 +608,9 @@ impl EpoxyClient { .await?; if self.certs_tampered { - response.headers_mut().insert( - HeaderName::from_static("X-Epoxy-CertsTampered"), - HeaderValue::from_static("true"), - ); + response + .headers_mut() + .insert("X-Epoxy-CertsTampered", HeaderValue::from_static("true")); } let response_headers: Array = response @@ -688,11 +690,11 @@ impl EpoxyClient { if jv.is_array() { let arr = Array::from(&jv); arr.push(&v); - object_set(&raw_headers, &k, arr.into()); + object_set(&raw_headers, k, arr.into()); } else if jv.is_truthy() { - object_set(&raw_headers, &k, Array::of2(&jv, &v).into()); + object_set(&raw_headers, k, Array::of2(&jv, &v).into()); } else { - object_set(&raw_headers, &k, v); + object_set(&raw_headers, k, v); } } utils::define_property(&resp, "rawHeaders", raw_headers.into()); diff --git a/client/src/stream_provider.rs b/client/src/stream_provider.rs index c8b374c..8ad3f00 100644 --- a/client/src/stream_provider.rs +++ b/client/src/stream_provider.rs @@ -2,7 +2,7 @@ use std::{io::ErrorKind, pin::Pin, sync::Arc, task::Poll}; use cfg_if::cfg_if; use futures_rustls::{ - rustls::{ClientConfig, RootCertStore}, + rustls::{crypto::ring::default_provider, ClientConfig, RootCertStore}, TlsConnector, }; use futures_util::{ @@ -20,7 +20,11 @@ use wisp_mux::{ ClientMux, MuxStreamAsyncRW, MuxStreamIo, StreamType, }; -use crate::{console_log, utils::IgnoreCloseNotify, EpoxyClientOptions, EpoxyError}; +use crate::{ + console_log, + utils::{IgnoreCloseNotify, NoCertificateVerification}, + EpoxyClientOptions, EpoxyError, +}; pub type ProviderUnencryptedStream = MuxStreamIo; pub type ProviderUnencryptedAsyncRW = MuxStreamAsyncRW; @@ -60,27 +64,33 @@ impl StreamProvider { wisp_generator: ProviderWispTransportGenerator, options: &EpoxyClientOptions, ) -> Result { - cfg_if! { - if #[cfg(feature = "full")] { - let pems: Result, webpki::Error>, std::io::Error> = options - .pem_files - .iter() - .flat_map(|x| { - rustls_pemfile::certs(&mut std::io::BufReader::new(x.as_bytes())) - .map(|x| x.map(|x| webpki::anchor_from_trusted_cert(&x).map(|x| x.to_owned()))) - .collect::>() - }) - .collect(); - let pems = pems.map_err(EpoxyError::Pemfile)??; - let certstore = RootCertStore::from_iter(pems.into_iter().chain(TLS_SERVER_ROOTS.iter().cloned())); - } else { - let certstore = RootCertStore::from_iter(TLS_SERVER_ROOTS.iter().cloned()); + let client_config = if options.disable_certificate_validation { + ClientConfig::builder() + .dangerous() + .with_custom_certificate_verifier(Arc::new(NoCertificateVerification::new( + default_provider(), + ))) + } else { + cfg_if! { + if #[cfg(feature = "full")] { + let pems: Result, webpki::Error>, std::io::Error> = options + .pem_files + .iter() + .flat_map(|x| { + rustls_pemfile::certs(&mut std::io::BufReader::new(x.as_bytes())) + .map(|x| x.map(|x| webpki::anchor_from_trusted_cert(&x).map(|x| x.to_owned()))) + .collect::>() + }) + .collect(); + let pems = pems.map_err(EpoxyError::Pemfile)??; + let certstore = RootCertStore::from_iter(pems.into_iter().chain(TLS_SERVER_ROOTS.iter().cloned())); + } else { + let certstore = RootCertStore::from_iter(TLS_SERVER_ROOTS.iter().cloned()); + } } + ClientConfig::builder().with_root_certificates(certstore) } - - let client_config = ClientConfig::builder() - .with_root_certificates(certstore) - .with_no_client_auth(); + .with_no_client_auth(); let client_config = Arc::new(client_config); Ok(Self { diff --git a/client/src/utils.rs b/client/src/utils.rs index 466f4c6..df53dfc 100644 --- a/client/src/utils.rs +++ b/client/src/utils.rs @@ -6,12 +6,21 @@ use std::{ use async_trait::async_trait; use bytes::{buf::UninitSlice, BufMut, Bytes, BytesMut}; -use futures_rustls::TlsStream; +use futures_rustls::{ + rustls::{ + self, + client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, + crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider}, + DigitallySignedStruct, SignatureScheme, + }, + TlsStream, +}; use futures_util::{ready, AsyncRead, AsyncWrite, Future, Stream, StreamExt, TryStreamExt}; use http::{HeaderValue, Uri}; use hyper::{body::Body, rt::Executor}; use js_sys::{Array, ArrayBuffer, JsString, Object, Uint8Array}; use pin_project_lite::pin_project; +use rustls_pki_types::{CertificateDer, ServerName, UnixTime}; use send_wrapper::SendWrapper; use wasm_bindgen::{prelude::*, JsCast, JsValue}; use wasm_bindgen_futures::JsFuture; @@ -300,6 +309,60 @@ impl AsyncWrite for IgnoreCloseNotify { } } +#[derive(Debug)] +pub struct NoCertificateVerification(CryptoProvider); + +impl NoCertificateVerification { + pub fn new(provider: CryptoProvider) -> Self { + Self(provider) + } +} + +impl ServerCertVerifier for NoCertificateVerification { + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp: &[u8], + _now: UnixTime, + ) -> Result { + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls12_signature( + message, + cert, + dss, + &self.0.signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls13_signature( + message, + cert, + dss, + &self.0.signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + self.0.signature_verification_algorithms.supported_schemes() + } +} + pub fn is_redirect(code: u16) -> bool { [301, 302, 303, 307, 308].contains(&code) }