diff --git a/apple-codesign/CHANGELOG.md b/apple-codesign/CHANGELOG.md index 54d014862..18570749d 100644 --- a/apple-codesign/CHANGELOG.md +++ b/apple-codesign/CHANGELOG.md @@ -14,6 +14,12 @@ Released on ReleaseDate. notarizations to Apple. (#124) * Fixed a bug where `.dSYM/` directories were incorrectly signed as bundles. (#128) +* The `sign` command has gained a `--shallow` argument to prevent traversing into + nested entities when signing. It currently only prevents traversal into nested + bundles. In the future, behavior may be expanded to also exclude signing of + additional Mach-O binaries inside bundles, among other potential changes. + Ultimately we want this signing mode to converge with the default behavior of + Apple's tooling. * (API) `BundleSigner` now requires calling `collect_nested_bundles()` to register child bundles for signing instead of signing all nested bundles by default. * aws-config 0.57 -> 1.1. diff --git a/apple-codesign/src/bundle_signing.rs b/apple-codesign/src/bundle_signing.rs index 0f0aed12a..b70663afe 100644 --- a/apple-codesign/src/bundle_signing.rs +++ b/apple-codesign/src/bundle_signing.rs @@ -173,10 +173,14 @@ impl BundleSigner { bundles.sort_by(|(a, _), (b, _)| b.len().cmp(&a.len())); if !bundles.is_empty() { - warn!( - "signing {} nested bundles in the following order:", - bundles.len() - ); + if settings.shallow() { + warn!("{} nested bundles will be copied instead of signed because shallow signing enabled:", bundles.len()); + } else { + warn!( + "signing {} nested bundles in the following order:", + bundles.len() + ); + } for bundle in &bundles { warn!("{}", bundle.0); } @@ -186,8 +190,11 @@ impl BundleSigner { let nested_dest_dir = dest_dir.join(rel); warn!("entering nested bundle {}", rel,); - // If we excluded this bundle from signing, just copy all the files. - if settings.path_exclusion_pattern_matches(rel) { + if settings.shallow() { + warn!("shallow signing enabled; bundle will be copied instead of signed"); + copy_bundle(&nested.bundle, &nested_dest_dir)?; + } else if settings.path_exclusion_pattern_matches(rel) { + // If we excluded this bundle from signing, just copy all the files. warn!("bundle is in exclusion list; it will be copied instead of signed"); copy_bundle(&nested.bundle, &nested_dest_dir)?; } else { diff --git a/apple-codesign/src/cli/mod.rs b/apple-codesign/src/cli/mod.rs index 2ae96369d..f42cda101 100644 --- a/apple-codesign/src/cli/mod.rs +++ b/apple-codesign/src/cli/mod.rs @@ -1454,6 +1454,29 @@ struct Sign { #[arg(long)] exclude: Vec, + /// Do not traverse into nested entities when signing. + /// + /// Some signable entities (like directory bundles) have child/nested entities + /// that can be signed. By default, signing traversed into these entities and + /// signs all entities recursively. + /// + /// Activating shallow signing mode using this flag overrides the default behavior. + /// + /// The behavior of this flag is subject to change. As currently implemented it + /// will: + /// + /// * Prevent signing nested bundles when signing a bundle. e.g. if an app + /// bundle contains a framework, only the app bundle will be signed. Additional + /// Mach-O binaries within a bundle may still be signed with this flag set. + /// + /// Activating shallow signing mode can result in signing failures if the skipped + /// nested entities aren't signed. For example, when signing an application bundle + /// containing an unsigned nested bundle/framework, signing will fail with an + /// error about a missing code signature. Always be sure to sign nested entities + /// before their parents when this mode is activated. + #[arg(long)] + shallow: bool, + /// Path to Mach-O binary to sign input_path: PathBuf, @@ -1511,6 +1534,8 @@ impl CliCommand for Sign { settings.set_team_id(team_name); } + settings.set_shallow(self.shallow); + for pattern in &self.exclude { settings.add_path_exclusion(pattern)?; } @@ -1527,6 +1552,7 @@ impl CliCommand for Sign { self.input_path.display(), output_path.display() ); + signer.sign_path(&self.input_path, output_path)?; } else { warn!("signing {} in place", self.input_path.display()); diff --git a/apple-codesign/src/signing_settings.rs b/apple-codesign/src/signing_settings.rs index b527f26b4..7dc653744 100644 --- a/apple-codesign/src/signing_settings.rs +++ b/apple-codesign/src/signing_settings.rs @@ -302,6 +302,7 @@ pub struct SigningSettings<'key> { time_stamp_url: Option, signing_time: Option>, path_exclusion_patterns: Vec, + shallow: bool, // Scope-specific settings. // These are BTreeMap so when we filter the keys, keys with higher precedence come @@ -512,6 +513,19 @@ impl<'key> SigningSettings<'key> { Ok(()) } + /// Whether to perform a shallow, non-nested signing operation. + /// + /// Can mean different things to different entities. For bundle signing, shallow + /// mode means not to recurse into nested bundles. + pub fn shallow(&self) -> bool { + self.shallow + } + + /// Set whether to perform a shallow signing operation. + pub fn set_shallow(&mut self, v: bool) { + self.shallow = v; + } + /// Obtain the primary digest type to use. pub fn digest_type(&self, scope: impl AsRef) -> DigestType { self.digest_type @@ -1230,6 +1244,7 @@ impl<'key> SigningSettings<'key> { signing_time: self.signing_time, team_id: self.team_id.clone(), path_exclusion_patterns: self.path_exclusion_patterns.clone(), + shallow: self.shallow, digest_type: self .digest_type .clone() diff --git a/apple-codesign/tests/cmd/sign-bundle-multiple-macho.trycmd b/apple-codesign/tests/cmd/sign-bundle-multiple-macho.trycmd index 14c7060cc..9929c3801 100644 --- a/apple-codesign/tests/cmd/sign-bundle-multiple-macho.trycmd +++ b/apple-codesign/tests/cmd/sign-bundle-multiple-macho.trycmd @@ -416,4 +416,14 @@ $ rcodesign print-signature-info MyApp.app.signed - - '' +$ rcodesign sign --shallow --entitlements-xml-file entitlements.plist MyApp.app MyApp.app.signed-shallow +setting entitlements XML for main signing target from path entitlements.plist +signing MyApp.app to MyApp.app.signed-shallow +signing bundle at MyApp.app +signing bundle at MyApp.app into MyApp.app.signed-shallow +signing Mach-O file Contents/MacOS/bin +signing Mach-O file Contents/MacOS/lib.dylib +signing Mach-O file Contents/Resources/non-nested-bin +signing main executable Contents/MacOS/MyApp + ``` diff --git a/apple-codesign/tests/cmd/sign-bundle-with-nested-framework.trycmd b/apple-codesign/tests/cmd/sign-bundle-with-nested-framework.trycmd index 3b9845e31..9db6469ee 100644 --- a/apple-codesign/tests/cmd/sign-bundle-with-nested-framework.trycmd +++ b/apple-codesign/tests/cmd/sign-bundle-with-nested-framework.trycmd @@ -802,4 +802,43 @@ $ rcodesign print-signature-info MyApp.app.signed - - '' +$ rcodesign sign --shallow MyApp.app MyApp.app.signed-shallow +? 1 +signing MyApp.app to MyApp.app.signed-shallow +signing bundle at MyApp.app +3 nested bundles will be copied instead of signed because shallow signing enabled: +Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app +Contents/Frameworks/Sparkle.framework/Versions/A +Contents/Frameworks/Sparkle.framework +entering nested bundle Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app +shallow signing enabled; bundle will be copied instead of signed +leaving nested bundle Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app +entering nested bundle Contents/Frameworks/Sparkle.framework/Versions/A +shallow signing enabled; bundle will be copied instead of signed +leaving nested bundle Contents/Frameworks/Sparkle.framework/Versions/A +entering nested bundle Contents/Frameworks/Sparkle.framework +shallow signing enabled; bundle will be copied instead of signed +leaving nested bundle Contents/Frameworks/Sparkle.framework +signing bundle at MyApp.app into MyApp.app.signed-shallow +Error: binary does not have code signature data + +$ rcodesign sign --shallow MyApp.app.signed MyApp.app.signed-shallow +signing MyApp.app.signed to MyApp.app.signed-shallow +signing bundle at MyApp.app.signed +3 nested bundles will be copied instead of signed because shallow signing enabled: +Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app +Contents/Frameworks/Sparkle.framework/Versions/A +Contents/Frameworks/Sparkle.framework +entering nested bundle Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app +shallow signing enabled; bundle will be copied instead of signed +leaving nested bundle Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app +entering nested bundle Contents/Frameworks/Sparkle.framework/Versions/A +shallow signing enabled; bundle will be copied instead of signed +leaving nested bundle Contents/Frameworks/Sparkle.framework/Versions/A +entering nested bundle Contents/Frameworks/Sparkle.framework +shallow signing enabled; bundle will be copied instead of signed +leaving nested bundle Contents/Frameworks/Sparkle.framework +signing bundle at MyApp.app.signed into MyApp.app.signed-shallow +signing main executable Contents/MacOS/MyApp + ``` diff --git a/apple-codesign/tests/cmd/sign.trycmd b/apple-codesign/tests/cmd/sign.trycmd index 5d3633302..8965467a0 100644 --- a/apple-codesign/tests/cmd/sign.trycmd +++ b/apple-codesign/tests/cmd/sign.trycmd @@ -333,6 +333,19 @@ Options: --exclude Glob expression of paths to exclude from signing + --shallow + Do not traverse into nested entities when signing. + + Some signable entities (like directory bundles) have child/nested entities that can be signed. By default, signing traversed into these entities and signs all entities recursively. + + Activating shallow signing mode using this flag overrides the default behavior. + + The behavior of this flag is subject to change. As currently implemented it will: + + * Prevent signing nested bundles when signing a bundle. e.g. if an app bundle contains a framework, only the app bundle will be signed. Additional Mach-O binaries within a bundle may still be signed with this flag set. + + Activating shallow signing mode can result in signing failures if the skipped nested entities aren't signed. For example, when signing an application bundle containing an unsigned nested bundle/framework, signing will fail with an error about a missing code signature. Always be sure to sign nested entities before their parents when this mode is activated. + --smartcard-slot Smartcard slot number of signing certificate to use (9c is common)