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};
|
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.
|
/// 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(
|
pub fn post(
|
||||||
url: &str,
|
url: &str,
|
||||||
content_type: &str,
|
content_type: &str,
|
||||||
body: &str,
|
body: &str,
|
||||||
headers: &[(String, String)],
|
headers: &[(String, String)],
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
|
verify: bool,
|
||||||
) -> Result<(u16, String), String> {
|
) -> Result<(u16, String), String> {
|
||||||
let (scheme, rest) = url.split_once("://").ok_or("bad url (no scheme)")?;
|
let (scheme, rest) = url.split_once("://").ok_or("bad url (no scheme)")?;
|
||||||
let (hostport, path) = match rest.split_once('/') {
|
let (hostport, path) = match rest.split_once('/') {
|
||||||
|
|
@ -47,18 +55,23 @@ pub fn post(
|
||||||
|
|
||||||
let raw = if https {
|
let raw = if https {
|
||||||
let mut b = SslConnector::builder(SslMethod::tls()).map_err(|e| e.to_string())?;
|
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 connector = b.build();
|
||||||
let mut tls = connector.connect(host, stream).map_err(|e| e.to_string())?;
|
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())?;
|
tls.write_all(req.as_bytes()).map_err(|e| e.to_string())?;
|
||||||
let mut buf = Vec::new();
|
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
|
buf
|
||||||
} else {
|
} else {
|
||||||
let mut s = stream;
|
let mut s = stream;
|
||||||
s.write_all(req.as_bytes()).map_err(|e| e.to_string())?;
|
s.write_all(req.as_bytes()).map_err(|e| e.to_string())?;
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
let _ = s.read_to_end(&mut buf);
|
let _ = s.take(MAX_RESPONSE).read_to_end(&mut buf);
|
||||||
buf
|
buf
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -471,6 +471,7 @@ impl Server {
|
||||||
headers: Vec<(String, String)>,
|
headers: Vec<(String, String)>,
|
||||||
) {
|
) {
|
||||||
let tx = self.event_tx.clone();
|
let tx = self.event_tx.clone();
|
||||||
|
let verify = self.conf_bool("http_tls_verify", true);
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let (status, body) = crate::http::post(
|
let (status, body) = crate::http::post(
|
||||||
&url,
|
&url,
|
||||||
|
|
@ -478,6 +479,7 @@ impl Server {
|
||||||
&body,
|
&body,
|
||||||
&headers,
|
&headers,
|
||||||
std::time::Duration::from_secs(10),
|
std::time::Duration::from_secs(10),
|
||||||
|
verify,
|
||||||
)
|
)
|
||||||
.unwrap_or((0, String::new()));
|
.unwrap_or((0, String::new()));
|
||||||
let _ = tx.send(crate::ircd::Event::HttpResult {
|
let _ = tx.send(crate::ircd::Event::HttpResult {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue