From 801614605fa47d730092ff50fc20cbc2aa0d1875 Mon Sep 17 00:00:00 2001 From: reverse Date: Wed, 19 Aug 2026 00:37:50 +0000 Subject: [PATCH] =?UTF-8?q?http:=20verify=20TLS=20certs=20by=20default=20+?= =?UTF-8?q?=20cap=20the=20response=20body=20=E2=80=94=20set=5Fverify(NONE)?= =?UTF-8?q?=20unconditionally=20let=20a=20MITM=20read/forge=20the=20accoun?= =?UTF-8?q?t-registration=20&=20captcha=20traffic=20(usernames,=20emails,?= =?UTF-8?q?=20plaintext=20passwords)=20POSTed=20to=20operator-configured?= =?UTF-8?q?=20https=20URLs;=20now=20the=20connector's=20cert+hostname=20ch?= =?UTF-8?q?eck=20stays=20on=20(opt=20out=20with=20http=5Ftls=5Fverify=3Dno?= =?UTF-8?q?),=20and=20read=5Fto=5Fend=20is=20bounded=20to=204=20MiB=20so?= =?UTF-8?q?=20a=20hostile=20endpoint=20can't=20OOM=20the=20worker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/http.rs | 21 +++++++++++++++++---- src/server.rs | 2 ++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/http.rs b/src/http.rs index 40bcd80..e8f6b4d 100644 --- a/src/http.rs +++ b/src/http.rs @@ -10,14 +10,22 @@ use std::time::Duration; use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; +/// Largest response body we'll buffer. A hostile or compromised endpoint could +/// otherwise stream unbounded data and OOM the worker thread (and, via mimalloc, +/// the whole process). +const MAX_RESPONSE: u64 = 4 * 1024 * 1024; + /// POST `body` to `url` with `content_type` and extra `headers`. Blocking. -/// Returns `(status_code, response_body)` or an error string. +/// Returns `(status_code, response_body)` or an error string. `verify` turns on +/// TLS certificate + hostname verification for https (the safe default); pass +/// `false` only for a trusted private endpoint with a self-signed cert. pub fn post( url: &str, content_type: &str, body: &str, headers: &[(String, String)], timeout: Duration, + verify: bool, ) -> Result<(u16, String), String> { let (scheme, rest) = url.split_once("://").ok_or("bad url (no scheme)")?; let (hostport, path) = match rest.split_once('/') { @@ -47,18 +55,23 @@ pub fn post( let raw = if https { let mut b = SslConnector::builder(SslMethod::tls()).map_err(|e| e.to_string())?; - b.set_verify(SslVerifyMode::NONE); // APIs are usually behind a trusted reverse proxy + // Default: keep the connector's secure verification (cert chain + hostname, + // applied by connect(host, ..)). Only downgrade when the operator opts out + // for a trusted reverse-proxy / self-signed endpoint. + if !verify { + b.set_verify(SslVerifyMode::NONE); + } let connector = b.build(); let mut tls = connector.connect(host, stream).map_err(|e| e.to_string())?; tls.write_all(req.as_bytes()).map_err(|e| e.to_string())?; let mut buf = Vec::new(); - let _ = tls.read_to_end(&mut buf); // close => EOF (Connection: close) + let _ = tls.take(MAX_RESPONSE).read_to_end(&mut buf); // bounded; close => EOF buf } else { let mut s = stream; s.write_all(req.as_bytes()).map_err(|e| e.to_string())?; let mut buf = Vec::new(); - let _ = s.read_to_end(&mut buf); + let _ = s.take(MAX_RESPONSE).read_to_end(&mut buf); buf }; diff --git a/src/server.rs b/src/server.rs index 69fd538..708c514 100644 --- a/src/server.rs +++ b/src/server.rs @@ -471,6 +471,7 @@ impl Server { headers: Vec<(String, String)>, ) { let tx = self.event_tx.clone(); + let verify = self.conf_bool("http_tls_verify", true); std::thread::spawn(move || { let (status, body) = crate::http::post( &url, @@ -478,6 +479,7 @@ impl Server { &body, &headers, std::time::Duration::from_secs(10), + verify, ) .unwrap_or((0, String::new())); let _ = tx.send(crate::ircd::Event::HttpResult {