From 997858edd89007728ebabcf26d94fee1ddb35219 Mon Sep 17 00:00:00 2001 From: Michal Tarnacki Date: Tue, 7 Jul 2026 09:51:14 +0200 Subject: [PATCH] fix(migtd): bound untrusted input sizes before allocation Reject pre-session payloads larger than 64 KiB before allocating the receive buffer, so an untrusted peer cannot request an excessive allocation. Bound peer certificates by the TLS input buffer size before DER parsing. The previous 8 KiB limit was arbitrary and smaller than a valid MigTD certificate, which embeds a TDX quote and the event log. Apply the bound in every verification path instead of the legacy one only, and export TLS_BUFFER_SIZE so the limit tracks the buffer it derives from. Signed-off-by: Michal Tarnacki Assisted-by: GitHub Copilot:GPT-5.6 Sol Assisted-by: GitHub Copilot:Claude Opus 5 --- src/crypto/src/rustls_impl/tls.rs | 3 +- src/migtd/src/migration/pre_session_data.rs | 9 +++++ src/migtd/src/ratls/server_client.rs | 43 ++++++++++++--------- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/crypto/src/rustls_impl/tls.rs b/src/crypto/src/rustls_impl/tls.rs index 1346ed9b9..139ad73ff 100644 --- a/src/crypto/src/rustls_impl/tls.rs +++ b/src/crypto/src/rustls_impl/tls.rs @@ -31,6 +31,7 @@ use super::ecdsa::EcdsaPk; pub type TlsLibError = rustls::Error; const TLS_CUSTOM_CALLBACK_ERROR: &str = "TlsCustomCallbackError"; +pub const TLS_BUFFER_SIZE: usize = 16 * 0x1000; pub struct SecureChannel { conn: TlsConnection, @@ -381,6 +382,7 @@ impl ClientCertVerifier for Verifier { } pub(crate) mod connection { + use super::TLS_BUFFER_SIZE; use alloc::{collections::VecDeque, sync::Arc, vec::Vec}; use async_io::{AsyncRead, AsyncWrite}; use rust_std_stub::io; @@ -396,7 +398,6 @@ pub(crate) mod connection { use zeroize::Zeroize; pub const PAGE_SIZE: usize = 0x1000; - pub const TLS_BUFFER_SIZE: usize = 16 * PAGE_SIZE; pub const APP_DATA_BUFFER_LIMIT: usize = PAGE_SIZE; #[derive(Debug)] diff --git a/src/migtd/src/migration/pre_session_data.rs b/src/migtd/src/migration/pre_session_data.rs index 8603c6ec5..ae057a3bb 100644 --- a/src/migtd/src/migration/pre_session_data.rs +++ b/src/migtd/src/migration/pre_session_data.rs @@ -194,6 +194,15 @@ pub(super) async fn receive_pre_session_data_packet MAX_PRE_SESSION_PAYLOAD { + log::error!( + "receive_pre_session_data_packet: payload size {} exceeds max {}\n", + pre_session_data_payload_size, + MAX_PRE_SESSION_PAYLOAD + ); + return Err(MigrationResult::InvalidParameter); + } let mut pre_session_data_payload = vec![0u8; pre_session_data_payload_size]; receive_pre_session_data(transport, &mut pre_session_data_payload) .await diff --git a/src/migtd/src/ratls/server_client.rs b/src/migtd/src/ratls/server_client.rs index bb7c282d9..1410aafb6 100644 --- a/src/migtd/src/ratls/server_client.rs +++ b/src/migtd/src/ratls/server_client.rs @@ -7,7 +7,7 @@ use async_io::{AsyncRead, AsyncWrite}; use crypto::{ ecdsa::EcdsaPk, hash::digest_sha384, - tls::{SecureChannel, TlsConfig}, + tls::{SecureChannel, TlsConfig, TLS_BUFFER_SIZE}, x509::{ AlgorithmIdentifier, AnyRef, BitStringRef, Certificate, CertificateBuilder, Decode, Encode, ExtendedKeyUsage, Extension, Extensions, Tag, @@ -762,6 +762,25 @@ fn verify_client_cert(cert: &[u8], quote: &[u8]) -> core::result::Result<(), Cry verify_peer_cert(false, cert, quote) } +// A certificate larger than the whole TLS input buffer can never be received. +const MAX_PEER_CERTIFICATE_SIZE: usize = TLS_BUFFER_SIZE; + +fn parse_peer_certificate(cert: &[u8]) -> core::result::Result { + if cert.len() > MAX_PEER_CERTIFICATE_SIZE { + log::error!( + "Certificate too large: {} bytes (max {})\n", + cert.len(), + MAX_PEER_CERTIFICATE_SIZE + ); + return Err(CryptoError::ParseCertificate); + } + + Certificate::from_der(cert).map_err(|e| { + log::error!("Failed to parse certificate from DER. Error: {:?}\n", e); + CryptoError::ParseCertificate + }) +} + #[cfg(not(feature = "test_disable_ra_and_accept_all"))] mod verify { use super::*; @@ -782,10 +801,7 @@ mod verify { log::error!("Mutual attestation error {:?}.\n", e); CryptoError::TlsVerifyPeerCert(MUTUAL_ATTESTATION_ERROR.to_string()) })?; - let cert = Certificate::from_der(cert).map_err(|e| { - log::error!("Failed to parse certificate from DER. Error: {:?}\n", e); - CryptoError::ParseCertificate - })?; + let cert = parse_peer_certificate(cert)?; let extensions = cert.tbs_certificate.extensions.as_ref().ok_or_else(|| { log::error!("Failed to get certificate extensions.\n"); CryptoError::ParseCertificate @@ -850,10 +866,7 @@ mod verify { cert: &[u8], peer_data: &[u8], ) -> core::result::Result<(), CryptoError> { - let cert = Certificate::from_der(cert).map_err(|_| { - log::error!("Failed to parse certificate from DER.\n"); - CryptoError::ParseCertificate - })?; + let cert = parse_peer_certificate(cert)?; let extensions = cert.tbs_certificate.extensions.as_ref().ok_or_else(|| { log::error!("Failed to get certificate extensions.\n"); @@ -915,10 +928,7 @@ mod verify { cert: &[u8], peer_data: &[u8], ) -> core::result::Result<(), CryptoError> { - let cert = Certificate::from_der(cert).map_err(|_| { - log::error!("Failed to parse certificate from DER.\n"); - CryptoError::ParseCertificate - })?; + let cert = parse_peer_certificate(cert)?; let extensions = cert.tbs_certificate.extensions.as_ref().ok_or_else(|| { log::error!("Failed to get certificate extensions.\n"); @@ -994,10 +1004,7 @@ mod verify { cert: &[u8], peer_data: &[u8], ) -> core::result::Result<(), CryptoError> { - let cert = Certificate::from_der(cert).map_err(|_| { - log::error!("Failed to parse certificate from DER.\n"); - CryptoError::ParseCertificate - })?; + let cert = parse_peer_certificate(cert)?; let extensions = cert.tbs_certificate.extensions.as_ref().ok_or_else(|| { log::error!("Failed to get certificate extensions.\n"); @@ -1182,7 +1189,7 @@ mod verify { cert: &[u8], _quote_local: &[u8], ) -> core::result::Result<(), CryptoError> { - let cert = Certificate::from_der(cert).map_err(|_| CryptoError::ParseCertificate)?; + let cert = parse_peer_certificate(cert)?; let extensions = cert .tbs_certificate