diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2019-12-18 12:55:28 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2019-12-18 12:55:28 +0100 |
commit | 2524e1aa5fc2419956e7ab14e9fb6554f7c1d350 (patch) | |
tree | 6e6680cda93a2ecbcae17d01792963dca09df377 /openpgp/src | |
parent | 10423d8a857bc2b50de315ab7b482c3b38e016df (diff) |
openpgp: Handle malformed subpackets when parsing.
- If a syntactically malformed subpacket is encountered, we do the
same as for malformed packets, we turn the whole signature into an
unknown packet.
- Fixes #200.
Diffstat (limited to 'openpgp/src')
-rw-r--r-- | openpgp/src/packet/signature/subpacket.rs | 471 | ||||
-rw-r--r-- | openpgp/src/parse/parse.rs | 341 |
2 files changed, 365 insertions, 447 deletions
diff --git a/openpgp/src/packet/signature/subpacket.rs b/openpgp/src/packet/signature/subpacket.rs index 47b8b8f3..db80f4b0 100644 --- a/openpgp/src/packet/signature/subpacket.rs +++ b/openpgp/src/packet/signature/subpacket.rs @@ -67,8 +67,6 @@ use std::time; use quickcheck::{Arbitrary, Gen}; -use buffered_reader::BufferedReader; - use crate::{ Error, Result, @@ -317,35 +315,6 @@ mod tests { } } - -// Struct holding an arbitrary subpacket. -// -// The value is uninterpreted. -struct SubpacketRaw<'a> { - length: SubpacketLength, - pub critical: bool, - pub tag: SubpacketTag, - pub value: &'a [u8], -} - -impl<'a> fmt::Debug for SubpacketRaw<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let value = if self.value.len() > 16 { - &self.value[..16] - } else { - self.value - }; - - f.debug_struct("SubpacketRaw") - .field("length", &self.length) - .field("critical", &self.critical) - .field("tag", &self.tag) - .field(&format!("value ({} bytes)", self.value.len())[..], - &value) - .finish() - } -} - /// Subpacket area. pub struct SubpacketArea { /// The subpackets. @@ -386,75 +355,6 @@ impl Hash for SubpacketArea { } } -/// Iterates over SubpacketAreas yielding raw packets. -struct SubpacketAreaIterRaw<'a> { - reader: buffered_reader::Memory<'a, ()>, - data: &'a [u8], -} - -impl<'a> Iterator for SubpacketAreaIterRaw<'a> { - // Start, length. - type Item = (usize, usize, SubpacketRaw<'a>); - - fn next(&mut self) -> Option<Self::Item> { - let length = match SubpacketLength::parse(&mut self.reader) { - Err(_) => return None, - Ok(v) => v, - }; - let len = length.len as usize; - - if self.reader.data(len).unwrap().len() < len { - // Subpacket extends beyond the end of the hashed - // area. Skip it. - self.reader.drop_eof().unwrap(); - // XXX: Return an error. See #200. - return None; - } - - if len == 0 { - // Hmm, a zero length packet. In that case, there is - // no header. - // XXX: Return an error. See #200. - return self.next(); - } - - let tag = if let Ok(tag) = self.reader.data_consume_hard(1) { - tag[0] - } else { - return None; - }; - let len = len - 1; - - // The critical bit is the high bit. Extract it. - let critical = tag & (1 << 7) != 0; - // Then clear it from the type. - let tag = tag & !(1 << 7); - - let start = self.reader.total_out(); - assert!(start <= ::std::u16::MAX as usize); - assert!(len <= ::std::u16::MAX as usize); - - let _ = self.reader.consume(len); - - Some((start, len, - SubpacketRaw { - length, - critical: critical, - tag: tag.into(), - value: &self.data[start..start + len], - })) - } -} - -impl<'a> SubpacketAreaIterRaw<'a> { - fn new(data: &'a [u8]) -> SubpacketAreaIterRaw<'a> { - SubpacketAreaIterRaw { - reader: buffered_reader::Memory::new(data), - data, - } - } -} - impl fmt::Debug for SubpacketArea { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_list() @@ -474,17 +374,16 @@ impl<'a> IntoIterator for &'a SubpacketArea { impl SubpacketArea { /// Returns a new subpacket area based on `data`. - pub fn new(data: &[u8]) -> SubpacketArea { + pub fn new(packets: Vec<Subpacket>) -> SubpacketArea { SubpacketArea { - packets: SubpacketAreaIterRaw::new(data) - .map(|(_, _, raw)| raw.into()).collect(), + packets, parsed: Mutex::new(RefCell::new(None)), } } /// Returns a empty subpacket area. pub fn empty() -> SubpacketArea { - SubpacketArea::new(&[]) + SubpacketArea::new(Vec::new()) } // Initialize `Signature::hashed_area_parsed` from @@ -590,12 +489,13 @@ pub struct NotationData { impl NotationData { /// Creates a new Notation Data subpacket payload. - pub fn new<F>(name: &str, value: &[u8], flags: F) -> Self - where F: Into<Option<NotationDataFlags>> + pub fn new<N, F>(name: N, value: &[u8], flags: F) -> Self + where N: AsRef<[u8]>, + F: Into<Option<NotationDataFlags>>, { Self { flags: flags.into().unwrap_or_default(), - name: name.into(), + name: name.as_ref().into(), value: value.into(), } } @@ -626,6 +526,12 @@ impl Default for NotationDataFlags { } } +impl From<u32> for NotationDataFlags { + fn from(v: u32) -> Self { + Self(v) + } +} + const NOTATION_DATA_FLAG_HUMAN_READABLE: u32 = 0x80000000; impl NotationDataFlags { @@ -873,7 +779,7 @@ pub struct Subpacket { /// roundtrip the subpackets, perfectly reproducing all the bits. /// To allow for suboptimal encoding of lenghts, we store the /// length when we parse subpackets. - pub(crate) // For serialize/mod.rs. + pub(crate) // For serialize/mod.rs, parse/parse.rs. length: SubpacketLength, /// Critical flag. critical: bool, @@ -901,15 +807,19 @@ impl Subpacket { /// Creates a new subpacket. pub fn new(value: SubpacketValue, critical: bool) -> Result<Subpacket> { - Ok(Self::with_tag(value.tag()?, value, critical)) + Ok(Self::with_length_tag( + SubpacketLength::from(1 /* Tag */ + value.len() as u32), + value.tag()?, value, critical)) } - /// Creates a new subpacket with the given tag. - pub fn with_tag(tag: SubpacketTag, value: SubpacketValue, - critical: bool) - -> Subpacket { + /// Creates a new subpacket with the given length and tag. + pub(crate) fn with_length_tag(length: SubpacketLength, + tag: SubpacketTag, + value: SubpacketValue, + critical: bool) + -> Subpacket { Subpacket { - length: SubpacketLength::from(1 /* Tag */ + value.len() as u32), + length, critical, tag, value, @@ -932,276 +842,6 @@ impl Subpacket { } } -fn from_be_u16(value: &[u8]) -> Option<u16> { - if value.len() >= 2 { - Some((value[0] as u16) << 8 - | (value[1] as u16)) - } else { - None - } -} - -fn from_be_u32(value: &[u8]) -> Option<u32> { - if value.len() >= 4 { - Some((value[0] as u32) << 24 - | (value[1] as u32) << 16 - | (value[2] as u32) << 8 - | (value[3] as u32)) - } else { - None - } -} - -impl<'a> From<SubpacketRaw<'a>> for Subpacket { - fn from(raw: SubpacketRaw<'a>) -> Self { - let value : Option<SubpacketValue> - = match raw.tag { - SubpacketTag::SignatureCreationTime => - // The timestamp is in big endian format. - from_be_u32(raw.value).map(|v| { - SubpacketValue::SignatureCreationTime( - v.into()) - }), - - SubpacketTag::SignatureExpirationTime => - // The time delta is in big endian format. - from_be_u32(raw.value).map(|v| { - SubpacketValue::SignatureExpirationTime( - v.into()) - }), - - SubpacketTag::ExportableCertification => - // One u8 holding a bool. - if raw.value.len() == 1 { - Some(SubpacketValue::ExportableCertification( - raw.value[0] == 1u8)) - } else { - None - }, - - SubpacketTag::TrustSignature => - // Two u8s. - if raw.value.len() == 2 { - Some(SubpacketValue::TrustSignature { - level: raw.value[0], - trust: raw.value[1], - }) - } else { - None - }, - - SubpacketTag::RegularExpression => { - let trim = if raw.value.len() > 0 - && raw.value[raw.value.len() - 1] == 0 { 1 } else { 0 }; - Some(SubpacketValue::RegularExpression( - raw.value[..raw.value.len() - trim].to_vec())) - }, - - SubpacketTag::Revocable => - // One u8 holding a bool. - if raw.value.len() == 1 { - Some(SubpacketValue::Revocable(raw.value[0] != 0u8)) - } else { - None - }, - - SubpacketTag::KeyExpirationTime => - // The time delta is in big endian format. - from_be_u32(raw.value).map(|v| { - SubpacketValue::KeyExpirationTime( - v.into()) - }), - - SubpacketTag::PreferredSymmetricAlgorithms => - // array of one-octet values. - Some(SubpacketValue::PreferredSymmetricAlgorithms( - raw.value.iter().map(|o| (*o).into()).collect())), - - SubpacketTag::RevocationKey => - // 1 octet of class, 1 octet of pk algorithm, 20 bytes - // for a v4 fingerprint and 32 bytes for a v5 - // fingerprint. - if raw.value.len() > 2 { - Some(SubpacketValue::RevocationKey { - class: raw.value[0], - pk_algo: raw.value[1].into(), - fp: Fingerprint::from_bytes(&raw.value[2..]), - }) - } else { - None - }, - - SubpacketTag::Issuer => - Some(SubpacketValue::Issuer( - KeyID::from_bytes(&raw.value[..]))), - - SubpacketTag::NotationData => - if raw.value.len() > 8 { - let flags = from_be_u32(raw.value).unwrap(); - let name_len - = from_be_u16(&raw.value[4..]).unwrap() as usize; - let value_len - = from_be_u16(&raw.value[6..]).unwrap() as usize; - - if raw.value.len() == 8 + name_len + value_len { - Some(SubpacketValue::NotationData( - NotationData { - flags: NotationDataFlags(flags), - name: raw.value[8..8 + name_len].to_vec(), - value: raw.value[8 + name_len..].to_vec(), - })) - } else { - None - } - } else { - None - }, - - SubpacketTag::PreferredHashAlgorithms => - // array of one-octet values. - Some(SubpacketValue::PreferredHashAlgorithms( - raw.value.iter().map(|o| (*o).into()).collect())), - - SubpacketTag::PreferredCompressionAlgorithms => - // array of one-octet values. - Some(SubpacketValue::PreferredCompressionAlgorithms( - raw.value.iter().map(|o| (*o).into()).collect())), - - SubpacketTag::KeyServerPreferences => - // N octets of flags. - Some(SubpacketValue::KeyServerPreferences( - KeyServerPreferences::new(raw.value))), - - SubpacketTag::PreferredKeyServer => - // String. - Some(SubpacketValue::PreferredKeyServer( - raw.value.to_vec())), - - SubpacketTag::PrimaryUserID => - // 1 octet, Boolean - if raw.value.len() == 1 { - Some(SubpacketValue::PrimaryUserID( - raw.value[0] != 0u8)) - } else { - None - }, - - SubpacketTag::PolicyURI => - // String. - Some(SubpacketValue::PolicyURI(raw.value.to_vec())), - - SubpacketTag::KeyFlags => - // N octets of flags. - Some(SubpacketValue::KeyFlags(KeyFlags::new(&raw.value))), - - SubpacketTag::SignersUserID => - // String. - Some(SubpacketValue::SignersUserID(raw.value.to_vec())), - - SubpacketTag::ReasonForRevocation => - // 1 octet of revocation code, N octets of reason string - if raw.value.len() >= 1 { - Some(SubpacketValue::ReasonForRevocation { - code: raw.value[0].into(), - reason: raw.value[1..].to_vec(), - }) - } else { - None - }, - - SubpacketTag::Features => - // N octets of flags - Some(SubpacketValue::Features(Features::new(raw.value))), - - SubpacketTag::SignatureTarget => - // 1 octet public-key algorithm, 1 octet hash algorithm, - // N octets hash - if raw.value.len() > 2 { - Some(SubpacketValue::SignatureTarget { - pk_algo: raw.value[0].into(), - hash_algo: raw.value[1].into(), - digest: raw.value[2..].to_vec(), - }) - } else { - None - }, - - SubpacketTag::EmbeddedSignature => { - use crate::parse::Parse; - // A signature packet. - Some(SubpacketValue::EmbeddedSignature( - match Signature::from_bytes(&raw.value) { - Ok(s) => Packet::Signature(s), - Err(e) => { - use crate::packet::{Tag, Unknown}; - let mut u = Unknown::new(Tag::Signature, e); - u.set_body(raw.value.to_vec()); - Packet::Unknown(u) - }, - } - )) - }, - - SubpacketTag::IssuerFingerprint => { - let version = raw.value.get(0); - if let Some(version) = version { - if *version == 4 { - Some(SubpacketValue::IssuerFingerprint( - Fingerprint::from_bytes(&raw.value[1..]))) - } else { - None - } - } else { - None - } - }, - - SubpacketTag::PreferredAEADAlgorithms => - // array of one-octet values. - Some(SubpacketValue::PreferredAEADAlgorithms( - raw.value.iter().map(|o| (*o).into()).collect())), - - SubpacketTag::IntendedRecipient => { - let version = raw.value.get(0); - if let Some(version) = version { - if *version == 4 { - Some(SubpacketValue::IntendedRecipient( - Fingerprint::from_bytes(&raw.value[1..]))) - } else { - None - } - } else { - None - } - }, - - SubpacketTag::Reserved(_) - | SubpacketTag::PlaceholderForBackwardCompatibility - | SubpacketTag::Private(_) - | SubpacketTag::Unknown(_) => - // Unknown tag. - Some(SubpacketValue::Unknown(raw.value.to_vec())), - }; - - if let Some(value) = value { - Subpacket { - length: raw.length.clone(), - critical: raw.critical, - tag: raw.tag, - value: value, - } - } else { - // Invalid. - Subpacket { - length: raw.length.clone(), - critical: raw.critical, - tag: raw.tag, - value: SubpacketValue::Invalid(raw.value.to_vec()), - } - } - } -} - #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub(crate) struct SubpacketLength { /// The length. @@ -1219,47 +859,8 @@ impl From<u32> for SubpacketLength { } impl SubpacketLength { - /// Parses a subpacket length. - pub(crate) fn parse<C>(bio: &mut buffered_reader::Memory<C>) - -> io::Result<Self> { - let octet1 = bio.data_consume_hard(1)?[0]; - if octet1 < 192 { - // One octet. - Ok(Self { - len: octet1 as u32, - raw: None, // Unambiguous. - }) - } else if 192 <= octet1 && octet1 < 255 { - // Two octets length. - let octet2 = bio.data_consume_hard(1)?[0]; - let len = ((octet1 as u32 - 192) << 8) + octet2 as u32 + 192; - Ok(Self { - len, - raw: if Self::len_optimal_encoding(len) == 2 { - None - } else { - Some(vec![octet1, octet2]) - }, - }) - } else { - // Five octets. - assert_eq!(octet1, 255); - let len = bio.read_be_u32()?; - Ok(Self { - len, - raw: if Self::len_optimal_encoding(len) == 5 { - None - } else { - Some(vec![ - octet1, - (len >> 24) as u8, - (len >> 16) as u8, - (len >> 8) as u8, - (len >> 0) as u8, - ]) - }, - }) - } + pub(crate) fn new(len: u32, raw: Option<Vec<u8>>) -> Self { + Self { len, raw } } /// Writes the subpacket length to `w`. @@ -1282,6 +883,11 @@ impl SubpacketLength { } } + /// Returns the length. + pub(crate) fn len(&self) -> usize { + self.len as usize + } + /// Returns the length of the serialized subpacket length. pub(crate) fn serialized_len(&self) -> usize { if let Some(ref raw) = self.raw { @@ -1292,7 +898,7 @@ impl SubpacketLength { } /// Returns the length of the optimal encoding of `len`. - fn len_optimal_encoding(len: u32) -> usize { + pub(crate) fn len_optimal_encoding(len: u32) -> usize { if len < 192 { 1 } else if len < 16320 { @@ -1303,19 +909,6 @@ impl SubpacketLength { } } -#[cfg(test)] -quickcheck! { - fn length_roundtrip(l: u32) -> bool { - let length = SubpacketLength::from(l); - let mut encoded = Vec::new(); - length.serialize(&mut encoded).unwrap(); - assert_eq!(encoded.len(), length.serialized_len()); - let mut reader = buffered_reader::Memory::new(&encoded); - SubpacketLength::parse(&mut reader).unwrap().len == l - } -} - - impl SubpacketArea { /// Returns the *last* instance of the specified subpacket. fn subpacket(&self, tag: SubpacketTag) -> Option<&Subpacket> { diff --git a/openpgp/src/parse/parse.rs b/openpgp/src/parse/parse.rs index 8393c9f4..a05a40cd 100644 --- a/openpgp/src/parse/parse.rs +++ b/openpgp/src/parse/parse.rs @@ -24,15 +24,19 @@ use crate::{ packet::signature::Signature4, packet::prelude::*, Packet, + Fingerprint, KeyID, crypto::SessionKey, }; use crate::types::{ AEADAlgorithm, CompressionAlgorithm, - SignatureType, + Features, HashAlgorithm, + KeyFlags, + KeyServerPreferences, PublicKeyAlgorithm, + SignatureType, SymmetricAlgorithm, Timestamp, }; @@ -44,7 +48,14 @@ use crate::message::MessageValidator; mod partial_body; use self::partial_body::BufferedReaderPartialBodyFilter; -use crate::packet::signature::subpacket::SubpacketArea; +use crate::packet::signature::subpacket::{ + NotationData, + Subpacket, + SubpacketArea, + SubpacketLength, + SubpacketTag, + SubpacketValue, +}; mod packet_pile_parser; pub use self::packet_pile_parser::PacketPileParser; @@ -351,6 +362,17 @@ impl<'a> PacketHeaderParser<'a> { Ok(self.reader.read_be_u32()?) } + fn parse_bool(&mut self, name: &'static str) -> Result<bool> { + self.field(name, 1); + let v = self.reader.data_consume_hard(1)?[0]; + match v { + 0 => Ok(false), + 1 => Ok(true), + n => Err(Error::MalformedPacket( + format!("Invalid value for bool: {}", n)).into()), + } + } + fn parse_bytes(&mut self, name: &'static str, amount: usize) -> Result<Vec<u8>> { self.field(name, amount); @@ -1002,12 +1024,12 @@ impl Signature4 { let hash_algo = php_try!(php.parse_u8("hash_algo")); let hashed_area_len = php_try!(php.parse_be_u16("hashed_area_len")); let hashed_area - = php_try!(php.parse_bytes("hashed_area", - hashed_area_len as usize)); + = php_try!(SubpacketArea::parse(&mut php, + hashed_area_len as usize)); let unhashed_area_len = php_try!(php.parse_be_u16("unhashed_area_len")); let unhashed_area - = php_try!(php.parse_bytes("unhashed_area", - unhashed_area_len as usize)); + = php_try!(SubpacketArea::parse(&mut php, + unhashed_area_len as usize)); let digest_prefix1 = php_try!(php.parse_u8("digest_prefix1")); let digest_prefix2 = php_try!(php.parse_u8("digest_prefix2")); if ! pk_algo.for_signing() { @@ -1019,8 +1041,8 @@ impl Signature4 { let hash_algo = hash_algo.into(); let mut pp = php.ok(Packet::Signature(Signature4::new( typ.into(), pk_algo.into(), hash_algo, - SubpacketArea::new(&hashed_area), - SubpacketArea::new(&unhashed_area), + hashed_area, + unhashed_area, [digest_prefix1, digest_prefix2], mpis).into()))?; @@ -1158,6 +1180,309 @@ fn signature_parser_test () { } } +impl SubpacketArea { + // Parses a subpacket area. + fn parse<'a>(php: &mut PacketHeaderParser<'a>, mut limit: usize) + -> Result<Self> + { + let mut packets = Vec::new(); + while limit > 0 { + let p = Subpacket::parse(php, limit)?; + assert!(limit >= p.length.len() + p.length.serialized_len()); + limit -= p.length.len() + p.length.serialized_len(); + packets.push(p); + } + assert!(limit == 0); + Ok(Self::new(packets)) + } +} + +impl Subpacket { + // Parses a raw subpacket. + fn parse<'a>(php: &mut PacketHeaderParser<'a>, limit: usize) + -> Result<Self> { + let length = SubpacketLength::parse(&mut php.reader)?; + php.field("subpacket length", length.serialized_len()); + let len = length.len() as usize; + + if limit < len { + return Err(Error::MalformedPacket( + "Subpacket extends beyond the end of the subpacket area".into()) + .into()); + } + + if len == 0 { + return Err(Error::MalformedPacket("Zero-length subpacket".into()) + .into()); + } + + let tag = php.parse_u8("subpacket tag")?; + let len = len - 1; + + // Remember our position in the reader to check subpacket boundaries. + let total_out_before = php.reader.total_out(); + + // The critical bit is the high bit. Extract it. + let critical = tag & (1 << 7) != 0; + // Then clear it from the type and convert it. + let tag: SubpacketTag = (tag & !(1 << 7)).into(); + + let value = match tag { + SubpacketTag::SignatureCreationTime => + SubpacketValue::SignatureCreationTime( + php.parse_be_u32("sig creation time")?.into()), + SubpacketTag::SignatureExpirationTime => + SubpacketValue::SignatureExpirationTime( + php.parse_be_u32("sig expiry time")?.into()), + SubpacketTag::ExportableCertification => + SubpacketValue::ExportableCertification( + php.parse_bool("exportable")?), + SubpacketTag::TrustSignature => + SubpacketValue::TrustSignature { + level: php.parse_u8("trust level")?, + trust: php.parse_u8("trust value")?, + }, + SubpacketTag::RegularExpression => { + let mut v = php.parse_bytes("regular expr", len)?; + if v.len() == 0 || v[v.len() - 1] != 0 { + return Err(Error::MalformedPacket( + "Regular expression not 0-terminated".into()) + .into()); + } + v.pop(); + SubpacketValue::RegularExpression(v) + }, + SubpacketTag::Revocable => + SubpacketValue::Revocable(php.parse_bool("revocable")?), + SubpacketTag::KeyExpirationTime => + SubpacketValue::KeyExpirationTime( + php.parse_be_u32("key expiry time")?.into()), + SubpacketTag::PreferredSymmetricAlgorithms => + SubpacketValue::PreferredSymmetricAlgorithms( + php.parse_bytes("pref sym algos", len)? + .iter().map(|o| (*o).into()).collect()), + SubpacketTag::RevocationKey => { + // 1 octet of class, 1 octet of pk algorithm, 20 bytes + // for a v4 fingerprint and 32 bytes for a v5 + // fingerprint. + if len < 22 { + return Err(Error::MalformedPacket( + "Short revocation key subpacket".into()) + .into()); + } + SubpacketValue::RevocationKey { + class: php.parse_u8("class")?, + pk_algo: php.parse_u8("pk algo")?.into(), + fp: Fingerprint::from_bytes(&php.parse_bytes("fingerprint", + len - 2)?), + } + }, + SubpacketTag::Issuer => + SubpacketValue::Issuer( + KeyID::from_bytes(&php.parse_bytes("issuer", len)?)), + SubpacketTag::NotationData => { + let flags = php.parse_be_u32("flags")?; + let name_len = php.parse_be_u16("name len")? as usize; + let value_len = php.parse_be_u16("value len")? as usize; + + if len != 8 + name_len + value_len { + |