http: verify TLS certs by default + cap the response body — set_verify(NONE) unconditionally let a MITM read/forge the account-registration & captcha traffic (usernames, emails, plaintext passwords) POSTed to operator-configured https URLs; now the connector's cert+hostname check stays on (opt out with http_tls_verify=no), and read_to_end is bounded to 4 MiB so a hostile endpoint can't OOM the worker
This commit is contained in:
parent
6682227f81
commit
801614605f
2 changed files with 19 additions and 4 deletions
21
src/http.rs
21
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
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue