Skip to content

Commit

Permalink
apple-codesign: add a shallow signing mode
Browse files Browse the repository at this point in the history
We want to enable a signing mode that is similar to Apple's tooling in
that it doesn't traverse nested entities by default. This commit is the
start of that feature.

This should give people with complex signing requirements a way to opt
into multiple `sign` invocations instead of having to pass a series
of complex CLI arguments to control nested signing.
  • Loading branch information
indygreg committed Jan 16, 2024
1 parent 63bbaab commit 5c65540
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 6 deletions.
6 changes: 6 additions & 0 deletions apple-codesign/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
19 changes: 13 additions & 6 deletions apple-codesign/src/bundle_signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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 {
Expand Down
26 changes: 26 additions & 0 deletions apple-codesign/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1454,6 +1454,29 @@ struct Sign {
#[arg(long)]
exclude: Vec<String>,

/// 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,

Expand Down Expand Up @@ -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)?;
}
Expand All @@ -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());
Expand Down
15 changes: 15 additions & 0 deletions apple-codesign/src/signing_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ pub struct SigningSettings<'key> {
time_stamp_url: Option<Url>,
signing_time: Option<chrono::DateTime<chrono::Utc>>,
path_exclusion_patterns: Vec<Pattern>,
shallow: bool,

// Scope-specific settings.
// These are BTreeMap so when we filter the keys, keys with higher precedence come
Expand Down Expand Up @@ -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<SettingsScope>) -> DigestType {
self.digest_type
Expand Down Expand Up @@ -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()
Expand Down
10 changes: 10 additions & 0 deletions apple-codesign/tests/cmd/sign-bundle-multiple-macho.trycmd
Original file line number Diff line number Diff line change
Expand Up @@ -416,4 +416,14 @@ $ rcodesign print-signature-info MyApp.app.signed
- </plist>
- ''

$ 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

```
39 changes: 39 additions & 0 deletions apple-codesign/tests/cmd/sign-bundle-with-nested-framework.trycmd
Original file line number Diff line number Diff line change
Expand Up @@ -802,4 +802,43 @@ $ rcodesign print-signature-info MyApp.app.signed
- </plist>
- ''

$ 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

```
13 changes: 13 additions & 0 deletions apple-codesign/tests/cmd/sign.trycmd
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,19 @@ Options:
--exclude <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 <SLOT>
Smartcard slot number of signing certificate to use (9c is common)

Expand Down

0 comments on commit 5c65540

Please sign in to comment.