Wednesday, June 6, 2012

Gatekeeper, XProtect and the Quarantine attribute

Apps can opt-in to Gatekeeper and Xprotect protection by adding LSFileQuarantineEnabled to their Contents/Info.plist. This means that any files created by that app will get tagged with the apple quarantine HFS+ extended attribute. For example, here's what chrome looks like when downloaded with Safari:
$ ls -l@eOd Downloads/googlechrome.dmg 
-rw-r--r--@ 1 user  group  - 39937778 Apr 16 11:31 Downloads/googlechrome.dmg
 com.apple.diskimages.fsck       20 
 com.apple.diskimages.recentcksum       79 
 com.apple.metadata:kMDItemDownloadedDate       53 
 com.apple.metadata:kMDItemWhereFroms      210 
 com.apple.quarantine       57 
and the same file downloaded with curl (which isn't a cocoa app and hence can't opt-in):
$ curl -o chrome_curl.dmg -L https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 38.0M  100 38.0M    0     0  15.7M      0  0:00:02  0:00:02 --:--:-- 18.2M
$ ls -l@eOd chrome_curl.dmg 
-rw-r--r--  1 user  group  - 39937627 Apr 20 14:47 chrome_curl.dmg
Looking at the contents of the extended attributes we see a bunch of interesting metadata, including the HTTP referrer in a binary plist inside 'com.apple.metadata:kMDItemWhereFroms' and the quaratine attribute in 'com.apple.quarantine'.
$ xattr -l Downloads/googlechrome.dmg 
com.apple.diskimages.fsck: |?W??\<!*
                                    ?#Z???r??u
com.apple.diskimages.recentcksum: i:325165 on 3A5356B1-31CB-32FB-AFC7-DBE23A5C1547 @ 1334601060 - CRC32:$AE7BD4AF
com.apple.metadata:kMDItemDownloadedDate:
00000000  62 70 6C 69 73 74 30 30 A1 01 33 41 B5 42 0E 78  |bplist00..3A.B.x|
00000010  17 F3 17 08 0A 00 00 00 00 00 00 01 01 00 00 00  |................|
00000020  00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 13                                   |.....|
00000035
com.apple.metadata:kMDItemWhereFroms:
00000000  62 70 6C 69 73 74 30 30 A2 01 02 5F 10 3D 68 74  |bplist00..._.=ht|
00000010  74 70 73 3A 2F 2F 64 6C 2E 67 6F 6F 67 6C 65 2E  |tps://dl.google.|
00000020  63 6F 6D 2F 63 68 72 6F 6D 65 2F 6D 61 63 2F 73  |com/chrome/mac/s|
00000030  74 61 62 6C 65 2F 47 47 52 4D 2F 67 6F 6F 67 6C  |table/GGRM/googl|
00000040  65 63 68 72 6F 6D 65 2E 64 6D 67 5F 10 61 68 74  |echrome.dmg_.aht|
00000050  74 70 73 3A 2F 2F 77 77 77 2E 67 6F 6F 67 6C 65  |tps://www.google|
00000060  2E 63 6F 6D 2F 63 68 72 6F 6D 65 3F 26 62 72 61  |.com/chrome?&bra|
00000070  6E 64 3D 43 48 4D 41 26 75 74 6D 5F 63 61 6D 70  |nd=CHMA&utm_camp|
00000080  61 69 67 6E 3D 65 6E 26 75 74 6D 5F 73 6F 75 72  |aign=en&utm_sour|
00000090  63 65 3D 65 6E 2D 68 61 2D 6E 61 2D 75 73 2D 62  |ce=en-ha-na-us-b|
000000A0  6B 26 75 74 6D 5F 6D 65 64 69 75 6D 3D 68 61 08  |k&utm_medium=ha.|
000000B0  0B 4B 00 00 00 00 00 00 01 01 00 00 00 00 00 00  |.K..............|
000000C0  00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
000000D0  00 AF                                            |..|
000000d2
com.apple.quarantine: 0002;4f91d6f8;Safari;A89FCF40-0748-46BE-9C5E-1599A280E9D6
Breaking down this string based on the description in:
/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Headers/LSQuarantine.h
  • 0002: kLSQuarantineType (I think), one of:
    • kLSQuarantineTypeWebDownload
    • kLSQuarantineTypeOtherDownload
    • kLSQuarantineTypeEmailAttachment
    • kLSQuarantineTypeInstantMessageAttachment
    • kLSQuarantineTypeCalendarEventAttachment
    • kLSQuarantineTypeOtherAttachment
  • 4f91d6f8: LSQuarantineTimeStamp, seconds since epoch timestamp in hex, print with 'date -r 0x4f91d6f8'
  • Safari: kLSQuarantineAgentName, the name of the process that wrote the file, or whatever the process sets via the API.
  • A89FCF40-0748-46BE-9C5E-1599A280E9D6: a UUID. The key (LSQuarantineEventIdentifier) used for entries in the SQLite DB.
Wait, what SQLite DB? The one here:
$ sqlite3 /Users/auser/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2
sqlite> .dump
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE LSQuarantineEvent (  LSQuarantineEventIdentifier TEXT PRIMARY KEY NOT NULL,  LSQuarantineTimeStamp REAL,  LSQuarantineAgentBundleIdentifier TEXT,  LSQuarantineAgentName TEXT,  LSQuarantineDataURLString TEXT,  LSQuarantineSenderName TEXT,  LSQuarantineSenderAddress TEXT,  LSQuarantineTypeNumber INTEGER,  LSQuarantineOriginTitle TEXT,  LSQuarantineOriginURLString TEXT,  LSQuarantineOriginAlias BLOB );
INSERT INTO "LSQuarantineEvent" VALUES('A89FCF40-0748-46BE-9C5E-1599A280E9D6',356650616.054473,'com.apple.Safari','Safari','https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg',NULL,NULL,0,NULL,'https://www.google.com/',NULL);
CREATE INDEX LSQuarantineEventIndex  ON LSQuarantineEvent (  LSQuarantineEventIdentifier );
COMMIT;
Note the NULL fields, which are optional fields that can be filled in by the developer who writes the file. The OS writes LSQuarantineAgentNameKey, LSQuarantineAgentBundleIdentifierKey, and LSQuarantineTimeStampKey by default. Chrome adds LSQuarantineDataURLString and LSQuarantineOriginURLString. Apple said in the Leopard Launch Services release notes:
When the Launch Services API is used to open a quarantined file and the file appears to be an application, script, or other executable file type, Launch Services will display an alert to confirm the user understands the file is some kind of application. If the file is opened, the quarantine properties are automatically cleared by Launch Services if the user has write access to the file.
So translating this to the Gatekeeper world, with the default settings Gatekeeper will block execution and fire an alert if:
  1. The file is opened with Launch Services (e.g. from the finder via user action, from another cocoa app, or as a mime handler). Excludes executables/installers run from the commandline, or programmatically via POSIX APIs. 'UnsafeExecutable' may play a role here (needs checking).
  2. The file has the quarantine xattr set.
  3. The file is not signed by apple or an apple developer cert.
Xprotect follows similar logic, and checks files against the attributes in
/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/XProtect.plist
to determine if the file is known-bad.

No comments: