Skip to content

Commit

Permalink
apple-codesign: digest data between segments
Browse files Browse the repository at this point in the history
Before, we failed to digest data between Mach-O segments. This
caused the emitted digests to be incorrect after a segment gap
was encountered.

We fix the issue by emitting a virtual segment containing the
bytes between the segments.

Closes #588.
Closes #616.
  • Loading branch information
indygreg committed Aug 6, 2022
1 parent 15a7613 commit b2d240f
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 6 deletions.
7 changes: 5 additions & 2 deletions apple-codesign/CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
======
Expand Down
33 changes: 29 additions & 4 deletions apple-codesign/src/macho.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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::<Vec<_>>()
}
Expand Down

0 comments on commit b2d240f

Please sign in to comment.