From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 820081FF15D for ; Thu, 8 Aug 2024 16:25:48 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 684AA1F39A; Thu, 8 Aug 2024 16:25:58 +0200 (CEST) From: Shannon Sterz To: pbs-devel@lists.proxmox.com Date: Thu, 8 Aug 2024 16:25:18 +0200 Message-Id: <20240808142518.248338-1-s.sterz@proxmox.com> X-Mailer: git-send-email 2.39.2 MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.047 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pbs-devel] [PATCH proxmox-offline-mirror] verifier: add ability to verify with keyrings X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox Backup Server development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pbs-devel-bounces@lists.proxmox.com Sender: "pbs-devel" some vendors don't just provide a single certificate but an entire keyring for their repositories. apt can handle those gracefully, so should we. this commit adds the ability to verify a repository's signatures with a keyring. we use `PacketParserEOF` to check if a stream of packets is likely a single certificate or a keyring. if it is a keyring, we try to verify a message with all certificates in the ring and only fail if no certificate can verify the message. Reported-by: Maximiliano Sandoval Signed-off-by: Shannon Sterz --- this came up in the enterprise support, so i can't link the exact ticket here, but it was about mirroring this mellanox repository: https://linux.mellanox.com/public/repo/mlnx_ofed/24.04-0.7.0.0/debian12.1/amd64/ mellanox says to install the corresponding keyring with this command: ``` wget -qO - https://www.mellanox.com/downloads/ofed/RPM-GPG-KEY-Mellanox | \ gpg --dearmor | tee /etc/apt/trusted.gpg.d/mellanox.gpg ``` i tested the below code with this mellanox repo, our no-subscription repo and the debian security updates repo. src/helpers/verifier.rs | 71 +++++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/src/helpers/verifier.rs b/src/helpers/verifier.rs index ed986af..0930bd7 100644 --- a/src/helpers/verifier.rs +++ b/src/helpers/verifier.rs @@ -1,12 +1,13 @@ -use anyhow::{bail, Error}; +use anyhow::{bail, format_err, Error}; use sequoia_openpgp::{ + cert::CertParser, parse::{ stream::{ DetachedVerifierBuilder, MessageLayer, MessageStructure, VerificationError, VerificationHelper, VerifierBuilder, }, - Parse, + PacketParser, PacketParserResult, Parse, }, policy::StandardPolicy, types::HashAlgorithm, @@ -96,8 +97,6 @@ pub(crate) fn verify_signature( detached_sig: Option<&[u8]>, weak_crypto: &WeakCryptoConfig, ) -> Result, Error> { - let cert = Cert::from_bytes(key)?; - let mut policy = StandardPolicy::new(); if weak_crypto.allow_sha1 { policy.accept_hash(HashAlgorithm::SHA1); @@ -113,23 +112,55 @@ pub(crate) fn verify_signature( } } - let helper = Helper { cert: &cert }; - - let verified = if let Some(sig) = detached_sig { - let mut verifier = - DetachedVerifierBuilder::from_bytes(sig)?.with_policy(&policy, None, helper)?; - verifier.verify_bytes(msg)?; - msg.to_vec() - } else { - let mut verified = Vec::new(); - let mut verifier = VerifierBuilder::from_bytes(msg)?.with_policy(&policy, None, helper)?; - let bytes = io::copy(&mut verifier, &mut verified)?; - println!("{bytes} bytes verified"); - if !verifier.message_processed() { - bail!("Failed to verify message!"); + let verifier = |cert| { + let helper = Helper { cert: &cert }; + + if let Some(sig) = detached_sig { + let mut verifier = + DetachedVerifierBuilder::from_bytes(sig)?.with_policy(&policy, None, helper)?; + verifier.verify_bytes(msg)?; + Ok(msg.to_vec()) + } else { + let mut verified = Vec::new(); + let mut verifier = + VerifierBuilder::from_bytes(msg)?.with_policy(&policy, None, helper)?; + let bytes = io::copy(&mut verifier, &mut verified)?; + println!("{bytes} bytes verified"); + if !verifier.message_processed() { + bail!("Failed to verify message!"); + } + Ok(verified) } - verified }; - Ok(verified) + let mut packed_parser = PacketParser::from_bytes(key)?; + + // parse all packets to see whether this is a simple certificate or a keyring + while let PacketParserResult::Some(pp) = packed_parser { + packed_parser = pp.recurse()?.1; + } + + if let PacketParserResult::EOF(eof) = packed_parser { + // verify against a single certificate + if eof.is_cert().is_ok() { + let cert = Cert::from_bytes(key)?; + return verifier(cert); + // verify against a keyring + } else if eof.is_keyring().is_ok() { + let packed_parser = PacketParser::from_bytes(key)?; + + return CertParser::from(packed_parser) + // flatten here as we ignore packets that aren't a certificate + .flatten() + // keep trying to verify the message until the first certificate that succeeds + .find_map(|c| verifier(c).ok()) + // if no certificate verified the message, abort + .ok_or_else(|| format_err!("No key in keyring could verify the message!")); + } + } + + // neither a keyring nor a certificate was detect, so we abort here + Err(format_err!( + "'key-path' contains neither a keyring nor a certificate, aborting!" + )) } -- 2.39.2 _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel