fix #5248: client: allow self-signed/untrusted certificate chains

instead of rejecting any non-leaf certificate not pre-validated by OpenSSL,
treat them as valid but keep track of the fact that the pre-validation result
is no logner trustable.

certificate chains completely trusted by openssl are still accepted like
before, and leaf certificates without a chain are also handled the same (since
the verify callback is only ever called with depth == 0 in that case).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
This commit is contained in:
Fabian Grünbichler 2024-03-26 09:15:03 +01:00 committed by Thomas Lamprecht
parent b1d01b82fc
commit df5854986c

View File

@ -332,6 +332,7 @@ impl HttpClient {
let interactive = options.interactive;
let fingerprint_cache = options.fingerprint_cache;
let prefix = options.prefix.clone();
let trust_openssl_valid = Arc::new(Mutex::new(true));
ssl_connector_builder.set_verify_callback(
openssl::ssl::SslVerifyMode::PEER,
move |valid, ctx| match Self::verify_callback(
@ -339,6 +340,7 @@ impl HttpClient {
ctx,
expected_fingerprint.as_ref(),
interactive,
Arc::clone(&trust_openssl_valid),
) {
Ok(None) => true,
Ok(Some(fingerprint)) => {
@ -561,8 +563,12 @@ impl HttpClient {
ctx: &mut X509StoreContextRef,
expected_fingerprint: Option<&String>,
interactive: bool,
trust_openssl: Arc<Mutex<bool>>,
) -> Result<Option<String>, Error> {
if openssl_valid {
let mut trust_openssl_valid = trust_openssl.lock().unwrap();
// we can only rely on openssl's prevalidation if we haven't forced it earlier
if openssl_valid && *trust_openssl_valid {
return Ok(None);
}
@ -571,11 +577,13 @@ impl HttpClient {
None => bail!("context lacks current certificate."),
};
let depth = ctx.error_depth();
if depth != 0 {
bail!("context depth != 0")
// force trust in case of a chain, but set flag to no longer trust prevalidation by openssl
if ctx.error_depth() > 0 {
*trust_openssl_valid = false;
return Ok(None);
}
// leaf certificate - if we end up here, we have to verify the fingerprint!
let fp = match cert.digest(openssl::hash::MessageDigest::sha256()) {
Ok(fp) => fp,
Err(err) => bail!("failed to calculate certificate FP - {}", err), // should not happen