indygreg / apple-platform-rs

Rust crates supporting Apple platform development
595 stars 49 forks source link

Wrong identifier used for nested bundles #149

Closed CendioOssman closed 2 weeks ago

CendioOssman commented 3 months ago

We have an application bundle that has another application bundle nested within it. rcodesign properly detects this, but it messes up the signature in a way that makes Apple reject the notarization:

notary log>   "issues": [
notary log>     {
notary log>       "architecture": "x86_64",
notary log>       "code": null,
notary log>       "docUrl": "https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/resolving_common_notarization_issues#3087735",
notary log>       "message": "The signature of the binary is invalid.",
notary log>       "path": "ThinLinc Client Notarized.app.zip/ThinLinc Client Notarized.app/Contents/lib/tlclient/vncviewer.app/Contents/MacOS/vncviewer",
notary log>       "severity": "error"
notary log>     }
notary log>   ],

The issue is the identifier used on the main binary for that nested bundle. It gets the generic one ("vncviewer"), rather than one of the bundle identifiers¹.

The workaround is to sign the nested bundle separately (although #148 caused some friction there).

¹ Apple seems to accept either identifier from ThinLinc Client Notarized.app or vncviewer.app

indygreg commented 3 weeks ago

I think this issue is related to your other issue (#148). It is likely there's a bug somewhere in bundle traversal that is causing things to get misidentified. Please provide more technical details so we can debug this.

indygreg commented 3 weeks ago

I'm going to call this a duplicate of #148 because the issues are intricately linked.

indygreg commented 3 weeks ago

Actually, this issue may be subtly different.

indygreg commented 2 weeks ago

Ok. I think I know what's happening.

First, fuller logs:

signing 1 nested bundles in the following order:
Contents/lib/tlclient/vncviewer.app
entering nested bundle Contents/lib/tlclient/vncviewer.app
...
signing main executable Contents/MacOS/vncviewer
leaving nested bundle Contents/lib/tlclient/vncviewer.app
...
signing Mach-O file Contents/lib/tlclient/unfsd
signing Mach-O file Contents/lib/tlclient/vncviewer.app/Contents/MacOS/vncviewer
signing main executable Contents/MacOS/tlclient

In non-shallow signing mode, we enter the nested bundle and sign Contents/lib/tlclient/vncviewer.app/Contents/MacOS/vncviewer.

Then later on when signing the main bundle we sign Contents/lib/tlclient/vncviewer.app/Contents/MacOS/vncviewer again!

I haven't verified this but I'm willing to bet that the second signing- which signs the Mach-O in the context of a regular Mach-O file and not the bundle's main executable - is setting the wrong identifier because of this fact.

We have special logic that looks for nested bundles. But only if the CodeResources rules entry has the nested flag set. In this case:

Contents/lib/tlclient/vncviewer.app matches rules2 CodeResourcesRule { pattern: "^.*", exclude: false, nested: false, omit: false, optional: false, weight: None, re: Regex("^.*") }

So essentially the CodeResources rules say the directory isn't special - can't be a bundle - and we proceed to walk it.

I initially wanted to say the structure of this bundle is malformed and the nested bundle must exist in a different directory.

But if Apple verifies/notarizes the bundle, then the bundle is seemingly correct. That's the test that matters.

I haven't verified this, but I'm willing to bet that because the nested flag isn't set that Apple's codesign will sign individual files in this bundle as files in the CodeResources file. Contrast with a single entry for the bundle's main executable's signature in the CodeResources file.

What we may need to do here is register nested bundles in an exclusion list as they are signed. Then when we go to sign the outer bundle, we ignore re-signing files we've seen before.

I need to think about this a bit more before I author a code change. But I'm optimistic I understand what's happening.

indygreg commented 2 weeks ago

Confirmed that Apple's codesign descends into the child bundle - outside a directory flagged as nested - and proceeds to sign its files as files, not a bundle.

                <key>lib/tlclient/vncviewer.app/Contents/Info.plist</key>
                <dict>
                        <key>hash</key>
                        <data>
                        VO6GT2ZWUznPmmykkuNWho2q+NU=
                        </data>
                        <key>hash2</key>
                        <data>
                        yRiSShAnwwfzYe+5VeFF0cSEGXq/BUrl0pt1asqkNOA=
                        </data>
                </dict>
                <key>lib/tlclient/vncviewer.app/Contents/MacOS/vncviewer</key>
                <dict>
                        <key>hash</key>
                        <data>
                        JeobnwK4ckgax0mgQQemKKO49jE=
                        </data>
                        <key>hash2</key>
                        <data>
                        YsVpcsFB4DdiN0ud7ySVxJXvksHgKDd4muxXkQkOOxQ=
                        </data>
                </dict>

So we need some mechanism to avoid signing entities that have already been signed as part of signing a child bundle. But we still need to capture those files in the parent's bundle.

Yuuon commented 2 weeks ago

We may have a simular(or same) issue with nested bundles. We have a in-Unity web browser plugin, which file structure looks like: VuplexWebViewMac.bundle

   |-- Contents
     |-- Frameworks
       |-- Vuplex WebView.app
         |-- Contents
           |-- Frameworks
             |-- Vuplex WebView Helper (GPU).app
             |-- Vuplex WebView Helper (Plugin).app
             |-- Vuplex WebView Helper (Renderer).app
             |-- Vuplex WebView Helper.app

So if I just try to sign it once for all from the top level, during the code sign, it will show message like:

entering nested bundle Contents/PlugIns/VuplexWebViewMac.bundle/Contents/Frameworks/Vuplex WebView.app
signing bundle at ./[filePath]/Contents/PlugIns/VuplexWebViewMac.bundle/Contents/Frameworks/Vuplex WebView.app into ./[filePath]/Contents/PlugIns/VuplexWebViewMac.bundle/Contents/Frameworks/Vuplex WebView.app
could not find main executable of presumed nested bundle: Contents/Frameworks/Vuplex WebView Helper (GPU).app
could not find main executable of presumed nested bundle: Contents/Frameworks/Vuplex WebView Helper (Plugin).app
could not find main executable of presumed nested bundle: Contents/Frameworks/Vuplex WebView Helper (Renderer).app
could not find main executable of presumed nested bundle: Contents/Frameworks/Vuplex WebView Helper.app
signing Mach-O file Contents/MacOS/Vuplex WebView
creating cryptographic signature with certificate [deleted]
bundle has no main executable to sign specially
leaving nested bundle Contents/PlugIns/VuplexWebViewMac.bundle/Contents/Frameworks/Vuplex WebView.app

And when notary, it will give "The signature of the binary is invalid." error for all the Mach-O file in the nested app, like below:

notary log>     {
notary log>       "architecture": "x86_64",
notary log>       "code": null,
notary log>       "docUrl": "https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/resolving_common_notarization_issues#3087735",
notary log>       "message": "The signature of the binary is invalid.",
notary log>       "path": "[filePath]/Contents/PlugIns/VuplexWebViewMac.bundle/Contents/Frameworks/Vuplex WebView.app/Contents/Frameworks/Vuplex WebView Helper (Plugin).app/Contents/MacOS/Vuplex WebView Helper (Plugin)",
notary log>       "severity": "error"
notary log>     },

Forget to mention, we're trying to sign our application on Linux. I tried to manually notary the files on Mac, using the traditional way(sign the last level of nested apps first, then sign their parent, then the parent of parent), and this method works. I checked this issue and related ones, it looks like closed to my problem but I'm not versy sure about it: Is the fix e04ddb2 suitable for my case too?

indygreg commented 2 weeks ago

@Yuuon aspects of your issue look to overlap with this issue.

If you are able to run a build of the main branch and report feedback, it would be helpful. You can download rcodesign executables from GitHub Actions from the rcodesign workflow.

If you still experience issues or can't test, please open a new issue and we can triage there.

CendioOssman commented 1 week ago

I can confirm that the current main successfully signs our complex bundle in a way that macOS accepts! Thanks for all your work!