diff --git a/apple-codesign/CHANGELOG.rst b/apple-codesign/CHANGELOG.rst index be033466c..e43225789 100644 --- a/apple-codesign/CHANGELOG.rst +++ b/apple-codesign/CHANGELOG.rst @@ -35,6 +35,11 @@ method was required for use with Apple's Transporter application, which we no longer use so we're no longer bound by its requirements. The old method will likely be dropped from a future release. +* Fixed signing of Mach-O binaries having a gap between segments. (This is known + to commonly occur in Go binaries.) In previous versions, we would compute + digests of the file incorrectly and would encounter an assertion when copying + Mach-O data to the output binary. Both of these issues should now be fixed. + (#588 and #616) * minicbor crate upgraded from version 0.15. This created API differences in remote signing code. * The APIs around Mach-O file parsing have been significantly overhauled. It @@ -43,8 +48,6 @@ to custom Mach-O functionality. Most code interfacing with a Mach-O file now uses these types. The ``AppleSignable`` trait has been deleted as it is no longer needed since we have the dedicated ``MachOBinary`` type. -* Fixed a bug where we hit an assertion when adding code signatures to Mach-O - binaries having padding between segments. (#588 and #616) 0.16.0 ====== diff --git a/apple-codesign/src/macho.rs b/apple-codesign/src/macho.rs index fdea89e27..ef048b859 100644 --- a/apple-codesign/src/macho.rs +++ b/apple-codesign/src/macho.rs @@ -198,6 +198,8 @@ impl<'a> MachOBinary<'a> { } /// Obtain __LINKEDIT segment data before the signature data. + /// + /// If there is no signature, returns all the data for the __LINKEDIT segment. pub fn linkedit_data_before_signature(&self) -> Option<&[u8]> { let segment = self .macho @@ -221,17 +223,40 @@ impl<'a> MachOBinary<'a> { /// The slices are likely digested as part of computing digests /// embedded in the code directory. pub fn digestable_segment_data(&self) -> Vec<&[u8]> { + let mut last_segment_end_offset = None; + self.macho .segments .iter() .filter(|segment| !matches!(segment.name(), Ok(SEG_PAGEZERO))) - .map(|segment| { + .flat_map(|segment| { + let mut segments = vec![]; + + let our_start_offset = segment.fileoff; + let our_end_offset = segment.fileoff + segment.filesize; + + // There can be gaps between segments. Emit that gap as its own virtual segment. + if let Some(last_segment_end_offset) = last_segment_end_offset { + if our_start_offset > last_segment_end_offset { + let missing_data = + &self.data[last_segment_end_offset as usize..our_start_offset as usize]; + segments.push(missing_data); + } + } + + last_segment_end_offset = Some(our_end_offset); + + // The __LINKEDIT segment is only digested up to the code signature. if matches!(segment.name(), Ok(SEG_LINKEDIT)) { - self.linkedit_data_before_signature() - .expect("__LINKEDIT data should resolve") + segments.push( + self.linkedit_data_before_signature() + .expect("__LINKEDIT data should resolve"), + ); } else { - segment.data + segments.push(segment.data); } + + segments }) .collect::>() }