Compare commits

...

201 Commits
0.8.5 ... 0.9.4

Author SHA1 Message Date
Russell Hancox
6ce0ef62e9 SantaGUI: Ensure connection is only made on main thread 2015-10-15 18:31:07 -04:00
Russell Hancox
2a03341fb6 santad: Add configuration option for turning off PAGEZERO protection. 2015-10-15 18:10:00 -04:00
Russell Hancox
77a55dde56 santad: Catch errors archiving/unarchiving SNTStoredEvent, delete events that fail 2015-10-15 18:09:46 -04:00
Russell Hancox
1a71cdff4a santad/santactl: Report back if rule adding/removing failed rather than assuming success. 2015-10-15 12:15:38 -04:00
Russell Hancox
63f65c51c3 SNTFileInfo: Use NSURL method for getting quarantine data, don't try to use <10.10 2015-10-15 12:14:53 -04:00
Russell Hancox
75de2526c1 santactl/binaryinfo: Only print quarantine fields if they're not empty 2015-10-14 23:37:16 -04:00
Russell Hancox
6fc4b7b120 santactl/binaryinfo: Increase key padding +1 2015-10-14 23:31:50 -04:00
Russell Hancox
7b8068139b santad, santactl/sync: Collect and upload quarantine data with events. 2015-10-14 23:02:20 -04:00
Russell Hancox
ced7de884f santactl/binaryinfo: Add quarantine data to output, add print method to simplify changes. 2015-10-14 20:12:04 -04:00
Russell Hancox
bc51c9f25b SNTFileInfo: Add com.apple.quarantine data accessors for downloaded files. 2015-10-14 20:11:32 -04:00
Russell Hancox
c412e8b9a7 SNTFileInfo: Fix embedded plist parsing, extract into separate method 2015-10-14 20:07:50 -04:00
Russell Hancox
4e0ff224b6 Project: Remove SNTCertificate/SNTCodesignChecker, use new CocoaPod versions 2015-10-12 17:23:42 -04:00
Russell Hancox
61c817c9cb Tests: Fix SNTRuleTable tests 2015-10-09 15:14:15 -04:00
Russell Hancox
2ed384f677 santactl/sync: Only update client mode at end of sync 2015-10-09 13:12:25 -04:00
Russell Hancox
7a851cb080 santad: Typo in comment 2015-10-08 19:54:23 -04:00
Russell Hancox
13aa889633 SNTFileInfo: Add fileSize method, use it in SNTEventLog 2015-10-08 17:57:02 -04:00
Russell Hancox
5c3fba5f41 santad: Prevent user/server from accidentally deleting rules that would kill the system. 2015-10-08 17:45:39 -04:00
Russell Hancox
145d9216bf Project: Don't bother with "xcodebuild clean" for Rakefile clean rule 2015-10-08 17:43:59 -04:00
Russell Hancox
84f46de940 Driver/Daemon: Collect process name in-kernel for file events, parent name for exec requests. For file events log process name and path, if possible. 2015-10-05 17:09:33 -04:00
Russell Hancox
cb9a5b6fbe santactl: Add --json option to both status and version commands. 2015-10-05 14:15:10 -04:00
Russell Hancox
d9718faba4 SNTFileInfo: Return non-embedded dict if locating embedded fails 2015-10-05 14:13:40 -04:00
Russell Hancox
5472ff41f0 santactl/status: Show timezone as UTF offset rather than name 2015-10-05 13:00:55 -04:00
Russell Hancox
4f94c3b310 santactl/status: Use fixed format for sync date output but still include TZ. 2015-10-03 19:57:19 -04:00
Russell Hancox
420f1efa50 santad: For file write events, print process name as well as pid. 2015-10-03 18:16:06 -04:00
Russell Hancox
5d2ce17817 santactl/status: When printing last sync date, use local timezone and locale settings 2015-10-03 18:15:41 -04:00
Russell Hancox
053cb823a1 santa-driver: Change C++ std to C++11
This is mostly just to quiet the warning about override not being set on getMetaClass, which is part of the OSDeclareDefaultStructors macro.
2015-10-03 18:15:11 -04:00
Russell Hancox
18a7992372 Config: Add more protected keys, only protect if a server is set 2015-10-02 16:35:30 -04:00
Russell Hancox
9e935f5bfb GUI: Include CFBundleName as first item in UI, if available. 2015-10-01 18:53:58 -04:00
Russell Hancox
9f49e24dc5 santad: Update file changes logging to use a configurable regex 2015-10-01 17:57:07 -04:00
Russell Hancox
dbf60f16bc santactl/sync: Fix typo causing clean sync on every run 2015-09-30 16:00:39 -04:00
Russell Hancox
0f3a228788 santactl/rule: Make help text a little clearer 2015-09-28 17:46:30 -04:00
Russell Hancox
d905f5b095 santactl/rule: Add ability to add certificate rules. Re-write argument parsing. 2015-09-28 17:20:34 -04:00
Russell Hancox
1c310486c7 santactl/status, santad: Show watchdog events in status output 2015-09-28 16:41:33 -04:00
Russell Hancox
4b01c6da91 santactl/status: Report some sync statuses. 2015-09-28 16:14:45 -04:00
Russell Hancox
5782378616 santactl/sync, santad: Add clean sync and last success options, use to initiate clean sync when database is re-created 2015-09-28 16:11:17 -04:00
Russell Hancox
64c97ebfba santad: If database open fails, delete and re-create. 2015-09-28 16:09:05 -04:00
Russell Hancox
5fd4d56b00 santactl/sync: Add ability to sync blacklist regex 2015-09-28 16:08:11 -04:00
Russell Hancox
e658b5167e Project: Update README a little 2015-09-24 18:15:03 -04:00
Russell Hancox
cea698d720 SNTCertificate: Add serialNumber and isCa properties. 2015-09-21 17:48:47 -04:00
Russell Hancox
c07f41c312 santad: Stop closing stdout/stderr 2015-09-21 15:59:32 -04:00
Russell Hancox
a837aa0334 santactl/status: Use dispatch group instead of sleeping 2015-09-21 15:59:20 -04:00
Russell Hancox
0050724e22 SNTXPCConnection: Use semaphore instead of variable & sleep. 2015-09-21 15:58:54 -04:00
Russell Hancox
adac4ac75c SantaGUI: windowWillClose and orderOut are being marked nonnull 2015-09-21 15:51:36 -04:00
Russell Hancox
718f37024a SNTConfigurator: Use NSPropertyListImmutable instead of kCFPropertyListImmutable 2015-09-21 15:51:03 -04:00
Russell Hancox
fcb3008539 Rakefile: Handle xcpretty missing better 2015-09-21 15:50:22 -04:00
Russell Hancox
8faf3eec53 santactl/sync: Validate incoming rules better 2015-09-16 15:59:50 -04:00
Russell Hancox
2bc3df3255 santad: Stop using mmap while reading files, it can be forced to crash by truncating the file. 2015-09-16 15:52:49 -04:00
Russell Hancox
5b0e550c85 santad: Add BlacklistRegex option, log a useful explanation when decision is made by scope 2015-09-16 14:19:33 -04:00
Russell Hancox
e52211abf2 santa-driver: Release proc_t acquired with proc_find. 2015-09-15 17:23:07 -04:00
Russell Hancox
9b6f231b34 santa-driver: Check for daemon earlier in FetchDecision 2015-09-14 18:20:33 -04:00
Russell Hancox
b71223705f santa-driver: If daemon fails to provide a response, print the path of the files it failed on 2015-09-14 18:19:56 -04:00
Russell Hancox
863fbe69bb santa-driver: Simplify AddToCache's locking 2015-09-14 18:19:28 -04:00
Russell Hancox
2d46279961 santa-driver: Use 0 as the client_pid when not connected 2015-09-14 18:18:51 -04:00
Russell Hancox
0d0207d77f santa-driver: lck_attr and lck_grp_attr need freeing 2015-09-14 18:18:20 -04:00
Russell Hancox
00bbade34f santa-driver: ClientConnected() should check if process is exiting/dying. 2015-09-14 18:08:57 -04:00
Russell Hancox
682f741ddc santad: Separate uid/gid fields in log. 2015-09-11 11:35:14 -04:00
Russell Hancox
3d2744c9e3 santactl/sync: Use lib compression for both preflight and event upload phases 2015-09-09 17:13:38 -04:00
Russell Hancox
cc286dcf16 santad: Fix event storage 2015-09-09 17:13:21 -04:00
Russell Hancox
27c6e2a7bd santa-driver: Don't send file mod messages unless daemon is connected 2015-09-09 14:22:31 -04:00
Russell Hancox
72c7a67ad5 Logging: Limit kernel messages to those actually sent by the kernel 2015-09-09 13:34:30 -04:00
Russell Hancox
8fe5e4e238 Logging: Update logMessage to use asl directly, adding a facility 2015-09-09 11:56:53 -04:00
Russell Hancox
02f23d0c62 santad: Add LogFileChanges option, remove LogAllEvents, fix key protection 2015-09-09 11:56:31 -04:00
Russell Hancox
ff6f4d4152 Common: Update SNTRule and SNTStoredEvent isEqual/hash/description methods. 2015-09-08 16:35:50 -04:00
Russell Hancox
2242f46792 Conf: Don't roll logs too regularly 2015-09-08 16:34:38 -04:00
Russell Hancox
642b5609b2 Tests: Fix tests after adding file write logging 2015-09-08 16:34:21 -04:00
Russell Hancox
98878f3e7c Kernel/santad: Add file write logging and exec argv's.
This necessitated a large refactoring of a bunch of code, hence being a large commit. This moves all event logging into a separate class, moves logging of executions to be from FileOp events rather than Vnode events (so we can get the argv after the execve call has finished) and implements the logging of cached execs.
2015-09-08 16:33:59 -04:00
Russell Hancox
3eb28deccf santa-driver: Verify input args are not nullptr's. 2015-09-08 14:41:34 -04:00
Russell Hancox
761a852156 santad: Always request sizeof(santa_message_t) regardless of previous message size 2015-09-08 14:40:50 -04:00
Russell Hancox
f4ddb11c1f santad: Force database permissions on startup 2015-09-08 14:33:25 -04:00
Russell Hancox
75158c11ea santa-driver: Don't create santa_message_t structs on the stack.
Also rename userId field to uid and add gid field to match
2015-08-31 15:21:25 -04:00
Russell Hancox
fe96706b0c KernelTests: Always unload kext and cleanup tmp after running 2015-08-27 18:03:40 -04:00
Russell Hancox
b87482e824 santad: Move page zero check to after binary/cert rule checks so 'bad' binaries can be whitelisted and notifications will be generated when they're blocked 2015-08-27 15:25:13 -04:00
Russell Hancox
a9ba99dc79 SNTFileInfo: Re-write mach header parsing 2015-08-27 15:25:12 -04:00
Russell Hancox
8884e92a1a Tests: Add test for missing/bad pagezero 2015-08-27 15:25:12 -04:00
Russell Hancox
6385514257 santad: Block 32-bit binaries with missing/invalid page zero 2015-08-27 15:25:12 -04:00
Russell Hancox
d3ad47022b Conf: Change log time format to ISO8601Z.3 2015-08-27 15:25:01 -04:00
Russell Hancox
138d4b507d SantaGUI: Fix fast-user-switching support. 2015-08-18 17:00:38 -04:00
Russell Hancox
3c0b195bcf Update travis.yml to add Cocoapod caching 2015-08-07 17:27:15 -04:00
Russell Hancox
d941a71bb5 Package: Forcibly make santactl symlink 2015-08-05 16:19:37 -04:00
Russell Hancox
08697d9daf KernelTests: Fix lots-of-executions test 2015-08-05 15:59:41 -04:00
Russell Hancox
8959871988 Rakefile: Clean before dist 2015-08-05 15:59:34 -04:00
Russell Hancox
bb43a04992 SNTFileInfo: Always try to get embedded info.plist before bundle plist 2015-08-05 12:01:05 -04:00
Russell Hancox
5f93dc7991 Project: Stop trying to be smart with logging destinations 2015-08-04 18:13:04 -04:00
Russell Hancox
9be8eb223c KernelTests: Stop blocking ps while tests are running, block ed instead. 2015-08-04 17:13:35 -04:00
Russell Hancox
e8b6c47e0f KernelTests: Remove timeout, chdir to tmp dir before executing, add lots-of-binaries test 2015-08-04 17:13:20 -04:00
Russell Hancox
697d442afb Project: Update Mac OS X -> OS X. 2015-08-04 13:54:55 -04:00
Russell Hancox
5dbd261b5a GUI: Allow selection of all fields and add ppid to end of parent name. 2015-08-04 13:53:47 -04:00
Russell Hancox
9bc94ca658 GUI: Add defaultBlockMessage configuration 2015-08-04 13:52:44 -04:00
Russell Hancox
4404b5f849 santactl/sync: Default to ephemeralSessionConfiguration to avoid caching 2015-08-03 18:03:51 -04:00
Russell Hancox
6a4b73b8a9 santa-driver: Before posting request to santa, ensure it exists in the cache already 2015-08-03 18:02:57 -04:00
Russell Hancox
b6146224b3 santa-driver: Make "cache too large" log info instead of debug 2015-08-03 18:02:34 -04:00
Russell Hancox
e3593c1b0c santad: fclose stderr for santactl sync too 2015-07-22 16:35:25 -04:00
Russell Hancox
90a2f10da6 santactl/rule: Print usage when args are bad, catch missing long arguments.
Fixes #20
2015-07-22 13:48:43 -04:00
Russell Hancox
60bab1c004 Rakefile: Don't miss santad/santactl dSYMs 2015-07-21 15:22:14 -04:00
Russell Hancox
0898940d0b santad: Pass santa_message_t straight to SNTExecutionController 2015-07-21 14:52:53 -04:00
Russell Hancox
38b65b0ca4 santad: Move uid->username lookup to where it's actually used 2015-07-21 14:52:53 -04:00
Russell Hancox
d36ce5eefc KernelTests: Update comments, add extra write-to-cached-file check 2015-07-21 14:52:48 -04:00
Russell Hancox
ff99ab9cfe santad: loggedInUsers:sessions: style clean-up 2015-07-21 14:22:42 -04:00
Russell Hancox
64995367c3 santad: Simplify eventStateForDecision:type: 2015-07-21 14:22:42 -04:00
Russell Hancox
c67f0ffc11 santad: Don't initiate event upload if syncing isn't enabled 2015-07-21 14:22:42 -04:00
Russell Hancox
d5403ae112 santa-driver: Move vnode-id and vnode-id-str fetching to avoid duplication 2015-07-21 14:22:42 -04:00
Russell Hancox
d21d64cbfe santad: Don't print log format every startup 2015-07-21 14:22:42 -04:00
Russell Hancox
347ee3c4f5 Merge pull request #19 from samsymons/podfile-fix
Fix an installation error with CocoaPods.
2015-07-21 14:20:41 -04:00
Sam Symons
77ed1cca29 Fix an installation error with CocoaPods.
In the later versions of CocoaPods, the `project` method was replaced
with `pods_project`. This was preventing the post_install hooks from
being run.
2015-07-21 10:41:03 -07:00
Russell Hancox
cfac7dbb37 Logging: Fix syslog logging and file rotation 2015-07-17 17:43:04 -04:00
Russell Hancox
f27d72f3f9 Tests: Update tests for new error argument to SNTFileInfo 2015-07-17 12:59:48 -04:00
Russell Hancox
3cd93b287e santa-driver: Simplify kauth callbacks, moving most of the logic to methods on SDM 2015-07-16 22:33:24 -04:00
Russell Hancox
5e5605881b santa-driver: remove client_proc, use ClientConnected() instead 2015-07-16 22:32:17 -04:00
Russell Hancox
a9b48610df santa-driver: Clear data queue if client crashes. Restore dataqueue lock, the docs lied. 2015-07-16 22:31:31 -04:00
Russell Hancox
3cca09a48c santa-driver: Remove request loops in GetFromDaemon
For large binaries it poses a risk of being overrun and as santad is working pretty reliably it's almost certainly not necessary anymore.
2015-07-15 18:55:05 -04:00
Russell Hancox
3134448eac santad: Close password database after getpwuid 2015-07-15 18:25:26 -04:00
Russell Hancox
663bdf945b KernelTest: Update for EACCES -> EPERM change 2015-07-15 18:24:13 -04:00
Russell Hancox
e94d1175e7 santad: If file can't be hashed, log an error and allow execution. 2015-07-13 11:20:39 -04:00
Russell Hancox
e20b761965 santa-driver: Change rejection errno to EPERM 2015-07-01 18:55:04 -04:00
Russell Hancox
90c64812d0 santad: close stdout before running santactl sync 2015-07-01 17:22:40 -04:00
Russell Hancox
08d368fc49 santad: Rename watchdog thread with reverse-dns name 2015-06-26 16:29:46 -04:00
Russell Hancox
39385f0bff santad: Put an autoreleasepool inside the watchdog thread. 2015-06-26 13:12:46 -04:00
Russell Hancox
8bc3418ce1 santad: Watchdog: only log memory use if it increased since last check. Increase threshold to 250MB 2015-06-25 17:58:17 -04:00
Russell Hancox
a145700398 Rakefile: Properly unload/load GUI during build. 2015-06-25 17:52:59 -04:00
Russell Hancox
409535e617 santactl: Style, indenting. 2015-06-25 11:04:57 -04:00
Russell Hancox
f625016efe santactl/sync: When rejecting a redirect, cancel the task to avoid hanging the task until timeout 2015-06-24 17:32:35 -04:00
Russell Hancox
f4c94ab1d7 santactl/sync: Failed log upload should not fail whole sync 2015-06-24 17:21:54 -04:00
Russell Hancox
8234706dd3 santad: Vacuum event database after removing multiple events. 2015-06-24 11:58:38 -04:00
Russell Hancox
1a31dc870f Merge branch 'whitelistscope'
* whitelistscope:
  Common: Rename kWhitelistDirsKey/WhitelistDirs to kWhitelistRegexKey/WhitelistRegex
  santad: NSRegularExpression doesn't work with XPC.
  santactl/sync: Sync whitelist regex
  santad: In a rule vs scope, rule wins.
  santad: Move whitelisted dirs feature to using regex instead of array. Faster and more flexible.
  santactl: Update help/error wordings
  santad: Add whitelisted directory support
2015-06-23 18:31:03 -04:00
Russell Hancox
a1712858c5 Common: Rename kWhitelistDirsKey/WhitelistDirs to kWhitelistRegexKey/WhitelistRegex 2015-06-23 18:30:42 -04:00
Russell Hancox
0059e768b9 Common: Add __NSString__ attribute to logMessage to catch format string bugs. Fix some warnings that found. 2015-06-23 18:10:46 -04:00
Russell Hancox
4fe1550bd2 santad: NSRegularExpression doesn't work with XPC. 2015-06-23 18:09:35 -04:00
Russell Hancox
0c182c8a7f santactl/sync: Sync whitelist regex 2015-06-23 17:33:43 -04:00
Russell Hancox
bcdf746def santad: In a rule vs scope, rule wins. 2015-06-23 17:33:08 -04:00
Russell Hancox
bc13ac3a98 santad: Move whitelisted dirs feature to using regex instead of array. Faster and more flexible. 2015-06-23 17:22:18 -04:00
Russell Hancox
a894e018cd santactl: Update help/error wordings 2015-06-23 17:21:17 -04:00
Russell Hancox
cbecfd444d santad: Add whitelisted directory support 2015-06-23 17:21:17 -04:00
Russell Hancox
357e5ef963 santactl/sync: NSString doesn't have an unsignedIntegerValue method 2015-06-23 17:14:47 -04:00
Russell Hancox
60594c9f03 santad/santactl-sync: Accept backoff interval from server, disable event uploads if back off is used, re-enable on next sync. 2015-06-23 15:54:30 -04:00
Russell Hancox
44b5bae8da santad: Add sync execution timer to santad 2015-06-23 15:52:39 -04:00
Russell Hancox
2e856196c5 santad: Move SIGCHLD SIG_IGN setting to main(), it doesn't need to be set repeatedly. 2015-06-23 15:36:59 -04:00
Russell Hancox
8672187c02 SantaGUI: Add keepalive to launchagent plist 2015-06-23 11:20:20 -04:00
Russell Hancox
cf251c45b8 Project: Update package Makefile for santad/santactl move 2015-06-22 15:57:10 -04:00
Russell Hancox
385c03096d Project: Missed santactl/santad move in Rakefile dist command 2015-06-22 15:35:03 -04:00
Russell Hancox
f323f5e3de santad: Up watchdog interval to 60s and CPU threshold to 20%.
Whilst during normal operation santad doesn't use more than 5% CPU, it does spike if lots
of processes start, such as during bootup. This change helps to reduce the noise.
2015-06-22 15:28:02 -04:00
Russell Hancox
9562ee86cd Project: Add missing copy to a few properties previously missed 2015-06-19 17:32:45 -04:00
Russell Hancox
adfb4bc861 SNTFileInfo: Better caching of properties 2015-06-19 17:31:48 -04:00
Russell Hancox
957232ca40 santactl: Fix event counting bug in status command 2015-06-16 18:02:41 -04:00
Russell Hancox
44c9d9aead santad: Add watchdog thread to print warnings if CPU/RAM usage seem high. 2015-06-15 16:31:55 -04:00
Russell Hancox
f95245cedd 10.11 Prep: Move santad,santactl from /usr/libexec,/usr/sbin to within santa-driver.kext 2015-06-15 16:18:51 -04:00
Russell Hancox
3c034adf48 GUI: Prevent reconnection loop when XPC connection dies 2015-06-10 16:46:32 -04:00
Russell Hancox
abd3c5a06d GUI: Restore constraint move Dismiss button when event detail URL is not set 2015-06-10 16:45:16 -04:00
Russell Hancox
ca4951a475 SNTFileWatcher: Update test file location 2015-06-09 13:50:43 -04:00
Russell Hancox
e751a3d307 SNTFileWatcher: Only get the fileSystemRepresentation once, to avoid high memory use when file doesn't exist 2015-06-09 13:10:29 -04:00
Russell Hancox
2a8bdfd714 santad: Use _exit instead of exit after fork. Oops. 2015-06-01 17:12:12 -04:00
Russell Hancox
be9dca3ee2 GUI: Add close button to About window. 2015-05-21 16:12:48 -04:00
Russell Hancox
32707fb501 santa-driver: Fix rare panic in CacheCheck where lock upgrade fails.
lck_rw_lock_shared_to_exclusive can return false if a previous reader upgraded. The result is the lock being unlocked and the panic is caused when unlocking a lock that isn't locked.
2015-05-20 11:13:19 -04:00
Russell Hancox
d72547e187 Project: Simplify package download URL generation in pkg Makefile 2015-05-18 18:36:30 -04:00
Russell Hancox
9150ddffb1 Project: Fix broken curl command in pkg Makefile 2015-05-18 17:52:33 -04:00
Russell Hancox
d5c1d66c2f KernelTests: Update tests for dataqueue usage changes 2015-05-18 16:39:27 -04:00
Russell Hancox
536b8969ed santactl/sync: LogUpload - logsToUpload is part of the same class, use self. 2015-05-18 16:31:43 -04:00
Russell Hancox
0db3b6d955 santactl/sync: Split out Log Upload request generation and Rule Download rule parsing from main request methods. 2015-05-18 14:33:21 -04:00
Russell Hancox
78bb9a1bd6 common: Correct comment on default loglevels 2015-05-18 13:06:31 -04:00
Russell Hancox
567e0b6431 santad: If exiting because of a failed dequeue, log the return code at ERROR level. 2015-05-18 13:06:31 -04:00
Russell Hancox
f2f27c5675 santa-driver: Up ACTION_REQUEST_SHUTDOWN from 60->90 2015-05-18 13:06:31 -04:00
Russell Hancox
5a7ac2287b santa-driver: Stop defining MAX_PATH_LEN, use MAXPATHLEN instead.
I can't recall why I did this.
2015-05-18 13:06:31 -04:00
Russell Hancox
f82da21b75 santactl/sync: Bug from 8cd9898, call completion handler even if no rules are downloaded. 2015-05-15 10:38:26 -04:00
Russell Hancox
969a5ef94e santactl/sync: Don't release SecAsn1Coder until we're done with its data. 2015-05-14 17:35:06 -04:00
Russell Hancox
fd7ad07193 santactl/sync: Handle DER decoding failures more gracefully. 2015-05-14 17:01:49 -04:00
Russell Hancox
3f5400b264 santa-driver: Split FetchDecision, notify daemon of missed executions, reorganize some methods. 2015-05-14 17:01:49 -04:00
Russell Hancox
466b5ed491 santa-driver: Make SantaDecisionManager::AddToCache default the microsecs parameter to the current uptime 2015-05-14 17:01:48 -04:00
Russell Hancox
25f1b71f10 santa-driver: Move dataqueue to SantaDecisionManager rather than
recreating it on every connect.
2015-05-14 17:01:48 -04:00
Russell Hancox
d1295f97b9 santa-driver: Rename owning_pid/proc to client_pid/proc. Minor style
cleanup
2015-05-14 17:01:48 -04:00
Russell Hancox
f5eb274aa0 santa-driver: remove unnecessary dataqueue lock 2015-05-14 17:01:48 -04:00
Russell Hancox
58b9dab74f santa-driver: SDM should call super::init 2015-05-14 17:01:48 -04:00
Russell Hancox
9f6b6d10dc santa-driver: Make room in santa_message_t->path for the terminator so we don't miss a character. 2015-05-14 17:01:47 -04:00
Russell Hancox
57f6e516c2 santa-driver: Mark all overriden methods with 'override' 2015-05-14 17:01:42 -04:00
Russell Hancox
8cd9898cf3 santactl/sync: Don't send empty rules array to daemon. 2015-05-11 16:27:02 -04:00
Russell Hancox
d53b04213a santa-driver: Remove empty spacing at end of line 2015-05-08 14:58:16 -04:00
Russell Hancox
ac99bd1070 santad: Add tests for SNTRuleTable 2015-05-08 14:57:53 -04:00
Russell Hancox
30df44df96 santad: Correctly delete corrupt events. Add test for this. 2015-05-08 14:57:37 -04:00
Russell Hancox
fc55b86f30 santad: Switch to uint32_t for table version numbers 2015-05-08 14:56:20 -04:00
Russell Hancox
59ffb67554 santad: Reject addRules requests with empty/nil array. Also switch to NSUInteger for rule counts. 2015-05-08 14:55:28 -04:00
Russell Hancox
d46b156b85 santa-driver: vnode_getattr sometimes panics if a vfs_context isn't available (when used with osxfuse for instance). 2015-05-07 18:24:29 -04:00
Russell Hancox
6492e70599 santactl: Instead of rejecting authentication challenges and trying again, cancel the whole request. 2015-05-07 17:56:47 -04:00
Russell Hancox
bc5d0f8685 santa-driver: Don't allow StopListener to return until both kauth scopes are done 2015-05-06 14:57:33 -04:00
Russell Hancox
838da16da1 santad: Delete events that fail to unarchive 2015-05-06 14:44:09 -04:00
Russell Hancox
6e242bf98d SantaGUI: Change Line to custom NSBox 2015-05-05 17:44:06 -04:00
Russell Hancox
be1e66c29d Project: Enable more warnings and then fix them. 2015-05-01 17:40:39 -04:00
Russell Hancox
57866308e3 santad: Consider scripts that are part of installer packages as in-scope. 2015-04-30 18:37:04 -04:00
Russell Hancox
63bc8fca2d santad: Don't post GUI notification for SILENT_BLACKLIST rules. 2015-04-30 18:36:43 -04:00
Russell Hancox
408712f00f santad: Don't log when client mode is not set in defaultDecision, as that can't actually happen. 2015-04-30 18:36:20 -04:00
Russell Hancox
8cb6046f94 GUI: Add parent process name, only show part of the shasum, resize window. 2015-04-30 18:24:54 -04:00
Russell Hancox
297fb4cb68 Add parent process name collection and upload 2015-04-30 18:21:13 -04:00
Russell Hancox
1501d413f0 Project: Add install.sh script and package Makefile
Adds an install.sh script that can be run from the distribution tarball
and a Luggage package makefile
2015-04-30 14:12:02 -04:00
Russell Hancox
e747ace0f3 santactl/binaryinfo: Add bundle details to file info 2015-04-30 12:34:31 -04:00
Russell Hancox
6b96f36b2b SantaGUI: Re-create AboutWindowController each time it's needed so that More Info button state is correct 2015-04-22 15:59:04 -04:00
Russell Hancox
f16fa691b5 santactl: include zlib.h instead of import 2015-04-21 16:43:09 -04:00
Russell Hancox
4fd5e1139f Project: Style clean-ups 2015-04-21 14:29:30 -04:00
Russell Hancox
0b33079833 Merge pull request #16 from marczak/readme-up
Updated README to clarify intentions and expectations
2015-04-21 09:14:50 -07:00
Edward Marczak
6069ed5801 Update README.md 2015-04-21 12:14:05 -04:00
Edward Marczak
c2a9061ea2 Updated README to clarify expectations. 2015-04-21 11:24:58 -04:00
Russell Hancox
ee963d62a4 Project: Update README to mention dyld issue 2015-04-21 10:02:32 -04:00
Russell Hancox
c12adbc8e6 Project: Update schemes to make Xcode be quiet. 2015-04-20 18:35:10 -04:00
Russell Hancox
e6b20bcce6 Project: update Podfile.lock 2015-04-20 18:07:33 -04:00
Russell Hancox
10333bba01 santa-driver: Change file-write cache check, use FileOp scope for most writes and use hasdirtyblks to catch flushed but still-open files. 2015-04-20 18:07:22 -04:00
106 changed files with 3697 additions and 2700 deletions

View File

@@ -1,5 +1,6 @@
---
language: objective-c
cache: cocoapods
before_install:
- gem install cocoapods xcpretty

84
Conf/Package/Makefile Normal file
View File

@@ -0,0 +1,84 @@
#
# Package Makefile for Santa
# Requires TheLuggage (github.com/unixorn/luggage) to be installed
#
# Will generate a package based on the latest release. You can replace
# the PACKAGE_VERSION variable with a specific variable instead if you wish.
#
LUGGAGE:=/usr/local/share/luggage/luggage.make
include ${LUGGAGE}
TITLE:=santa
REVERSE_DOMAIN:=com.google
# Get latest Release version using the GitHub API. Each release is bound to a
# git tag, which should always be a semantic version number. The most recent
# release is always first in the API result.
PACKAGE_VERSION:=$(shell curl -fs https://api.github.com/repos/google/santa/releases |\
python -c 'import json, sys; print json.load(sys.stdin)[0]["tag_name"]' 2>/dev/null)
# Get the download URL for the latest Release. Each release should have a
# tarball named santa-$version.tar.bz2 containing all of the files associated
# with that release. The tarball layout is:
#
# santa-$version.tar.bz2
# +--santa-$version
# |-- binaries
# | |-- santa-driver.kext
# | |-- Santa.app
# |-- conf
# | |-- install.sh
# | |-- com.google.santad.plist
# | |-- com.google.santagui.plist
# | +-- com.google.santa.asl.conf
# +--dsym
# |-- santa-driver.kext.dSYM
# |-- Santa.app.dSYM
# |-- santad.dSYM
# +-- santactl.dSYM
PACKAGE_DOWNLOAD_URL:="https://github.com/google/santa/releases/download/${PACKAGE_VERSION}/santa-${PACKAGE_VERSION}.tar.bz2"
PAYLOAD:=pack-Library-Extensions-santa-driver.kext \
pack-applications-Santa.app \
pack-Library-LaunchDaemons-com.google.santad.plist \
pack-Library-LaunchAgents-com.google.santagui.plist \
pack-etc-asl-com.google.santa.asl.conf \
pack-script-preinstall \
pack-script-postinstall
santa-driver.kext: download
Santa.app: download
com.google.santad.plist: download
com.google.santagui.plist: download
com.google.santa.asl.conf: download
download:
$(if $(PACKAGE_VERSION),, $(error GitHub API returned unexpected result. Wait a while and try again))
@curl -fL ${PACKAGE_DOWNLOAD_URL} | tar xvj --strip=2
@rm -rf *.dSYM
pack-etc-asl-com.google.santa.asl.conf: com.google.santa.asl.conf l_private_etc
@sudo mkdir -p ${WORK_D}/private/etc/asl
@sudo chown root:wheel ${WORK_D}/private/etc/asl
@sudo chmod 755 ${WORK_D}/private/etc/asl
@sudo install -m 644 -o root -g wheel com.google.santa.asl.conf ${WORK_D}/private/etc/asl
pack-Library-Extensions-santa-driver.kext: santa-driver.kext l_Library
@sudo mkdir -p ${WORK_D}/Library/Extensions
@sudo ${DITTO} --noqtn santa-driver.kext ${WORK_D}/Library/Extensions/santa-driver.kext
@sudo chown -R root:wheel ${WORK_D}/Library/Extensions/santa-driver.kext
@sudo chmod -R 755 ${WORK_D}/Library/Extensions/santa-driver.kext
clean: myclean
myclean:
@rm -rf *.dSYM
@rm -rf Santa.app
@rm -rf santa-driver.kext
@rm -f config.plist
@rm -f com.google.santa.asl.conf
@rm -f com.google.santad.plist
@rm -f com.google.santagui.plist
@rm -f install.sh

27
Conf/Package/postinstall Normal file
View File

@@ -0,0 +1,27 @@
#!/bin/bash
# Load the kernel extension, santad, sync client
# If a user is logged in, also load the GUI agent.
# If the target volume is not /, do nothing
[[ $3 != "/" ]] && exit 0
# Restart syslogd to pick up ASL configuration change
/usr/bin/killall -HUP syslogd
/sbin/kextload /Library/Extensions/santa-driver.kext
sleep 1
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santad.plist
sleep 1
# Create hopefully useful symlink for santactl
/bin/ln -sf /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin
user=$(/usr/bin/stat -f '%u' /dev/console)
[[ -z "$user" ]] && exit 0
/bin/launchctl asuser ${user} /bin/launchctl load /Library/LaunchAgents/com.google.santagui.plist
exit 0

26
Conf/Package/preinstall Normal file
View File

@@ -0,0 +1,26 @@
#!/bin/bash
# Unload the kernel extension, santad, sync client
# If a user is logged in, also unload the GUI agent.
# If the target volume is not /, do nothing
[[ $3 != "/" ]] && exit 0
/bin/launchctl remove com.google.santad
sleep 1
/sbin/kextunload -b com.google.santa-driver >/dev/null 2>&1
# Remove cruft from old Santa versions
/bin/rm /usr/libexec/santad
/bin/rm /usr/sbin/santactl
/bin/launchctl remove com.google.santasync
/bin/rm /Library/LaunchDaemons/com.google.santasync.plist
sleep 1
user=$(/usr/bin/stat -f '%u' /dev/console)
[[ -n "$user" ]] && /bin/launchctl asuser ${user} /bin/launchctl remove com.google.santagui
exit 0

View File

@@ -1,4 +1,8 @@
# Copy this file to /etc/asl to log all messages from santa-driver to the log file
? [S= Message santa-driver:] claim only
? [S= Message santa-driver:] file /var/log/santa.log format="[$((Time)(utc.3))] $Message"
> /var/log/santa.log mode=0644 rotate=seq compress file_max=5M all_max=100M
> /var/log/santa.log format="[$((Time)(ISO8601Z.3))] $Message" mode=0644 rotate=seq compress file_max=25M all_max=100M uid=0 gid=0
? [= Sender kernel] [S= Message santa-driver:] claim
? [= Sender kernel] [S= Message santa-driver:] file /var/log/santa.log
? [= Sender santad] claim
? [= Sender santad] file /var/log/santa.log
? [= Sender santactl] claim
? [= Sender santactl] file /var/log/santa.log

View File

@@ -6,19 +6,15 @@
<string>com.google.santad</string>
<key>ProgramArguments</key>
<array>
<string>/usr/libexec/santad</string>
<string>/Library/Extensions/santa-driver.kext/Contents/MacOS/santad</string>
</array>
<key>MachServices</key>
<dict>
<key>SantaXPCNotifications</key>
<true/>
<key>SantaXPCControl</key>
<true/>
<key>SantaXPCNotifications</key>
<true/>
<key>SantaXPCControl</key>
<true/>
</dict>
<key>StandardOutPath</key>
<string>/var/log/santa.log</string>
<key>StandardErrorPath</key>
<string>/var/log/santa.log</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>

View File

@@ -10,5 +10,7 @@
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.google.santasync</string>
<key>ProgramArguments</key>
<array>
<string>/usr/sbin/santactl</string>
<string>sync</string>
</array>
<key>StandardErrorPath</key>
<string>/var/log/santa.log</string>
<key>ProcessType</key>
<string>Background</string>
<key>StartInterval</key>
<integer>600</integer>
</dict>
</plist>

58
Conf/install.sh Executable file
View File

@@ -0,0 +1,58 @@
#!/bin/bash
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root" 1>&2
exit 1
fi
if [[ -d "binaries" ]]; then
SOURCE="."
elif [[ -d "../binaries" ]]; then
SOURCE=".."
else
echo "Can't find binaries, run install.sh from inside the conf directory" 1>&2
exit 1
fi
# Determine if anyone is logged into the GUI
GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
# Unload santad and scheduled sync job.
/bin/launchctl remove com.google.santad >/dev/null 2>&1
# Unload kext.
/sbin/kextunload -b com.google.santa-driver >/dev/null 2>&1
# Unload GUI agent if someone is logged in.
[[ -n "$GUI_USER" ]] && \
/bin/launchctl asuser ${GUI_USER} /bin/launchctl remove /Library/LaunchAgents/com.google.santagui.plist
# Cleanup cruft from old versions
/bin/launchctl remove com.google.santasync >/dev/null 2>&1
/bin/rm /Library/LaunchDaemons/com.google.santasync.plist >/dev/null 2>&1
/bin/rm /usr/libexec/santad >/dev/null 2>&1
/bin/rm /usr/sbin/santactl >/dev/null 2>&1
# Copy new files.
/bin/cp -r ${SOURCE}/binaries/santa-driver.kext /Library/Extensions
/bin/cp -r ${SOURCE}/binaries/Santa.app /Applications
/bin/ln -s /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin
/bin/cp ${SOURCE}/conf/com.google.santad.plist /Library/LaunchDaemons
/bin/cp ${SOURCE}/conf/com.google.santagui.plist /Library/LaunchAgents
/bin/cp ${SOURCE}/conf/com.google.santa.asl.conf /etc/asl/
# Reload syslogd to pick up ASL configuration change.
/usr/bin/killall -HUP syslogd
# Load kext.
/sbin/kextload /Library/Extensions/santa-driver.kext
# Load santad and scheduled sync jobs.
/bin/launchctl load /Library/LaunchDaemons/com.google.santad.plist
# Load GUI agent if someone is logged in.
[[ -n "$GUI_USER" ]] && \
/bin/launchctl asuser ${GUI_USER} /bin/launchctl load /Library/LaunchAgents/com.google.santagui.plist
exit 0

21
Podfile
View File

@@ -2,15 +2,28 @@ platform :osx, "10.9"
inhibit_all_warnings!
target :santactl do
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
end
target :Santa do
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
end
target :santad do
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
pod 'FMDB'
post_install do |rep|
rep.project.targets.each do |target|
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
if config.name != 'Release' then
break
end
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ''
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] <<= "NDEBUG=1"
end
@@ -19,6 +32,8 @@ target :santad do
end
target :LogicTests do
pod 'OCMock'
pod 'FMDB'
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
pod 'OCMock'
end

View File

@@ -4,14 +4,21 @@ PODS:
- FMDB/common (2.5)
- FMDB/standard (2.5):
- FMDB/common
- OCMock (3.1.2)
- MOLCertificate (1.1)
- MOLCodesignChecker (1.2):
- MOLCertificate (~> 1.1)
- OCMock (3.2)
DEPENDENCIES:
- FMDB
- MOLCertificate
- MOLCodesignChecker
- OCMock
SPEC CHECKSUMS:
FMDB: 96e8f1bcc1329e269330f99770ad4285d9003e52
OCMock: a10ea9f0a6e921651f96f78b6faee95ebc813b92
MOLCertificate: 57fb88b33e83008d45c75644937fca61ed9f63d3
MOLCodesignChecker: e887eeeb7cd87c9b808d1682187c8a27f8ff1100
OCMock: 28def049ef47f996b515a8eeea958be7ccab2dbb
COCOAPODS: 0.36.1
COCOAPODS: 0.39.0

View File

@@ -1,7 +1,7 @@
Santa [![Build Status](https://travis-ci.org/google/santa.png?branch=master)](https://travis-ci.org/google/santa)
=====
Santa is a binary whitelisting/blacklisting system for Mac OS X. It consists of
Santa is a binary whitelisting/blacklisting system for OS X. It consists of
a kernel extension that monitors for executions, a userland daemon that makes
execution decisions based on the contents of a SQLite database, a GUI agent that
notifies the user in case of a block decision and a command-line utility for
@@ -46,11 +46,35 @@ server.
programming interfaces to do its job. This means that the kext code should
continue to work across OS versions.
Intentions and Expectations
===========================
No single system or process will stop *all* attacks, or provide 100% security.
Santa is written with the intention of helping protect users from themselves.
People often download malware and trust it, giving the malware credentials, or
allowing unknown software to exfiltrate more data about your system. As a
centrally managed component, Santa can help stop the spread of malware among a
larger fleet of machines. Additionally, Santa can aid in analyzing what is
running in your fleet.
Santa is part of a defense-in-depth strategy, and you should continue to protect
hosts in whatever other ways you see fit.
Get Help
========
If you have questions or need help getting started, the
[santa-dev](https://groups.google.com/forum/#!forum/santa-dev) group is the
best place to start.
Known Issues
============
Santa is not yet a 1.0 and we have some known issues to be aware of:
* Santa only blocks execution (execve and variants), it doesn't protect against
dynamic libraries loaded with dlopen, libraries on disk that have been replaced or
libraries loaded using `DYLD_INSERT_LIBRARIES`. We are working on also protecting
against these avenues of attack.
* Kext communication security: the kext will only accept a connection from a
single client at a time and said client must be running as root. We haven't yet
found a good way to ensure the kext only accepts connections from a valid client.
@@ -80,7 +104,6 @@ option) if it would be useful to others.
Building
========
```sh
git clone https://github.com/google/santa
cd santa
@@ -98,12 +121,10 @@ and for security-reasons parts of Santa will not operate properly if not signed.
Kext Signing
============
10.9 requires a special Developer ID certificate to sign kernel extensions and
if the kext is not signed with one of these special certificates a warning will
be shown when loading the kext for the first time. In 10.10 this is a hard error
and the kext will not load at all unless the machine is booted with a debug
boot-arg.
Kernel extensions on OS X 10.9 and later must be signed using an Apple-provided
Developer ID certificate with a kernel extension flag. Without it, the only way
to load an extension is to enable kext-dev-mode or disable SIP, depending on the
OS version.
There are two possible solutions for this, for distribution purposes:
@@ -118,17 +139,11 @@ and distribute a new version of the pre-signed kext.
Apple will only grant this for broad distribution within an organization, they
won't issue them just for testing purposes.
If you just want to locally test changes to the kext code, you should enable
kext-dev mode, instructions for which can be found on the Apple developer site.
Contributing
============
Patches to this project are very much welcome. Please see the [CONTRIBUTING](https://github.com/google/santa/blob/master/CONTRIBUTING.md)
file.
Disclaimer
==========
This is **not** an official Google product.

View File

@@ -1,21 +1,24 @@
require 'timeout'
WORKSPACE = 'Santa.xcworkspace'
DEFAULT_SCHEME = 'All'
OUTPUT_PATH = 'Build'
DIST_PATH = 'Dist'
BINARIES = ['Santa.app', 'santa-driver.kext', 'santad', 'santactl']
BINARIES = ['Santa.app', 'santa-driver.kext']
DSYMS = ['Santa.app.dSYM', 'santa-driver.kext.dSYM', 'santad.dSYM', 'santactl.dSYM']
XCPRETTY_DEFAULTS = '-sc'
XCODEBUILD_DEFAULTS = "-workspace #{WORKSPACE} -derivedDataPath #{OUTPUT_PATH} -parallelizeTargets"
$DISABLE_XCPRETTY = false
task :default do
system("rake -sT")
end
def xcodebuild(opts)
if system "xcodebuild #{XCODEBUILD_DEFAULTS} #{opts} | " \
"xcpretty #{XCPRETTY_DEFAULTS} && " \
"exit ${PIPESTATUS[0]}"
command = "xcodebuild #{XCODEBUILD_DEFAULTS} #{opts}"
if not $DISABLE_XCPRETTY
command << " | xcpretty #{XCPRETTY_DEFAULTS} && exit ${PIPESTATUS[0]}"
end
if system command
puts "\e[32mPass\e[0m"
else
raise "\e[31mFail\e[0m"
@@ -29,20 +32,18 @@ task :init do
end
unless system 'xcpretty -v >/dev/null 2>&1'
puts "xcpretty is not installed. Install with 'sudo gem install xcpretty'"
$DISABLE_XCPRETTY = true
end
end
task :remove_existing do
system 'sudo rm -rf /santa-driver.kext'
system 'sudo rm -rf /Library/Extensions/santa-driver.kext'
system 'sudo rm -rf /Applications/Santa.app'
system 'sudo rm /usr/libexec/santad'
system 'sudo rm /usr/sbin/santactl'
end
desc "Clean"
task :clean => :init do
puts "Cleaning"
xcodebuild("-scheme All clean")
FileUtils.rm_rf(OUTPUT_PATH)
FileUtils.rm_rf(DIST_PATH)
end
@@ -82,16 +83,13 @@ namespace :install do
task :install, [:configuration] do |t, args|
config = args[:configuration]
system 'sudo cp conf/com.google.santad.plist /Library/LaunchDaemons'
system 'sudo cp conf/com.google.santasync.plist /Library/LaunchDaemons'
system 'sudo cp conf/com.google.santagui.plist /Library/LaunchAgents'
system 'sudo cp conf/com.google.santa.asl.conf /etc/asl'
Rake::Task['build:build'].invoke(config)
puts "Installing with configuration: #{config}"
Rake::Task['remove_existing'].invoke()
system "sudo cp -r #{OUTPUT_PATH}/Products/#{config}/santa-driver.kext /"
system "sudo cp -r #{OUTPUT_PATH}/Products/#{config}/santa-driver.kext /Library/Extensions"
system "sudo cp -r #{OUTPUT_PATH}/Products/#{config}/Santa.app /Applications"
system "sudo cp #{OUTPUT_PATH}/Products/#{config}/santad /usr/libexec"
system "sudo cp #{OUTPUT_PATH}/Products/#{config}/santactl /usr/sbin"
end
end
@@ -99,6 +97,7 @@ end
task :dist do
desc "Create distribution folder"
Rake::Task['clean'].invoke()
Rake::Task['build:build'].invoke("Release")
FileUtils.rm_rf(DIST_PATH)
@@ -109,10 +108,14 @@ task :dist do
BINARIES.each do |x|
FileUtils.cp_r("#{OUTPUT_PATH}/Products/Release/#{x}", "#{DIST_PATH}/binaries")
FileUtils.cp_r("#{OUTPUT_PATH}/Products/Release/#{x}.dSYM", "#{DIST_PATH}/dsym")
end
Dir.glob("Conf/*") {|x| FileUtils.cp(x, "#{DIST_PATH}/conf")}
DSYMS.each do |x|
FileUtils.cp_r("#{OUTPUT_PATH}/Products/Release/#{x}", "#{DIST_PATH}/dsym")
end
Dir.glob("Conf/*") {|x| File.directory?(x) or FileUtils.cp(x, "#{DIST_PATH}/conf")}
puts "Distribution folder created"
end
@@ -130,16 +133,17 @@ namespace :tests do
Rake::Task['unload'].invoke()
Rake::Task['install:debug'].invoke()
Rake::Task['load_kext'].invoke
timeout = 30
puts "Running kernel tests with a #{timeout} second timeout"
FileUtils.mkdir_p("/tmp/santa_kerneltests_tmp")
begin
Timeout::timeout(timeout) {
system "sudo #{OUTPUT_PATH}/Products/Debug/KernelTests"
}
rescue Timeout::Error
puts "ERROR: tests ran for longer than #{timeout} seconds and were killed."
puts "\033[?25l\033[12h" # hide cursor
puts "Running kernel tests"
system "cd /tmp/santa_kerneltests_tmp && sudo #{Dir.pwd}/#{OUTPUT_PATH}/Products/Debug/KernelTests"
rescue Exception
ensure
puts "\033[?25h\033[12l\n\n" # unhide cursor
FileUtils.rm_rf("/tmp/santa_kerneltests_tmp")
Rake::Task['unload_kext'].execute
end
Rake::Task['unload_kext'].execute
end
end
@@ -156,7 +160,7 @@ end
task :unload_gui do
puts "Unloading GUI agent"
system "sudo killall Santa 2>/dev/null"
system "launchctl unload /Library/LaunchAgents/com.google.santagui.plist 2>/dev/null"
end
desc "Unload"
@@ -169,12 +173,12 @@ end
task :load_kext do
puts "Loading kernel extension"
system "sudo kextload /santa-driver.kext"
system "sudo kextload /Library/Extensions/santa-driver.kext"
end
task :load_gui do
puts "Loading GUI agent"
system "open /Applications/Santa.app"
system "launchctl load /Library/LaunchAgents/com.google.santagui.plist 2>/dev/null"
end
desc "Load"

View File

@@ -26,6 +26,7 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
02D7203A6CEE2A946FB08ECE /* libPods-santactl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A2EA300312100AE62218F84E /* libPods-santactl.a */; };
0D0016A3192BCD3C005E7FCD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D9A7F3E1759330500035EB5 /* Foundation.framework */; };
0D0016A6192BCD3C005E7FCD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D0016A5192BCD3C005E7FCD /* main.m */; };
0D0016AE192BCD8C005E7FCD /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D3AFBF718FB4C870087BCEE /* IOKit.framework */; };
@@ -36,21 +37,16 @@
0D10BE891A0AAF6700C0C944 /* SNTDropRootPrivs.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D10BE851A0AABD600C0C944 /* SNTDropRootPrivs.m */; };
0D10BE8B1A0AB23300C0C944 /* SNTDERDecoderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D10BE8A1A0AB23300C0C944 /* SNTDERDecoderTest.m */; };
0D10BE8C1A0AB3FD00C0C944 /* SNTDERDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D7FFD4A1A017D4B00F34435 /* SNTDERDecoder.m */; };
0D1AF477187C7A2C00D3298D /* SNTCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D1AF476187C7A2C00D3298D /* SNTCertificate.m */; };
0D1AF478187C7A2C00D3298D /* SNTCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D1AF476187C7A2C00D3298D /* SNTCertificate.m */; };
0D1B477019A53419008CADD3 /* SNTAboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D1B476E19A53419008CADD3 /* SNTAboutWindowController.m */; };
0D1B477119A53419008CADD3 /* AboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0D1B476F19A53419008CADD3 /* AboutWindow.xib */; };
0D20710E1A7C4A86008B0A9A /* SNTStoredEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD604A19105433006B445C /* SNTStoredEvent.m */; };
0D28D53819D9F5910015C5EB /* SNTConfigurator.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D42D2B719D2042900955F08 /* SNTConfigurator.m */; };
0D2CD4611A81C7B100C9C910 /* dn.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0D2CD4601A81C7B100C9C910 /* dn.plist */; };
0D31DF4718D254B3002B300D /* SNTCodesignChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D59C0E317710E6000748EBF /* SNTCodesignChecker.m */; };
0D35BD9F18FD71CE00921A21 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D9A7F3E1759330500035EB5 /* Foundation.framework */; };
0D35BDA218FD71CE00921A21 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D35BDA118FD71CE00921A21 /* main.m */; };
0D35BDAC18FD7CFD00921A21 /* SNTCommandController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D35BDAB18FD7CFD00921A21 /* SNTCommandController.m */; };
0D35BDB518FD84F600921A21 /* SNTCommandSync.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D35BDB418FD84F600921A21 /* SNTCommandSync.m */; };
0D35BDBD18FDA23600921A21 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D3AFBF718FB4C870087BCEE /* IOKit.framework */; };
0D35BDC018FDA5C800921A21 /* SNTCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D1AF476187C7A2C00D3298D /* SNTCertificate.m */; };
0D35BDC218FDA5D100921A21 /* SNTCodesignChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D59C0E317710E6000748EBF /* SNTCodesignChecker.m */; };
0D35BDC418FDA5D100921A21 /* SNTXPCConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6FDC9518C93A020044685C /* SNTXPCConnection.m */; };
0D377C2A17A071B7008453DB /* SNTEventTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D377C2917A071B7008453DB /* SNTEventTable.m */; };
0D37C10F18F6029A0069BC61 /* SNTDatabaseTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D37C10E18F6029A0069BC61 /* SNTDatabaseTable.m */; };
@@ -78,8 +74,11 @@
0D4644C5182AF81700098690 /* SantaDecisionManager.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0D4644C3182AF81700098690 /* SantaDecisionManager.cc */; };
0D4644C6182AF81700098690 /* SantaDecisionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D4644C4182AF81700098690 /* SantaDecisionManager.h */; };
0D4A5007176A4602004F63BF /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D4A5006176A4602004F63BF /* Security.framework */; };
0D536ED71B8E7A2E0039A26D /* bad_pagezero in Resources */ = {isa = PBXBuildFile; fileRef = 0D536ED51B8E7A2E0039A26D /* bad_pagezero */; };
0D536ED81B8E7A2E0039A26D /* missing_pagezero in Resources */ = {isa = PBXBuildFile; fileRef = 0D536ED61B8E7A2E0039A26D /* missing_pagezero */; };
0D536EDB1B94E9230039A26D /* SNTEventLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D536EDA1B94E9230039A26D /* SNTEventLog.m */; };
0D536EDC1B94E9230039A26D /* SNTEventLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D536EDA1B94E9230039A26D /* SNTEventLog.m */; };
0D54E0B11976F8D3000BB59F /* SNTFileInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD6041190ACCB8006B445C /* SNTFileInfo.m */; };
0D59C0E417710E6000748EBF /* SNTCodesignChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D59C0E317710E6000748EBF /* SNTCodesignChecker.m */; };
0D63DD5C1906FCB400D346C4 /* SNTDatabaseController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D63DD5B1906FCB400D346C4 /* SNTDatabaseController.m */; };
0D63DD5E1906FCB400D346C4 /* SNTDatabaseController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D63DD5B1906FCB400D346C4 /* SNTDatabaseController.m */; };
0D668E8118D1121700E29A8B /* SNTMessageWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D668E8018D1121700E29A8B /* SNTMessageWindow.m */; };
@@ -88,7 +87,6 @@
0D6FDC8318C68D7E0044685C /* GIAG2.crt in Resources */ = {isa = PBXBuildFile; fileRef = 0D6FDC8218C68D7E0044685C /* GIAG2.crt */; };
0D6FDC8518C68E500044685C /* GIAG2.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0D6FDC8418C68E500044685C /* GIAG2.pem */; };
0D6FDC8718C6913D0044685C /* apple.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0D6FDC8618C6913D0044685C /* apple.pem */; };
0D6FDC8C18C69AF90044685C /* SNTCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D1AF476187C7A2C00D3298D /* SNTCertificate.m */; };
0D6FDC9618C93A020044685C /* SNTXPCConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6FDC9518C93A020044685C /* SNTXPCConnection.m */; };
0D6FDC9718C93A020044685C /* SNTXPCConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6FDC9518C93A020044685C /* SNTXPCConnection.m */; };
0D7A7AF3174FCF4C00B77646 /* SantaMessage.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0D7A7AF1174FCF4C00B77646 /* SantaMessage.cc */; };
@@ -113,10 +111,13 @@
0DA73CA11934F8100056D7C4 /* SNTLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DA73C9E1934F8100056D7C4 /* SNTLogging.m */; };
0DA73CA21934F88D0056D7C4 /* SNTLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DA73C9E1934F8100056D7C4 /* SNTLogging.m */; };
0DB390991AB1E11400614002 /* SNTCommandVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DB390981AB1E11400614002 /* SNTCommandVersion.m */; };
0DB537871AFD36EB00487F92 /* SNTRuleTableTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DB537861AFD36EB00487F92 /* SNTRuleTableTest.m */; };
0DB8ACC1185662DC00FEF9C7 /* SNTApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DB8ACC0185662DC00FEF9C7 /* SNTApplication.m */; };
0DC5D86D191AED220078A5C0 /* SNTRuleTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DC5D86C191AED220078A5C0 /* SNTRuleTable.m */; };
0DC5D86E191AED220078A5C0 /* SNTRuleTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DC5D86C191AED220078A5C0 /* SNTRuleTable.m */; };
0DC5D871192160180078A5C0 /* SNTCommandSyncLogUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DC5D870192160180078A5C0 /* SNTCommandSyncLogUpload.m */; };
0DC765EA1B28D9EA00BAE651 /* santad in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0D9A7F3D1759330500035EB5 /* santad */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
0DC765EB1B28D9EA00BAE651 /* santactl in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0D35BD9E18FD71CE00921A21 /* santactl */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
0DCA552718C95928002A7DAE /* SNTXPCConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6FDC9518C93A020044685C /* SNTXPCConnection.m */; };
0DCD5FBF1909D64A006B445C /* SNTCommandBinaryInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD5FBE1909D64A006B445C /* SNTCommandBinaryInfo.m */; };
0DCD6042190ACCB8006B445C /* SNTFileInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD6041190ACCB8006B445C /* SNTFileInfo.m */; };
@@ -134,12 +135,9 @@
0DCD605C19117A90006B445C /* SNTCommandSyncPreflight.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD605B19117A90006B445C /* SNTCommandSyncPreflight.m */; };
0DCD6062191188B1006B445C /* SNTAuthenticatingURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD6061191188B1006B445C /* SNTAuthenticatingURLSession.m */; };
0DD0D487194F5187005F27EB /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D3AFBF718FB4C870087BCEE /* IOKit.framework */; };
0DD0D48B194F6193005F27EB /* SNTCertificateTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD0D48A194F6193005F27EB /* SNTCertificateTest.m */; };
0DD0D48D194F6D5B005F27EB /* SNTCodesignCheckerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD0D48C194F6D5B005F27EB /* SNTCodesignCheckerTest.m */; };
0DD0D48F194F78F8005F27EB /* SNTFileInfoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD0D48E194F78F8005F27EB /* SNTFileInfoTest.m */; };
0DD0D491194F9947005F27EB /* SNTExecutionControllerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD0D490194F9947005F27EB /* SNTExecutionControllerTest.m */; };
0DD0D492194F9BEF005F27EB /* SNTLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DA73C9E1934F8100056D7C4 /* SNTLogging.m */; };
0DD65D98184D2F0A00822DA7 /* SNTCodesignChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D59C0E317710E6000748EBF /* SNTCodesignChecker.m */; };
0DE4C8A118FEF28200466D04 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D8C200B180F359A00CE2BF8 /* Security.framework */; };
0DE4C8A618FF3B1700466D04 /* SNTCommandFlushCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE4C8A518FF3B1700466D04 /* SNTCommandFlushCache.m */; };
0DE50F681912716A007B2B0C /* SNTRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE50F671912716A007B2B0C /* SNTRule.m */; };
@@ -147,6 +145,8 @@
0DE50F6C19130358007B2B0C /* SNTStoredEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD604A19105433006B445C /* SNTStoredEvent.m */; };
0DE50F6E191304E0007B2B0C /* SNTRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE50F671912716A007B2B0C /* SNTRule.m */; };
0DE6788D1784A8C2007A9E52 /* SNTExecutionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */; };
0DE71A751B95F7F900518526 /* SNTCachedDecision.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE71A741B95F7F900518526 /* SNTCachedDecision.m */; };
0DE71A761B95F7F900518526 /* SNTCachedDecision.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE71A741B95F7F900518526 /* SNTCachedDecision.m */; };
0DEFB7C01ACB28B000B92AAE /* SNTCommandSyncConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7BF1ACB28B000B92AAE /* SNTCommandSyncConstants.m */; };
0DEFB7C41ACDD80100B92AAE /* SNTFileWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7C31ACDD80100B92AAE /* SNTFileWatcher.m */; };
0DEFB7C51ACDD80100B92AAE /* SNTFileWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7C31ACDD80100B92AAE /* SNTFileWatcher.m */; };
@@ -156,6 +156,7 @@
0DF395661AB76ABC00CBC520 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 0DF395651AB76ABC00CBC520 /* libz.dylib */; };
4092327A1A51B66400A04527 /* SNTCommandRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 409232791A51B65D00A04527 /* SNTCommandRule.m */; };
8BFD9B39112F4D16B3D0EFFB /* libPods-LogicTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 752301D17AA44BDE8B6D0541 /* libPods-LogicTests.a */; };
CB7CD33A4D1B4745552333F4 /* libPods-Santa.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E7544A9B819620883181F257 /* libPods-Santa.a */; };
E86AE075D7F24FB88FB627C5 /* libPods-santad.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A84545E322F475FA0B505D5 /* libPods-santad.a */; };
/* End PBXBuildFile section */
@@ -202,8 +203,36 @@
remoteGlobalIDString = 0D9A7F3C1759330400035EB5;
remoteInfo = santad;
};
0DC765E51B28D9C600BAE651 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0D91BCA8174E8A6500131A7D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 0D9A7F3C1759330400035EB5;
remoteInfo = santad;
};
0DC765E71B28D9C600BAE651 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0D91BCA8174E8A6500131A7D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 0D35BD9D18FD71CE00921A21;
remoteInfo = santactl;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
0DC765E91B28D9CB00BAE651 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 6;
files = (
0DC765EA1B28D9EA00BAE651 /* santad in CopyFiles */,
0DC765EB1B28D9EA00BAE651 /* santactl in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0A84545E322F475FA0B505D5 /* libPods-santad.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-santad.a"; sourceTree = BUILT_PRODUCTS_DIR; };
0D0016A2192BCD3C005E7FCD /* KernelTests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = KernelTests; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -215,8 +244,6 @@
0D10BE851A0AABD600C0C944 /* SNTDropRootPrivs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTDropRootPrivs.m; sourceTree = "<group>"; };
0D10BE881A0AAC2100C0C944 /* SNTDropRootPrivs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTDropRootPrivs.h; sourceTree = "<group>"; };
0D10BE8A1A0AB23300C0C944 /* SNTDERDecoderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTDERDecoderTest.m; sourceTree = "<group>"; };
0D1AF475187C7A2C00D3298D /* SNTCertificate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCertificate.h; sourceTree = "<group>"; };
0D1AF476187C7A2C00D3298D /* SNTCertificate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCertificate.m; sourceTree = "<group>"; };
0D1B476D19A53419008CADD3 /* SNTAboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTAboutWindowController.h; sourceTree = "<group>"; };
0D1B476E19A53419008CADD3 /* SNTAboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTAboutWindowController.m; sourceTree = "<group>"; };
0D1B476F19A53419008CADD3 /* AboutWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AboutWindow.xib; sourceTree = "<group>"; };
@@ -265,8 +292,10 @@
0D4644C3182AF81700098690 /* SantaDecisionManager.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SantaDecisionManager.cc; sourceTree = "<group>"; };
0D4644C4182AF81700098690 /* SantaDecisionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SantaDecisionManager.h; sourceTree = "<group>"; };
0D4A5006176A4602004F63BF /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = "<absolute>"; };
0D59C0E217710E6000748EBF /* SNTCodesignChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCodesignChecker.h; sourceTree = "<group>"; };
0D59C0E317710E6000748EBF /* SNTCodesignChecker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCodesignChecker.m; sourceTree = "<group>"; };
0D536ED51B8E7A2E0039A26D /* bad_pagezero */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = bad_pagezero; sourceTree = "<group>"; };
0D536ED61B8E7A2E0039A26D /* missing_pagezero */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = missing_pagezero; sourceTree = "<group>"; };
0D536ED91B94E9230039A26D /* SNTEventLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTEventLog.h; sourceTree = "<group>"; };
0D536EDA1B94E9230039A26D /* SNTEventLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTEventLog.m; sourceTree = "<group>"; };
0D63DD5A1906FCB400D346C4 /* SNTDatabaseController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTDatabaseController.h; sourceTree = "<group>"; };
0D63DD5B1906FCB400D346C4 /* SNTDatabaseController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTDatabaseController.m; sourceTree = "<group>"; };
0D668E7F18D1121700E29A8B /* SNTMessageWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTMessageWindow.h; sourceTree = "<group>"; };
@@ -301,6 +330,7 @@
0DA73C9E1934F8100056D7C4 /* SNTLogging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTLogging.m; sourceTree = "<group>"; };
0DB2B92318085753001C01D9 /* santad-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "santad-Prefix.pch"; sourceTree = "<group>"; };
0DB390981AB1E11400614002 /* SNTCommandVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SNTCommandVersion.m; path = version/SNTCommandVersion.m; sourceTree = "<group>"; };
0DB537861AFD36EB00487F92 /* SNTRuleTableTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTRuleTableTest.m; sourceTree = "<group>"; };
0DB8ACBF185662DC00FEF9C7 /* SNTApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTApplication.h; sourceTree = "<group>"; };
0DB8ACC0185662DC00FEF9C7 /* SNTApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SNTApplication.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
0DB8ACE41858D73000FEF9C7 /* santad-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "santad-Info.plist"; sourceTree = "<group>"; };
@@ -321,8 +351,6 @@
0DCD605B19117A90006B445C /* SNTCommandSyncPreflight.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandSyncPreflight.m; sourceTree = "<group>"; };
0DCD6060191188B1006B445C /* SNTAuthenticatingURLSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTAuthenticatingURLSession.h; sourceTree = "<group>"; };
0DCD6061191188B1006B445C /* SNTAuthenticatingURLSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTAuthenticatingURLSession.m; sourceTree = "<group>"; };
0DD0D48A194F6193005F27EB /* SNTCertificateTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCertificateTest.m; sourceTree = "<group>"; };
0DD0D48C194F6D5B005F27EB /* SNTCodesignCheckerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCodesignCheckerTest.m; sourceTree = "<group>"; };
0DD0D48E194F78F8005F27EB /* SNTFileInfoTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTFileInfoTest.m; sourceTree = "<group>"; };
0DD0D490194F9947005F27EB /* SNTExecutionControllerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTExecutionControllerTest.m; sourceTree = "<group>"; };
0DE4C8A518FF3B1700466D04 /* SNTCommandFlushCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandFlushCache.m; sourceTree = "<group>"; };
@@ -330,6 +358,8 @@
0DE50F671912716A007B2B0C /* SNTRule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTRule.m; sourceTree = "<group>"; };
0DE6788B1784A8C2007A9E52 /* SNTExecutionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTExecutionController.h; sourceTree = "<group>"; };
0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SNTExecutionController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
0DE71A731B95F7F900518526 /* SNTCachedDecision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCachedDecision.h; sourceTree = "<group>"; };
0DE71A741B95F7F900518526 /* SNTCachedDecision.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCachedDecision.m; sourceTree = "<group>"; };
0DEFB7BF1ACB28B000B92AAE /* SNTCommandSyncConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandSyncConstants.m; sourceTree = "<group>"; };
0DEFB7C11ACB28BC00B92AAE /* SNTCommandSyncConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTCommandSyncConstants.h; sourceTree = "<group>"; };
0DEFB7C21ACDD80100B92AAE /* SNTFileWatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTFileWatcher.h; sourceTree = "<group>"; };
@@ -340,10 +370,16 @@
0DF395651AB76ABC00CBC520 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
13A4FE400F3857C0F5831498 /* Pods-LogicTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LogicTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LogicTests/Pods-LogicTests.debug.xcconfig"; sourceTree = "<group>"; };
409232791A51B65D00A04527 /* SNTCommandRule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SNTCommandRule.m; path = rule/SNTCommandRule.m; sourceTree = "<group>"; };
54DD0E77B8BE005AFB7E944A /* Pods-santactl.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santactl.release.xcconfig"; path = "Pods/Target Support Files/Pods-santactl/Pods-santactl.release.xcconfig"; sourceTree = "<group>"; };
583E60E97931BA83D5273304 /* Pods-santactl.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santactl.debug.xcconfig"; path = "Pods/Target Support Files/Pods-santactl/Pods-santactl.debug.xcconfig"; sourceTree = "<group>"; };
627BB4EC9917DC20E89D718C /* Pods-santad.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santad.debug.xcconfig"; path = "Pods/Target Support Files/Pods-santad/Pods-santad.debug.xcconfig"; sourceTree = "<group>"; };
752301D17AA44BDE8B6D0541 /* libPods-LogicTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LogicTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
8003CA1D3E46447BCEA56440 /* Pods-santad.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santad.release.xcconfig"; path = "Pods/Target Support Files/Pods-santad/Pods-santad.release.xcconfig"; sourceTree = "<group>"; };
8D738300867507BD7985972F /* Pods-Santa.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santa.release.xcconfig"; path = "Pods/Target Support Files/Pods-Santa/Pods-Santa.release.xcconfig"; sourceTree = "<group>"; };
A2EA300312100AE62218F84E /* libPods-santactl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-santactl.a"; sourceTree = BUILT_PRODUCTS_DIR; };
BE74E23CF5A553E5F02462B9 /* Pods-LogicTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LogicTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-LogicTests/Pods-LogicTests.release.xcconfig"; sourceTree = "<group>"; };
DE1F4E0329B023976A1BAC19 /* Pods-Santa.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santa.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Santa/Pods-Santa.debug.xcconfig"; sourceTree = "<group>"; };
E7544A9B819620883181F257 /* libPods-Santa.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Santa.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -374,6 +410,7 @@
0DE4C8A118FEF28200466D04 /* Security.framework in Frameworks */,
0D35BDBD18FDA23600921A21 /* IOKit.framework in Frameworks */,
0D35BD9F18FD71CE00921A21 /* Foundation.framework in Frameworks */,
02D7203A6CEE2A946FB08ECE /* libPods-santactl.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -385,6 +422,7 @@
0D6F12D819EC8822006B218E /* SecurityInterface.framework in Frameworks */,
0D8C200C180F359A00CE2BF8 /* Security.framework in Frameworks */,
0D385DB8180DE4A900418BC6 /* Cocoa.framework in Frameworks */,
CB7CD33A4D1B4745552333F4 /* libPods-Santa.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -414,13 +452,12 @@
isa = PBXGroup;
children = (
0D260DB018B68E12002A0B55 /* Resources */,
0DD0D48A194F6193005F27EB /* SNTCertificateTest.m */,
0DD0D48C194F6D5B005F27EB /* SNTCodesignCheckerTest.m */,
0D10BE8A1A0AB23300C0C944 /* SNTDERDecoderTest.m */,
0D41DAD31A7C28C800A890FE /* SNTEventTableTest.m */,
0DD0D490194F9947005F27EB /* SNTExecutionControllerTest.m */,
0DD0D48E194F78F8005F27EB /* SNTFileInfoTest.m */,
0DEFB7C71ACF0BFE00B92AAE /* SNTFileWatcherTest.m */,
0DB537861AFD36EB00487F92 /* SNTRuleTableTest.m */,
0D3AFBE618FB32CB0087BCEE /* SNTXPCConnectionTest.m */,
);
path = LogicTests;
@@ -429,6 +466,8 @@
0D260DB018B68E12002A0B55 /* Resources */ = {
isa = PBXGroup;
children = (
0D536ED51B8E7A2E0039A26D /* bad_pagezero */,
0D536ED61B8E7A2E0039A26D /* missing_pagezero */,
0D2CD4601A81C7B100C9C910 /* dn.plist */,
0D6FDC8618C6913D0044685C /* apple.pem */,
0D6FDC8218C68D7E0044685C /* GIAG2.crt */,
@@ -594,6 +633,8 @@
0D385DB7180DE4A900418BC6 /* Cocoa.framework */,
752301D17AA44BDE8B6D0541 /* libPods-LogicTests.a */,
0A84545E322F475FA0B505D5 /* libPods-santad.a */,
E7544A9B819620883181F257 /* libPods-Santa.a */,
A2EA300312100AE62218F84E /* libPods-santactl.a */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -618,10 +659,6 @@
0D91BCD5174E8AAB00131A7D /* common */ = {
isa = PBXGroup;
children = (
0D1AF475187C7A2C00D3298D /* SNTCertificate.h */,
0D1AF476187C7A2C00D3298D /* SNTCertificate.m */,
0D59C0E217710E6000748EBF /* SNTCodesignChecker.h */,
0D59C0E317710E6000748EBF /* SNTCodesignChecker.m */,
0D91BCE4174E8B5E00131A7D /* SNTCommonEnums.h */,
0D42D2B619D2042900955F08 /* SNTConfigurator.h */,
0D42D2B719D2042900955F08 /* SNTConfigurator.m */,
@@ -655,18 +692,22 @@
isa = PBXGroup;
children = (
0DA73CA519363C9F0056D7C4 /* DataLayer */,
0D3AF83118F87CEF0087BCEE /* Resources */,
0D9A7F411759330500035EB5 /* main.m */,
0DB8ACBF185662DC00FEF9C7 /* SNTApplication.h */,
0DB8ACC0185662DC00FEF9C7 /* SNTApplication.m */,
0DE71A731B95F7F900518526 /* SNTCachedDecision.h */,
0DE71A741B95F7F900518526 /* SNTCachedDecision.m */,
0D8E18CB19107B56000F89B8 /* SNTDaemonControlController.h */,
0D8E18CC19107B56000F89B8 /* SNTDaemonControlController.m */,
0D63DD5A1906FCB400D346C4 /* SNTDatabaseController.h */,
0D63DD5B1906FCB400D346C4 /* SNTDatabaseController.m */,
0D7D01851774F93A005DBAB4 /* SNTDriverManager.h */,
0D7D01861774F93A005DBAB4 /* SNTDriverManager.m */,
0D536ED91B94E9230039A26D /* SNTEventLog.h */,
0D536EDA1B94E9230039A26D /* SNTEventLog.m */,
0DE6788B1784A8C2007A9E52 /* SNTExecutionController.h */,
0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */,
0D9A7F411759330500035EB5 /* main.m */,
0D3AF83118F87CEF0087BCEE /* Resources */,
);
name = santad;
path = Source/santad;
@@ -724,6 +765,10 @@
BE74E23CF5A553E5F02462B9 /* Pods-LogicTests.release.xcconfig */,
627BB4EC9917DC20E89D718C /* Pods-santad.debug.xcconfig */,
8003CA1D3E46447BCEA56440 /* Pods-santad.release.xcconfig */,
DE1F4E0329B023976A1BAC19 /* Pods-Santa.debug.xcconfig */,
8D738300867507BD7985972F /* Pods-Santa.release.xcconfig */,
583E60E97931BA83D5273304 /* Pods-santactl.debug.xcconfig */,
54DD0E77B8BE005AFB7E944A /* Pods-santactl.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
@@ -779,6 +824,7 @@
0D260DA918B68E12002A0B55 /* Frameworks */,
0D260DAA18B68E12002A0B55 /* Resources */,
85CE5DF0D54C438A8933A631 /* Copy Pods Resources */,
4B68B2D454C1C55D29AA76D8 /* Embed Pods Frameworks */,
);
buildRules = (
);
@@ -793,9 +839,11 @@
isa = PBXNativeTarget;
buildConfigurationList = 0D35BDA918FD71CE00921A21 /* Build configuration list for PBXNativeTarget "santactl" */;
buildPhases = (
AAFCB010997D370F4297BF35 /* Check Pods Manifest.lock */,
0DD98E671A5DD02000A754C6 /* Update Version Info */,
0D35BD9A18FD71CE00921A21 /* Sources */,
0D35BD9B18FD71CE00921A21 /* Frameworks */,
32AA711FA23052E0F2EE59EB /* Copy Pods Resources */,
);
buildRules = (
);
@@ -810,10 +858,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 0D385DE3180DE4A900418BC6 /* Build configuration list for PBXNativeTarget "Santa" */;
buildPhases = (
4347374E751B0BF8CF6A7110 /* Check Pods Manifest.lock */,
0DD98E681A5DD03E00A754C6 /* Update Version Info */,
0D385DB2180DE4A900418BC6 /* Sources */,
0D385DB3180DE4A900418BC6 /* Frameworks */,
0D385DB4180DE4A900418BC6 /* Resources */,
309C93A0ACB98D32A337D908 /* Embed Pods Frameworks */,
2AB78B9612925DE634AE8F7C /* Copy Pods Resources */,
);
buildRules = (
);
@@ -832,10 +883,13 @@
0DD98E691A5DD5C900A754C6 /* Update Module Version */,
0D91BCAE174E8A7E00131A7D /* Sources */,
0D91BCB0174E8A7E00131A7D /* Headers */,
0DC765E91B28D9CB00BAE651 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
0DC765E61B28D9C600BAE651 /* PBXTargetDependency */,
0DC765E81B28D9C600BAE651 /* PBXTargetDependency */,
);
name = "santa-driver";
productName = "santa-driver";
@@ -906,8 +960,10 @@
0D6FDC8518C68E500044685C /* GIAG2.pem in Resources */,
0D6FDC8318C68D7E0044685C /* GIAG2.crt in Resources */,
0D6F12DA19EDE51E006B218E /* tubitak.crt in Resources */,
0D536ED71B8E7A2E0039A26D /* bad_pagezero in Resources */,
0D2CD4611A81C7B100C9C910 /* dn.plist in Resources */,
0D6FDC8718C6913D0044685C /* apple.pem in Resources */,
0D536ED81B8E7A2E0039A26D /* missing_pagezero in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1017,6 +1073,51 @@
shellPath = /bin/sh;
shellScript = "GIT_TAG=$(git describe --abbrev=0 --tags)\nsed -i '' \"s/TO.BE.FILLED/${GIT_TAG}/\" ${DERIVED_FILE_DIR}/santa-driver_info.c";
};
2AB78B9612925DE634AE8F7C /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Santa/Pods-Santa-resources.sh\"\n";
showEnvVarsInLog = 0;
};
309C93A0ACB98D32A337D908 /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Santa/Pods-Santa-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
32AA711FA23052E0F2EE59EB /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-santactl/Pods-santactl-resources.sh\"\n";
showEnvVarsInLog = 0;
};
34C9C9E8C5454BBE980DF8A9 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -1047,6 +1148,36 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-santad/Pods-santad-resources.sh\"\n";
showEnvVarsInLog = 0;
};
4347374E751B0BF8CF6A7110 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
4B68B2D454C1C55D29AA76D8 /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LogicTests/Pods-LogicTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
85CE5DF0D54C438A8933A631 /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -1062,6 +1193,21 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LogicTests/Pods-LogicTests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
AAFCB010997D370F4297BF35 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
AE05898CB3CE4507B2F43B91 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -1093,6 +1239,7 @@
buildActionMask = 2147483647;
files = (
0D88680C1AC48A1400B86659 /* SNTSystemInfo.m in Sources */,
0D536EDC1B94E9230039A26D /* SNTEventLog.m in Sources */,
0D63DD5E1906FCB400D346C4 /* SNTDatabaseController.m in Sources */,
0D3AFBF018FB4C6C0087BCEE /* SNTDriverManager.m in Sources */,
0DCD6044190ACCB8006B445C /* SNTFileInfo.m in Sources */,
@@ -1101,13 +1248,13 @@
0DD0D491194F9947005F27EB /* SNTExecutionControllerTest.m in Sources */,
0D3AFBEF18FB4C6C0087BCEE /* SNTExecutionController.m in Sources */,
0D3AFBEC18FB48E70087BCEE /* SNTEventTable.m in Sources */,
0DB537871AFD36EB00487F92 /* SNTRuleTableTest.m in Sources */,
0DCD604D19105433006B445C /* SNTStoredEvent.m in Sources */,
0DCD605819115E57006B445C /* SNTXPCControlInterface.m in Sources */,
0D10BE891A0AAF6700C0C944 /* SNTDropRootPrivs.m in Sources */,
0DEFB7C61ACDE5F600B92AAE /* SNTFileWatcher.m in Sources */,
0D10BE8B1A0AB23300C0C944 /* SNTDERDecoderTest.m in Sources */,
0DEFB7C81ACF0BFE00B92AAE /* SNTFileWatcherTest.m in Sources */,
0DD0D48B194F6193005F27EB /* SNTCertificateTest.m in Sources */,
0D28D53819D9F5910015C5EB /* SNTConfigurator.m in Sources */,
0D3AFBE718FB32CB0087BCEE /* SNTXPCConnectionTest.m in Sources */,
0DCD605719115E54006B445C /* SNTDaemonControlController.m in Sources */,
@@ -1115,12 +1262,10 @@
0D3AFBEE18FB4C6C0087BCEE /* SNTApplication.m in Sources */,
0DD0D48F194F78F8005F27EB /* SNTFileInfoTest.m in Sources */,
0DC5D86E191AED220078A5C0 /* SNTRuleTable.m in Sources */,
0D31DF4718D254B3002B300D /* SNTCodesignChecker.m in Sources */,
0DD0D492194F9BEF005F27EB /* SNTLogging.m in Sources */,
0DD0D48D194F6D5B005F27EB /* SNTCodesignCheckerTest.m in Sources */,
0DE71A761B95F7F900518526 /* SNTCachedDecision.m in Sources */,
0DCD605919115E5A006B445C /* SNTXPCNotifierInterface.m in Sources */,
0DE50F691912B0CD007B2B0C /* SNTRule.m in Sources */,
0D6FDC8C18C69AF90044685C /* SNTCertificate.m in Sources */,
0D10BE8C1A0AB3FD00C0C944 /* SNTDERDecoder.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1130,7 +1275,6 @@
buildActionMask = 2147483647;
files = (
0DA73CA21934F88D0056D7C4 /* SNTLogging.m in Sources */,
0D35BDC218FDA5D100921A21 /* SNTCodesignChecker.m in Sources */,
0D35BDB518FD84F600921A21 /* SNTCommandSync.m in Sources */,
0DCD5FBF1909D64A006B445C /* SNTCommandBinaryInfo.m in Sources */,
0DEFB7C01ACB28B000B92AAE /* SNTCommandSyncConstants.m in Sources */,
@@ -1151,7 +1295,6 @@
0DCD6043190ACCB8006B445C /* SNTFileInfo.m in Sources */,
0DE50F6E191304E0007B2B0C /* SNTRule.m in Sources */,
0D0A1EC3191998C900B8450F /* SNTCommandSyncRuleDownload.m in Sources */,
0D35BDC018FDA5C800921A21 /* SNTCertificate.m in Sources */,
0DB390991AB1E11400614002 /* SNTCommandVersion.m in Sources */,
0D42D2B519D1D98A00955F08 /* SNTSystemInfo.m in Sources */,
0D827E6719DF3C74006EC811 /* SNTCommandStatus.m in Sources */,
@@ -1173,8 +1316,6 @@
0D827E6519DF392E006EC811 /* SNTConfigurator.m in Sources */,
0D385DF2180DE51600418BC6 /* SNTMessageWindowController.m in Sources */,
0D385DF3180DE51600418BC6 /* SNTNotificationManager.m in Sources */,
0D1AF478187C7A2C00D3298D /* SNTCertificate.m in Sources */,
0DD65D98184D2F0A00822DA7 /* SNTCodesignChecker.m in Sources */,
0D385DC4180DE4A900418BC6 /* main.m in Sources */,
0D1B477019A53419008CADD3 /* SNTAboutWindowController.m in Sources */,
0D668E8118D1121700E29A8B /* SNTMessageWindow.m in Sources */,
@@ -1206,8 +1347,8 @@
0DCD604B19105433006B445C /* SNTStoredEvent.m in Sources */,
0DB8ACC1185662DC00FEF9C7 /* SNTApplication.m in Sources */,
0D9A7F421759330500035EB5 /* main.m in Sources */,
0D1AF477187C7A2C00D3298D /* SNTCertificate.m in Sources */,
0DA73C9F1934F8100056D7C4 /* SNTLogging.m in Sources */,
0DE71A751B95F7F900518526 /* SNTCachedDecision.m in Sources */,
0DCD6042190ACCB8006B445C /* SNTFileInfo.m in Sources */,
0DEFB7C41ACDD80100B92AAE /* SNTFileWatcher.m in Sources */,
0DC5D86D191AED220078A5C0 /* SNTRuleTable.m in Sources */,
@@ -1217,9 +1358,9 @@
0D377C2A17A071B7008453DB /* SNTEventTable.m in Sources */,
0DE50F681912716A007B2B0C /* SNTRule.m in Sources */,
0D37C10F18F6029A0069BC61 /* SNTDatabaseTable.m in Sources */,
0D59C0E417710E6000748EBF /* SNTCodesignChecker.m in Sources */,
0D42D2B819D2042900955F08 /* SNTConfigurator.m in Sources */,
0DCD605519115D17006B445C /* SNTXPCControlInterface.m in Sources */,
0D536EDB1B94E9230039A26D /* SNTEventLog.m in Sources */,
0DCD604F19115A06006B445C /* SNTXPCNotifierInterface.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1257,6 +1398,16 @@
target = 0D9A7F3C1759330400035EB5 /* santad */;
targetProxy = 0D9A7F591759393600035EB5 /* PBXContainerItemProxy */;
};
0DC765E61B28D9C600BAE651 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0D9A7F3C1759330400035EB5 /* santad */;
targetProxy = 0DC765E51B28D9C600BAE651 /* PBXContainerItemProxy */;
};
0DC765E81B28D9C600BAE651 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0D35BD9D18FD71CE00921A21 /* santactl */;
targetProxy = 0DC765E71B28D9C600BAE651 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
@@ -1289,8 +1440,10 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INSTALL_PATH = "";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
WARNING_CFLAGS = "";
};
name = Debug;
};
@@ -1317,8 +1470,10 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INSTALL_PATH = "";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
WARNING_CFLAGS = "";
};
name = Release;
};
@@ -1367,6 +1522,7 @@
);
INFOPLIST_FILE = "Tests/LogicTests/Resources/Tests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = "";
WRAPPER_EXTENSION = xctest;
};
name = Debug;
@@ -1410,12 +1566,14 @@
);
INFOPLIST_FILE = "Tests/LogicTests/Resources/Tests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = "";
WRAPPER_EXTENSION = xctest;
};
name = Release;
};
0D35BDA718FD71CE00921A21 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 583E60E97931BA83D5273304 /* Pods-santactl.debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_MODULES = YES;
@@ -1448,6 +1606,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "${DERIVED_FILE_DIR}/santactl-Info.plist";
INSTALL_PATH = "";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
};
@@ -1455,6 +1614,7 @@
};
0D35BDA818FD71CE00921A21 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 54DD0E77B8BE005AFB7E944A /* Pods-santactl.release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_MODULES = YES;
@@ -1481,6 +1641,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "${DERIVED_FILE_DIR}/santactl-Info.plist";
INSTALL_PATH = "";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
};
@@ -1488,6 +1649,7 @@
};
0D385DE4180DE4A900418BC6 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = DE1F4E0329B023976A1BAC19 /* Pods-Santa.debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
@@ -1529,6 +1691,7 @@
};
0D385DE5180DE4A900418BC6 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8D738300867507BD7985972F /* Pods-Santa.release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
@@ -1568,12 +1731,20 @@
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_WARN_CXX0X_EXTENSIONS = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
GCC_C_LANGUAGE_STANDARD = c99;
MACOSX_DEPLOYMENT_TARGET = 10.9;
ONLY_ACTIVE_ARCH = YES;
PROVISIONING_PROFILE = "";
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = macosx;
WARNING_CFLAGS = (
"-Wall",
"-Wextra",
"-Wno-unused-parameter",
);
};
name = Debug;
};
@@ -1583,12 +1754,20 @@
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_WARN_CXX0X_EXTENSIONS = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = c99;
MACOSX_DEPLOYMENT_TARGET = 10.9;
PROVISIONING_PROFILE = "";
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = macosx;
WARNING_CFLAGS = (
"-Wall",
"-Wextra",
"-Wno-unused-parameter",
);
};
name = Release;
};
@@ -1604,6 +1783,7 @@
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -1622,7 +1802,10 @@
OTHER_LDFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
WARNING_CFLAGS = "-Wno-deprecated-register";
WARNING_CFLAGS = (
"-Wno-deprecated-register",
"$(inherit)",
);
WRAPPER_EXTENSION = kext;
};
name = Debug;
@@ -1639,6 +1822,7 @@
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREPROCESSOR_DEFINITIONS = "";
@@ -1653,7 +1837,10 @@
MODULE_VERSION = TO.BE.FILLED;
OTHER_LDFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = "-Wno-deprecated-register";
WARNING_CFLAGS = (
"-Wno-deprecated-register",
"$(inherit)",
);
WRAPPER_EXTENSION = kext;
};
name = Release;
@@ -1701,7 +1888,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "${DERIVED_FILE_DIR}/santad-Info.plist";
INSTALL_PATH = /usr/sbin;
INSTALL_PATH = "";
LIBRARY_SEARCH_PATHS = "$(inherited)";
PRODUCT_NAME = "$(TARGET_NAME)";
};
@@ -1729,7 +1916,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "${DERIVED_FILE_DIR}/santad-Info.plist";
INSTALL_PATH = /usr/sbin;
INSTALL_PATH = "";
LIBRARY_SEARCH_PATHS = "$(inherited)";
PRODUCT_NAME = "$(TARGET_NAME)";
};

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -49,6 +49,15 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D91BCDC174E8AE600131A7D"
BuildableName = "All"
BlueprintName = "All"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0600"
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -48,7 +48,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D0016A1192BCD3C005E7FCD"
@@ -66,7 +67,8 @@
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D0016A1192BCD3C005E7FCD"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -48,7 +48,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D385DB5180DE4A900418BC6"
@@ -66,7 +67,8 @@
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D385DB5180DE4A900418BC6"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -39,6 +39,15 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D91BCB3174E8A7E00131A7D"
BuildableName = "santa-driver.kext"
BlueprintName = "santa-driver"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -48,7 +48,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D35BD9D18FD71CE00921A21"
@@ -66,7 +67,8 @@
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D35BD9D18FD71CE00921A21"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -49,7 +49,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D9A7F3C1759330400035EB5"
@@ -67,7 +68,8 @@
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D9A7F3C1759330400035EB5"

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14C1514" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14E46" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
@@ -14,7 +14,7 @@
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Santa" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES"/>
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<rect key="contentRect" x="196" y="240" width="480" height="200"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
<view key="contentView" id="se5-gp-TjO">
@@ -37,7 +37,7 @@
<rect key="frame" x="18" y="65" width="444" height="60"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="center" id="CcT-ul-1eA">
<font key="font" metaFont="system"/>
<string key="title">Santa is an application whitelisting system for Mac OS X.
<string key="title">Santa is an application whitelisting system for OS X.
There are no user-configurable settings.</string>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@@ -65,6 +65,9 @@ There are no user-configurable settings.</string>
<buttonCell key="cell" type="push" title="Dismiss" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="uSw-o1-lWW">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<connections>
<action selector="orderOut:" target="-1" id="6oW-zI-zn5"/>

View File

@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14C1514" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="8191" systemVersion="15A282b" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="8191"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="SNTMessageWindowController">
<connections>
<outlet property="applicationNameLabel" destination="qgf-Jf-cJr" id="1JX-X8-03v"/>
<outlet property="openEventButton" destination="7ua-5a-uSd" id="9s4-ZA-Vlo"/>
<outlet property="window" destination="9Bq-yh-54f" id="Uhs-WF-TV9"/>
</connections>
@@ -15,14 +16,15 @@
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="none" id="9Bq-yh-54f" customClass="SNTMessageWindow">
<windowStyleMask key="styleMask" utility="YES"/>
<rect key="contentRect" x="167" y="107" width="550" height="331"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
<rect key="contentRect" x="167" y="107" width="497" height="381"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<view key="contentView" id="Iwq-Lx-rLv">
<rect key="frame" x="0.0" y="0.0" width="550" height="331"/>
<rect key="frame" x="0.0" y="0.0" width="497" height="381"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="t8c-Fx-e5h">
<rect key="frame" x="234" y="261" width="83" height="40"/>
<rect key="frame" x="207" y="311" width="83" height="40"/>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Santa" id="7YA-iB-Zma">
<font key="font" size="34" name="HelveticaNeue-UltraLight"/>
<color key="textColor" red="0.18696189413265307" green="0.18696189413265307" blue="0.18696189413265307" alpha="1" colorSpace="calibratedRGB"/>
@@ -30,11 +32,12 @@
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cD5-Su-lXR">
<rect key="frame" x="25" y="214" width="500" height="17"/>
<rect key="frame" x="22" y="264" width="454" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="496" id="XgJ-EV-tBa"/>
<constraint firstAttribute="width" constant="450" id="XgJ-EV-tBa"/>
</constraints>
<textFieldCell key="cell" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="center" title="A message to the user goes here..." allowsEditingTextAttributes="YES" id="5tH-bG-UJA">
<animations/>
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="center" title="A message to the user goes here..." allowsEditingTextAttributes="YES" id="5tH-bG-UJA">
<font key="font" metaFont="system"/>
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
@@ -44,10 +47,11 @@
</connections>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pc8-G9-4pJ">
<rect key="frame" x="175" y="167" width="324" height="17"/>
<rect key="frame" x="165" y="192" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="320" id="xVR-j3-dLw"/>
<constraint firstAttribute="width" constant="290" id="xVR-j3-dLw"/>
</constraints>
<animations/>
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Binary Path" id="E7T-9h-ofr">
<font key="font" metaFont="system"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
@@ -57,25 +61,46 @@
<binding destination="-2" name="value" keyPath="self.event.filePath" id="qfp-sR-Nmu"/>
</connections>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PXc-xv-A28">
<rect key="frame" x="175" y="117" width="304" height="17"/>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qgf-Jf-cJr">
<rect key="frame" x="165" y="217" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="300" id="4hh-R2-86s"/>
<constraint firstAttribute="width" constant="290" id="Pav-ZA-iAu"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="charWrapping" selectable="YES" sendsActionOnEndEditing="YES" title="File SHA-256" id="X4W-9e-eIu">
<animations/>
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Application Name" id="3UG-ca-d1k">
<font key="font" metaFont="systemBold"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.event.fileBundleName" id="enC-Cl-UWt">
<dictionary key="options">
<string key="NSNullPlaceholder">Unknown</string>
</dictionary>
</binding>
</connections>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PXc-xv-A28">
<rect key="frame" x="165" y="142" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="4hh-R2-86s"/>
</constraints>
<animations/>
<textFieldCell key="cell" lineBreakMode="charWrapping" selectable="YES" sendsActionOnEndEditing="YES" title="Part of SHA-256" id="X4W-9e-eIu">
<font key="font" metaFont="system"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.event.fileSHA256" id="SzX-Ep-rBa"/>
<binding destination="-2" name="value" keyPath="self.shortenedHash" id="xgu-71-9ZT"/>
</connections>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="C3G-wL-u7w">
<rect key="frame" x="175" y="142" width="309" height="17"/>
<rect key="frame" x="165" y="167" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="305" id="Dem-wH-KHm"/>
<constraint firstAttribute="width" constant="290" id="Dem-wH-KHm"/>
</constraints>
<animations/>
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="Code signing information" placeholderString="" id="ztA-La-XgT">
<font key="font" metaFont="system"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
@@ -90,15 +115,26 @@
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="oFj-ol-xpL">
<rect key="frame" x="18" y="92" width="120" height="17"/>
<rect key="frame" x="8" y="92" width="120" height="17"/>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="User" id="1ut-uT-hQD">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="eQb-0a-76J">
<rect key="frame" x="8" y="117" width="120" height="17"/>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Parent" id="gze-4A-1w5">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lvJ-Rk-UT5">
<rect key="frame" x="18" y="142" width="120" height="17"/>
<rect key="frame" x="8" y="167" width="120" height="17"/>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Publisher" id="yL9-yD-JXX">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@@ -106,10 +142,11 @@
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="d9e-Wv-Y5H">
<rect key="frame" x="18" y="167" width="120" height="17"/>
<rect key="frame" x="8" y="192" width="120" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="116" id="Kqd-nX-7df"/>
</constraints>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Path" id="KgY-X1-ESG">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@@ -117,39 +154,50 @@
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEB-eH-x2Y">
<rect key="frame" x="18" y="117" width="120" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="SHA-256" id="eKN-Ic-5zy">
<rect key="frame" x="8" y="142" width="120" height="17"/>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Identifier" id="eKN-Ic-5zy">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="h6f-PY-cc0">
<rect key="frame" x="175" y="92" width="368" height="17"/>
<rect key="frame" x="165" y="92" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="364" id="on6-pj-m2k"/>
<constraint firstAttribute="width" constant="290" id="on6-pj-m2k"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Executing User" id="HRT-Be-ePf">
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Executing User" id="HRT-Be-ePf">
<font key="font" metaFont="system"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.event.executingUser" id="xe2-U2-WrZ"/>
<binding destination="-2" name="value" keyPath="self.event.executingUser" id="IcM-Lt-xTT">
<dictionary key="options">
<string key="NSNullPlaceholder">Unknown</string>
</dictionary>
</binding>
</connections>
</textField>
<box horizontalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="4Li-ul-zIi">
<rect key="frame" x="154" y="92" width="5" height="92"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<box horizontalHuggingPriority="750" title="Line" boxType="custom" borderType="line" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="4Li-ul-zIi">
<rect key="frame" x="146" y="92" width="1" height="142"/>
<constraints>
<constraint firstAttribute="width" constant="1" id="0o1-Jh-epf"/>
</constraints>
<animations/>
<color key="borderColor" white="0.0" alpha="0.17999999999999999" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<button toolTip="Show code signing certificate chain" translatesAutoresizingMaskIntoConstraints="NO" id="cJf-k6-OxS" userLabel="Publisher Certs">
<rect key="frame" x="51" y="143" width="14" height="14"/>
<rect key="frame" x="40" y="168" width="15" height="15"/>
<constraints>
<constraint firstAttribute="width" constant="14" id="QTm-Iv-m5p"/>
<constraint firstAttribute="height" constant="14" id="YwG-0s-jop"/>
<constraint firstAttribute="width" constant="15" id="QTm-Iv-m5p"/>
<constraint firstAttribute="height" constant="15" id="YwG-0s-jop"/>
</constraints>
<animations/>
<buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="NSInfo" imagePosition="overlaps" alignment="center" refusesFirstResponder="YES" imageScaling="proportionallyDown" inset="2" id="R72-Qy-Xbb">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@@ -164,11 +212,12 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BbV-3h-mmL">
<rect key="frame" x="282" y="33" width="110" height="25"/>
<rect key="frame" x="256" y="33" width="110" height="25"/>
<constraints>
<constraint firstAttribute="width" constant="110" id="6Uh-Bd-N64"/>
<constraint firstAttribute="height" constant="22" id="GH6-nw-6rD"/>
</constraints>
<animations/>
<buttonCell key="cell" type="roundTextured" title="Dismiss" bezelStyle="texturedRounded" alignment="center" refusesFirstResponder="YES" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="XR6-Xa-gP4">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@@ -182,10 +231,11 @@ DQ
</connections>
</button>
<button verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="7ua-5a-uSd">
<rect key="frame" x="158" y="33" width="112" height="25"/>
<rect key="frame" x="132" y="33" width="112" height="25"/>
<constraints>
<constraint firstAttribute="width" priority="900" constant="112" id="Pec-Pa-4aZ"/>
</constraints>
<animations/>
<buttonCell key="cell" type="roundTextured" title="Open Event..." bezelStyle="texturedRounded" alignment="center" refusesFirstResponder="YES" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="X1b-TF-1TL">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@@ -198,48 +248,106 @@ DQ
<action selector="openEventDetails:" target="-2" id="VhL-ql-rCV"/>
</connections>
</button>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="f1p-GL-O3o">
<rect key="frame" x="165" y="117" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="h3Y-mO-38F"/>
</constraints>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Parent Name" id="ieo-WK-aDD">
<font key="font" metaFont="system"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="displayPatternValue1" keyPath="self.event.parentName" id="Lce-TO-q9V">
<dictionary key="options">
<string key="NSDisplayPattern">%{value1}@ (%{value2}@)</string>
</dictionary>
</binding>
<binding destination="-2" name="displayPatternValue2" keyPath="self.event.ppid" previousBinding="Lce-TO-q9V" id="ofI-kH-F2d">
<dictionary key="options">
<string key="NSDisplayPattern">%{value1}@ (%{value2}@)</string>
</dictionary>
</binding>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pDa-fA-vnC">
<rect key="frame" x="8" y="217" width="120" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="116" id="8mA-zi-Ev7"/>
</constraints>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Application" id="Hy7-WF-6xW">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="hidden" keyPath="self.event.fileBundleName" id="r2Q-hh-Uy5">
<dictionary key="options">
<string key="NSValueTransformerName">NSIsNil</string>
</dictionary>
</binding>
</connections>
</textField>
</subviews>
<constraints>
<constraint firstItem="h6f-PY-cc0" firstAttribute="bottom" secondItem="4Li-ul-zIi" secondAttribute="bottom" id="1Nc-gl-xMe"/>
<constraint firstItem="f1p-GL-O3o" firstAttribute="centerY" secondItem="eQb-0a-76J" secondAttribute="centerY" id="2Aq-1E-Ybz"/>
<constraint firstItem="BbV-3h-mmL" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" priority="500" constant="193" id="2uo-Cm-Tfp"/>
<constraint firstItem="h6f-PY-cc0" firstAttribute="top" secondItem="f1p-GL-O3o" secondAttribute="bottom" constant="8" id="496-VQ-Fx5"/>
<constraint firstItem="eQb-0a-76J" firstAttribute="leading" secondItem="lvJ-Rk-UT5" secondAttribute="trailing" constant="-116" id="6Q5-Oo-1cI"/>
<constraint firstItem="BbV-3h-mmL" firstAttribute="top" secondItem="oFj-ol-xpL" secondAttribute="bottom" constant="35" id="7K6-bY-Rn6"/>
<constraint firstItem="pDa-fA-vnC" firstAttribute="centerY" secondItem="qgf-Jf-cJr" secondAttribute="centerY" id="AKX-pe-hEX"/>
<constraint firstItem="C3G-wL-u7w" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="ALv-0v-szi"/>
<constraint firstItem="f1p-GL-O3o" firstAttribute="top" secondItem="PXc-xv-A28" secondAttribute="bottom" constant="8" id="E6D-7P-17g"/>
<constraint firstItem="cJf-k6-OxS" firstAttribute="centerY" secondItem="C3G-wL-u7w" secondAttribute="centerY" id="FdL-ZZ-Vbe"/>
<constraint firstItem="t8c-Fx-e5h" firstAttribute="top" secondItem="Iwq-Lx-rLv" secondAttribute="top" constant="30" id="FuB-GX-0jg"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="bottom" secondItem="4Li-ul-zIi" secondAttribute="bottom" id="G0I-O2-S91"/>
<constraint firstItem="lvJ-Rk-UT5" firstAttribute="leading" secondItem="cJf-k6-OxS" secondAttribute="trailing" constant="-45" id="GD2-Ka-deo"/>
<constraint firstItem="h6f-PY-cc0" firstAttribute="centerY" secondItem="oFj-ol-xpL" secondAttribute="centerY" id="GXI-pT-FM1"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="20" id="IwX-ja-ZIs"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="top" secondItem="pDa-fA-vnC" secondAttribute="top" id="Gd4-Nr-n5G"/>
<constraint firstItem="qgf-Jf-cJr" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="Ht4-Lg-U5N"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="IwX-ja-ZIs"/>
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="top" secondItem="4Li-ul-zIi" secondAttribute="top" priority="500" id="JY4-N1-j8e"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="d9e-Wv-Y5H" secondAttribute="leading" priority="999" id="MVr-jY-GDj"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" constant="30" id="Nsl-zf-poH"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" priority="750" constant="30" id="Nsl-zf-poH"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="SCl-Ky-VmT"/>
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="centerY" secondItem="pc8-G9-4pJ" secondAttribute="centerY" id="SLv-F7-w5k"/>
<constraint firstItem="7ua-5a-uSd" firstAttribute="top" secondItem="oFj-ol-xpL" secondAttribute="bottom" constant="35" id="Scq-zQ-Sao"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="KEB-eH-x2Y" secondAttribute="trailing" constant="20" id="Seb-c0-MUL"/>
<constraint firstAttribute="centerX" secondItem="cD5-Su-lXR" secondAttribute="centerX" id="V0a-Py-iEc"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="lvJ-Rk-UT5" secondAttribute="leading" priority="999" id="Z6G-l9-G4a"/>
<constraint firstAttribute="centerX" secondItem="BbV-3h-mmL" secondAttribute="centerX" priority="900" id="acs-5J-vQY"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="top" secondItem="eQb-0a-76J" secondAttribute="bottom" constant="8" id="abm-cM-PN0"/>
<constraint firstItem="pDa-fA-vnC" firstAttribute="leading" secondItem="oFj-ol-xpL" secondAttribute="leading" id="asc-Ga-WHD"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="eQb-0a-76J" secondAttribute="trailing" constant="20" id="b0B-3w-grH"/>
<constraint firstItem="KEB-eH-x2Y" firstAttribute="leading" secondItem="oFj-ol-xpL" secondAttribute="leading" priority="999" id="b5A-M7-ZsD"/>
<constraint firstItem="KEB-eH-x2Y" firstAttribute="centerY" secondItem="PXc-xv-A28" secondAttribute="centerY" id="cHe-pZ-0Oq"/>
<constraint firstItem="cD5-Su-lXR" firstAttribute="top" secondItem="t8c-Fx-e5h" secondAttribute="bottom" constant="30" id="dYg-zP-wh2"/>
<constraint firstItem="h6f-PY-cc0" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="eSz-lz-Fdh"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="top" secondItem="pc8-G9-4pJ" secondAttribute="top" id="fzY-94-y2n"/>
<constraint firstAttribute="centerX" secondItem="t8c-Fx-e5h" secondAttribute="centerX" constant="-0.5" id="h3d-Kc-q88"/>
<constraint firstItem="qgf-Jf-cJr" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" constant="30" id="esg-lX-BAT"/>
<constraint firstItem="f1p-GL-O3o" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="fGd-YS-phP"/>
<constraint firstAttribute="centerX" secondItem="t8c-Fx-e5h" secondAttribute="centerX" id="h3d-Kc-q88"/>
<constraint firstItem="BbV-3h-mmL" firstAttribute="leading" secondItem="7ua-5a-uSd" secondAttribute="trailing" constant="12" id="ioO-NJ-Jqo"/>
<constraint firstItem="C3G-wL-u7w" firstAttribute="centerY" secondItem="lvJ-Rk-UT5" secondAttribute="centerY" id="jfs-YI-7Ae"/>
<constraint firstItem="lvJ-Rk-UT5" firstAttribute="trailing" secondItem="KEB-eH-x2Y" secondAttribute="trailing" id="jlD-Lo-abc"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="oFj-ol-xpL" secondAttribute="trailing" constant="20" id="kOG-Cj-hFG"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="qgf-Jf-cJr" secondAttribute="bottom" constant="8" id="lWU-tC-vWg"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="trailing" secondItem="lvJ-Rk-UT5" secondAttribute="trailing" id="lse-kg-lA2"/>
<constraint firstItem="eQb-0a-76J" firstAttribute="top" secondItem="KEB-eH-x2Y" secondAttribute="bottom" constant="8" id="m2z-1O-ifB"/>
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="trailing" secondItem="KEB-eH-x2Y" secondAttribute="trailing" id="pdq-a6-Y73"/>
<constraint firstAttribute="centerX" secondItem="7ua-5a-uSd" secondAttribute="centerX" constant="61" id="phL-j9-rPq"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="lvJ-Rk-UT5" secondAttribute="trailing" constant="20" id="qKi-KT-jzJ"/>
<constraint firstItem="PXc-xv-A28" firstAttribute="bottom" secondItem="h6f-PY-cc0" secondAttribute="top" constant="-8" id="sG1-gQ-Qoo"/>
<constraint firstItem="C3G-wL-u7w" firstAttribute="bottom" secondItem="PXc-xv-A28" secondAttribute="top" constant="-8" id="snd-8T-LjC"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="d9e-Wv-Y5H" secondAttribute="trailing" constant="20" id="stz-Vm-Kxo"/>
<constraint firstItem="PXc-xv-A28" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="tAa-1s-xVZ"/>
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="width" secondItem="eQb-0a-76J" secondAttribute="width" id="u4p-1B-x5B"/>
<constraint firstAttribute="bottom" secondItem="BbV-3h-mmL" secondAttribute="bottom" constant="35" id="ukF-FH-DE8"/>
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="baseline" secondItem="pc8-G9-4pJ" secondAttribute="baseline" id="xGd-Xr-3Z0"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="bottom" secondItem="C3G-wL-u7w" secondAttribute="top" constant="-8" id="zst-nc-VqA"/>
</constraints>
<animations/>
</view>
<point key="canvasLocation" x="113" y="777.5"/>
<point key="canvasLocation" x="112.5" y="308"/>
</window>
</objects>
<resources>

View File

@@ -39,23 +39,31 @@
[[SNTConfigurator configurator] reloadConfigData];
}];
self.aboutWindowController = [[SNTAboutWindowController alloc] init];
self.notificationManager = [[SNTNotificationManager alloc] init];
NSNotificationCenter *workspaceNotifications = [[NSWorkspace sharedWorkspace] notificationCenter];
[workspaceNotifications addObserver:self
selector:@selector(killConnection)
name:NSWorkspaceSessionDidResignActiveNotification
object:nil];
[workspaceNotifications addObserver:self
selector:@selector(createConnection)
name:NSWorkspaceSessionDidBecomeActiveNotification
object:nil];
[workspaceNotifications addObserverForName:NSWorkspaceSessionDidResignActiveNotification
object:nil
queue:[NSOperationQueue currentQueue]
usingBlock:^(NSNotification *note) {
self.listener.invalidationHandler = nil;
self.listener.rejectedHandler = nil;
[self.listener invalidate];
self.listener = nil;
}];
[workspaceNotifications addObserverForName:NSWorkspaceSessionDidBecomeActiveNotification
object:nil
queue:[NSOperationQueue currentQueue]
usingBlock:^(NSNotification *note) {
[self attemptReconnection];
}];
[self createConnection];
}
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag {
self.aboutWindowController = [[SNTAboutWindowController alloc] init];
[self.aboutWindowController showWindow:self];
return NO;
}
@@ -63,32 +71,25 @@
#pragma mark Connection handling
- (void)createConnection {
__weak __typeof(self) weakSelf = self;
dispatch_sync(dispatch_get_main_queue(), ^{
__weak __typeof(self) weakSelf = self;
self.listener = [[SNTXPCConnection alloc] initClientWithName:[SNTXPCNotifierInterface serviceId]
options:NSXPCConnectionPrivileged];
self.listener.exportedInterface = [SNTXPCNotifierInterface notifierInterface];
self.listener.exportedObject = self.notificationManager;
self.listener.rejectedHandler = ^{
[weakSelf performSelectorInBackground:@selector(attemptReconnection)
withObject:nil];
};
self.listener.invalidationHandler = self.listener.rejectedHandler;
[self.listener resume];
}
- (void)killConnection {
self.listener.invalidationHandler = nil;
[self.listener invalidate];
self.listener = nil;
self.listener = [[SNTXPCConnection alloc] initClientWithName:[SNTXPCNotifierInterface serviceId]
options:NSXPCConnectionPrivileged];
self.listener.exportedInterface = [SNTXPCNotifierInterface notifierInterface];
self.listener.exportedObject = self.notificationManager;
self.listener.rejectedHandler = ^{
[weakSelf attemptReconnection];
};
self.listener.invalidationHandler = self.listener.rejectedHandler;
[self.listener resume];
});
}
- (void)attemptReconnection {
// TODO(rah): Make this smarter.
sleep(10);
[self performSelectorOnMainThread:@selector(createConnection)
withObject:nil
waitUntilDone:NO];
sleep(5);
[self createConnection];
}
#pragma mark Menu Management

View File

@@ -43,8 +43,8 @@
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.15f];
[[NSAnimationContext currentContext] setCompletionHandler:^{
[weakSelf.windowController windowWillClose:nil];
[weakSelf orderOut:nil];
[weakSelf.windowController windowWillClose:sender];
[weakSelf orderOut:sender];
[weakSelf setAlphaValue:1.f];
}];
[[self animator] setAlphaValue:0.f];

View File

@@ -37,7 +37,7 @@
///
/// The custom message to display for this event
///
@property NSString *customMessage;
@property(copy) NSString *customMessage;
///
/// The delegate to inform when the notification is dismissed
@@ -47,12 +47,12 @@
///
/// A 'friendly' string representing the certificate information
///
@property(readonly) NSString *publisherInfo;
@property(readonly, nonatomic) NSString *publisherInfo;
///
/// An optional message to display with this block.
///
@property(readonly) NSAttributedString *attributedCustomMessage;
@property(readonly, nonatomic) NSAttributedString *attributedCustomMessage;
///
/// Reference to the "Open Event" button in the XIB. Used to either remove the button
@@ -60,4 +60,10 @@
///
@property IBOutlet NSButton *openEventButton;
///
/// Reference to the "Application Name" label in the XIB. Used to remove if application
/// doesn't have a CFBundleName.
///
@property IBOutlet NSTextField *applicationNameLabel;
@end

View File

@@ -16,7 +16,7 @@
#import <SecurityInterface/SFCertificatePanel.h>
#import "SNTCertificate.h"
#import "MOLCertificate.h"
#import "SNTConfigurator.h"
#import "SNTFileInfo.h"
#import "SNTMessageWindow.h"
@@ -46,6 +46,10 @@
[self.openEventButton setTitle:eventDetailText];
}
}
if (!self.event.fileBundleName) {
[self.applicationNameLabel removeFromSuperview];
}
}
- (IBAction)showWindow:(id)sender {
@@ -63,7 +67,7 @@
- (IBAction)showCertInfo:(id)sender {
// SFCertificatePanel expects an NSArray of SecCertificateRef's
NSMutableArray *certArray = [NSMutableArray arrayWithCapacity:[self.event.signingChain count]];
for (SNTCertificate *cert in self.event.signingChain) {
for (MOLCertificate *cert in self.event.signingChain) {
[certArray addObject:(id)cert.certRef];
}
@@ -100,8 +104,12 @@
}
}
- (NSString *)shortenedHash {
return [self.event.fileSHA256 substringWithRange:NSMakeRange(0, 10)];
}
- (NSString *)publisherInfo {
SNTCertificate *leafCert = [self.event.signingChain firstObject];
MOLCertificate *leafCert = [self.event.signingChain firstObject];
if (leafCert.commonName && leafCert.orgName) {
return [NSString stringWithFormat:@"%@ - %@", leafCert.orgName, leafCert.commonName];
@@ -129,8 +137,11 @@
if ([self.customMessage length] > 0) {
message = self.customMessage;
} else {
message = @"The following application has been blocked from executing<br />"
@"because its trustworthiness cannot be determined.";
message = [[SNTConfigurator configurator] defaultBlockMessage];
if (!message) {
message = @"The following application has been blocked from executing<br />"
@"because its trustworthiness cannot be determined.";
}
}
NSString *fullHTML = [NSString stringWithFormat:@"%@%@%@", htmlHeader, message, htmlFooter];

View File

@@ -20,5 +20,4 @@
///
@interface SNTNotificationManager : NSObject<SNTMessageWindowControllerDelegate, SNTNotifierXPC>
@end

View File

@@ -54,8 +54,8 @@
- (void)postBlockNotification:(SNTStoredEvent *)event withCustomMessage:(NSString *)message {
// See if this binary is already in the list of pending notifications.
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"event.fileSHA256==%@",
event.fileSHA256];
NSPredicate *predicate =
[NSPredicate predicateWithFormat:@"event.fileSHA256==%@", event.fileSHA256];
if ([[self.pendingNotifications filteredArrayUsingPredicate:predicate] count]) return;
if (!event) {

View File

@@ -1,111 +0,0 @@
/// Copyright 2015 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// SNTCertificate wraps a @c SecCertificateRef to provide Objective-C accessors to
/// commonly used certificate data. Accessors cache data for repeated access.
///
@interface SNTCertificate : NSObject<NSSecureCoding>
///
/// Initialize a SNTCertificate object with a valid SecCertificateRef. Designated initializer.
///
/// @param certRef valid SecCertificateRef, which will be retained.
///
- (instancetype)initWithSecCertificateRef:(SecCertificateRef)certRef;
///
/// Initialize a SNTCertificate object with certificate data in DER format.
///
/// @param certData DER-encoded certificate data.
/// @return initialized SNTCertificate or nil if certData is not a DER-encoded certificate.
///
- (instancetype)initWithCertificateDataDER:(NSData *)certData;
///
/// Initialize a SNTCertificate object with certificate data in PEM format.
/// If multiple PEM certificates exist within the string, the first is used.
///
/// @param certData PEM-encoded certificate data.
/// @return initialized SNTCertifcate or nil if certData is not a PEM-encoded certificate.
///
- (instancetype)initWithCertificateDataPEM:(NSString *)certData;
///
/// Returns an array of SNTCertificate's for all of the certificates in @c pemData.
///
/// @param pemData PEM-encoded certificates.
/// @return array of SNTCertificate objects.
///
+ (NSArray *)certificatesFromPEM:(NSString *)pemData;
///
/// Access the underlying certificate ref.
///
@property(readonly) SecCertificateRef certRef;
///
/// SHA-1 hash of the certificate data.
///
@property(readonly) NSString *SHA1;
///
/// SHA-256 hash of the certificate data.
///
@property(readonly) NSString *SHA256;
///
/// Certificate data.
///
@property(readonly) NSData *certData;
///
/// Common Name e.g: "Software Signing"
///
@property(readonly) NSString *commonName;
///
/// Country Name e.g: "US"
///
@property(readonly) NSString *countryName;
///
/// Organizational Name e.g: "Apple Inc."
///
@property(readonly) NSString *orgName;
///
/// Organizational Unit Name e.g: "Apple Software"
///
@property(readonly) NSString *orgUnit;
///
/// Issuer details, same fields as above.
///
@property(readonly) NSString *issuerCommonName;
@property(readonly) NSString *issuerCountryName;
@property(readonly) NSString *issuerOrgName;
@property(readonly) NSString *issuerOrgUnit;
///
/// Validity Not Before
///
@property(readonly) NSDate *validFrom;
///
/// Validity Not After
///
@property(readonly) NSDate *validUntil;
@end

View File

@@ -1,362 +0,0 @@
/// Copyright 2015 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTCertificate.h"
#import <CommonCrypto/CommonDigest.h>
#import <Security/Security.h>
@interface SNTCertificate ()
/// A container for cached property values
@property NSMutableDictionary *memoizedData;
@end
@implementation SNTCertificate
static NSString *const kCertDataKey = @"certData";
#pragma mark Init/Dealloc
- (instancetype)initWithSecCertificateRef:(SecCertificateRef)certRef {
self = [super init];
if (self) {
_certRef = certRef;
CFRetain(_certRef);
}
return self;
}
- (instancetype)initWithCertificateDataDER:(NSData *)certData {
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
if (cert) {
// Despite the header file claiming that SecCertificateCreateWithData will return NULL if
// @c certData doesn't contain a valid DER-encoded X509 cert, this isn't always true.
// radar://problem/16124651
// To workaround, check that the certificate serial number can be retrieved. According to
// RFC5280, the serial number field is required.
NSData *ser = CFBridgingRelease(SecCertificateCopySerialNumber(cert, NULL));
if (ser) {
self = [self initWithSecCertificateRef:cert];
} else {
self = nil;
}
CFRelease(cert); // was retained in initWithSecCertificateRef
} else {
self = nil;
}
return self;
}
- (instancetype)initWithCertificateDataPEM:(NSString *)certData {
// Find the PEM and extract the base64-encoded DER data from within
NSScanner *scanner = [NSScanner scannerWithString:certData];
NSString *base64der;
// Locate and parse DER data into |base64der|
[scanner scanUpToString:@"-----BEGIN CERTIFICATE-----" intoString:NULL];
if (!([scanner scanString:@"-----BEGIN CERTIFICATE-----" intoString:NULL] &&
[scanner scanUpToString:@"-----END CERTIFICATE-----" intoString:&base64der] &&
[scanner scanString:@"-----END CERTIFICATE-----" intoString:NULL])) {
return nil;
}
// base64-decode the DER
SecTransformRef transform = SecDecodeTransformCreate(kSecBase64Encoding, NULL);
if (!transform) return nil;
NSData *input = [base64der dataUsingEncoding:NSUTF8StringEncoding];
NSData *output = nil;
if (SecTransformSetAttribute(transform,
kSecTransformInputAttributeName,
(__bridge CFDataRef)input,
NULL)) {
output = CFBridgingRelease(SecTransformExecute(transform, NULL));
}
if (transform) CFRelease(transform);
return [self initWithCertificateDataDER:output];
}
+ (NSArray *)certificatesFromPEM:(NSString *)pemData {
NSScanner *scanner = [NSScanner scannerWithString:pemData];
NSMutableArray *certs = [[NSMutableArray alloc] init];
while (YES) {
NSString *curCert;
[scanner scanUpToString:@"-----BEGIN CERTIFICATE-----" intoString:NULL];
[scanner scanUpToString:@"-----END CERTIFICATE-----" intoString:&curCert];
// If there was no data, break.
if (!curCert) break;
curCert = [curCert stringByAppendingString:@"-----END CERTIFICATE-----"];
SNTCertificate *cert = [[SNTCertificate alloc] initWithCertificateDataPEM:curCert];
// If the data couldn't be turned into a valid SNTCertificate, continue.
if (!cert) continue;
[certs addObject:cert];
}
return certs;
}
- (instancetype)init {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (void)dealloc {
if (_certRef) CFRelease(_certRef);
}
#pragma mark Equality & description
- (BOOL)isEqual:(SNTCertificate *)other {
if (self == other) return YES;
if (![other isKindOfClass:[SNTCertificate class]]) return NO;
return [self.certData isEqual:other.certData];
}
- (NSUInteger)hash {
return [self.certData hash];
}
- (NSString *)description {
return [NSString stringWithFormat:@"/O=%@/OU=%@/CN=%@",
self.orgName,
self.orgUnit,
self.commonName];
}
#pragma mark NSSecureCoding
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.certData forKey:kCertDataKey];
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
NSData *certData = [decoder decodeObjectOfClass:[NSData class] forKey:kCertDataKey];
if ([certData length] == 0) return nil;
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
self = [self initWithSecCertificateRef:cert];
if (cert) CFRelease(cert);
return self;
}
#pragma mark Private Accessors
///
/// For a given selector, caches the value that selector would return on subsequent invocations,
/// using the provided block to get the value on the first invocation.
/// Assumes the selector's value will never change.
///
- (id)memoizedSelector:(SEL)selector forBlock:(id (^)(void))block {
NSString *selName = NSStringFromSelector(selector);
if (!self.memoizedData) {
self.memoizedData = [NSMutableDictionary dictionary];
}
if (!self.memoizedData[selName]) {
id val = block();
if (val) {
self.memoizedData[selName] = val;
} else {
self.memoizedData[selName] = [NSNull null];
}
}
// Return the value if there is one, or nil if the value is NSNull
return self.memoizedData[selName] != [NSNull null] ? self.memoizedData[selName] : nil;
}
- (NSDictionary *)allCertificateValues {
return [self memoizedSelector:_cmd forBlock:^id{
return CFBridgingRelease(SecCertificateCopyValues(self.certRef, NULL, NULL));
}];
}
- (NSDictionary *)x509SubjectName {
return [self memoizedSelector:_cmd forBlock:^id{
return [self allCertificateValues][(__bridge NSString *)kSecOIDX509V1SubjectName];
}];
}
- (NSDictionary *)x509IssuerName {
return [self memoizedSelector:_cmd forBlock:^id{
return [self allCertificateValues][(__bridge NSString *)kSecOIDX509V1IssuerName];
}];
}
///
/// Retrieve the value with the specified label from the X509 dictionary provided
///
/// @param desiredLabel The label you want, e.g: kSecOIDOrganizationName.
/// @param dict The dictionary to look in (Subject or Issuer)
/// @return An @c NSString, the value for the specified label.
///
- (NSString *)x509ValueForLabel:(NSString *)desiredLabel fromDictionary:(NSDictionary *)dict {
@try {
NSArray *valArray = dict[(__bridge NSString *)kSecPropertyKeyValue];
for (NSDictionary *curCertVal in valArray) {
NSString *valueLabel = curCertVal[(__bridge NSString *)kSecPropertyKeyLabel];
if ([valueLabel isEqual:desiredLabel]) {
return curCertVal[(__bridge NSString *)kSecPropertyKeyValue];
}
}
return nil;
}
@catch (NSException *exception) {
return nil;
}
}
///
/// Retrieve the specified date from the certificate's values and convert from a reference date
/// to an NSDate object.
///
/// @param key The identifier for the date: @c kSecOIDX509V1ValiditityNot{Before,After}
/// @return An @c NSDate representing the date and time the certificate is valid from or expires.
///
- (NSDate *)dateForX509Key:(NSString *)key {
NSDictionary *curCertVal = [self allCertificateValues][key];
NSNumber *value = curCertVal[(__bridge NSString *)kSecPropertyKeyValue];
NSTimeInterval interval = [value doubleValue];
if (interval) {
return [NSDate dateWithTimeIntervalSinceReferenceDate:interval];
}
return nil;
}
#pragma mark Public Accessors
- (NSString *)SHA1 {
return [self memoizedSelector:_cmd forBlock:^id{
NSMutableData *SHA1Buffer = [[NSMutableData alloc] initWithCapacity:CC_SHA1_DIGEST_LENGTH];
CC_SHA1([self.certData bytes], (CC_LONG)[self.certData length], [SHA1Buffer mutableBytes]);
const unsigned char *bytes = (const unsigned char *)[SHA1Buffer bytes];
NSMutableString *hexDigest = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
[hexDigest appendFormat:@"%02x", bytes[i]];
}
return hexDigest;
}];
}
- (NSString *)SHA256 {
return [self memoizedSelector:_cmd forBlock:^id{
NSMutableData *SHA256Buffer = [[NSMutableData alloc] initWithCapacity:CC_SHA256_DIGEST_LENGTH];
CC_SHA256([self.certData bytes], (CC_LONG)[self.certData length], [SHA256Buffer mutableBytes]);
const unsigned char *bytes = (const unsigned char *)[SHA256Buffer bytes];
NSMutableString *hexDigest = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
[hexDigest appendFormat:@"%02x", bytes[i]];
}
return hexDigest;
}];
}
- (NSData *)certData {
return CFBridgingRelease(SecCertificateCopyData(self.certRef));
}
- (NSString *)commonName {
return [self memoizedSelector:_cmd forBlock:^id{
CFStringRef commonName = NULL;
SecCertificateCopyCommonName(self.certRef, &commonName);
return CFBridgingRelease(commonName);
}];
}
- (NSString *)countryName {
return [self memoizedSelector:_cmd forBlock:^id{
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDCountryName
fromDictionary:[self x509SubjectName]];
}];
}
- (NSString *)orgName {
return [self memoizedSelector:_cmd forBlock:^id{
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationName
fromDictionary:[self x509SubjectName]];
}];
}
- (NSString *)orgUnit {
return [self memoizedSelector:_cmd forBlock:^id{
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationalUnitName
fromDictionary:[self x509SubjectName]];
}];
}
- (NSDate *)validFrom {
return [self memoizedSelector:_cmd forBlock:^id{
return [self dateForX509Key:(__bridge NSString *)kSecOIDX509V1ValidityNotBefore];
}];
}
- (NSDate *)validUntil {
return [self memoizedSelector:_cmd forBlock:^id{
return [self dateForX509Key:(__bridge NSString *)kSecOIDX509V1ValidityNotAfter];
}];
}
- (NSString *)issuerCommonName {
return [self memoizedSelector:_cmd forBlock:^id{
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDCommonName
fromDictionary:[self x509IssuerName]];
}];
}
- (NSString *)issuerCountryName {
return [self memoizedSelector:_cmd forBlock:^id{
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDCountryName
fromDictionary:[self x509IssuerName]];
}];
}
- (NSString *)issuerOrgName {
return [self memoizedSelector:_cmd forBlock:^id{
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationName
fromDictionary:[self x509IssuerName]];
}];
}
- (NSString *)issuerOrgUnit {
return [self memoizedSelector:_cmd forBlock:^id{
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationalUnitName
fromDictionary:[self x509IssuerName]];
}];
}
@end

View File

@@ -1,90 +0,0 @@
/// Copyright 2015 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
@class SNTCertificate;
///
/// SNTCodesignChecker validates a binary (either on-disk or in memory) has been signed
/// and if so allows for pulling out the certificates that were used to sign it.
///
@interface SNTCodesignChecker : NSObject
///
/// The SecStaticCodeRef that this SNTCodesignChecker is working around
///
@property(readonly) SecStaticCodeRef codeRef;
///
/// Returns a dictionary of raw signing information
///
@property(readonly) NSDictionary *signingInformation;
///
/// Returns an array of @c SNTCertificate objects representing the chain that signed this binary.
///
@property(readonly) NSArray *certificates;
///
/// Returns the leaf certificate that this binary was signed with
///
@property(readonly) SNTCertificate *leafCertificate;
///
/// Returns the on-disk path of this binary.
///
@property(readonly) NSString *binaryPath;
///
/// Designated initializer
/// Takes ownership of the codeRef reference.
///
/// @param codeRef a SecStaticCodeRef or SecCodeRef representing a binary.
/// @return an initialized SNTCodesignChecker if the binary is validly signed, nil otherwise.
///
- (instancetype)initWithSecStaticCodeRef:(SecStaticCodeRef)codeRef;
///
/// Convenience initializer for a binary on disk.
///
/// @param binaryPath A binary file on disk
/// @return an initialized SNTCodesignChecker if file is a binary and is signed, nil otherwise.
///
- (instancetype)initWithBinaryPath:(NSString *)binaryPath;
///
/// Convenience initializer for a binary that is running, by its process ID.
///
/// @param PID Id of a running process.
/// @return an initialized SNTCodesignChecker if binary is signed, nil otherwise.
///
- (instancetype)initWithPID:(pid_t)PID;
///
/// Convenience initializer for the currently running process.
///
/// @return an initialized SNTCodesignChecker if current binary is signed, nil otherwise.
///
- (instancetype)initWithSelf;
///
/// Compares the signatures of the binaries represented by this SNTCodesignChecker and
/// @c otherChecker.
///
/// If both binaries are correctly signed and the leaf signatures are identical.
///
/// @return YES if both binaries are signed with the same leaf certificate.
///
- (BOOL)signingInformationMatches:(SNTCodesignChecker *)otherChecker;
@end

View File

@@ -1,196 +0,0 @@
/// Copyright 2015 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTCodesignChecker.h"
#import <Security/Security.h>
#import "SNTCertificate.h"
/**
* kStaticSigningFlags are the flags used when validating signatures on disk.
*
* Don't validate resources but do validate nested code. Ignoring resources _dramatically_ speeds
* up validation (see below) but does mean images, plists, etc will not be checked and modifying
* these will not be considered invalid. To ensure any code inside the binary is still checked,
* we check nested code.
*
* Timings with different flags:
* Checking Xcode 5.1.1 bundle:
* kSecCSDefaultFlags: 3.895s
* kSecCSDoNotValidateResources: 0.013s
* kSecCSDoNotValidateResources | kSecCSCheckNestedCode: 0.013s
*
* Checking Google Chrome 36.0.1985.143 bundle:
* kSecCSDefaultFlags: 0.529s
* kSecCSDoNotValidateResources: 0.032s
* kSecCSDoNotValidateResources | kSecCSCheckNestedCode: 0.033s
*/
const SecCSFlags kStaticSigningFlags = kSecCSDoNotValidateResources | kSecCSCheckNestedCode;
/**
* kSigningFlags are the flags used when validating signatures for running binaries.
*
* No special flags needed currently.
*/
const SecCSFlags kSigningFlags = kSecCSDefaultFlags;
@interface SNTCodesignChecker ()
/// Array of @c SNTCertificate's representing the chain of certs this executable was signed with.
@property NSMutableArray *certificates;
@end
@implementation SNTCodesignChecker
#pragma mark Init/dealloc
- (instancetype)initWithSecStaticCodeRef:(SecStaticCodeRef)codeRef {
self = [super init];
if (self) {
// First check the signing is valid
if (CFGetTypeID(codeRef) == SecStaticCodeGetTypeID()) {
if (SecStaticCodeCheckValidity(codeRef, kStaticSigningFlags, NULL) != errSecSuccess) {
return nil;
}
} else if (CFGetTypeID(codeRef) == SecCodeGetTypeID()) {
if (SecCodeCheckValidity((SecCodeRef)codeRef, kSigningFlags, NULL) != errSecSuccess) {
return nil;
}
} else {
return nil;
}
// Get CFDictionary of signing information for binary
OSStatus status = errSecSuccess;
CFDictionaryRef signingDict = NULL;
status = SecCodeCopySigningInformation(codeRef, kSecCSSigningInformation, &signingDict);
_signingInformation = CFBridgingRelease(signingDict);
if (status != errSecSuccess) return nil;
// Get array of certificates.
NSArray *certs = _signingInformation[(id)kSecCodeInfoCertificates];
if (!certs) return nil;
// Wrap SecCertificateRef objects in SNTCertificate and put in a new NSArray
NSMutableArray *mutableCerts = [[NSMutableArray alloc] initWithCapacity:certs.count];
for (int i = 0; i < certs.count; ++i) {
SecCertificateRef certRef = (__bridge SecCertificateRef)certs[i];
SNTCertificate *newCert = [[SNTCertificate alloc] initWithSecCertificateRef:certRef];
[mutableCerts addObject:newCert];
}
_certificates = [mutableCerts copy];
_codeRef = codeRef;
CFRetain(_codeRef);
}
return self;
}
- (instancetype)initWithBinaryPath:(NSString *)binaryPath {
SecStaticCodeRef codeRef = NULL;
// Get SecStaticCodeRef for binary
if (SecStaticCodeCreateWithPath((__bridge CFURLRef)[NSURL fileURLWithPath:binaryPath
isDirectory:NO],
kSecCSDefaultFlags,
&codeRef) == errSecSuccess) {
self = [self initWithSecStaticCodeRef:codeRef];
} else {
self = nil;
}
if (codeRef) CFRelease(codeRef);
return self;
}
- (instancetype)initWithPID:(pid_t)PID {
SecCodeRef codeRef = NULL;
NSDictionary *attributes = @{(__bridge NSString *)kSecGuestAttributePid: @(PID)};
if (SecCodeCopyGuestWithAttributes(NULL,
(__bridge CFDictionaryRef)attributes,
kSecCSDefaultFlags,
&codeRef) == errSecSuccess) {
self = [self initWithSecStaticCodeRef:codeRef];
} else {
self = nil;
}
if (codeRef) CFRelease(codeRef);
return self;
}
- (instancetype)initWithSelf {
SecCodeRef codeSelf = NULL;
if (SecCodeCopySelf(kSecCSDefaultFlags, &codeSelf) == errSecSuccess) {
self = [self initWithSecStaticCodeRef:codeSelf];
} else {
self = nil;
}
if (codeSelf) CFRelease(codeSelf);
return self;
}
- (instancetype)init {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (void)dealloc {
if (_codeRef) {
CFRelease(_codeRef);
_codeRef = NULL;
}
}
#pragma mark Description
- (NSString *)description {
NSString *binarySource;
if (CFGetTypeID(self.codeRef) == SecStaticCodeGetTypeID()) {
binarySource = @"On-disk";
} else {
binarySource = @"In-memory";
}
return [NSString stringWithFormat:@"%@ binary, signed by %@, located at: %@",
binarySource,
self.leafCertificate.orgName,
self.binaryPath];
}
#pragma mark Public accessors
- (SNTCertificate *)leafCertificate {
return [self.certificates firstObject];
}
- (NSString *)binaryPath {
CFURLRef path;
OSStatus status = SecCodeCopyPath(_codeRef, kSecCSDefaultFlags, &path);
NSURL *pathURL = CFBridgingRelease(path);
if (status != errSecSuccess) return nil;
return [pathURL path];
}
- (BOOL)signingInformationMatches:(SNTCodesignChecker *)otherChecker {
return [self.certificates isEqual:otherChecker.certificates];
}
@end

View File

@@ -65,4 +65,8 @@ typedef enum {
EVENTSTATE_MAX
} santa_eventstate_t;
static const char *kKextPath = "/Library/Extensions/santa-driver.kext";
static const char *kSantaDPath = "/Library/Extensions/santa-driver.kext/Contents/MacOS/santad";
static const char *kSantaCtlPath = "/Library/Extensions/santa-driver.kext/Contents/MacOS/santactl";
#endif // SANTA__COMMON__COMMONENUMS_H

View File

@@ -28,20 +28,49 @@ extern NSString * const kDefaultConfigFilePath;
///
/// The operating mode.
///
@property santa_clientmode_t clientMode;
@property(nonatomic) santa_clientmode_t clientMode;
///
/// Whether or not to log all events, even for whitelisted binaries.
/// The regex of paths to log file changes for. Regexes are specified in ICU format.
///
@property BOOL logAllEvents;
/// The regex flags IXSM can be used, though the s (dotalL) and m (multiline) flags are
/// pointless as a path only ever has a single line.
/// If the regex doesn't begin with ^ to match from the beginning of the line, it will be added.
///
@property(nonatomic) NSRegularExpression *fileChangesRegex;
# pragma mark - GUI Settings
///
/// The regex of whitelisted paths. Regexes are specified in ICU format.
///
/// The regex flags IXSM can be used, though the s (dotall) and m (multiline) flags are
/// pointless as a path only ever has a single line.
/// If the regex doesn't begin with ^ to match from the beginning of the line, it will be added.
///
@property(nonatomic) NSRegularExpression *whitelistPathRegex;
///
/// The regex of blacklisted paths. Regexes are specified in ICU format.
///
/// The regex flags IXSM can be used, though the s (dotall) and m (multiline) flags are
/// pointless as a path only ever has a single line.
/// If the regex doesn't begin with ^ to match from the beginning of the line, it will be added.
///
@property(nonatomic) NSRegularExpression *blacklistPathRegex;
///
/// Enable __PAGEZERO protection, defaults to YES
/// If this flag is set to NO, 32-bit binaries that are missing
/// the __PAGEZERO segment will not be blocked.
///
@property(readonly, nonatomic) BOOL enablePageZeroProtection;
#pragma mark - GUI Settings
///
/// The URL to open when the user clicks "More Info..." when opening Santa.app.
/// If unset, the button will not be displayed.
///
@property(readonly) NSURL *moreInfoURL;
@property(readonly, nonatomic) NSURL *moreInfoURL;
///
/// When the user gets a block notification, a button can be displayed which will
@@ -57,66 +86,88 @@ extern NSString * const kDefaultConfigFilePath;
///
/// If this item isn't set, the Open Event button will not be displayed.
///
@property(readonly) NSString *eventDetailURL;
@property(readonly, nonatomic) NSString *eventDetailURL;
///
/// Related to the above property, this string represents the text to show on the button.
///
@property(readonly) NSString *eventDetailText;
@property(readonly, nonatomic) NSString *eventDetailText;
# pragma mark - Sync Settings
///
/// For any rule that doesn't have a custom message, this setting overrides the message
/// text that is display. If unset, a reasonable default is provided.
///
@property(readonly, nonatomic) NSString *defaultBlockMessage;
#pragma mark - Sync Settings
///
/// The base URL of the sync server.
///
@property(readonly) NSURL *syncBaseURL;
@property(readonly, nonatomic) NSURL *syncBaseURL;
///
/// If YES, mid-execution event uploads are skipped.
/// This property is never stored on disk.
///
@property BOOL syncBackOff;
///
/// The machine owner.
///
@property(readonly) NSString *machineOwner;
@property(readonly, nonatomic) NSString *machineOwner;
///
/// The last date of successful sync.
///
@property(nonatomic) NSDate *syncLastSuccess;
///
/// If YES a clean sync is required.
///
@property(nonatomic) BOOL syncCleanRequired;
///
/// If set, this over-rides the default machine ID used for syncing.
///
@property(readonly) NSString *machineID;
@property(readonly, nonatomic) NSString *machineID;
# pragma mark Server Auth Settings
#pragma mark Server Auth Settings
///
/// If set, this is valid PEM containing one or more certificates to be used to evaluate the
/// server's SSL chain, overriding the list of trusted CAs distributed with the OS.
///
@property(readonly) NSData *syncServerAuthRootsData;
@property(readonly, nonatomic) NSData *syncServerAuthRootsData;
///
/// This property is the same as the above but is a file on disk containing the PEM data.
///
@property(readonly) NSString *syncServerAuthRootsFile;
@property(readonly, nonatomic) NSString *syncServerAuthRootsFile;
# pragma mark Client Auth Settings
#pragma mark Client Auth Settings
///
/// If set, this contains the location of a PKCS#12 certificate to be used for sync authentication.
///
@property(readonly) NSString *syncClientAuthCertificateFile;
@property(readonly, nonatomic) NSString *syncClientAuthCertificateFile;
///
/// Contains the password for the pkcs#12 certificate.
///
@property(readonly) NSString *syncClientAuthCertificatePassword;
@property(readonly, nonatomic) NSString *syncClientAuthCertificatePassword;
///
/// If set, this is the Common Name of a certificate in the System keychain to be used for
/// sync authentication. The corresponding private key must also be in the keychain.
///
@property(readonly) NSString *syncClientAuthCertificateCn;
@property(readonly, nonatomic) NSString *syncClientAuthCertificateCn;
///
/// If set, this is the Issuer Name of a certificate in the System keychain to be used for
/// sync authentication. The corresponding private key must also be in the keychain.
///
@property(readonly) NSString *syncClientAuthCertificateIssuer;
@property(readonly, nonatomic) NSString *syncClientAuthCertificateIssuer;
///
/// Retrieve an initialized singleton configurator object using the default file path.

View File

@@ -20,6 +20,14 @@
@interface SNTConfigurator ()
@property NSString *configFilePath;
@property NSMutableDictionary *configData;
/// Creating NSRegularExpression objects is not fast, so cache them.
@property NSRegularExpression *cachedFileChangesRegex;
@property NSRegularExpression *cachedWhitelistDirRegex;
@property NSRegularExpression *cachedBlacklistDirRegex;
/// Array of keys that cannot be changed while santad is running if santad didn't make the change.
@property(readonly) NSArray *protectedKeys;
@end
@implementation SNTConfigurator
@@ -29,14 +37,19 @@ NSString * const kDefaultConfigFilePath = @"/var/db/santa/config.plist";
/// The keys in the config file
static NSString * const kClientModeKey = @"ClientMode";
static NSString * const kLogAllEventsKey = @"LogAllEvents";
static NSString * const kFileChangesRegexKey = @"FileChangesRegex";
static NSString * const kWhitelistRegexKey = @"WhitelistRegex";
static NSString * const kBlacklistRegexKey = @"BlacklistRegex";
static NSString * const kEnablePageZeroProtectionKey = @"EnablePageZeroProtection";
static NSString * const kMoreInfoURLKey = @"MoreInfoURL";
static NSString * const kEventDetailURLKey = @"EventDetailURL";
static NSString * const kEventDetailTextKey = @"EventDetailText";
static NSString * const kDefaultBlockMessage = @"DefaultBlockMessage";
static NSString * const kSyncBaseURLKey = @"SyncBaseURL";
static NSString * const kSyncLastSuccess = @"SyncLastSuccess";
static NSString * const kSyncCleanRequired = @"SyncCleanRequired";
static NSString * const kClientAuthCertificateFileKey = @"ClientAuthCertificateFile";
static NSString * const kClientAuthCertificatePasswordKey = @"ClientAuthCertificatePassword";
static NSString * const kClientAuthCertificateCNKey = @"ClientAuthCertificateCN";
@@ -62,7 +75,7 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
return self;
}
# pragma mark Singleton retriever
#pragma mark Singleton retriever
+ (instancetype)configurator {
static SNTConfigurator *sharedConfigurator = nil;
@@ -73,12 +86,19 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
return sharedConfigurator;
}
# pragma mark Public Interface
#pragma mark Protected Keys
- (NSArray *)protectedKeys {
return @[ kClientModeKey, kWhitelistRegexKey, kBlacklistRegexKey,
kFileChangesRegexKey, kSyncBaseURLKey ];
}
#pragma mark Public Interface
- (santa_clientmode_t)clientMode {
int cm = [self.configData[kClientModeKey] intValue];
if (cm > CLIENTMODE_UNKNOWN && cm < CLIENTMODE_MAX) {
return cm;
return (santa_clientmode_t)cm;
} else {
self.configData[kClientModeKey] = @(CLIENTMODE_MONITOR);
return CLIENTMODE_MONITOR;
@@ -92,15 +112,74 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
}
}
- (BOOL)logAllEvents {
return [self.configData[kLogAllEventsKey] boolValue];
- (NSRegularExpression *)whitelistPathRegex {
if (!self.cachedWhitelistDirRegex && self.configData[kWhitelistRegexKey]) {
NSString *re = self.configData[kWhitelistRegexKey];
if (![re hasPrefix:@"^"]) re = [@"^" stringByAppendingString:re];
self.cachedWhitelistDirRegex = [NSRegularExpression regularExpressionWithPattern:re
options:0
error:NULL];
}
return self.cachedWhitelistDirRegex;
}
- (void)setLogAllEvents:(BOOL)logAllEvents {
self.configData[kLogAllEventsKey] = @(logAllEvents);
- (void)setWhitelistPathRegex:(NSRegularExpression *)re {
if (!re) {
[self.configData removeObjectForKey:kWhitelistRegexKey];
} else {
self.configData[kWhitelistRegexKey] = [re pattern];
}
self.cachedWhitelistDirRegex = nil;
[self saveConfigToDisk];
}
- (NSRegularExpression *)blacklistPathRegex {
if (!self.cachedBlacklistDirRegex && self.configData[kBlacklistRegexKey]) {
NSString *re = self.configData[kBlacklistRegexKey];
if (![re hasPrefix:@"^"]) re = [@"^" stringByAppendingString:re];
self.cachedBlacklistDirRegex = [NSRegularExpression regularExpressionWithPattern:re
options:0
error:NULL];
}
return self.cachedBlacklistDirRegex;
}
- (void)setBlacklistPathRegex:(NSRegularExpression *)re {
if (!re) {
[self.configData removeObjectForKey:kBlacklistRegexKey];
} else {
self.configData[kBlacklistRegexKey] = [re pattern];
}
self.cachedBlacklistDirRegex = nil;
[self saveConfigToDisk];
}
- (NSRegularExpression *)fileChangesRegex {
if (!self.cachedFileChangesRegex && self.configData[kFileChangesRegexKey]) {
NSString *re = self.configData[kFileChangesRegexKey];
if (![re hasPrefix:@"^"]) re = [@"^" stringByAppendingString:re];
self.cachedFileChangesRegex = [NSRegularExpression regularExpressionWithPattern:re
options:0
error:NULL];
}
return self.cachedFileChangesRegex;
}
- (void)setFileChangesRegex:(NSRegularExpression *)re {
if (!re) {
[self.configData removeObjectForKey:kFileChangesRegexKey];
} else {
self.configData[kFileChangesRegexKey] = [re pattern];
}
self.cachedFileChangesRegex = nil;
[self saveConfigToDisk];
}
- (BOOL)enablePageZeroProtection {
NSNumber *keyValue = self.configData[kEnablePageZeroProtectionKey];
return keyValue ? [keyValue boolValue] : YES;
}
- (NSURL *)moreInfoURL {
return [NSURL URLWithString:self.configData[kMoreInfoURLKey]];
}
@@ -113,6 +192,10 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
return self.configData[kEventDetailTextKey];
}
- (NSString *)defaultBlockMessage {
return self.configData[kDefaultBlockMessage];
}
- (NSURL *)syncBaseURL {
return [NSURL URLWithString:self.configData[kSyncBaseURLKey]];
}
@@ -141,6 +224,24 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
return self.configData[kServerAuthRootsFileKey];
}
- (NSDate *)syncLastSuccess {
return self.configData[kSyncLastSuccess];
}
- (void)setSyncLastSuccess:(NSDate *)syncLastSuccess {
self.configData[kSyncLastSuccess] = syncLastSuccess;
[self saveConfigToDisk];
}
- (BOOL)syncCleanRequired {
return [self.configData[kSyncCleanRequired] boolValue];
}
- (void)setSyncCleanRequired:(BOOL)syncCleanRequired {
self.configData[kSyncCleanRequired] = @(syncCleanRequired);
[self saveConfigToDisk];
}
- (NSString *)machineOwner {
NSString *machineOwner;
@@ -180,8 +281,6 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
}
- (void)reloadConfigData {
if (!self.configData) self.configData = [NSMutableDictionary dictionary];
NSFileManager *fm = [NSFileManager defaultManager];
if (![fm fileExistsAtPath:self.configFilePath]) return;
@@ -196,7 +295,7 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
NSDictionary *configData =
[NSPropertyListSerialization propertyListWithData:readData
options:kCFPropertyListImmutable
options:NSPropertyListImmutable
format:NULL
error:&error];
if (error) {
@@ -204,16 +303,28 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
return;
}
// Ensure no-one is trying to change the client mode behind Santa's back.
if (self.configData[kClientModeKey] && configData[kClientModeKey] &&
![self.configData[kClientModeKey] isEqual:configData[kClientModeKey]] &&
geteuid() == 0) {
NSMutableDictionary *configDataMutable = [configData mutableCopy];
configDataMutable[kClientModeKey] = self.configData[kClientModeKey];
self.configData = configDataMutable;
[self saveConfigToDisk];
} else {
if (!self.configData) {
self.configData = [configData mutableCopy];
} else if (self.syncBaseURL) {
// Ensure no-one is trying to change protected keys behind our back.
NSMutableDictionary *configDataMutable = [configData mutableCopy];
BOOL changed = NO;
for (NSString *key in self.protectedKeys) {
if (geteuid() == 0 &&
((self.configData[key] && !configData[key]) ||
(!self.configData[key] && configData[key]) ||
(self.configData[key] && ![self.configData[key] isEqual:configData[key]]))) {
if (self.configData[key]) {
configDataMutable[key] = self.configData[key];
} else {
[configDataMutable removeObjectForKey:key];
}
changed = YES;
LOGI(@"Ignoring changed configuration key: %@", key);
}
}
self.configData = configDataMutable;
if (changed) [self saveConfigToDisk];
}
}

View File

@@ -17,4 +17,4 @@
///
/// @return YES if dropping was successful or unnecessary.
///
BOOL DropRootPrivileges();
BOOL DropRootPrivileges(void);

View File

@@ -16,8 +16,9 @@
BOOL DropRootPrivileges() {
if (getuid() == 0 || geteuid() == 0 || getgid() == 0 || getegid() == 0) {
if (setgid(-2) != 0 || setgroups(0, NULL) != 0 || setegid(-2) != 0 ||
setuid(-2) != 0 || seteuid(-2) != 0) {
uid_t nobody = (uid_t)-2;
if (setgid(nobody) != 0 || setgroups(0, NULL) != 0 || setegid(nobody) != 0 ||
setuid(nobody) != 0 || seteuid(nobody) != 0) {
return false;
}

View File

@@ -23,6 +23,16 @@
///
/// @param path The path of the file this instance is to represent. The path will be
/// converted to an absolute, standardized path if it isn't already.
/// @param error If an error occurred and nil is returned, this will be a pointer to an NSError
/// describing the problem.
///
- (instancetype)initWithPath:(NSString *)path error:(NSError **)error;
///
/// Convenience initializer.
///
/// @param path The path to the file this instance is to represent. The path will be
/// converted to an absolute, standardized path if it isn't already.
///
- (instancetype)initWithPath:(NSString *)path;
@@ -82,6 +92,11 @@
///
- (BOOL)isScript;
///
/// @return YES if this file has a bad/missing __PAGEZERO .
///
- (BOOL)isMissingPageZero;
///
/// @return An NSBundle if this file is part of a bundle.
///
@@ -94,8 +109,8 @@
///
/// @return Either the Info.plist in the bundle this file is part of, or an embedded plist if there
/// is one. In the odd case that a file has both an embedded Info.plist and is part of a bundle,
/// the Info.plist from the bundle will be returned.
/// is one. In the unlikely event that a file has both an embedded Info.plist and is part of a
/// bundle, the embedded plist will be returned.
///
- (NSDictionary *)infoPlist;
@@ -120,9 +135,28 @@
- (NSString *)bundleShortVersionString;
///
/// @return any URLs this file may have been downloaded from, using the
/// @c com.apple.metadata:kMDItemWhereFroms extended attribute.
/// @return LaunchServices quarantine data - download URL as an absolute string.
///
- (NSArray *)downloadURLs;
- (NSString *)quarantineDataURL;
///
/// @return LaunchServices quarantine data - referer URL as an absolute string.
///
- (NSString *)quarantineRefererURL;
///
/// @return LaunchServices quarantine data - agent bundle ID.
///
- (NSString *)quarantineAgentBundleID;
///
/// @return LaunchServices quarantine data - timestamp.
///
- (NSDate *)quarantineTimestamp;
///
/// @return The size of the file in bytes.
///
- (NSUInteger)fileSize;
@end

View File

@@ -18,57 +18,74 @@
#include <mach-o/loader.h>
#include <mach-o/swap.h>
#include <sys/xattr.h>
// Simple class to hold the data of a mach_header and the offset within the file
// in which that header was found.
@interface MachHeaderWithOffset : NSObject
@property NSData *data;
@property uint32_t offset;
- (instancetype)initWithData:(NSData *)data offset:(uint32_t)offset;
@end
@implementation MachHeaderWithOffset
- (instancetype)initWithData:(NSData *)data offset:(uint32_t)offset {
self = [super init];
if (self) {
_data = data;
_offset = offset;
}
return self;
}
@end
@interface SNTFileInfo ()
@property NSString *path;
@property NSData *fileData;
// Dictionary of MachHeaderWithOffset objects where the keys are the architecture strings
@property NSDictionary *machHeaders;
// Cached properties
@property NSBundle *bundleRef;
@property NSDictionary *infoDict;
@property NSDictionary *quarantineDict;
@end
@implementation SNTFileInfo
- (instancetype)initWithPath:(NSString *)path {
extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
- (instancetype)initWithPath:(NSString *)path error:(NSError **)error {
self = [super init];
if (self) {
// Convert to absolute, standardized path
path = [path stringByResolvingSymlinksInPath];
if (![path isAbsolutePath]) {
NSString *cwd = [[NSFileManager defaultManager] currentDirectoryPath];
path = [cwd stringByAppendingPathComponent:path];
}
path = [path stringByStandardizingPath];
// Determine if file exists.
// If path is actually a directory, check to see if it's a bundle and has a CFBundleExecutable.
BOOL directory;
if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&directory]) {
return nil;
} else if (directory) {
NSString *infoPath = [path stringByAppendingPathComponent:@"Contents/Info.plist"];
NSDictionary *d = [NSDictionary dictionaryWithContentsOfFile:infoPath];
if (d && d[@"CFBundleExecutable"]) {
path = [path stringByAppendingPathComponent:@"Contents/MacOS"];
_path = [path stringByAppendingPathComponent:d[@"CFBundleExecutable"]];
} else {
return nil;
_path = [self resolvePath:path];
if (_path.length == 0) {
if (error) {
NSString *errStr = @"Unable to resolve empty path";
if (path) errStr = [@"Unable to resolve path: " stringByAppendingString:path];
*error = [NSError errorWithDomain:@"com.google.santa.fileinfo"
code:260
userInfo:@{ NSLocalizedDescriptionKey: errStr }];
}
} else {
_path = path;
return nil;
}
_fileData = [NSData dataWithContentsOfFile:_path options:NSDataReadingMappedIfSafe error:nil];
if (!_fileData) return nil;
_fileData = [NSData dataWithContentsOfFile:_path
options:NSDataReadingUncached
error:error];
if (_fileData.length == 0) return nil;
[self parseMachHeaders];
}
return self;
}
- (instancetype)initWithPath:(NSString *)path {
return [self initWithPath:path error:NULL];
}
- (NSString *)SHA1 {
unsigned char sha1[CC_SHA1_DIGEST_LENGTH];
CC_SHA1([self.fileData bytes], (unsigned int)[self.fileData length], sha1);
CC_SHA1(self.fileData.bytes, (unsigned int)self.fileData.length, sha1);
// Convert the binary SHA into hex
NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
@@ -80,118 +97,100 @@
}
- (NSString *)SHA256 {
unsigned char sha2[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(self.fileData.bytes, (unsigned int)self.fileData.length, sha2);
unsigned char sha256[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(self.fileData.bytes, (unsigned int)self.fileData.length, sha256);
NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:CC_SHA256_DIGEST_LENGTH *2];
NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
[buf appendFormat:@"%02x", (unsigned char)sha2[i]];
[buf appendFormat:@"%02x", (unsigned char)sha256[i]];
}
return buf;
}
- (NSString *)machoType {
if ([self isDylib]) { return @"Dynamic Library"; }
if ([self isKext]) { return @"Kernel Extension"; }
if ([self isFat]) { return @"Fat Binary"; }
if ([self isMachO]) { return @"Thin Binary"; }
if ([self isScript]) { return @"Script"; }
if ([self isDylib]) return @"Dynamic Library";
if ([self isKext]) return @"Kernel Extension";
if ([self isFat]) return @"Fat Binary";
if ([self isMachO]) return @"Thin Binary";
if ([self isScript]) return @"Script";
return @"Unknown (not executable?)";
}
- (NSArray *)architectures {
if (![self isMachO]) return nil;
if ([self isFat]) {
NSMutableArray *ret = [[NSMutableArray alloc] init];
// Retrieve just the fat_header, if possible.
NSData *head = [self safeSubdataWithRange:NSMakeRange(0, sizeof(struct fat_header))];
if (!head) return nil;
struct fat_header *fat_header = (struct fat_header *)[head bytes];
// Get number of architectures in the binary
uint32_t narch = NSSwapBigIntToHost(fat_header->nfat_arch);
// Retrieve just the fat_arch's, make a mutable copy and if necessary swap the bytes
NSData *archs = [self safeSubdataWithRange:NSMakeRange(sizeof(struct fat_header),
sizeof(struct fat_arch) * narch)];
if (!archs) return nil;
struct fat_arch *fat_archs = (struct fat_arch *)[archs bytes];
// For each arch, get the name of it's architecture
for (int i = 0; i < narch; ++i) {
[ret addObject:[self nameForCPUType:NSSwapBigIntToHost(fat_archs[i].cputype)]];
}
return ret;
} else {
struct mach_header *hdr = [self firstMachHeader];
return @[ [self nameForCPUType:hdr->cputype] ];
}
return nil;
return [self.machHeaders allKeys];
}
- (BOOL)isDylib {
struct mach_header *mach_header = [self firstMachHeader];
if (!mach_header) return NO;
if (mach_header->filetype == MH_DYLIB ||
mach_header->filetype == MH_FVMLIB) {
return YES;
}
if (mach_header && mach_header->filetype == MH_DYLIB) return YES;
return NO;
}
- (BOOL)isKext {
struct mach_header *mach_header = [self firstMachHeader];
if (!mach_header) return NO;
if (mach_header->filetype == MH_KEXT_BUNDLE) {
return YES;
}
if (mach_header && mach_header->filetype == MH_KEXT_BUNDLE) return YES;
return NO;
}
- (BOOL)isMachO {
return ([self.fileData length] >= 160 &&
([self isMachHeader:(struct mach_header *)[self.fileData bytes]] || [self isFat]));
return ([self.machHeaders count] > 0);
}
- (BOOL)isFat {
return ([self isFatHeader:(struct fat_header *)[self.fileData bytes]]);
return ([self.machHeaders count] > 1);
}
- (BOOL)isScript {
if ([self.fileData length] < 1) return NO;
char magic[2];
[self.fileData getBytes:&magic length:2];
const char *magic = (const char *)[[self safeSubdataWithRange:NSMakeRange(0, 2)] bytes];
return (strncmp("#!", magic, 2) == 0);
}
- (BOOL)isExecutable {
struct mach_header *mach_header = [self firstMachHeader];
if (!mach_header) return NO;
if (mach_header->filetype == MH_OBJECT ||
mach_header->filetype == MH_EXECUTE ||
mach_header->filetype == MH_PRELOAD) {
return YES;
}
if (mach_header->filetype == MH_OBJECT || mach_header->filetype == MH_EXECUTE) return YES;
return NO;
}
# pragma mark Bundle Information
- (BOOL)isMissingPageZero {
// This method only checks i386 arch because the kernel enforces this for other archs
// See bsd/kern/mach_loader.c, search for enforce_hard_pagezero.
MachHeaderWithOffset *x86Header = self.machHeaders[@"i386"];
if (!x86Header) return NO;
struct mach_header *mh = (struct mach_header *)[x86Header.data bytes];
if (mh->filetype != MH_EXECUTE) return NO;
NSRange range = NSMakeRange(x86Header.offset + sizeof(struct mach_header),
sizeof(struct segment_command));
NSData *lcData = [self safeSubdataWithRange:range];
if (!lcData) return NO;
// This code assumes the __PAGEZERO is always the first load-command in the file.
// Given that the OS X ABI says "the static linker creates a __PAGEZERO segment
// as the first segment of an executable file." this should be OK.
struct load_command *lc = (struct load_command *)[lcData bytes];
if (lc->cmd == LC_SEGMENT) {
struct segment_command *segment = (struct segment_command *)lc;
if (segment->vmaddr == 0 && segment->vmsize != 0 &&
segment->initprot == 0 && segment->maxprot == 0 &&
strcmp("__PAGEZERO", segment->segname) == 0) {
return NO;
}
}
return YES;
}
#pragma mark Bundle Information
///
/// Try and determine the bundle that the represented executable is contained within, if any.
///
/// Rationale: An NSBundle has a method executablePath for discovering the main binary within a
/// bundle but provides no way to get an NSBundle object when only the executablePath is known. Also,
/// a bundle can contain multiple binaries within the MacOS folder and we want any of these to count
/// as being part of the bundle.
/// bundle but provides no way to get an NSBundle object when only the executablePath is known.
/// Also a bundle can contain multiple binaries within the MacOS folder and we want any of these
/// to count as being part of the bundle.
///
/// This method relies on executable bundles being laid out as follows:
///
@@ -206,21 +205,19 @@
/// NSBundle reference for Bundle.app.
///
- (NSBundle *)bundle {
if (self.bundleRef) return self.bundleRef;
if (!self.bundleRef) {
self.bundleRef = (NSBundle *)[NSNull null];
NSArray *pathComponents = [self.path pathComponents];
// Check that the full path is at least 4-levels deep:
// e.g: /Calendar.app/Contents/MacOS/Calendar
NSArray *pathComponents = [self.path pathComponents];
if ([pathComponents count] < 4) return nil;
// Check that the full path is at least 4-levels deep:
// e.g: /Calendar.app/Contents/MacOS/Calendar
if ([pathComponents count] < 4) return nil;
pathComponents = [pathComponents subarrayWithRange:NSMakeRange(0, [pathComponents count] - 3)];
self.bundleRef = [NSBundle bundleWithPath:[NSString pathWithComponents:pathComponents]];
// Clear the bundle if it doesn't have a bundle ID
if (![self.bundleRef objectForInfoDictionaryKey:@"CFBundleIdentifier"]) self.bundleRef = nil;
return self.bundleRef;
pathComponents = [pathComponents subarrayWithRange:NSMakeRange(0, [pathComponents count] - 3)];
NSBundle *bndl = [NSBundle bundleWithPath:[NSString pathWithComponents:pathComponents]];
if (bndl && [bndl objectForInfoDictionaryKey:@"CFBundleIdentifier"]) self.bundleRef = bndl;
}
return self.bundleRef == (NSBundle *)[NSNull null] ? nil : self.bundleRef;
}
- (NSString *)bundlePath {
@@ -228,101 +225,193 @@
}
- (NSDictionary *)infoPlist {
if (self.infoDict) return self.infoDict;
if (!self.infoDict) {
NSDictionary *d = [self embeddedPlist];
if (d) {
self.infoDict = d;
return self.infoDict;
}
if ([self bundle]) {
self.infoDict = [[self bundle] infoDictionary];
return self.infoDict;
d = self.bundle.infoDictionary;
if (d) {
self.infoDict = d;
return self.infoDict;
}
self.infoDict = (NSDictionary *)[NSNull null];
}
NSURL *url = [NSURL fileURLWithPath:self.path isDirectory:NO];
self.infoDict =
(__bridge_transfer NSDictionary*)CFBundleCopyInfoDictionaryForURL((__bridge CFURLRef) url);
return self.infoDict;
return self.infoDict == (NSDictionary *)[NSNull null] ? nil : self.infoDict;
}
- (NSString *)bundleIdentifier {
return [[self infoPlist] objectForKey:@"CFBundleIdentifier"];
return [self.infoPlist objectForKey:@"CFBundleIdentifier"];
}
- (NSString *)bundleName {
return [[self infoPlist] objectForKey:@"CFBundleName"];
return [self.infoPlist objectForKey:@"CFBundleName"];
}
- (NSString *)bundleVersion {
return [[self infoPlist] objectForKey:@"CFBundleVersion"];
return [self.infoPlist objectForKey:@"CFBundleVersion"];
}
- (NSString *)bundleShortVersionString {
return [[self infoPlist] objectForKey:@"CFBundleShortVersionString"];
return [self.infoPlist objectForKey:@"CFBundleShortVersionString"];
}
- (NSArray *)downloadURLs {
char *path = (char *)[self.path fileSystemRepresentation];
size_t size = getxattr(path, "com.apple.metadata:kMDItemWhereFroms", NULL, 0, 0, 0);
char *value = malloc(size);
if (!value) return nil;
- (NSString *)quarantineDataURL {
NSURL *url = [self quarantineData][(__bridge NSString *)kLSQuarantineDataURLKey];
return [url absoluteString];
}
if (getxattr(path, "com.apple.metadata:kMDItemWhereFroms", value, size, 0, 0) == -1) {
free(value);
return nil;
- (NSString *)quarantineRefererURL {
NSURL *url = [self quarantineData][(__bridge NSString *)kLSQuarantineOriginURLKey];
return [url absoluteString];
}
- (NSString *)quarantineAgentBundleID {
return [self quarantineData][(__bridge NSString *)kLSQuarantineAgentBundleIdentifierKey];
}
- (NSDate *)quarantineTimestamp {
return [self quarantineData][(__bridge NSString *)kLSQuarantineTimeStampKey];
}
- (NSUInteger)fileSize {
return self.fileData.length;
}
#pragma mark Internal Methods
- (void)parseMachHeaders {
if (self.machHeaders) return;
// Sanity check file length
if (self.fileData.length < sizeof(struct mach_header)) {
self.machHeaders = [NSDictionary dictionary];
return;
}
NSData *data = [NSData dataWithBytes:value length:size];
free(value);
NSMutableDictionary *machHeaders = [NSMutableDictionary dictionary];
if (data) {
NSArray *urls = [NSPropertyListSerialization propertyListWithData:data
options:NSPropertyListImmutable
format:NULL
error:NULL];
return urls;
NSData *machHeader = [self parseSingleMachHeader:self.fileData];
if (machHeader) {
struct mach_header *mh = (struct mach_header *)[machHeader bytes];
MachHeaderWithOffset *mhwo = [[MachHeaderWithOffset alloc] initWithData:machHeader offset:0];
machHeaders[[self nameForCPUType:mh->cputype]] = mhwo;
} else {
NSRange range = NSMakeRange(0, sizeof(struct fat_header));
NSData *fatHeader = [self safeSubdataWithRange:range];
struct fat_header *fh = (struct fat_header *)[fatHeader bytes];
if (fatHeader && (fh->magic == FAT_MAGIC || fh->magic == FAT_CIGAM)) {
int nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
range = NSMakeRange(sizeof(struct fat_header), sizeof(struct fat_arch) * nfat_arch);
NSMutableData *fatArchs = [[self safeSubdataWithRange:range] mutableCopy];
if (fatArchs) {
struct fat_arch *fat_arch = (struct fat_arch *)[fatArchs mutableBytes];
for (int i = 0; i < nfat_arch; i++) {
int offset = OSSwapBigToHostInt32(fat_arch[i].offset);
int size = OSSwapBigToHostInt32(fat_arch[i].size);
int cputype = OSSwapBigToHostInt(fat_arch[i].cputype);
range = NSMakeRange(offset, size);
NSData *machHeader = [self parseSingleMachHeader:[self safeSubdataWithRange:range]];
if (machHeader) {
NSString *key = [self nameForCPUType:cputype];
MachHeaderWithOffset *mhwo = [[MachHeaderWithOffset alloc] initWithData:machHeader
offset:offset];
machHeaders[key] = mhwo;
}
}
}
}
}
self.machHeaders = [machHeaders copy];
}
- (NSData *)parseSingleMachHeader:(NSData *)inputData {
if (inputData.length < sizeof(struct mach_header)) return nil;
struct mach_header *mh = (struct mach_header *)[inputData bytes];
if (mh->magic == MH_CIGAM || mh->magic == MH_CIGAM_64) {
NSMutableData *mutableInput = [inputData mutableCopy];
mh = (struct mach_header *)[mutableInput mutableBytes];
swap_mach_header(mh, NXHostByteOrder());
}
if (mh->magic == MH_MAGIC || mh->magic == MH_MAGIC_64) {
return [NSData dataWithBytes:mh length:sizeof(struct mach_header)];
}
return nil;
}
# pragma mark Internal Methods
///
/// Locate an embedded plist in the file
///
- (NSDictionary *)embeddedPlist {
// Look for an embedded Info.plist if there is one.
// This could (and used to) use CFBundleCopyInfoDictionaryForURL but that uses mmap to read
// the file and so can cause SIGBUS if the file is deleted/truncated while it's working.
MachHeaderWithOffset *mhwo = [[self.machHeaders allValues] firstObject];
if (!mhwo) return nil;
struct mach_header *mh = (struct mach_header *)mhwo.data.bytes;
if (mh->filetype != MH_EXECUTE) return self.infoDict;
BOOL is64 = (mh->magic == MH_MAGIC_64 || mh->magic == MH_CIGAM_64);
uint32_t ncmds = mh->ncmds;
uint32_t nsects = 0;
uint64_t offset = mhwo.offset;
uint32_t sz_header = is64 ? sizeof(struct mach_header_64) : sizeof(struct mach_header);
uint32_t sz_segment = is64 ? sizeof(struct segment_command_64) : sizeof(struct segment_command);
uint32_t sz_section = is64 ? sizeof(struct section_64) : sizeof(struct section);
offset += sz_header;
// Loop through the load commands looking for the segment named __TEXT
for (uint32_t i = 0; i < ncmds; i++) {
NSData *cmdData = [self safeSubdataWithRange:NSMakeRange(offset, sz_segment)];
if (!cmdData) return nil;
struct segment_command_64 *lc = (struct segment_command_64 *)[cmdData bytes];
if (lc->cmd == LC_SEGMENT || lc->cmd == LC_SEGMENT_64) {
if (strncmp(lc->segname, "__TEXT", 6) == 0) {
nsects = lc->nsects;
offset += sz_segment;
break;
}
}
offset += lc->cmdsize;
}
// Loop through the sections in the __TEXT segment looking for an __info_plist section.
for (uint32_t i = 0; i < nsects; i++) {
NSData *sectData = [self safeSubdataWithRange:NSMakeRange(offset, sz_section)];
if (!sectData) return nil;
struct section_64 *sect = (struct section_64 *)[sectData bytes];
if (strncmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
NSData *plistData = [self safeSubdataWithRange:NSMakeRange(sect->offset, sect->size)];
if (!plistData) return nil;
NSDictionary *plist;
plist = [NSPropertyListSerialization propertyListWithData:plistData
options:NSPropertyListImmutable
format:NULL
error:NULL];
if (plist) return plist;
}
offset += sz_section;
}
return nil;
}
///
/// Look through the file for the first mach_header. If the file is thin, this will be the
/// header at the beginning of the file. If the file is fat, it will be the first
/// architecture-specific header.
/// Return one of the mach_header's in this file.
///
- (struct mach_header *)firstMachHeader {
if (![self isMachO]) return NULL;
return (struct mach_header *)([[[[self.machHeaders allValues] firstObject] data] bytes]);
struct mach_header *mach_header = (struct mach_header *)[self.fileData bytes];
struct fat_header *fat_header = (struct fat_header *)[self.fileData bytes];
if ([self isFatHeader:fat_header]) {
// Get the bytes for the fat_arch
NSData *archHdr = [self safeSubdataWithRange:NSMakeRange(sizeof(struct fat_header),
sizeof(struct fat_arch))];
if (!archHdr) return nil;
struct fat_arch *fat_arch = (struct fat_arch *)[archHdr bytes];
// Get bytes for first mach_header
NSData *machHdr = [self safeSubdataWithRange:NSMakeRange(NSSwapBigIntToHost(fat_arch->offset),
sizeof(struct mach_header))];
if (!machHdr) return nil;
mach_header = (struct mach_header *)[machHdr bytes];
}
if ([self isMachHeader:mach_header]) {
return mach_header;
}
return NULL;
}
- (BOOL)isMachHeader:(struct mach_header *)header {
return (header->magic == MH_MAGIC || header->magic == MH_MAGIC_64 ||
header->magic == MH_CIGAM || header->magic == MH_CIGAM_64);
}
- (BOOL)isFatHeader:(struct fat_header *)header {
return (header->magic == FAT_MAGIC || header->magic == FAT_CIGAM);
}
///
@@ -333,11 +422,27 @@
@try {
return [self.fileData subdataWithRange:range];
}
@catch (NSException *exception) {
@catch (NSException *e) {
return nil;
}
}
///
/// Retrieve quarantine data for a file
///
- (NSDictionary *)quarantineData {
if (!self.quarantineDict && NSURLQuarantinePropertiesKey != NULL) {
NSURL *url = [NSURL fileURLWithPath:self.path];
NSDictionary *d = [url resourceValuesForKeys:@[ NSURLQuarantinePropertiesKey ] error:NULL];
self.quarantineDict = d[NSURLQuarantinePropertiesKey];
if (!self.quarantineDict) self.quarantineDict = (NSDictionary *)[NSNull null];
}
return (self.quarantineDict == (NSDictionary *)[NSNull null]) ? nil : self.quarantineDict;
}
///
/// Return a human-readable string for a cpu_type_t.
///
- (NSString *)nameForCPUType:(cpu_type_t)cpuType {
switch (cpuType) {
case CPU_TYPE_X86:
@@ -354,4 +459,32 @@
return nil;
}
- (NSString *)resolvePath:(NSString *)path {
// Convert to absolute, standardized path
path = [path stringByResolvingSymlinksInPath];
if (![path isAbsolutePath]) {
NSString *cwd = [[NSFileManager defaultManager] currentDirectoryPath];
path = [cwd stringByAppendingPathComponent:path];
}
path = [path stringByStandardizingPath];
// Determine if file exists.
// If path is actually a directory, check to see if it's a bundle and has a CFBundleExecutable.
BOOL directory;
if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&directory]) {
return nil;
} else if (directory) {
NSString *infoPath = [path stringByAppendingPathComponent:@"Contents/Info.plist"];
NSDictionary *d = [NSDictionary dictionaryWithContentsOfFile:infoPath];
if (d && d[@"CFBundleExecutable"]) {
path = [path stringByAppendingPathComponent:@"Contents/MacOS"];
return [path stringByAppendingPathComponent:d[@"CFBundleExecutable"]];
} else {
return nil;
}
} else {
return path;
}
}
@end

View File

@@ -48,7 +48,7 @@
}
- (void)beginWatchingFile {
__weak typeof(self) weakSelf = self;
__weak __typeof(self) weakSelf = self;
int mask = (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE |
DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_RENAME);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
@@ -70,12 +70,13 @@
close(fd);
}
while ((fd = open([weakSelf.filePath fileSystemRepresentation], O_EVTONLY)) < 0) {
const char *filePathCString = [weakSelf.filePath fileSystemRepresentation];
while ((fd = open(filePathCString, O_EVTONLY)) < 0) {
usleep(1000);
}
weakSelf.monitoringSource = dispatch_source_create(
DISPATCH_SOURCE_TYPE_VNODE, fd, mask, queue);
weakSelf.monitoringSource =
dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, mask, queue);
dispatch_source_set_event_handler(weakSelf.monitoringSource, weakSelf.internalEventHandler);
dispatch_source_set_cancel_handler(weakSelf.monitoringSource, weakSelf.internalCancelHandler);
dispatch_resume(weakSelf.monitoringSource);
@@ -91,9 +92,7 @@
int fd = (int)dispatch_source_get_handle(self.monitoringSource);
dispatch_source_set_event_handler_f(self.monitoringSource, NULL);
dispatch_source_set_cancel_handler(self.monitoringSource, ^{
close(fd);
});
dispatch_source_set_cancel_handler(self.monitoringSource, ^{ close(fd); });
dispatch_source_cancel(self.monitoringSource);
self.monitoringSource = nil;

View File

@@ -16,11 +16,12 @@
/// Common defines between kernel <-> userspace
///
#include <sys/param.h>
#ifndef SANTA__COMMON__KERNELCOMMON_H
#define SANTA__COMMON__KERNELCOMMON_H
// Defines the lengths of paths and Vnode IDs passed around.
#define MAX_PATH_LEN 1024 // ==PATH_LEN from syslimits.h
#define MAX_VNODE_ID_STR 21 // digits in UINT64_MAX + 1 for NULL-terminator
// Defines the name of the userclient class and the driver bundle ID.
@@ -50,24 +51,39 @@ typedef enum {
ACTION_RESPOND_CHECKBW_ALLOW = 11,
ACTION_RESPOND_CHECKBW_DENY = 12,
// NOTIFY
ACTION_NOTIFY_EXEC = 20,
ACTION_NOTIFY_WRITE = 21,
ACTION_NOTIFY_RENAME = 22,
ACTION_NOTIFY_LINK = 23,
ACTION_NOTIFY_EXCHANGE = 24,
ACTION_NOTIFY_DELETE = 25,
// SHUTDOWN
ACTION_REQUEST_SHUTDOWN = 60,
ACTION_REQUEST_SHUTDOWN = 90,
// ERROR
ACTION_ERROR = 99,
} santa_action_t;
#define CHECKBW_RESPONSE_VALID(x) (x == ACTION_RESPOND_CHECKBW_ALLOW || \
x == ACTION_RESPOND_CHECKBW_DENY)
#define CHECKBW_RESPONSE_VALID(x) \
(x == ACTION_RESPOND_CHECKBW_ALLOW || x == ACTION_RESPOND_CHECKBW_DENY)
// Message struct that is sent down the IODataQueue.
typedef struct {
santa_action_t action;
uint64_t vnode_id;
uid_t userId;
uid_t uid;
gid_t gid;
pid_t pid;
pid_t ppid;
char path[MAX_PATH_LEN];
char path[MAXPATHLEN];
char newpath[MAXPATHLEN];
// For file events, this is the process name.
// For exec requests, this is the parent process name.
// While process names can technically be 4*MAXPATHLEN, that never
// actually happens, so only take MAXPATHLEN and throw away any excess.
char pname[MAXPATHLEN];
} santa_message_t;
#endif // SANTA__COMMON__KERNELCOMMON_H

View File

@@ -32,26 +32,29 @@
#else // KERNEL
#define LOG_LEVEL_ERROR 1
#define LOG_LEVEL_WARN 2
#define LOG_LEVEL_INFO 3
#define LOG_LEVEL_DEBUG 4
typedef enum : NSUInteger {
LOG_LEVEL_ERROR,
LOG_LEVEL_WARN,
LOG_LEVEL_INFO,
LOG_LEVEL_DEBUG
} LogLevel;
///
/// Logging function.
///
/// @param level one of the levels defined above
/// @param destination a FILE, generally should be stdout or stderr
/// @param destination a FILE, generally stdout/stderr. If the file is closed, the log
/// will instead be sent to syslog.
/// @param format the printf style format string
/// @param ... the arguments to format.
///
void logMessage(int level, FILE *destination, NSString *format, ...);
void logMessage(LogLevel level, FILE *destination, NSString *format, ...)
__attribute__((format(__NSString__, 3, 4)));
/// Simple logging macros
#define LOGD(logFormat, ...) logMessage(LOG_LEVEL_DEBUG, stdout, logFormat, ##__VA_ARGS__);
#define LOGI(logFormat, ...) logMessage(LOG_LEVEL_INFO, stdout, logFormat, ##__VA_ARGS__);
#define LOGW(logFormat, ...) logMessage(LOG_LEVEL_WARN, stderr, logFormat, ##__VA_ARGS__);
#define LOGE(logFormat, ...) logMessage(LOG_LEVEL_ERROR, stderr, logFormat, ##__VA_ARGS__);
#define LOGD(logFormat, ...) logMessage(LOG_LEVEL_DEBUG, stdout, logFormat, ##__VA_ARGS__)
#define LOGI(logFormat, ...) logMessage(LOG_LEVEL_INFO, stdout, logFormat, ##__VA_ARGS__)
#define LOGW(logFormat, ...) logMessage(LOG_LEVEL_WARN, stderr, logFormat, ##__VA_ARGS__)
#define LOGE(logFormat, ...) logMessage(LOG_LEVEL_ERROR, stderr, logFormat, ##__VA_ARGS__)
#endif // KERNEL

View File

@@ -14,28 +14,32 @@
#import "SNTLogging.h"
#import <asl.h>
#ifdef DEBUG
static int logLevel = LOG_LEVEL_DEBUG; // default to info
static LogLevel logLevel = LOG_LEVEL_DEBUG;
#else
static int logLevel = LOG_LEVEL_INFO;
static LogLevel logLevel = LOG_LEVEL_INFO; // default to info
#endif
void logMessage(int level, FILE *destination, NSString *format, ...) {
static NSDateFormatter *dateFormatter;
static NSString *binaryName;
void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
static BOOL useSyslog = NO;
static const char *binaryName;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
[dateFormatter setDateFormat:@"YYYY-MM-dd HH:mm:ss.SSS'Z"];
binaryName = [[NSProcessInfo processInfo] processName];
binaryName = [[[NSProcessInfo processInfo] processName] UTF8String];
// If debug logging is enabled, the process must be restarted.
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--debug"]) {
logLevel = LOG_LEVEL_DEBUG;
}
// If requested, redirect output to syslog.
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--syslog"] ||
strcmp(binaryName, "santad") == 0) {
useSyslog = YES;
}
});
if (logLevel < level) return;
@@ -45,19 +49,22 @@ void logMessage(int level, FILE *destination, NSString *format, ...) {
NSString *s = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
// Only prepend timestamp, severity and binary name if stdout is not a TTY
if (isatty(fileno(destination))) {
fprintf(destination, "%s\n", [s UTF8String]);
} else {
NSString *levelName;
if (useSyslog) {
aslclient client = asl_open(NULL, "com.google.santa", 0);
asl_set_filter(client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
char *levelName;
int syslogLevel = ASL_LEVEL_DEBUG;
switch (level) {
case LOG_LEVEL_ERROR: levelName = @"E"; break;
case LOG_LEVEL_WARN: levelName = @"W"; break;
case LOG_LEVEL_INFO: levelName = @"I"; break;
case LOG_LEVEL_DEBUG: levelName = @"D"; break;
case LOG_LEVEL_ERROR: levelName = "E"; syslogLevel = ASL_LEVEL_ERR; break;
case LOG_LEVEL_WARN: levelName = "W"; syslogLevel = ASL_LEVEL_WARNING; break;
case LOG_LEVEL_INFO: levelName = "I"; syslogLevel = ASL_LEVEL_INFO; break;
case LOG_LEVEL_DEBUG: levelName = "D"; syslogLevel = ASL_LEVEL_DEBUG; break;
}
fprintf(destination, "%s\n", [[NSString stringWithFormat:@"[%@] %@ %@: %@",
[dateFormatter stringFromDate:[NSDate date]], levelName, binaryName, s] UTF8String]);
asl_log(client, NULL, syslogLevel, "%s %s: %s", levelName, binaryName, [s UTF8String]);
asl_close(client);
} else {
fprintf(destination, "%s\n", [s UTF8String]);
}
}

View File

@@ -22,7 +22,7 @@
///
/// The hash of the object this rule is for
///
@property NSString *shasum;
@property(copy) NSString *shasum;
///
/// The state of this rule
@@ -37,7 +37,7 @@
///
/// A custom message that will be displayed if this rule blocks a binary from executing
///
@property NSString *customMsg;
@property(copy) NSString *customMsg;
///
/// Designated initializer.

View File

@@ -22,7 +22,7 @@
customMsg:(NSString *)customMsg {
self = [super init];
if (self) {
_shasum = shasum;
_shasum = shasum;
_state = state;
_type = type;
_customMsg = customMsg;
@@ -34,11 +34,10 @@
#define ENCODE(obj, key) if (obj) [coder encodeObject:obj forKey:key]
#define DECODE(cls, key) [decoder decodeObjectOfClass:[cls class] forKey:key]
#define DECODEARRAY(cls, key) \
[decoder decodeObjectOfClasses:[NSSet setWithObjects:[NSArray class], [cls class], nil] \
forKey:key]
+ (BOOL)supportsSecureCoding { return YES; }
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(NSCoder *)coder {
ENCODE(self.shasum, @"shasum");
@@ -58,4 +57,28 @@
return self;
}
#undef DECODE
#undef ENCODE
- (BOOL)isEqual:(id)other {
if (other == self) return YES;
if (![other isKindOfClass:[SNTRule class]]) return NO;
SNTRule *o = other;
return ([self.shasum isEqual:o.shasum] && self.state == o.state && self.type == o.type);
}
- (NSUInteger)hash {
NSUInteger prime = 31;
NSUInteger result = 1;
result = prime * result + [self.shasum hash];
result = prime * result + self.state;
result = prime * result + self.type;
return result;
}
- (NSString *)description {
return [NSString stringWithFormat:@"SNTRule: SHA-256: %@, State: %d, Type: %d",
self.shasum, self.state, self.type];
}
@end

View File

@@ -55,7 +55,7 @@
@property NSString *fileBundleVersionString;
///
/// If the executed file was signed, this is an NSArray of SNTCertificate's
/// If the executed file was signed, this is an NSArray of MOLCertificate's
/// representing the signing chain.
///
@property NSArray *signingChain;
@@ -95,4 +95,18 @@
///
@property NSNumber *ppid;
///
/// The name of the parent process.
///
@property NSString *parentName;
///
/// Quarantine data about the executed file, if any.
///
@property NSString *quarantineDataURL;
@property NSString *quarantineRefererURL;
@property NSDate *quarantineTimestamp;
@property NSString *quarantineAgentBundleID;
@end

View File

@@ -14,7 +14,7 @@
#import "SNTStoredEvent.h"
#import "SNTCertificate.h"
#import "MOLCertificate.h"
@implementation SNTStoredEvent
@@ -24,7 +24,9 @@
[decoder decodeObjectOfClasses:[NSSet setWithObjects:[NSArray class], [cls class], nil] \
forKey:key]
+ (BOOL)supportsSecureCoding { return YES; }
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(NSCoder *)coder {
ENCODE(self.idx, @"idx");
@@ -43,9 +45,15 @@
ENCODE(@(self.decision), @"decision");
ENCODE(self.pid, @"pid");
ENCODE(self.ppid, @"ppid");
ENCODE(self.parentName, @"parentName");
ENCODE(self.loggedInUsers, @"loggedInUsers");
ENCODE(self.currentSessions, @"currentSessions");
ENCODE(self.quarantineDataURL, @"quarantineDataURL");
ENCODE(self.quarantineRefererURL, @"quarantineRefererURL");
ENCODE(self.quarantineTimestamp, @"quarantineTiemstamp");
ENCODE(self.quarantineAgentBundleID, @"quarantineAgentBundleID");
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
@@ -60,25 +68,31 @@
_fileBundleVersion = DECODE(NSString, @"fileBundleVersion");
_fileBundleVersionString = DECODE(NSString, @"fileBundleVersionString");
_signingChain = DECODEARRAY(SNTCertificate, @"signingChain");
_signingChain = DECODEARRAY(MOLCertificate, @"signingChain");
_executingUser = DECODE(NSString, @"executingUser");
_occurrenceDate = DECODE(NSDate, @"occurrenceDate");
_decision = [DECODE(NSNumber, @"decision") intValue];
_decision = (santa_eventstate_t)[DECODE(NSNumber, @"decision") intValue];
_pid = DECODE(NSNumber, @"pid");
_ppid = DECODE(NSNumber, @"ppid");
_parentName = DECODE(NSString, @"parentName");
_loggedInUsers = DECODEARRAY(NSString, @"loggedInUsers");
_currentSessions = DECODEARRAY(NSString, @"currentSessions");
_quarantineDataURL = DECODE(NSString, @"quarantineDataURL");
_quarantineRefererURL = DECODE(NSString, @"quarantineRefererURL");
_quarantineTimestamp = DECODE(NSDate, @"quarantineTimestamp");
_quarantineAgentBundleID = DECODE(NSString, @"quarantineAgentBundleID");
}
return self;
}
- (BOOL)isEqual:(SNTStoredEvent *)other {
- (BOOL)isEqual:(id)other {
if (other == self) return YES;
if (![other isKindOfClass:[SNTStoredEvent class]]) return NO;
return ([self.fileSHA256 isEqual:other.fileSHA256] &&
[self.idx isEqual:other.idx]);
SNTStoredEvent *o = other;
return ([self.fileSHA256 isEqual:o.fileSHA256] && [self.idx isEqual:o.idx]);
}
- (NSUInteger)hash {
@@ -91,8 +105,8 @@
}
- (NSString *)description {
return [NSString stringWithFormat:@"SNTStoredEvent[%@] with SHA-256: %@",
self.idx, self.fileSHA256];
return
[NSString stringWithFormat:@"SNTStoredEvent[%@] with SHA-256: %@", self.idx, self.fileSHA256];
}
@end

View File

@@ -21,11 +21,8 @@
kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
if (!platformExpert) return nil;
NSString *serial = CFBridgingRelease(
IORegistryEntryCreateCFProperty(platformExpert,
CFSTR(kIOPlatformSerialNumberKey),
kCFAllocatorDefault,
0));
NSString *serial = CFBridgingRelease(IORegistryEntryCreateCFProperty(
platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0));
IOObjectRelease(platformExpert);
@@ -37,10 +34,8 @@
kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
if (!platformExpert) return nil;
NSString *uuid = CFBridgingRelease(
IORegistryEntryCreateCFProperty(platformExpert,
CFSTR(kIOPlatformUUIDKey),
kCFAllocatorDefault, 0));
NSString *uuid = CFBridgingRelease(IORegistryEntryCreateCFProperty(
platformExpert, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0));
IOObjectRelease(platformExpert);
@@ -65,11 +60,11 @@
return @(hostname);
}
# pragma mark - Internal
#pragma mark - Internal
+ (NSDictionary *)_systemVersionDictionary {
return [NSDictionary dictionaryWithContentsOfFile:
@"/System/Library/CoreServices/SystemVersion.plist"];
return [NSDictionary
dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"];
}
@end

View File

@@ -63,7 +63,7 @@ typedef void (^SNTXPCRejectedBlock)(void);
/// @warning Do not send a message to this object if you didn't set @c remoteInterface above
/// before calling the @c resume method. Doing so will throw an exception.
///
@property(readonly) id remoteObjectProxy;
@property(readonly, nonatomic) id remoteObjectProxy;
///
/// The interface this object exports.

View File

@@ -14,7 +14,7 @@
#import "SNTXPCConnection.h"
#import "SNTCodesignChecker.h"
#import "MOLCodesignChecker.h"
@protocol XPCConnectionValidityRequest
- (void)isConnectionValidWithBlock:(void (^)(BOOL))block;
@@ -59,8 +59,7 @@
if (self) {
Protocol *validatorProtocol = @protocol(XPCConnectionValidityRequest);
_validatorInterface = [NSXPCInterface interfaceWithProtocol:validatorProtocol];
_currentConnection = [[NSXPCConnection alloc] initWithMachServiceName:name
options:options];
_currentConnection = [[NSXPCConnection alloc] initWithMachServiceName:name options:options];
if (!_validatorInterface || !_currentConnection) return nil;
}
@@ -75,33 +74,31 @@
#pragma mark Connection set-up
- (void)resume {
if (_listenerObject) {
if (self.listenerObject) {
// A new listener doesn't do anything until a client connects.
self.listenerObject.delegate = self;
[self.listenerObject resume];
} else {
// A new client begins the validation process.
NSXPCConnection *connection = _currentConnection;
NSXPCConnection *connection = self.currentConnection;
connection.remoteObjectInterface = _validatorInterface;
connection.remoteObjectInterface = self.validatorInterface;
connection.invalidationHandler = ^{
[self invokeInvalidationHandler];
self.currentConnection = nil;
};
connection.interruptionHandler = ^{
[self.currentConnection invalidate];
};
connection.interruptionHandler = ^{ [self.currentConnection invalidate]; };
[connection resume];
__block BOOL verificationComplete = NO;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[[connection remoteObjectProxy] isConnectionValidWithBlock:^void(BOOL response) {
pid_t pid = self.currentConnection.processIdentifier;
SNTCodesignChecker *selfCS = [[SNTCodesignChecker alloc] initWithSelf];
SNTCodesignChecker *otherCS = [[SNTCodesignChecker alloc] initWithPID:pid];
MOLCodesignChecker *selfCS = [[MOLCodesignChecker alloc] initWithSelf];
MOLCodesignChecker *otherCS = [[MOLCodesignChecker alloc] initWithPID:pid];
if (response && [otherCS signingInformationMatches:selfCS]) {
[self.currentConnection suspend];
@@ -110,20 +107,19 @@
self.currentConnection.exportedObject = self.exportedObject;
[self invokeAcceptedHandler];
[self.currentConnection resume];
verificationComplete = YES;
dispatch_semaphore_signal(sema);
} else {
[self invokeRejectedHandler];
[self.currentConnection invalidate];
self.currentConnection = nil;
verificationComplete = YES;
dispatch_semaphore_signal(sema);
}
}];
// Wait for validation to complete, at most 5s
for (int sleepLoops = 0; sleepLoops < 1000 && !verificationComplete; sleepLoops++) {
usleep(5000);
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
[self invalidate];
}
if (!verificationComplete) [self invalidate];
}
}
@@ -133,7 +129,7 @@
if (self.currentConnection) return NO;
connection.exportedObject = self;
connection.exportedInterface = _validatorInterface;
connection.exportedInterface = self.validatorInterface;
connection.invalidationHandler = ^{
[self invokeInvalidationHandler];
@@ -157,8 +153,8 @@
- (void)isConnectionValidWithBlock:(void (^)(BOOL))block {
pid_t pid = self.currentConnection.processIdentifier;
SNTCodesignChecker *selfCS = [[SNTCodesignChecker alloc] initWithSelf];
SNTCodesignChecker *otherCS = [[SNTCodesignChecker alloc] initWithPID:pid];
MOLCodesignChecker *selfCS = [[MOLCodesignChecker alloc] initWithSelf];
MOLCodesignChecker *otherCS = [[MOLCodesignChecker alloc] initWithPID:pid];
if ([otherCS signingInformationMatches:selfCS]) {
[self.currentConnection suspend];

View File

@@ -25,26 +25,35 @@
///
/// Kernel ops
///
- (void)cacheCount:(void (^)(uint64_t))reply;
- (void)cacheCount:(void (^)(int64_t))reply;
- (void)flushCache:(void (^)(BOOL))reply;
///
/// Database ops
///
- (void)databaseRuleCounts:(void (^)(uint64_t binary, uint64_t certificate))reply;
- (void)databaseRuleAddRule:(SNTRule *)rule cleanSlate:(BOOL)cleanSlate reply:(void (^)())reply;
- (void)databaseRuleAddRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate reply:(void (^)())reply;
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate))reply;
- (void)databaseRuleAddRule:(SNTRule *)rule cleanSlate:(BOOL)cleanSlate
reply:(void (^)(BOOL success))reply;
- (void)databaseRuleAddRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate
reply:(void (^)(BOOL success))reply;
- (void)databaseEventCount:(void (^)(uint64_t count))reply;
- (void)databaseEventCount:(void (^)(int64_t count))reply;
- (void)databaseEventForSHA256:(NSString *)sha256 reply:(void (^)(SNTStoredEvent *))reply;
- (void)databaseEventsPending:(void (^)(NSArray *events))reply;
- (void)databaseRemoveEventsWithIDs:(NSArray *)ids;
///
/// Misc ops
/// Config ops
///
- (void)clientMode:(void (^)(santa_clientmode_t))reply;
- (void)watchdogCPUEvents:(void (^)(uint64_t))reply;
- (void)watchdogRAMEvents:(void (^)(uint64_t))reply;
- (void)setClientMode:(santa_clientmode_t)mode reply:(void (^)())reply;
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply;
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply;
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)())reply;
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)())reply;
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)())reply;
@end

View File

@@ -20,36 +20,54 @@ OSDefineMetaClassAndStructors(SantaDecisionManager, OSObject);
#pragma mark Object Lifecycle
bool SantaDecisionManager::init() {
sdm_lock_grp_ = lck_grp_alloc_init("santa-locks", lck_grp_attr_alloc_init());
dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, lck_attr_alloc_init());
cached_decisions_lock_ = lck_rw_alloc_init(sdm_lock_grp_, lck_attr_alloc_init());
if (!super::init()) return false;
sdm_lock_grp_attr_ = lck_grp_attr_alloc_init();
sdm_lock_grp_ = lck_grp_alloc_init("santa-locks", sdm_lock_grp_attr_);
sdm_lock_attr_ = lck_attr_alloc_init();
dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
cached_decisions_lock_ = lck_rw_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
cached_decisions_ = OSDictionary::withCapacity(1000);
owning_pid_ = 0;
dataqueue_ = IOSharedDataQueue::withEntries(kMaxQueueEvents,
sizeof(santa_message_t));
if (!dataqueue_) return kIOReturnNoMemory;
return kIOReturnSuccess;
client_pid_ = 0;
return true;
}
void SantaDecisionManager::free() {
if (cached_decisions_) {
cached_decisions_->release();
cached_decisions_ = NULL;
}
OSSafeReleaseNULL(dataqueue_);
OSSafeReleaseNULL(cached_decisions_);
if (cached_decisions_lock_) {
lck_rw_free(cached_decisions_lock_, sdm_lock_grp_);
cached_decisions_lock_ = NULL;
cached_decisions_lock_ = nullptr;
}
if (dataqueue_lock_ ) {
if (dataqueue_lock_) {
lck_mtx_free(dataqueue_lock_, sdm_lock_grp_);
dataqueue_lock_ = NULL;
dataqueue_lock_ = nullptr;
}
if (sdm_lock_attr_) {
lck_attr_free(sdm_lock_attr_);
sdm_lock_attr_ = nullptr;
}
if (sdm_lock_grp_) {
lck_grp_free(sdm_lock_grp_);
sdm_lock_grp_ = NULL;
sdm_lock_grp_ = nullptr;
}
if (sdm_lock_grp_attr_) {
lck_grp_attr_free(sdm_lock_grp_attr_);
sdm_lock_grp_attr_ = nullptr;
}
super::free();
@@ -57,52 +75,57 @@ void SantaDecisionManager::free() {
#pragma mark Client Management
void SantaDecisionManager::ConnectClient(IOSharedDataQueue *queue, pid_t pid) {
void SantaDecisionManager::ConnectClient(mach_port_t port, pid_t pid) {
if (!pid) return;
if (!queue) return;
// Any decisions made while the daemon wasn't
// connected should be cleared
ClearCache();
lck_mtx_lock(dataqueue_lock_);
dataqueue_ = queue;
dataqueue_->retain();
dataqueue_->setNotificationPort(port);
lck_mtx_unlock(dataqueue_lock_);
owning_pid_ = pid;
owning_proc_ = proc_find(pid);
client_pid_ = pid;
failed_queue_requests_ = 0;
}
void SantaDecisionManager::DisconnectClient() {
if (owning_pid_ < 1) return;
owning_pid_ = -1;
void SantaDecisionManager::DisconnectClient(bool itDied) {
if (client_pid_ < 1) return;
client_pid_ = 0;
// Ask santad to shutdown, in case it's running.
santa_message_t message;
message.action = ACTION_REQUEST_SHUTDOWN;
message.userId = 0;
message.pid = 0;
message.ppid = 0;
message.vnode_id = 0;
PostToQueue(message);
if (!itDied) {
santa_message_t *message = new santa_message_t;
message->action = ACTION_REQUEST_SHUTDOWN;
PostToQueue(message);
delete message;
dataqueue_->setNotificationPort(nullptr);
} else {
// If the client died, reset the data queue so when it reconnects
// it doesn't get swamped straight away.
lck_mtx_lock(dataqueue_lock_);
dataqueue_->release();
dataqueue_ = IOSharedDataQueue::withEntries(kMaxQueueEvents,
sizeof(santa_message_t));
lck_mtx_unlock(dataqueue_lock_);
}
lck_mtx_lock(dataqueue_lock_);
dataqueue_->release();
dataqueue_ = NULL;
lck_mtx_unlock(dataqueue_lock_);
proc_rele(owning_proc_);
owning_proc_ = NULL;
}
bool SantaDecisionManager::ClientConnected() {
return owning_pid_ > 0;
proc_t p = proc_find(client_pid_);
bool is_exiting = proc_exiting(p);
proc_rele(p);
return (client_pid_ > 0 && !is_exiting);
}
# pragma mark Listener Control
IOMemoryDescriptor *SantaDecisionManager::GetMemoryDescriptor() {
return dataqueue_->getMemoryDescriptor();
}
#pragma mark Listener Control
kern_return_t SantaDecisionManager::StartListener() {
vnode_listener_ = kauth_listen_scope(KAUTH_SCOPE_VNODE,
@@ -110,14 +133,22 @@ kern_return_t SantaDecisionManager::StartListener() {
reinterpret_cast<void *>(this));
if (!vnode_listener_) return kIOReturnInternalError;
LOGD("Vnode listener started.");
fileop_listener_ = kauth_listen_scope(KAUTH_SCOPE_FILEOP,
fileop_scope_callback,
reinterpret_cast<void *>(this));
if (!fileop_listener_) return kIOReturnInternalError;
LOGD("Listeners started.");
return kIOReturnSuccess;
}
kern_return_t SantaDecisionManager::StopListener() {
kauth_unlisten_scope(vnode_listener_);
vnode_listener_ = NULL;
vnode_listener_ = nullptr;
kauth_unlisten_scope(fileop_listener_);
fileop_listener_ = nullptr;
// Wait for any active invocations to finish before returning
do {
@@ -127,7 +158,7 @@ kern_return_t SantaDecisionManager::StopListener() {
// Delete any cached decisions
ClearCache();
LOGD("Vnode listener stopped.");
LOGD("Listeners stopped.");
return kIOReturnSuccess;
}
@@ -136,39 +167,44 @@ kern_return_t SantaDecisionManager::StopListener() {
void SantaDecisionManager::AddToCache(
const char *identifier, santa_action_t decision, uint64_t microsecs) {
lck_rw_lock_exclusive(cached_decisions_lock_);
if (cached_decisions_->getCount() > kMaxCacheSize) {
// This could be made a _lot_ smarter, say only removing entries older
// than a certain time period. However, with a kMaxCacheSize set
// sufficiently large and a kMaxAllowCacheTimeMilliseconds set
// sufficiently low, this should only ever occur if someone is purposefully
// trying to make the cache grow.
LOGD("Cache too large, flushing.");
cached_decisions_->flushCollection();
LOGI("Cache too large, flushing.");
ClearCache();
}
if (decision == ACTION_REQUEST_CHECKBW) {
SantaMessage *pending = new SantaMessage();
pending->setAction(ACTION_REQUEST_CHECKBW, 0);
lck_rw_lock_exclusive(cached_decisions_lock_);
cached_decisions_->setObject(identifier, pending);
lck_rw_unlock_exclusive(cached_decisions_lock_);
pending->release(); // it was retained when added to the dictionary
} else {
SantaMessage *pending = OSDynamicCast(
SantaMessage, cached_decisions_->getObject(identifier));
lck_rw_lock_exclusive(cached_decisions_lock_);
SantaMessage *pending =
OSDynamicCast(SantaMessage, cached_decisions_->getObject(identifier));
if (pending) {
pending->setAction(decision, microsecs);
}
lck_rw_unlock_exclusive(cached_decisions_lock_);
}
lck_rw_unlock_exclusive(cached_decisions_lock_);
}
void SantaDecisionManager::CacheCheck(const char *identifier) {
lck_rw_lock_shared(cached_decisions_lock_);
bool shouldInvalidate = (cached_decisions_->getObject(identifier) != NULL);
bool shouldInvalidate = (cached_decisions_->getObject(identifier) != nullptr);
if (shouldInvalidate) {
lck_rw_lock_shared_to_exclusive(cached_decisions_lock_);
if (!lck_rw_lock_shared_to_exclusive(cached_decisions_lock_)) {
// shared_to_exclusive will return false if a previous reader upgraded
// and if that happens the lock will have been unlocked. If that happens,
// which is rare, relock exclusively.
lck_rw_lock_exclusive(cached_decisions_lock_);
}
cached_decisions_->removeObject(identifier);
lck_rw_unlock_exclusive(cached_decisions_lock_);
} else {
@@ -186,13 +222,15 @@ void SantaDecisionManager::ClearCache() {
lck_rw_unlock_exclusive(cached_decisions_lock_);
}
#pragma mark Decision Fetching
santa_action_t SantaDecisionManager::GetFromCache(const char *identifier) {
santa_action_t result = ACTION_UNSET;
uint64_t decision_time = 0;
lck_rw_lock_shared(cached_decisions_lock_);
SantaMessage *cached_decision = OSDynamicCast(
SantaMessage, cached_decisions_->getObject(identifier));
SantaMessage *cached_decision =
OSDynamicCast(SantaMessage, cached_decisions_->getObject(identifier));
if (cached_decision) {
result = cached_decision->getAction();
decision_time = cached_decision->getMicrosecs();
@@ -227,105 +265,108 @@ santa_action_t SantaDecisionManager::GetFromCache(const char *identifier) {
return result;
}
# pragma mark Queue Management
bool SantaDecisionManager::PostToQueue(santa_message_t message) {
lck_mtx_lock(dataqueue_lock_);
bool kr = false;
if (dataqueue_) {
kr = dataqueue_->enqueue(&message, sizeof(message));
}
lck_mtx_unlock(dataqueue_lock_);
return kr;
}
santa_action_t SantaDecisionManager::FetchDecision(
const kauth_cred_t credential,
const vfs_context_t vfs_context,
const vnode_t vnode) {
santa_action_t SantaDecisionManager::GetFromDaemon(
santa_message_t *message, const char *vnode_id_str) {
santa_action_t return_action = ACTION_UNSET;
// Fetch Vnode ID & string
uint64_t vnode_id = GetVnodeIDForVnode(vfs_context, vnode);
char vnode_id_str[MAX_VNODE_ID_STR];
snprintf(vnode_id_str, MAX_VNODE_ID_STR, "%llu", vnode_id);
// Check to see if item is in cache
return_action = GetFromCache(vnode_id_str);
// If item wasn't in cache, fetch decision from daemon.
if (!CHECKBW_RESPONSE_VALID(return_action)) {
// Add pending request to cache
// Wait for the daemon to respond or die.
do {
// Add pending request to cache.
AddToCache(vnode_id_str, ACTION_REQUEST_CHECKBW, 0);
// Get path
char path[MAX_PATH_LEN];
int name_len = MAX_PATH_LEN;
if (vn_getpath(vnode, path, &name_len) != 0) {
path[0] = '\0';
}
if (!ClientConnected()) {
LOGI("Execution request without daemon running: %s", path);
AddToCache(vnode_id_str,
ACTION_RESPOND_CHECKBW_ALLOW,
GetCurrentUptime());
return ACTION_RESPOND_CHECKBW_ALLOW;
}
// Prepare to send message to daemon
santa_message_t message;
strncpy(message.path, path, MAX_PATH_LEN);
message.userId = kauth_cred_getuid(credential);
message.pid = proc_selfpid();
message.ppid = proc_selfppid();
message.action = ACTION_REQUEST_CHECKBW;
message.vnode_id = vnode_id;
// Wait for the daemon to respond or die.
do {
// Send request to daemon...
if (!PostToQueue(message)) {
OSIncrementAtomic(&failed_queue_requests_);
if (failed_queue_requests_ > kMaxQueueFailures) {
LOGE("Failed to queue more than %d requests, killing daemon", kMaxQueueFailures);
proc_signal(owning_pid_, SIGKILL);
}
LOGE("Failed to queue request for %s.", path);
CacheCheck(vnode_id_str);
return ACTION_ERROR;
// Send request to daemon...
if (!PostToQueue(message)) {
OSIncrementAtomic(&failed_queue_requests_);
if (failed_queue_requests_ > kMaxQueueFailures) {
LOGE("Failed to queue more than %d requests, killing daemon",
kMaxQueueFailures);
proc_signal(client_pid_, SIGKILL);
client_pid_ = 0;
}
// ... and wait for it to respond. If after kRequestLoopSleepMilliseconds
// * kMaxRequestLoops it still hasn't responded, send request again.
for (int i = 0; i < kMaxRequestLoops; ++i) {
IOSleep(kRequestLoopSleepMilliseconds);
return_action = GetFromCache(vnode_id_str);
if (CHECKBW_RESPONSE_VALID(return_action)) break;
}
} while (!CHECKBW_RESPONSE_VALID(return_action) &&
proc_exiting(owning_proc_) == 0);
// If response is still not valid, the daemon exited
if (!CHECKBW_RESPONSE_VALID(return_action)) {
LOGE("Daemon process did not respond correctly. Allowing executions "
"until it comes back.");
LOGE("Failed to queue request for %s.", message->path);
CacheCheck(vnode_id_str);
return ACTION_ERROR;
}
do {
IOSleep(kRequestLoopSleepMilliseconds);
return_action = GetFromCache(vnode_id_str);
} while (return_action == ACTION_REQUEST_CHECKBW && ClientConnected());
} while (!CHECKBW_RESPONSE_VALID(return_action) && ClientConnected());
// If response is still not valid, the daemon exited
if (!CHECKBW_RESPONSE_VALID(return_action)) {
LOGE("Daemon process did not respond correctly. Allowing executions "
"until it comes back. Executable path: %s", message->path);
CacheCheck(vnode_id_str);
return ACTION_ERROR;
}
return return_action;
}
# pragma mark Misc
santa_action_t SantaDecisionManager::FetchDecision(
const kauth_cred_t cred,
const vnode_t vp,
const uint64_t vnode_id,
const char *vnode_id_str) {
santa_action_t return_action = ACTION_UNSET;
if (!ClientConnected()) return ACTION_RESPOND_CHECKBW_ALLOW;
uint64_t SantaDecisionManager::GetVnodeIDForVnode(const vfs_context_t context,
const vnode_t vp) {
// Check to see if item is in cache
return_action = GetFromCache(vnode_id_str);
// If item was in cache return it.
if CHECKBW_RESPONSE_VALID(return_action) return return_action;
// Get path
char path[MAXPATHLEN];
int name_len = MAXPATHLEN;
if (vn_getpath(vp, path, &name_len) != 0) {
path[0] = '\0';
}
santa_message_t *message = NewMessage();
strlcpy(message->path, path, sizeof(message->path));
message->action = ACTION_REQUEST_CHECKBW;
message->vnode_id = vnode_id;
proc_name(message->ppid, message->pname, sizeof(message->pname));
santa_action_t ret = GetFromDaemon(message, vnode_id_str);
delete message;
return ret;
}
#pragma mark Misc
santa_message_t* SantaDecisionManager::NewMessage() {
santa_message_t *message = new santa_message_t;
message->uid = kauth_getuid();
message->gid = kauth_getgid();
message->pid = proc_selfpid();
message->ppid = proc_selfppid();
return message;
}
bool SantaDecisionManager::PostToQueue(santa_message_t *message) {
bool kr = false;
lck_mtx_lock(dataqueue_lock_);
kr = dataqueue_->enqueue(message, sizeof(santa_message_t));
if (!kr) {
// If enqueue failed, pop an item off the queue and try again.
uint32_t dataSize = sizeof(santa_message_t);
dataqueue_->dequeue(0, &dataSize);
kr = dataqueue_->enqueue(message, sizeof(santa_message_t));
}
lck_mtx_unlock(dataqueue_lock_);
return kr;
}
uint64_t SantaDecisionManager::GetVnodeIDForVnode(
const vfs_context_t ctx, const vnode_t vp) {
struct vnode_attr vap;
VATTR_INIT(&vap);
VATTR_WANTED(&vap, va_fileid);
vnode_getattr(vp, &vap, context);
vnode_getattr(vp, &vap, ctx);
return vap.va_fileid;
}
@@ -336,7 +377,7 @@ uint64_t SantaDecisionManager::GetCurrentUptime() {
return (uint64_t)((sec * 1000000) + usec);
}
# pragma mark Invocation Tracking & PID comparison
#pragma mark Invocation Tracking & PID comparison
void SantaDecisionManager::IncrementListenerInvocations() {
OSIncrementAtomic(&listener_invocations_);
@@ -346,81 +387,150 @@ void SantaDecisionManager::DecrementListenerInvocations() {
OSDecrementAtomic(&listener_invocations_);
}
int SantaDecisionManager::VnodeCallback(const kauth_cred_t cred,
const vfs_context_t ctx,
const vnode_t vp,
int *errno) {
// Only operate on regular files (not directories, symlinks, etc.).
if (vnode_vtype(vp) != VREG) return KAUTH_RESULT_DEFER;
// Get ID for the vnode and convert it to a string.
uint64_t vnode_id = GetVnodeIDForVnode(ctx, vp);
char vnode_str[MAX_VNODE_ID_STR];
snprintf(vnode_str, MAX_VNODE_ID_STR, "%llu", vnode_id);
// Fetch decision
santa_action_t returnedAction = FetchDecision(cred, vp, vnode_id, vnode_str);
// If file has dirty blocks, remove from cache and deny. This would usually
// be the case if a file has been written to and flushed but not yet
// closed.
if (vnode_hasdirtyblks(vp)) {
CacheCheck(vnode_str);
returnedAction = ACTION_RESPOND_CHECKBW_DENY;
}
switch (returnedAction) {
case ACTION_RESPOND_CHECKBW_ALLOW:
return KAUTH_RESULT_ALLOW;
case ACTION_RESPOND_CHECKBW_DENY:
*errno = EPERM;
return KAUTH_RESULT_DENY;
default:
// NOTE: Any unknown response or error condition causes us to fail open.
// Whilst from a security perspective this is bad, it's important that
// we don't break user's machines.
return KAUTH_RESULT_DEFER;
}
}
void SantaDecisionManager::FileOpCallback(
const kauth_action_t action, const vnode_t vp,
const char *path, const char *new_path) {
if (vp) {
vfs_context_t context = vfs_context_create(nullptr);
uint64_t vnode_id = GetVnodeIDForVnode(context, vp);
vfs_context_rele(context);
if (action == KAUTH_FILEOP_CLOSE) {
char vnode_id_str[MAX_VNODE_ID_STR];
snprintf(vnode_id_str, MAX_VNODE_ID_STR, "%llu", vnode_id);
CacheCheck(vnode_id_str);
} else if (action == KAUTH_FILEOP_EXEC) {
santa_message_t *message = NewMessage();
message->vnode_id = vnode_id;
message->action = ACTION_NOTIFY_EXEC;
strlcpy(message->path, path, sizeof(message->path));
PostToQueue(message);
delete message;
return;
}
}
// Filter out modifications to locations that are definitely not useful.
if (ClientConnected() && !strprefix(path, "/.") && !strprefix(path, "/dev")) {
santa_message_t *message = NewMessage();
strlcpy(message->path, path, sizeof(message->path));
if (new_path) strlcpy(message->newpath, new_path, sizeof(message->newpath));
proc_name(message->pid, message->pname, sizeof(message->pname));
switch (action) {
case KAUTH_FILEOP_CLOSE:
message->action = ACTION_NOTIFY_WRITE; break;
case KAUTH_FILEOP_RENAME:
message->action = ACTION_NOTIFY_RENAME; break;
case KAUTH_FILEOP_LINK:
message->action = ACTION_NOTIFY_LINK; break;
case KAUTH_FILEOP_EXCHANGE:
message->action = ACTION_NOTIFY_EXCHANGE; break;
case KAUTH_FILEOP_DELETE:
message->action = ACTION_NOTIFY_DELETE; break;
default: delete message; return;
}
PostToQueue(message);
delete message;
}
}
#undef super
#pragma mark Kauth Callback
#pragma mark Kauth Callbacks
extern "C" int fileop_scope_callback(
kauth_cred_t credential, void *idata, kauth_action_t action,
uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) {
SantaDecisionManager *sdm = OSDynamicCast(
SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
vnode_t vp = nullptr;
char *path = nullptr;
char *new_path = nullptr;
switch (action) {
case KAUTH_FILEOP_CLOSE:
if (!(arg2 & KAUTH_FILEOP_CLOSE_MODIFIED)) return KAUTH_RESULT_DEFER;
// Intentional fall-through
case KAUTH_FILEOP_DELETE:
case KAUTH_FILEOP_EXEC:
vp = reinterpret_cast<vnode_t>(arg0);
if (vp && vnode_vtype(vp) != VREG) return KAUTH_RESULT_DEFER;
path = reinterpret_cast<char *>(arg1);
break;
case KAUTH_FILEOP_RENAME:
case KAUTH_FILEOP_EXCHANGE:
case KAUTH_FILEOP_LINK:
path = reinterpret_cast<char *>(arg0);
new_path = reinterpret_cast<char *>(arg1);
break;
default:
return KAUTH_RESULT_DEFER;
}
sdm->IncrementListenerInvocations();
sdm->FileOpCallback(action, vp, path, new_path);
sdm->DecrementListenerInvocations();
return KAUTH_RESULT_DEFER;
}
extern "C" int vnode_scope_callback(
kauth_cred_t credential, void *idata, kauth_action_t action,
uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) {
// The default action is to defer
int returnResult = KAUTH_RESULT_DEFER;
// Cast arguments to correct types
if (idata == NULL) {
LOGE("Vnode callback established without valid decision manager.");
return returnResult;
}
SantaDecisionManager *sdm = OSDynamicCast(
SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
vfs_context_t vfs_context = reinterpret_cast<vfs_context_t>(arg0);
vnode_t vnode = reinterpret_cast<vnode_t>(arg1);
// Only operate on regular files (not directories, symlinks, etc.)
vtype vt = vnode_vtype(vnode);
if (vt != VREG) return returnResult;
// Don't operate on ACCESS events, as they're advisory
if (action & KAUTH_VNODE_ACCESS) return returnResult;
// Filter for only writes
if (action & KAUTH_VNODE_WRITE_DATA ||
action & KAUTH_VNODE_APPEND_DATA ||
action & KAUTH_VNODE_DELETE) {
char vnode_id_str[MAX_VNODE_ID_STR];
snprintf(vnode_id_str, MAX_VNODE_ID_STR, "%llu",
sdm->GetVnodeIDForVnode(vfs_context, vnode));
// If an execution request is pending, deny write
if (sdm->GetFromCache(vnode_id_str) == ACTION_REQUEST_CHECKBW) {
LOGD("Denying write due to pending execution: %s", vnode_id_str);
*(reinterpret_cast<int *>(arg3)) = EACCES;
return KAUTH_RESULT_DENY;
}
// Otherwise remove from cache
sdm->CacheCheck(vnode_id_str);
return returnResult;
if (action & KAUTH_VNODE_ACCESS ||
!(action & KAUTH_VNODE_EXECUTE) ||
idata == nullptr) {
return KAUTH_RESULT_DEFER;
}
// Filter for only EXECUTE actions
if (action & KAUTH_VNODE_EXECUTE) {
sdm->IncrementListenerInvocations();
SantaDecisionManager *sdm =
OSDynamicCast(SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
// Fetch decision
santa_action_t returnedAction = sdm->FetchDecision(
credential, vfs_context, vnode);
switch (returnedAction) {
case ACTION_RESPOND_CHECKBW_ALLOW:
returnResult = KAUTH_RESULT_ALLOW;
break;
case ACTION_RESPOND_CHECKBW_DENY:
*(reinterpret_cast<int *>(arg3)) = EACCES;
returnResult = KAUTH_RESULT_DENY;
break;
default:
// NOTE: Any unknown response or error condition causes us to fail open.
// Whilst from a security perspective this is bad, it's important that
// we don't break user's machines.
break;
}
sdm->DecrementListenerInvocations();
return returnResult;
}
return returnResult;
sdm->IncrementListenerInvocations();
int result = sdm->VnodeCallback(credential,
reinterpret_cast<vfs_context_t>(arg0),
reinterpret_cast<vnode_t>(arg1),
reinterpret_cast<int *>(arg3));
sdm->DecrementListenerInvocations();
return result;
}

View File

@@ -15,7 +15,9 @@
#ifndef SANTA__SANTA_DRIVER__SANTADECISIONMANAGER_H
#define SANTA__SANTA_DRIVER__SANTADECISIONMANAGER_H
#include <IOKit/IODataQueueShared.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/IOSharedDataQueue.h>
#include <libkern/c++/OSDictionary.h>
#include <sys/kauth.h>
@@ -26,40 +28,6 @@
#include "SNTKernelCommon.h"
#include "SNTLogging.h"
///
/// The maximum number of milliseconds a cached deny message should be
/// considered valid.
///
const uint64_t kMaxDenyCacheTimeMilliseconds = 500;
///
/// The maximum number of milliseconds a cached allow message should be
/// considered valid.
///
const uint64_t kMaxAllowCacheTimeMilliseconds = 1000 * 60 * 60 * 24;
///
/// While waiting for a response from the daemon, this is the number of
/// milliseconds to sleep for before checking the cache for a response.
///
const int kRequestLoopSleepMilliseconds = 10;
///
/// While waiting for a response from the daemon, this is the maximum number
/// of loops to wait before sending the request again.
///
const int kMaxRequestLoops = 50;
///
/// Maximum number of entries in the in-kernel cache.
///
const int kMaxCacheSize = 10000;
///
/// Maximum number of PostToQueue failures to allow.
///
const int kMaxQueueFailures = 10;
///
/// SantaDecisionManager is responsible for intercepting Vnode execute actions
/// and responding to the request appropriately.
@@ -73,33 +41,37 @@ class SantaDecisionManager : public OSObject {
public:
/// Used for initialization after instantiation. Required because
/// constructors cannot throw inside kernel-space.
bool init();
bool init() override;
/// Called automatically when retain count drops to 0.
void free();
void free() override;
/// Called by SantaDriverClient during connection to provide the shared
/// dataqueue memory to the client.
IOMemoryDescriptor *GetMemoryDescriptor();
/// Called by SantaDriverClient when a client connects, providing the data
/// queue used to pass messages and the pid of the client process.
void ConnectClient(IOSharedDataQueue *queue, pid_t pid);
void ConnectClient(mach_port_t port, pid_t pid);
/// Called by SantaDriverClient when a client disconnects
void DisconnectClient();
void DisconnectClient(bool itDied = false);
/// Returns whether a client is currently connected or not.
bool ClientConnected();
/// Starts the kauth listener.
/// Starts the kauth listeners.
kern_return_t StartListener();
/// Stops the kauth listener. After stopping new callback requests,
/// Stops the kauth listeners. After stopping new callback requests,
/// waits until all current invocations have finished before clearing the
/// cache and returning.
kern_return_t StopListener();
/// Adds a decision to the cache, with a timestamp.
void AddToCache(const char *identifier,
const santa_action_t decision,
const uint64_t microsecs);
const uint64_t microsecs = GetCurrentUptime());
/// Checks to see if a given identifier is in the cache and removes it.
void CacheCheck(const char *identifier);
@@ -110,37 +82,123 @@ class SantaDecisionManager : public OSObject {
/// Clears the cache.
void ClearCache();
/// Fetches a response from the cache, first checking to see if the
/// entry has expired.
santa_action_t GetFromCache(const char *identifier);
/// Posts the requested message to the client data queue, if there is one.
/// Uses dataqueue_lock_ to ensure two threads don't try to write to the
/// queue at the same time.
bool PostToQueue(santa_message_t);
/// Fetches an execution decision for a file, first using the cache and then
/// by sending a message to the daemon and waiting until a response arrives.
/// If a daemon isn't connected, will allow execution and cache, logging
/// the path to the executed file.
santa_action_t FetchDecision(const kauth_cred_t credential,
const vfs_context_t vfs_context,
const vnode_t vnode);
/// Fetches the vnode_id for a given vnode.
uint64_t GetVnodeIDForVnode(const vfs_context_t context, const vnode_t vp);
/// Returns the current system uptime in microseconds
uint64_t GetCurrentUptime();
/// Increments the count of active vnode callback's pending.
void IncrementListenerInvocations();
/// Decrements the count of active vnode callback's pending.
void DecrementListenerInvocations();
///
/// Vnode Callback
/// @param cred The kauth credential for this request.
/// @param ctx The VFS context for this request.
/// @param vp The Vnode for this request.
/// @param errno A pointer to return an errno style error.
/// @return int A valid KAUTH_RESULT_*.
///
int VnodeCallback(const kauth_cred_t cred, const vfs_context_t ctx,
const vnode_t vp, int *errno);
///
/// FileOp Callback
/// @param vp The Vnode for this request.
///
void FileOpCallback(kauth_action_t action, const vnode_t vp, const char *path, const char *new_path);
protected:
///
/// The maximum number of milliseconds a cached deny message should be
/// considered valid.
///
const uint64_t kMaxDenyCacheTimeMilliseconds = 500;
///
/// The maximum number of milliseconds a cached allow message should be
/// considered valid.
///
const uint64_t kMaxAllowCacheTimeMilliseconds = 1000 * 60 * 60 * 24;
///
/// While waiting for a response from the daemon, this is the number of
/// milliseconds to sleep for before checking the cache for a response.
///
const int kRequestLoopSleepMilliseconds = 10;
///
/// Maximum number of entries in the in-kernel cache.
///
const int kMaxCacheSize = 10000;
///
/// Maximum number of PostToQueue failures to allow.
///
const int kMaxQueueFailures = 10;
///
/// The maximum number of messages can be kept in
/// the IODataQueue at any time.
///
const int kMaxQueueEvents = 512;
/// Fetches a response from the cache, first checking to see if the
/// entry has expired.
santa_action_t GetFromCache(const char *identifier);
/// Fetches a response from the daemon. Handles both daemon death
/// and failure to post messages to the daemon.
///
/// @param message The message to send to the daemon
/// @param identifier The vnode ID string for this request
/// @return santa_action_t The response for this request
///
santa_action_t GetFromDaemon(santa_message_t *message,
const char *identifier);
///
/// Fetches an execution decision for a file, first using the cache and then
/// by sending a message to the daemon and waiting until a response arrives.
/// If a daemon isn't connected, will allow execution and cache, logging
/// the path to the executed file.
///
/// @param cred The credential for this request.
/// @param vp The Vnode for this request.
/// @param vnode_id The ID for this vnode.
/// @param vnode_id_str A string representation of the above ID.
///
santa_action_t FetchDecision(const kauth_cred_t cred,
const vnode_t vp,
const uint64_t vnode_id,
const char *vnode_id_str);
///
/// Posts the requested message to the client data queue.
///
/// @param message The message to send
/// @return bool true if sending was successful.
///
bool PostToQueue(santa_message_t *message);
///
/// Fetches the vnode_id for a given vnode.
///
/// @param ctx The VFS context to use.
/// @param vp The Vnode to get the ID for
/// @return uint64_t The Vnode ID as a 64-bit unsigned int.
///
uint64_t GetVnodeIDForVnode(const vfs_context_t ctx, const vnode_t vp);
///
/// Creates a new santa_message_t with some fields pre-filled.
///
santa_message_t* NewMessage();
/// Returns the current system uptime in microseconds
static uint64_t GetCurrentUptime();
private:
lck_grp_t *sdm_lock_grp_;
lck_grp_attr_t *sdm_lock_grp_attr_;
lck_attr_t *sdm_lock_attr_;
lck_rw_t *cached_decisions_lock_;
lck_mtx_t *dataqueue_lock_;
@@ -151,10 +209,10 @@ class SantaDecisionManager : public OSObject {
SInt32 listener_invocations_;
pid_t owning_pid_;
proc_t owning_proc_;
pid_t client_pid_;
kauth_listener_t vnode_listener_;
kauth_listener_t fileop_listener_;
};
///
@@ -164,11 +222,26 @@ class SantaDecisionManager : public OSObject {
/// @param action that was requested
/// @param VFS context
/// @param Vnode being operated on
/// @param Parent Vnode. May be NULL.
/// @param Parent Vnode. May be nullptr.
/// @param Pointer to an errno-style error.
///
extern "C" int vnode_scope_callback(
kauth_cred_t credential, void *idata, kauth_action_t action,
uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
///
/// The kauth callback function for the FileOp scope
/// @param actor's credentials
/// @param data that was passed when the listener was registered
/// @param action that was requested
/// @param depends on action, usually the vnode ref.
/// @param depends on action.
/// @param depends on action, usually 0.
/// @param depends on action, usually 0.
///
extern "C" int fileop_scope_callback(
kauth_cred_t credential, void *idata, kauth_action_t action,
uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
#endif // SANTA__SANTA_DRIVER__SANTADECISIONMANAGER_H

View File

@@ -24,8 +24,12 @@ bool SantaDriver::start(IOService *provider) {
if (!super::start(provider)) return false;
santaDecisionManager = new SantaDecisionManager;
santaDecisionManager->init();
santaDecisionManager->StartListener();
if (!santaDecisionManager->init() ||
santaDecisionManager->StartListener() != kIOReturnSuccess) {
santaDecisionManager->release();
santaDecisionManager = nullptr;
return false;
}
registerService();
@@ -37,14 +41,14 @@ bool SantaDriver::start(IOService *provider) {
void SantaDriver::stop(IOService *provider) {
santaDecisionManager->StopListener();
santaDecisionManager->release();
santaDecisionManager = NULL;
santaDecisionManager = nullptr;
LOGI("Unloaded.");
super::stop(provider);
}
SantaDecisionManager* SantaDriver::GetDecisionManager() {
SantaDecisionManager *SantaDriver::GetDecisionManager() {
return santaDecisionManager;
}

View File

@@ -31,17 +31,16 @@ class com_google_SantaDriver : public IOService {
public:
/// Called by the kernel when the kext is loaded
bool start(IOService *provider);
bool start(IOService *provider) override;
/// Called by the kernel when the kext is unloaded
void stop(IOService *provider);
void stop(IOService *provider) override;
/// Returns a pointer to the SantaDecisionManager created in start().
SantaDecisionManager* GetDecisionManager();
SantaDecisionManager *GetDecisionManager();
private:
SantaDecisionManager *santaDecisionManager;
};
#endif // SANTA__SANTA_DRIVER__SANTADRIVER_H

View File

@@ -36,77 +36,58 @@ bool SantaDriverClient::initWithTask(
}
bool SantaDriverClient::start(IOService *provider) {
fProvider = OSDynamicCast(com_google_SantaDriver, provider);
myProvider = OSDynamicCast(com_google_SantaDriver, provider);
if (!fProvider) return false;
if (!myProvider) return false;
if (!super::start(provider)) return false;
fSDM = fProvider->GetDecisionManager();
if (!fSDM) return false;
decisionManager = myProvider->GetDecisionManager();
if (!decisionManager) return false;
return true;
}
void SantaDriverClient::stop(IOService *provider) {
super::stop(provider);
fProvider = NULL;
myProvider = nullptr;
decisionManager = nullptr;
}
IOReturn SantaDriverClient::clientClose() {
terminate(kIOServiceSynchronous);
return kIOReturnSuccess;
decisionManager->DisconnectClient(true);
return terminate(kIOServiceSynchronous) ? kIOReturnSuccess : kIOReturnError;
}
bool SantaDriverClient::terminate(IOOptionBits options) {
fSDM->DisconnectClient();
decisionManager->DisconnectClient();
LOGI("Client disconnected.");
if (fSharedMemory) {
fSharedMemory->release();
fSharedMemory = NULL;
}
if (fDataQueue) {
fDataQueue->release();
fDataQueue = NULL;
}
if (fProvider && fProvider->isOpen(this)) fProvider->close(this);
if (myProvider && myProvider->isOpen(this)) myProvider->close(this);
return super::terminate(options);
}
#pragma mark Fetching memory and data queue notifications
IOReturn SantaDriverClient::registerNotificationPort(mach_port_t port,
UInt32 type,
UInt32 ref) {
if ((!fDataQueue) || (port == MACH_PORT_NULL)) return kIOReturnError;
IOReturn SantaDriverClient::registerNotificationPort(
mach_port_t port, UInt32 type, UInt32 ref) {
if (port == MACH_PORT_NULL) return kIOReturnError;
fDataQueue->setNotificationPort(port);
decisionManager->ConnectClient(port, proc_selfpid());
LOGI("Client connected, PID: %d.", proc_selfpid());
return kIOReturnSuccess;
}
IOReturn SantaDriverClient::clientMemoryForType(UInt32 type,
IOOptionBits *options,
IOMemoryDescriptor **memory) {
*memory = NULL;
IOReturn SantaDriverClient::clientMemoryForType(
UInt32 type, IOOptionBits *options, IOMemoryDescriptor **memory) {
if (type != kIODefaultMemoryType) return kIOReturnNoMemory;
*options = 0;
*memory = decisionManager->GetMemoryDescriptor();
(*memory)->retain();
if (type == kIODefaultMemoryType) {
if (!fSharedMemory) return kIOReturnNoMemory;
fSharedMemory->retain(); // client will decrement this ref
*memory = fSharedMemory;
fSDM->ConnectClient(fDataQueue, proc_selfpid());
LOGI("Client connected, PID: %d.", proc_selfpid());
return kIOReturnSuccess;
}
return kIOReturnNoMemory;
return kIOReturnSuccess;
}
#pragma mark Callable Methods
@@ -114,23 +95,11 @@ IOReturn SantaDriverClient::clientMemoryForType(UInt32 type,
IOReturn SantaDriverClient::open() {
if (isInactive()) return kIOReturnNotAttached;
if (!fProvider->open(this)) {
if (!myProvider->open(this)) {
LOGW("A second client tried to connect.");
return kIOReturnExclusiveAccess;
}
fDataQueue = IOSharedDataQueue::withCapacity((sizeof(santa_message_t) +
DATA_QUEUE_ENTRY_HEADER_SIZE)
* kMaxQueueEvents);
if (!fDataQueue) return kIOReturnNoMemory;
fSharedMemory = fDataQueue->getMemoryDescriptor();
if (!fSharedMemory) {
fDataQueue->release();
fDataQueue = NULL;
return kIOReturnVMError;
}
return kIOReturnSuccess;
}
@@ -145,9 +114,7 @@ IOReturn SantaDriverClient::static_open(
IOReturn SantaDriverClient::allow_binary(const uint64_t vnode_id) {
char vnode_id_str[21];
snprintf(vnode_id_str, sizeof(vnode_id_str), "%llu", vnode_id);
fSDM->AddToCache(vnode_id_str,
ACTION_RESPOND_CHECKBW_ALLOW,
fSDM->GetCurrentUptime());
decisionManager->AddToCache(vnode_id_str, ACTION_RESPOND_CHECKBW_ALLOW);
return kIOReturnSuccess;
}
@@ -157,16 +124,16 @@ IOReturn SantaDriverClient::static_allow_binary(
void *reference,
IOExternalMethodArguments *arguments) {
if (!target) return kIOReturnBadArgument;
if (arguments->scalarInput == nullptr) return kIOReturnBadArgument;
return target->allow_binary(
*(static_cast<const uint64_t *>(arguments->scalarInput)));
static_cast<const uint64_t>(*arguments->scalarInput));
}
IOReturn SantaDriverClient::deny_binary(const uint64_t vnode_id) {
char vnode_id_str[21];
snprintf(vnode_id_str, sizeof(vnode_id_str), "%llu", vnode_id);
fSDM->AddToCache(vnode_id_str,
ACTION_RESPOND_CHECKBW_DENY,
fSDM->GetCurrentUptime());
decisionManager->AddToCache(vnode_id_str, ACTION_RESPOND_CHECKBW_DENY);
return kIOReturnSuccess;
}
@@ -176,12 +143,14 @@ IOReturn SantaDriverClient::static_deny_binary(
void *reference,
IOExternalMethodArguments *arguments) {
if (!target) return kIOReturnBadArgument;
if (arguments->scalarInput == nullptr) return kIOReturnBadArgument;
return target->deny_binary(
*(static_cast<const uint64_t *>(arguments->scalarInput)));
static_cast<const uint64_t>(*arguments->scalarInput));
}
IOReturn SantaDriverClient::clear_cache() {
fSDM->ClearCache();
decisionManager->ClearCache();
return kIOReturnSuccess;
}
@@ -194,7 +163,7 @@ IOReturn SantaDriverClient::static_clear_cache(
}
IOReturn SantaDriverClient::cache_count(uint64_t *output) {
*output = fSDM->CacheCount();
*output = decisionManager->CacheCount();
return kIOReturnSuccess;
}

View File

@@ -16,9 +16,6 @@
#define SANTA__SANTA_DRIVER__SANTADRIVERUSERCLIENT_H
#include <IOKit/IOUserClient.h>
#include <IOKit/IOSharedDataQueue.h>
#include <IOKit/IOLib.h>
#include <IOKit/IODataQueueShared.h>
#include <sys/kauth.h>
#include <sys/vnode.h>
#include <sys/proc.h>
@@ -28,9 +25,6 @@
#include "SantaMessage.h"
#include "SNTKernelCommon.h"
// The maximum number of messages can be kept in the IODataQueue at any time.
const int kMaxQueueEvents = 256;
///
/// This class is instantiated by IOKit when a new client process attempts to
/// connect to the driver. Starting, stopping, handling connections, allocating
@@ -39,33 +33,34 @@ const int kMaxQueueEvents = 256;
/// Documentation on how the IOUserClient parts of this code work can be found
/// here:
/// https://developer.apple.com/library/mac/samplecode/SimpleUserClient/Listings/User_Client_Info_txt.html
/// https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/WritingDeviceDriver/WritingDeviceDriver.pdf
///
class com_google_SantaDriverClient : public IOUserClient {
OSDeclareDefaultStructors(com_google_SantaDriverClient);
public:
/// Called as part of IOServiceOpen in clients
bool initWithTask(task_t owningTask, void *securityID, UInt32 type);
bool initWithTask(task_t owningTask, void *securityID, UInt32 type) override;
/// Called after initWithTask as part of IOServiceOpen
bool start(IOService *provider);
bool start(IOService *provider) override;
/// Called when this class is stopping
void stop(IOService *provider);
void stop(IOService *provider) override;
/// Called when a client disconnects
IOReturn clientClose();
IOReturn clientClose() override;
/// Called when the driver is shutting down
bool terminate(IOOptionBits options);
bool terminate(IOOptionBits options) override;
/// Called in clients with IOConnectSetNotificationPort
IOReturn registerNotificationPort(
mach_port_t port, UInt32 type, UInt32 refCon);
mach_port_t port, UInt32 type, UInt32 refCon) override;
/// Called in clients with IOConnectMapMemory
IOReturn clientMemoryForType(
UInt32 type, IOOptionBits *options, IOMemoryDescriptor **memory);
UInt32 type, IOOptionBits *options, IOMemoryDescriptor **memory) override;
/// Called in clients with IOConnectCallScalarMethod etc. Dispatches
/// to the requested selector using the SantaDriverMethods enum in
@@ -74,7 +69,7 @@ class com_google_SantaDriverClient : public IOUserClient {
UInt32 selector,
IOExternalMethodArguments *arguments,
IOExternalMethodDispatch *dispatch,
OSObject *target, void *reference);
OSObject *target, void *reference) override;
///
/// The userpsace callable methods are below. Each method corresponds
@@ -118,10 +113,8 @@ class com_google_SantaDriverClient : public IOUserClient {
IOExternalMethodArguments *arguments);
private:
IOSharedDataQueue *fDataQueue;
IOMemoryDescriptor *fSharedMemory;
com_google_SantaDriver *fProvider;
SantaDecisionManager *fSDM;
com_google_SantaDriver *myProvider;
SantaDecisionManager *decisionManager;
};
#endif // SANTA__SANTA_DRIVER__SANTADRIVERUSERCLIENT_H

View File

@@ -24,8 +24,8 @@ santa_action_t SantaMessage::getAction() const {
return action_;
}
void SantaMessage::setAction(const santa_action_t action,
const uint64_t microsecs) {
void SantaMessage::setAction(
const santa_action_t action, const uint64_t microsecs) {
action_ = action;
microsecs_ = microsecs;
}

View File

@@ -36,7 +36,7 @@ static NSMutableDictionary *registeredCommands;
int longestCommandName = 0;
for (NSString *cmdName in registeredCommands) {
if ([cmdName length] > longestCommandName) {
if ((int)[cmdName length] > longestCommandName) {
longestCommandName = (int)[cmdName length];
}
}

View File

@@ -16,8 +16,8 @@
#include "SNTLogging.h"
#import "SNTCertificate.h"
#import "SNTCodesignChecker.h"
#import "MOLCertificate.h"
#import "MOLCodesignChecker.h"
#import "SNTFileInfo.h"
@interface SNTCommandBinaryInfo : NSObject<SNTCommand>
@@ -25,7 +25,7 @@
@implementation SNTCommandBinaryInfo
REGISTER_COMMAND_NAME(@"binaryinfo");
REGISTER_COMMAND_NAME(@"binaryinfo")
+ (BOOL)requiresRoot {
return NO;
@@ -55,41 +55,58 @@ REGISTER_COMMAND_NAME(@"binaryinfo");
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:filePath];
if (!fileInfo) {
printf("Invalid file\n");
printf("Invalid or empty file\n");
exit(1);
}
printf("%-12s: %s\n", "Path", [[fileInfo path] UTF8String]);
printf("%-12s: %s\n", "SHA-256", [[fileInfo SHA256] UTF8String]);
printf("%-12s: %s\n", "SHA-1", [[fileInfo SHA1] UTF8String]);
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = @"YYYY/MM/dd HH:mm:ss Z";
[self printKey:@"Path" value:fileInfo.path];
[self printKey:@"SHA-256" value:fileInfo.SHA256];
[self printKey:@"SHA-1" value:fileInfo.SHA1];
[self printKey:@"Bundle Name" value:fileInfo.bundleName];
[self printKey:@"Bundle Version" value:fileInfo.bundleVersion];
[self printKey:@"Bundle Version Str" value:fileInfo.bundleShortVersionString];
if (fileInfo.quarantineDataURL) {
[self printKey:@"Download Referer URL" value:fileInfo.quarantineRefererURL];
[self printKey:@"Download URL" value:fileInfo.quarantineDataURL];
[self printKey:@"Download Timestamp"
value:[dateFormatter stringFromDate:fileInfo.quarantineTimestamp]];
[self printKey:@"Download Agent" value:fileInfo.quarantineAgentBundleID];
}
NSArray *archs = [fileInfo architectures];
if (archs) {
printf("%-12s: %s (%s)\n", "Type",
[[fileInfo machoType] UTF8String],
[[archs componentsJoinedByString:@", "] UTF8String]);
NSString *s = [NSString stringWithFormat:@"%@ (%@)",
fileInfo.machoType, [archs componentsJoinedByString:@", "]];
[self printKey:@"Type" value:s];
} else {
printf("%-12s: %s\n", "Type", [[fileInfo machoType] UTF8String]);
[self printKey:@"Type" value:fileInfo.machoType];
}
SNTCodesignChecker *csc = [[SNTCodesignChecker alloc] initWithBinaryPath:filePath];
printf("%-12s: %s\n", "Code-signed", (csc) ? "Yes" : "No");
if ([fileInfo isMissingPageZero]) {
[self printKey:@"Page Zero" value:@"__PAGEZERO segment missing/bad!"];
}
MOLCodesignChecker *csc = [[MOLCodesignChecker alloc] initWithBinaryPath:filePath];
[self printKey:@"Code-signed" value:(csc) ? @"Yes" : @"No"];
if (csc) {
printf("Signing chain:\n");
[csc.certificates enumerateObjectsUsingBlock:^(SNTCertificate *c,
[csc.certificates enumerateObjectsUsingBlock:^(MOLCertificate *c,
unsigned long idx,
BOOL *stop) {
idx++; // index from 1
printf(" %2lu. %-20s: %s\n", idx, "SHA-256", [c.SHA256 UTF8String]);
printf(" %2lu. %-20s: %s\n", idx + 1, "SHA-256", [c.SHA256 UTF8String]);
printf(" %-20s: %s\n", "SHA-1", [c.SHA1 UTF8String]);
printf(" %-20s: %s\n", "Common Name", [c.commonName UTF8String]);
printf(" %-20s: %s\n", "Organization", [c.orgName UTF8String]);
printf(" %-20s: %s\n", "Organizational Unit", [c.orgUnit UTF8String]);
printf(" %-20s: %s\n", "Valid From", [[c.validFrom description] UTF8String]);
printf(" %-20s: %s\n", "Valid Until", [[c.validUntil description] UTF8String]);
printf(" %-20s: %s\n", "Valid From",
[[dateFormatter stringFromDate:c.validFrom] UTF8String]);
printf(" %-20s: %s\n", "Valid Until",
[[dateFormatter stringFromDate:c.validUntil] UTF8String]);
printf("\n");
}];
}
@@ -97,4 +114,8 @@ REGISTER_COMMAND_NAME(@"binaryinfo");
exit(0);
}
+ (void)printKey:(NSString *)key value:(NSString *)value {
printf("%-21s: %s\n", [key UTF8String], [value UTF8String]);
}
@end

View File

@@ -23,7 +23,7 @@
@implementation SNTCommandFlushCache
REGISTER_COMMAND_NAME(@"flushcache");
REGISTER_COMMAND_NAME(@"flushcache")
+ (BOOL)requiresRoot {
return YES;

View File

@@ -16,8 +16,8 @@
#include "SNTLogging.h"
#import "SNTCertificate.h"
#import "SNTCodesignChecker.h"
#import "MOLCertificate.h"
#import "MOLCodesignChecker.h"
#import "SNTConfigurator.h"
#import "SNTDropRootPrivs.h"
#import "SNTFileInfo.h"
@@ -32,7 +32,7 @@
@implementation SNTCommandRule
REGISTER_COMMAND_NAME(@"rule");
REGISTER_COMMAND_NAME(@"rule")
+ (BOOL)requiresRoot {
return YES;
@@ -47,119 +47,108 @@ REGISTER_COMMAND_NAME(@"rule");
}
+ (NSString *)longHelpText {
return (@"Usage: santactl rule {add|remove} [options]\n"
@" --whitelist: add to whitelist\n"
@" --blacklist: add to blacklist\n"
@" --silent-blacklist: add to silent blacklist\n"
@" --message {message}: custom message\n"
@" --path {path}: path of binary to add\n"
@" --sha256 {sha256}: hash to add\n");
return (@"Usage: santactl rule [options]\n"
@" One of:\n"
@" --whitelist: add to whitelist\n"
@" --blacklist: add to blacklist\n"
@" --silent-blacklist: add to silent blacklist\n"
@" --remove: remove existing rule\n"
@"\n"
@" One of:\n"
@" --path {path}: path of binary/bundle to add/remove.\n"
@" Will add the hash of the file currently at that path.\n"
@" --sha256 {sha256}: hash to add/remove\n"
@"\n"
@" Optionally:\n"
@" --certificate: add certificate rule instead of binary\n"
@" --message {message}: custom message\n");
}
+ (void)printErrorUsageAndExit:(NSString *)error {
printf("%s\n\n", [error UTF8String]);
printf("%s\n", [[self longHelpText] UTF8String]);
exit(1);
}
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
SNTConfigurator *config = [SNTConfigurator configurator];
// Ensure we have no privileges
if (!DropRootPrivileges()) {
printf("Failed to drop root privileges.\n");
exit(1);
}
if ([config syncBaseURL] != nil) {
printf("SyncBaseURL is set, rules are managed centrally.\n");
exit(1);
}
NSString *action = [arguments firstObject];
// add or remove
if (!action) {
printf("Missing action - add or remove?\n");
exit(1);
}
int state = RULESTATE_UNKNOWN;
if ([action compare:@"add" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
} else if ([action compare:@"remove" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
state = RULESTATE_REMOVE;
} else {
printf("Unknown action, expected add or remove.\n");
exit(1);
}
NSString *customMsg = @"";
NSString *SHA256 = nil;
NSString *filePath = nil;
// parse arguments
for (int i=1; i < [arguments count] ; i++ ) {
NSString* argument = [arguments objectAtIndex:i];
if ([argument compare:@"--whitelist" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
state = RULESTATE_WHITELIST;
} else if ([argument compare:@"--blacklist" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
state = RULESTATE_BLACKLIST;
} else if ([argument compare:@"--silent-blacklist" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
state = RULESTATE_SILENT_BLACKLIST;
} else if ([argument compare:@"--message" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
if (++i > ([arguments count])) {
printf("No message specified.\n");
}
customMsg = [arguments objectAtIndex:i];
} else if ([argument compare:@"--path" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
if (++i > ([arguments count])) {
printf("No path specified.\n");
}
filePath = [arguments objectAtIndex:i];
} else if ([argument compare:@"--sha256" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
if (++i > ([arguments count])) {
printf("No SHA-256 specified.\n");
}
SHA256 = [arguments objectAtIndex:i];
} else {
printf("Unknown argument %s.\n", [argument UTF8String]);
exit(1);
}
}
if (state == RULESTATE_UNKNOWN) {
printf("No state specified.\n");
exit(1);
}
if (filePath) {
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:filePath];
if (!fileInfo) {
printf("Not a regular file or executable bundle.\n");
exit(1);
}
SHA256 = [fileInfo SHA256];
} else if (SHA256) {
} else {
printf("No SHA-256 or binary specified.\n");
exit(1);
}
SNTRule *newRule = [[SNTRule alloc] init];
newRule.shasum = SHA256;
newRule.state = state;
newRule.state = RULESTATE_UNKNOWN;
newRule.type = RULETYPE_BINARY;
newRule.customMsg = customMsg;
[[daemonConn remoteObjectProxy] databaseRuleAddRule:newRule cleanSlate:NO reply:^{
if (state == RULESTATE_REMOVE) {
printf("Removed rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
} else {
printf("Added rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
}
exit(0);
}];
NSString *path;
// Parse arguments
for (NSUInteger i = 0; i < arguments.count ; i++ ) {
NSString *arg = arguments[i];
if ([arg caseInsensitiveCompare:@"--whitelist"] == NSOrderedSame) {
newRule.state = RULESTATE_WHITELIST;
} else if ([arg caseInsensitiveCompare:@"--blacklist"] == NSOrderedSame) {
newRule.state = RULESTATE_BLACKLIST;
} else if ([arg caseInsensitiveCompare:@"--silent-blacklist"] == NSOrderedSame) {
newRule.state = RULESTATE_SILENT_BLACKLIST;
} else if ([arg caseInsensitiveCompare:@"--remove"] == NSOrderedSame) {
newRule.state = RULESTATE_REMOVE;
} else if ([arg caseInsensitiveCompare:@"--certificate"] == NSOrderedSame) {
newRule.type = RULETYPE_CERT;
} else if ([arg caseInsensitiveCompare:@"--path"] == NSOrderedSame) {
if (++i > arguments.count - 1) {
[self printErrorUsageAndExit:@"--path requires an argument"];
}
path = arguments[i];
} else if ([arg caseInsensitiveCompare:@"--sha256"] == NSOrderedSame) {
if (++i > arguments.count - 1) {
[self printErrorUsageAndExit:@"--sha256 requires an argument"];
}
newRule.shasum = arguments[i];
if (newRule.shasum.length != 64) {
[self printErrorUsageAndExit:@"--sha256 requires a valid SHA-256 as the argument"];
}
} else if ([arg caseInsensitiveCompare:@"--message"] == NSOrderedSame) {
if (++i > arguments.count - 1) {
[self printErrorUsageAndExit:@"--message requires an argument"];
}
newRule.customMsg = arguments[i];
} else {
[self printErrorUsageAndExit:[@"Unknown argument: %@" stringByAppendingString:arg]];
}
}
if (path) {
SNTFileInfo *fi = [[SNTFileInfo alloc] initWithPath:path];
if (newRule.type == RULETYPE_BINARY) {
newRule.shasum = fi.SHA256;
} else if (newRule.type == RULETYPE_CERT) {
MOLCodesignChecker *cs = [[MOLCodesignChecker alloc] initWithBinaryPath:fi.path];
newRule.shasum = cs.leafCertificate.SHA256;
}
}
if (newRule.state == RULESTATE_UNKNOWN) {
[self printErrorUsageAndExit:@"No state specified"];
} else if (!newRule.shasum) {
[self printErrorUsageAndExit:@"Either SHA-256 or path to file must be specified"];
}
[[daemonConn remoteObjectProxy] databaseRuleAddRule:newRule cleanSlate:NO reply:^(BOOL success) {
if (!success) {
printf("Failed to modify rules.");
exit(1);
} else {
if (newRule.state == RULESTATE_REMOVE) {
printf("Removed rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
} else {
printf("Added rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
}
exit(0);
}
}];
}
@end

View File

@@ -14,6 +14,7 @@
#import "SNTCommandController.h"
#import "SNTConfigurator.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@@ -22,7 +23,7 @@
@implementation SNTCommandStatus
REGISTER_COMMAND_NAME(@"status");
REGISTER_COMMAND_NAME(@"status")
+ (BOOL)requiresRoot {
return NO;
@@ -37,12 +38,17 @@ REGISTER_COMMAND_NAME(@"status");
}
+ (NSString *)longHelpText {
return nil;
return (@"Provides details about Santa while it's running.\n"
@" Use --json to output in JSON format");
}
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
dispatch_group_t group = dispatch_group_create();
// Daemon status
__block NSString *clientMode;
__block uint64_t cpuEvents, ramEvents;
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] clientMode:^(santa_clientmode_t cm) {
switch (cm) {
case CLIENTMODE_MONITOR:
@@ -52,35 +58,102 @@ REGISTER_COMMAND_NAME(@"status");
default:
clientMode = [NSString stringWithFormat:@"Unknown (%d)", cm]; break;
}
dispatch_group_leave(group);
}];
do { usleep(5000); } while (!clientMode);
printf(">>> Daemon Info\n");
printf(" %-25s | %s\n", "Mode", [clientMode UTF8String]);
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] watchdogCPUEvents:^(uint64_t events) {
cpuEvents = events;
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] watchdogRAMEvents:^(uint64_t events) {
ramEvents = events;
dispatch_group_leave(group);
}];
BOOL fileLogging = ([[SNTConfigurator configurator] fileChangesRegex] != nil);
// Kext status
__block uint64_t cacheCount = -1;
[[daemonConn remoteObjectProxy] cacheCount:^(uint64_t count) {
__block int64_t cacheCount = -1;
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] cacheCount:^(int64_t count) {
cacheCount = count;
dispatch_group_leave(group);
}];
do { usleep(5000); } while (cacheCount == -1);
printf(">>> Kernel Info\n");
printf(" %-25s | %lld\n", "Kernel cache count", cacheCount);
// Database counts
__block uint64_t eventCount = 1, binaryRuleCount = -1, certRuleCount = -1;
[[daemonConn remoteObjectProxy] databaseRuleCounts:^(uint64_t binary, uint64_t certificate) {
__block int64_t eventCount = -1, binaryRuleCount = -1, certRuleCount = -1;
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] databaseRuleCounts:^(int64_t binary, int64_t certificate) {
binaryRuleCount = binary;
certRuleCount = certificate;
dispatch_group_leave(group);
}];
[[daemonConn remoteObjectProxy] databaseEventCount:^(uint64_t count) {
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] databaseEventCount:^(int64_t count) {
eventCount = count;
dispatch_group_leave(group);
}];
do { usleep(5000); } while (eventCount == -1 || binaryRuleCount == -1 || certRuleCount == -1);
printf(">>> Database Info\n");
printf(" %-25s | %lld\n", "Binary Rules", binaryRuleCount);
printf(" %-25s | %lld\n", "Certificate Rules", certRuleCount);
printf(" %-25s | %lld\n", "Events Pending Upload", eventCount);
// Sync status
NSString *syncURLStr = [[[SNTConfigurator configurator] syncBaseURL] absoluteString];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = @"YYYY/MM/dd HH:mm:ss Z";
NSDate *lastSyncSuccess = [[SNTConfigurator configurator] syncLastSuccess];
NSString *lastSyncSuccessStr = [dateFormatter stringFromDate:lastSyncSuccess] ?: @"Never";
BOOL syncCleanReqd = [[SNTConfigurator configurator] syncCleanRequired];
// Wait a maximum of 5s for stats collected from daemon to arrive.
if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5))) {
fprintf(stderr, "Failed to retrieve some stats from daemon\n\n");
}
if ([arguments containsObject:@"--json"]) {
NSDictionary *stats = @{
@"daemon": @{
@"mode": clientMode,
@"file_logging": @(fileLogging),
@"watchdog_cpu_events": @(cpuEvents),
@"watchdog_ram_events": @(ramEvents),
},
@"kernel": @{
@"cache_count": @(cacheCount),
},
@"database": @{
@"binary_rules": @(binaryRuleCount),
@"certificate_rules": @(certRuleCount),
@"events_pending_upload": @(eventCount),
},
@"sync": @{
@"server": syncURLStr,
@"clean_required": @(syncCleanReqd),
@"last_successful": lastSyncSuccessStr
},
};
NSData *statsData = [NSJSONSerialization dataWithJSONObject:stats
options:NSJSONWritingPrettyPrinted
error:nil];
NSString *statsStr = [[NSString alloc] initWithData:statsData encoding:NSUTF8StringEncoding];
printf("%s\n", [statsStr UTF8String]);
} else {
printf(">>> Daemon Info\n");
printf(" %-22s | %s\n", "Mode", [clientMode UTF8String]);
printf(" %-22s | %s\n", "File Logging", (fileLogging ? "Yes" : "No"));
printf(" %-22s | %lld\n", "Watchdog CPU Events", cpuEvents);
printf(" %-22s | %lld\n", "Watchdog RAM Events", ramEvents);
printf(">>> Kernel Info\n");
printf(" %-22s | %lld\n", "Kernel cache count", cacheCount);
printf(">>> Database Info\n");
printf(" %-22s | %lld\n", "Binary Rules", binaryRuleCount);
printf(" %-22s | %lld\n", "Certificate Rules", certRuleCount);
printf(" %-22s | %lld\n", "Events Pending Upload", eventCount);
if (syncURLStr) {
printf(">>> Sync Info\n");
printf(" %-22s | %s\n", "Sync Server", [syncURLStr UTF8String]);
printf(" %-22s | %s\n", "Clean Sync Required", (syncCleanReqd ? "Yes" : "No"));
printf(" %-22s | %s\n", "Last Successful Sync", [lastSyncSuccessStr UTF8String]);
}
}
exit(0);
}

View File

@@ -14,7 +14,7 @@
#import "NSData+Zlib.h"
#import <zlib.h>
#include <zlib.h>
@implementation NSData (Zlib)
@@ -29,7 +29,7 @@
stream.total_out = 0;
stream.avail_out = 0;
int chunkSize = 16384;
NSUInteger chunkSize = 16384;
int windowSize = 15;
if (includeHeader) {
@@ -63,4 +63,4 @@
return [self compressIncludingGzipHeader:YES];
}
@end
@end

View File

@@ -27,7 +27,7 @@
/// If set, this is the user-agent to send with requests, otherwise remains the default
/// CFNetwork-based name.
///
@property(nonatomic) NSString *userAgent;
@property(copy, nonatomic) NSString *userAgent;
///
/// If set to YES, this session refuses redirect requests. Defaults to NO.
@@ -38,24 +38,24 @@
/// If set, the server that we connect to _must_ match this string. Redirects to other
/// hosts will not be allowed.
///
@property(nonatomic) NSString *serverHostname;
@property(copy, nonatomic) NSString *serverHostname;
///
/// This should be PEM data containing one or more certificates to use to verify the server's
/// certificate chain. This will override the trusted roots in the System Roots.
///
@property(nonatomic) NSData *serverRootsPemData;
@property(copy, nonatomic) NSData *serverRootsPemData;
///
/// If set and client certificate authentication is needed, the pkcs#12 file will be loaded
///
@property(nonatomic) NSString *clientCertFile;
@property(copy, nonatomic) NSString *clientCertFile;
///
/// If set and client certificate authentication is needed, the password being used for
/// loading the clientCertFile
///
@property(nonatomic) NSString *clientCertPassword;
@property(copy, nonatomic) NSString *clientCertPassword;
///
/// If set and client certificate authentication is needed, will search the keychain for a
@@ -65,7 +65,7 @@
/// @note If this property is not set and neither is |clientCertIssuerCn|, the allowed issuers
/// provided by the server will be used to find a matching certificate.
///
@property(nonatomic) NSString *clientCertCommonName;
@property(copy, nonatomic) NSString *clientCertCommonName;
///
/// If set and client certificate authentication is needed, will search the keychain for a
@@ -76,7 +76,7 @@
/// @note If this property is not set and neither is |clientCertCommonName|, the allowed issuers
/// provided by the server will be used to find a matching certificate.
///
@property(nonatomic) NSString *clientCertIssuerCn;
@property(copy, nonatomic) NSString *clientCertIssuerCn;
/// Designated initializer
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration;

View File

@@ -14,7 +14,7 @@
#import "SNTAuthenticatingURLSession.h"
#import "SNTCertificate.h"
#import "MOLCertificate.h"
#import "SNTDERDecoder.h"
#import "SNTLogging.h"
@@ -31,7 +31,7 @@
}
- (instancetype)init {
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
[config setTLSMinimumSupportedProtocol:kTLSProtocol12];
[config setHTTPShouldUsePipelining:YES];
return [self initWithSessionConfiguration:config];
@@ -58,24 +58,24 @@
NSURLProtectionSpace *protectionSpace = challenge.protectionSpace;
if (challenge.previousFailureCount > 0) {
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
return;
}
if (self.serverHostname && ![self.serverHostname isEqual:protectionSpace.host]) {
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
return;
}
if (![protectionSpace.protocol isEqual:NSURLProtectionSpaceHTTPS]) {
LOGE(@"%@ is not a secure protocol", protectionSpace.protocol);
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
return;
}
if (!protectionSpace.receivesCredentialSecurely) {
LOGE(@"Secure authentication or protocol cannot be established.");
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
return;
}
@@ -88,7 +88,7 @@
return;
} else {
LOGE(@"Server asked for client authentication but no usable client certificate found.");
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
return;
}
} else if (authMethod == NSURLAuthenticationMethodServerTrust) {
@@ -98,7 +98,7 @@
return;
} else {
LOGE(@"Unable to verify server identity.");
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
return;
}
}
@@ -112,6 +112,8 @@
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest *))completionHandler {
if (self.refusesRedirects) {
LOGD(@"Rejected redirection to: %@", request.URL);
[task cancel]; // without this, the connection hangs until timeout!?!
completionHandler(NULL);
} else {
completionHandler(request);
@@ -136,7 +138,7 @@
///
- (NSURLCredential *)clientCredentialForProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
__block OSStatus err = errSecSuccess;
__block SecIdentityRef foundIdentity;
__block SecIdentityRef foundIdentity = NULL;
if (self.clientCertFile) {
NSError *error;
@@ -169,7 +171,8 @@
err = SecItemCopyMatching((__bridge CFDictionaryRef)@{
(id)kSecClass : (id)kSecClassIdentity,
(id)kSecReturnRef : @YES,
(id)kSecMatchLimit : (id)kSecMatchLimitAll }, (CFTypeRef *)&cfIdentities);
(id)kSecMatchLimit : (id)kSecMatchLimitAll
}, (CFTypeRef *)&cfIdentities);
if (err != errSecSuccess) {
LOGD(@"Client Trust: Failed to load client identities, SecItemCopyMatching returned: %d",
@@ -190,7 +193,7 @@
return;
}
SNTCertificate *clientCert = [[SNTCertificate alloc] initWithSecCertificateRef:certificate];
MOLCertificate *clientCert = [[MOLCertificate alloc] initWithSecCertificateRef:certificate];
CFRelease(certificate);
// Switch identity finding method depending on config
@@ -213,12 +216,16 @@
} else {
for (NSData *allowedIssuer in protectionSpace.distinguishedNames) {
SNTDERDecoder *decoder = [[SNTDERDecoder alloc] initWithData:allowedIssuer];
if (!decoder) continue;
if (!decoder) {
LOGW(@"Unable to decode allowed distinguished name.");
continue;
}
if ([clientCert.issuerCommonName isEqual:decoder.commonName] &&
[clientCert.issuerCountryName isEqual:decoder.countryName] &&
[clientCert.issuerOrgName isEqual:decoder.organizationName] &&
[clientCert.issuerOrgUnit isEqual:decoder.organizationalUnit]) {
foundIdentity = identityRef;
CFRetain(foundIdentity);
*stop = YES;
@@ -232,7 +239,7 @@
if (foundIdentity) {
SecCertificateRef certificate = NULL;
err = SecIdentityCopyCertificate(foundIdentity, &certificate);
SNTCertificate *clientCert = [[SNTCertificate alloc] initWithSecCertificateRef:certificate];
MOLCertificate *clientCert = [[MOLCertificate alloc] initWithSecCertificateRef:certificate];
LOGD(@"Client Trust: Valid client identity %@.", clientCert);
NSURLCredential *cred =
[NSURLCredential credentialWithIdentity:foundIdentity
@@ -270,11 +277,11 @@
if (self.serverRootsPemData) {
NSString *pemStrings = [[NSString alloc] initWithData:self.serverRootsPemData
encoding:NSASCIIStringEncoding];
NSArray *certs = [SNTCertificate certificatesFromPEM:pemStrings];
NSArray *certs = [MOLCertificate certificatesFromPEM:pemStrings];
// Make a new array of the SecCertificateRef's from the SNTCertificate's.
// Make a new array of the SecCertificateRef's from the MOLCertificate's.
NSMutableArray *certRefs = [[NSMutableArray alloc] initWithCapacity:certs.count];
for (SNTCertificate *cert in certs) {
for (MOLCertificate *cert in certs) {
[certRefs addObject:(id)cert.certRef];
}
@@ -295,9 +302,9 @@
}
// Print details about the server's leaf certificate.
SecCertificateRef firstCert = SecTrustGetCertificateAtIndex(protectionSpace.serverTrust, 0);
SecCertificateRef firstCert = SecTrustGetCertificateAtIndex(serverTrust, 0);
if (firstCert) {
SNTCertificate *cert = [[SNTCertificate alloc] initWithSecCertificateRef:firstCert];
MOLCertificate *cert = [[MOLCertificate alloc] initWithSecCertificateRef:firstCert];
LOGD(@"Server Trust: Server leaf cert: %@", cert);
}

View File

@@ -35,7 +35,7 @@
@implementation SNTCommandSync
REGISTER_COMMAND_NAME(@"sync");
REGISTER_COMMAND_NAME(@"sync")
+ (BOOL)requiresRoot {
return NO;
@@ -46,7 +46,7 @@ REGISTER_COMMAND_NAME(@"sync");
}
+ (NSString *)shortHelpText {
return @"Synchronizes Santa with the server.";
return @"Synchronizes Santa with a configured server.";
}
+ (NSString *)longHelpText {
@@ -168,11 +168,11 @@ REGISTER_COMMAND_NAME(@"sync");
completionHandler:^(BOOL success) {
if (success) {
LOGI(@"Log upload complete");
[self eventUpload];
} else {
LOGE(@"Log upload failed, aborting run");
exit(1);
LOGE(@"Log upload failed, continuing anyway");
}
[self eventUpload];
}];
}
@@ -198,11 +198,11 @@ REGISTER_COMMAND_NAME(@"sync");
daemonConn:self.daemonConn
completionHandler:^(BOOL success) {
if (success) {
LOGI(@"Event upload complete");
exit(0);
LOGI(@"Event upload complete");
exit(0);
} else {
LOGW(@"Event upload failed");
exit(1);
LOGW(@"Event upload failed");
exit(1);
}
}];
}

View File

@@ -30,6 +30,8 @@ extern NSString * const kClientMode;
extern NSString * const kClientModeMonitor;
extern NSString * const kClientModeLockdown;
extern NSString * const kCleanSync;
extern NSString * const kWhitelistRegex;
extern NSString * const kBlacklistRegex;
extern NSString * const kEvents;
extern NSString * const kFileSHA256;
@@ -55,6 +57,7 @@ extern NSString * const kFileBundleVersion;
extern NSString * const kFileBundleShortVersionString;
extern NSString * const kPID;
extern NSString * const kPPID;
extern NSString * const kParentName;
extern NSString * const kSigningChain;
extern NSString * const kCertSHA256;
extern NSString * const kCertCN;
@@ -62,6 +65,10 @@ extern NSString * const kCertOrg;
extern NSString * const kCertOU;
extern NSString * const kCertValidFrom;
extern NSString * const kCertValidUntil;
extern NSString * const kQuarantineDataURL;
extern NSString * const kQuarantineRefererURL;
extern NSString * const kQuarantineTimestamp;
extern NSString * const kQuarantineAgentBundleID;
extern NSString * const kLogUploadField;
@@ -77,3 +84,5 @@ extern NSString * const kRuleTypeBinary;
extern NSString * const kRuleTypeCertificate;
extern NSString * const kRuleCustomMsg;
extern NSString * const kCursor;
extern NSString * const kBackoffInterval;

View File

@@ -32,6 +32,8 @@ NSString * const kClientMode = @"client_mode";
NSString * const kClientModeMonitor = @"MONITOR";
NSString * const kClientModeLockdown = @"LOCKDOWN";
NSString * const kCleanSync = @"clean_sync";
NSString * const kWhitelistRegex = @"whitelist_regex";
NSString * const kBlacklistRegex = @"blacklist_regex";
NSString * const kEvents = @"events";
NSString * const kFileSHA256 = @"file_sha256";
@@ -57,6 +59,7 @@ NSString * const kFileBundleVersion = @"file_bundle_version";
NSString * const kFileBundleShortVersionString = @"file_bundle_version_string";
NSString * const kPID = @"pid";
NSString * const kPPID = @"ppid";
NSString * const kParentName = @"parent_name";
NSString * const kSigningChain = @"signing_chain";
NSString * const kCertSHA256 = @"sha256";
NSString * const kCertCN = @"cn";
@@ -64,6 +67,10 @@ NSString * const kCertOrg = @"org";
NSString * const kCertOU = @"ou";
NSString * const kCertValidFrom = @"valid_from";
NSString * const kCertValidUntil = @"valid_until";
NSString * const kQuarantineDataURL = @"quarantine_data_url";
NSString * const kQuarantineRefererURL = @"quarantine_referer_url";
NSString * const kQuarantineTimestamp = @"quarantine_timestamp";
NSString * const kQuarantineAgentBundleID = @"quarantine_agent_bundle_id";
NSString * const kLogUploadField = @"files";
@@ -79,3 +86,5 @@ NSString * const kRuleTypeBinary = @"BINARY";
NSString * const kRuleTypeCertificate = @"CERTIFICATE";
NSString * const kRuleCustomMsg = @"custom_msg";
NSString * const kCursor = @"cursor";
NSString * const kBackoffInterval = @"backoff";

View File

@@ -16,7 +16,8 @@
#include "SNTLogging.h"
#import "SNTCertificate.h"
#import "NSData+Zlib.h"
#import "MOLCertificate.h"
#import "SNTCommandSyncConstants.h"
#import "SNTCommandSyncState.h"
#import "SNTStoredEvent.h"
@@ -71,7 +72,7 @@
+ (void)uploadEventsFromArray:(NSArray *)events
toURL:(NSURL *)url
inSession:(NSURLSession *)session
batchSize:(int32_t)batchSize
batchSize:(NSUInteger)batchSize
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler {
NSMutableArray *uploadEvents = [[NSMutableArray alloc] init];
@@ -97,6 +98,13 @@
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
[req setHTTPMethod:@"POST"];
[req setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSData *compressed = [requestBody zlibCompressed];
if (compressed) {
requestBody = compressed;
[req setValue:@"zlib" forHTTPHeaderField:@"Content-Encoding"];
}
[req setHTTPBody:requestBody];
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
@@ -104,12 +112,12 @@
NSError *error) {
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %d %@",
LOGE(@"HTTP Response: %ld %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
handler(NO);
} else {
LOGI(@"Uploaded %d events", eventIds.count);
LOGI(@"Uploaded %lu events", eventIds.count);
[[daemonConn remoteObjectProxy] databaseRemoveEventsWithIDs:eventIds];
@@ -164,10 +172,16 @@
ADDKEY(newEvent, kPID, event.pid);
ADDKEY(newEvent, kPPID, event.ppid);
ADDKEY(newEvent, kParentName, event.parentName);
ADDKEY(newEvent, kQuarantineDataURL, event.quarantineDataURL);
ADDKEY(newEvent, kQuarantineRefererURL, event.quarantineRefererURL);
ADDKEY(newEvent, kQuarantineTimestamp, @([event.quarantineTimestamp timeIntervalSince1970]));
ADDKEY(newEvent, kQuarantineAgentBundleID, event.quarantineAgentBundleID);
NSMutableArray *signingChain = [NSMutableArray arrayWithCapacity:event.signingChain.count];
for (int i = 0; i < event.signingChain.count; i++) {
SNTCertificate *cert = [event.signingChain objectAtIndex:i];
for (NSUInteger i = 0; i < event.signingChain.count; i++) {
MOLCertificate *cert = [event.signingChain objectAtIndex:i];
NSMutableDictionary *certDict = [NSMutableDictionary dictionary];
ADDKEY(certDict, kCertSHA256, cert.SHA256);

View File

@@ -37,10 +37,29 @@
[NSString stringWithFormat:@"multipart/form-data; charset=UTF-8; boundary=%@", boundary];
[req setValue:contentType forHTTPHeaderField:@"Content-Type"];
NSArray *logsToUpload = [self logsToUpload];
// Upload the logs
[[session uploadTaskWithRequest:req
fromData:[self requestBodyWithLogs:logsToUpload andBoundary:boundary]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %ld %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
handler(NO);
} else {
LOGI(@"Uploaded %lu logs", [logsToUpload count]);
handler(YES);
}
}] resume];
}
+ (NSData *)requestBodyWithLogs:(NSArray *)logsToUpload andBoundary:(NSString *)boundary {
// Prepare the body of the request, encoded as a multipart/form-data.
// Along the way, gzip the individual log files and append .gz to their filenames.
NSMutableData *reqBody = [[NSMutableData alloc] init];
NSArray *logsToUpload = [SNTCommandSyncLogUpload logsToUpload];
for (NSString *log in logsToUpload) {
[reqBody appendData:
[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
@@ -48,30 +67,16 @@
[[NSString stringWithFormat:@"Content-Disposition: form-data; "
@"name=\"%@\"; "
@"filename=\"%@.gz\"\r\n", kLogUploadField, [log lastPathComponent]]
dataUsingEncoding:NSUTF8StringEncoding]];
dataUsingEncoding:NSUTF8StringEncoding]];
[reqBody appendData:
[@"Content-Type: application/x-gzip\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[reqBody appendData:[[NSData dataWithContentsOfFile:log] gzipCompressed]];
[reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
[reqBody appendData:
[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// Upload the logs
[[session uploadTaskWithRequest:req
fromData:reqBody
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %d %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
handler(NO);
} else {
LOGI(@"Uploaded %d logs", [logsToUpload count]);
handler(YES);
}
}] resume];
return reqBody;
}
+ (NSArray *)logsToUpload {

View File

@@ -18,6 +18,8 @@
#import "SNTCommandSyncConstants.h"
#import "SNTCommandSyncState.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@implementation SNTCommandSyncPostflight
@@ -33,15 +35,33 @@
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %d %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
handler(NO);
} else {
handler(YES);
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %ld %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
handler(NO);
} else {
NSDictionary *r = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if (syncState.newClientMode) {
[[daemonConn remoteObjectProxy] setClientMode:syncState.newClientMode reply:^{}];
}
NSString *backoffInterval = r[kBackoffInterval];
if (backoffInterval) {
[[daemonConn remoteObjectProxy] setNextSyncInterval:[backoffInterval intValue] reply:^{}];
}
if (syncState.cleanSync) {
[[daemonConn remoteObjectProxy] setSyncCleanRequired:NO reply:^{}];
}
// Update last sync success
[[daemonConn remoteObjectProxy] setSyncLastSuccess:[NSDate date] reply:^{}];
handler(YES);
}
}] resume];
}

View File

@@ -17,8 +17,10 @@
#include "SNTKernelCommon.h"
#include "SNTLogging.h"
#import "NSData+Zlib.h"
#import "SNTCommandSyncConstants.h"
#import "SNTCommandSyncState.h"
#import "SNTConfigurator.h"
#import "SNTSystemInfo.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@@ -40,24 +42,34 @@
requestDict[kOSBuild] = [SNTSystemInfo osBuild];
requestDict[kPrimaryUser] = syncState.machineOwner;
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--clean"]) {
// If user requested it or we've never had a successful sync, try from a clean slate.
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--clean"] ||
[[SNTConfigurator configurator] syncCleanRequired]) {
requestDict[kRequestCleanSync] = @YES;
}
NSData *requestBody = [NSJSONSerialization dataWithJSONObject:requestDict
options:0
error:nil];
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
[req setHTTPMethod:@"POST"];
[req setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[req setHTTPBody:requestBody];
NSData *compressed = [requestBody zlibCompressed];
if (compressed) {
requestBody = compressed;
[req setValue:@"zlib" forHTTPHeaderField:@"Content-Encoding"];
}
[req setHTTPBody:requestBody];
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %d %@",
LOGE(@"HTTP Response: %ld %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
handler(NO);
@@ -68,14 +80,21 @@
syncState.uploadLogURL = [NSURL URLWithString:r[kUploadLogsURL]];
if ([r[kClientMode] isEqual:kClientModeMonitor]) {
[[daemonConn remoteObjectProxy] setClientMode:CLIENTMODE_MONITOR reply:^{}];
syncState.newClientMode = CLIENTMODE_MONITOR;
} else if ([r[kClientMode] isEqual:kClientModeLockdown]) {
[[daemonConn remoteObjectProxy] setClientMode:CLIENTMODE_LOCKDOWN reply:^{}];
syncState.newClientMode = CLIENTMODE_LOCKDOWN;
}
if ([r[kWhitelistRegex] isKindOfClass:[NSString class]]) {
[[daemonConn remoteObjectProxy] setWhitelistPathRegex:r[kWhitelistRegex] reply:^{}];
}
if ([r[kBlacklistRegex] isKindOfClass:[NSString class]]) {
[[daemonConn remoteObjectProxy] setBlacklistPathRegex:r[kBlacklistRegex] reply:^{}];
}
if ([r[kCleanSync] boolValue]) {
syncState.cleanSync = YES;
LOGD(@"Clean sync requested by server");
}
handler(YES);

View File

@@ -60,49 +60,24 @@
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if ([(NSHTTPURLResponse *)response statusCode] != 200) {
LOGD(@"HTTP Response Code: %d", [(NSHTTPURLResponse *)response statusCode]);
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %ld %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
handler(NO);
} else {
NSDictionary *resp = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if (!resp) {
LOGE(@"Failed to decode server's response");
handler(NO);
return;
}
NSArray *receivedRules = resp[kRules];
for (NSDictionary *rule in receivedRules) {
if (![rule isKindOfClass:[NSDictionary class]]) continue;
SNTRule *newRule = [[SNTRule alloc] init];
newRule.shasum = rule[kRuleSHA256];
if ([rule[kRulePolicy] isEqual:kRulePolicyWhitelist]) {
newRule.state = RULESTATE_WHITELIST;
} else if ([rule[kRulePolicy] isEqual:kRulePolicyBlacklist]) {
newRule.state = RULESTATE_BLACKLIST;
} else if ([rule[kRulePolicy] isEqual:kRulePolicySilentBlacklist]) {
newRule.state = RULESTATE_SILENT_BLACKLIST;
} else if ([rule[kRulePolicy] isEqual:kRulePolicyRemove]) {
newRule.state = RULESTATE_REMOVE;
} else {
continue;
}
if ([rule[kRuleType] isEqual:kRuleTypeBinary]) {
newRule.type = RULETYPE_BINARY;
} else if ([rule[kRuleType] isEqual:kRuleTypeCertificate]) {
newRule.type = RULETYPE_CERT;
} else {
continue;
}
NSString *customMsg = rule[kRuleCustomMsg];
if (customMsg) {
newRule.customMsg = customMsg;
}
[syncState.downloadedRules addObject:newRule];
SNTRule *r = [self ruleFromDictionary:rule];
if (r) [syncState.downloadedRules addObject:r];
}
if (resp[kCursor]) {
@@ -113,17 +88,61 @@
daemonConn:daemonConn
completionHandler:handler];
} else {
[[daemonConn remoteObjectProxy] databaseRuleAddRules:syncState.downloadedRules
cleanSlate:syncState.cleanSync
reply:^{
if (syncState.downloadedRules.count) {
LOGI(@"Added %d rule(s)", syncState.downloadedRules.count);
}
handler(YES);
}];
if (syncState.downloadedRules.count) {
[[daemonConn remoteObjectProxy] databaseRuleAddRules:syncState.downloadedRules
cleanSlate:syncState.cleanSync
reply:^(BOOL success) {
if (success) {
LOGI(@"Added %lu rule(s)", syncState.downloadedRules.count);
handler(YES);
} else {
LOGE(@"Failed to add rules to database");
handler(NO);
}
}];
} else {
handler(YES);
}
}
}
}] resume];
}
+ (SNTRule *)ruleFromDictionary:(NSDictionary *)dict {
if (![dict isKindOfClass:[NSDictionary class]]) return nil;
SNTRule *newRule = [[SNTRule alloc] init];
newRule.shasum = dict[kRuleSHA256];
if (newRule.shasum.length != 64) return nil;
NSString *policyString = dict[kRulePolicy];
if ([policyString isEqual:kRulePolicyWhitelist]) {
newRule.state = RULESTATE_WHITELIST;
} else if ([policyString isEqual:kRulePolicyBlacklist]) {
newRule.state = RULESTATE_BLACKLIST;
} else if ([policyString isEqual:kRulePolicySilentBlacklist]) {
newRule.state = RULESTATE_SILENT_BLACKLIST;
} else if ([policyString isEqual:kRulePolicyRemove]) {
newRule.state = RULESTATE_REMOVE;
} else {
return nil;
}
NSString *ruleTypeString = dict[kRuleType];
if ([ruleTypeString isEqual:kRuleTypeBinary]) {
newRule.type = RULETYPE_BINARY;
} else if ([ruleTypeString isEqual:kRuleTypeCertificate]) {
newRule.type = RULETYPE_CERT;
} else {
return nil;
}
NSString *customMsg = dict[kRuleCustomMsg];
if (customMsg.length) {
newRule.customMsg = customMsg;
}
return newRule;
}
@end

View File

@@ -12,6 +12,8 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
/// An instance of this class is passed to each stage of the sync process for storing data
/// that might be needed in later stages.
@interface SNTCommandSyncState : NSObject
@@ -20,13 +22,16 @@
@property NSURL *syncBaseURL;
/// Machine identifier and owner
@property NSString *machineID;
@property NSString *machineOwner;
@property(copy) NSString *machineID;
@property(copy) NSString *machineOwner;
/// Clean sync flag, sent from server. If True, all existing rules
/// should be deleted before inserting any new rules.
@property BOOL cleanSync;
/// New client mode sent from server
@property santa_clientmode_t newClientMode;
/// Batch size for uploading events, sent from server
@property int32_t eventBatchSize;

View File

@@ -134,8 +134,10 @@
data.length,
kSequenceOfSetOfOIDValueTemplate,
&a);
SecAsn1CoderRelease(coder);
if (err != errSecSuccess) return nil;
if (err != errSecSuccess) {
SecAsn1CoderRelease(coder);
return nil;
}
// The data is decoded but now it's in a number of embedded structs.
// Massage that into a nice dictionary of OID->String pairs.
@@ -145,7 +147,10 @@
OIDKeyValue *keyValue = anAttr->vals[0];
// Sanity check
if (keyValue->value.Length > data.length) return nil;
if (keyValue->value.Length > data.length) {
SecAsn1CoderRelease(coder);
return nil;
}
// Get the string value. First try creating as a UTF-8 string. If that fails,
// fallback to trying as an ASCII string. If it still doesn't work, continue on
@@ -169,6 +174,7 @@
dict[objectId] = valueString;
}
SecAsn1CoderRelease(coder);
return dict;
}

View File

@@ -16,6 +16,7 @@
#include <IOKit/kext/KextManager.h>
#import "SNTCommonEnums.h"
#import "SNTFileInfo.h"
#import "SNTKernelCommon.h"
#import "SNTXPCConnection.h"
@@ -25,7 +26,7 @@
@implementation SNTCommandVersion
REGISTER_COMMAND_NAME(@"version");
REGISTER_COMMAND_NAME(@"version")
+ (BOOL)requiresRoot {
return NO;
@@ -40,14 +41,30 @@ REGISTER_COMMAND_NAME(@"version");
}
+ (NSString *)longHelpText {
return nil;
return (@"Show versions of all Santa components.\n"
@" Use --json to output in JSON format.");
}
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
printf("%-15s | %s\n", "santa-driver", [[self santaKextVersion] UTF8String]);
printf("%-15s | %s\n", "santad", [[self santadVersion] UTF8String]);
printf("%-15s | %s\n", "santactl", [[self santactlVersion] UTF8String]);
printf("%-15s | %s\n", "SantaGUI", [[self santaAppVersion] UTF8String]);
if ([arguments containsObject:@"--json"]) {
NSDictionary *versions = @{
@"santa-driver": [self santaKextVersion],
@"santad": [self santadVersion],
@"santactl": [self santactlVersion],
@"SantaGUI": [self santaAppVersion],
};
NSData *versionsData = [NSJSONSerialization dataWithJSONObject:versions
options:NSJSONWritingPrettyPrinted
error:nil];
NSString *versionsStr = [[NSString alloc] initWithData:versionsData
encoding:NSUTF8StringEncoding];
printf("%s\n", [versionsStr UTF8String]);
} else {
printf("%-15s | %s\n", "santa-driver", [[self santaKextVersion] UTF8String]);
printf("%-15s | %s\n", "santad", [[self santadVersion] UTF8String]);
printf("%-15s | %s\n", "santactl", [[self santactlVersion] UTF8String]);
printf("%-15s | %s\n", "SantaGUI", [[self santaAppVersion] UTF8String]);
}
exit(0);
}
@@ -57,12 +74,11 @@ REGISTER_COMMAND_NAME(@"version");
(__bridge CFArrayRef)@[ @"CFBundleVersion" ])
);
if (loadedKexts[@(USERCLIENT_ID)] && loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"]) {
if (loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"]) {
return loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"];
}
SNTFileInfo *driverInfo =
[[SNTFileInfo alloc] initWithPath:@"/Library/Extensions/santa-driver.kext"];
SNTFileInfo *driverInfo = [[SNTFileInfo alloc] initWithPath:@(kKextPath)];
if (driverInfo) {
return [driverInfo.bundleVersion stringByAppendingString:@" (unloaded)"];
}
@@ -71,13 +87,13 @@ REGISTER_COMMAND_NAME(@"version");
}
+ (NSString *)santadVersion {
SNTFileInfo *daemonInfo = [[SNTFileInfo alloc] initWithPath:@"/usr/libexec/santad"];
SNTFileInfo *daemonInfo = [[SNTFileInfo alloc] initWithPath:@(kSantaDPath)];
return daemonInfo.bundleVersion;
}
+ (NSString *)santaAppVersion {
SNTFileInfo *guiInfo =
[[SNTFileInfo alloc] initWithPath:@"/Applications/Santa.app/Contents/MacOS/Santa"];
[[SNTFileInfo alloc] initWithPath:@"/Applications/Santa.app/Contents/MacOS/Santa"];
return guiInfo.bundleVersion;
}

View File

@@ -1,3 +1,3 @@
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#endif
#endif

View File

@@ -12,7 +12,6 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include <pwd.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -25,6 +24,7 @@
#import "SNTDaemonControlController.h"
#import "SNTDatabaseController.h"
#import "SNTDriverManager.h"
#import "SNTEventLog.h"
#import "SNTEventTable.h"
#import "SNTExecutionController.h"
#import "SNTFileWatcher.h"
@@ -35,6 +35,7 @@
@interface SNTApplication ()
@property SNTDriverManager *driverManager;
@property SNTEventLog *eventLog;
@property SNTEventTable *eventTable;
@property SNTExecutionController *execController;
@property SNTFileWatcher *configFileWatcher;
@@ -60,13 +61,13 @@
// Initialize tables
_ruleTable = [SNTDatabaseController ruleTable];
if (! _ruleTable) {
if (!_ruleTable) {
LOGE(@"Failed to initialize rule table.");
return nil;
}
_eventTable = [SNTDatabaseController eventTable];
if (! _eventTable) {
if (!_eventTable) {
LOGE(@"Failed to initialize event table.");
return nil;
}
@@ -94,11 +95,14 @@
chmod([kDefaultConfigFilePath fileSystemRepresentation], 0644);
}];
_eventLog = [[SNTEventLog alloc] init];
// Initialize the binary checker object
_execController = [[SNTExecutionController alloc] initWithDriverManager:_driverManager
ruleTable:_ruleTable
eventTable:_eventTable
notifierConnection:_notifierConnection];
notifierConnection:_notifierConnection
eventLog:_eventLog];
if (!_execController) return nil;
}
@@ -109,8 +113,8 @@
LOGI(@"Connected to driver, activating.");
// Create a concurrent queue to put requests on, then set its priority to high.
dispatch_queue_t q = dispatch_queue_create("com.google.santad.driver_queue",
DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t q =
dispatch_queue_create("com.google.santad.driver_queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(q, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
[self.driverManager listenWithBlock:^(santa_message_t message) {
@@ -118,24 +122,31 @@
switch (message.action) {
case ACTION_REQUEST_SHUTDOWN: {
LOGI(@"Driver requested a shutdown");
// Sleep before exiting to give driver chance to ready itself
exit(0);
}
case ACTION_REQUEST_CHECKBW: {
// Validate the binary aynchronously on a concurrent queue so we don't
// hold up other execution requests in the background.
case ACTION_NOTIFY_DELETE:
case ACTION_NOTIFY_EXCHANGE:
case ACTION_NOTIFY_LINK:
case ACTION_NOTIFY_RENAME:
case ACTION_NOTIFY_WRITE: {
dispatch_async(q, ^{
struct passwd *user = getpwuid(message.userId);
NSString *userName;
if (user) {
userName = @(user->pw_name);
}
[self.execController validateBinaryWithPath:@(message.path)
userName:userName
pid:@(message.pid)
ppid:@(message.ppid)
vnodeId:message.vnode_id];
NSRegularExpression *re = [[SNTConfigurator configurator] fileChangesRegex];
NSString *path = @(message.path);
if ([re numberOfMatchesInString:path options:0 range:NSMakeRange(0, path.length)]) {
[self.eventLog logFileModification:message];
}
});
break;
}
case ACTION_NOTIFY_EXEC: {
dispatch_async(q, ^{
[self.eventLog logAllowedExecution:message];
});
break;
}
case ACTION_REQUEST_CHECKBW: {
dispatch_async(q, ^{
[self.execController validateBinaryWithMessage:message];
});
break;
}

View File

@@ -0,0 +1,32 @@
/// Copyright 2015 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
///
/// Store information about executions from decision making for later logging.
///
@interface SNTCachedDecision : NSObject
@property uint64_t vnodeId;
@property santa_eventstate_t decision;
@property NSString *decisionExtra;
@property NSString *sha256;
@property NSString *certSHA256;
@property NSString *certCommonName;
@property NSString *customMsg;
@property BOOL silentBlock;
@end

View File

@@ -0,0 +1,18 @@
/// Copyright 2015 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTCachedDecision.h"
@implementation SNTCachedDecision
@end

View File

@@ -17,25 +17,69 @@
#import "SNTConfigurator.h"
#import "SNTDatabaseController.h"
#import "SNTDriverManager.h"
#import "SNTDropRootPrivs.h"
#import "SNTEventTable.h"
#import "SNTLogging.h"
#import "SNTRule.h"
#import "SNTRuleTable.h"
// Globals used by the santad watchdog thread
uint64_t watchdogCPUEvents = 0;
uint64_t watchdogRAMEvents = 0;
@interface SNTDaemonControlController ()
@property dispatch_source_t syncTimer;
@end
@implementation SNTDaemonControlController
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager {
self = [super init];
if (self) {
_driverManager = driverManager;
_syncTimer = [self createSyncTimer];
[self rescheduleSyncSecondsFromNow:30];
}
return self;
}
- (dispatch_source_t)createSyncTimer {
dispatch_source_t syncTimerQ = dispatch_source_create(
DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
dispatch_source_set_event_handler(syncTimerQ, ^{
[self rescheduleSyncSecondsFromNow:600];
if (![[SNTConfigurator configurator] syncBaseURL]) return;
[[SNTConfigurator configurator] setSyncBackOff:NO];
if (fork() == 0) {
// Ensure we have no privileges
if (!DropRootPrivileges()) {
_exit(EPERM);
}
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "--syslog", NULL));
}
});
dispatch_resume(syncTimerQ);
return syncTimerQ;
}
- (void)rescheduleSyncSecondsFromNow:(uint64_t)seconds {
uint64_t interval = seconds * NSEC_PER_SEC;
uint64_t leeway = (seconds * 0.05) * NSEC_PER_SEC;
dispatch_source_set_timer(self.syncTimer, dispatch_walltime(NULL, interval), interval, leeway);
}
#pragma mark Kernel ops
- (void)cacheCount:(void (^)(uint64_t))reply; {
uint64_t count = [self.driverManager cacheCount];
- (void)cacheCount:(void (^)(int64_t))reply {
int64_t count = [self.driverManager cacheCount];
reply(count);
}
@@ -45,17 +89,19 @@
#pragma mark Database ops
- (void)databaseRuleCounts:(void (^)(uint64_t binary, uint64_t certificate))reply {
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate))reply {
SNTRuleTable *rdb = [SNTDatabaseController ruleTable];
reply([rdb binaryRuleCount], [rdb certificateRuleCount]);
}
- (void)databaseRuleAddRule:(SNTRule *)rule cleanSlate:(BOOL)cleanSlate reply:(void (^)())reply {
- (void)databaseRuleAddRule:(SNTRule *)rule cleanSlate:(BOOL)cleanSlate
reply:(void (^)(BOOL success))reply {
[self databaseRuleAddRules:@[ rule ] cleanSlate:cleanSlate reply:reply];
}
- (void)databaseRuleAddRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate reply:(void (^)())reply {
[[SNTDatabaseController ruleTable] addRules:rules cleanSlate:cleanSlate];
- (void)databaseRuleAddRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate
reply:(void (^)(BOOL success))reply {
BOOL success = [[SNTDatabaseController ruleTable] addRules:rules cleanSlate:cleanSlate];
// If any rules were added that were not whitelist, flush cache.
NSPredicate *p = [NSPredicate predicateWithFormat:@"SELF.state != %d", RULESTATE_WHITELIST];
@@ -64,10 +110,10 @@
[self.driverManager flushCache];
}
reply();
reply(success);
}
- (void)databaseEventCount:(void (^)(uint64_t count))reply {
- (void)databaseEventCount:(void (^)(int64_t count))reply {
reply([[SNTDatabaseController eventTable] pendingEventsCount]);
}
@@ -94,4 +140,44 @@
reply();
}
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply {
[self rescheduleSyncSecondsFromNow:seconds];
[[SNTConfigurator configurator] setSyncBackOff:YES];
reply();
}
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply {
[[SNTConfigurator configurator] setSyncLastSuccess:date];
reply();
}
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)())reply {
[[SNTConfigurator configurator] setSyncCleanRequired:cleanReqd];
reply();
}
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)())reply {
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
options:0
error:NULL];
[[SNTConfigurator configurator] setWhitelistPathRegex:re];
reply();
}
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)())reply {
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
options:0
error:NULL];
[[SNTConfigurator configurator] setBlacklistPathRegex:re];
reply();
}
- (void)watchdogCPUEvents:(void (^)(uint64_t))reply {
reply(watchdogCPUEvents);
}
- (void)watchdogRAMEvents:(void (^)(uint64_t))reply {
reply(watchdogRAMEvents);
}
@end

View File

@@ -14,6 +14,9 @@
#import "SNTDatabaseController.h"
#include <sys/types.h>
#include <sys/stat.h>
#import "SNTEventTable.h"
#import "SNTLogging.h"
#import "SNTRuleTable.h"
@@ -31,11 +34,11 @@ static NSString * const kEventsDatabaseName = @"events.db";
[self createDatabasePath];
NSString *fullPath = [kDatabasePath stringByAppendingPathComponent:kEventsDatabaseName];
eventDatabaseQueue = [[FMDatabaseQueue alloc] initWithPath:fullPath];
chown([fullPath UTF8String], 0, 0);
chmod([fullPath UTF8String], 0600);
#ifndef DEBUG
[eventDatabaseQueue inDatabase:^(FMDatabase *db) {
db.logsErrors = NO;
}];
#ifndef DEBUG
[eventDatabaseQueue inDatabase:^(FMDatabase *db) { db.logsErrors = NO; }];
#endif
});
@@ -49,11 +52,11 @@ static NSString * const kEventsDatabaseName = @"events.db";
[self createDatabasePath];
NSString *fullPath = [kDatabasePath stringByAppendingPathComponent:kRulesDatabaseName];
ruleDatabaseQueue = [[FMDatabaseQueue alloc] initWithPath:fullPath];
chown([fullPath UTF8String], 0, 0);
chmod([fullPath UTF8String], 0600);
#ifndef DEBUG
[ruleDatabaseQueue inDatabase:^(FMDatabase *db) {
db.logsErrors = NO;
}];
#ifndef DEBUG
[ruleDatabaseQueue inDatabase:^(FMDatabase *db) { db.logsErrors = NO; }];
#endif
});
return [[SNTRuleTable alloc] initWithDatabaseQueue:ruleDatabaseQueue];
@@ -65,9 +68,11 @@ static NSString * const kEventsDatabaseName = @"events.db";
+ (void)createDatabasePath {
NSFileManager *fm = [NSFileManager defaultManager];
NSDictionary *attrs = @{ NSFileOwnerAccountName: @"root",
NSFileGroupOwnerAccountName: @"wheel",
NSFilePosixPermissions: @0755 };
NSDictionary *attrs = @{
NSFileOwnerAccountName : @"root",
NSFileGroupOwnerAccountName : @"wheel",
NSFilePosixPermissions : @0755
};
if (![fm fileExistsAtPath:kDatabasePath]) {
[fm createDirectoryAtPath:kDatabasePath

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
// This is imported in the header rather than implementation to saves
// This is imported in the header rather than implementation to save
// classes that use this one from also having to import FMDB stuff.
#import <FMDB/FMDB.h>
@@ -28,7 +28,7 @@
/// is the current version of the table. The return value is the new version of the table. If
/// updating the table failed, return a negative number. If there was no update to apply, return 0.
///
- (int)initializeDatabase:(FMDatabase *)db fromVersion:(int)version;
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version;
///
/// Wrappers around the respective FMDatabaseQueue methods. If the object we initialized with was

View File

@@ -27,7 +27,16 @@
self = [super init];
if (self) {
[db inDatabase:^(FMDatabase *db) {
if (![db goodConnection]) {
[db close];
[[NSFileManager defaultManager] removeItemAtPath:[db databasePath] error:NULL];
[db open];
}
}];
_dbQ = db;
[self updateTableSchema];
}
return self;
@@ -38,20 +47,20 @@
return nil;
}
- (int)initializeDatabase:(FMDatabase *)db fromVersion:(int)version {
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version {
[self doesNotRecognizeSelector:_cmd];
return -1;
return 0;
}
/// Called at the end of initialization to ensure the table in the
/// database exists and uses the latest schema.
- (void)updateTableSchema {
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
int currentVersion = [db userVersion];
int newVersion = [self initializeDatabase:db fromVersion:currentVersion];
uint32_t currentVersion = [db userVersion];
uint32_t newVersion = [self initializeDatabase:db fromVersion:currentVersion];
if (newVersion < 1) return;
LOGD(@"Updated %@ from version %d to %d", [self className], currentVersion, newVersion);
LOGI(@"Updated %@ from version %d to %d", [self className], currentVersion, newVersion);
[db setUserVersion:newVersion];
}];

View File

@@ -83,12 +83,10 @@ static const int MAX_DELAY = 15;
IOServiceClose(_connection);
}
# pragma mark Incoming messages
#pragma mark Incoming messages
- (void)listenWithBlock:(void (^)(santa_message_t message))callback {
kern_return_t kr;
santa_message_t vdata;
UInt32 dataSize;
mach_vm_address_t address = 0;
mach_vm_size_t size = 0;
@@ -120,20 +118,20 @@ static const int MAX_DELAY = 15;
}
self.queueMemory = (IODataQueueMemory *)address;
dataSize = sizeof(vdata);
while (IODataQueueWaitForAvailableData(self.queueMemory,
self.receivePort) == kIOReturnSuccess) {
do {
while (IODataQueueDataAvailable(self.queueMemory)) {
santa_message_t vdata;
uint32_t dataSize = sizeof(vdata);
kr = IODataQueueDequeue(self.queueMemory, &vdata, &dataSize);
if (kr == kIOReturnSuccess) {
callback(vdata);
} else {
LOGD(@"Error receiving data: %d", kr);
LOGE(@"Error dequeuing data: %d", kr);
exit(2);
}
}
}
} while (IODataQueueWaitForAvailableData(self.queueMemory, self.receivePort) == kIOReturnSuccess);
IOConnectUnmapMemory(self.connection, kIODefaultMemoryType, mach_task_self(), address);
mach_port_destroy(mach_task_self(), self.receivePort);

View File

@@ -0,0 +1,31 @@
/// Copyright 2015 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTKernelCommon.h"
@class SNTCachedDecision;
///
/// Logs execution and file write events to syslog
///
@interface SNTEventLog : NSObject
- (void)logFileModification:(santa_message_t)message;
- (void)saveDecisionDetails:(SNTCachedDecision *)cd;
- (void)logDeniedExecution:(SNTCachedDecision *)cd withMessage:(santa_message_t)message;
- (void)logAllowedExecution:(santa_message_t)message;
@end

218
Source/santad/SNTEventLog.m Normal file
View File

@@ -0,0 +1,218 @@
/// Copyright 2015 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTEventLog.h"
#include <libproc.h>
#include <sys/sysctl.h>
#import "SNTCachedDecision.h"
#import "MOLCertificate.h"
#import "SNTCommonEnums.h"
#import "SNTFileInfo.h"
#import "SNTKernelCommon.h"
#import "SNTLogging.h"
@interface SNTEventLog ()
@property NSMutableDictionary *detailStore;
@end
@implementation SNTEventLog
- (instancetype)init {
self = [super init];
if (self) {
_detailStore = [NSMutableDictionary dictionaryWithCapacity:10000];
}
return self;
}
- (void)saveDecisionDetails:(SNTCachedDecision *)cd {
self.detailStore[@(cd.vnodeId)] = cd;
}
- (void)logFileModification:(santa_message_t)message {
NSString *action, *path, *newpath, *sha256, *outStr;
path = @(message.path);
switch (message.action) {
case ACTION_NOTIFY_DELETE: {
action = @"DELETE";
break;
}
case ACTION_NOTIFY_EXCHANGE: {
action = @"EXCHANGE";
newpath = @(message.newpath);
break;
}
case ACTION_NOTIFY_LINK: {
action = @"LINK";
newpath = @(message.newpath);
break;
}
case ACTION_NOTIFY_RENAME: {
action = @"RENAME";
newpath = @(message.newpath);
break;
}
case ACTION_NOTIFY_WRITE: {
action = @"WRITE";
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:path];
if (fileInfo.fileSize < 1024 * 1024) {
sha256 = fileInfo.SHA256;
} else {
sha256 = @"(too large)";
}
break;
}
default: action = @"UNKNOWN"; break;
}
outStr = [NSString stringWithFormat:@"action=%@|path=%@", action, [self sanitizeString:path]];
if (newpath) {
outStr = [outStr stringByAppendingFormat:@"|newpath=%@", [self sanitizeString:newpath]];
}
char ppath[PATH_MAX];
if (proc_pidpath(message.pid, ppath, PATH_MAX) < 1) {
strncpy(ppath, "(null)", 6);
}
outStr =
[outStr stringByAppendingFormat:@"|pid=%d|ppid=%d|process=%s|processpath=%s|uid=%d|gid=%d",
message.pid, message.ppid, message.pname, ppath, message.uid, message.gid];
if (sha256) {
outStr = [outStr stringByAppendingFormat:@"|sha256=%@", sha256];
}
LOGI(@"%@", outStr);
}
- (void)logDeniedExecution:(SNTCachedDecision *)cd withMessage:(santa_message_t)message {
[self logExecution:message withDecision:cd];
}
- (void)logAllowedExecution:(santa_message_t)message {
SNTCachedDecision *cd = self.detailStore[@(message.vnode_id)];
[self logExecution:message withDecision:cd];
}
- (void)logExecution:(santa_message_t)message withDecision:(SNTCachedDecision *)cd {
NSString *d, *r, *args, *outLog;
switch (cd.decision) {
case EVENTSTATE_ALLOW_BINARY:
d = @"ALLOW"; r = @"BINARY"; args = [self argsForPid:message.pid]; break;
case EVENTSTATE_ALLOW_CERTIFICATE:
d = @"ALLOW"; r = @"CERTIFICATE"; args = [self argsForPid:message.pid]; break;
case EVENTSTATE_ALLOW_SCOPE:
d = @"ALLOW"; r = @"SCOPE"; args = [self argsForPid:message.pid]; break;
case EVENTSTATE_ALLOW_UNKNOWN:
d = @"ALLOW"; r = @"UNKNOWN"; args = [self argsForPid:message.pid]; break;
case EVENTSTATE_BLOCK_BINARY:
d = @"DENY"; r = @"BINARY"; break;
case EVENTSTATE_BLOCK_CERTIFICATE:
d = @"DENY"; r = @"CERT"; break;
case EVENTSTATE_BLOCK_SCOPE:
d = @"DENY"; r = @"SCOPE"; break;
case EVENTSTATE_BLOCK_UNKNOWN:
d = @"DENY"; r = @"UNKNOWN"; break;
default:
d = @"ALLOW"; r = @"NOTRUNNING"; args = [self argsForPid:message.pid]; break;
}
outLog = [NSString stringWithFormat:@"action=EXEC|decision=%@|reason=%@", d, r];
if (cd.decisionExtra) {
outLog = [outLog stringByAppendingFormat:@"|explain=%@", cd.decisionExtra];
}
outLog = [outLog stringByAppendingFormat:@"|sha256=%@|path=%@|args=%@",
cd.sha256, [self sanitizeString:@(message.path)], [self sanitizeString:args]];
if (cd.certSHA256) {
outLog = [outLog stringByAppendingFormat:@"|cert_sha256=%@|cert_cn=%@",
cd.certSHA256, [self sanitizeString:cd.certCommonName]];
}
outLog = [outLog stringByAppendingFormat:@"|pid=%d|ppid=%d|uid=%d|gid=%d",
message.pid, message.ppid, message.uid, message.gid];
LOGI(@"%@", outLog);
}
#pragma mark Helpers
- (NSString *)sanitizeString:(NSString *)inStr {
inStr = [inStr stringByReplacingOccurrencesOfString:@"|" withString:@"<pipe>"];
inStr = [inStr stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
inStr = [inStr stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
return inStr;
}
- (NSString *)argsForPid:(pid_t)pid {
int mib[3];
// Get size of buffer required to store process arguments.
mib[0] = CTL_KERN;
mib[1] = KERN_ARGMAX;
int argmax;
size_t size = sizeof(argmax);
if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) return nil;
// Create buffer to store args
NSMutableData *argsdata = [NSMutableData dataWithCapacity:argmax];
char *argsdatabytes = (char *)argsdata.mutableBytes;
// Fetch args
mib[0] = CTL_KERN;
mib[1] = KERN_PROCARGS2;
mib[2] = pid;
size = (size_t)argmax;
if (sysctl(mib, 3, argsdatabytes, &size, NULL, 0) == -1) return nil;
// Get argc
int argc;
memcpy(&argc, argsdatabytes, sizeof(argc));
// Get pointer to beginning of string space
char *cp;
cp = (char *) argsdatabytes + sizeof(argc);
// Skip over exec_path
for (; cp < &argsdatabytes[size]; cp++) {
if (*cp == '\0') {
cp++;
break;
}
}
// Skip trailing NULL bytes
for (; cp < &argsdatabytes[size]; cp++) if (*cp != '\0') break;
// Loop over the argv array, stripping newlines in each arg and putting in a new array.
NSMutableArray *args = [NSMutableArray arrayWithCapacity:argc];
for (int i = 0; i < argc; i++) {
NSString *arg = @(cp);
[args addObject:arg];
// Move the pointer past this string and the terminator at the end.
cp += strlen(cp) + 1;
}
// Return the args as a space-separated list
return [args componentsJoinedByString:@" "];
}
@end

View File

@@ -24,6 +24,7 @@
///
/// Add event to the database.
///
/// @param event the event to store.
/// @return YES if event was successfully stored.
///
@@ -31,18 +32,23 @@
///
/// Retrieves all events in the database
///
/// @return NSArray of SNTStoredEvent's
///
- (NSArray *)pendingEvents;
///
/// Retrieves number of events in database without fetching every event.
///
/// @return Number of events in database.
///
- (NSUInteger)pendingEventsCount;
///
/// Retrieve an event from the database.
/// Retrieve an event from the database with a given SHA-256. If multiple events
/// exist for the same SHA-256, just the first is returned.
///
/// @param sha256 a SHA-256 of the binary to return an event for.
/// @return a single SNTStoredEvent.
///
- (SNTStoredEvent *)pendingEventForSHA256:(NSString *)sha256;
@@ -50,12 +56,15 @@
///
/// Delete a single event from the database using its index.
///
- (void)deleteEventWithId:(NSNumber *)id;
/// @param index the event ID.
///
- (void)deleteEventWithId:(NSNumber *)index;
///
/// Delete multiple events from the database with an array of IDs.
///
/// @param indexes an array of event IDs.
///
- (void)deleteEventsWithIds:(NSArray *)ids;
- (void)deleteEventsWithIds:(NSArray *)indexes;
@end

View File

@@ -14,23 +14,21 @@
#import "SNTEventTable.h"
#import "SNTCertificate.h"
#import "MOLCertificate.h"
#import "SNTLogging.h"
#import "SNTStoredEvent.h"
@implementation SNTEventTable
- (int)initializeDatabase:(FMDatabase *)db fromVersion:(int)version {
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version {
int newVersion = 0;
if (version < 1) {
[db executeUpdate:@"CREATE TABLE 'events' ("
"'idx' INTEGER PRIMARY KEY AUTOINCREMENT,"
"'filesha256' TEXT NOT NULL,"
"'eventdata' BLOB"
@");"];
@"'idx' INTEGER PRIMARY KEY AUTOINCREMENT,"
@"'filesha256' TEXT NOT NULL,"
@"'eventdata' BLOB);"];
[db executeUpdate:@"CREATE INDEX filesha256 ON events (filesha256);"];
newVersion = 1;
}
@@ -43,10 +41,14 @@
if (!event.fileSHA256 ||
!event.filePath ||
!event.occurrenceDate ||
!event.executingUser ||
!event.decision) return NO;
NSData *eventData = [NSKeyedArchiver archivedDataWithRootObject:event];
NSData *eventData;
@try {
eventData = [NSKeyedArchiver archivedDataWithRootObject:event];
} @catch (NSException *exception) {
return NO;
}
__block BOOL success = NO;
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
@@ -57,16 +59,6 @@
return success;
}
- (SNTStoredEvent *)eventFromResultSet:(FMResultSet *)rs {
NSData *eventData = [rs dataForColumn:@"eventdata"];
if (!eventData) return nil;
SNTStoredEvent *event = [NSKeyedUnarchiver unarchiveObjectWithData:eventData];
event.idx = @([rs intForColumn:@"idx"]);
return event;
}
#pragma mark Querying/Retreiving
- (NSUInteger)pendingEventsCount {
@@ -81,11 +73,14 @@
__block SNTStoredEvent *storedEvent;
[self inDatabase:^(FMDatabase *db) {
FMResultSet *rs = [db executeQuery:@"SELECT * FROM events WHERE filesha256=? LIMIT 1;",
sha256];
FMResultSet *rs =
[db executeQuery:@"SELECT * FROM events WHERE filesha256=? LIMIT 1;", sha256];
if ([rs next]) {
storedEvent = [self eventFromResultSet:rs];
if (!storedEvent) {
[db executeUpdate:@"DELETE FROM events WHERE idx=?", [rs objectForColumnName:@"idx"]];
}
}
[rs close];
@@ -101,7 +96,12 @@
FMResultSet *rs = [db executeQuery:@"SELECT * FROM events"];
while ([rs next]) {
[pendingEvents addObject:[self eventFromResultSet:rs]];
id obj = [self eventFromResultSet:rs];
if (obj) {
[pendingEvents addObject:obj];
} else {
[db executeUpdate:@"DELETE FROM events WHERE idx=?", [rs objectForColumnName:@"idx"]];
}
}
[rs close];
@@ -110,6 +110,20 @@
return pendingEvents;
}
- (SNTStoredEvent *)eventFromResultSet:(FMResultSet *)rs {
NSData *eventData = [rs dataForColumn:@"eventdata"];
if (!eventData) return nil;
SNTStoredEvent *event;
@try {
event = [NSKeyedUnarchiver unarchiveObjectWithData:eventData];
event.idx = @([rs intForColumn:@"idx"]);
} @catch (NSException *exception) {}
return event;
}
#pragma mark Deleting
- (void)deleteEventWithId:(NSNumber *)index {
@@ -122,6 +136,9 @@
for (NSNumber *index in indexes) {
[self deleteEventWithId:index];
}
[self inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"VACUUM"];
}];
}
@end

View File

@@ -13,9 +13,11 @@
/// limitations under the License.
#include "SNTCommonEnums.h"
#include "SNTKernelCommon.h"
@class SNTCodesignChecker;
@class MOLCodesignChecker;
@class SNTDriverManager;
@class SNTEventLog;
@class SNTEventTable;
@class SNTRuleTable;
@class SNTXPCConnection;
@@ -29,36 +31,28 @@
/// + (If denied or unknown) Storing details about the execution event to the database
/// for upload and spwaning santactl to quickly try and send that to the server.
/// + (If denied) Potentially sending a message to SantaGUI to notify the user
/// + Logging the event to the log file
///
@interface SNTExecutionController : NSObject
@property SNTDriverManager *driverManager;
@property SNTRuleTable *ruleTable;
@property SNTEventLog *eventLog;
@property SNTEventTable *eventTable;
@property SNTRuleTable *ruleTable;
@property SNTXPCConnection *notifierConnection;
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
ruleTable:(SNTRuleTable *)ruleTable
eventTable:(SNTEventTable *)eventTable
notifierConnection:(SNTXPCConnection *)notifierConn;
notifierConnection:(SNTXPCConnection *)notifierConn
eventLog:(SNTEventLog *)eventLog;
///
/// Handles the logic of deciding whether to allow the binary to run or not, sends the response to
/// the kernel, logs the event to the log and if necessary stores the event in the database and
/// sends a notification to the GUI agent.
///
/// @param path the binary that's being executed
/// @param userName the user who's executing the binary
/// @param pid the process id being executed
/// @param ppid the parent process id
/// @param vnoteId the id of the vnode being executed
/// @param message The message sent from the kernel.
///
- (void)validateBinaryWithPath:(NSString *)path
userName:(NSString *)userName
pid:(NSNumber *)pid
ppid:(NSNumber *)ppid
vnodeId:(uint64_t)vnodeId;
- (void)validateBinaryWithMessage:(santa_message_t)message;
@end

View File

@@ -14,15 +14,20 @@
#import "SNTExecutionController.h"
#include <libproc.h>
#include <pwd.h>
#include <utmpx.h>
#include "SNTLogging.h"
#import "SNTCertificate.h"
#import "SNTCodesignChecker.h"
#import "SNTCachedDecision.h"
#import "MOLCertificate.h"
#import "MOLCodesignChecker.h"
#import "SNTCommonEnums.h"
#import "SNTConfigurator.h"
#import "SNTDriverManager.h"
#import "SNTDropRootPrivs.h"
#import "SNTEventLog.h"
#import "SNTEventTable.h"
#import "SNTFileInfo.h"
#import "SNTRule.h"
@@ -38,75 +43,119 @@
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
ruleTable:(SNTRuleTable *)ruleTable
eventTable:(SNTEventTable *)eventTable
notifierConnection:(SNTXPCConnection *)notifier {
notifierConnection:(SNTXPCConnection *)notifier
eventLog:(SNTEventLog *)eventLog {
self = [super init];
if (self) {
_driverManager = driverManager;
_ruleTable = ruleTable;
_eventTable = eventTable;
_notifierConnection = notifier;
LOGI(@"Log format: Decision (A|D), Reason (B|C|S|?), SHA-256, Path, Cert SHA-256, Cert CN");
_eventLog = eventLog;
// Workaround for xpcproxy/libsecurity bug on Yosemite
// This establishes the XPC connection between libsecurity and syspolicyd.
// Not doing this causes a deadlock as establishing this link goes through xpcproxy.
(void)[[SNTCodesignChecker alloc] initWithSelf];
(void)[[MOLCodesignChecker alloc] initWithSelf];
}
return self;
}
#pragma mark Binary Validation
- (void)validateBinaryWithPath:(NSString *)path
userName:(NSString *)userName
pid:(NSNumber *)pid
ppid:(NSNumber *)ppid
vnodeId:(uint64_t)vnodeId {
SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:path];
NSString *sha256 = [binInfo SHA256];
// These will be filled in either in later steps
santa_action_t respondedAction = ACTION_UNSET;
SNTRule *rule;
// Step 1 - binary rule?
rule = [self.ruleTable binaryRuleForSHA256:sha256];
- (santa_eventstate_t)makeDecision:(SNTCachedDecision *)cd binaryInfo:(SNTFileInfo *)fi {
SNTRule *rule = [self.ruleTable binaryRuleForSHA256:cd.sha256];
if (rule) {
respondedAction = [self actionForRuleState:rule.state];
[self.driverManager postToKernelAction:respondedAction forVnodeID:vnodeId];
}
SNTCodesignChecker *csInfo = [[SNTCodesignChecker alloc] initWithBinaryPath:path];
// Step 2 - cert rule?
if (!rule) {
rule = [self.ruleTable certificateRuleForSHA256:csInfo.leafCertificate.SHA256];
if (rule) {
respondedAction = [self actionForRuleState:rule.state];
[self.driverManager postToKernelAction:respondedAction forVnodeID:vnodeId];
switch (rule.state) {
case RULESTATE_WHITELIST:
return EVENTSTATE_ALLOW_BINARY;
case RULESTATE_SILENT_BLACKLIST:
cd.silentBlock = YES;
case RULESTATE_BLACKLIST:
cd.customMsg = rule.customMsg;
return EVENTSTATE_BLOCK_BINARY;
default: break;
}
}
// Step 3 - in scope?
if (![self fileIsInScope:path]) {
[self.driverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW forVnodeID:vnodeId];
[self logDecisionForEventState:EVENTSTATE_ALLOW_SCOPE sha256:sha256 path:path leafCert:nil];
rule = [self.ruleTable certificateRuleForSHA256:cd.certSHA256];
if (rule) {
switch (rule.state) {
case RULESTATE_WHITELIST:
return EVENTSTATE_ALLOW_CERTIFICATE;
case RULESTATE_SILENT_BLACKLIST:
cd.silentBlock = YES;
case RULESTATE_BLACKLIST:
cd.customMsg = rule.customMsg;
return EVENTSTATE_BLOCK_CERTIFICATE;
default: break;
}
}
NSString *msg = [self fileIsScopeBlacklisted:fi];
if (msg) {
cd.decisionExtra = msg;
return EVENTSTATE_BLOCK_SCOPE;
}
msg = [self fileIsScopeWhitelisted:fi];
if (msg) {
cd.decisionExtra = msg;
return EVENTSTATE_ALLOW_SCOPE;
}
switch ([[SNTConfigurator configurator] clientMode]) {
case CLIENTMODE_MONITOR: return EVENTSTATE_ALLOW_UNKNOWN;
case CLIENTMODE_LOCKDOWN: return EVENTSTATE_BLOCK_UNKNOWN;
default: return EVENTSTATE_BLOCK_UNKNOWN;
}
}
- (void)validateBinaryWithMessage:(santa_message_t)message {
// Get info about the file. If we can't get this info, allow execution and log an error.
NSError *fileInfoError;
SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:@(message.path) error:&fileInfoError];
if (!binInfo) {
LOGW(@"Failed to read file %@: %@", binInfo.path, fileInfoError.localizedDescription);
[self.driverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
forVnodeID:message.vnode_id];
return;
}
// Step 4 - default rule :-(
if (!rule) {
respondedAction = [self defaultDecision];
[self.driverManager postToKernelAction:respondedAction forVnodeID:vnodeId];
}
// Get codesigning info about the file.
MOLCodesignChecker *csInfo = [[MOLCodesignChecker alloc] initWithBinaryPath:binInfo.path];
// Actually make the decision.
SNTCachedDecision *cd = [[SNTCachedDecision alloc] init];
cd.sha256 = binInfo.SHA256;
cd.certCommonName = csInfo.leafCertificate.commonName;
cd.certSHA256 = csInfo.leafCertificate.SHA256;
cd.vnodeId = message.vnode_id;
cd.decision = [self makeDecision:cd binaryInfo:binInfo];
// Save decision details for logging the execution later.
santa_action_t action = [self actionForEventState:cd.decision];
if (action == ACTION_RESPOND_CHECKBW_ALLOW) [self.eventLog saveDecisionDetails:cd];
// Send the decision to the kernel.
[self.driverManager postToKernelAction:action forVnodeID:cd.vnodeId];
// Log to database if necessary.
if (cd.decision != EVENTSTATE_ALLOW_BINARY &&
cd.decision != EVENTSTATE_ALLOW_CERTIFICATE &&
cd.decision != EVENTSTATE_ALLOW_SCOPE) {
// Step 5 - log to database and potentially alert user
if (respondedAction == ACTION_RESPOND_CHECKBW_DENY ||
!rule ||
[[SNTConfigurator configurator] logAllEvents]) {
SNTStoredEvent *se = [[SNTStoredEvent alloc] init];
se.fileSHA256 = sha256;
se.filePath = path;
se.occurrenceDate = [[NSDate alloc] init];
se.fileSHA256 = cd.sha256;
se.filePath = binInfo.path;
se.decision = cd.decision;
se.signingChain = csInfo.certificates;
se.pid = @(message.pid);
se.ppid = @(message.ppid);
se.parentName = @(message.pname);
se.fileBundleID = [binInfo bundleIdentifier];
se.fileBundleName = [binInfo bundleName];
@@ -118,184 +167,121 @@
se.fileBundleVersion = [binInfo bundleVersion];
}
se.signingChain = csInfo.certificates;
se.executingUser = userName;
se.occurrenceDate = [[NSDate alloc] init];
se.decision = [self eventStateForDecision:respondedAction type:rule.type];
se.pid = pid;
se.ppid = ppid;
struct passwd *user = getpwuid(message.uid);
endpwent();
if (user) {
se.executingUser = @(user->pw_name);
}
NSArray *loggedInUsers, *currentSessions;
[self loggedInUsers:&loggedInUsers sessions:&currentSessions];
se.currentSessions = currentSessions;
se.loggedInUsers = loggedInUsers;
se.quarantineDataURL = binInfo.quarantineDataURL;
se.quarantineRefererURL = binInfo.quarantineRefererURL;
se.quarantineTimestamp = binInfo.quarantineTimestamp;
se.quarantineAgentBundleID = binInfo.quarantineAgentBundleID;
[self.eventTable addStoredEvent:se];
if (respondedAction == ACTION_RESPOND_CHECKBW_DENY) {
// If binary was blocked, do the needful
if (action != ACTION_RESPOND_CHECKBW_ALLOW) {
[self.eventLog logDeniedExecution:cd withMessage:message];
// So the server has something to show the user straight away, initiate an event
// upload for the blocked binary rather than waiting for the next sync.
// The event upload is skipped if the full path is equal to that of /usr/sbin/santactl so that
/// on the off chance that santactl is not whitelisted, we don't get into an infinite loop.
if (![path isEqual:@"/usr/sbin/santactl"]) {
[self initiateEventUploadForSHA256:sha256];
}
[self initiateEventUploadForEvent:se];
[[self.notifierConnection remoteObjectProxy] postBlockNotification:se
withCustomMessage:rule.customMsg];
if (!cd.silentBlock) {
[[self.notifierConnection remoteObjectProxy] postBlockNotification:se
withCustomMessage:cd.customMsg];
}
}
}
// Step 6 - log to log file
[self logDecisionForEventState:[self eventStateForDecision:respondedAction type:rule.type]
sha256:sha256
path:path
leafCert:csInfo.leafCertificate];
}
///
/// Checks whether the file at @c path is in-scope for checking with Santa.
///
/// Files that are out of scope:
/// + Non Mach-O files
/// + Files in whitelisted directories.
/// + Non Mach-O files that are not part of an installer package.
/// + Files in whitelisted path.
///
/// @return @c YES if file is in scope, @c NO otherwise.
///
- (BOOL)fileIsInScope:(NSString *)path {
// Determine if file is within a whitelisted directory.
if ([self pathIsInWhitelistedDir:path]) {
return NO;
- (NSString *)fileIsScopeWhitelisted:(SNTFileInfo *)fi {
// Determine if file is within a whitelisted path
NSRegularExpression *re = [[SNTConfigurator configurator] whitelistPathRegex];
if ([re numberOfMatchesInString:fi.path options:0 range:NSMakeRange(0, fi.path.length)]) {
return @"Whitelist Regex";
}
// If file is not a Mach-O file, we're not interested.
// TODO(rah): Consider adding an option to check scripts
SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:path];
if (![binInfo isMachO]) {
return NO;
// If file is not a Mach-O file, we're not interested unless it's part of an install package.
// TODO(rah): Consider adding an option to check all scripts.
// TODO(rah): Consider adding an option to disable package script checks.
if (!fi.isMachO && ![fi.path hasPrefix:@"/private/tmp/PKInstallSandbox."]) {
return @"Not a Mach-O";
}
return YES;
return nil;
}
- (BOOL)pathIsInWhitelistedDir:(NSString *)path {
// TODO(rah): Implement this.
return NO;
}
- (santa_eventstate_t)eventStateForDecision:(santa_action_t)decision type:(santa_ruletype_t)type {
if (decision == ACTION_RESPOND_CHECKBW_ALLOW) {
if (type == RULETYPE_BINARY) {
return EVENTSTATE_ALLOW_BINARY;
} else if (type == RULETYPE_CERT) {
return EVENTSTATE_ALLOW_CERTIFICATE;
} else {
return EVENTSTATE_ALLOW_UNKNOWN;
}
} else if (decision == ACTION_RESPOND_CHECKBW_DENY) {
if (type == RULETYPE_BINARY) {
return EVENTSTATE_BLOCK_BINARY;
} else if (decision == RULETYPE_CERT) {
return EVENTSTATE_BLOCK_CERTIFICATE;
} else {
return EVENTSTATE_BLOCK_UNKNOWN;
}
} else {
return EVENTSTATE_UNKNOWN;
}
}
- (void)logDecisionForEventState:(santa_eventstate_t)eventState
sha256:(NSString *)sha256
path:(NSString *)path
leafCert:(SNTCertificate *)cert {
NSString *d, *r, *outLog;
switch (eventState) {
case EVENTSTATE_ALLOW_BINARY:
d = @"A"; r = @"B"; break;
case EVENTSTATE_ALLOW_CERTIFICATE:
d = @"A"; r = @"C"; break;
case EVENTSTATE_ALLOW_SCOPE:
d = @"A"; r = @"S"; break;
case EVENTSTATE_ALLOW_UNKNOWN:
d = @"A"; r = @"?"; break;
case EVENTSTATE_BLOCK_BINARY:
d = @"D"; r = @"B"; break;
case EVENTSTATE_BLOCK_CERTIFICATE:
d = @"D"; r = @"C"; break;
case EVENTSTATE_BLOCK_UNKNOWN:
d = @"D"; r = @"?"; break;
default:
d = @"?"; r = @"?"; break;
- (NSString *)fileIsScopeBlacklisted:(SNTFileInfo *)fi {
NSRegularExpression *re = [[SNTConfigurator configurator] blacklistPathRegex];
if ([re numberOfMatchesInString:fi.path options:0 range:NSMakeRange(0, fi.path.length)]) {
return @"Blacklist Regex";
}
// Ensure there are no pipes in the path name (as this will be confusing in the log)
NSString *printPath = [path stringByReplacingOccurrencesOfString:@"|" withString:@"<pipe>"];
if (cert && cert.SHA256 && cert.commonName) {
// Also ensure there are no pipes in the cert's common name.
NSString *printCommonName = [cert.commonName stringByReplacingOccurrencesOfString:@"|"
withString:@"<pipe>"];
outLog = [NSString stringWithFormat:@"%@|%@|%@|%@|%@|%@",
d, r, sha256, printPath, cert.SHA256, printCommonName];
} else {
outLog = [NSString stringWithFormat:@"%@|%@|%@|%@", d, r, sha256, printPath];
if ([[SNTConfigurator configurator] enablePageZeroProtection] && fi.isMissingPageZero) {
return @"Missing __PAGEZERO";
}
// Now make sure none of the log line has a newline in it.
LOGI(@"%@", [[outLog componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]
componentsJoinedByString:@" "]);
return nil;
}
- (void)initiateEventUploadForSHA256:(NSString *)sha256 {
signal(SIGCHLD, SIG_IGN);
pid_t child = fork();
if (child == 0) {
fclose(stdout);
fclose(stderr);
- (void)initiateEventUploadForEvent:(SNTStoredEvent *)event {
// The event upload is skipped if the full path is equal to that of santactl so that
// on the off chance that santactl is not whitelisted, we don't get into an infinite loop.
// It's also skipped if there isn't a server configured or the last sync caused a backoff.
if ([event.filePath isEqual:@(kSantaCtlPath)] ||
![[SNTConfigurator configurator] syncBaseURL] ||
[[SNTConfigurator configurator] syncBackOff]) return;
if (fork() == 0) {
// Ensure we have no privileges
if (!DropRootPrivileges()) {
exit(1);
_exit(EPERM);
}
exit(execl("/usr/sbin/santactl", "/usr/sbin/santactl", "sync",
"singleevent", [sha256 UTF8String], NULL));
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "singleevent",
[event.fileSHA256 UTF8String], NULL));
}
}
- (santa_action_t)defaultDecision {
switch ([[SNTConfigurator configurator] clientMode]) {
case CLIENTMODE_MONITOR:
return ACTION_RESPOND_CHECKBW_ALLOW;
case CLIENTMODE_LOCKDOWN:
return ACTION_RESPOND_CHECKBW_DENY;
default:
// This should never happen, panic and lockdown.
LOGE(@"Client mode is unset while enforcement is in effect. Blocking.");
return ACTION_RESPOND_CHECKBW_DENY;
}
}
- (santa_action_t)actionForRuleState:(santa_rulestate_t)state {
- (santa_action_t)actionForEventState:(santa_eventstate_t)state {
switch (state) {
case RULESTATE_WHITELIST:
case EVENTSTATE_ALLOW_BINARY:
case EVENTSTATE_ALLOW_CERTIFICATE:
case EVENTSTATE_ALLOW_SCOPE:
case EVENTSTATE_ALLOW_UNKNOWN:
return ACTION_RESPOND_CHECKBW_ALLOW;
case RULESTATE_BLACKLIST:
case RULESTATE_SILENT_BLACKLIST:
case EVENTSTATE_BLOCK_BINARY:
case EVENTSTATE_BLOCK_CERTIFICATE:
case EVENTSTATE_BLOCK_SCOPE:
case EVENTSTATE_BLOCK_UNKNOWN:
return ACTION_RESPOND_CHECKBW_DENY;
default:
return ACTION_ERROR;
LOGW(@"Invalid event state %d", state);
return ACTION_RESPOND_CHECKBW_DENY;
}
}
- (void)loggedInUsers:(NSArray **)users sessions:(NSArray **)sessions {
struct utmpx *nxt;
NSMutableDictionary *loggedInUsers = [[NSMutableDictionary alloc] init];
NSMutableDictionary *loggedInHosts = [[NSMutableDictionary alloc] init];
struct utmpx *nxt;
while ((nxt = getutxent())) {
if (nxt->ut_type != USER_PROCESS) continue;
@@ -308,11 +294,11 @@
sessionName = [NSString stringWithFormat:@"%s@%s", nxt->ut_user, nxt->ut_line];
}
if ([userName length] > 0) {
if (userName.length > 0) {
loggedInUsers[userName] = [NSNull null];
}
if ([sessionName length] > 1) {
if (sessionName.length > 1) {
loggedInHosts[sessionName] = [NSNull null];
}
}

View File

@@ -27,17 +27,17 @@
///
/// @return Number of rules in the database
///
- (long)ruleCount;
- (NSUInteger)ruleCount;
///
/// @return Number of binary rules in the database
///
- (long)binaryRuleCount;
- (NSUInteger)binaryRuleCount;
///
/// @return Number of certificate rules in the database
///
- (long)certificateRuleCount;
- (NSUInteger)certificateRuleCount;
///
/// @return Rule for binary with given SHA-256
@@ -54,7 +54,7 @@
/// transaction will abort if any rule fails to add.
///
/// @param rules Array of SNTRule's to add.
/// @param cleanslate If true, remove all rules before adding the new rules.
/// @param cleanSlate If true, remove all rules before adding the new rules.
/// @return YES if all rules were added successfully.
///
- (BOOL)addRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate;

View File

@@ -14,14 +14,26 @@
#import "SNTRuleTable.h"
#import "SNTCertificate.h"
#import "SNTCodesignChecker.h"
#import "MOLCertificate.h"
#import "MOLCodesignChecker.h"
#import "SNTConfigurator.h"
#import "SNTLogging.h"
#import "SNTRule.h"
@interface SNTRuleTable ()
@property NSString *santadCertSHA;
@property NSString *launchdCertSHA;
@end
@implementation SNTRuleTable
- (int)initializeDatabase:(FMDatabase *)db fromVersion:(int)version {
int newVersion = 0;
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version {
// Save hashes of the signing certs for launchd and santad
self.santadCertSHA = [[[[MOLCodesignChecker alloc] initWithSelf] leafCertificate] SHA256];
self.launchdCertSHA = [[[[MOLCodesignChecker alloc] initWithPID:1] leafCertificate] SHA256];
uint32_t newVersion = 0;
if (version < 1) {
[db executeUpdate:@"CREATE TABLE 'rules' ("
@@ -39,14 +51,14 @@
// Insert the codesigning certs for the running santad and launchd into the initial database.
// This helps prevent accidentally denying critical system components while the database
// is empty. This 'initial database' will then be cleared on the first successful sync.
NSString *santadSHA = [[[[SNTCodesignChecker alloc] initWithSelf] leafCertificate] SHA256];
NSString *launchdSHA = [[[[SNTCodesignChecker alloc] initWithPID:1] leafCertificate] SHA256];
[db executeUpdate:@"INSERT INTO rules (shasum, state, type) VALUES (?, ?, ?)",
santadSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
self.santadCertSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
[db executeUpdate:@"INSERT INTO rules (shasum, state, type) VALUES (?, ?, ?)",
launchdSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
self.launchdCertSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
newVersion = 1;
[[SNTConfigurator configurator] setSyncCleanRequired:YES];
}
return newVersion;
@@ -54,24 +66,24 @@
#pragma mark Entry Counts
- (long)ruleCount {
__block long count = 0;
- (NSUInteger)ruleCount {
__block NSUInteger count = 0;
[self inDatabase:^(FMDatabase *db) {
count = [db longForQuery:@"SELECT COUNT(*) FROM rules"];
count = [db longForQuery:@"SELECT COUNT(*) FROM rules"];
}];
return count;
}
- (long)binaryRuleCount {
__block long count = 0;
- (NSUInteger)binaryRuleCount {
__block NSUInteger count = 0;
[self inDatabase:^(FMDatabase *db) {
count = [db longForQuery:@"SELECT COUNT(*) FROM binrules"];
}];
return count;
}
- (long)certificateRuleCount {
__block long count = 0;
- (NSUInteger)certificateRuleCount {
__block NSUInteger count = 0;
[self inDatabase:^(FMDatabase *db) {
count = [db longForQuery:@"SELECT COUNT(*) FROM certrules"];
}];
@@ -95,7 +107,7 @@
[self inDatabase:^(FMDatabase *db) {
FMResultSet *rs = [db executeQuery:@"SELECT * FROM certrules WHERE shasum=? LIMIT 1", SHA256];
if ([rs next]) {
rule = [self ruleFromResultSet:rs];
rule = [self ruleFromResultSet:rs];
}
[rs close];
}];
@@ -120,16 +132,34 @@
#pragma mark Adding
- (BOOL)addRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate {
if (!rules || rules.count < 1) {
LOGE(@"Received request to add rules with nil/empty array.");
return NO;
}
__block BOOL failed = NO;
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Protect rules for santad/launchd certificates.
NSPredicate *p = [NSPredicate predicateWithFormat:
@"(SELF.shasum = %@ OR SELF.shasum = %@) AND SELF.type = %d",
self.santadCertSHA, self.launchdCertSHA, RULETYPE_CERT];
NSArray *requiredHashes = [rules filteredArrayUsingPredicate:p];
p = [NSPredicate predicateWithFormat:@"SELF.state == %d", RULESTATE_WHITELIST];
NSArray *requiredHashesWhitelist = [requiredHashes filteredArrayUsingPredicate:p];
if ((cleanSlate && requiredHashesWhitelist.count != 2) ||
(requiredHashes.count != requiredHashesWhitelist.count)) {
LOGE(@"Received request to remove whitelist for launchd/santad ceritifcates.");
*rollback = failed = YES;
return;
}
if (cleanSlate) {
[db executeUpdate:@"DELETE FROM rules"];
}
for (SNTRule *rule in rules) {
if (![rule isKindOfClass:[SNTRule class]] ||
!rule.shasum || rule.shasum.length == 0 ||
if (![rule isKindOfClass:[SNTRule class]] || rule.shasum.length == 0 ||
rule.state == RULESTATE_UNKNOWN || rule.type == RULETYPE_UNKNOWN) {
*rollback = failed = YES;
return;
@@ -143,8 +173,8 @@
}
} else {
if (![db executeUpdate:@"INSERT OR REPLACE INTO rules "
@"(shasum, state, type, custommsg) "
@"VALUES (?, ?, ?, ?);",
@"(shasum, state, type, custommsg) "
@"VALUES (?, ?, ?, ?);",
rule.shasum, @(rule.state), @(rule.type), rule.customMsg]) {
*rollback = failed = YES;
return;

View File

@@ -14,12 +14,76 @@
#include "SNTLogging.h"
#include <pthread/pthread.h>
#include <sys/resource.h>
#import "SNTApplication.h"
extern uint64_t watchdogCPUEvents;
extern uint64_t watchdogRAMEvents;
/// Converts a timeval struct to double, converting the microseconds value to seconds.
static inline double timeval_to_double(struct timeval tv) {
return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
}
/// The watchdog thread function, used to monitor santad CPU/RAM usage and print a warning
/// if it goes over certain thresholds.
void *watchdogThreadFunction(__unused void *idata) {
pthread_setname_np("com.google.santa.watchdog");
// Number of seconds to wait between checks.
const int timeInterval = 60;
// Amount of CPU usage to trigger warning, as a percentage averaged over timeInterval
// santad's usual CPU usage is 0-3% but can occasionally spike if lots of processes start at once.
const int cpuWarnThreshold = 20.0;
// Amount of RAM usage to trigger warning, in MB.
// santad's usual RAM usage is between 5-50MB but can spike if lots of processes start at once.
const int memWarnThreshold = 250;
double prevTotalTime = 0.0;
double prevRamUseMB = 0.0;
struct rusage usage;
struct mach_task_basic_info taskInfo;
mach_msg_type_number_t taskInfoCount = MACH_TASK_BASIC_INFO_COUNT;
while(true) {
@autoreleasepool {
sleep(timeInterval);
// CPU
getrusage(RUSAGE_SELF, &usage);
double totalTime = timeval_to_double(usage.ru_utime) + timeval_to_double(usage.ru_stime);
double percentage = (((totalTime - prevTotalTime) / (double)timeInterval) * 100.0);
prevTotalTime = totalTime;
if (percentage > cpuWarnThreshold) {
LOGW(@"Watchdog: potentially high CPU use, ~%.2f%% over last %d seconds.",
percentage, timeInterval);
watchdogCPUEvents++;
}
// RAM
if (KERN_SUCCESS == task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
(task_info_t)&taskInfo, &taskInfoCount)) {
double ramUseMB = (double) taskInfo.resident_size / 1024 / 1024;
if (ramUseMB > memWarnThreshold && ramUseMB > prevRamUseMB) {
LOGW(@"Watchdog: potentially high RAM use, RSS is %.2fMB.", ramUseMB);
watchdogRAMEvents++;
}
prevRamUseMB = ramUseMB;
}
}
}
return NULL;
}
int main(int argc, const char *argv[]) {
@autoreleasepool {
// Do not buffer stdout
setbuf(stdout, NULL);
// Do not wait on child processes
signal(SIGCHLD, SIG_IGN);
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
@@ -33,6 +97,10 @@ int main(int argc, const char *argv[]) {
SNTApplication *s = [[SNTApplication alloc] init];
[s performSelectorInBackground:@selector(run) withObject:nil];
// Create watchdog thread
pthread_t watchdogThread;
pthread_create(&watchdogThread, NULL, watchdogThreadFunction, NULL);
[[NSRunLoop mainRunLoop] run];
}
}

View File

@@ -29,23 +29,30 @@
///
#define TSTART(testName) \
printf(" %-50s ", testName);
do { printf(" %-50s ", testName); } while (0)
#define TPASS() \
printf("\x1b[32mPASS\x1b[0m\n");
do { printf("\x1b[32mPASS\x1b[0m\n"); } while (0)
#define TPASSINFO(fmt, ...) \
printf("\x1b[32mPASS\x1b[0m\n " fmt "\n", ##__VA_ARGS__);
do { printf("\x1b[32mPASS\x1b[0m\n " fmt "\n", ##__VA_ARGS__); } while (0)
#define TFAIL() \
printf("\x1b[31mFAIL\x1b[0m\n"); \
exit(1);
do { \
printf("\x1b[31mFAIL\x1b[0m\n"); \
exit(1); \
} while (0)
#define TFAILINFO(fmt, ...) \
printf("\x1b[31mFAIL\x1b[0m\n -> " fmt "\n\nTest failed.\n\n", ##__VA_ARGS__); \
exit(1);
do { \
printf("\x1b[31mFAIL\x1b[0m\n -> " fmt "\n\nTest failed.\n\n", ##__VA_ARGS__); \
exit(1); \
} while (0)
@interface SantaKernelTests : NSObject
@property io_connect_t connection;
@property int timesSeenLs;
@property int timesSeenCat;
@property int timesSeenCp;
@property int testExeIteration;
@property int timesSeenTestExeIteration;
- (void)runTests;
@end
@@ -60,16 +67,15 @@
t.standardInput = nil;
t.standardOutput = nil;
t.standardError = nil;
return t;
}
- (NSString *)sha256ForPath:(NSString *)path {
unsigned char sha256[CC_SHA256_DIGEST_LENGTH];
NSData *psData = [NSData dataWithContentsOfFile:path
options:NSDataReadingMappedIfSafe
error:nil];
CC_SHA256([psData bytes], (unsigned int)[psData length], sha256);
NSData *fData = [NSData dataWithContentsOfFile:path
options:NSDataReadingMappedIfSafe
error:nil];
CC_SHA256([fData bytes], (unsigned int)[fData length], sha256);
char buf[CC_SHA256_DIGEST_LENGTH * 2 + 1];
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
snprintf(buf + (2*i), 4, "%02x", (unsigned char)sha256[i]);
@@ -80,8 +86,8 @@
#pragma mark - Driver Helpers
/// Call in-kernel function: |kSantaUserClientReceive| passing the |action| and |vnodeId| via a
/// |santa_message_t| struct.
/// Call in-kernel function: |kSantaUserClientAllowBinary| or |kSantaUserClientDenyBinary|
/// passing the |vnodeID|.
- (void)postToKernelAction:(santa_action_t)action forVnodeID:(uint64_t)vnodeid {
if (action == ACTION_RESPOND_CHECKBW_ALLOW) {
IOConnectCallScalarMethod(self.connection, kSantaUserClientAllowBinary, &vnodeid, 1, 0, 0);
@@ -188,17 +194,26 @@
}
TPASS();
// Fetch the SHA-256 of /bin/ps, as we'll be using that for the cache invalidation test.
NSString *psSHA = [self sha256ForPath:@"/bin/ps"];
// Fetch the SHA-256 of /bin/ed, as we'll be using that for the cache invalidation test.
NSString *edSHA = [self sha256ForPath:@"/bin/ed"];
// Create the RE used for matching testexe's
NSString *cwd = [[NSFileManager defaultManager] currentDirectoryPath];
NSString *pattern = [cwd stringByAppendingPathComponent:@"testexe\\.(\\d+)"];
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
options:0
error:NULL];
/// Begin listening for events
queueMemory = (IODataQueueMemory *)address;
while (IODataQueueWaitForAvailableData(queueMemory, receivePort) == kIOReturnSuccess) {
do {
while (IODataQueueDataAvailable(queueMemory)) {
dataSize = sizeof(vdata);
kr = IODataQueueDequeue(queueMemory, &vdata, &dataSize);
if (kr == kIOReturnSuccess) {
if ([[self sha256ForPath:@(vdata.path)] isEqual:psSHA]) {
if (vdata.action != ACTION_REQUEST_CHECKBW) continue;
if ([[self sha256ForPath:@(vdata.path)] isEqual:edSHA]) {
[self postToKernelAction:ACTION_RESPOND_CHECKBW_DENY forVnodeID:vdata.vnode_id];
} else if (strncmp("/bin/mv", vdata.path, strlen("/bin/mv")) == 0) {
[self postToKernelAction:ACTION_RESPOND_CHECKBW_DENY forVnodeID:vdata.vnode_id];
@@ -220,6 +235,26 @@
}
TPASSINFO("Received pid, ppid: %d, %d", vdata.pid, vdata.ppid);
} else {
NSString *path = @(vdata.path);
// If current executable is one of our test exe's from handlesLotsOfBinaries,
// check that the number has increased.
NSArray *matches = [re matchesInString:path
options:0
range:NSMakeRange(0, path.length)];
if (matches.count == 1 && [matches[0] numberOfRanges] == 2) {
NSUInteger count = [[path substringWithRange:[matches[0] rangeAtIndex:1]] intValue];
if (count <= self.testExeIteration && count > 0) {
self.timesSeenTestExeIteration++;
if (self.timesSeenTestExeIteration > 2) {
TFAILINFO("Saw same binary several times");
}
} else {
self.timesSeenTestExeIteration = 0;
self.testExeIteration = (int)count;
}
}
// Allow everything not related to our testing.
[self postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW forVnodeID:vdata.vnode_id];
}
@@ -227,7 +262,7 @@
TFAILINFO("Error receiving data: %d", kr);
}
}
}
} while (IODataQueueWaitForAvailableData(queueMemory, receivePort) == kIOReturnSuccess);
IOConnectUnmapMemory(self.connection, kIODefaultMemoryType, mach_task_self(), address);
mach_port_destroy(mach_task_self(), receivePort);
@@ -239,11 +274,11 @@
- (void)receiveAndBlockTests {
TSTART("Blocks denied binaries");
NSTask *ps = [self taskWithPath:@"/bin/ps"];
NSTask *ed = [self taskWithPath:@"/bin/ed"];
@try {
[ps launch];
[ps waitUntilExit];
[ed launch];
[ed waitUntilExit];
TFAIL();
}
@catch (NSException *exception) {
@@ -282,12 +317,12 @@
// Copy the ls binary to a new file
NSFileManager *fm = [NSFileManager defaultManager];
if (![fm copyItemAtPath:@"/bin/pwd" toPath:@"santakerneltests_tmp" error:nil]) {
if (![fm copyItemAtPath:@"/bin/pwd" toPath:@"invalidacachetest_tmp" error:nil]) {
TFAILINFO("Failed to create temp file");
}
// Launch the new file to put it in the cache
NSTask *pwd = [self taskWithPath:@"santakerneltests_tmp"];
NSTask *pwd = [self taskWithPath:@"invalidacachetest_tmp"];
[pwd launch];
[pwd waitUntilExit];
@@ -296,28 +331,42 @@
TFAILINFO("First launch of test binary failed");
}
// Now replace the contents of the test file (which is cached) with the contents of /bin/ps,
// Now replace the contents of the test file (which is cached) with the contents of /bin/ed,
// which is 'blacklisted' by SHA-256 during the tests.
FILE *infile = fopen("/bin/ps", "r");
FILE *outfile = fopen("santakerneltests_tmp", "w");
FILE *infile = fopen("/bin/ed", "r");
FILE *outfile = fopen("invalidacachetest_tmp", "w");
int ch;
while ((ch = fgetc(infile)) != EOF) {
fputc(ch, outfile);
}
fclose(infile);
fclose(outfile);
// Now try running the temp file again. If it succeeds, the test failed.
NSTask *ps = [self taskWithPath:@"santakerneltests_tmp"];
NSTask *ed = [self taskWithPath:@"invalidacachetest_tmp"];
@try {
[ps launch];
[ps waitUntilExit];
TFAIL();
[ed launch];
[ed waitUntilExit];
TFAILINFO("Launched after write while file open");
[fm removeItemAtPath:@"invalidacachetest_tmp" error:nil];
} @catch (NSException *exception) {
// This is a pass, but we have more to do.
}
// Close the file to flush the write.
fclose(outfile);
// And try running the temp file again. If it succeeds, the test failed.
ed = [self taskWithPath:@"invalidacachetest_tmp"];
@try {
[ed launch];
[ed waitUntilExit];
TFAILINFO("Launched after file closed");
} @catch (NSException *exception) {
TPASS();
} @finally {
[fm removeItemAtPath:@"santakerneltests_tmp" error:nil];
[fm removeItemAtPath:@"invalidacachetest_tmp" error:nil];
}
}
@@ -358,12 +407,12 @@
} else if (pid > 0) {
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status) && WEXITSTATUS(status) == EACCES) {
if (WIFEXITED(status) && WEXITSTATUS(status) == EPERM) {
TPASS();
} else if (WIFSTOPPED(status)) {
TFAILINFO("Process was executed and is waiting for debugger");
} else {
TFAILINFO("Process did not exit with EACCESS as expected");
TFAILINFO("Process did not exit with EPERM as expected");
}
} else if (pid == 0) {
fclose(stdout);
@@ -374,6 +423,36 @@
}
}
/// Tests that the kernel can handle _lots_ of executions.
- (void)handlesLotsOfBinaries {
TSTART("Handles lots of binaries");
const int LIMIT = 12000;
for (int i = 0; i < LIMIT; i++) {
printf("\033[s"); // save cursor position
printf("%d/%i", i+1, LIMIT);
NSString *fname = [@"testexe" stringByAppendingFormat:@".%i", i];
[[NSFileManager defaultManager] copyItemAtPath:@"/bin/hostname" toPath:fname error:NULL];
@try {
NSTask *testexec = [self taskWithPath:fname];
[testexec launch];
[testexec waitUntilExit];
} @catch (NSException *e) {
TFAILINFO("Failed to launch");
}
unlink([fname UTF8String]);
printf("\033[u"); // restore cursor position
}
printf("\033[K\033[u"); // clear line, restore cursor position
TPASS();
}
#pragma mark - Main
- (void)runTests {
@@ -388,7 +467,7 @@
[self performSelectorInBackground:@selector(beginListening) withObject:nil];
// Wait for driver to finish getting ready
sleep(1.0);
sleep(1);
printf("\n-> Functional tests:\033[m\n");
[self receiveAndBlockTests];
@@ -396,6 +475,7 @@
[self invalidatesCacheTests];
[self clearCacheTests];
[self blocksDeniedTracedBinaries];
[self handlesLotsOfBinaries];
printf("\nAll tests passed.\n\n");
}

Binary file not shown.

Binary file not shown.

View File

@@ -1,239 +0,0 @@
/// Copyright 2015 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import <OCMock/OCMock.h>
#import <XCTest/XCTest.h>
#import "SNTCertificate.h"
@interface SNTCertificate (Testing)
- (NSString *)x509ValueForLabel:(NSString *)desiredLabel fromDictionary:(NSDictionary *)dict;
- (NSDate *)dateForX509Key:(NSString *)key;
@end
@interface SNTCertificateTest : XCTestCase
@property NSString *testDataPEM1;
@property NSString *testDataPEM2;
@property NSData *testDataDER1;
@property NSData *testDataDER2;
@property NSString *testDataPrivateKey;
@end
@implementation SNTCertificateTest
- (void)setUp {
[super setUp];
NSString *file = [[NSBundle bundleForClass:[self class]] pathForResource:@"GIAG2" ofType:@"pem"];
self.testDataPEM1 = [NSString stringWithContentsOfFile:file
encoding:NSUTF8StringEncoding
error:nil];
file = [[NSBundle bundleForClass:[self class]] pathForResource:@"apple" ofType:@"pem"];
self.testDataPEM2 = [NSString stringWithContentsOfFile:file
encoding:NSUTF8StringEncoding
error:nil];
file = [[NSBundle bundleForClass:[self class]] pathForResource:@"GIAG2" ofType:@"crt"];
self.testDataDER1 = [NSData dataWithContentsOfFile:file];
file = [[NSBundle bundleForClass:[self class]] pathForResource:@"tubitak" ofType:@"crt"];
self.testDataDER2 = [NSData dataWithContentsOfFile:file];
self.testDataPrivateKey = @"-----BEGIN RSA PRIVATE KEY-----"
@"MIICXQIBAAKBgQDk2F9JsQQjKSveMwazXzFLbiiOD0RkDiRX1LTmQtVdi514F6l/"
@"RwohMrwxQpsoKwyzEngX58+PrGZ0XZrcVcHn666521IxswHZPaacBlWZ7k9XkB2Y"
@"m8mxULMBG9iIv/k5tRJN3MuJdtbQc8qLBsyFFsytL8hSRvBQNyP7N/OqnQIDAQAB"
@"AoGATpLUNNMonoH2Y/aVKGVY4ZNTLWOkkc4hQF7yNdVguRvE14UYV3Em0zs+TpOV"
@"/na5h4qh3WNkaupAy1eQYnK3fqmGLZw5e8cBCgUkIi8P//zMrKlgJKwfzQHSdJSP"
@"pkCvj2kliFwNzbA026jcwGEYV+uRCNazO5ldtOcP5EDb+qkCQQD/Ihc2mjtf7oq1"
@"VZSzo0xch3NtzTZMyFCRWqMpXHQO1fZTAe96EbI85zsTRmOVuqKnGxBvvtHJr2QY"
@"UoZ72+f7AkEA5Z9qte46t1F1ME3ZzWd6Ob1obCmuAa75eTPAgQKc+1bSVeFMGLTz"
@"n2M9wZ+mIpWvJp8QRdmOi0zpEArHqa68RwJBAO1YoY/CW4obOB8JxpR3TgqmV9PG"
@"HMXBdHJEh5Vq1O0YT1dZbZd57v6JfoOn7+zS+43Jt7i9JB0kdVHLNCD1qxECQQC3"
@"wXGGEhVO6pMbitGHvQ1k85yDIn+rvTjLs4yUMWErCfnc3CUniHeFz8d2EarD9oFq"
@"KNS+8TFPbMb+HYJW2gy1AkAHGBUKmZNPGiKJEUjc5jN1uN+B9OMLDX+3rMUO9Q2x"
@"jsn0m7Mobx+pPqbIAvsklMtA4Qdrt5a9pnwEgTWoJPYA"
@"-----END RSA PRIVATE KEY-----";
}
- (void)tearDown {
[super tearDown];
}
- (void)testInitWithDER {
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataDER:self.testDataDER1];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.commonName, @"Google Internet Authority G2");
XCTAssertEqualObjects(sut.orgUnit, nil);
XCTAssertEqualObjects(sut.orgName, @"Google Inc");
XCTAssertEqualObjects(sut.countryName, @"US");
XCTAssertEqualObjects(sut.issuerCommonName, @"GeoTrust Global CA");
XCTAssertEqualObjects(sut.issuerOrgName, @"GeoTrust Inc.");
XCTAssertEqualObjects(sut.issuerOrgUnit, nil);
XCTAssertEqualObjects(sut.issuerCountryName, @"US");
XCTAssertEqualObjects(sut.SHA1, @"d83c1a7f4d0446bb2081b81a1670f8183451ca24");
XCTAssertEqualObjects(sut.SHA256,
@"a047a37fa2d2e118a4f5095fe074d6cfe0e352425a7632bf8659c03919a6c81d");
XCTAssertEqualObjects(sut.validFrom, [NSDate dateWithString:@"2013-04-05 15:15:55 +0000"]);
XCTAssertEqualObjects(sut.validUntil, [NSDate dateWithString:@"2015-04-04 15:15:55 +0000"]);
sut = [[SNTCertificate alloc] initWithCertificateDataDER:self.testDataDER2];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.commonName,
@"TÜBİTAK UEKAE Kök Sertifika Hizmet Sağlayıcısı - Sürüm 3");
XCTAssertEqualObjects(sut.orgUnit,
@"Ulusal Elektronik ve Kriptoloji Araştırma Enstitüsü - UEKAE");
XCTAssertEqualObjects(sut.orgName,
@"Türkiye Bilimsel ve Teknolojik Araştırma Kurumu - TÜBİTAK");
XCTAssertEqualObjects(sut.countryName, @"TR");
}
- (void)testInitWithValidPEM {
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.commonName, @"Google Internet Authority G2");
XCTAssertEqualObjects(sut.orgUnit, nil);
XCTAssertEqualObjects(sut.orgName, @"Google Inc");
XCTAssertEqualObjects(sut.issuerCommonName, @"GeoTrust Global CA");
XCTAssertEqualObjects(sut.SHA1, @"d83c1a7f4d0446bb2081b81a1670f8183451ca24");
XCTAssertEqualObjects(sut.SHA256,
@"a047a37fa2d2e118a4f5095fe074d6cfe0e352425a7632bf8659c03919a6c81d");
XCTAssertEqualObjects(sut.validFrom, [NSDate dateWithString:@"2013-04-05 15:15:55 +0000"]);
XCTAssertEqualObjects(sut.validUntil, [NSDate dateWithString:@"2015-04-04 15:15:55 +0000"]);
sut = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM2];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.commonName, @"www.apple.com");
XCTAssertEqualObjects(sut.orgUnit, @"ISG for Akamai");
XCTAssertEqualObjects(sut.orgName, @"Apple Inc.");
XCTAssertEqualObjects(sut.issuerCommonName, @"VeriSign Class 3 Extended Validation SSL SGC CA");
XCTAssertEqualObjects(sut.issuerOrgName, @"VeriSign, Inc.");
XCTAssertEqualObjects(sut.issuerOrgUnit, @"VeriSign Trust Network");
XCTAssertEqualObjects(sut.SHA1, @"96df534f6f4306ca474d9078fc346b20f856f0d4");
XCTAssertEqualObjects(sut.SHA256,
@"129d39ff4384197dc2bcbe1a83a69b3405b7df33254b1b1ee29a23847a23555a");
XCTAssertEqualObjects(sut.validFrom, [NSDate dateWithString:@"2013-11-14 00:00:00 +0000"]);
XCTAssertEqualObjects(sut.validUntil, [NSDate dateWithString:@"2015-11-14 23:59:59 +0000"]);
}
- (void)testInitWithValidPEMAfterKey {
NSString *pemWithKey = [self.testDataPrivateKey stringByAppendingString:self.testDataPEM1];
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:pemWithKey];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.commonName, @"Google Internet Authority G2");
}
- (void)testInitWithEmptyPEM {
NSString *badPEM = @"-----BEGIN CERTIFICATE----------END CERTIFICATE-----";
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:badPEM];
XCTAssertNil(sut);
}
- (void)testInitWithTruncatedPEM {
NSString *badPEM = @"-----BEGIN CERTIFICATE-----"
@"MIICXQIBAAKBgQDk2F9JsQQjKSveMwazXzFLbiiOD0RkDiRX1LTmQtVdi514F6l/";
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:badPEM];
XCTAssertNil(sut);
}
- (void)testInitWithInvalidPEM {
NSString *badPEM = @"This is not a valid PEM";
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:badPEM];
XCTAssertNil(sut);
badPEM = @"-----BEGIN CERTIFICATE-----Hello Thar-----END CERTIFICATE-----";
sut = [[SNTCertificate alloc] initWithCertificateDataPEM:badPEM];
XCTAssertNil(sut);
}
- (void)testInitWithMultipleCertsInPEM {
NSString *multiPEM = [self.testDataPEM1 stringByAppendingString:self.testDataPEM2];
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:multiPEM];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.commonName, @"Google Internet Authority G2");
}
- (void)testArrayOfCerts {
NSString *multiPEM = [self.testDataPEM1 stringByAppendingString:self.testDataPEM2];
NSArray *certs = [SNTCertificate certificatesFromPEM:multiPEM];
XCTAssertNotNil(certs);
XCTAssertEqual(certs.count, 2);
XCTAssertEqualObjects([certs[0] commonName], @"Google Internet Authority G2");
XCTAssertEqualObjects([certs[1] commonName], @"www.apple.com");
}
- (void)testPlainInit {
XCTAssertThrows([[SNTCertificate alloc] init]);
}
- (void)testEquals {
SNTCertificate *sut1 = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1];
SNTCertificate *sut2 = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1];
XCTAssertEqualObjects(sut1, sut2);
}
- (void)testDescription {
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1];
XCTAssertEqualObjects([sut description], @"/O=Google Inc/OU=(null)/CN=Google Internet Authority G2");
}
- (void)testSecureCoding {
XCTAssertTrue([SNTCertificate supportsSecureCoding]);
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1];
NSMutableData *encodedObject = [[NSMutableData alloc] init];
NSKeyedArchiver *archive = [[NSKeyedArchiver alloc] initForWritingWithMutableData:encodedObject];
[archive encodeObject:sut forKey:@"exampleCert"];
[archive finishEncoding];
NSKeyedUnarchiver *unarchive = [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedObject];
SNTCertificate *newCert = [unarchive decodeObjectForKey:@"exampleCert"];
XCTAssertNotNil(newCert);
XCTAssertEqualObjects(newCert, sut);
XCTAssertEqualObjects(newCert.SHA1, sut.SHA1);
}
- (void)testCachingAccessors {
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1];
id sutMock = OCMPartialMock(sut);
// Access each of the properties to get them cached
(void)sut.orgName;
(void)sut.issuerCommonName;
(void)sut.validFrom;
(void)sut.countryName;
(void)sut.issuerCountryName;
// Now break some of the properties
OCMExpect([sutMock x509ValueForLabel:OCMOCK_ANY fromDictionary:OCMOCK_ANY]);
OCMExpect([sutMock dateForX509Key:OCMOCK_ANY]);
XCTAssertEqualObjects(sut.orgName, @"Google Inc");
XCTAssertEqualObjects(sut.issuerCommonName, @"GeoTrust Global CA");
XCTAssertEqualObjects(sut.validFrom, [NSDate dateWithString:@"2013-04-05 15:15:55 +0000"]);
XCTAssertEqualObjects(sut.countryName, @"US");
XCTAssertEqualObjects(sut.issuerCountryName, @"US");
}
@end

View File

@@ -1,103 +0,0 @@
/// Copyright 2015 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import <XCTest/XCTest.h>
#import "SNTCertificate.h"
#import "SNTCodesignChecker.h"
/**
Tests for @c SNTCodesignChecker
Most of these tests rely on some facts about @c launchd:
* launchd is in /sbin
* launchd is PID 1
* launchd is signed
* launchd's leaf cert has a CN of "Software Signing"
* launchd's leaf cert has an OU of "Apple Software"
* launchd's leaf cert has an ON of "Apple Inc."
These facts are pretty stable, so shouldn't be a problem.
**/
@interface SNTCodesignCheckerTest : XCTestCase
@end
@implementation SNTCodesignCheckerTest
- (void)testInitWithBinaryPath {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithBinaryPath:@"/sbin/launchd"];
XCTAssertNotNil(sut);
}
- (void)testInitWithInvalidBinaryPath {
SNTCodesignChecker *sut =
[[SNTCodesignChecker alloc] initWithBinaryPath:@"/tmp/this/file/doesnt/exist"];
XCTAssertNil(sut);
}
- (void)testInitWithPID {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithPID:1];
XCTAssertNotNil(sut);
}
- (void)testInitWithInvalidPID {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithPID:999999999];
XCTAssertNil(sut);
}
- (void)testInitWithSelf {
// n.b: 'self' in this case is xctest, which should always be signed.
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithSelf];
XCTAssertNotNil(sut);
}
- (void)testPlainInit {
XCTAssertThrows([[SNTCodesignChecker alloc] init]);
}
- (void)testDescription {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithPID:1];
XCTAssertEqualObjects([sut description],
@"In-memory binary, signed by Apple Inc., located at: /sbin/launchd");
}
- (void)testLeafCertificate {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithPID:1];
XCTAssertNotNil(sut.leafCertificate);
}
- (void)testBinaryPath {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithPID:1];
XCTAssertEqualObjects(sut.binaryPath, @"/sbin/launchd");
}
- (void)testSigningInformationMatches {
SNTCodesignChecker *sut1 = [[SNTCodesignChecker alloc] initWithBinaryPath:@"/sbin/launchd"];
SNTCodesignChecker *sut2 = [[SNTCodesignChecker alloc] initWithPID:1];
XCTAssertTrue([sut1 signingInformationMatches:sut2]);
}
- (void)testCodeRef {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithSelf];
XCTAssertNotNil((id)sut.codeRef);
}
- (void)testSigningInformation {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithPID:1];
XCTAssertNotNil(sut.signingInformation);
XCTAssertEqualObjects(sut.signingInformation[@"source"], @"embedded");
}
@end

Some files were not shown because too many files have changed in this diff Show More