The problem
Apple has faced three fundamental problems when building defences around macOS (… I am not writing that these concepts also apply to iOS and other *OSs…):
- Code integrity
- How do I know that the binary I am about to run is exactly the one distributed by the developer?
- How do I detect further modifications/tampering (typical example: malware that “patches” binaries)?
- Identity and trust
- Who created this binary
- Can I trust this entity?
- How do I build a chain of trust?
- Sandboxing and capabilities
- What can be accessed by this binary (e.g.: cam, microphone, keychain, …)
- How can sandboxing be enforced without encoding the rules in kernel?
Apple solution is a Multi-layered system.
Apple did not “solve the problem” with a digital signature. That would have been too simplistic and inefficient. Technically speaking, Apple has built a SuperBlob containing specialised blobs, each addressing a specific task.
This is totally in line with the separation of concerns principle: each blob responds to a specific problem.
The five fundamental blobs
Blob 1: CodeDirectory (slot 0)
This is the beating heart of the solution - everything else is rooted on it.
The problem
- how can the integrity of a 100MB binary be checked without fully loading it into memory?
- If the system was to check the hash of the whole file, the performances would not be acceptable.
The solution
- the binary is splitted into pages (usually, 4kB)
- each page has its own hash (SHA-256)
The CodeDirectory blob contains:
- an array storing the hashes of the pages
- metadata such as
- Identifier: bundle ID
com.apple.ls
- Team ID: who signed?
- CDHash: hash of the CodeDirectory structure itself. This signs the binary for Gatekeeper.
Benefit of this solution
- Lazy verification: the OS verifies only the pages that are load (on-demand paging)
- If only 10 pages out of 1000 are executed, only these 10 are verified
Notes
There is a variant - the CodeDirectory Alternate. The slots dedicated to this structure are addressed from 0x1000 onwards and are used for multiple hashing algorithms (SHA1 legacy and SHA-256)
Blob 2: Entitlements (slot 5) and DER entitlements (slot 7)
The problems
- How to specify “this binary can access the camera” without kernel hardcoding
- How to implement an efficient sandboxing, with flexible rules
The solution
- XML (or DER, for recent binaries) records declaring capabilities
<key>com.apple.security.device.camera</key><true/>
- XML (slot 5) - pure text format, human readable. Historically used
- DER (slot 7) - compact binary format (ASN.1). More efficient in terms of performances.
- The kernel reads the entitlements of a binary before its execution
- During
execve(), the kernel invokes cs_entitlements_blob_get()
- Gatekeeper validates before, to allow for code execution
Blob 3: Requirements (slot 2)
The problems
- Certifying that a binary has been signed is not enough.
- It is crucial understanding:
- signed by whom?
- with which conditions?