All this security and privacy stuff was going to be so simple, according to the presentations at WWDC this year. To quote Robert Kendall-Kuppe in session 703:
“So to start I want to say that for any of the software that you previously distributed it doesn’t have to meet any new requirements. You can submit your existing distributed software for notarization as is without change, but for new software you need to make sure that it meets a few security requirements. In particular, it has to be completely and correctly signed and it needs to adopt the hardened runtime. And by new software I mean software signed on or after June 1st of 2019.”
As far as privacy is concerned, that was to produce a level playing field: to be hardened, apps are normally built with Xcode 10 or later, which also incorporates entitlements to allow access to resources which are protected in Mojave and later. Versions of Xcode earlier than 10 support neither hardening nor those privacy entitlements.
The way this works in practice was explained at WWDC the previous year: apps built against earlier versions of the SDK, for High Sierra or before, weren’t expected to have any privacy entitlements. Should they need to access newly protected resources, the user would be invited to give their consent. However, apps built against the Mojave SDK in Xcode 10 aren’t given such leeway: if they try to access a protected resource without having that entitlement built in (and providing a purpose string for the access), they can be crashed immediately.
Under the new rules which came into effect on 1 June 2019, all newly-built apps had to be hardened and notarized, thus would use the Mojave SDK and follow the new rules on accessing protected resources.
You can see this at work when you open an app in Mojave. Here’s one that I built earlier using the Mojave SDK, but which hasn’t been hardened. This sequence of log entries shows it being checked by TCC, the Transparency Consent and Control system (times in seconds).
First TCC calls for a trust evaluation on the app
1.594743 tccd Security SecTrustEvaluateIfNecessary
1.596248 trustd asynchronously fetching CRL (http://crl.apple.com/root.crl) for client (tccd[311]/0#-1 LF=0)
1.596295 trustd cert[2]: AnchorTrusted =(leaf)[force]> 0
It then records that the app was built against the Mojave SDK, so knows to apply the new rules, even though it’s not hardened or notarized
1.597495 tccd /Users/hoakley/Documents/TaccySoft.app/Contents/MacOS/Taccy (offset 0) linked against SDK version 0xa0e00
1.597508 tccd -[TCCDPlatformMacOS evaluatePolicyForPromptingforService:byIdentity:attributionChain:]: policyResult = 0; isApplePlatformBinary = 0
1.597536 tccd Derived team id of <private> for {ID: co.eclecticlight.Taccy, PID[80913], auid: 501, euid: 501, binary path: '/Users/hoakley/Documents/TaccySoft.app/Contents/MacOS/Taccy'}
1.597625 tccd do_TCCCreateDesignatedRequirementIdentityFromAuditToken: creating property list from identity dictionary
1.597641 tccd do_TCCCreateDesignatedRequirementIdentityFromAuditToken: property list successfully created
When the same app is built with hardening enabled, the sequence is very similar, but TCC has a different prompting policy for the hardened runtime. This results in some different entries:
1.627691 tccd /Users/hoakley/Documents/TaccyHard.app/Contents/MacOS/Taccy (offset 0) linked against SDK version 0xa0e00
1.627717 tccd Prompting policy for hardened runtime; service: kTCCServiceAppleEvents requires entitlement com.apple.security.automation.apple-events but it is missing for ACC:{ID: co.eclecticlight.Taccy, PID[80928], auid: 501, euid: 501, binary path: '/Users/hoakley/Documents/TaccyHard.app/Contents/MacOS/Taccy'}, REQ:{ID: com.apple.appleeventsd, PID[69], auid: 55, euid: 55, binary path: '/System/Library/CoreServices/appleeventsd'}
1.627719 tccd -[TCCDPlatformMacOS evaluatePolicyForPromptingforService:byIdentity:attributionChain:]: policyResult = 4; isApplePlatformBinary = 0
1.627743 tccd Derived team id of <private> for {ID: co.eclecticlight.Taccy, PID[80928], auid: 501, euid: 501, binary path: '/Users/hoakley/Documents/TaccyHard.app/Contents/MacOS/Taccy'}
1.627858 tccd do_TCCCreateDesignatedRequirementIdentityFromAuditToken: creating property list from identity dictionary
1.627873 tccd do_TCCCreateDesignatedRequirementIdentityFromAuditToken: property list successfully created
If that app were then to try accessing protected resources, whether hardened or not, TCC could crash it immediately, as it lacks those entitlements and purpose strings. Given the fact that, from 1 June 2019, all newly signed apps had to follow this, privacy behaviours in Mojave and Catalina were now going to be more uniform.
Until Apple changed its policy on 3 September 2019.
From then until the end of the year, developers can notarize newly-signed apps which are built with older versions of Xcode and which aren’t hardened. Privacy protection still applies to them, of course, but it means that for four months developers are able to sign and notarize new apps which are given the same special treatment TCC has to apply to legacy apps: they aren’t required to have privacy entitlements or to have reason strings.
For a user, this is deeply confusing. For three months, from 1 June 2019, all newly signed macOS apps had to be hardened and notarized, which almost inevitably required them to be built against the Mojave SDK, and to meet Mojave’s stringent requirements for privacy protection. If one of those apps attempts to access protected resources to which it lacks an entitlement, it is almost certain to be crashed.
Then for four months from September to the end of 2019, all newly signed apps still have to be notarized, but as that no longer requires hardening, and they can be built with older SDKs, they needn’t meet the 2018 Mojave requirements for privacy protection, in which case they will be treated not as new but legacy apps. This allows them to access protected resources without entitlements or reason strings, and TCC merely prompts the user for their consent.
I’m sure that no one would try to exploit that. Would they?