Blog

Notarise me softly

October 11, 2019

This is not yet another rant about macOS application notarisation, what it means, is it good or bad… There are already plenty of those, telling you everything you should and should not know about the subject. This is rather a developer’s rand about Apple not fixing some significant bugs in macOS, after more than a year since the issue has been initially reported, not even in the latest major version update (Catalina). Many people don’t like reading rants, especially full of technical mumbo-jumbo, so I will try to make this read easy and pleasant for a casual reader.

Ever since Apple introduced application sandboxing and XPC services, it advises and heavily advocates a “new” paradigm in designing macOS applications; identify critical and independent parts of an application, factor them out, implement each as a separate XPC service and let the application manage communication between services. Make everything more secure and robust by sandboxing all components and giving each only absolutely necessary minimal set of sandbox entitlements. This reduces potential damage in case any component gets securely compromised. There are a couple of WWDC videos explaining and implementing this concept, like this one (Apple Developer Program membership required). Accessing user contacts is a good example. If an application needs access to contacts, it should not access them directly, but from a dedicated XPC service instead, passing required data back to the application. The service is given only AddressBook (Contacts) sandbox entitlement, so in case it ever gets compromised, no damage to user’s personal data could be done, as the service cannot access disks nor network. This is explained at the beginning of the video, from 1:17 to 3:41.

GitFinder is implemented exactly that way. But why would GitFinder need access to contacts in the first place? If you are familiar with other git clients, you noticed they display an image/avatar of people, who created commits. To provide those images, most git clients rely on Gravatar. I like Gravatar and I use it too, but if a committer is a person already in my contacts, I prefer to see the image stored there. Therefore, GifFinder first consults the contacts for the image of a person and only if it isn't available, or the access to contacts is declined, GitFinder falls back to Gravatar. At the time of this writing, this is the only reason GitFinder would access user contacts.

What does application need to fulfil to access contacts? Firstly, it has to declare intention for such access and that is accomplished by defining NSContactsUsageDescription key in application’s Info.plist file. This key was optional prior Mojave, but is mandatory since. The value of the key should describe reasons to access contacts. The reason is displayed the very first time the application tries to access contacts in the user consent dialog. Using the dialog, a user can allow or decline access. Once the decision is made, it can be changed from System Preferences > Security & Privacy > Contacts. If the access is declined, every further call to Contacts API will fail. Secondly, in case of a sandboxed application, it also needs to have AddressBook entitlement set to YES. Without it, calls to Contacts API will fail as well. It is important to remember that in order to access contacts, the user consent dialog HAS to appear the first time such access is attempted. And in order for the consent dialog to appear, both contact usage description key and AddressBook sandbox entitlement must be defined.

Things get a bit more complicated if an attempt to access contact comes not from the application, but from its bundled XPC service, like explained in the video. In such case, the first assumption would be that the consent dialog will not contain the name of the application, but the name of the service, something like:

XPC service consent dialog

No doubt this would be very confusing to users. Luckily, smart folks at Apple thought of such scenarios, so macOS security subsystem (for simplicity, I will refer to it as TCC) understands that the AvatarService is trying to access contacts ON BEHALF of GitFinder application. Executing

log stream --debug --predicate 'subsystem == "com.apple.TCC"'

from the terminal while attempting to access contacts from the XPC service produces a logging message:

Prompting for access to kTCCServiceAddressBook from /Applications/
GitFinder.app/Contents/MacOS/GitFinder on behalf of /Applications/
GitFinder.app/Contents/XPCServices/ag.zigz.GitFinder.AvatarService.xpc/
Contents/MacOS/ag.zigz.GitFinder.AvatarService

Note the part “on behalf of” in this logging message. The consequence is users still see unambiguous consent dialog, like:

Application consent dialog

It seems Apple engineers thought about everything to enable proper access to contacts while still designing and implementing applications according to the “new” paradigm they promote so much. This all works nice and well.

Well, at least it did until notarisation kicked in. Notarisation is not the exact reason for this bug, but hardened runtime is, which is required will be required after January 2020 to notarise an application. The application + XPC service combo build with hardened runtime fails to access contacts. Having proper sandbox entitlement defined, the XPC service requests access to contacts and passes that request to the application, just like it did before hardened runtime. However, this time around the TCC does not understand that the request comes from the XPC service on behalf of the application. Instead, it thinks the request comes from the application itself and hence checks if the application has required sandbox entitlement (AddressBook). Since the entitlement isn't there, because the application does NOT need it at all, access to contact is denied. This can be seen logging again messages from the TCC while attempting to access contacts from hardened runtime combo. The logging message of interest this time says:

Prompting policy for hardened runtime; service: kTCCServiceAddressBook
requires entitlement com.apple.security.personal-information.addressbook
but it is missing for RESP:{…responsible path: ‘/Applications/
GitFinder.app/Contents/MacOS/GitFinder' … REQ:{…binary path:
'Applications/GitFinder.app/Contents/XPCServices/ag.zigz.GitFinder.
AvatarService.xpc/Contents/MacOS/ag.zigz.GitFinder.AvatarService.xpc’}

The consequence of absence of absolutely unnecessary sandbox entitlement is the user consent dialog NOT being displayed at all. The user won't even know the application would like to access contacts and the access will be permanently declined. It is interesting to note this happens only if the application hasn't tried to access contacts before. If the access has already been granted (or declined), for example running an older version of the application built without hardened runtime, everything continues to work as expected.

This behaviour is not limited to accessing contacts only. I haven't tried it myself, but I strongly believe the same thing happens when trying to access calendar, reminders, camera, microphone and other system resources requiring user consent. Thus, I would suspect a lot of application out there are affected. Therefore I filed a bug report immediately after I discovered it and I even opened a DTS (Developer Technical Support) issue with Apple support engineers. Unfortunately, even though I had (and I guess I still have) thorough correspondence with two support people, I haven't had any chance to discuss the issue with someone actually responsible for fixing it. Guys I talked to were very friendly and supportive, but the best they could tell me was “… however, you have to understand that macOS Engineering has many different competing demands on their time, and it’s ultimately their decision as to how to prioritise a bug like this against all the other work that needs to be done. DTS has only limited input in that process, and I’ve already taken this as far as I can.”

Perhaps macOS engineers do not consider this bug to be of some higher priority, because there is a really simple workaround: just define AddressBook sandbox entitlement for application too and everything will start working again as it should. But that BRAKES COMPLETELY nice privilege separation that Apple advocates so much, explained in the video; completely unnecessary sandbox entitlement defined for the application only increases chances of damages, as the application, being bigger and more complicated, is much easier target for security attacks than tiny dedicated XPC service is.

Catalina was officially released just a few days ago, with this issue not fixed! Recent relaxation of notarisation requirements postponed the final outcome of this story for January 2020. My hopes this bug will be fixed until then are on life support, but still alive. Surely there aren't any intention from Apple to fix this in Mojave, now that Catalina is out. When the time of unavoidable hardened runtime comes, GitFinder will probably go with superfluous AddressBook sandbox entitlement defined for the application, thus breaking nice privilege separation it possesses now. Such a shame for Apple breaking their own well thought-out design concepts and paradigms.

← All articles