Compare commits

...

228 Commits

Author SHA1 Message Date
Russell Hancox
1d9af01353 Project: Bump dependency versions, fix reload command (#554)
* Project: Bump dependency versions, fix reload command

The reload command would fail if you used multiple compilation modes for
building as it would try extracting the versions from both comp modes.

The dependency bump includes a fix for #553
2021-05-03 21:37:42 -04:00
Adam Sindelar
9c6af7fc03 Docs: Add job posting link to README 2021-04-26 12:16:35 -04:00
Tom Burgin
543b1a29fe add default provisioning profile rules (#548) 2021-04-19 17:18:18 -04:00
Tom Burgin
625ec67789 handle PHONE_REGISTRATION_ERROR (#549) 2021-04-19 17:16:53 -04:00
Tom Burgin
c5696d71e7 add build release rule (#547) 2021-04-19 13:58:59 -04:00
Tom Burgin
5f3cef52de cleanup (#546) 2021-04-19 13:37:21 -04:00
Tom Burgin
eeed0b5aa6 santactl: migrate from fcmstream to fcmconnection (#545) 2021-04-19 11:51:32 -04:00
Russell Hancox
9ef171e663 Docs: Fix more broken docs links (#543) 2021-04-19 11:17:13 -04:00
Russell Hancox
ad1868a50f santad: Fix transitive rules when using the sysx cache feature (#540)
This fixes transitive allowlisting when `EnableSysxCache` is turned on, reduces the deadline timer to fire 5s before the ES deadline, remaps our DEBUG logs to NOTICE so they can be more easily seen in Console and prevents transitive rules being created for paths under /dev/.
2021-03-04 09:47:32 -05:00
Russell Hancox
78643d3c49 fileinfo: Don't use non-bundle dirs as possible ancestors (#537) 2021-02-01 11:09:32 -05:00
Russell Hancox
8b22c85a64 Project: run buildifier on BUILD files (#534) 2021-01-28 10:31:07 -05:00
Russell Hancox
58fe5d3d76 santad: Use OS_FALLTHROUGH (#535) 2021-01-28 10:30:47 -05:00
Russell Hancox
8b2227967e santad: Fix caching of deny decisions (#533) 2021-01-28 10:12:20 -05:00
Russell Hancox
65693acea1 Docs: fix syncing-overview link in santactl doc (#531) 2021-01-27 12:35:02 -05:00
Russell Hancox
7cea383930 Docs: the docs build can't use symlinks ref. out of the docs dir (#530) 2021-01-27 12:25:50 -05:00
headmin
5ae2376158 Docs: Add example .mobileconfig profile to enable Notifications settings (#529) 2021-01-27 11:00:34 -05:00
Russell Hancox
e851337eac Docs: Fix some broken links in the index (#528) 2021-01-27 10:32:30 -05:00
Russell Hancox
2e53834980 santactl/sync: retry individual requests during a sync (#526)
Each request is retried up to 5 times with gaps of 2s, 4s, 6s, 8s
2021-01-26 15:58:52 -05:00
Hugh Neale
aef139e93c The configuration key "enabled_transitive_rules" should be "enable_transitive_rules" (#525) 2021-01-26 14:20:15 -05:00
Russell Hancox
a9e5bf09a7 santad: Add some TODOs related to cache 2021-01-11 13:16:38 -05:00
Russell Hancox
4ee3f281c3 santactl/status: Output cache details for sysx 2021-01-11 13:16:38 -05:00
Russell Hancox
462ce89d42 Project: Fix test locations 2021-01-11 13:16:38 -05:00
Russell Hancox
44117833c0 Project: Fix build rule 2021-01-11 13:16:38 -05:00
Russell Hancox
8b6e029da2 Project: bump version to 2021.1
This is a new versioning scheme.
2021-01-11 13:16:38 -05:00
Russell Hancox
f183e246df santad: Make use of caching endpoint security optional 2021-01-11 13:16:38 -05:00
Russell Hancox
c60a35f280 santad: Add caching layer to EndpointSecurity
This first commit is very rough, just adding the caching as simply as
possible. Refactoring is needed.
2021-01-11 13:16:38 -05:00
Russell Hancox
4f65965277 santactl/fileinfo: Fix fileinfo tests on BigSur + multiarch plists (#523)
The fileinfo tests didn't work on BigSur because of some path and binary changes.

Also, the embeddedPlist method didn't work on fat binaries, of which there are now
many, because of M1 machines. I think we didn't notice this before because we pull
the embedded plist from the first arch listed in the headers dict which generally
seemed to pick x86_64 first but with the arm64/arm64e option being added
that now appears first.

Also fixed some errors handling 32-bit segment/sections and added a test for this.
2021-01-07 19:46:48 -05:00
Tom Burgin
01e4e15b81 santactl sync: add config option to enable legacy zlib content encoding (#522) 2020-12-23 10:36:39 -05:00
Russell Hancox
532cb37e0b CI: split out driver and userspace builds (#521) 2020-12-23 08:38:39 -05:00
Tom Burgin
9d379d3884 release: split out the kext into a separate release label (#520)
* fix SNTLoggingKernel BUILD rule (#518)

* release: split out santa-driver.kext

* release: update ci

* remove ipa script rule

* update ci
2020-12-19 18:23:54 -05:00
Tom Burgin
3e7a191bf7 fix SNTLoggingKernel BUILD rule (#518) 2020-12-17 16:35:13 -05:00
Ryan Diers
c5a048f4d9 santactl/sync: Use deflate as Content-Encoding instead of zlib
The latter was not standards-compliant.
2020-12-14 16:19:48 -05:00
Hugh Neale
f4769bad90 Added Zercurity to list of available sync servers (#511) 2020-12-08 20:29:28 -05:00
Russell Hancox
254497ad15 Project: don't reference obsolete rake commands in CONTRIBUTING (#513) 2020-12-08 09:57:42 -05:00
avanzini
0a83445838 Log pidversion along with pid. (#512) 2020-12-08 09:46:34 -05:00
Tom Burgin
eff287259e project: update Xcode project to build universal binaries (#509) 2020-11-17 16:18:16 -05:00
Russell Hancox
6f2c0e3457 Project: remove Travis, update CI status in README (#508) 2020-11-02 09:59:35 -05:00
Russell Hancox
38769f7cd1 Project: Add GitHub Actions CI workflow (#507) 2020-10-30 12:23:01 -04:00
Russell Hancox
fa785ad3c2 Kernel: fix some header imports (#505) 2020-10-26 10:05:25 -04:00
Russell Hancox
5dae0cabdd Project: fix some lint (#504) 2020-10-22 14:01:32 -04:00
Russell Hancox
a8b4f4ea7e Project: move travis to xcode12 (#503) 2020-10-22 13:50:32 -04:00
Russell Hancox
2221c93bbc santa-driver: Fix some new Xcode 12 warnings (#502)
The ossharedptr-misuse warning is generated from within system headers and I couldn't
find a simple way to prevent that other than disabling the warning entirely. We don't
use OSSharedPtr directly anyway.
2020-10-22 13:41:31 -04:00
Tom Burgin
d1c33baf35 project: add EnableDebugLogging option (#501)
* project: add EnableDebugLogging option

* review updates
2020-10-22 10:11:18 -04:00
Tom Burgin
d2bbdff373 Add the option to ignore actions from other ES clients (#498)
* [com.google.santa.daemon]: add the option to ignore actions from other ES clients

* review updates

* review updates
2020-10-21 13:20:13 -04:00
Russell Hancox
db1d65f944 Project: Update dependency versions (#500)
MOLAuthenticatingURLSession: v2.8 -> v2.9
rules_apple: v0.19.0 -> v0.20.0
2020-10-21 11:55:38 -04:00
Hugh Neale
d17aeac2f4 Make it possible to remotely set the FullSyncInterval (#494)
Make it possible for the sync server to set the FullSyncInterval with "full_sync_interval" during `preflight`
2020-10-01 13:47:55 -04:00
Hugh Neale
7840270dd0 Support for %hostname%, %uuid% and %serial% to eventDetailURLForEvent (#493)
Added support for %hostname%, %uuid% and %serial% to eventDetailURLForEvent to provide additional system information for blocked events & updated documentation references for supported URL params.
2020-08-31 10:38:35 -04:00
Russell Hancox
dcf44c9872 Fix video in README (#492)
Fixes #491
2020-08-27 17:28:40 -04:00
Russell Hancox
fc365c888f Create CNAME 2020-08-27 16:21:08 -04:00
Russell Hancox
85f0782399 Delete CNAME 2020-08-27 16:21:03 -04:00
Russell Hancox
64bc34c302 santactl/rule: make flags consistent with help text (#486) 2020-07-29 13:39:41 -04:00
Russell Hancox
e2fc4c735d santad: Prevent kext from being loaded when ES is running (#484) 2020-07-21 10:18:22 -04:00
Russell Hancox
ff9cb34490 Project: avoid public visibility (#483) 2020-07-20 12:19:14 -04:00
Russell Hancox
60405f1e10 Fix some recent warnings (#482) 2020-07-20 11:36:25 -04:00
Edward Eigerman
ac9d3b2adf Update AboutWindow.xib (#481)
Remove the word "whitelist" from the user-facing window.
2020-07-17 22:11:23 -04:00
Russell Hancox
7e8bd46da3 Docs: fix readthedocs config (#480)
Fixes #479
2020-07-16 12:37:45 -04:00
Tom Burgin
2f6ed455e5 add fork and exit logging (#478)
* added fork and exit logging

* what did you use?

* review updates
2020-07-09 16:36:23 -04:00
Tom Burgin
8cb86b6d1d syncservice: create stub for syncservice (#477)
* stub for santasyncservice

* update protocol
2020-07-08 15:42:42 -04:00
Russell Hancox
fc074f6014 santactl: Make logging around rule download clearer (#476) 2020-07-08 10:09:56 -04:00
bfreezy
a7856e60e8 Add example System Extension and TCC configuration profiles (#474)
* add system extension policy example

* add tcc profile policy example

* set bundle ID to com.google.santa.daemon
2020-06-11 20:44:59 -04:00
Russell Hancox
41a40c9fbd Docs: remove whitelist/blacklist (#471) 2020-06-08 13:46:18 -04:00
Russell Hancox
8c18f6ebf5 Project: Update terminology in README (#470) 2020-06-08 12:41:44 -04:00
Tom Burgin
949053fedd update kext cache (#469) 2020-06-08 11:15:22 -04:00
Russell Hancox
8d2c39b71d Project: update whitelist/blacklist -> allowlist/blocklist (part 1: code) (#468) 2020-06-08 11:11:30 -04:00
Russell Hancox
8f872fb4fc Project: disable known deprecated warnings (#467) 2020-06-04 11:52:24 -04:00
Russell Hancox
5512f8cf19 santad/sysx: Prevent unlinking databases (#465)
* santad/sysx: Prevent unlinking databases
2020-06-01 13:21:30 -04:00
Russell Hancox
6742b38e31 santad: If database is locked don't attempt to unlink it (#466)
* santad: If database is locked don't attempt to unlink it
2020-05-29 17:22:23 -04:00
Russell Hancox
d1635f7e11 santad: Fix decision fetching for certs by hash (#464)
* santad: Fix decision fetching for certs by hash

Fixes #463
2020-05-11 11:43:07 -04:00
Tom Burgin
e2b865c081 prevent a dual duel (#462)
* prevent a dual duel

* bump version
2020-05-04 11:42:08 -04:00
Bradley Kemp
012b02de5d Update EventDetailURL docs
%bundle_id% and %bundle_ver% do not exist any more, they were removed by 6f417a1775 (diff-3250262f27ab2cb96ad4b47abdc9d51fL95-L108)
2020-05-01 07:22:57 -04:00
Russell Hancox
11ebead617 Add security policy link to README 2020-04-08 13:26:05 -04:00
Russell Hancox
e3fbabfe37 Create SECURITY.md 2020-04-08 13:26:05 -04:00
Russell Hancox
8757da7822 Version bump to 1.13 2020-04-07 17:14:02 -04:00
Russell Hancox
428582f471 santa-driver: fix use-after-free race in Get*MemoryDescriptor() 2020-04-07 17:14:02 -04:00
Russell Hancox
6e0effc0f4 santa-driver: fix off-by-one bug in externalMethod 2020-04-07 17:14:02 -04:00
Russell Hancox
683114fbec santa-driver: fix integer overflow/underflow in bucket_counts() 2020-04-07 17:14:02 -04:00
Tom Burgin
d9ebb4e3db version bump (#455) 2020-03-17 16:27:40 -04:00
Tom Burgin
e6aaf2f198 Santa.app: don't request SystemExtension loading (#454) 2020-03-17 16:23:48 -04:00
Tom Burgin
1c3757d4ab santactl: don't watch for config changes (#453)
* santactl: don't watch for config changes

* bump version
2020-03-16 18:40:36 -04:00
Tom Burgin
4346bb29c2 santactl: sanitize rule payload (#450)
* santactl: sanitize rule payload

* version bump
2020-02-27 15:16:40 -05:00
Tom Burgin
09655df8fc com.google.santa.daemon: reorder cleanup() (#448)
* com.google.santa.daemon: reorder cleanup()

* version bump
2020-02-26 15:13:51 -05:00
Tom Burgin
7504cd36e1 Simplify install scripts (#447)
* installer to respect EnableSystemExtension

* conform
2020-02-26 12:58:12 -05:00
Tom Burgin
cafef66933 version bump (#446) 2020-02-25 15:14:42 -05:00
Tom Burgin
0c4e9d4b06 slurp up com.google.santa.daemon dsyms (#445) 2020-02-21 18:28:15 -05:00
Tom Burgin
ac07f5d54b santad: add prefixes on a background thread (#444)
* add prefixes on a background thread

* version bump
2020-02-21 16:54:42 -05:00
Tom Burgin
d116f7b01e santad: wait for driver connection before adding prefix filters (#443)
* wait for driver connection before adding prefix filters

* version bump

* fix travis build
2020-02-21 14:58:12 -05:00
Tom Burgin
63ca34bc54 santad: fix launch path and args for loading the system extension (#442)
* missing /

* version bump

* that was close
2020-02-20 20:01:42 -05:00
Tom Burgin
c894029c33 version bump to 1.3 (#441) 2020-02-19 17:08:30 -05:00
Tom Burgin
de2bdd6653 update EnableSystemExtension when the config changes 🤦 (#440) 2020-02-19 17:03:58 -05:00
Tom Burgin
2d066ad671 version bump to 1.2 (#439) 2020-02-19 14:06:20 -05:00
Tom Burgin
24854d4ad7 Config: EnableSystemExtension option (#438)
* Config: add EnableSystemExtension option

* format

* i don't trust kvo

* review updates
2020-02-18 17:48:06 -05:00
Russell Hancox
99ee0af178 Project: bump version to 1.1 (#436) 2020-02-12 11:17:44 -05:00
Russell Hancox
bf6f78df09 common: Eliminate VLA usage in SNTFileInfo (#435)
VLAs complicate static analysis and bloat stack size. Replace VLA allocation with calls to malloc and free
2020-02-11 10:55:57 -05:00
Russell Hancox
c05806916b santad: Add config flag to block all binaries with bad signatures. (#434)
* santad: Add option to block all binaries with bad signatures.
2020-02-10 13:45:22 -05:00
Russell Hancox
e48ce0cfe3 santad: Move signature fetching into SNTPolicyProcessor (#433)
This also removes an unnecessary hash, checks code signatures on non-MachO files (which is rare but possible) and fixes a rare crash in EndpointSecurityManager
2020-02-07 14:32:00 -05:00
Tom Burgin
eabca469b9 update readme with a note about system extension (#431) 2020-02-06 12:50:33 -05:00
Russell Hancox
f6dc36e812 santactl/sync: Skip event upload for clean sync
This lets a clean sync clear out the existing events without attempting to upload them.
2020-01-13 14:56:01 -05:00
Russell Hancox
ac7cbdfd16 Project: update apple rules to 0.19.0 2020-01-13 14:25:38 -05:00
Tom Burgin
d1d008af0a don't log TRUNCATE and don't log fileops from com.google.santa.daemon (#428)
* don't log TRUNCATE and don't log fileops from com.google.santa.daemon

* review updates
2019-12-20 14:00:16 -05:00
Tom Burgin
5db56e01f5 cleanup 10.14 -> 10.15 upgrade artifacts (#427)
* cleanup 10.14 -> 10.15 upgrade artifacts

* exit exit

* exit exit
2019-12-19 15:56:59 -05:00
Tom Burgin
726c49bec5 com.google.santa.daemon: handle es deadline (#426)
* com.google.santa.daemon: deny execs that are about to exceed the es deadline

* update comment

* actually handle the deadline
2019-12-16 13:03:20 -05:00
Tom Burgin
ae5db5dde7 com.google.santa.daemon: lookup the tty for deny decisions before posting the decision (#425) 2019-12-13 15:24:21 -05:00
Tom Burgin
2671807f0e com.google.santa.daemon: don't reload if versions have not changed (#424) 2019-12-12 14:02:23 -05:00
Tom Burgin
70c8626016 fix com.google.santa.daemon path for critical system binary checking (#423) 2019-12-12 11:41:16 -05:00
Tom Burgin
436c472a49 es event provider: support transitive whitelisting (#422)
* es event provider: support transitive whitelisting

* remove vector

* truncate check

* consistent log style

* review updates
2019-12-12 11:30:05 -05:00
Tom Burgin
ed5be6b062 com.google.santa.daemon: async es message handling (#421)
* fix Santa.xcodeproj

* com.google.santa.daemon: some es tweaks

* review updates
2019-12-09 11:21:12 -05:00
Russell Hancox
a38f24728a santactl/status: Remove kext section of status on 10.15+ 2019-11-25 19:32:54 -05:00
Russell Hancox
4af026356f santactl/version: print useful status for santa-driver on 10.15 2019-11-25 19:32:30 -05:00
Russell Hancox
c6e1bb5618 santad: Fix Apple-cert trust 2019-11-25 19:31:51 -05:00
Tom Burgin
e64d2e7ad4 Update README.md (#416) 2019-11-10 12:18:33 -05:00
Russell Hancox
3d393e9aa4 santa-driver: Workaround 10.15 SDK Dispatch() issue 2019-11-09 08:18:51 -05:00
Russell Hancox
b8f3122ee9 santad: Don't need macos_command_line_application anymore 2019-11-08 22:22:09 -05:00
Russell Hancox
8acfa6591e santa-driver: Fix compilation of SNTPrefixTree 2019-11-08 22:22:09 -05:00
Russell Hancox
25b75b0e1b santad: Re-work targets to avoid unnecessary postprocessing 2019-11-08 22:22:09 -05:00
Russell Hancox
cb01b77f84 Project: no longer need to move the embedded provisionprofile 2019-11-08 22:22:09 -05:00
Russell Hancox
61582a0324 Project: standardize Info.plist and entitlement paths 2019-11-08 22:22:09 -05:00
Russell Hancox
a17b5d51a4 Project: more BUILD file cleanups, remove commented provisioning_profile attrs 2019-11-08 22:22:09 -05:00
Russell Hancox
447ea8674b Project: run buildifier on all bazel files, fix typo (#405) 2019-11-08 22:22:09 -05:00
Russell Hancox
c5eec850e1 Project: update santad path (#404) 2019-11-08 22:22:09 -05:00
Russell Hancox
1870631150 Project: Update bazel rules for endpointsec (#403) 2019-11-08 22:22:09 -05:00
Russell Hancox
20ed1659c1 santad: Don't store rules for santad/launchd, keep in-mem (#402) 2019-11-08 22:22:09 -05:00
Tom Burgin
258de3efba handle all ACTION_RESPOND_* (#401) 2019-11-08 22:22:09 -05:00
Tom Burgin
394fd5fab9 add required santad entitlements (#400) 2019-11-08 22:22:09 -05:00
Russell Hancox
53b7ef86ed santad: Log file changes, use prefix trees (#398) 2019-11-08 22:22:09 -05:00
Russell Hancox
423479771e santad: Use args from endpointsecurity rather than using the sysctl (#396)
This should be much more reliable and, in theory, faster.
2019-11-08 22:22:09 -05:00
Tom Burgin
933271826b simplify santabundleservice xpc connection protocol (#397)
* simplify santabundleservice xpc connection protocol

* fix BUILD deps

* fix BUILD deps

* know
2019-11-08 22:22:09 -05:00
Tom Burgin
880170ea7d make santabundleservice a command line app (#395)
* make santabundleservice a command line app

* bazel - don't build santabs.xpc
2019-11-08 22:22:09 -05:00
Russell Hancox
e58ec37881 santad: Fix BUILD after moving EventProviders (#394) 2019-11-08 22:22:09 -05:00
Russell Hancox
dece50dd10 Logging: under 10.15, force santad into syslog mode (#393) 2019-11-08 22:22:09 -05:00
Russell Hancox
9db9fc6009 santad: Move event providers into a new group, make ES connection logic smarter (#392) 2019-11-08 22:22:09 -05:00
Russell Hancox
f38c030805 Add file IDs to messages (#391) 2019-11-08 22:22:09 -05:00
Tom Burgin
d8060d3af9 update component paths (#390) 2019-11-08 22:22:09 -05:00
Russell Hancox
34b4090b42 Project: fix some new Xcode11 warnings (#389) 2019-11-08 22:22:09 -05:00
Tom Burgin
c6ca3d64b3 add SNTEventProvider interface (#388)
* Add SNTEventProvider interface

* execution controller test should use the event provider interface

* * Xcode project: Use manual signing
* SNTEndpointSecurityManager: Don't cache deny decisions
* Review updates

* review updates
2019-11-08 22:22:09 -05:00
Tom Burgin
4913426631 * Added Xcode project + pods (#387)
* * Added Xcode project + pods
* Cleaned up unused SNTXPCUnprivilegedControlInterface MachServices id.
* Change santad's MachServices id to be compatible with the default SystemExtension namespace template.

* pods

* bazel

* switch MachService name for 10.15+

* build with SystemExensions framework

* build with Xcode 11

* launchd.plist fix

* use @available

* * Request SystemExtension activation on a background thread.
* Create a constant for the "com.google.santa.daemon" SystemExtension id.
2019-11-08 22:22:09 -05:00
Russell Hancox
455a1c76c3 Docs: update building docs. Fixes #411.
Will need updating again when we merge endpointsec.
2019-11-08 22:11:35 -05:00
Russell Hancox
e5a5f6f9fb Bump MOLAuthenticatingURLSession version (#384) 2019-08-02 16:16:51 -04:00
ancdesign
7ef88d06a5 fix typo (inital -> intial) (#378) 2019-07-29 08:29:04 -04:00
Russell Hancox
bc82d7988b santad: Add /usr/lib/dyld to critical system binaries (#376)
dyld is also authorized by santad and a bad cache eviction plus trustd/ocspd not running can result in deadlock.

Fixes #375, probably.
2019-07-22 17:05:34 -04:00
Russell Hancox
545fa858e4 SantaGUI: ensure bundle listeners are invalidated (#373)
When setting a new bundle service listener, it was possible for an existing listener to be replaced without invalidating it first. This can cause crashes if a process somehow tries to connect to that listener later on.
2019-07-17 11:27:50 -04:00
Russell Hancox
71c917649e Set theme jekyll-theme-cayman 2019-07-12 13:56:04 -04:00
Tom Burgin
3781556cf5 Create CNAME 2019-07-12 13:52:59 -04:00
Tom Burgin
765d10a7c3 rename Docs -> docs (#372) 2019-07-12 13:50:19 -04:00
Tom Burgin
3583113381 santactl: nil prefix value check - fixes #361 (#362)
* santactl: nil prefix value check - fixes #361

* santactl: check all filters
2019-07-12 13:13:43 -04:00
Tom Burgin
46cd60e579 Use updated deps (#370)
* Use updated deps

* update travis build settings
2019-06-23 12:43:50 -04:00
Tom Burgin
8198e59736 tests: Create a SantaPrefixTree userland lib (#359) 2019-03-04 14:22:19 -05:00
Russell Hancox
c5f0f5d177 Project: Use MOLCodesignChecker v2.1 (#356) 2019-02-27 16:15:13 -05:00
Tom Burgin
ebc93954be SantaGUI: Fix message text. Add support for Dark Mode. (#354) 2019-02-21 16:53:20 -05:00
Russell Hancox
cb4d2984b3 SantaCache: Fix possible divide by zero in bucket count calculation (#353) 2019-02-20 17:56:42 -05:00
Russell Hancox
4c2018ef67 SantaCache: Fix flaky test (#352) 2019-02-20 13:18:40 -05:00
Russell Hancox
06d8295d0a Project: Use apple_resource_group for test resources. Fix bazelrc (#351) 2019-02-19 12:20:30 -05:00
Tom Burgin
ef8e9975e9 c++11 features (#350) 2019-02-15 17:14:10 -05:00
Russell Hancox
31509f4b9c Project: Minor tidy-ups (#349) 2019-02-15 16:59:32 -05:00
Tom Burgin
497c1f393f project format (#347)
* starlark format

* Source/santa-driver -> Source/santa_driver

* buildifier

* kernel_tests unloads the driver

* review updates

* review updates
2019-02-15 15:38:06 -05:00
Tom Burgin
8334a245c7 cleanup unused includes (#346) 2019-02-15 11:12:38 -05:00
Tom Burgin
e8826a2941 add licenses and default_visibility to each BUILD file (#345)
* add licenses and default_visibility to each BUILD file

* remove default_visibility the bins are public

* oops
2019-02-15 11:03:28 -05:00
Tom Burgin
ef040c1e7d resurrect action=BUNDLE logs (#344)
They were lost in the refactoring of the logging utility 4a2cf9d722.
2019-02-11 13:46:37 -05:00
Russell Hancox
dc692c8256 Project: Move tests with the code they're testing (#343)
Add helper to make declaring unit tests easier
Add unit_tests test_suite containing all unit tests
Fix reload rule
Update to workspace-relative header locations that were missed before
2019-02-06 15:09:09 -05:00
Russell Hancox
e9c7bfc087 Project: Make all imports workspace-relative, remove include attributes from all rules. (#339) 2019-01-22 14:24:11 -05:00
Russell Hancox
22c72625c8 Project: Split BUILD file into several. Part 1/3 (#338)
* Project: Split BUILD file into several. Part 1/3

The tests fail in this PR because the rules need updating. I'll fix them in a follow-up PR.
2019-01-22 12:06:48 -05:00
Tom Burgin
65a2212890 BUILD: buildifier formatting (#336) 2019-01-17 16:23:37 -05:00
Russell Hancox
0a7c08cafc santactl/version: Make version command not crash with new Santa.app location (#335) 2019-01-16 17:16:39 -05:00
Tom Burgin
831a32160b BUILD: Some tweaks (#334) 2019-01-16 16:49:54 -05:00
Russell Hancox
b186419e54 Sync: Remove LogUpload. (#333)
Fixes #331
2019-01-07 14:27:44 -05:00
Russell Hancox
1dc579c00f Project: Fix badges in README (#332) 2019-01-07 12:28:57 -05:00
Russell Hancox
abdd6c319a Project: Update docs for switch to bazel (#330)
* Project: Update docs for switch to bazel

I also made it so the santactl compilation won't include debug-only commands in release builds and the release rule will fail on a non-opt build.
2019-01-04 19:45:00 -05:00
Russell Hancox
5dd93fadfa Project: Convert to bazel, part 2 (#329)
Project: Convert to bazel, part 2

The main thing to call out in this PR is that Santa.app is now embedded inside santa-driver.kext along with everything else. The package will handle updating this automatically but it should be called out in release notes to make everyone aware.

* Switch to using macos_kernel_extension and macos_xpc_service, stop using product_type.
* Have Bazel embed all related binaries inside santa-driver.kext, including Santa.app. This simplifies the :release and :reload rules.
* Add commands for unload, load and reload, removing any need to keep the Rakefile around 😃
* Make the :kernel_tests rule a command that replicates what the Rakefile did for this.
* Added a project-wide .bazelrc that always generates dSYMs

Documentation changes to follow, as all the building instructions are now out of date.
2019-01-04 16:23:35 -05:00
Russell Hancox
e6fcbf59df Proj: Convert to Bazel build, remove other build systems. (#326)
This necessitated fixing some warnings, updating the resource
locations inside some tests and updating the Travis config.

I'll send a follow-up PR shortly that adds the fuzzing targets and updates the Rakefile and documentation.
2018-12-14 11:57:32 -05:00
Tom Burgin
9fd04ed301 SantaPrefixTree: Fix a bug and add some more tests (#324)
* SantaPrefixTree: Fix a threading bug.
Tests: Add logic tests for SantaPrefixTree.

* clean up

* don't ifdef so much

* more #define less #ifdef

* less lambda more of rah's ideas
2018-12-03 17:08:16 -05:00
Tom Burgin
e4b5f595ce * Add note about vnode map. (#323)
* Fix logic tests under Xcode 10.1.
2018-11-19 12:44:28 -05:00
Tom Burgin
212b02589b Update README.md (#319) 2018-11-12 10:50:28 -05:00
Tom Burgin
42c3631995 Update uninstall.sh (#318)
I think this was added by accident.
2018-11-09 16:13:38 -05:00
Tom Burgin
2695355dd2 add in-kernel filemod prefix filter (#313)
* add in-kernel filemod prefix filter

* byte lookup

* added pruning and tests

* clang-format

* add TODO

* don't need seen

* review updates

* reset filter on client connect

* DisconnectClient: reset filter
AddPrefix: when a branch is needed create the whole branch immediately

* don't use strlen in HasPrefix
use strnlen in AddPrefix
up max nodes to 1024

* use new[] and delete[] for the prune "stack"
revert clang-format changes to kernel tests
remove reset node count

* words

* count not size
2018-11-08 15:37:30 -05:00
Darío Hereñú
db0cd861d6 README: Paragraph formatting L157 to L168 (proposal) (#317) 2018-11-08 13:12:23 -05:00
Victor Vrantchan
57d6a962de update deployment documentation (#312)
- SyncBaseURL is not overridable by the server.
- ATS requires a self signed certificate to exist in the system roots. Providing roots enables in the Santa configuration enables pinning.

Closes #309
2018-11-05 13:06:49 -05:00
Russell Hancox
91608d7366 santad: Document implicit rule ordering (#315)
Also add a test to ensure this doesn't change one day without us noticing
2018-11-02 12:12:19 -04:00
Russell Hancox
7d4f1ffc45 config: Ensure syncBaseURL ends with a / (#311)
* config: Ensure syncBaseURL ends with a /

Without the trailing / the last path component is removed by `URLWithString:relativeToURL:`
2018-10-09 18:27:04 -04:00
Victor Vrantchan
ba539bb555 docs: remove space before period. (#308) 2018-10-07 15:09:17 -04:00
Victor Vrantchan
d9ecbf06c0 Document recent changes to sync server configuration (#307)
- bundles_enabled became enable_bundles.
- enabled_transitive_whitelisting was added.

Both changes were implemented in #300
2018-10-07 15:09:04 -04:00
Tom Burgin
01df4623c7 santa-driver: add back the root and non-root caches (#302)
* santa-driver: add back the root and non-root caches

* cachehistogram: clarify buckets and entries

* review changes
2018-09-26 12:41:04 -04:00
Tom Burgin
c9cb91a22e ocspd also seems integral to cs validation (#301) 2018-09-26 08:45:39 -04:00
Russell Hancox
1f9d60aecc common: Allow transitive whitelisting to be controlled by sync servers. (#300)
Also rename TransitiveWhitelistingEnabled -> EnableTransitiveWhitelisting and BundlesEnabled -> EnableBundles
2018-09-26 08:43:31 -04:00
nguyen-phillip
52c5b5aade add newline to output of "santactl help sync" (#299) 2018-09-25 13:55:52 -04:00
Tom Burgin
2d98173c51 fix cache invalidation on macOS Mojave (#298) 2018-09-21 15:22:34 -04:00
Tom Burgin
5e3f13be70 intentional fall-through (#297)
* intentional fall-through

* russell's idea
2018-09-20 18:40:23 -04:00
Tom Burgin
90b894b88a santad: add critical system binaries (#296)
* santad: add critical system binaries

* review updates

* use a getter
2018-09-20 17:17:12 -04:00
nguyen-phillip
6dc7387881 Add transitive whitelisting to Santa (#224)
Add transitive whitelisting.

Binaries may be identified with WHITELIST_COMPILER rules.  Any executable they output will then be marked locally with a transitive whitelist rule and allowed to run if the TransitiveWhitelistingEnabled config key is true.
2018-07-20 11:47:04 -04:00
Tom Burgin
b14b017d72 santa-driver: add IOMatchCategory (#292) 2018-07-18 11:33:09 -04:00
Tom Burgin
d0ede18bf4 MOLCertificate --> 1.9 (#290) 2018-07-06 12:56:15 -04:00
Alessandro Gario
6d223aea03 Various fixes (documentation, and an additional check on the JSON received from the syncserver) (#288)
* santa-driver: Fix documentation warnings
* SantaCache: Fix documentation warnings
* santactl: Always make sure that the syncserver JSON is a dictionary
2018-07-06 09:42:22 -04:00
Alessandro Gario
f7986b0a05 Update MOLXPCConnection; add support for unprivileged XPC interfaces (#287)
* Update MOLXPCConnection; add support for unprivileged XPC interfaces

* Code review changes
2018-07-05 17:20:49 -04:00
Alessandro Gario
629e70287c Add CMake support, implement fuzzers (#284)
The new CMake project allows the user to select which SDK version
to use. The Xcode path is also configurable to support non-standard
installation paths and/or systems with multiple versions installed.

Code signing can now be configured via command line, using the
CODESIGN_IDENTITY environment variable.

New fuzzing targets (libFuzzer)
 - SantaCache
 - santactl
 - santad

New make targets:
 - tests: Runs the tests
 - fuzz: Runs the fuzzer
 - redist: Regenerates the redistributable folder
 - install: Installs Santa
2018-06-29 14:15:16 -04:00
Russell Hancox
3c2a88144c santad: Wait for driver appearance using IOKit notifications. (#278)
Continue loading without driver, report status in santactl.
2018-06-12 16:15:41 -04:00
dgw
3651f18566 readme: s/precendence/precedence/ (#283) 2018-06-12 15:01:42 -04:00
Russell Hancox
472fea75b1 KernelTests: Simplify kernel tests (#282)
This change does 2 major things:

1) Makes the test runnable from within Xcode, unloading any running
santad and santa-driver, loading the just-built driver from the same folder and
then running each test.

2) Makes each test responsible for declaring what should happen to
incoming requests from the driver, instead of keeping all of that
code near the top of the file. This makes each test much clearer in what
should be happening.
2018-06-12 09:53:20 -04:00
Russell Hancox
e1b5438865 santa-driver: Re-factor some destruction methods (#281) 2018-06-11 12:54:29 -04:00
Russell Hancox
fbbf523333 santa-driver: Stop catching hasdirtyblks, the loader handles this (#280) 2018-06-06 18:56:00 -04:00
Russell Hancox
15fa53d744 santa-driver: Switch to a struct for vnode IDs, holding both the file… (#276)
santa-driver: Switch to a struct for vnode IDs, holding both the filesystem ID and vnode ID.

Also drop the separate caches for root/non-root as this doesn't offer any benefit anymore.
2018-06-05 06:43:49 -04:00
Russell Hancox
9595f80fde santad: Don't get code signature info for non Mach-O's. (#277) 2018-06-05 06:43:11 -04:00
Russell Hancox
61a67e45c1 SantaCache: Add command to print histogram of bucket distribution (#275)
* SantaCache: Add santactl command to print histogram of bucket distribution.

This currently only prints the distribution of the non-root cache. In the near future I'll unify the caches again which stops this being a problem.
2018-06-01 17:02:39 -04:00
Russell Hancox
143e690dab SantaCache: Add very basic distribution test (#273)
* SantaCache: Add very basic distribution test
2018-06-01 13:11:24 -04:00
Russell Hancox
ebd507f143 Project: Update cocoapods, again (#274) 2018-06-01 12:01:21 -04:00
Russell Hancox
f71bc0a8f7 santa-driver: Fix cache CAS operations, which haven't been working (#272)
* santa-driver: Fix cache CAS operations, which haven't really been working.
2018-06-01 11:38:25 -04:00
Russell Hancox
edc0c72464 SantaCache: Templatize key types (#271) 2018-05-30 15:50:23 -04:00
Tom Burgin
c3ce4f718b Update configuration.md (#270) 2018-05-30 11:06:45 -04:00
Tom Burgin
40ee482973 Update SNTSyslogEventLog.m (#269)
remove extra space
2018-05-29 16:39:18 -04:00
Mitchell Grenier
a5d2e6fdd2 Optional MachineID for Logs (#256)
* First draft, no UUID caching

* Cache UUID in SNTEventLog super class

* Add a configuration flag for UUID decoration

* Port from UUID to MachineID

* KVO complicance

* Remove extra newline I accidentally introduced
2018-05-29 16:16:21 -04:00
Tom Burgin
e9a835a642 log deny because of dirty vnode (#267)
* log dirty vnode blocks

* review update
2018-05-25 14:16:16 -04:00
Tom Burgin
ac7b95ceb6 santa-driver: do not invalidate cached decisions on KAUTH_VNODE_ACCESS (#266)
* santa-driver: do not invalidate cached decisions on KAUTH_VNODE_ACCESS

* downtown
2018-05-25 10:47:48 -04:00
Russell Hancox
055b2d8ede Update project for Xcode 9.1, add codesigning flags (#264)
* Project: Update project to Xcode 9.1 and handle all the new warnings that entails.

* Project: Add library-validation and kill flags to codesigning options
2018-05-22 10:13:59 -04:00
Russell Hancox
a75cd0a0f5 Update README.md (#263)
Update the sync client section to point at known open-source solutions, remove the Xcode 7.3.1 build requirement
2018-05-18 12:26:37 -04:00
Matthew Suozzo
2b1ddf9a4e Fix typo in sync help text (#259) 2018-05-09 21:53:51 -04:00
Tom Burgin
b70442e483 Project: Use MOLXPCConnection (#258)
* Project: Use MOLXPCConnection

* review fixes
2018-05-09 11:40:35 -04:00
Tom Burgin
798b0fab15 fileinfo: whitelist bundle extensions when performing an ancestor search (#257)
* fileinfo: whitelist bundle extensions when performing an ancestor search

* fix tests

* conform to russell's every whim
2018-05-07 13:57:58 -04:00
Russell Hancox
e8630132d7 Project: make building on case-sensitive volumes work (#255) 2018-04-27 12:58:54 -04:00
Russell Hancox
273ae5f21a santad: Missed an import (#254) 2018-04-27 10:51:53 -04:00
Tom Burgin
06b688fef4 Update .travis.yml (#253) 2018-04-26 17:23:32 -04:00
Russell Hancox
59cc038ab2 All: stop using @import for reasons. (#252) 2018-04-26 17:19:19 -04:00
Tom Burgin
ea5a6c3438 downstream --> upstream changes (#251) 2018-04-25 16:16:56 -04:00
Tom Burgin
e2adfdf3cf Add EventLog Docs (#250) 2018-04-24 16:33:53 -04:00
Tom Burgin
5ee6531627 santad / santactl: validate all architectures within universal binaries (#249) 2018-04-24 16:11:49 -04:00
Tom Burgin
1cf8ee09e1 sync-state: Mitigate com.apple.ManagedClient flapping (#248)
* sync-state: Mitigate com.apple.ManagedClient flapping

* 10 min
2018-04-09 13:34:33 -04:00
Tom Burgin
4a2cf9d722 santad: event logger (#246)
* kext symbols

* santad: Create FileLog and Syslog options

* review updates

* review updates

* be a good citizen and let go of things you do not need
2018-04-03 13:15:12 -04:00
Tom Burgin
6a6a32c1cf santactl: Update to MOLFCMClient v1.7 (#245) 2018-03-13 13:07:44 -04:00
Tom Burgin
ce03611b52 santabs: Serialize calls to -[SNTBundleService createConnection] (#244) 2018-03-12 17:04:53 -04:00
Tom Burgin
bbe9f83878 Import fixes (#243)
* All: use common import style for cocoapods <PodName/PodName.h>

* All: Update Pods
2018-03-12 16:02:55 -04:00
Tom Burgin
40e6c6aa92 sync-state: perform sync-state operations on a serial q (#242)
* sync-state serial

* delete it
2018-03-07 17:35:02 -05:00
Tom Burgin
9f6ccf092a code cleanup (#241) 2018-02-26 10:51:44 -05:00
275 changed files with 13014 additions and 7732 deletions

2
.bazelrc Normal file
View File

@@ -0,0 +1,2 @@
build --apple_generate_dsym --define=apple.propagate_embedded_extra_outputs=yes
build --host_force_python=PY2

25
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: CI
on:
push:
paths-ignore:
- 'docs/**'
branches:
- '*'
pull_request:
paths-ignore:
- 'docs/**'
branches:
- main
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: Build Userspace
run: bazel build --apple_generate_dsym -c opt :release --define=SANTA_BUILD_TYPE=ci
- name: Build Driver
run: bazel build --apple_generate_dsym -c opt :release_driver --define=SANTA_BUILD_TYPE=ci
- name: Test
run: bazel test :unit_tests --define=SANTA_BUILD_TYPE=ci

8
.gitignore vendored
View File

@@ -1,11 +1,9 @@
.DS_Store
Build
santa-*
!santa-driver
!*.md
default.profraw
*.provisionprofile
bazel-*
Pods
Santa.xcodeproj/xcuserdata
Santa.xcodeproj/project.xcworkspace
Santa.xcworkspace/xcuserdata
Santa.xcworkspace/xcshareddata
Source/DevelopmentTeam.xcconfig

View File

@@ -1,14 +0,0 @@
---
language: objective-c
cache:
- bundler
- cocoapods
sudo: false
osx_image: xcode7
before_install:
- gem install cocoapods xcpretty
- pod setup >/dev/null
script:
- xcodebuild -workspace Santa.xcworkspace -scheme All -derivedDataPath build build test CODE_SIGN_IDENTITY='' | xcpretty -sc && exit ${PIPESTATUS[0]}

235
BUILD Normal file
View File

@@ -0,0 +1,235 @@
load("@build_bazel_rules_apple//apple:versioning.bzl", "apple_bundle_version")
load("//:helper.bzl", "run_command")
load("//:version.bzl", "SANTA_VERSION")
package(default_visibility = ["//:santa_package_group"])
licenses(["notice"])
exports_files(["LICENSE"])
# The version label for mac_* rules.
apple_bundle_version(
name = "version",
build_version = SANTA_VERSION,
short_version_string = SANTA_VERSION,
)
# Used to detect release builds
config_setting(
name = "release_build",
values = {"define": "SANTA_BUILD_TYPE=release"},
visibility = [":santa_package_group"],
)
# Used to detect CI builds
config_setting(
name = "ci_build",
values = {"define": "SANTA_BUILD_TYPE=ci"},
visibility = [":santa_package_group"],
)
# Used to detect optimized builds
config_setting(
name = "opt_build",
values = {"compilation_mode": "opt"},
)
package_group(
name = "santa_package_group",
packages = ["//..."],
)
################################################################################
# Loading/Unloading/Reloading
################################################################################
run_command(
name = "unload",
cmd = """
sudo launchctl unload /Library/LaunchDaemons/com.google.santad.plist 2>/dev/null
sudo launchctl unload /Library/LaunchDaemons/com.google.santa.bundleservice.plist 2>/dev/null
sudo kextunload -b com.google.santa-driver 2>/dev/null
launchctl unload /Library/LaunchAgents/com.google.santa.plist 2>/dev/null
""",
)
run_command(
name = "load",
cmd = """
sudo launchctl load /Library/LaunchDaemons/com.google.santad.plist
sudo launchctl load /Library/LaunchDaemons/com.google.santa.bundleservice.plist
launchctl load /Library/LaunchAgents/com.google.santa.plist
""",
)
run_command(
name = "reload",
srcs = [
"//Source/santa:Santa",
"//Source/santa_driver",
],
cmd = """
set -e
rm -rf /tmp/bazel_santa_reload
unzip -d /tmp/bazel_santa_reload \
$${BUILD_WORKSPACE_DIRECTORY}/bazel-out/*$(COMPILATION_MODE)*/bin/Source/santa_driver/santa_driver.zip >/dev/null
unzip -d /tmp/bazel_santa_reload \
$${BUILD_WORKSPACE_DIRECTORY}/bazel-out/*$(COMPILATION_MODE)*/bin/Source/santa/Santa.zip >/dev/null
echo "You may be asked for your password for sudo"
sudo BINARIES=/tmp/bazel_santa_reload CONF=$${BUILD_WORKSPACE_DIRECTORY}/Conf \
$${BUILD_WORKSPACE_DIRECTORY}/Conf/install.sh
rm -rf /tmp/bazel_santa_reload
echo "Time to stop being naughty"
""",
)
################################################################################
# Release rules - used to create a release tarball
################################################################################
genrule(
name = "release",
srcs = [
"//Source/santa:Santa",
"Conf/install.sh",
"Conf/uninstall.sh",
"Conf/com.google.santa.bundleservice.plist",
"Conf/com.google.santad.plist",
"Conf/com.google.santa.plist",
"Conf/com.google.santa.asl.conf",
"Conf/com.google.santa.newsyslog.conf",
"Conf/Package/Makefile",
"Conf/Package/postinstall",
"Conf/Package/preinstall",
],
outs = ["santa-" + SANTA_VERSION + ".tar.gz"],
cmd = select({
"//conditions:default": """
echo "ERROR: Trying to create a release tarball without optimization."
echo "Please add '-c opt' flag to bazel invocation"
""",
":opt_build": """
# Extract Santa.zip
for SRC in $(SRCS); do
if [ "$$(basename $${SRC})" == "Santa.zip" ]; then
mkdir -p $(@D)/binaries
unzip -q $${SRC} -d $(@D)/binaries >/dev/null
fi
done
# Copy config files
for SRC in $(SRCS); do
if [[ "$$(dirname $${SRC})" == *"Conf" ]]; then
mkdir -p $(@D)/conf
cp $${SRC} $(@D)/conf/
fi
done
# Gather together the dSYMs. Throw an error if no dSYMs were found
for SRC in $(SRCS); do
case $${SRC} in
*santad.dSYM*Info.plist)
mkdir -p $(@D)/dsym
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santad.dSYM
;;
*santactl.dSYM*Info.plist)
mkdir -p $(@D)/dsym
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santactl.dSYM
;;
*santabundleservice.dSYM*Info.plist)
mkdir -p $(@D)/dsym
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santabundleservice.dSYM
;;
*Santa.app.dSYM*Info.plist)
mkdir -p $(@D)/dsym
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/Santa.app.dSYM
;;
*com.google.santa.daemon.systemextension.dSYM*Info.plist)
mkdir -p $(@D)/dsym
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/com.google.santa.daemon.systemextension.dSYM
;;
esac
done
# Cause a build failure if the dSYMs are missing.
if [[ ! -d "$(@D)/dsym" ]]; then
echo "dsym dir missing: Did you forget to use --apple_generate_dsym?"
echo "This flag is required for the 'release' target."
exit 1
fi
# Update all the timestamps to now. Bazel avoids timestamps to allow
# builds to be hermetic and cacheable but for releases we want the
# timestamps to be more-or-less correct.
find $(@D)/{binaries,conf,dsym} -exec touch {} \\;
# Create final output tar
tar -C $(@D) -czpf $(@) binaries dsym conf
""",
}),
heuristic_label_expansion = 0,
)
genrule(
name = "release_driver",
srcs = [
"//Source/santa_driver",
],
outs = ["santa-driver-" + SANTA_VERSION + ".tar.gz"],
cmd = select({
"//conditions:default": """
echo "ERROR: Trying to create a release tarball without optimization."
echo "Please add '-c opt' flag to bazel invocation"
""",
":opt_build": """
# Extract santa_driver.zip
for SRC in $(SRCS); do
if [ "$$(basename $${SRC})" == "santa_driver.zip" ]; then
mkdir -p $(@D)/binaries
unzip -q $${SRC} -d $(@D)/binaries >/dev/null
fi
done
# Gather together the dSYMs. Throw an error if no dSYMs were found
for SRC in $(SRCS); do
case $${SRC} in
*santa-driver.kext.dSYM*Info.plist)
mkdir -p $(@D)/dsym
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santa-driver.kext.dSYM
;;
esac
done
# Cause a build failure if the dSYMs are missing.
if [[ ! -d "$(@D)/dsym" ]]; then
echo "dsym dir missing: Did you forget to use --apple_generate_dsym?"
echo "This flag is required for the 'release' target."
exit 1
fi
# Update all the timestamps to now. Bazel avoids timestamps to allow
# builds to be hermetic and cacheable but for releases we want the
# timestamps to be more-or-less correct.
find $(@D)/{binaries,dsym} -exec touch {} \\;
# Create final output tar
tar -C $(@D) -czpf $(@) binaries dsym
""",
}),
heuristic_label_expansion = 0,
)
test_suite(
name = "unit_tests",
tests = [
"//Source/common:SantaCacheTest",
"//Source/common:SNTFileInfoTest",
"//Source/common:SNTPrefixTreeTest",
"//Source/santactl:SNTCommandFileInfoTest",
"//Source/santactl:SNTCommandSyncTest",
"//Source/santad:SNTEventTableTest",
"//Source/santad:SNTExecutionControllerTest",
"//Source/santad:SNTRuleTableTest",
],
)

View File

@@ -1,37 +0,0 @@
Want to contribute? Great! First, read this page (including the small print at the end).
### Before you contribute
Before we can use your code, you must sign the
[Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual)
(CLA), which you can do online. The CLA is necessary mainly because you own the
copyright to your changes, even after your contribution becomes part of our
codebase, so we need your permission to use and distribute your code. We also
need to be sure of various other things—for instance that you'll tell us if you
know that your code infringes on other people's patents. You don't have to sign
the CLA until after you've submitted your code for review and a member has
approved it, but you must do it before we can put your code into our codebase.
Before you start working on a larger contribution, you should get in touch with
us first through the [issue tracker](https://github.com/google/santa/issues)
with your idea so that we can help out and possibly guide you. Coordinating up
front makes it much easier to avoid frustration later on.
### Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. It's also a good idea to run the
tests beforehand, which you can do with the following commands:
```sh
rake tests:logic
rake tests:kernel # only necessary if you're changing the kext code
```
### Code Style
All code submissions should try to match the surrounding code. Wherever possible,
code should adhere to either the
[Google Objective-C Style Guide](https://google.github.io/styleguide/objcguide.xml)
or the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html).
### The small print
Contributions made by corporations are covered by a different agreement than
the one above, the [Software Grant and Corporate Contributor License Agreement](https://developers.google.com/open-source/cla/corporate).

1
CONTRIBUTING.md Symbolic link
View File

@@ -0,0 +1 @@
docs/development/contributing.md

View File

@@ -32,6 +32,7 @@ PACKAGE_VERSION:=$(shell curl -fs https://api.github.com/repos/google/santa/rele
# | |-- com.google.santad.plist
# | |-- com.google.santagui.plist
# | +-- com.google.santa.asl.conf
# | +-- com.google.santa.newsyslog.conf
# +--dsym
# |-- santa-driver.kext.dSYM
# |-- Santa.app.dSYM
@@ -44,6 +45,7 @@ PAYLOAD:=pack-Library-Extensions-santa-driver.kext \
pack-Library-LaunchDaemons-com.google.santad.plist \
pack-Library-LaunchAgents-com.google.santagui.plist \
pack-etc-asl-com.google.santa.asl.conf \
pack-etc-newsyslog.d-com.google.santa.newsyslog.conf \
pack-script-preinstall \
pack-script-postinstall
@@ -52,6 +54,7 @@ Santa.app: download
com.google.santad.plist: download
com.google.santagui.plist: download
com.google.santa.asl.conf: download
com.google.santa.newsyslog.conf: download
download:
$(if $(PACKAGE_VERSION),, $(error GitHub API returned unexpected result. Wait a while and try again))
@@ -65,6 +68,12 @@ pack-etc-asl-com.google.santa.asl.conf: com.google.santa.asl.conf l_private_etc
@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-etc-newsyslog.d-com.google.santa.newsyslog.conf: com.google.santa.newsyslog.conf l_private_etc
@sudo mkdir -p ${WORK_D}/private/etc/newsyslog.d
@sudo chown root:wheel ${WORK_D}/private/etc/newsyslog.d
@sudo chmod 755 ${WORK_D}/private/etc/newsyslog.d
@sudo install -m 644 -o root -g wheel com.google.santa.newsyslog.conf ${WORK_D}/private/etc/newsyslog.d
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
@@ -79,6 +88,7 @@ myclean:
@rm -rf santa-driver.kext
@rm -f config.plist
@rm -f com.google.santa.asl.conf
@rm -f com.google.santa.newsyslog.conf
@rm -f com.google.santad.plist
@rm -f com.google.santagui.plist
@rm -f install.sh

View File

@@ -1,6 +1,6 @@
#!/bin/bash
# Load the kernel extension, santad, sync client
# Load com.google.santa.daemon and com.google.santa.bundleservice
# If a user is logged in, also load the GUI agent.
# If the target volume is not /, do nothing
@@ -9,20 +9,23 @@
# 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
mkdir -p /usr/local/bin
/bin/ln -sf /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin/santactl
/bin/ln -sf /Applications/Santa.app/Contents/MacOS/santactl /usr/local/bin/santactl
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
# Remove the kext before com.google.santa.daemon loads if the SystemExtension is already present.
# This prevents Santa from dueling itself if the "EnableSystemExtension" config is set to false.
/bin/launchctl list EQHXZ8M8AV.com.google.santa.daemon > /dev/null 2>&1 && rm -rf /Library/Extensions/santa-driver.kext
# Load com.google.santa.daemon, its main has logic to handle loading the kext
# or relaunching itself as a SystemExtension.
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santad.plist
# Load com.google.santa.bundleservice
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santa.bundleservice.plist
GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
[[ -z "${GUI_USER}" ]] && exit 0
/bin/launchctl asuser "${GUI_USER}" /bin/launchctl load /Library/LaunchAgents/com.google.santa.plist
exit 0

View File

@@ -6,21 +6,26 @@
[[ $3 != "/" ]] && exit 0
/bin/launchctl remove com.google.santad
/bin/launchctl remove com.google.santad || true
/bin/launchctl remove com.google.santa.bundleservice || true
sleep 1
/bin/sleep 1
/sbin/kextunload -b com.google.santa-driver >/dev/null 2>&1
/sbin/kextunload -b com.google.santa-driver >/dev/null 2>&1 || true
# Remove cruft from old Santa versions
/bin/rm /usr/libexec/santad
/bin/rm /usr/sbin/santactl
/bin/rm -f /usr/libexec/santad
/bin/rm -f /usr/sbin/santactl
/bin/launchctl remove com.google.santasync
/bin/rm /Library/LaunchDaemons/com.google.santasync.plist
/bin/rm -f /Library/LaunchDaemons/com.google.santasync.plist
/bin/rm -rf /Applications/Santa.app
/bin/rm -rf /Library/Extensions/santa-driver.kext
sleep 1
/bin/sleep 1
user=$(/usr/bin/stat -f '%u' /dev/console)
[[ -n "$user" ]] && /bin/launchctl asuser ${user} /bin/launchctl remove com.google.santagui
GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
[[ -z "${GUI_USER}" ]] && exit 0
/bin/launchctl asuser "${GUI_USER}" /bin/launchctl remove com.google.santagui
/bin/launchctl asuser "${GUI_USER}" /bin/launchctl remove com.google.santa
exit 0

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//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.santa.bundleservice</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/Santa.app/Contents/MacOS/santabundleservice</string>
<string>--syslog</string>
</array>
<key>MachServices</key>
<dict>
<key>com.google.santa.bundleservice</key>
<true/>
</dict>
<key>RunAtLoad</key>
<false/>
<key>KeepAlive</key>
<false/>
<key>ProcessType</key>
<string>Interactive</string>
<key>ThrottleInterval</key>
<integer>0</integer>
</dict>
</plist>

View File

@@ -0,0 +1,2 @@
# logfilename [owner:group] mode count size(KiB) when flags [/pid_file] # [sig_num]
/var/db/santa/santa.log root:wheel 644 10 25000 * NZ

View File

@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.google.santagui</string>
<string>com.google.santa</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/Santa.app/Contents/MacOS/Santa</string>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//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.santa.syncservice</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/Santa.app/Contents/MacOS/santasyncservice</string>
<string>--syslog</string>
</array>
<key>MachServices</key>
<dict>
<key>com.google.santa.syncservice</key>
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>

View File

@@ -1,26 +1,24 @@
<?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">
<!DOCTYPE plist PUBLIC "-//Apple//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.santad</string>
<key>ProgramArguments</key>
<array>
<string>/Library/Extensions/santa-driver.kext/Contents/MacOS/santad</string>
<string>--syslog</string>
</array>
<key>MachServices</key>
<dict>
<key>SantaXPCControl</key>
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true />
<key>ProcessType</key>
<string>Interactive</string>
<key>ThrottleInterval</key>
<integer>1</integer>
<key>Label</key>
<string>com.google.santad</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/Santa.app/Contents/Library/SystemExtensions/com.google.santa.daemon.systemextension/Contents/MacOS/com.google.santa.daemon</string>
<string>--syslog</string>
</array>
<key>MachServices</key>
<dict>
<key>com.google.santa.daemon</key>
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ProcessType</key>
<string>Interactive</string>
</dict>
</plist>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Minimal Configuration -->
<key>ClientMode</key>
<integer>1</integer>
<!-- For documentation of other keys, see the following URL:
https://github.com/google/santa/wiki/Configuration-Keys -->
</dict>
</plist>

View File

@@ -5,55 +5,74 @@ if [[ $EUID -ne 0 ]]; then
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
if [[ -z "${BINARIES}" || -z "${CONF}" ]]; then
if [[ -d "binaries" ]]; then
BINARIES="${PWD}/binaries"
CONF="${PWD}/conf"
elif [[ -d "../binaries" ]]; then
BINARIES="${PWD}/../binaries"
CONF="${PWD}/../conf"
else
echo "Can't find binaries, run install.sh from inside the conf directory" 1>&2
exit 1
fi
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 bundle service
/bin/launchctl remove com.google.santa.bundleservice >/dev/null 2>&1
# Unload kext.
/sbin/kextunload -b com.google.santa-driver >/dev/null 2>&1
# Determine if anyone is logged into the GUI
GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
# Unload GUI agent if someone is logged in.
[[ -n "${GUI_USER}" ]] && \
/bin/launchctl asuser "${GUI_USER}" /bin/launchctl remove com.google.santagui
[[ -n "$GUI_USER" ]] && \
/bin/launchctl asuser ${GUI_USER} /bin/launchctl remove /Library/LaunchAgents/com.google.santagui.plist
/bin/launchctl asuser "${GUI_USER}" /bin/launchctl remove com.google.santa
# 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
/bin/rm -rf /Applications/Santa.app 2>&1
/bin/rm -rf /Library/Extensions/santa-driver.kext 2>&1
# Copy new files.
/bin/cp -r ${SOURCE}/binaries/santa-driver.kext /Library/Extensions
/bin/cp -r ${SOURCE}/binaries/Santa.app /Applications
mkdir -p /usr/local/bin
/bin/ln -s /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin
/bin/mkdir -p /var/db/santa
/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/
/bin/cp -r ${BINARIES}/Santa.app /Applications
# Only copy the kext if the SystemExtension is not present.
# This prevents Santa from dueling itself if the "EnableSystemExtension" config is set to false.
/bin/launchctl list EQHXZ8M8AV.com.google.santa.daemon > /dev/null 2>&1 || /bin/cp -r ${BINARIES}/santa-driver.kext /Library/Extensions && /usr/sbin/kextcache -update-volume / -bundle-id com.google.santa-driver
/bin/mkdir -p /usr/local/bin
/bin/ln -s /Applications/Santa.app/Contents/MacOS/santactl /usr/local/bin 2>/dev/null
/bin/cp ${CONF}/com.google.santa.plist /Library/LaunchAgents
/bin/cp ${CONF}/com.google.santa.bundleservice.plist /Library/LaunchDaemons
/bin/cp ${CONF}/com.google.santad.plist /Library/LaunchDaemons
/bin/cp ${CONF}/com.google.santa.asl.conf /etc/asl/
/bin/cp ${CONF}/com.google.santa.newsyslog.conf /etc/newsyslog.d/
# 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.
# Load com.google.santa.daemon
/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
# Load com.google.santa.bundleservice
/bin/launchctl load /Library/LaunchDaemons/com.google.santa.bundleservice.plist
# Load GUI agent if someone is logged in.
[[ -z "${GUI_USER}" ]] && exit 0
/bin/launchctl asuser "${GUI_USER}" /bin/launchctl load -w /Library/LaunchAgents/com.google.santa.plist
exit 0

View File

@@ -6,20 +6,28 @@
[ "$EUID" != 0 ] && printf "%s\n" "This requires running as root/sudo." && exit 1
# For macOS 10.15+ this will block up to 60 seconds
/bin/launchctl list EQHXZ8M8AV.com.google.santa.daemon > /dev/null 2>&1 && /Applications/Santa.app/Contents/MacOS/Santa --unload-system-extension
/bin/launchctl remove com.google.santad
sleep 1
/sbin/kextunload -b com.google.santa-driver >/dev/null 2>&1
user=$(/usr/bin/stat -f '%u' /dev/console)
[[ -n "$user" ]] && /bin/launchctl asuser ${user} /bin/launchctl remove com.google.santagui
[[ -n "$user" ]] && /bin/launchctl asuser ${user} /bin/launchctl remove com.google.santa
# and to clean out the log config, although it won't write after wiping the binary
/usr/bin/killall -HUP syslogd
# delete artifacts on-disk
/bin/rm -rf /Applications/Santa.app
/bin/rm -rf /Library/Extensions/santa-driver.kext
/bin/rm -f /Library/LaunchAgents/com.google.santagui.plist
/bin/rm -f /Library/LaunchAgents/com.google.santa.plist
/bin/rm -f /Library/LaunchDaemons/com.google.santad.plist
/bin/rm -f /Library/LaunchDaemons/com.google.santa.bundleservice.plist
/bin/rm -f /private/etc/asl/com.google.santa.asl.conf
/bin/rm -f /private/etc/newsyslog.d/com.google.santa.newsyslog.conf
/bin/rm -f /usr/local/bin/santactl # just a symlink
#uncomment to remove the config file and all databases, log files
#/bin/rm -rf /var/db/santa
#/bin/rm -f /var/log/santa*

View File

@@ -1,141 +0,0 @@
# Events
Events are a defined set of data, core to how Santa interacts with a sync server. Events are generated when there is a blocked `execve()` while in Lockdown or Monitor mode. Events are also generated in Monitor mode for an `execve()` that was allowed to run, but would have been blocked in Lockdown mode. This allows an admin to roll out Santa to their macOS fleet in Monitor mode but still collect meaningful data. The events collected while in Monitor mode can be used to build a reasonably comprehensive whitelist of signing certificates and binaries before switching the fleet to Lockdown mode.
##### Event Data
Events begin their life as an [SNTStoredEvent](https://github.com/google/santa/blob/master/Source/common/SNTStoredEvent.h) object. The SNTStoredEvent class is just a simple storage class that has properties for all the relevant bits of information. More importantly the class implements the [NSSecureCoding](https://developer.apple.com/documentation/foundation/nssecurecoding?language=objc) protocol. This allows the objects to be encoded and decoded for storage in the events sqlite3 database on disk and sent over XPC to another process.
Events are temporarily stored in a database until they are uploaded. The format is subject the change; accessing the events database directly will most likely break in future releases. If direct access to the events database is required, raise a [issue on the Santa GitHub](https://github.com/google/santa/issues).
###### JSON
Before an event is uploaded to a sync server, the event data is copied into a JSON blob. Here is an example of Firefox being blocked and sent for upload:
```json
{
"events": [
{
"file_path": "/var/folders/l5/pd9rhsp54s79_9_qcy746_tw00b_4p/T/AppTranslocation/254C1357-7461-457B-B734-A0FDAF0F26D9/d/Firefox.app/Contents/MacOS",
"file_bundle_version": "5417.6.28",
"parent_name": "launchd",
"logged_in_users": [
"bur"
],
"quarantine_timestamp": 0,
"signing_chain": [
{
"cn": "Developer ID Application: Mozilla Corporation (43AQ936H96)",
"valid_until": 1652123338,
"org": "Mozilla Corporation",
"valid_from": 1494270538,
"ou": "43AQ936H96",
"sha256": "96f18e09d65445985c7df5df74ef152a0bc42e8934175a626180d9700c343e7b"
},
{
"cn": "Developer ID Certification Authority",
"valid_until": 1801519935,
"org": "Apple Inc.",
"valid_from": 1328134335,
"ou": "Apple Certification Authority",
"sha256": "7afc9d01a62f03a2de9637936d4afe68090d2de18d03f29c88cfb0b1ba63587f"
},
{
"cn": "Apple Root CA",
"valid_until": 2054670036,
"org": "Apple Inc.",
"valid_from": 1146001236,
"ou": "Apple Certification Authority",
"sha256": "b0b1730ecbc7ff4505142c49f1295e6eda6bcaed7e2c68c5be91b5a11001f024"
}
],
"file_bundle_name": "Firefox",
"executing_user": "bur",
"ppid": 1,
"file_bundle_path": "/var/folders/l5/pd9rhsp54s79_9_qcy746_tw00b_4p/T/AppTranslocation/254C1357-7461-457B-B734-A0FDAF0F26D9/d/Firefox.app",
"file_name": "firefox",
"execution_time": 1501691337.059514,
"file_sha256": "dd78f456a0929faf5dcbb6d952992d900bfdf025e1e77af60f0b029f0b85bf09",
"decision": "BLOCK_BINARY",
"file_bundle_id": "org.mozilla.firefox",
"file_bundle_version_string": "54.0.1",
"pid": 49368,
"current_sessions": [
"bur@console",
"bur@ttys000",
"bur@ttys001",
"bur@ttys002",
"bur@ttys003",
"bur@ttys004"
]
}
]
}
```
##### Event Lifecycle
1. santad generates a new event
2. santad compares, or adds if not present, the event's SHA-256 file hash to an in-memory cache with a timeout of 10 min. If an event with an matching hash is present in cache, the event is dropped.
3. santad saves the event to `/var/db/santa/events.db` with a unique ID assigned as the key.
4. santad sends an XPC message to the santactl daemon. The message contains the event with instructions to upload the event immediately. This is non-blocking and is performed on a background thread.
##### Bundle Events
Bundle events are a special type of event that are generated when a sync server supports receiving the associated bundle events, instead of just the original event. For example: `/Applications/Keynote.app/Contents/MacOS/Keynote` is blocked and an event representing the binary is uploaded. A whitelist rule is created for that one binary. Great, you can now run `/Applications/Keynote.app/Contents/MacOS/Keynote`, but what about all the other supporting binaries contained in the bundle? You would have to wait until they are executed until an event would be generated. It is very common for a bundle to contain multiple binaries, as shown here with Keynote.app. Waiting to get a block is not a very good user experience.
```sh
⇒ santactl bundleinfo /Applications/Keynote.app
Hashing time: 1047 ms
9 events found
BundleHash: b475667ab1ab6eddea48bfc2bed76fcef89b8f85ed456c8068351292f7cb4806
BundleID: com.apple.iWork.Keynote
SHA-256: be3aa404ee79c2af863132b93b0eedfdbc34c6e35d4fda2ade6dd637692ead84
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.MovieCompatibilityConverter.xpc/Contents/MacOS/com.apple.iWork.MovieCompatibilityConverter
BundleID: com.apple.iWork.Keynote
SHA-256: 3b2582fd5e7652b653276b3980c248dc973e8082e9d0678c96a08d7d1a8366ba
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.PICTConverter.xpc/Contents/MacOS/com.apple.iWork.PICTConverter
BundleID: com.apple.iWork.Keynote
SHA-256: f1bf3be05d511d7c7f651cf7b130d4977f8d28d0bfcd7c5de4144b95eaab7ad7
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.ExternalResourceAccessor.xpc/Contents/XPCServices/com.apple.iWork.TCMovieExtractor.xpc/Contents/MacOS/com.apple.iWork.TCMovieExtractor
BundleID: com.apple.iWork.Keynote
SHA-256: b59bc8548c91088a40d9023abb5d22fa8731b4aa17693fcb5b98c795607d219a
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.BitmapTracer.xpc/Contents/MacOS/com.apple.iWork.BitmapTracer
BundleID: com.apple.iWork.Keynote
SHA-256: 08cb407f541d867f1a63dc3ae44eeedd5181ca06c61df6ef62b5dc7192951a4b
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.TCUtilities32.xpc/Contents/MacOS/com.apple.iWork.TCUtilities32
BundleID: com.apple.iWork.Keynote
SHA-256: b965ae7be992d1ce818262752d0cf44297a88324a593c67278d78ca4d16fcc39
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.ExternalResourceAccessor.xpc/Contents/XPCServices/com.apple.iWork.TCMovieExtractor.xpc/Contents/XPCServices/com.apple.iWork.TCMovieExtractor.TCUtilities32.xpc/Contents/MacOS/com.apple.iWork.TCMovieExtractor.TCUtilities32
BundleID: com.apple.iWork.Keynote
SHA-256: 59668dc27314f0f6f5daa5f02b564c176f64836c88e2dfe166e90548f47336f1
Path: /Applications/Keynote.app/Contents/MacOS/Keynote
BundleID: com.apple.iWork.Keynote
SHA-256: 7ce324f919b14e14d327004b09f83ca81345fd4438c87ead4b699f89e9485595
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.ExternalResourceAccessor.xpc/Contents/XPCServices/com.apple.iWork.ExternalResourceValidator.xpc/Contents/MacOS/com.apple.iWork.ExternalResourceValidator
BundleID: com.apple.iWork.Keynote
SHA-256: 6b47f551565d886388eeec5e876b6de9cdd71ef36d43b0762e6ebf02bdd8515d
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.ExternalResourceAccessor.xpc/Contents/MacOS/com.apple.iWork.ExternalResourceAccessor
```
Bundle events provide a mechanism to generate and upload events for all the executable Mach-O binaries within a bundle. To enable bundle event generation a configuration must be set in the preflight sync stage on the sync server. Once set the sync server can use bundle events to drive a better user experience.
Bundle events can be differentiated by the existence of these fields:
| Field | Value |
| ------------------------ | ---------------------------------------- |
| decision | BUNDLE_BINARY |
| file_bundle_hash | Super Hash of all binary hashes |
| file_bundle_hash_millis | The time in milliseconds it took to find all of the binaries, hash and produce a super hash |
| file_bundle_binary_count | Number of binaries within the bundle |
To avoid redundant uploads of a bundle event Santa will wait for the sync server to ask for them. The server will respond to event uploads with a request like this:
| Field | Value |
| ---------------------------- | ---------------------------------------- |
| event_upload_bundle_binaries | An array of bundle hashes that the sync server needs to be uploaded |
When santactl receives this type of request, it sends an XPC reply to santad to save all the bundle events to the events.db. It then attempts to upload all the bundle events, purging the successes from the events.db. Any failures will be uploaded during the next full sync.

View File

@@ -1,43 +0,0 @@
# Interprocess Communication (IPC)
Most IPC within Santa is done by way of Apple's [XPC](https://developer.apple.com/documentation/xpc?language=objc). Santa wraps [NSXPCConnection](https://developer.apple.com/documentation/foundation/nsxpcconnection?language=objc) to provide client multiplexing, signature validation of connecting clients and forced connection establishment. This is called SNTXPCConnection.
Communication between santad and santa-driver (KEXT) is done with a [IOUserClient](https://developer.apple.com/documentation/kernel/iouserclient?language=objc) subclass and IOKit/IOKitLib.h functions.
##### Who starts who?
The santad and Santa (GUI) processes are both started and kept alive by launchd as a LaunchDaemon and a LaunchAgent, respectively. This means santad runs as root and Santa (GUI) runs as the console user.
There can be multiple Santa (GUI) processes running, one per user logged into the GUI (assuming fast-user switching is enabled). While multiple processes might be running, only the one for the user currently logged-in will be connected to santad and receiving notifications.
When using a sync server, the santactl process is started by santad. Before the new process starts, all privileges are dropped. santactl runs as _nobody_.
The santabs process is started by launchd via an XPC service connection from santad. XPC services inherit their initiator's privileges meaning santabs runs as root, which is necessary to ensure it has permission to read all files.
| Process | Parent Process | Running User |
| -------- | -------------- | ------------ |
| santad | launchd | root |
| Santa | launchd | user |
| santactl | santad | nobody |
| santabs | launchd | root |
##### Who communicates with who?
In short, santad has two-way communication with every other process. In addition, Santa and santabs have two-way communication between each other. For other combinations, there is no direct communication.
![Santa IPC](santa_ipc.png)
##### SNTXPCConnection and two way communication
`SNTXPCConnection` enforces a server / client model for XPC connections. This allows for strong signature validation and forced connection establishment. The only problem with this model is the lack of two-way communication. For example, process A can call methods on process B and retrieve a response, but process B cannot call methods on process A.
To accomplish two-way communication, the following approach can be used:
1. Process A creates a server with an anonymous `NSXPCListener`.
2. Process A sends the anonymous `NSXPCListenerEndpoint` to process B over an already established `SNTXPCConnection`.
3. Process B can now communicate directly with process A.
This is a powerful notion. It enables forced connection establishment between both processes, which is critical when reliability is a concern.

View File

@@ -1,94 +0,0 @@
# santa-driver
santa-driver is a macOS [kernel extension](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptIntro/introduction.html) (KEXT) that makes use of the [Kernel Authorization](https://developer.apple.com/library/content/technotes/tn2127/_index.html) (Kauth) KPI. This allows santa-driver to listen for events and either deny or defer the decision of those events. The santa-driver acts as an intermediary layer between Kauth and santad, with some caching to lower the overhead of decision making.
##### Kauth
santa-driver utilizes two Kauth scopes `KAUTH_SCOPE_VNODE` and `KAUTH_SCOPE_FILEOP `. It registers itself with the Kauth API by calling `kauth_listen_scope()` for each scope. This function takes three arguments:
* `const char *scope`
* `kauth_scope_callback_t _callback`_
* `void *contex`
It returns a `kauth_listener_t` that is stored for later use, in Santa's case to stop listening.
###### KAUTH_SCOPE_VNODE
Here is how santa-driver starts listening for `KAUTH_SCOPE_VNODE` events.
```c++
vnode_listener_ = kauth_listen_scope(
KAUTH_SCOPE_VNODE, vnode_scope_callback, reinterpret_cast<void *>(this));
```
The function `vnode_scope_callback` is called for every vnode event. There are many types of vnode events, they complete list can be viewed in the kauth.h. There are many types of vnode events, the complete list can be viewed in kauth.h. Santa is only concerned with regular files generating `KAUTH_VNODE_EXECUTE` [1] and `KAUTH_VNODE_WRITE_DATA` events. All non-regular files and unnecessary vnode events are filtered out.
Here is how santa-driver stops listening for `KAUTH_SCOPE_VNODE` events:
```c++
kauth_unlisten_scope(vnode_listener_);
```
[1] `KAUTH_VNODE_EXECUTE` events that do not have the `KAUTH_VNODE_ACCESS` advisory bit set.
###### KAUTH_SCOPE_FILEOP
Santa also listens for file operations, this is mainly used for logging [1] and cache invalidation.
* `KAUTH_FILEOP_DELETE`, `KAUTH_FILEOP_RENAME`, `KAUTH_FILEOP_EXCHANGE` and `KAUTH_FILEOP_LINK` are logged
* `KAUTH_FILEOP_EXEC` is used to log `execve()`s. Since the `KAUTH_VNODE_EXECUTE` is used to allow or deny an `execve()` the process arguments have not been setup yet. Since `KAUTH_FILEOP_EXEC` is triggered after an `execve()` it is used to log the `execve()`.
[1] `KAUTH_FILEOP_CLOSE` is used to invalidate that file's representation in the cache. If a file has changed it needs to be re-evalauted. This is particularly necessary for files that were added to the cache with an action of allow.
##### Driver Interface
santa-driver implements an [IOUserClient](https://developer.apple.com/documentation/kernel/iouserclient?language=objc) subclass and santad interacts with it through IOKit/IOKitLib.h functions.
[//]: # "TODO(bur, rah) Flesh out the details"
##### Cache
To aid in performance, santa-driver utilizes a caching system to hold the state of all observed `execve()` events.
###### Key
The key is a `uint64_t`. The top 32 bits hold the filesystem ID, while the bottom 32 bits hold the file unique ID. Together we call this the vnode_id.
```c++
uint64_t vnode_id = (((uint64_t)fsid << 32) | fileid);
```
###### Value
The value is a `uint64_t` containing the action necessary, along with the decision timestamp. The action is stored in the top 8 bits. The decision timestamp is stored in the remaining 56 bits.
```c++
santa_action_t action = (santa_action_t)(cache_val >> 56);
uint64_t decision_time = (cache_val & ~(0xFF00000000000000));
```
The possible actions are:
| Actions | Expiry Time | Description |
| ----------------------- | ---------------- | ---------------------------------------- |
| `ACTION_REQUEST_BINARY` | None | Awaiting an allow or deny decision from santad. |
| `ACTION_RESPOND_ALLOW` | None | Allow the `execve()` |
| `ACTION_RESPOND_DENY` | 500 milliseconds | Deny the `execve()`, but re-evalaute after 500 milliseconds. If someone is trying to run a banned binary continually every millisecond for example, only 2 evaluation requests to santad for would occur per second. This mitigates a denial of service type attack on santad. |
###### Invalidation
Besides the expiry time for individual entries, the entire cache will be cleared if any of the following events takes place:
* Addition of a blacklist rule
* Addition of a blacklist regex scope
* Cache fills up. This defaults to `5000` entries for the root volume and `500` for all other mounted volumes.
To view the current kernel cache count see the "Kernel info" section of `santactl status`:
```sh
⇒ santactl status
>>> Kernel Info
Root cache count | 107
Non-root cache count | 0
```

View File

@@ -1,44 +0,0 @@
# santabs
The santabs process is an XPC service for the santa-driver.kext bundle, meaning only binaries within that bundle can launch santabs. It will be launched with the same privileges as its calling process. Currently, santad is the only caller of santabs, so santabs runs as root.
##### Events
The santabs process is quite simple and only does one thing: it generates non-execution events for the contents of a bundle.
When there is an `execve()` that is blocked within a bundle, a few actions take place:
1. The highest ancestor bundle in the tree is found
* So `/Applications/DVD Player.app/Contents/MacOS/DVD Player` would be `/Applications/DVD Player.app`
* Or `/Applications/Safari.app/Contents/PlugIns/CacheDeleteExtension.appex/Contents/MacOS/CacheDeleteExtension` would be `/Applications/Safari.app`
2. The ancestor bundle is then searched for Mach-O executables
* For Safari that would currently be 4 binaries
* ```sh
Hashing time: 53 ms
4 events found
BundleHash: 718773556ca5ea798f984fde2fe1a5994f175900b26d2964c9358a0f469a4ac6
BundleID: com.apple.Safari
SHA-256: ea872e83a518ce442ed050c4408a448d915e2bae90ef8455ce7805448d864a3e
Path: /Applications/Safari.app/Contents/PlugIns/CacheDeleteExtension.appex/Contents/MacOS/CacheDeleteExtension
BundleID: com.apple.Safari
SHA-256: 1a43283857b1822164f82af274c476204748c0a2894dbcaa11ed17f78e0273cc
Path: /Applications/Safari.app/Contents/MacOS/Safari
BundleID: com.apple.Safari
SHA-256: ab0ac54dd90144931b681d1e84e198c6510be44ac5339437bc004e60777af7ba
Path: /Applications/Safari.app/Contents/Resources/appdiagnose
BundleID: com.apple.Safari
SHA-256: f49c5aa3a7373127d0b4945782b1fa375dd3707d66808fd66b7c0756430defa8
Path: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.BrowserDataImportingService.xpc/Contents/MacOS/com.apple.Safari.BrowserDataImportingService
```
3. Events are created for each binary and the bundle hash is calculated
4. These events are sent to the sync server for processing
##### Bundle Hash
The found events are sorted by their file SHA-256 hash. The hashes are concatenated and then SHA-256 hashed. This is now a strong indicator on what Mach-O executables were within the bundle at the time of scan. This can then be verified by the sync server when deciding to generate rules.

View File

@@ -1,23 +0,0 @@
# santad
The santad process does the heavy lifting when it comes to making decisions about binary executions. It also handles brokering all of the XPC connections between the various components of Santa. It does all of this with performance being at the forefront.
##### A note on performance
On an idling machine, santad and the other components of Santa consume virtually no CPU and a minimal amount of memory (5-50MB). When lots of processes `execve()` at the same time, the CPU and memory usage can spike. All of the `execve()` decisions are made on high priority threads to ensure decisions are posted back to the kernel as soon as possible. A watchdog thread will log warnings when sustained high CPU (>20%) and memory (>250MB) usage by santad is detected.
##### On Launch
The very first thing santad does once it has been launched is to load and connect to santa-driver. Only one connection may be active at any given time.
At this point, santa-driver is loaded and running in the kernel, but is allowing all executions and not sending any messages to santad. Before santad tells santa-driver it is ready to receive messages, it needs to setup a few more things:
* The rule and event databases are initialized
* Connections to Santa (GUI) and santactl sync daemon are established.
* The config file is processed.
santad is now ready to start processing decision and logging messages from santa-driver. The listeners are started and santad sits in a run loop awaiting messages from santa-driver.
##### Running
Messages are read from a shared memory queue (`IODataQueueMemory` ) on a single thread. A callback is invoked for each message. The callback then dispatches all the work of processing a decision message to a concurrent high priority queue. The log messages are dispatched to a low priority queue for processing.

View File

@@ -1,27 +0,0 @@
# Scopes
In addition to rules, Santa can whitelist or blacklist based on scopes. Currently, only a few scopes are implemented. They fall into one of two categories: a whitelist scope or blacklist scope. Scopes are evaluated after rules, with blacklist evaluation preceding whitelist.
Scopes are a broader way of whitelisting or blacklisting `execve()`s.
For configuration of scopes see [configuration.md](../deployment/configuration.md).
##### Blacklist Scopes
| Scope | Configurable |
| -------------------- | ------------ |
| Blacklist Path Regex | Yes |
| Missing __PAGEZERO | Yes |
##### Whitelist Scopes
| Scope | Configurable |
| -------------------- | ------------ |
| Whitelist Path Regex | Yes |
| Not a Mach-O | No |
As seen above, Santa will whitelist any non Mach-O binary under a whitelist scope. However, a blacklist regex or binary SHA-256 rule can be used to block non Mach-O `execve()`s since they are evaluated before the whitelist scopes.
##### Regex Caveats
The paths covered by the whitelist and blacklist regex patterns are not tracked. If an `execve()` is allowed initially, then moved into a blacklist directory, Santa has no knowledge of that move. Since santa-driver caches decisions, the recently moved file will continue to be allowed to `execve()` even though it is now within a blacklisted regex path. The cache holds "allow" decisions until invalidated and "deny" decisions for 500 milliseconds. Going from a blacklist path to a whitelist path is not largely affected.

View File

@@ -1,108 +0,0 @@
# Building
Santa makes use of [rake](https://ruby.github.io/rake/) for building and testing Santa. All of the [releases](https://github.com/google/santa/releases) are made using this same process. Santa's releases are codesigned with Google's KEXT signing certificate. This allows Santa to be loaded with SIP fully enabled.
#### Cloning
Clone the source and change into the directory.
```sh
git clone https://github.com/google/santa
cd santa
```
The above command will default to using the `master` branch. If you wanted to build, run or test a specific version of Santa use this command.
```sh
git checkout <version, i.e. 0.9.19>
```
#### Building
Build a debug version of Santa. This keeps all the debug symbols, adds additional logs and does not optimize the compiled output. For speed sensitive tests make sure to benchmark a release version too.
```sh
rake build:debug
```
Build a release version of Santa.
```sh
rake build:release
```
Both of these just output the binaries that makeup Santa in the default Xcode build location. To actually run what was built, see the next section.
#### Running
On macOS 10.11+ System Integrity Protection (SIP) prevents loading of kernel extensions that are not signed by an Apple KEXT signing certificate. To be able to load and test a non-release version of Santa, SIP will have to be configured to allow non-Apple KEXT signing certificates.
__This is only to be done a machine that is actively developing, unloading and loading kernel extensions.__
1. Boot into Recovery Mode: Reboot and hold down `command+r`
2. From the utilities menu select `Terminal`
3. Disable the KEXT feature of SIP: `csrutil enable --without kext`
4. Reboot
You should now be able to load and run a non-release version of Santa.
Build and run a debug version of Santa.
```sh
rake reload:debug
```
Build and run a release version of Santa.
```sh
rake reload:release
```
#### Debugging
Xcode and lldb can be used to debug Santa, just like any other project. Instead of clicking the play button to launch and attach to a process, you can attach to an already running, or soon to by running, component of Santa. To do this select the Debug menu and choose `Attach to Process by PID or Name… `. Below are the four components of Santa and who to debug the process as.
Note: santa-driver (the kernel extension) cannot be debugged by attaching with Xcode.
Note: Xcode can attach to santad without interruption, however any breakpoints in the decision making codepath can deadlock the machine. Using lldb directly to attach to santad will deadlock the machine.
| process | user |
| -------- | ---- |
| santad | root |
| Santa* | me |
| santactl | me |
| santabs | root |
Xcode will then wait for the process to start. Issue this command to restart all the Santa processes in debug mode.
*The Santa (GUI) process is the only component of Santa that can be launched and debugged from Xcode directly. All the other components are launched with privileges and/or are scoped to an XPC service that launchd scopes to a hosting bundle. Thus the need for the `Attach to Process by PID or Name…` technique. See the [ipc](../details/ipc.md) document for for details.
```sh
rake reload:debug
```
Now the process is attached in Xcode and you can debug your day away.
#### Tests
Run all the logic / unit tests
```sh
rake tests:logic
```
Run all of santa-driver kernel extension tests
```sh
rake tests:kernel
```
#### Releases
Creates a release build of Santa with a version based of the newest tag. Also saves the dsym files for each component of Santa. This makes debugging and interpreting future crashes or kernel panics much easier.
```sh
rake dist
```

View File

@@ -1,31 +0,0 @@
# Binary Whitelisting Overview
#### Background
The decision flow starts in the kernel. The macOS kernel is extensible by way of a kernel extension (KEXT). macOS makes available kernel programming interfaces (KPIs) to be used by a KEXT. Santa utilizes the Kernel Authorization (Kauth) KPI. This is a very powerful and verbose interface that gives Santa the ability to listen in on most vnode and file systems operations and to take actions, directly or indirectly, on the operations being performed. Still, there are some limitations to Kauth which are pointed out in the santa-driver document. For more information on the santa-driver KEXT see the [santa-driver.md](../details/santa-driver.md) document.
#### Flow of an execve()
This is a high level overview of the binary whitelisting / blacklisting decision process. For a more detailed account of each part, see the respective documentation. This flow does not cover the logging component of Santa, see the [logs.md](../details/logs.md) documentation for more info.
###### Kernel Space
0. santa-driver registers itself as a `KAUTH_SCOPE_VNODE` listener. This flow follows how santa-driver handles `KAUTH_VNODE_EXECUTE` events.
1. A santa-driver Kauth callback function is executed by the kernel when a process is trying to `execve()`. In most cases, the `execve()` takes place right after a process calls `fork()` to start a new process. This function is running on a kernel thread representing the new process. Information on where to find the executable is provided. This information is known as the `vnode_id`.
2. santa-driver then checks if its cache has an allow or deny entry for the `vnode_id`. If so it returns that decision to the Kauth KPI.
* If Kauth receives a deny, it will stop the `execve()` from taking place.
* If Kauth receives an allow, it will defer the decision. If there are other Kauth listeners, they also have a chance deny or defer.
3. If there is no entry for the `vnode_id` in the cache a few actions occur:
* santa-driver hands off the decision making to santad.
* A new entry is created in the cache for the `vnode_id` with a special value of `ACTION_REQUEST_BINARY`. This is used as a placeholder until the decision from santad comes back. If another process tries to `execve()` the same `vnode_id`, santa-driver will have that thread wait for the in-flight decision from santad. All subsequent `execve()`s for the same `vnode_id` will use the decision in the cache as explained in #2, until the cache is invalidated. See the [santa-driver.md](../details/santa-driver.md) document for more details on the cache invalidation.
* If the executing file is written to while any of the threads are waiting for a response the `ACTION_REQUEST_BINARY` entry is removed, forcing the decision-making process to be restarted.
###### User Space
1. santad is listening for decision requests from santa-driver.
* More information is collected about the executable that lives at the `vnode_id`. Since this codepath has a sleeping kernel thread waiting for a decision, extra care is taken to be as performant as possible.
2. santad uses the information it has gathered to make a decision to allow or deny the `execve()`. There are more details on how these decisions are made in the [rules.md](../details/rules.md) and [scopes.md](../details/scopes.md) documents.
3. The decision is posted back to santa-driver.
4. If there was a deny decision, a message is sent to Santa GUI to display a user popup notification.

View File

@@ -1,27 +0,0 @@
# Syncing Overview
#### Background
Santa can be run and configured without a sync server. Doing so will enable an admin to configure rules with the `santactl rule` command. Using a sync server will enable an admin to configures rules and multiple other settings from the sync server itself. Santa was designed from the start with a sync server in mind. This allows an admin to easily configure and sync rules across a fleet of macOS systems. This document explains the syncing process.
#### Flow of a full sync
This is a high level overview of the syncing process. For a more a more detailed account of each part, see the respective documentation. The santaclt binary can be run in one of two modes, daemon and non-daemon. The non-daemon mode does one full sync and exits. This is the typical way a user will interact with Santa, mainly to force a full sync. The daemon mode is used by santad to schedule full syncs, listen for push notifications and upload events.
0. When the santad process starts up, it looks for a `SyncBaseURL` key/value in the config. If one exists it will `fork()` and `execve()` `santactl sync —-daemon`. Before the new process calls `execve()`, all privileges are dropped. All privileged actions are then restricted to the XPC interface made available to santactl by santad. Since this santactl process is running as a daemon it too exports an XPC interface so santad can interact with the process efficiently and securely. To ensure syncing reliability santad will restart the santactl daemon if it is killed or crashes.
1. The santactl daemon process now schedules a full sync for 15 sec in the future. The 15 sec is used to let santad settle before santactl starts sending rules from the sync server to process.
2. The full sync starts. There are a number of stages to a full sync:
1. preflight: The sync server can set various settings for Santa.
2. logupload (optional): The sync server can request that the Santa logs be uploaded to an endpoint.
3. eventupload (optional): If Santa has generated events, it will upload them to the sync-server.
4. ruledownload: Download rules from the sync server.
5. postflight: Updates timestamps for successful syncs.
3. After the full sync completes a new full sync will be scheduled, by default this will be 10min. However there are a few ways to manipulate this:
1. The sync server can send down a configuration in the preflight to override the 10min interval. It can be anything greater than 10min.
2. Firebase Cloud Messaging (FCM) can be used. The sync server can send down a configuration in the preflight to have the santactl daemon to start listening for FCM messages. If a connection to FCM is made, the full sync interval drops to a default of 4 hours. This can be further configured by a preflight configuration. The FCM connection allows the sync-sever to talk directly with Santa. This way we can reduce polling the sync server dramatically.
4. Full syncs will continue to take place at their configured interval. If configured FCM messages will continue to be digested and acted upon.
#### santactl XPC interface
When running as a daemon, the santactl process makes available an XPC interface for use by santad. This allows santad to send blocked binary or bundle events directly to santactl for immediate upload to the sync-server, enabling a smoother user experience. The binary that was blocked on macOS is immediately available for viewing or handling on the sync-server.

4
Fuzzing/libFuzzer/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
bin
llvm-*.src
llvm-*.src.tar.xz

109
Fuzzing/libFuzzer/build.sh Executable file
View File

@@ -0,0 +1,109 @@
#!/usr/bin/env bash
LLVM_VERSION='5.0.1'
LLVM_COMPILERRT_TARBALL_NAME="llvm-${LLVM_VERSION}.src.tar.xz"
LLVM_COMPILERRT_SRC_FOLDER_NAME=`echo "${LLVM_COMPILERRT_TARBALL_NAME}" | cut -d '.' -f 1-4`
LLVM_COMPILERRT_TARBALL_URL="http://releases.llvm.org/${LLVM_VERSION}/${LLVM_COMPILERRT_TARBALL_NAME}"
LIBFUZZER_FOLDER="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
LOG_FILE=`mktemp`
main() {
echo "libFuzzer build script"
echo " > Checking dependencies..."
checkDependencies || return 1
echo " > Entering libFuzzer folder..."
cd "${LIBFUZZER_FOLDER}" > /dev/null 2>&1
if [ $? -ne 0 ] ; then
echo "Failed to enter the libFuzzer folder: ${LIBFUZZER_FOLDER}"
return 1
fi
if [ ! -f "${LLVM_COMPILERRT_TARBALL_NAME}" ] ; then
echo " > Downloading the LLVM tarball..."
curl "${LLVM_COMPILERRT_TARBALL_URL}" -o "${LLVM_COMPILERRT_TARBALL_NAME}" > "${LOG_FILE}" 2>&1
if [ $? -ne 0 ] ; then
dumpLogFile "Failed to download the LLVM tarball"
return 1
fi
else
echo " > An existing LLVM tarball was found"
fi
if [ -d "${LLVM_COMPILERRT_SRC_FOLDER_NAME}" ] ; then
echo " > Deleting existing LLVM folder..."
rm -rf "${LLVM_COMPILERRT_SRC_FOLDER_NAME}" > "${LOG_FILE}" 2>&1
if [ $? -ne 0 ] ; then
dumpLogFile "Failed to delete the existing source folder"
return 1
fi
fi
echo " > Extracting the LLVM tarball..."
tar xf "${LLVM_COMPILERRT_TARBALL_NAME}" > "${LOG_FILE}" 2>&1
if [ $? -ne 0 ] ; then
rm "${LLVM_COMPILERRT_TARBALL_NAME}" "${LLVM_COMPILERRT_SRC_FOLDER_NAME}"
dumpLogFile "Failed to extract the LLVM tarball"
return 1
fi
if [ -d "bin" ] ; then
echo " > Deleting existing bin folder..."
rm -rf "bin" > "${LOG_FILE}" 2>&1
if [ $? -ne 0 ] ; then
dumpLogFile "Failed to delete the existing bin folder"
return 1
fi
fi
mkdir "bin" > "${LOG_FILE}" 2>&1
if [ $? -ne 0 ] ; then
dumpLogFile "Failed to create the bin folder"
return 1
fi
echo " > Building libFuzzer..."
( cd "bin" && "../${LLVM_COMPILERRT_SRC_FOLDER_NAME}/lib/Fuzzer/build.sh" ) > "${LOG_FILE}" 2>&1
if [ $? -ne 0 ] ; then
dumpLogFile "Failed to build the library"
return 1
fi
printf "\nFinished building libFuzzer\n"
rm "${LOG_FILE}"
return 0
}
checkDependencies() {
executable_list=( "clang++" "curl" "tar" )
for executable in "${executable_list[@]}" ; do
which "${executable}" > /dev/null 2>&1
if [ $? -ne 0 ] ; then
echo "The following program was not found: ${executable}"
return 1
fi
done
return 0
}
dumpLogFile() {
if [ $# -eq 1 ] ; then
local message="$1"
else
local message="An error has occurred"
fi
printf "${message}\n"
printf "Log file follows\n===\n"
cat "${LOG_FILE}"
printf "\n===\n"
rm "${LOG_FILE}"
}
main $@
exit $?

3
Fuzzing/santacache/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
santacache.dSYM
santacache

Binary file not shown.

View File

@@ -0,0 +1,41 @@
/// Copyright 2018 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 <SantaCache.h>
#include <iostream>
#include <cstdint>
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
static SantaCache<uint64_t, uint64_t> decision_cache(5000, 2);
std::uint64_t fields[2] = {};
if (size > 16) {
std::cout << "Invalid size! Start with -max_len=16\n";
return 1;
}
std::memcpy(fields, data, size);
decision_cache.set(fields[0], fields[1]);
auto returned_value = decision_cache.get(fields[0]);
if (returned_value != fields[1]) {
std::cout << fields[0] << ", " << fields[1] << " -> " << returned_value << "\n";
return 1;
}
return 0;
}

View File

@@ -0,0 +1,16 @@
{
"rules": [
{
"rule_type": "BINARY",
"policy": "BLACKLIST",
"sha256": "2dc104631939b4bdf5d6bccab76e166e37fe5e1605340cf68dab919df58b8eda",
"custom_msg": "blacklist firefox"
},
{
"rule_type": "CERTIFICATE",
"policy": "BLACKLIST",
"sha256": "e7726cf87cba9e25139465df5bd1557c8a8feed5c7dd338342d8da0959b63c8d",
"custom_msg": "blacklist dash app certificate"
}
]
}

View File

@@ -0,0 +1,62 @@
/// Copyright 2018 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 <iostream>
#include <cstdint>
#include <vector>
#include <SNTCommandSyncRuleDownload.h>
#include <SNTCommandSyncState.h>
#include <SNTCommandSyncConstants.h>
#include <SNTRule.h>
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
NSData *buffer = [NSData dataWithBytes:static_cast<const void *>(data) length:size];
if (!buffer) {
return 0;
}
NSError *error;
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:buffer options:0 error:&error];
if (!response) {
return 0;
}
if (![response isKindOfClass:[NSDictionary class]]) {
return 0;
}
if (![response objectForKey:kRules]) {
return 0;
}
SNTCommandSyncState *state = [[SNTCommandSyncState alloc] init];
if (!state) {
return 0;
}
SNTCommandSyncRuleDownload *obj = [[SNTCommandSyncRuleDownload alloc] initWithState:state];
if (!obj) {
return 0;
}
for (NSDictionary *ruleDict in response[kRules]) {
SNTRule *rule = [obj ruleFromDictionary:ruleDict];
if (rule) {
std::cerr << "Rule: " << [[rule description] UTF8String] << "\n";
}
}
return 0;
}

View File

@@ -0,0 +1 @@
К'.p▒└G╗М┐║ЙSЮ╝и▌РУерЭxt1iАЫШ9ы*H╩4R"═©$-├Уww╙+Р╝╘[┼иу╧oС┬ОwRpЗя≤х°е

View File

@@ -0,0 +1,55 @@
/// Copyright 2018 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 <iostream>
#include <cstdint>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "SNTCommandController.h"
#import "SNTRule.h"
#import "SNTXPCControlInterface.h"
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
if (size > 16) {
std::cerr << "Invalid buffer size of " << size
<< " (should be <= 16)" << std::endl;
return 1;
}
santa_vnode_id_t vnodeID = {};
std::memcpy(&vnodeID, data, size);
MOLXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
daemonConn.invalidationHandler = ^{
printf("An error occurred communicating with the daemon, is it running?\n");
exit(1);
};
[daemonConn resume];
[[daemonConn remoteObjectProxy] checkCacheForVnodeID:vnodeID
withReply:^(santa_action_t action) {
if (action == ACTION_RESPOND_ALLOW) {
std::cerr << "File exists in [whitelist] kernel cache" << std::endl;;
} else if (action == ACTION_RESPOND_DENY) {
std::cerr << "File exists in [blacklist] kernel cache" << std::endl;;
} else if (action == ACTION_UNSET) {
std::cerr << "File does not exist in cache" << std::endl;;
}
}];
return 0;
}

View File

@@ -0,0 +1,51 @@
/// Copyright 2018 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 <iostream>
#include <cstdint>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "SNTCommandController.h"
#import "SNTRule.h"
#import "SNTXPCControlInterface.h"
#pragma pack(push, 1)
#pragma pack(pop)
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
auto *eventId = reinterpret_cast<const std::uint64_t *>(data);
std::size_t eventIdCount = size / sizeof(std::uint64_t);
if (eventIdCount == 0) {
return 0;
}
MOLXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
daemonConn.invalidationHandler = ^{
printf("An error occurred communicating with the daemon, is it running?\n");
exit(1);
};
[daemonConn resume];
NSMutableSet *eventIds = [NSMutableSet setWithCapacity:eventIdCount];
for (std::size_t i = 0; i < eventIdCount; i++) {
auto id = [NSNumber numberWithInteger:eventId[i]];
[eventIds addObject:id];
}
[[daemonConn remoteObjectProxy] databaseRemoveEventsWithIDs:[eventIds allObjects]];
return 0;
}

View File

@@ -0,0 +1,73 @@
/// Copyright 2018 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 <iostream>
#include <cstdint>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "SNTCommandController.h"
#import "SNTRule.h"
#import "SNTXPCControlInterface.h"
#pragma pack(push, 1)
struct InputData {
std::uint32_t cleanSlate;
std::uint32_t state;
std::uint32_t type;
char hash[33];
};
#pragma pack(pop)
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
if (size > sizeof(InputData)) {
std::cerr << "Invalid buffer size of " << size
<< " (should be <= " << sizeof(InputData)
<< ")" << std::endl;
return 1;
}
InputData input_data = {};
std::memcpy(&input_data, data, size);
SNTRule *newRule = [[SNTRule alloc] init];
newRule.state = (SNTRuleState) input_data.state;
newRule.type = (SNTRuleType) input_data.type;
newRule.shasum = @(input_data.hash);
newRule.customMsg = @"";
MOLXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
daemonConn.invalidationHandler = ^{
printf("An error occurred communicating with the daemon, is it running?\n");
exit(1);
};
[daemonConn resume];
[[daemonConn remoteObjectProxy] databaseRuleAddRules:@[newRule]
cleanSlate:NO
reply:^(NSError *error) {
if (!error) {
if (newRule.state == SNTRuleStateRemove) {
printf("Removed rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
} else {
printf("Added rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
}
}
}];
return 0;
}

66
Podfile
View File

@@ -1,54 +1,22 @@
platform :osx, "10.9"
inhibit_all_warnings!
target :Santa do
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
def common_pods
pod 'MOLXPCConnection'
pod 'MOLCodesignChecker'
pod 'FMDB'
pod 'MOLCertificate'
pod 'OCMock'
pod 'MOLAuthenticatingURLSession'
pod 'MOLFCMClient'
end
target :santad do
pod 'FMDB'
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
target :santabs do
pod 'FMDB'
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
project './Santa.xcodeproj'
project = Xcodeproj::Project.open "./Santa.xcodeproj"
project.targets.each do |t|
if t.name == "santa-driver"
next
end
target t.name do
common_pods
end
end
target :santactl do
pod 'FMDB'
pod 'MOLAuthenticatingURLSession'
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
pod 'MOLFCMClient', '~> 1.3'
end
target :LogicTests do
pod 'FMDB'
pod 'MOLAuthenticatingURLSession'
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
pod 'OCMock'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
if config.name != 'Release' then
break
end
# This is necessary to get FMDB to not NSLog stuff.
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ''
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] <<= "NDEBUG=1"
# Enable more compiler optimizations.
config.build_settings['GCC_OPTIMIZATION_LEVEL'] = 'fast'
config.build_settings['LLVM_LTO'] = 'YES'
end
end
end

View File

@@ -1,32 +1,46 @@
PODS:
- FMDB (2.6.2):
- FMDB/standard (= 2.6.2)
- FMDB/standard (2.6.2)
- MOLAuthenticatingURLSession (2.2):
- MOLCertificate (~> 1.5)
- MOLCertificate (1.7)
- MOLCodesignChecker (1.8):
- MOLCertificate (~> 1.7)
- MOLFCMClient (1.5):
- MOLAuthenticatingURLSession (~> 2.1)
- OCMock (3.4)
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- MOLAuthenticatingURLSession (2.4):
- MOLCertificate (~> 1.8)
- MOLCertificate (1.9)
- MOLCodesignChecker (1.10):
- MOLCertificate (~> 1.8)
- MOLFCMClient (1.8):
- MOLAuthenticatingURLSession (~> 2.4)
- MOLXPCConnection (1.2):
- MOLCodesignChecker (~> 1.9)
- OCMock (3.5)
DEPENDENCIES:
- FMDB
- MOLAuthenticatingURLSession
- MOLCertificate
- MOLCodesignChecker
- MOLFCMClient (~> 1.3)
- MOLFCMClient
- MOLXPCConnection
- OCMock
SPEC REPOS:
https://github.com/CocoaPods/Specs.git:
- FMDB
- MOLAuthenticatingURLSession
- MOLCertificate
- MOLCodesignChecker
- MOLFCMClient
- MOLXPCConnection
- OCMock
SPEC CHECKSUMS:
FMDB: 854a0341b4726e53276f2a8996f06f1b80f9259a
MOLAuthenticatingURLSession: 5a5e31eb73248c3e92c79b9a285f031194e8404c
MOLCertificate: 1cdb264405631b4bbdcf4cde7627469290cf1187
MOLCodesignChecker: 93460b82eb41b671c1c8eff9fc904d0d3d149e16
MOLFCMClient: 88debb79f8c0454c3dd4f6514c2453e57a963c08
OCMock: 35ae71d6a8fcc1b59434d561d1520b9dd4f15765
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
MOLAuthenticatingURLSession: c238aa1c9a7b1077eb39a6f40204bfe76a7d204e
MOLCertificate: e9e88a396c57032cab847f51a46e20c730cd752a
MOLCodesignChecker: b0d5db9d2f9bd94e0fd093891a5d40e5ad77cbc0
MOLFCMClient: 2bfbacd45cc11e1ca3c077e97b80401c4e4a54f1
MOLXPCConnection: c27af5cb1c43b18319698b0e568a8ddc2fc1e306
OCMock: 4ab4577fc941af31f4a0398f6e7e230cf21fc72a
PODFILE CHECKSUM: acd378b3727c923d912e09812da344f7375c14fe
PODFILE CHECKSUM: d03767a9915896232523962c98d9ff7294aec2b7
COCOAPODS: 1.4.0
COCOAPODS: 1.10.0

251
README.md
View File

@@ -1,54 +1,85 @@
Santa
[![Build Status](https://travis-ci.org/google/santa.png?branch=master)](https://travis-ci.org/google/santa)
[![Documentation Status](https://readthedocs.org/projects/santa/badge/?version=latest)](https://santa.readthedocs.io/en/latest/?badge=latest)
=====
# Santa ![CI](https://github.com/google/santa/workflows/CI/badge.svg?branch=main)
<p align="center">
<a href="#santa--">
<img src="./Source/SantaGUI/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
</a>
<img src="./Source/santa/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
</p>
Santa is a binary whitelisting/blacklisting system for macOS. 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
managing the system and synchronizing the database with a server.
Santa is not yet at 1.0. We're writing more tests, fixing bugs, working on TODOs
and finishing up a security audit.
Santa is a binary authorization system for macOS. It consists of a kernel
extension (or a system extension on macOS 10.15+) 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 managing the system and synchronizing the
database with a server.
It is named Santa because it keeps track of binaries that are naughty or nice.
Santa is a project of Google's Macintosh Operations Team.
Docs
========
The Santa docs are stored in the [Docs](https://github.com/google/santa/blob/master/Docs) directory. A Read the Docs instance is available here: https://santa.readthedocs.io.
# We're hiring!
Admin-Related Features
========
Want to work on Santa at Google NYC? [Apply on our Careers page!](https://goo.gle/3tO060z)
* Multiple modes: In the default MONITOR mode, all binaries except
those marked as blacklisted will be allowed to run, whilst being logged and recorded in the events database. In LOCKDOWN mode, only whitelisted binaries are
allowed to run.
# Docs
* Event logging: When the kext is loaded, all binary launches are logged.
When in either mode, all unknown or denied binaries are stored in the database to enable later aggregation.
The Santa docs are stored in the
[Docs](https://github.com/google/santa/blob/master/docs) directory. A Read the
Docs instance is available here: https://santa.readthedocs.io.
* Certificate-based rules, with override levels: Instead of relying on a binary's hash (or 'fingerprint'), executables can be whitelisted/blacklisted by their signing
certificate. You can therefore trust/block all binaries by a given publisher that were signed with that cert across version updates. A
binary can only be whitelisted by its certificate if its signature validates
correctly, but a rule for a binary's fingerprint will override a decision for a
certificate; i.e. you can whitelist a certificate while blacklisting a binary
signed with that certificate, or vice-versa.
The docs include deployment options, details on how parts of Santa work and
instructions for developing Santa itself.
* Path-based rules (via NSRegularExpression/ICU): This allows a similar feature to that found in Managed Client (the precursor to configuration profiles, which used the same implementation mechanism), Application Launch Restrictions via the mcxalr binary. This implementation carries the added benefit of being configurable via regex, and not relying on LaunchServices. As detailed in the wiki, when evaluating rules this holds the lowest precendence.
# Get Help
* Failsafe cert rules: You cannot put in a deny rule that would block the certificate used to sign launchd, a.k.a. pid 1, and therefore all components used in macOS. The binaries in every OS update (and in some cases entire new versions) are therefore auto-whitelisted. This does not affect binaries from Apple's App Store, which use various certs that change regularly for common apps. Likewise, you cannot blacklist Santa itself, and Santa uses a distinct separate cert than other Google apps.
If you have questions or otherwise need help getting started,
the [santa-dev](https://groups.google.com/forum/#!forum/santa-dev) group is a
great place.
If you believe you have a bug, feel free to report [an
issue](https://github.com/google/santa/isues) and we'll respond as soon as we
can.
If you believe you've found a vulnerability, please read the
[security policy](https://github.com/google/santa/security/policy) for
disclosure reporting.
# Admin-Related Features
* Multiple modes: In the default MONITOR mode, all binaries except those marked
as blocked will be allowed to run, whilst being logged and recorded in
the events database. In LOCKDOWN mode, only listed binaries are allowed to
run.
* Event logging: When the kext is loaded, all binary launches are logged. When
in either mode, all unknown or denied binaries are stored in the database to
enable later aggregation.
* Certificate-based rules, with override levels: Instead of relying on a
binary's hash (or 'fingerprint'), executables can be allowed/blocked by their
signing certificate. You can therefore allow/block all binaries by a
given publisher that were signed with that cert across version updates. A
binary can only be allowed by its certificate if its signature validates
correctly but a rule for a binary's fingerprint will override a decision for
a certificate; i.e. you can allowlist a certificate while blocking a binary
signed with that certificate, or vice-versa.
* Path-based rules (via NSRegularExpression/ICU): This allows a similar feature
to that found in Managed Client (the precursor to configuration profiles,
which used the same implementation mechanism), Application Launch
Restrictions via the mcxalr binary. This implementation carries the added
benefit of being configurable via regex, and not relying on LaunchServices.
As detailed in the wiki, when evaluating rules this holds the lowest
precedence.
* Failsafe cert rules: You cannot put in a deny rule that would block the
certificate used to sign launchd, a.k.a. pid 1, and therefore all components
used in macOS. The binaries in every OS update (and in some cases entire new
versions) are therefore automatically allowed. This does not affect binaries
from Apple's App Store, which use various certs that change regularly for
common apps. Likewise, you cannot block Santa itself, and Santa uses a
distinct separate cert than other Google apps.
# Intentions and Expectations
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
@@ -57,120 +88,102 @@ centrally managed component, Santa can help stop the spread of malware among a
large fleet of machines. Independently, Santa can aid in analyzing what is
running on your computer.
Santa is part of a defense-in-depth strategy, and you should continue to protect
hosts in whatever other ways you see fit.
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 otherwise need help getting started, the
[santa-dev](https://groups.google.com/forum/#!forum/santa-dev) group is a
great place. Please consult the [wiki](https://github.com/google/santa/wiki) and [issues](https://github.com/google/santa/issues) as well.
# Security and Performance-Related Features
Security and Performance-Related Features
============
* In-kernel caching: whitelisted binaries are cached in the kernel so the
processing required to make a request is only done if the binary
isn't already cached.
* In-kernel caching: allowed binaries are cached in the kernel so the
processing required to make a request is only done if the binary isn't
already cached.
* Userland components validate each other: each of the userland components (the
daemon, the GUI agent and the command-line utility) communicate with each other
using XPC and check that their signing certificates are identical before any
communication is accepted.
daemon, the GUI agent and the command-line utility) communicate with each
other using XPC and check that their signing certificates are identical
before any communication is accepted.
* Kext uses only KPIs: the kernel extension only uses provided kernel
programming interfaces to do its job. This means that the kext code should
continue to work across OS versions.
programming interfaces to do its job. This means that the kext code should
continue to work across OS versions.
Known Issues
============
Santa is not yet at 1.0 and we have some known issues to be aware of:
# Known Issues
* 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`. As of version 0.9.1 we *do* address [__PAGEZERO missing issues](b87482e) that were exploited in some versions of macOS. We are working on also protecting against similar avenues of attack.
dynamic libraries loaded with dlopen, libraries on disk that have been
replaced, or libraries loaded using `DYLD_INSERT_LIBRARIES`. As of version
0.9.1 we *do* address [__PAGEZERO missing issues](b87482e) that were
exploited in some versions of macOS. We are working on also protecting
against similar 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.
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.
* Database protection: the SQLite database is installed with permissions so that
only the root user can read/write it. We're considering approaches to secure
this further.
* Sync client: The `santactl` command-line client includes a flag to synchronize with a management server, which uploads events that have occurred on the
machine and downloads new rules. We're still very heavily working on this
server (which is AppEngine-based and will be open-sourced in the future), so the
sync client code is unfinished. It does show the 'API' that we're expecting to
use so if you'd like to write your own management server, feel free to look at
how the client currently works (and suggest changes!)
* Database protection: the SQLite database is installed with permissions so
that only the root user can read/write it. We're considering approaches to
secure this further.
* Scripts: Santa is currently written to ignore any execution that isn't a
binary. This is because after weighing the administration cost vs the benefit,
we found it wasn't worthwhile. Additionally, a number of applications make use
of temporary generated scripts, which we can't possibly whitelist and not doing
so would cause problems. We're happy to revisit this (or at least make it an
option) if it would be useful to others.
binary. This is because after weighing the administration cost vs the
benefit, we found it wasn't worthwhile. Additionally, a number of
applications make use of temporary generated scripts, which we can't possibly
allowlist and not doing so would cause problems. We're happy to revisit this
(or at least make it an option) if it would be useful to others.
* Documentation: This is currently limited.
# Sync Servers
* Tests: There aren't enough of them.
* The `santactl` command-line client includes a flag to synchronize with a
management server, which uploads events that have occurred on the machine and
downloads new rules. There are several open-source servers you can sync with:
Screenshots
===========
* [Upvote](https://github.com/google/upvote) - An AppEngine-based server
that implements social voting to make managing a large fleet easier.
* [Moroz](https://github.com/groob/moroz) - A simple golang server that
serves hardcoded rules from simple configuration files.
* [Zentral](https://github.com/zentralopensource/zentral/wiki) - A
centralized service that pulls data from multiple sources and deploy
configurations to multiple services.
* [Zercurity](https://github.com/zercurity/zercurity) - A dockerized service
for managing and monitoring applications across a large fleet utilizing
Santa + Osquery.
A tool like Santa doesn't really lend itself to screenshots, so here's a video instead.
* Alternatively, `santactl` can configure rules locally (without a sync
server).
<p align="center">
<img src="https://zippy.gfycat.com/MadFatalAmphiuma.gif" alt="Santa Block Video" />
</p>
# Screenshots
Building
========
Firstly, make sure you're using Xcode 7.3.1 as currently we do not support
building with Xcode 8.
A tool like Santa doesn't really lend itself to screenshots, so here's a video
instead.
```sh
git clone https://github.com/google/santa
cd santa
# Build a debug build. This will install any necessary CocoaPods, create the
# workspace and build, outputting the full log only if an error occurred.
# If CocoaPods is not installed, you'll be prompted to install it.
#
# For other build/install/run options, run rake without any arguments
rake build:debug
```
<p align="center"> <img src="https://thumbs.gfycat.com/MadFatalAmphiuma-small.gif" alt="Santa Block Video" /> </p>
Note: the Xcode project is setup to use any installed "Mac Developer" certificate
and for security-reasons parts of Santa will not operate properly if not signed.
For more details on building see the [building.md](https://github.com/google/santa/blob/master/Docs/development/building.md) document.
Kext Signing
============
# Kext Signing
Kernel extensions on macOS 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.
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:
1) Use a [pre-built, pre-signed version](https://github.com/google/santa/releases)
of the kext that we supply. Each time changes are made to the kext code we will
update the pre-built version that you can make use of. This doesn't prevent you
from making changes to the non-kext parts of Santa and distributing those.
If you make changes to the kext and make a pull request, we can merge them in
and distribute a new version of the pre-signed kext.
1) Use a [pre-built, pre-signed
version](https://github.com/google/santa/releases) of the kext that we supply.
Each time changes are made to the kext code we will update the pre-built
version that you can make use of. This doesn't prevent you from making changes
to the non-kext parts of Santa and distributing those. If you make changes to
the kext and make a pull request, we can merge them in and distribute a new
version of the pre-signed kext.
2) Apply for your own [kext signing certificate](https://developer.apple.com/contact/kext/).
Apple will only grant this for broad distribution within an organization, they
won't issue them just for testing purposes.
2) Apply for your own [kext signing
certificate](https://developer.apple.com/contact/kext/). Apple will only grant
this for broad distribution within an organization, they won't issue them just
for testing purposes.
Contributing
============
Patches to this project are very much welcome. Please see the [CONTRIBUTING](https://github.com/google/santa/blob/master/CONTRIBUTING.md)
# Contributing
Patches to this project are very much welcome. Please see the
[CONTRIBUTING](https://github.com/google/santa/blob/master/CONTRIBUTING.md)
file.
Disclaimer
==========
# Disclaimer
This is **not** an official Google product.

225
Rakefile
View File

@@ -1,225 +0,0 @@
require 'openssl'
WORKSPACE = 'Santa.xcworkspace'
DEFAULT_SCHEME = 'All'
OUTPUT_PATH = 'Build'
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"
DEVTEAM_FILE = 'Source/DevelopmentTeam.xcconfig'
DEVTEAM_CERT_CN = 'Mac Developer'
$DISABLE_XCPRETTY = false
task :default do
system("rake -sT")
end
def xcodebuild(opts)
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"
end
end
def xcodebuilddir
if not $xcode_build_dir
output = `xcodebuild #{XCODEBUILD_DEFAULTS} -scheme All -showBuildSettings`
if match = output.match(/BUILD_DIR = (.*)/)
$xcode_build_dir = match.captures.first
puts "Found Xcode build dir #{$xcode_build_dir}"
end
end
$xcode_build_dir
end
task :init do
unless File.exists?(WORKSPACE) and File.exists?('Pods')
puts "Pods missing, running 'pod install'"
system "pod install" or raise "CocoaPods is not installed. Install with 'sudo gem install cocoapods'"
end
unless system 'xcpretty -v >/dev/null 2>&1'
puts "xcpretty is not installed. Install with 'sudo gem install xcpretty'"
$DISABLE_XCPRETTY = true
end
cert_pem = `security find-certificate -p -c '#{DEVTEAM_CERT_CN}'`
cert = OpenSSL::X509::Certificate.new cert_pem
team_id = cert.subject.to_a.find {|f| f[0] == "OU"}[1]
File.open(DEVTEAM_FILE, 'w') { |f|
f.puts("// This file is auto-generated. Do not edit manually")
f.puts("DEVELOPMENT_TEAM = #{team_id}")
}
end
task :remove_existing do
system 'sudo rm -rf /Library/Extensions/santa-driver.kext'
system 'sudo rm -rf /Applications/Santa.app'
end
desc "Clean"
task :clean => :init do
puts "Cleaning"
FileUtils.rm_rf(OUTPUT_PATH)
xcodebuild("-scheme All clean")
end
# Build
namespace :build do
desc "Build: Debug"
task :debug do
Rake::Task['build:build'].invoke("Debug")
end
desc "Build: Release"
task :release do
Rake::Task['build:build'].invoke("Release")
end
task :build, [:configuration] => :init do |t, args|
config = args[:configuration]
puts "Building with configuration: #{config}"
xcodebuild("-scheme All -configuration #{config} build")
end
end
# Install
namespace :install do
desc "Install: Debug"
task :debug do
Rake::Task['install:install'].invoke("Debug")
end
desc "Install: Release"
task :release do
Rake::Task['install:install'].invoke("Release")
end
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.santagui.plist /Library/LaunchAgents'
system 'sudo cp conf/com.google.santa.asl.conf /etc/asl'
system '/usr/bin/killall -HUP syslogd'
Rake::Task['build:build'].invoke(config)
puts "Installing with configuration: #{config}"
Rake::Task['remove_existing'].invoke()
system "sudo cp -r #{xcodebuilddir}/#{config}/santa-driver.kext /Library/Extensions"
system "sudo cp -r #{xcodebuilddir}/#{config}/Santa.app /Applications"
end
end
# Dist
task :dist do
desc "Create distribution folder"
Rake::Task['clean'].invoke()
Rake::Task['build:build'].invoke("Release")
dist_path = "santa-#{`defaults read #{xcodebuilddir}/Release/santa-driver.kext/Contents/Info.plist CFBundleVersion`.strip}"
FileUtils.rm_rf(dist_path)
FileUtils.mkdir_p("#{dist_path}/binaries")
FileUtils.mkdir_p("#{dist_path}/conf")
FileUtils.mkdir_p("#{dist_path}/dsym")
BINARIES.each do |x|
FileUtils.cp_r("#{xcodebuilddir}/Release/#{x}", "#{dist_path}/binaries")
end
DSYMS.each do |x|
FileUtils.cp_r("#{xcodebuilddir}/Release/#{x}", "#{dist_path}/dsym")
end
Dir.glob("Conf/*") {|x| File.directory?(x) or FileUtils.cp(x, "#{dist_path}/conf")}
puts "Distribution folder #{dist_path} created"
end
# Tests
namespace :tests do
desc "Tests: Logic"
task :logic => [:init] do
puts "Running logic tests"
xcodebuild("-scheme LogicTests test")
end
desc "Tests: Kernel"
task :kernel do
Rake::Task['unload'].invoke()
Rake::Task['install:debug'].invoke()
Rake::Task['load_kext'].invoke
FileUtils.mkdir_p("/tmp/santa_kerneltests_tmp")
begin
puts "\033[?25l\033[12h" # hide cursor
puts "Running kernel tests"
system "cd /tmp/santa_kerneltests_tmp && sudo #{xcodebuilddir}/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
end
end
# Load/Unload
task :unload_daemon do
puts "Unloading daemon"
system "sudo launchctl unload /Library/LaunchDaemons/com.google.santad.plist 2>/dev/null"
end
task :unload_kext do
puts "Unloading kernel extension"
system "sudo kextunload -b com.google.santa-driver 2>/dev/null"
end
task :unload_gui do
puts "Unloading GUI agent"
system "launchctl unload /Library/LaunchAgents/com.google.santagui.plist 2>/dev/null"
end
desc "Unload"
task :unload => [:unload_daemon, :unload_kext, :unload_gui]
task :load_daemon do
puts "Loading daemon"
system "sudo launchctl load /Library/LaunchDaemons/com.google.santad.plist"
end
task :load_kext do
puts "Loading kernel extension"
system "sudo kextload /Library/Extensions/santa-driver.kext"
end
task :load_gui do
puts "Loading GUI agent"
system "launchctl load /Library/LaunchAgents/com.google.santagui.plist 2>/dev/null"
end
desc "Load"
task :load => [:load_kext, :load_daemon, :load_gui]
namespace :reload do
desc "Reload: Debug"
task :debug do
Rake::Task['unload'].invoke()
Rake::Task['install:debug'].invoke()
Rake::Task['load'].invoke()
end
desc "Reload: Release"
task :release do
Rake::Task['unload'].invoke()
Rake::Task['install:release'].invoke()
Rake::Task['load'].invoke()
end
end

12
SECURITY.md Normal file
View File

@@ -0,0 +1,12 @@
# Reporting a Vulnerability
If you believe you have found a security vulnerability, we would appreciate private disclosure
so that we can work on a fix before disclosure. Any vulnerabilities reported to us will be
disclosed publicly either when a new version with fixes is released or 90 days has passed,
whichever comes first.
To report vulnerabilities to us privately, please e-mail `santa-team@google.com`.
If you want to encrypt your e-mail, you can use our GPG key `0x92AFE41DAB49BBB6`
available on pool.sks-keyservers.net:
`gpg --keyserver pool.sks-keyservers.net --recv-key 0x92AFE41DAB49BBB6`

File diff suppressed because it is too large Load Diff

View File

@@ -1,83 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D91BCDC174E8AE600131A7D"
BuildableName = "All"
BlueprintName = "All"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
enableAddressSanitizer = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D260DAB18B68E12002A0B55"
BuildableName = "LogicTests.xctest"
BlueprintName = "LogicTests"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D91BCDC174E8AE600131A7D"
BuildableName = "All"
BlueprintName = "All"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,91 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D0016A1192BCD3C005E7FCD"
BuildableName = "KernelTests"
BlueprintName = "KernelTests"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D0016A1192BCD3C005E7FCD"
BuildableName = "KernelTests"
BlueprintName = "KernelTests"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D0016A1192BCD3C005E7FCD"
BuildableName = "KernelTests"
BlueprintName = "KernelTests"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D0016A1192BCD3C005E7FCD"
BuildableName = "KernelTests"
BlueprintName = "KernelTests"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,99 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D260DAB18B68E12002A0B55"
BuildableName = "LogicTests.xctest"
BlueprintName = "LogicTests"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D260DAB18B68E12002A0B55"
BuildableName = "LogicTests.xctest"
BlueprintName = "LogicTests"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D260DAB18B68E12002A0B55"
BuildableName = "LogicTests.xctest"
BlueprintName = "LogicTests"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D260DAB18B68E12002A0B55"
BuildableName = "LogicTests.xctest"
BlueprintName = "LogicTests"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D260DAB18B68E12002A0B55"
BuildableName = "LogicTests.xctest"
BlueprintName = "LogicTests"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -14,7 +14,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D385DB5180DE4A900418BC6"
BlueprintIdentifier = "C779C2DD22F0E95000EE2541"
BuildableName = "Santa.app"
BlueprintName = "Santa"
ReferencedContainer = "container:Santa.xcodeproj">
@@ -29,17 +29,6 @@
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D385DB5180DE4A900418BC6"
BuildableName = "Santa.app"
BlueprintName = "Santa"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@@ -55,14 +44,12 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D385DB5180DE4A900418BC6"
BlueprintIdentifier = "C779C2DD22F0E95000EE2541"
BuildableName = "Santa.app"
BlueprintName = "Santa"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@@ -74,7 +61,7 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D385DB5180DE4A900418BC6"
BlueprintIdentifier = "C779C2DD22F0E95000EE2541"
BuildableName = "Santa.app"
BlueprintName = "Santa"
ReferencedContainer = "container:Santa.xcodeproj">

View File

@@ -1,71 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D91BCB3174E8A7E00131A7D"
BuildableName = "santa-driver.kext"
BlueprintName = "santa-driver"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D91BCB3174E8A7E00131A7D"
BuildableName = "santa-driver.kext"
BlueprintName = "santa-driver"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C78227531E1C3C58006EB2D6"
BuildableName = "santabs.xpc"
BlueprintName = "santabs"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C78227531E1C3C58006EB2D6"
BuildableName = "santabs.xpc"
BlueprintName = "santabs"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C78227531E1C3C58006EB2D6"
BuildableName = "santabs.xpc"
BlueprintName = "santabs"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,91 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D35BD9D18FD71CE00921A21"
BuildableName = "santactl"
BlueprintName = "santactl"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D35BD9D18FD71CE00921A21"
BuildableName = "santactl"
BlueprintName = "santactl"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D35BD9D18FD71CE00921A21"
BuildableName = "santactl"
BlueprintName = "santactl"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D35BD9D18FD71CE00921A21"
BuildableName = "santactl"
BlueprintName = "santactl"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -14,9 +14,9 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D9A7F3C1759330400035EB5"
BuildableName = "santad"
BlueprintName = "santad"
BlueprintIdentifier = "C779C4E522F0F51400EE2541"
BuildableName = "com.google.santa.daemon"
BlueprintName = "com.google.santa.daemon"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildActionEntry>
@@ -29,17 +29,6 @@
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D9A7F3C1759330400035EB5"
BuildableName = "santad"
BlueprintName = "santad"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@@ -56,14 +45,12 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D9A7F3C1759330400035EB5"
BuildableName = "santad"
BlueprintName = "santad"
BlueprintIdentifier = "C779C4E522F0F51400EE2541"
BuildableName = "com.google.santa.daemon"
BlueprintName = "com.google.santa.daemon"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@@ -75,9 +62,9 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D9A7F3C1759330400035EB5"
BuildableName = "santad"
BlueprintName = "santad"
BlueprintIdentifier = "C779C4E522F0F51400EE2541"
BuildableName = "com.google.santa.daemon"
BlueprintName = "com.google.santa.daemon"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>

View File

@@ -1 +1,10 @@
<?xml version='1.0' encoding='UTF-8'?><Workspace version='1.0'><FileRef location='group:Santa.xcodeproj'/><FileRef location='group:Pods/Pods.xcodeproj'/></Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Santa.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

245
Source/common/BUILD Normal file
View File

@@ -0,0 +1,245 @@
load("//:helper.bzl", "santa_unit_test")
package(default_visibility = ["//:santa_package_group"])
licenses(["notice"]) # Apache 2.0
cc_library(
name = "SantaCache",
srcs = ["SantaCache.h"],
deps = ["//Source/common:SNTKernelCommon"],
)
santa_unit_test(
name = "SantaCacheTest",
srcs = [
"SantaCache.h",
"SantaCacheTest.mm",
],
deps = ["//Source/common:SNTKernelCommon"],
)
objc_library(
name = "SNTBlockMessage",
srcs = ["SNTBlockMessage.m"],
hdrs = ["SNTBlockMessage.h"],
deps = [
":SNTConfigurator",
":SNTLogging",
":SNTStoredEvent",
],
)
objc_library(
name = "SNTBlockMessage_SantaGUI",
srcs = ["SNTBlockMessage.m"],
hdrs = ["SNTBlockMessage.h"],
defines = ["SANTAGUI"],
deps = [
":SNTConfigurator",
":SNTLogging",
":SNTStoredEvent",
],
)
objc_library(
name = "SNTCachedDecision",
srcs = ["SNTCachedDecision.m"],
hdrs = ["SNTCachedDecision.h"],
deps = [
":SNTCommonEnums",
":SNTKernelCommon",
],
)
objc_library(
name = "SNTCommonEnums",
hdrs = ["SNTCommonEnums.h"],
)
objc_library(
name = "SNTConfigurator",
srcs = ["SNTConfigurator.m"],
hdrs = ["SNTConfigurator.h"],
deps = [
":SNTCommonEnums",
":SNTStrengthify",
":SNTSystemInfo",
],
)
objc_library(
name = "SNTDropRootPrivs",
srcs = ["SNTDropRootPrivs.m"],
hdrs = ["SNTDropRootPrivs.h"],
)
objc_library(
name = "SNTFileInfo",
srcs = ["SNTFileInfo.m"],
hdrs = ["SNTFileInfo.h"],
deps = [
"@FMDB",
"@MOLCodesignChecker",
],
)
cc_library(
name = "SNTKernelCommon",
hdrs = ["SNTKernelCommon.h"],
)
cc_library(
name = "SNTLoggingKernel",
hdrs = ["SNTLogging.h"],
copts = [
"-mkernel",
"-I__BAZEL_XCODE_SDKROOT__/System/Library/Frameworks/Kernel.framework/Headers",
],
defines = ["KERNEL"],
)
objc_library(
name = "SNTLogging",
srcs = ["SNTLogging.m"],
hdrs = ["SNTLogging.h"],
deps = [":SNTConfigurator"],
)
cc_library(
name = "SNTPrefixTree",
srcs = ["SNTPrefixTree.cc"],
hdrs = ["SNTPrefixTree.h"],
copts = ["-std=c++11"],
deps = [":SNTLogging"],
)
cc_library(
name = "SNTPrefixTreeKernel",
srcs = ["SNTPrefixTree.cc"],
hdrs = ["SNTPrefixTree.h"],
copts = [
"-std=c++11",
"-mkernel",
"-I__BAZEL_XCODE_SDKROOT__/System/Library/Frameworks/Kernel.framework/Headers",
],
defines = ["KERNEL"],
deps = [":SNTLoggingKernel"],
)
objc_library(
name = "SNTRule",
srcs = ["SNTRule.m"],
hdrs = ["SNTRule.h"],
deps = [":SNTCommonEnums"],
)
objc_library(
name = "SNTStoredEvent",
srcs = ["SNTStoredEvent.m"],
hdrs = ["SNTStoredEvent.h"],
deps = [
":SNTCommonEnums",
"@MOLCertificate",
],
)
cc_library(
name = "SNTStrengthify",
hdrs = ["SNTStrengthify.h"],
)
objc_library(
name = "SNTSystemInfo",
srcs = ["SNTSystemInfo.m"],
hdrs = ["SNTSystemInfo.h"],
sdk_frameworks = ["IOKit"],
)
objc_library(
name = "SNTXPCBundleServiceInterface",
srcs = ["SNTXPCBundleServiceInterface.m"],
hdrs = ["SNTXPCBundleServiceInterface.h"],
deps = [
":SNTStoredEvent",
"@MOLXPCConnection",
],
)
objc_library(
name = "SNTXPCControlInterface",
srcs = ["SNTXPCControlInterface.m"],
hdrs = ["SNTXPCControlInterface.h"],
deps = [
":SNTConfigurator",
":SNTStoredEvent",
":SNTXPCUnprivilegedControlInterface",
"@MOLXPCConnection",
],
)
objc_library(
name = "SNTXPCNotifierInterface",
srcs = ["SNTXPCNotifierInterface.m"],
hdrs = ["SNTXPCNotifierInterface.h"],
deps = [
":SNTCommonEnums",
":SNTXPCBundleServiceInterface",
],
)
objc_library(
name = "SNTXPCSyncdInterface",
srcs = ["SNTXPCSyncdInterface.m"],
hdrs = ["SNTXPCSyncdInterface.h"],
deps = [
":SNTCommonEnums",
":SNTStoredEvent",
],
)
objc_library(
name = "SNTXPCSyncServiceInterface",
srcs = ["SNTXPCSyncServiceInterface.m"],
hdrs = ["SNTXPCSyncServiceInterface.h"],
deps = [
":SNTStoredEvent",
"@MOLXPCConnection",
],
)
objc_library(
name = "SNTXPCUnprivilegedControlInterface",
srcs = ["SNTXPCUnprivilegedControlInterface.m"],
hdrs = ["SNTXPCUnprivilegedControlInterface.h"],
deps = [
":SNTCommonEnums",
":SNTKernelCommon",
":SNTRule",
":SNTStoredEvent",
":SNTXPCBundleServiceInterface",
"@MOLCertificate",
"@MOLXPCConnection",
],
)
santa_unit_test(
name = "SNTFileInfoTest",
srcs = ["SNTFileInfoTest.m"],
resources = [
"testdata/bad_pagezero",
"testdata/missing_pagezero",
"testdata/32bitplist",
],
structured_resources = glob([
"testdata/BundleExample.app/**",
"testdata/DirectoryBundle/**",
]),
deps = [":SNTFileInfo"],
)
santa_unit_test(
name = "SNTPrefixTreeTest",
srcs = ["SNTPrefixTreeTest.mm"],
deps = ["SNTPrefixTree"],
)

View File

@@ -13,9 +13,9 @@
/// limitations under the License.
#ifdef SANTAGUI
@import Cocoa;
#import <Cocoa/Cocoa.h>
#else
@import Foundation;
#import <Foundation/Foundation.h>
#endif
@class SNTStoredEvent;

View File

@@ -12,11 +12,12 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTBlockMessage.h"
#import "Source/common/SNTBlockMessage.h"
#import "SNTConfigurator.h"
#import "SNTLogging.h"
#import "SNTStoredEvent.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/common/SNTSystemInfo.h"
@implementation SNTBlockMessage
@@ -26,10 +27,24 @@
@"body {"
@" font-family: 'Lucida Grande', 'Helvetica', sans-serif;"
@" font-size: 13px;"
@" color: #666;"
@" color: %@;"
@" text-align: center;"
@"}"
// Supported in beta WebKit. Not sure if it is dynamic when used with NSAttributedString.
@"@media (prefers-color-scheme: dark) {"
@" body {"
@" color: #ddd;"
@" }"
@"}"
@"</style></head><body>";
// Support Dark Mode. Note, the returned NSAttributedString is static and does not update when
// the OS switches modes.
NSString *mode = [NSUserDefaults.standardUserDefaults stringForKey:@"AppleInterfaceStyle"];
BOOL dark = [mode isEqualToString:@"Dark"];
htmlHeader = [NSString stringWithFormat:htmlHeader, dark ? @"#ddd" : @"#333"];
NSString *htmlFooter = @"</body></html>";
NSString *message;
@@ -51,7 +66,7 @@
NSString *fullHTML = [NSString stringWithFormat:@"%@%@%@", htmlHeader, message, htmlFooter];
#ifdef NSAppKitVersionNumber10_0
#ifdef SANTAGUI
NSData *htmlData = [fullHTML dataUsingEncoding:NSUTF8StringEncoding];
return [[NSAttributedString alloc] initWithHTML:htmlData documentAttributes:NULL];
#else
@@ -92,6 +107,9 @@
+ (NSURL *)eventDetailURLForEvent:(SNTStoredEvent *)event {
SNTConfigurator *config = [SNTConfigurator configurator];
NSString *hostname = [SNTSystemInfo longHostname];
NSString *uuid = [SNTSystemInfo hardwareUUID];
NSString *serial = [SNTSystemInfo serialNumber];
NSString *formatStr = config.eventDetailURL;
if (!formatStr.length) return nil;
@@ -108,6 +126,15 @@
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%machine_id%"
withString:config.machineID];
}
if (hostname.length) {
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%hostname%" withString:hostname];
}
if (uuid.length) {
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%uuid%" withString:uuid];
}
if (serial.length) {
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%serial%" withString:serial];
}
return [NSURL URLWithString:formatStr];
}

View File

@@ -12,21 +12,27 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@import Foundation;
#import <Foundation/Foundation.h>
#import "SNTCommonEnums.h"
#import "Source/common/SNTCommonEnums.h"
#import "Source/common/SNTKernelCommon.h"
@class MOLCertificate;
///
/// Store information about executions from decision making for later logging.
///
@interface SNTCachedDecision : NSObject
@property uint64_t vnodeId;
@property santa_vnode_id_t vnodeId;
@property SNTEventState decision;
@property NSString *decisionExtra;
@property NSString *sha256;
@property NSString *certSHA256;
@property NSString *certCommonName;
@property NSArray<MOLCertificate *> *certChain;
@property NSString *quarantineURL;
@property NSString *customMsg;

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTCachedDecision.h"
#import "Source/common/SNTCachedDecision.h"
@implementation SNTCachedDecision
@end

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@import Foundation;
#import <Foundation/Foundation.h>
///
/// These enums are used in various places throughout the Santa client code.
@@ -29,10 +29,13 @@ typedef NS_ENUM(NSInteger, SNTRuleType) {
typedef NS_ENUM(NSInteger, SNTRuleState) {
SNTRuleStateUnknown,
SNTRuleStateWhitelist = 1,
SNTRuleStateBlacklist = 2,
SNTRuleStateSilentBlacklist = 3,
SNTRuleStateAllow = 1,
SNTRuleStateBlock = 2,
SNTRuleStateSilentBlock = 3,
SNTRuleStateRemove = 4,
SNTRuleStateAllowCompiler = 5,
SNTRuleStateAllowTransitive = 6,
};
typedef NS_ENUM(NSInteger, SNTClientMode) {
@@ -58,6 +61,9 @@ typedef NS_ENUM(NSInteger, SNTEventState) {
SNTEventStateAllowBinary = 1 << 25,
SNTEventStateAllowCertificate = 1 << 26,
SNTEventStateAllowScope = 1 << 27,
SNTEventStateAllowCompiler = 1 << 28,
SNTEventStateAllowTransitive = 1 << 29,
SNTEventStateAllowPendingTransitive = 1 << 30,
// Block and Allow masks
SNTEventStateBlock = 0xFF << 16,
@@ -68,7 +74,6 @@ typedef NS_ENUM(NSInteger, SNTRuleTableError) {
SNTRuleTableErrorEmptyRuleArray,
SNTRuleTableErrorInsertOrReplaceFailed,
SNTRuleTableErrorInvalidRule,
SNTRuleTableErrorMissingRequiredRule,
SNTRuleTableErrorRemoveFailed
};
@@ -80,6 +85,13 @@ typedef NS_ENUM(NSInteger, SNTBundleEventAction) {
SNTBundleEventActionSendEvents,
};
// Indicates where to store event logs.
typedef NS_ENUM(NSInteger, SNTEventLogType) {
SNTEventLogTypeSyslog,
SNTEventLogTypeFilelog,
};
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";
static const char *kSantaDPath = "/Applications/Santa.app/Contents/Library/SystemExtensions/com.google.santa.daemon.systemextension/Contents/MacOS/com.google.santa.daemon";
static const char *kSantaCtlPath = "/Applications/Santa.app/Contents/MacOS/santactl";
static const char *kSantaAppPath = "/Applications/Santa.app";

View File

@@ -12,9 +12,9 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@import Foundation;
#import <Foundation/Foundation.h>
#import "SNTCommonEnums.h"
#import "Source/common/SNTCommonEnums.h"
///
/// Singleton that provides an interface for managing configuration values on disk
@@ -36,32 +36,32 @@
- (void)setSyncServerClientMode:(SNTClientMode)newMode;
///
/// The regex of whitelisted paths. Regexes are specified in ICU format.
/// The regex of allowed 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(readonly, nonatomic) NSRegularExpression *whitelistPathRegex;
@property(readonly, nonatomic) NSRegularExpression *allowedPathRegex;
///
/// Set the regex of whitelisted paths as received from a sync server.
/// Set the regex of allowed paths as received from a sync server.
///
- (void)setSyncServerWhitelistPathRegex:(NSRegularExpression *)re;
- (void)setSyncServerAllowedPathRegex:(NSRegularExpression *)re;
///
/// The regex of blacklisted paths. Regexes are specified in ICU format.
/// The regex of blocked 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(readonly, nonatomic) NSRegularExpression *blacklistPathRegex;
@property(readonly, nonatomic) NSRegularExpression *blockedPathRegex;
///
/// Set the regex of blacklisted paths as received from a sync server.
/// Set the regex of blocked paths as received from a sync server.
///
- (void)setSyncServerBlacklistPathRegex:(NSRegularExpression *)re;
- (void)setSyncServerBlockedPathRegex:(NSRegularExpression *)re;
///
/// The regex of paths to log file changes for. Regexes are specified in ICU format.
@@ -72,6 +72,57 @@
///
@property(readonly, nonatomic) NSRegularExpression *fileChangesRegex;
///
/// A list of ignore prefixes which are checked in-kernel.
/// This is more performant than FileChangesRegex when ignoring whole directory trees.
///
/// For example adding a prefix of "/private/tmp/" will turn off file change log generation
/// in-kernel for that entire tree. Since they are ignored by the kernel, they never reach santad
/// and are not seen by the fileChangesRegex. Note the trailing "/", without it any file or
/// directory starting with "/private/tmp" would be ignored.
///
/// By default "/." and "/dev/" are added.
///
/// Memory in the kernel is precious. A total of MAXPATHLEN (1024) nodes are allowed.
/// Using all 1024 nodes will result in santa-driver allocating ~2MB of wired memory.
/// An ASCII character uses 1 node. An UTF-8 encoded Unicode character uses 1-4 nodes.
/// Prefixes are added to the running config in-order, one by one. The prefix will be ignored if
/// (the running config's current size) + (the prefix's size) totals up to more than 1024 nodes.
/// The running config is stored in a prefix tree.
/// Prefixes that share prefixes are effectively de-duped; their shared node sized components only
/// take up 1 node. For example these 3 prefixes all have a common prefix of "/private/".
/// They will only take up 21 nodes instead of 39.
///
/// "/private/tmp/"
/// "/private/var/"
/// "/private/new/"
///
/// -> [t] -> [m] -> [p] -> [/]
///
/// [/] -> [p] -> [r] -> [i] -> [v] -> [a] -> [t] -> [e] -> [/] -> [v] -> [a] -> [r] -> [/]
///
/// -> [n] -> [e] -> [w] -> [/]
///
/// Prefixes with Unicode characters work similarly. Assuming a UTF-8 encoding these two prefixes
/// are actually the same for the first 3 nodes. They take up 7 nodes instead of 10.
///
/// "/🤘"
/// "/🖖"
///
/// -> [0xa4] -> [0x98]
///
/// [/] -> [0xf0] -> [0x9f]
///
/// -> [0x96] -> [0x96]
///
/// To disable file change logging completely add "/".
/// TODO(bur): Make this default if no FileChangesRegex is set.
///
/// Filters are only applied on santad startup.
/// TODO(bur): Support add / remove of filters while santad is running.
///
@property(readonly, nonatomic) NSArray *fileChangesPrefixFilters;
///
/// Enable __PAGEZERO protection, defaults to YES
/// If this flag is set to NO, 32-bit binaries that are missing
@@ -79,6 +130,58 @@
///
@property(readonly, nonatomic) BOOL enablePageZeroProtection;
///
/// Enable bad signature protection, defaults to NO.
/// When enabled, a binary that is signed but has a bad signature (cert revoked, binary
/// tampered with, etc.) will be blocked regardless of client-mode unless a binary allowlist
/// rule exists.
///
@property(readonly, nonatomic) BOOL enableBadSignatureProtection;
///
/// Defines how event logs are stored. Options are:
/// SNTEventLogTypeSyslog: Sent to ASL or ULS (if built with the 10.12 SDK or later).
/// SNTEventLogTypeFilelog: Sent to a file on disk. Use eventLogPath to specify a path.
/// Defaults to SNTEventLogTypeFilelog.
/// For mobileconfigs use EventLogType as the key and syslog or filelog strings as the value.
///
/// @note: This property is KVO compliant, but should only be read once at santad startup.
///
@property(readonly, nonatomic) SNTEventLogType eventLogType;
///
/// If eventLogType is set to Filelog, eventLogPath will provide the path to save logs.
/// Defaults to /var/db/santa/santa.log.
///
/// @note: This property is KVO compliant, but should only be read once at santad startup.
///
@property(readonly, nonatomic) NSString *eventLogPath;
///
/// Enabling this appends the Santa machine ID to the end of each log line. If nothing
/// has been overriden, this is the host's UUID.
/// Defaults to NO.
///
@property(readonly, nonatomic) BOOL enableMachineIDDecoration;
///
/// Use the bundled SystemExtension on macOS 10.15+, defaults to YES.
/// Disable to continue using the bundled KEXT.
/// This is a one way switch, if this is ever true on macOS 10.15+ the KEXT will be deleted.
/// This gives admins control over the timing of switching to the SystemExtension. The intended use case is to have an MDM deliver
/// the requisite SystemExtension and TCC profiles before attempting to load.
///
@property(readonly, nonatomic) BOOL enableSystemExtension;
///
/// Use an internal cache for decisions instead of relying on the caching
/// mechanism built-in to the EndpointSecurity framework. This may increase
/// performance, particularly when Santa is run alongside other system
/// extensions.
/// Has no effect if the system extension is not being used. Defaults to NO.
///
@property(readonly, nonatomic) BOOL enableSysxCache;
#pragma mark - GUI Settings
///
@@ -97,6 +200,9 @@
/// %file_sha% -- SHA-256 of the file that was blocked.
/// %machine_id% -- ID of the machine.
/// %username% -- executing user.
/// %serial% -- System's serial number.
/// %uuid% -- System's UUID.
/// %hostname% -- System's full hostname.
///
/// @note: This is not an NSURL because the format-string parsing is done elsewhere.
///
@@ -170,7 +276,16 @@
/// If YES, enables bundle detection for blocked events. This property is not stored on disk.
/// Its value is set by a sync server that supports bundles. Defaults to NO.
///
@property BOOL bundlesEnabled;
@property BOOL enableBundles;
#pragma mark Transitive Allowlist Settings
///
/// If YES, binaries marked with SNTRuleStateAllowCompiler rules are allowed to transitively
/// allow any executables that they produce. If NO, SNTRuleStateAllowCompiler rules are
/// interpreted as if they were simply SNTRuleStateAllow rules. Defaults to NO.
///
@property BOOL enableTransitiveRules;
#pragma mark Server Auth Settings
@@ -209,6 +324,51 @@
///
@property(readonly, nonatomic) NSString *syncClientAuthCertificateIssuer;
///
/// If true, forks and exits will be logged. Defaults to false.
///
@property(readonly, nonatomic) BOOL enableForkAndExitLogging;
///
/// If true, ignore actions from other endpoint security clients. Defaults to false. This only
/// applies when running as a sysx.
///
@property(readonly, nonatomic) BOOL ignoreOtherEndpointSecurityClients;
///
/// If true, debug logging will be enabled for all Santa components. Defaults to false.
/// Passing --debug as an executable argument will enable debug logging for that specific component.
///
@property(readonly, nonatomic) BOOL enableDebugLogging;
///
/// If true, compressed requests from "santactl sync" will set "Content-Encoding" to "zlib"
/// instead of the new default "deflate". If syncing with Upvote deployed at commit 0b4477d
/// or below, set this option to true.
/// Defaults to false.
///
@property(readonly, nonatomic) BOOL enableBackwardsCompatibleContentEncoding;
///
/// Contains the FCM project name.
///
@property(readonly, nonatomic) NSString *fcmProject;
///
/// Contains the FCM project entity.
///
@property(readonly, nonatomic) NSString *fcmEntity;
///
/// Contains the FCM project API key.
///
@property(readonly, nonatomic) NSString *fcmAPIKey;
///
/// True if fcmProject, fcmEntity and fcmAPIKey are all set. Defaults to false.
///
@property(readonly, nonatomic) BOOL fcmEnabled;
///
/// Retrieve an initialized singleton configurator object using the default file path.
///

View File

@@ -12,20 +12,18 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTConfigurator.h"
#import "Source/common/SNTConfigurator.h"
#include <sys/stat.h>
#import "SNTFileWatcher.h"
#import "SNTLogging.h"
#import "SNTStrengthify.h"
#import "SNTSystemInfo.h"
#import "Source/common/SNTStrengthify.h"
#import "Source/common/SNTSystemInfo.h"
@interface SNTConfigurator ()
/// A NSUserDefaults object set to use the com.google.santa suite.
@property(readonly, nonatomic) NSUserDefaults *defaults;
// Keys and expected value types.
/// Keys and expected value types.
@property(readonly, nonatomic) NSDictionary *syncServerKeyTypes;
@property(readonly, nonatomic) NSDictionary *forcedConfigKeyTypes;
@@ -33,8 +31,8 @@
@property NSMutableDictionary *syncState;
@property NSMutableDictionary *configState;
/// Watcher for the sync-state.plist.
@property(nonatomic) SNTFileWatcher *syncStateWatcher;
/// Was --debug passed as an argument to this process?
@property(readonly, nonatomic) BOOL debugFlag;
@end
@implementation SNTConfigurator
@@ -70,13 +68,37 @@ static NSString *const kModeNotificationMonitor = @"ModeNotificationMonitor";
static NSString *const kModeNotificationLockdown = @"ModeNotificationLockdown";
static NSString *const kEnablePageZeroProtectionKey = @"EnablePageZeroProtection";
static NSString *const kEnableBadSignatureProtectionKey = @"EnableBadSignatureProtection";
static NSString *const kFileChangesRegexKey = @"FileChangesRegex";
static NSString *const kFileChangesPrefixFiltersKey = @"FileChangesPrefixFilters";
static NSString *const kEventLogType = @"EventLogType";
static NSString *const kEventLogPath = @"EventLogPath";
static NSString *const kEnableMachineIDDecoration = @"EnableMachineIDDecoration";
static NSString *const kEnableSystemExtension = @"EnableSystemExtension";
static NSString *const kEnableSysxCache = @"EnableSysxCache";
static NSString *const kEnableForkAndExitLogging = @"EnableForkAndExitLogging";
static NSString *const kIgnoreOtherEndpointSecurityClients = @"IgnoreOtherEndpointSecurityClients";
static NSString *const kEnableDebugLogging = @"EnableDebugLogging";
static NSString *const kEnableBackwardsCompatibleContentEncoding = @"EnableBackwardsCompatibleContentEncoding";
static NSString *const kFCMProject = @"FCMProject";
static NSString *const kFCMEntity = @"FCMEntity";
static NSString *const kFCMAPIKey = @"FCMAPIKey";
// The keys managed by a sync server or mobileconfig.
static NSString *const kClientModeKey = @"ClientMode";
static NSString *const kWhitelistRegexKey = @"WhitelistRegex";
static NSString *const kBlacklistRegexKey = @"BlacklistRegex";
static NSString *const kEnableTransitiveRulesKey = @"EnableTransitiveRules";
static NSString *const kEnableTransitiveRulesKeyDeprecated = @"EnableTransitiveWhitelisting";
static NSString *const kAllowedPathRegexKey = @"AllowedPathRegex";
static NSString *const kAllowedPathRegexKeyDeprecated = @"WhitelistRegex";
static NSString *const kBlockedPathRegexKey = @"BlockedPathRegex";
static NSString *const kBlockedPathRegexKeyDeprecated = @"BlacklistRegex";
// The keys managed by a sync server.
static NSString *const kFullSyncLastSuccess = @"FullSyncLastSuccess";
@@ -86,45 +108,74 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
- (instancetype)init {
self = [super init];
if (self) {
Class number = [NSNumber class];
Class re = [NSRegularExpression class];
Class date = [NSDate class];
Class string = [NSString class];
Class data = [NSData class];
Class array = [NSArray class];
_syncServerKeyTypes = @{
kClientModeKey : [NSNumber class],
kWhitelistRegexKey : [NSRegularExpression class],
kBlacklistRegexKey : [NSRegularExpression class],
kFullSyncLastSuccess : [NSDate class],
kRuleSyncLastSuccess : [NSDate class],
kSyncCleanRequired : [NSNumber class]
kClientModeKey : number,
kEnableTransitiveRulesKey : number,
kEnableTransitiveRulesKeyDeprecated : number,
kAllowedPathRegexKey : re,
kAllowedPathRegexKeyDeprecated : re,
kBlockedPathRegexKey : re,
kBlockedPathRegexKeyDeprecated : re,
kFullSyncLastSuccess : date,
kRuleSyncLastSuccess : date,
kSyncCleanRequired : number
};
_forcedConfigKeyTypes = @{
kClientModeKey : [NSNumber class],
kFileChangesRegexKey : [NSRegularExpression class],
kWhitelistRegexKey : [NSRegularExpression class],
kBlacklistRegexKey : [NSRegularExpression class],
kEnablePageZeroProtectionKey : [NSNumber class],
kMoreInfoURLKey : [NSString class],
kEventDetailURLKey : [NSString class],
kEventDetailTextKey : [NSString class],
kUnknownBlockMessage : [NSString class],
kBannedBlockMessage : [NSString class],
kModeNotificationMonitor : [NSString class],
kModeNotificationLockdown : [NSString class],
kSyncBaseURLKey : [NSString class],
kClientAuthCertificateFileKey : [NSString class],
kClientAuthCertificatePasswordKey : [NSString class],
kClientAuthCertificateCNKey : [NSString class],
kClientAuthCertificateIssuerKey : [NSString class],
kServerAuthRootsDataKey : [NSData class],
kServerAuthRootsFileKey : [NSString class],
kMachineOwnerKey : [NSString class],
kMachineIDKey : [NSString class],
kMachineOwnerPlistFileKey : [NSString class],
kMachineOwnerPlistKeyKey : [NSString class],
kMachineIDPlistFileKey : [NSString class],
kMachineIDPlistKeyKey : [NSString class],
kClientModeKey : number,
kEnableTransitiveRulesKey : number,
kEnableTransitiveRulesKeyDeprecated : number,
kFileChangesRegexKey : re,
kFileChangesPrefixFiltersKey : array,
kAllowedPathRegexKey : re,
kAllowedPathRegexKeyDeprecated : re,
kBlockedPathRegexKey : re,
kBlockedPathRegexKeyDeprecated : re,
kEnablePageZeroProtectionKey : number,
kEnableBadSignatureProtectionKey : number,
kMoreInfoURLKey : string,
kEventDetailURLKey : string,
kEventDetailTextKey : string,
kUnknownBlockMessage : string,
kBannedBlockMessage : string,
kModeNotificationMonitor : string,
kModeNotificationLockdown : string,
kSyncBaseURLKey : string,
kClientAuthCertificateFileKey : string,
kClientAuthCertificatePasswordKey : string,
kClientAuthCertificateCNKey : string,
kClientAuthCertificateIssuerKey : string,
kServerAuthRootsDataKey : data,
kServerAuthRootsFileKey : string,
kMachineOwnerKey : string,
kMachineIDKey : string,
kMachineOwnerPlistFileKey : string,
kMachineOwnerPlistKeyKey : string,
kMachineIDPlistFileKey : string,
kMachineIDPlistKeyKey : string,
kEventLogType : string,
kEventLogPath : string,
kEnableMachineIDDecoration : number,
kEnableSystemExtension : number,
kEnableSysxCache : number,
kEnableForkAndExitLogging : number,
kIgnoreOtherEndpointSecurityClients : number,
kEnableDebugLogging : number,
kEnableBackwardsCompatibleContentEncoding : number,
kFCMProject : string,
kFCMEntity : string,
kFCMAPIKey : string,
};
_defaults = [NSUserDefaults standardUserDefaults];
[_defaults addSuiteNamed:@"com.google.santa"];
_configState = [self readForcedConfig];
_syncState = [self readSyncStateFromDisk] ?: [NSMutableDictionary dictionary];
_debugFlag = [[NSProcessInfo processInfo].arguments containsObject:@"--debug"];
[self startWatchingDefaults];
}
return self;
@@ -141,94 +192,189 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return sharedConfigurator;
}
+ (NSSet *)syncAndConfigStateSet {
static NSSet *set;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
set = [[self syncStateSet] setByAddingObjectsFromSet:[self configStateSet]];
});
return set;
}
+ (NSSet *)syncStateSet {
static NSSet *set;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
set = [NSSet setWithObject:NSStringFromSelector(@selector(syncState))];
});
return set;
}
+ (NSSet *)configStateSet {
static NSSet *set;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
set = [NSSet setWithObject:NSStringFromSelector(@selector(configState))];
});
return set;
}
#pragma mark KVO Dependencies
+ (NSSet *)keyPathsForValuesAffectingClientMode {
return [NSSet setWithObjects:@"syncState", @"configState", nil];
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingWhitelistPathRegex {
return [NSSet setWithObjects:@"syncState", @"configState", nil];
+ (NSSet *)keyPathsForValuesAffectingAllowlistPathRegex {
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingBlacklistPathRegex {
return [NSSet setWithObjects:@"syncState", @"configState", nil];
+ (NSSet *)keyPathsForValuesAffectingBlocklistPathRegex {
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingFileChangesRegex {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingFileChangesPrefixFiltersKey {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingSyncBaseURL {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnablePageZeroProtection {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingMoreInfoURL {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEventDetailURL {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEventDetailText {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingUnknownBlockMessage {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingBannedBlockMessage {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingModeNotificationMonitor {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingModeNotificationLockdown {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingSyncClientAuthCertificateFile {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingSyncClientAuthCertificatePassword {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingSyncClientAuthCertificateCn {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingSyncClientAuthCertificateIssuer {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingSyncServerAuthRootsData {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingSyncServerAuthRootsFile {
return [NSSet setWithObject:@"configState"];
}
+ (NSSet *)keyPathsForValuesAffectingFullSyncLastSuccess {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingMachineOwner {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingMachineID {
return [NSSet setWithObject:@"configState"];
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingFullSyncLastSuccess {
return [self syncStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingRuleSyncLastSuccess {
return [self syncStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingSyncCleanRequired {
return [self syncStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEventLogType {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEventLogPath {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableMachineIDDecoration {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableTransitiveRules {
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableSystemExtension {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableSysxCache {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableForkAndExitLogging {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingIgnoreOtherEndpointSecurityClients {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableDebugLogging {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableBackwardsCompatibleContentEncoding {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingFcmProject {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingFcmEntity {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingFcmAPIKey {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingFcmEnabled {
return [self configStateSet];
}
#pragma mark Public Interface
@@ -250,35 +396,78 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
- (void)setSyncServerClientMode:(SNTClientMode)newMode {
if (newMode == SNTClientModeMonitor || newMode == SNTClientModeLockdown) {
[self updateSyncStateForKey:kClientModeKey value:@(newMode)];
} else {
LOGW(@"Ignoring request to change client mode to %ld", newMode);
}
}
- (NSRegularExpression *)whitelistPathRegex {
return self.syncState[kWhitelistRegexKey] ?: self.configState[kWhitelistRegexKey];
- (BOOL)enableTransitiveRules {
NSNumber *n = self.syncState[kEnableTransitiveRulesKey];
if (n) return [n boolValue];
n = self.syncState[kEnableTransitiveRulesKeyDeprecated];
if (n) return [n boolValue];
n = self.configState[kEnableTransitiveRulesKeyDeprecated];
if (n) return [n boolValue];
return [self.configState[kEnableTransitiveRulesKey] boolValue];
}
- (void)setSyncServerWhitelistPathRegex:(NSRegularExpression *)re {
[self updateSyncStateForKey:kWhitelistRegexKey value:re];
- (void)setEnableTransitiveRules:(BOOL)enabled {
[self updateSyncStateForKey:kEnableTransitiveRulesKey value:@(enabled)];
}
- (NSRegularExpression *)blacklistPathRegex {
return self.syncState[kBlacklistRegexKey] ?: self.configState[kBlacklistRegexKey];
- (NSRegularExpression *)allowedPathRegex {
NSRegularExpression *r = self.syncState[kAllowedPathRegexKey];
if (r) return r;
r = self.syncState[kAllowedPathRegexKeyDeprecated];
if (r) return r;
r = self.configState[kAllowedPathRegexKey];
if (r) return r;
return self.configState[kAllowedPathRegexKeyDeprecated];
}
- (void)setSyncServerBlacklistPathRegex:(NSRegularExpression *)re {
[self updateSyncStateForKey:kBlacklistRegexKey value:re];
- (void)setSyncServerAllowedPathRegex:(NSRegularExpression *)re {
[self updateSyncStateForKey:kAllowedPathRegexKey value:re];
}
- (NSRegularExpression *)blockedPathRegex {
NSRegularExpression *r = self.syncState[kBlockedPathRegexKey];
if (r) return r;
r = self.syncState[kBlockedPathRegexKeyDeprecated];
if (r) return r;
r = self.configState[kBlockedPathRegexKey];
if (r) return r;
return self.configState[kBlockedPathRegexKeyDeprecated];
}
- (void)setSyncServerBlockedPathRegex:(NSRegularExpression *)re {
[self updateSyncStateForKey:kBlockedPathRegexKey value:re];
}
- (NSRegularExpression *)fileChangesRegex {
return self.configState[kFileChangesRegexKey];
}
- (NSArray *)fileChangesPrefixFilters {
NSArray *filters = self.configState[kFileChangesPrefixFiltersKey];
for (id filter in filters) {
if (![filter isKindOfClass:[NSString class]]) {
return nil;
}
}
return filters;
}
- (NSURL *)syncBaseURL {
NSString *urlString = self.configState[kSyncBaseURLKey];
if (![urlString hasSuffix:@"/"]) urlString = [urlString stringByAppendingString:@"/"];
NSURL *url = [NSURL URLWithString:urlString];
if (urlString && !url) LOGW(@"SyncBaseURL is not a valid URL!");
return url;
}
@@ -287,6 +476,11 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return number ? [number boolValue] : YES;
}
- (BOOL)enableBadSignatureProtection {
NSNumber *number = self.configState[kEnableBadSignatureProtectionKey];
return number ? [number boolValue] : NO;
}
- (NSURL *)moreInfoURL {
return [NSURL URLWithString:self.configState[kMoreInfoURLKey]];
}
@@ -344,7 +538,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
}
- (void)setFullSyncLastSuccess:(NSDate *)fullSyncLastSuccess {
self.syncState[kFullSyncLastSuccess] = fullSyncLastSuccess;
[self updateSyncStateForKey:kFullSyncLastSuccess value:fullSyncLastSuccess];
self.ruleSyncLastSuccess = fullSyncLastSuccess;
}
@@ -353,8 +547,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
}
- (void)setRuleSyncLastSuccess:(NSDate *)ruleSyncLastSuccess {
self.syncState[kRuleSyncLastSuccess] = ruleSyncLastSuccess;
[self saveSyncStateToDisk];
[self updateSyncStateForKey:kRuleSyncLastSuccess value:ruleSyncLastSuccess];
}
- (BOOL)syncCleanRequired {
@@ -362,8 +555,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
}
- (void)setSyncCleanRequired:(BOOL)syncCleanRequired {
self.syncState[kSyncCleanRequired] = @(syncCleanRequired);
[self saveSyncStateToDisk];
[self updateSyncStateForKey:kSyncCleanRequired value:@(syncCleanRequired)];
}
- (NSString *)machineOwner {
@@ -395,17 +587,84 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return machineId.length ? machineId : [SNTSystemInfo hardwareUUID];
}
- (SNTEventLogType)eventLogType {
NSString *s = [self.configState[kEventLogType] lowercaseString];
return [s isEqualToString:@"syslog"] ? SNTEventLogTypeSyslog : SNTEventLogTypeFilelog;
}
- (NSString *)eventLogPath {
return self.configState[kEventLogPath] ?: @"/var/db/santa/santa.log";
}
- (BOOL)enableMachineIDDecoration {
NSNumber *number = self.configState[kEnableMachineIDDecoration];
return number ? [number boolValue] : NO;
}
- (BOOL)enableSystemExtension {
if (@available(macOS 10.15, *)) {
NSFileManager *fm = [NSFileManager defaultManager];
if (![fm fileExistsAtPath:@"/Library/Extensions/santa-driver.kext"]) return YES;
NSNumber *number = self.configState[kEnableSystemExtension];
return number ? [number boolValue] : YES;
} else {
return NO;
}
}
- (BOOL)enableSysxCache {
NSNumber *number = self.configState[kEnableSysxCache];
return number ? [number boolValue] : NO;
}
- (BOOL)enableForkAndExitLogging {
NSNumber *number = self.configState[kEnableForkAndExitLogging];
return number ? [number boolValue] : NO;
}
- (BOOL)ignoreOtherEndpointSecurityClients {
NSNumber *number = self.configState[kIgnoreOtherEndpointSecurityClients];
return number ? [number boolValue] : NO;
}
- (BOOL)enableDebugLogging {
NSNumber *number = self.configState[kEnableDebugLogging];
return [number boolValue] || self.debugFlag;
}
- (BOOL)enableBackwardsCompatibleContentEncoding {
NSNumber *number = self.configState[kEnableBackwardsCompatibleContentEncoding];
return number ? [number boolValue] : NO;
}
- (NSString *)fcmProject {
return self.configState[kFCMProject];
}
- (NSString *)fcmEntity {
return self.configState[kFCMEntity];
}
- (NSString *)fcmAPIKey {
return self.configState[kFCMAPIKey];
}
- (BOOL)fcmEnabled {
return (self.fcmProject.length && self.fcmEntity.length && self.fcmAPIKey.length);
}
#pragma mark Private
///
/// Update the syncState. Triggers a KVO event for all dependents.
///
- (BOOL)updateSyncStateForKey:(NSString *)key value:(id)value {
NSMutableDictionary *syncState = self.syncState.mutableCopy;
syncState[key] = value;
self.syncState = syncState;
[self saveSyncStateToDisk];
return YES;
- (void)updateSyncStateForKey:(NSString *)key value:(id)value {
dispatch_async(dispatch_get_main_queue(), ^{
NSMutableDictionary *syncState = self.syncState.mutableCopy;
syncState[key] = value;
self.syncState = syncState;
[self saveSyncStateToDisk];
});
}
///
@@ -427,15 +686,6 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
continue;
}
}
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
WEAKIFY(self);
self.syncStateWatcher = [[SNTFileWatcher alloc] initWithFilePath:kSyncStateFilePath
handler:^(unsigned long data) {
STRONGIFY(self);
[self syncStateFileChanged:data];
}];
});
return syncState;
}
@@ -449,46 +699,13 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
if (geteuid() != 0) return;
// Either remove
NSMutableDictionary *syncState = self.syncState.mutableCopy;
syncState[kWhitelistRegexKey] = [syncState[kWhitelistRegexKey] pattern];
syncState[kBlacklistRegexKey] = [syncState[kBlacklistRegexKey] pattern];
syncState[kAllowedPathRegexKey] = [syncState[kAllowedPathRegexKey] pattern];
syncState[kBlockedPathRegexKey] = [syncState[kBlockedPathRegexKey] pattern];
[syncState writeToFile:kSyncStateFilePath atomically:YES];
[[NSFileManager defaultManager] setAttributes:@{ NSFilePosixPermissions : @0644 }
ofItemAtPath:kSyncStateFilePath error:NULL];
}
///
/// Ensure permissions are 0644.
/// Revert any out-of-band changes.
///
- (void)syncStateFileChanged:(unsigned long)data {
if (data & DISPATCH_VNODE_ATTRIB) {
const char *cPath = [kSyncStateFilePath fileSystemRepresentation];
struct stat fileStat;
stat(cPath, &fileStat);
int mask = S_IRWXU | S_IRWXG | S_IRWXO;
int desired = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
if (fileStat.st_uid != 0 || fileStat.st_gid != 0 || (fileStat.st_mode & mask) != desired) {
LOGI(@"Sync state file permissions changed, fixing.");
chown(cPath, 0, 0);
chmod(cPath, desired);
}
} else {
NSDictionary *newSyncState = [self readSyncStateFromDisk];
for (NSString *key in self.syncState) {
if (((self.syncState[key] && !newSyncState[key]) ||
(!self.syncState[key] && newSyncState[key]) ||
(self.syncState[key] && ![self.syncState[key] isEqualTo:newSyncState[key]]))) {
// Ignore sync url and dates
if ([key isEqualToString:kRuleSyncLastSuccess] ||
[key isEqualToString:kFullSyncLastSuccess]) continue;
LOGE(@"Sync state file changed, replacing");
[self saveSyncStateToDisk];
return;
}
}
}
}
- (void)clearSyncState {
self.syncState = [NSMutableDictionary dictionary];
}
@@ -521,8 +738,9 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
}
- (void)startWatchingDefaults {
// Only santad should listen.
if (geteuid() != 0) return;
// Only com.google.santa.daemon should listen.
NSString *processName = [[NSProcessInfo processInfo] processName];
if (![processName isEqualToString:@"com.google.santa.daemon"]) return;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(defaultsChanged:)
name:NSUserDefaultsDidChangeNotification

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@import Foundation;
#import <Foundation/Foundation.h>
///
/// Simple function to check and drop root privileges.

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTDropRootPrivs.h"
#import "Source/common/SNTDropRootPrivs.h"
BOOL DropRootPrivileges() {
if (getuid() == 0 || geteuid() == 0 || getgid() == 0 || getegid() == 0) {

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@import Foundation;
#import <Foundation/Foundation.h>
@class MOLCodesignChecker;
@@ -212,6 +212,11 @@
///
- (NSUInteger)fileSize;
///
/// @return The underlying file handle.
///
@property(readonly) NSFileHandle *fileHandle;
///
/// @return Returns an instance of MOLCodeSignChecker initialized with the file's binary path.
/// Both the MOLCodesignChecker and any resulting NSError are cached and returned on subsequent

View File

@@ -12,18 +12,19 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTFileInfo.h"
#import "Source/common/SNTFileInfo.h"
#import <CommonCrypto/CommonDigest.h>
#import <fmdb/FMDB.h>
#import <MOLCodesignChecker/MOLCodesignChecker.h>
#include <mach-o/arch.h>
#include <mach-o/loader.h>
#include <mach-o/swap.h>
#include <pwd.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#import <FMDB/FMDB.h>
// Simple class to hold the data of a mach_header and the offset within the file
// in which that header was found.
@@ -142,67 +143,71 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
- (void)hashSHA1:(NSString **)sha1 SHA256:(NSString **)sha256 {
const int MAX_CHUNK_SIZE = 256 * 1024; // 256 KB
const size_t chunkSize = _fileSize > MAX_CHUNK_SIZE ? MAX_CHUNK_SIZE : _fileSize;
char chunk[chunkSize];
char *chunk = malloc(chunkSize);
CC_SHA1_CTX c1;
CC_SHA256_CTX c256;
@try {
CC_SHA1_CTX c1;
CC_SHA256_CTX c256;
if (sha1) CC_SHA1_Init(&c1);
if (sha256) CC_SHA256_Init(&c256);
if (sha1) CC_SHA1_Init(&c1);
if (sha256) CC_SHA256_Init(&c256);
int fd = self.fileHandle.fileDescriptor;
int fd = self.fileHandle.fileDescriptor;
fcntl(fd, F_RDAHEAD, 1);
struct radvisory radv;
radv.ra_offset = 0;
const int MAX_ADVISORY_READ = 10 * 1024 * 1024;
radv.ra_count = (int)_fileSize < MAX_ADVISORY_READ ? (int)_fileSize : MAX_ADVISORY_READ;
fcntl(fd, F_RDADVISE, &radv);
ssize_t bytesRead;
fcntl(fd, F_RDAHEAD, 1);
struct radvisory radv;
radv.ra_offset = 0;
const int MAX_ADVISORY_READ = 10 * 1024 * 1024;
radv.ra_count = (int)_fileSize < MAX_ADVISORY_READ ? (int)_fileSize : MAX_ADVISORY_READ;
fcntl(fd, F_RDADVISE, &radv);
ssize_t bytesRead;
for (uint64_t offset = 0; offset < _fileSize;) {
bytesRead = pread(fd, chunk, chunkSize, offset);
if (bytesRead > 0) {
if (sha1) CC_SHA1_Update(&c1, chunk, (CC_LONG)bytesRead);
if (sha256) CC_SHA256_Update(&c256, chunk, (CC_LONG)bytesRead);
offset += bytesRead;
} else if (bytesRead == -1 && errno == EINTR) {
continue;
} else {
return;
for (uint64_t offset = 0; offset < _fileSize;) {
bytesRead = pread(fd, chunk, chunkSize, offset);
if (bytesRead > 0) {
if (sha1) CC_SHA1_Update(&c1, chunk, (CC_LONG)bytesRead);
if (sha256) CC_SHA256_Update(&c256, chunk, (CC_LONG)bytesRead);
offset += bytesRead;
} else if (bytesRead == -1 && errno == EINTR) {
continue;
} else {
return;
}
}
}
// We turn off Read Ahead that we turned on
fcntl(fd, F_RDAHEAD, 0);
if (sha1) {
unsigned char digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1_Final(digest, &c1);
NSString *const SHA1FormatString =
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x";
*sha1 = [[NSString alloc]
initWithFormat:SHA1FormatString, digest[0], digest[1], digest[2],
digest[3], digest[4], digest[5], digest[6], digest[7],
digest[8], digest[9], digest[10], digest[11], digest[12],
digest[13], digest[14], digest[15], digest[16],
digest[17], digest[18], digest[19]];
}
if (sha256) {
unsigned char digest[CC_SHA256_DIGEST_LENGTH];
CC_SHA256_Final(digest, &c256);
NSString *const SHA256FormatString =
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x";
// We turn off Read Ahead that we turned on
fcntl(fd, F_RDAHEAD, 0);
if (sha1) {
unsigned char digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1_Final(digest, &c1);
NSString *const SHA1FormatString =
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x";
*sha1 = [[NSString alloc]
initWithFormat:SHA1FormatString, digest[0], digest[1], digest[2],
digest[3], digest[4], digest[5], digest[6], digest[7],
digest[8], digest[9], digest[10], digest[11], digest[12],
digest[13], digest[14], digest[15], digest[16],
digest[17], digest[18], digest[19]];
}
if (sha256) {
unsigned char digest[CC_SHA256_DIGEST_LENGTH];
CC_SHA256_Final(digest, &c256);
NSString *const SHA256FormatString =
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x";
*sha256 = [[NSString alloc]
initWithFormat:SHA256FormatString, digest[0], digest[1], digest[2],
digest[3], digest[4], digest[5], digest[6], digest[7],
digest[8], digest[9], digest[10], digest[11], digest[12],
digest[13], digest[14], digest[15], digest[16],
digest[17], digest[18], digest[19], digest[20],
digest[21], digest[22], digest[23], digest[24],
digest[25], digest[26], digest[27], digest[28],
digest[29], digest[30], digest[31]];
*sha256 = [[NSString alloc]
initWithFormat:SHA256FormatString, digest[0], digest[1], digest[2],
digest[3], digest[4], digest[5], digest[6], digest[7],
digest[8], digest[9], digest[10], digest[11], digest[12],
digest[13], digest[14], digest[15], digest[16],
digest[17], digest[18], digest[19], digest[20],
digest[21], digest[22], digest[23], digest[24],
digest[25], digest[26], digest[27], digest[28],
digest[29], digest[30], digest[31]];
}
} @finally {
free(chunk);
}
}
@@ -287,7 +292,8 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
- (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[[self nameForCPUType:CPU_TYPE_X86]];
MachHeaderWithOffset *x86Header = self.machHeaders[[self nameForCPUType:CPU_TYPE_X86
cpuSubType:CPU_SUBTYPE_I386_ALL]];
if (!x86Header) return NO;
struct mach_header *mh = (struct mach_header *)[x86Header.data bytes];
@@ -315,6 +321,26 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
#pragma mark Bundle Information
///
/// Directories with a "Contents/Info.plist" entry can be mistaken as a bundle. To be considered an
/// ancestor, the bundle must have a valid extension.
///
- (NSSet *)allowedAncestorExtensions {
static NSSet *set;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
set = [NSSet setWithArray:@[
@"app",
@"bundle",
@"framework",
@"kext",
@"xctest",
@"xpc",
]];
});
return set;
}
///
/// Try and determine the bundle that the represented executable is contained within, if any.
///
@@ -325,18 +351,21 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
///
/// This method walks up the path until a bundle is found, if any.
///
/// @param ancestor YES this will return the highest NSBundle found in the tree. No will return the
/// the lowest.
/// @param ancestor YES this will return the highest NSBundle, with a valid extension, found in the
/// tree. NO will return the the lowest NSBundle, without validating the extension.
///
-(NSBundle *)findBundleWithAncestor:(BOOL)ancestor {
- (NSBundle *)findBundleWithAncestor:(BOOL)ancestor {
NSBundle *bundle;
NSMutableArray *pathComponents = [[self.path pathComponents] mutableCopy];
// Ignore the root path "/", for some reason this is considered a bundle.
while (pathComponents.count > 1) {
NSBundle *bndl = [NSBundle bundleWithPath:[NSString pathWithComponents:pathComponents]];
if (bndl && [bndl objectForInfoDictionaryKey:@"CFBundleIdentifier"]) {
bundle = bndl;
if ([bndl objectForInfoDictionaryKey:@"CFBundleIdentifier"]) {
if ((!ancestor && bndl.bundlePath.pathExtension.length) ||
[[self allowedAncestorExtensions] containsObject:bndl.bundlePath.pathExtension]) {
bundle = bndl;
}
if (!ancestor) break;
}
[pathComponents removeLastObject];
@@ -443,7 +472,7 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
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;
machHeaders[[self nameForCPUType:mh->cputype cpuSubType:mh->cpusubtype]] = mhwo;
} else {
NSRange range = NSMakeRange(0, sizeof(struct fat_header));
NSData *fatHeader = [self safeSubdataWithRange:range];
@@ -459,11 +488,12 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
int offset = OSSwapBigToHostInt32(fat_arch[i].offset);
int size = OSSwapBigToHostInt32(fat_arch[i].size);
int cputype = OSSwapBigToHostInt(fat_arch[i].cputype);
int cpusubtype = OSSwapBigToHostInt(fat_arch[i].cpusubtype);
range = NSMakeRange(offset, size);
NSData *machHeader = [self parseSingleMachHeader:[self safeSubdataWithRange:range]];
if (machHeader) {
NSString *key = [self nameForCPUType:cputype];
NSString *key = [self nameForCPUType:cputype cpuSubType:cpusubtype];
MachHeaderWithOffset *mhwo = [[MachHeaderWithOffset alloc] initWithData:machHeader
offset:offset];
machHeaders[key] = mhwo;
@@ -521,24 +551,51 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
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 (memcmp(lc->segname, "__TEXT", 6) == 0) {
if (is64) {
struct segment_command_64 *lc = (struct segment_command_64 *)[cmdData bytes];
if (lc->cmd == LC_SEGMENT_64 && memcmp(lc->segname, "__TEXT", 6) == 0) {
nsects = lc->nsects;
offset += sz_segment;
break;
}
offset += lc->cmdsize;
} else {
struct segment_command *lc = (struct segment_command *)[cmdData bytes];
if (lc->cmd == LC_SEGMENT && memcmp(lc->segname, "__TEXT", 6) == 0) {
nsects = lc->nsects;
offset += sz_segment;
break;
}
offset += lc->cmdsize;
}
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 (sect && memcmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
NSData *plistData = [self safeSubdataWithRange:NSMakeRange(sect->offset, sect->size)];
uint64_t sectoffset, sectsize = 0;
BOOL found = NO;
if (is64) {
struct section_64 *sect = (struct section_64 *)[sectData bytes];
if (sect && memcmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
sectoffset = sect->offset;
sectsize = sect->size;
found = YES;
}
} else {
struct section *sect = (struct section *)[sectData bytes];
if (sect && memcmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
sectoffset = sect->offset;
sectsize = sect->size;
found = YES;
}
}
if (found) {
NSData *plistData = [self safeSubdataWithRange:NSMakeRange(mhwo.offset + sectoffset,
sectsize)];
if (!plistData) return nil;
NSDictionary *plist;
plist = [NSPropertyListSerialization propertyListWithData:plistData
@@ -647,20 +704,15 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
///
/// Return a human-readable string for a cpu_type_t.
///
- (NSString *)nameForCPUType:(cpu_type_t)cpuType {
switch (cpuType) {
case CPU_TYPE_X86:
return @"i386";
case CPU_TYPE_X86_64:
return @"x86-64";
case CPU_TYPE_POWERPC:
return @"ppc";
case CPU_TYPE_POWERPC64:
return @"ppc64";
default:
return @"unknown";
- (NSString *)nameForCPUType:(cpu_type_t)cpuType cpuSubType:(cpu_subtype_t)cpuSubType {
const NXArchInfo *archInfo = NXGetArchInfoFromCpuType(cpuType, cpuSubType);
NSString *arch;
if (archInfo && archInfo->name) {
arch = @(archInfo->name);
} else {
arch = [NSString stringWithFormat:@"%i:%i", cpuType, cpuSubType];
}
return nil;
return arch;
}
///

View File

@@ -12,15 +12,25 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@import XCTest;
#import <XCTest/XCTest.h>
#import "SNTFileInfo.h"
#import "Source/common/SNTFileInfo.h"
@interface SNTFileInfoTest : XCTestCase
@end
@implementation SNTFileInfoTest
- (NSString *)directoryBundle {
NSString *rp = [[NSBundle bundleForClass:[self class]] resourcePath];
return [rp stringByAppendingPathComponent:@"testdata/DirectoryBundle"];
}
- (NSString *)bundleExample {
NSString *rp = [[NSBundle bundleForClass:[self class]] resourcePath];
return [rp stringByAppendingPathComponent:@"testdata/BundleExample.app"];
}
- (void)testPathStandardizing {
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/Applications/Safari.app"];
XCTAssertNotNil(sut);
@@ -29,9 +39,8 @@
sut = [[SNTFileInfo alloc] initWithPath:@"../../../../../../../../../../../../../../../bin/ls"];
XCTAssertEqualObjects(sut.path, @"/bin/ls");
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/bin/qlmanage"];
XCTAssertEqualObjects(sut.path, @"/System/Library/Frameworks/QuickLook.framework/Versions/A/"
@"Resources/quicklookd.app/Contents/MacOS/qlmanage");
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/sbin/DirectoryService"];
XCTAssertEqualObjects(sut.path, @"/usr/libexec/dspluginhelperd");
}
- (void)testSHA1 {
@@ -62,7 +71,6 @@
XCTAssertTrue(sut.isExecutable);
XCTAssertFalse(sut.isDylib);
XCTAssertFalse(sut.isFat);
XCTAssertFalse(sut.isKext);
XCTAssertFalse(sut.isScript);
}
@@ -96,7 +104,7 @@
}
- (void)testDylibs {
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/usr/lib/libsqlite3.dylib"];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/usr/lib/system/libsystem_platform.dylib"];
XCTAssertTrue(sut.isMachO);
XCTAssertTrue(sut.isDylib);
@@ -120,8 +128,7 @@
}
- (void)testBundle {
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"BundleExample"
ofType:@"app"];
NSString *path = [self bundleExample];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
XCTAssertNotNil([sut bundle]);
@@ -134,19 +141,18 @@
}
- (void)testAncestorBundle {
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"BundleExample"
ofType:@"app"];
NSString *path = [self bundleExample];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
sut.useAncestorBundle = YES;
XCTAssertNotNil([sut bundle]);
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.LogicTests");
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.santa.UnitTest.SNTFileInfoTest");
XCTAssertNotNil([sut bundleVersion]);
XCTAssertNotNil([sut bundleShortVersionString]);
NSString *ancestorBundlePath = path;
for (int i = 0; i < 3; i++) {
for (int i = 0; i < 4; i++) {
ancestorBundlePath = [ancestorBundlePath stringByDeletingLastPathComponent];
}
XCTAssertEqualObjects([sut bundlePath], ancestorBundlePath);
@@ -159,15 +165,33 @@
XCTAssertNotNil([sut bundle]);
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.LogicTests");
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.santa.UnitTest.SNTFileInfoTest");
XCTAssertNotNil([sut bundleVersion]);
XCTAssertNotNil([sut bundleShortVersionString]);
XCTAssertEqualObjects([sut bundlePath], path);
}
- (void)testDirectoryBundleIsNotAncestor {
NSString *path = [self directoryBundle];
NSString *directoryBundle = @"/tmp/DirectoryBundle";
NSFileManager *fm = [NSFileManager defaultManager];
[fm removeItemAtPath:directoryBundle error:NULL];
[fm copyItemAtPath:path toPath:directoryBundle error:NULL];
path = [directoryBundle stringByAppendingString:@"/Contents/Resources/BundleExample.app"];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
sut.useAncestorBundle = YES;
XCTAssertNotNil([sut bundle]);
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.santa.BundleExample");
XCTAssertEqualObjects([sut bundleName], @"BundleExample");
XCTAssertEqualObjects([sut bundleVersion], @"1");
XCTAssertEqualObjects([sut bundleShortVersionString], @"1.0");
XCTAssertEqualObjects([sut bundlePath], path);
}
- (void)testBundleCacheReset {
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"BundleExample"
ofType:@"app"];
NSString *path = [self bundleExample];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
XCTAssertNotNil([sut bundle]);
@@ -182,12 +206,12 @@
XCTAssertNotNil([sut bundle]);
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.LogicTests");
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.santa.UnitTest.SNTFileInfoTest");
XCTAssertNotNil([sut bundleVersion]);
XCTAssertNotNil([sut bundleShortVersionString]);
NSString *ancestorBundlePath = path;
for (int i = 0; i < 3; i++) {
for (int i = 0; i < 4; i++) {
ancestorBundlePath = [ancestorBundlePath stringByDeletingLastPathComponent];
}
XCTAssertEqualObjects([sut bundlePath], ancestorBundlePath);
@@ -205,10 +229,16 @@
}
- (void)testEmbeddedInfoPlist {
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"32bitplist"
ofType:@""];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
XCTAssertNotNil([sut infoPlist]);
XCTAssertEqualObjects([sut infoPlist][@"CFBundleShortVersionString"], @"1.0");
XCTAssertEqualObjects([sut infoPlist][@"CFBundleIdentifier"], @"com.google.i386plist");
// csreq is installed on all machines with Xcode installed. If you're running these tests,
// it should be available..
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/usr/bin/csreq"];
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/bin/csreq"];
XCTAssertNotNil([sut infoPlist]);
}

View File

@@ -1,35 +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 Foundation;
///
/// Simple file watching class using dispatch sources. Will automatically
/// reload the watch if the file is deleted and continue watching for
/// events until deallocated.
///
@interface SNTFileWatcher : NSObject
///
/// Designated initializer
/// Initializes the watcher and begins watching for modifications.
///
/// @param filePath the file to watch.
/// @param handler the handler to call when changes happen. The argument to the block is the
/// type of change that happened as a bitmask to be compared with DISPATCH_VNODE_* constants.
///
- (nonnull instancetype)initWithFilePath:(nonnull NSString *)filePath
handler:(nonnull void (^)(unsigned long))handler;
@end

View File

@@ -1,94 +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 "SNTFileWatcher.h"
#import "SNTStrengthify.h"
@interface SNTFileWatcher ()
@property NSString *filePath;
@property(copy) void (^handler)(unsigned long);
@property dispatch_source_t source;
@end
@implementation SNTFileWatcher
- (instancetype)init {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (instancetype)initWithFilePath:(nonnull NSString *)filePath
handler:(nonnull void (^)(unsigned long))handler {
self = [super init];
if (self) {
_filePath = filePath;
_handler = handler;
[self startWatchingFile];
}
return self;
}
- (void)dealloc {
[self stopWatchingFile];
}
- (void)startWatchingFile {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
int mask = (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME |
DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_ATTRIB);
dispatch_async(queue, ^{
int fd = -1;
const char *filePath = [self.filePath fileSystemRepresentation];
while ((fd = open(filePath, O_EVTONLY | O_CLOEXEC)) < 0) {
usleep(200000); // wait 200ms
}
self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, mask, queue);
WEAKIFY(self);
dispatch_source_set_event_handler(self.source, ^{
STRONGIFY(self);
unsigned long data = dispatch_source_get_data(self.source);
self.handler(data);
if (data & DISPATCH_VNODE_DELETE || data & DISPATCH_VNODE_RENAME) {
[self stopWatchingFile];
[self startWatchingFile];
}
sleep(2);
});
dispatch_source_set_registration_handler(self.source, ^{
STRONGIFY(self);
self.handler(0);
});
dispatch_source_set_cancel_handler(self.source, ^{
close(fd);
});
dispatch_resume(self.source);
});
}
- (void)stopWatchingFile {
if (!self.source) return;
dispatch_source_set_event_handler_f(self.source, NULL);
dispatch_source_cancel(self.source);
self.source = nil;
}
@end

View File

@@ -16,13 +16,11 @@
/// 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_VNODE_ID_STR 21 // digits in UINT64_MAX + 1 for NULL-terminator
#include <stdint.h>
#include <sys/param.h>
// Defines the name of the userclient class and the driver bundle ID.
#define USERCLIENT_CLASS "com_google_SantaDriver"
@@ -36,11 +34,16 @@
enum SantaDriverMethods {
kSantaUserClientOpen,
kSantaUserClientAllowBinary,
kSantaUserClientAllowCompiler,
kSantaUserClientDenyBinary,
kSantaUserClientAcknowledgeBinary,
kSantaUserClientClearCache,
kSantaUserClientRemoveCacheEntry,
kSantaUserClientCacheCount,
kSantaUserClientCheckCache,
kSantaUserClientCacheBucketCount,
kSantaUserClientFilemodPrefixFilterAdd,
kSantaUserClientFilemodPrefixFilterReset,
// Any methods supported by the driver should be added above this line to
// ensure this remains the count of methods.
@@ -49,7 +52,7 @@ enum SantaDriverMethods {
typedef enum {
QUEUETYPE_DECISION,
QUEUETYPE_LOG
QUEUETYPE_LOG,
} santa_queuetype_t;
// Enum defining actions that can be passed down the IODataQueue and in
@@ -66,7 +69,11 @@ typedef enum {
ACTION_RESPOND_DENY = 21,
ACTION_RESPOND_TOOLONG = 22,
ACTION_RESPOND_ACK = 23,
ACTION_RESPOND_ALLOW_COMPILER = 24,
// The following response is stored only in the kernel decision cache.
// It is removed by SNTCompilerController
ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE = 25,
// NOTIFY
ACTION_NOTIFY_EXEC = 30,
ACTION_NOTIFY_WRITE = 31,
@@ -74,21 +81,45 @@ typedef enum {
ACTION_NOTIFY_LINK = 33,
ACTION_NOTIFY_EXCHANGE = 34,
ACTION_NOTIFY_DELETE = 35,
ACTION_NOTIFY_WHITELIST = 36,
ACTION_NOTIFY_FORK = 37,
ACTION_NOTIFY_EXIT = 38,
// ERROR
ACTION_ERROR = 99,
} santa_action_t;
#define RESPONSE_VALID(x) \
(x == ACTION_RESPOND_ALLOW || x == ACTION_RESPOND_DENY)
(x == ACTION_RESPOND_ALLOW || \
x == ACTION_RESPOND_DENY || \
x == ACTION_RESPOND_ALLOW_COMPILER || \
x == ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE)
// Struct to manage vnode IDs
typedef struct santa_vnode_id_t {
uint64_t fsid;
uint64_t fileid;
#ifdef __cplusplus
bool operator==(const santa_vnode_id_t& rhs) const {
return fsid == rhs.fsid && fileid == rhs.fileid;
}
// This _must not_ be used for anything security-sensitive. It exists solely to make
// the msleep/wakeup calls easier.
uint64_t unsafe_simple_id() const {
return (((uint64_t)fsid << 32) | fileid);
}
#endif
} santa_vnode_id_t;
// Message struct that is sent down the IODataQueue.
typedef struct {
santa_action_t action;
uint64_t vnode_id;
santa_vnode_id_t vnode_id;
uid_t uid;
gid_t gid;
pid_t pid;
int pidversion;
pid_t ppid;
char path[MAXPATHLEN];
char newpath[MAXPATHLEN];
@@ -97,6 +128,18 @@ typedef struct {
// While process names can technically be 4*MAXPATHLEN, that never
// actually happens, so only take MAXPATHLEN and throw away any excess.
char pname[MAXPATHLEN];
// For messages that originate from EndpointSecurity, this points to a copy of the message.
void *es_message;
// For messages that originate from EndpointSecurity, this points to an NSArray of the arguments.
void *args_array;
} santa_message_t;
// Used for the kSantaUserClientCacheBucketCount request.
typedef struct {
uint16_t per_bucket[1024];
uint64_t start;
} santa_bucket_count_t;
#endif // SANTA__COMMON__KERNELCOMMON_H

View File

@@ -34,7 +34,11 @@
#else // KERNEL
@import Foundation;
#ifdef __cplusplus
extern "C" {
#endif
#import <Foundation/Foundation.h>
typedef enum : NSUInteger {
LOG_LEVEL_ERROR,
@@ -60,6 +64,10 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...)
#define LOGW(logFormat, ...) logMessage(LOG_LEVEL_WARN, stderr, logFormat, ##__VA_ARGS__)
#define LOGE(logFormat, ...) logMessage(LOG_LEVEL_ERROR, stderr, logFormat, ##__VA_ARGS__)
#ifdef __cplusplus
} // extern C
#endif
#endif // KERNEL
#endif // SANTA__COMMON__LOGGING_H

View File

@@ -12,7 +12,9 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTLogging.h"
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTConfigurator.h"
#import <asl.h>
#import <pthread.h>
@@ -24,25 +26,28 @@ static LogLevel logLevel = LOG_LEVEL_INFO; // default to info
#endif
void syslogClientDestructor(void *arg) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
asl_close((aslclient)arg);
#pragma clang diagnostic pop
}
void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
static BOOL useSyslog = NO;
static const char *binaryName;
static NSString *binaryName;
static dispatch_once_t pred;
static pthread_key_t syslogKey = 0;
dispatch_once(&pred, ^{
binaryName = [[[NSProcessInfo processInfo] processName] UTF8String];
binaryName = [[NSProcessInfo processInfo] processName];
// If debug logging is enabled, the process must be restarted.
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--debug"]) {
if ([SNTConfigurator configurator].enableDebugLogging) {
logLevel = LOG_LEVEL_DEBUG;
}
// If requested, redirect output to syslog.
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--syslog"]) {
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--syslog"] ||
[binaryName isEqualToString:@"com.google.santa.daemon"]) {
useSyslog = YES;
pthread_key_create(&syslogKey, syslogClientDestructor);
}
@@ -58,8 +63,11 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
if (useSyslog) {
aslclient client = (aslclient)pthread_getspecific(syslogKey);
if (client == NULL) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
client = asl_open(NULL, "com.google.santa", 0);
asl_set_filter(client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
#pragma clang diagnostic pop
pthread_setspecific(syslogKey, client);
}
@@ -76,15 +84,21 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
break;
case LOG_LEVEL_INFO:
levelName = "I";
syslogLevel = ASL_LEVEL_INFO;
syslogLevel = ASL_LEVEL_NOTICE; // Maps to ULS Default
break;
case LOG_LEVEL_DEBUG:
levelName = "D";
syslogLevel = ASL_LEVEL_DEBUG;
// Log debug messages at the same ASL level as INFO.
// While it would make sense to use DEBUG, watching debug-level logs
// in Console means enabling all debug logs, which is absurdly noisy.
syslogLevel = ASL_LEVEL_NOTICE;
break;
}
asl_log(client, NULL, syslogLevel, "%s %s: %s", levelName, binaryName, [s UTF8String]);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
asl_log(client, NULL, syslogLevel, "%s %s: %s", levelName, binaryName.UTF8String, s.UTF8String);
#pragma clang diagnostic pop
} else {
[s appendString:@"\n"];
size_t len = [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding];

View File

@@ -0,0 +1,259 @@
/// Copyright 2018 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 "Source/common/SNTPrefixTree.h"
#ifdef KERNEL
#include <libkern/locks.h>
#include "Source/common/SNTLogging.h"
#else
#include <mutex>
#include <string.h>
#define LOGD(format, ...) // NOP
#define LOGE(format, ...) // NOP
#define lck_rw_lock_shared(l) pthread_rwlock_rdlock(&l)
#define lck_rw_unlock_shared(l) pthread_rwlock_unlock(&l)
#define lck_rw_lock_exclusive(l) pthread_rwlock_wrlock(&l)
#define lck_rw_unlock_exclusive(l) pthread_rwlock_unlock(&l)
#define lck_rw_lock_shared_to_exclusive(l) ({ pthread_rwlock_unlock(&l); false; })
#define lck_rw_lock_exclusive_to_shared(l) ({ pthread_rwlock_unlock(&l); pthread_rwlock_rdlock(&l); })
#define lck_mtx_lock(l) l->lock()
#define lck_mtx_unlock(l) l->unlock()
#endif // KERNEL
SNTPrefixTree::SNTPrefixTree(uint32_t max_nodes) {
root_ = new SantaPrefixNode();
node_count_ = 0;
max_nodes_ = max_nodes;
#ifdef KERNEL
spt_lock_grp_attr_ = lck_grp_attr_alloc_init();
spt_lock_grp_ = lck_grp_alloc_init("santa-prefix-tree-lock", spt_lock_grp_attr_);
spt_lock_attr_ = lck_attr_alloc_init();
spt_lock_ = lck_rw_alloc_init(spt_lock_grp_, spt_lock_attr_);
spt_add_lock_ = lck_mtx_alloc_init(spt_lock_grp_, spt_lock_attr_);
#else
pthread_rwlock_init(&spt_lock_, nullptr);
spt_add_lock_ = new std::mutex;
#endif
}
IOReturn SNTPrefixTree::AddPrefix(const char *prefix, uint64_t *node_count) {
// Serialize requests to AddPrefix. Otherwise one AddPrefix thread could overwrite whole
// branches of another. HasPrefix is still free to read the tree, until AddPrefix needs to
// modify it.
lck_mtx_lock(spt_add_lock_);
// Don't allow an empty prefix.
if (prefix[0] == '\0') return kIOReturnBadArgument;
LOGD("Trying to add prefix: %s", prefix);
// Enforce max tree depth.
size_t len = strnlen(prefix, max_nodes_);
// Grab a shared lock until a new branch is required.
lck_rw_lock_shared(spt_lock_);
SantaPrefixNode *node = root_;
for (int i = 0; i < len; ++i) {
// If there is a node in the path that is considered a prefix, stop adding.
// For our purposes we only care about the shortest path that matches.
if (node->isPrefix) break;
// Only process a byte at a time.
uint8_t value = prefix[i];
// Create the child if it does not exist.
if (!node->children[value]) {
// Upgrade the shared lock.
// If the upgrade fails, the shared lock is released.
if (!lck_rw_lock_shared_to_exclusive(spt_lock_)) {
// Grab a new exclusive lock.
lck_rw_lock_exclusive(spt_lock_);
}
// Is there enough room for the rest of the prefix?
if ((node_count_ + (len - i)) > max_nodes_) {
LOGE("Prefix tree is full, can not add: %s", prefix);
if (node_count) *node_count = node_count_;
lck_rw_unlock_exclusive(spt_lock_);
lck_mtx_unlock(spt_add_lock_);
return kIOReturnNoResources;
}
// Create the rest of the prefix.
while (i < len) {
value = prefix[i++];
SantaPrefixNode *new_node = new SantaPrefixNode();
node->children[value] = new_node;
++node_count_;
node = new_node;
}
// This is the end, mark the node as a prefix.
LOGD("Added prefix: %s", prefix);
node->isPrefix = true;
// Downgrade the exclusive lock
lck_rw_lock_exclusive_to_shared(spt_lock_);
} else if (i + 1 == len) {
// If the child does exist and it is the end...
// Set the new, higher prefix and prune the now dead nodes.
if (!lck_rw_lock_shared_to_exclusive(spt_lock_)) {
lck_rw_lock_exclusive(spt_lock_);
}
PruneNode(node->children[value]);
SantaPrefixNode *new_node = new SantaPrefixNode();
new_node->isPrefix = true;
node->children[value] = new_node;
++node_count_;
LOGD("Added prefix: %s", prefix);
lck_rw_lock_exclusive_to_shared(spt_lock_);
}
// Get ready for the next iteration.
node = node->children[value];
}
if (node_count) *node_count = node_count_;
lck_rw_unlock_shared(spt_lock_);
lck_mtx_unlock(spt_add_lock_);
return kIOReturnSuccess;
}
bool SNTPrefixTree::HasPrefix(const char *string) {
lck_rw_lock_shared(spt_lock_);
auto found = false;
SantaPrefixNode *node = root_;
// A well formed tree will always break this loop. Even if string doesn't terminate.
const char *p = string;
while (*p) {
// Only process a byte at a time.
node = node->children[(uint8_t)*p++];
// If it doesn't exist in the tree, no match.
if (!node) break;
// If it does exist, is it a prefix?
if (node->isPrefix) {
found = true;
break;
}
}
lck_rw_unlock_shared(spt_lock_);
return found;
}
void SNTPrefixTree::Reset() {
lck_rw_lock_exclusive(spt_lock_);
PruneNode(root_);
root_ = new SantaPrefixNode();
node_count_ = 0;
lck_rw_unlock_exclusive(spt_lock_);
}
void SNTPrefixTree::PruneNode(SantaPrefixNode *target) {
if (!target) return;
// For deep trees, a recursive approach will generate too many stack frames. Make a "stack"
// and walk the tree.
auto stack = new SantaPrefixNode *[node_count_ + 1];
if (!stack) {
LOGE("Unable to prune tree!");
return;
}
auto count = 0;
// Seed the "stack" with a starting node.
stack[count++] = target;
// Start at the target node and walk the tree to find and delete all the sub-nodes.
while (count) {
auto node = stack[--count];
for (int i = 0; i < 256; ++i) {
if (!node->children[i]) continue;
stack[count++] = node->children[i];
}
delete node;
--node_count_;
}
delete[] stack;
}
SNTPrefixTree::~SNTPrefixTree() {
lck_rw_lock_exclusive(spt_lock_);
PruneNode(root_);
root_ = nullptr;
lck_rw_unlock_exclusive(spt_lock_);
#ifdef KERNEL
if (spt_lock_) {
lck_rw_free(spt_lock_, spt_lock_grp_);
spt_lock_ = nullptr;
}
if (spt_add_lock_) {
lck_mtx_free(spt_add_lock_, spt_lock_grp_);
spt_add_lock_ = nullptr;
}
if (spt_lock_attr_) {
lck_attr_free(spt_lock_attr_);
spt_lock_attr_ = nullptr;
}
if (spt_lock_grp_) {
lck_grp_free(spt_lock_grp_);
spt_lock_grp_ = nullptr;
}
if (spt_lock_grp_attr_) {
lck_grp_attr_free(spt_lock_grp_attr_);
spt_lock_grp_attr_ = nullptr;
}
#else
pthread_rwlock_destroy(&spt_lock_);
#endif
}

View File

@@ -0,0 +1,100 @@
/// Copyright 2018 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.
#ifndef SANTA__SANTA_DRIVER__SANTAPREFIXTREE_H
#define SANTA__SANTA_DRIVER__SANTAPREFIXTREE_H
#include <IOKit/IOReturn.h>
#include <sys/param.h>
#ifdef KERNEL
#include <libkern/locks.h>
#else
// Support for unit testing.
#include <mutex>
#include <pthread.h>
#include <stdint.h>
#endif // KERNEL
///
/// SantaPrefixTree is a simple prefix tree implementation.
/// Operations are thread safe.
///
class SNTPrefixTree {
public:
// Add a prefix to the tree.
// Optionally pass node_count to get the number of nodes after the add.
IOReturn AddPrefix(const char *, uint64_t *node_count = nullptr);
// Check if the tree has a prefix for string.
bool HasPrefix(const char *string);
// Reset the tree.
void Reset();
SNTPrefixTree(uint32_t max_nodes = kDefaultMaxNodes);
~SNTPrefixTree();
private:
///
/// SantaPrefixNode is a wrapper class that represents one byte.
/// 1 node can represent a whole ASCII character.
/// For example a pointer to the 'A' node will be stored at children[0x41].
/// It takes 1-4 nodes to represent a UTF-8 encoded Unicode character.
///
/// The path for "/🤘" would look like this:
/// children[0x2f] -> children[0xf0] -> children[0x9f] -> children[0xa4] -> children[0x98]
///
/// The path for "/dev" is:
/// children[0x2f] -> children[0x64] -> children[0x65] -> children[0x76]
///
/// Lookups of children are O(1).
///
/// Having the nodes represented by a smaller width, such as a nibble (1/2 byte), would
/// drastically decrease the memory footprint but would double required dereferences.
///
/// TODO(bur): Potentially convert this into a full on radix tree.
///
class SantaPrefixNode {
public:
bool isPrefix;
SantaPrefixNode *children[256];
};
// PruneNode will remove the passed in node from the tree.
// The passed in node and all subnodes will be deleted.
// It is the caller's responsibility to reset the pointer to this node (held by the parent).
// If the tree is in use grab the exclusive lock.
void PruneNode(SantaPrefixNode *);
SantaPrefixNode *root_;
// Each node takes up ~2k, assuming MAXPATHLEN is 1024 max out at ~2MB.
static const uint32_t kDefaultMaxNodes = MAXPATHLEN;
uint32_t max_nodes_;
uint32_t node_count_;
#ifdef KERNEL
lck_grp_t *spt_lock_grp_;
lck_grp_attr_t *spt_lock_grp_attr_;
lck_attr_t *spt_lock_attr_;
lck_rw_t *spt_lock_;
lck_mtx_t *spt_add_lock_;
#else // KERNEL
pthread_rwlock_t spt_lock_;
std::mutex *spt_add_lock_;
#endif // KERNEL
};
#endif /* SANTA__SANTA_DRIVER__SANTAPREFIXTREE_H */

View File

@@ -0,0 +1,70 @@
/// Copyright 2018 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>
#include "Source/common/SNTPrefixTree.h"
@interface SNTPrefixTreeTest : XCTestCase
@end
@implementation SNTPrefixTreeTest
- (void)testAddAndHas {
auto t = SNTPrefixTree();
XCTAssertFalse(t.HasPrefix("/private/var/tmp/file1"));
t.AddPrefix("/private/var/tmp/");
XCTAssertTrue(t.HasPrefix("/private/var/tmp/file1"));
}
- (void)testReset {
auto t = SNTPrefixTree();
t.AddPrefix("/private/var/tmp/");
XCTAssertTrue(t.HasPrefix("/private/var/tmp/file1"));
t.Reset();
XCTAssertFalse(t.HasPrefix("/private/var/tmp/file1"));
}
- (void)testThreading {
uint32_t count = 4096;
auto t = new SNTPrefixTree(count * (uint32_t)[NSUUID UUID].UUIDString.length);
NSMutableArray *UUIDs = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count; ++i) {
[UUIDs addObject:[NSUUID UUID].UUIDString];
}
// Create a bunch of background noise.
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_apply(UINT64_MAX, dispatch_get_global_queue(0, 0), ^(size_t i) {
t->HasPrefix([UUIDs[i % count] UTF8String]);
});
});
// Fill up the tree.
dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t i) {
if (t->AddPrefix([UUIDs[i] UTF8String]) != kIOReturnSuccess) {
XCTFail();
}
});
// Make sure every leaf byte is found.
dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t i) {
if (!t->HasPrefix([UUIDs[i] UTF8String])) {
XCTFail();
}
});
}
@end

View File

@@ -12,9 +12,9 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@import Foundation;
#import <Foundation/Foundation.h>
#import "SNTCommonEnums.h"
#import "Source/common/SNTCommonEnums.h"
///
/// Represents a Rule.
@@ -41,12 +41,32 @@
///
@property(copy) NSString *customMsg;
///
/// The time when this rule was last retrieved from the rules database, if rule is transitive.
/// Stored as number of seconds since 00:00:00 UTC on 1 January 2001.
///
@property(readonly) NSUInteger timestamp;
///
/// Designated initializer.
///
- (instancetype)initWithShasum:(NSString *)shasum
state:(SNTRuleState)state
type:(SNTRuleType)type
customMsg:(NSString *)customMsg
timestamp:(NSUInteger)timestamp;
///
/// Initialize with a default timestamp: current time if rule state is transitive, 0 otherwise.
///
- (instancetype)initWithShasum:(NSString *)shasum
state:(SNTRuleState)state
type:(SNTRuleType)type
customMsg:(NSString *)customMsg;
///
/// Sets timestamp of rule to the current time.
///
- (void)resetTimestamp;
@end

View File

@@ -12,26 +12,51 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTRule.h"
#import "Source/common/SNTRule.h"
@interface SNTRule()
@property(readwrite) NSUInteger timestamp;
@end
@implementation SNTRule
- (instancetype)initWithShasum:(NSString *)shasum
state:(SNTRuleState)state
type:(SNTRuleType)type
customMsg:(NSString *)customMsg {
customMsg:(NSString *)customMsg
timestamp:(NSUInteger)timestamp {
self = [super init];
if (self) {
_shasum = shasum;
_state = state;
_type = type;
_customMsg = customMsg;
_timestamp = timestamp;
}
return self;
}
- (instancetype)initWithShasum:(NSString *)shasum
state:(SNTRuleState)state
type:(SNTRuleType)type
customMsg:(NSString *)customMsg {
self = [self initWithShasum:shasum
state:state
type:type
customMsg:customMsg
timestamp:0];
// Initialize timestamp to current time if rule is transitive.
if (self && state == SNTRuleStateAllowTransitive) {
[self resetTimestamp];
}
return self;
}
#pragma mark NSSecureCoding
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-literal-conversion"
#define ENCODE(obj, key) if (obj) [coder encodeObject:obj forKey:key]
#define DECODE(cls, key) [decoder decodeObjectOfClass:[cls class] forKey:key]
@@ -44,6 +69,7 @@
ENCODE(@(self.state), @"state");
ENCODE(@(self.type), @"type");
ENCODE(self.customMsg, @"custommsg");
ENCODE(@(self.timestamp), @"timestamp");
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
@@ -53,12 +79,14 @@
_state = [DECODE(NSNumber, @"state") intValue];
_type = [DECODE(NSNumber, @"type") intValue];
_customMsg = DECODE(NSString, @"custommsg");
_timestamp = [DECODE(NSNumber, @"timestamp") unsignedIntegerValue];
}
return self;
}
#undef DECODE
#undef ENCODE
#pragma clang diagnostic pop
- (BOOL)isEqual:(id)other {
if (other == self) return YES;
@@ -77,8 +105,14 @@
}
- (NSString *)description {
return [NSString stringWithFormat:@"SNTRule: SHA-256: %@, State: %ld, Type: %ld",
self.shasum, self.state, self.type];
return [NSString stringWithFormat:@"SNTRule: SHA-256: %@, State: %ld, Type: %ld, Timestamp: %lu",
self.shasum, self.state, self.type, (unsigned long)self.timestamp];
}
# pragma mark Last-access Timestamp
- (void)resetTimestamp {
self.timestamp = (NSUInteger)[[NSDate date] timeIntervalSinceReferenceDate];
}
@end

View File

@@ -12,9 +12,9 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@import Foundation;
#import <Foundation/Foundation.h>
#import "SNTCommonEnums.h"
#import "Source/common/SNTCommonEnums.h"
///
/// Represents an event stored in the database.

View File

@@ -12,12 +12,15 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTStoredEvent.h"
#import "Source/common/SNTStoredEvent.h"
#import "MOLCertificate.h"
#import <MOLCertificate/MOLCertificate.h>
@implementation SNTStoredEvent
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-literal-conversion"
#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) \
@@ -129,4 +132,6 @@
[NSString stringWithFormat:@"SNTStoredEvent[%@] with SHA-256: %@", self.idx, self.fileSHA256];
}
#pragma clang diagnostic pop
@end

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@import Foundation;
#import <Foundation/Foundation.h>
///
/// Simple class for fetching system information

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTSystemInfo.h"
#import "Source/common/SNTSystemInfo.h"
@implementation SNTSystemInfo

View File

@@ -12,7 +12,9 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@import Foundation;
#import <Foundation/Foundation.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
@class SNTStoredEvent;
@@ -25,7 +27,7 @@ typedef void (^SNTBundleHashBlock)(NSString *, NSArray<SNTStoredEvent *> *, NSNu
///
/// @param listener The listener to connect back to the SantaGUI.
///
- (void)setBundleNotificationListener:(NSXPCListenerEndpoint *)listener;
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener;
///
/// Hash a bundle for an event. The SNTBundleHashBlock will be called with nil parameters if a
@@ -39,6 +41,11 @@ typedef void (^SNTBundleHashBlock)(NSString *, NSArray<SNTStoredEvent *> *, NSNu
///
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event reply:(SNTBundleHashBlock)reply;
///
/// santabundleservice is launched on demand by launchd, call spindown to let santabundleservice know you are done with it.
///
- (void)spindown;
@end
@interface SNTXPCBundleServiceInterface : NSObject
@@ -52,6 +59,12 @@ typedef void (^SNTBundleHashBlock)(NSString *, NSArray<SNTStoredEvent *> *, NSNu
///
/// Returns the MachService ID for this service.
///
+ (NSString *)serviceId;
+ (NSString *)serviceID;
///
/// Retrieve a pre-configured MOLXPCConnection for communicating with santabundleservice.
/// Connections just needs any handlers set and then can be resumed and used.
///
+ (MOLXPCConnection *)configuredConnection;
@end

View File

@@ -12,9 +12,9 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTXPCBundleServiceInterface.h"
#import "Source/common/SNTXPCBundleServiceInterface.h"
#import "SNTStoredEvent.h"
#import "Source/common/SNTStoredEvent.h"
@implementation SNTXPCBundleServiceInterface
@@ -29,8 +29,15 @@
return r;
}
+ (NSString *)serviceId {
return @"com.google.santabs";
+ (NSString *)serviceID {
return @"com.google.santa.bundleservice";
}
+ (MOLXPCConnection *)configuredConnection {
MOLXPCConnection *c = [[MOLXPCConnection alloc] initClientWithName:[self serviceID]
privileged:YES];
c.remoteInterface = [self bundleServiceInterface];
return c;
}
@end

View File

@@ -1,135 +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 Foundation;
/**
A wrapper around NSXPCListener and NSXPCConnection to provide client multiplexing, signature
validation of connecting clients and forced connection establishment.
Example server started by @c launchd where the @c launchd job has a @c MachServices key:
@code
SNTXPCConnection *conn = [[SNTXPCConnection alloc] initServerWithName:@"MyServer"];
conn.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyServerProtocol)];
conn.exportedObject = myObject;
[conn resume];
@endcode
Example client, connecting to above server:
@code
SNTXPCConnection *conn = [[SNTXPCConnection alloc] initClientWithName:"MyServer"
withOptions:0];
conn.remoteInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyServerProtocol)];
conn.invalidationHandler = ^{ NSLog(@"Connection invalidated") };
[conn resume];
@endcode
The client can send a message to the server with:
@code
[conn.remoteObjectProxy selectorInRemoteInterface];
@endcode
One advantage of the way that SNTXPCConnection works over using NSXPCConnection directly is that
from the client-side once the resume method has finished, the connection is either valid or the
invalidation handler will be called. Ordinarily, the connection doesn't actually get made until
the first message is sent across it.
@note messages are always delivered on a background thread!
*/
@interface SNTXPCConnection : NSObject<NSXPCListenerDelegate>
/**
Initialize a new server with a given listener, provided by `[NSXPCListener anonymousListener]`.
*/
- (nullable instancetype)initServerWithListener:(nonnull NSXPCListener *)listener;
/**
Initializer for the 'server' side of the connection, started by launchd.
@param name MachService name, must match the MachServices key in the launchd.plist
*/
- (nullable instancetype)initServerWithName:(nonnull NSString *)name;
/**
Initialize a new client to a service exported by a LaunchDaemon.
@param name MachService name
@param privileged Use YES if the server is running as root.
*/
- (nullable instancetype)initClientWithName:(nonnull NSString *)name privileged:(BOOL)privileged;
/**
Initialize a new client to a service within a bundle.
@param name service name
*/
- (nullable instancetype)initClientWithServiceName:(nonnull NSString *)name;
/**
Initialize a new client with a listener endpoint sent from another process.
@param listener An NSXPCListenerEndpoint to connect to.
*/
- (nullable instancetype)initClientWithListener:(nonnull NSXPCListenerEndpoint *)listener;
/**
Call when the properties of the object have been set-up and you're ready for connections.
For clients, this call can take up to 2s to complete for connection to finish establishing though
in basically all cases it will actually complete in a few milliseconds.
*/
- (void)resume;
/**
Invalidate the connection(s). This must be done before the object can be released.
*/
- (void)invalidate;
/**
The interface the remote object should conform to. (client)
*/
@property(retain, nullable) NSXPCInterface *remoteInterface;
/**
A proxy to the object at the other end of the connection. (client)
@note If the connection to the server failed, this will be nil, so you can safely send messages
and rely on the invalidationHandler for handling the failure.
*/
@property(readonly, nonatomic, nullable) id remoteObjectProxy;
/**
The interface this object exports. (server)
*/
@property(retain, nullable) NSXPCInterface *exportedInterface;
/**
The object that responds to messages from the other end. (server)
*/
@property(retain, nullable) id exportedObject;
/**
A block to run when a/the connection is accepted and fully established.
*/
@property(copy, nullable) void (^acceptedHandler)(void);
/**
A block to run when a/the connection is invalidated/interrupted/rejected.
*/
@property(copy, nullable) void (^invalidationHandler)(void);
@end

View File

@@ -1,210 +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 "SNTXPCConnection.h"
#import "MOLCodesignChecker.h"
#import "SNTStrengthify.h"
/**
Protocol used during connection establishment, @see SNTXPCConnectionInterface
*/
@protocol SNTXPCConnectionProtocol
- (void)connectWithReply:(void (^)())reply;
@end
/**
Recipient object used during connection establishment. Each incoming connection
has one of these objects created which accept the message in the protocol
and call the block provided during creation before replying.
This allows the server to reset the connection's exported interface and
object to the correct values after the client has sent the establishment message.
*/
@interface SNTXPCConnectionInterface : NSObject<SNTXPCConnectionProtocol>
@property(strong) void (^block)(void);
@end
@implementation SNTXPCConnectionInterface
- (void)connectWithReply:(void (^)())reply {
if (self.block) self.block();
reply();
}
@end
@interface SNTXPCConnection ()
@property NSXPCInterface *validationInterface;
/// The XPC listener (server only).
@property NSXPCListener *listenerObject;
/// The current connection object (client only).
@property NSXPCConnection *currentConnection;
@end
@implementation SNTXPCConnection
#pragma mark Initializers
- (instancetype)initServerWithListener:(NSXPCListener *)listener {
self = [super init];
if (self) {
_listenerObject = listener;
_validationInterface =
[NSXPCInterface interfaceWithProtocol:@protocol(SNTXPCConnectionProtocol)];
}
return self;
}
- (instancetype)initServerWithName:(NSString *)name {
return [self initServerWithListener:[[NSXPCListener alloc] initWithMachServiceName:name]];
}
- (instancetype)initClientWithListener:(NSXPCListenerEndpoint *)listener {
self = [super init];
if (self) {
_currentConnection = [[NSXPCConnection alloc] initWithListenerEndpoint:listener];
if (!_currentConnection) return nil;
_validationInterface =
[NSXPCInterface interfaceWithProtocol:@protocol(SNTXPCConnectionProtocol)];
}
return self;
}
- (instancetype)initClientWithName:(NSString *)name privileged:(BOOL)privileged {
self = [super init];
if (self) {
NSXPCConnectionOptions options = (privileged ? NSXPCConnectionPrivileged : 0);
_currentConnection = [[NSXPCConnection alloc] initWithMachServiceName:name options:options];
if (!_currentConnection) return nil;
_validationInterface =
[NSXPCInterface interfaceWithProtocol:@protocol(SNTXPCConnectionProtocol)];
}
return self;
}
- (instancetype)initClientWithServiceName:(NSString *)name {
self = [super init];
if (self) {
_currentConnection = [[NSXPCConnection alloc] initWithServiceName:name];
if (!_currentConnection) return nil;
_validationInterface =
[NSXPCInterface interfaceWithProtocol:@protocol(SNTXPCConnectionProtocol)];
}
return self;
}
- (instancetype)init {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
#pragma mark Connection set-up
- (void)resume {
if (self.listenerObject) {
self.listenerObject.delegate = self;
[self.listenerObject resume];
} else {
WEAKIFY(self);
// Set-up the connection with the remote interface set to the validation interface,
// send a message to the listener to finish establishing the connection
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
self.currentConnection.remoteObjectInterface = self.validationInterface;
self.currentConnection.interruptionHandler = self.currentConnection.invalidationHandler = ^{
STRONGIFY(self);
if (self.invalidationHandler) self.invalidationHandler();
};
[self.currentConnection resume];
[[self.currentConnection remoteObjectProxy] connectWithReply:^{
STRONGIFY(self);
// The connection is now established
[self.currentConnection suspend];
self.currentConnection.remoteObjectInterface = self.remoteInterface;
[self.currentConnection resume];
dispatch_semaphore_signal(sema);
if (self.acceptedHandler) self.acceptedHandler();
}];
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC))) {
// This is unusual - as we're not inside a block - but necessary in case the caller sets an
// invalidation handler that causes this instance to be released (which is a reasonable
// approach). If establishing a connection fails, the invalidation handler will be called
// and then shortly after this bit of code will run causing a crash.
STRONGIFY(self);
// Connection was not established in a reasonable time, invalidate.
self.currentConnection.remoteObjectInterface = nil; // ensure clients don't try to use it.
[self.currentConnection invalidate];
}
}
}
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)connection {
pid_t pid = connection.processIdentifier;
MOLCodesignChecker *otherCS = [[MOLCodesignChecker alloc] initWithPID:pid];
if (![otherCS signingInformationMatches:[[MOLCodesignChecker alloc] initWithSelf]]) {
return NO;
}
// The client passed the code signature check, now we need to resume the listener and
// return YES so that the client can send the connectWithReply message. Once the client does
// we reset the connection's exportedInterface and exportedObject.
SNTXPCConnectionInterface *ci = [[SNTXPCConnectionInterface alloc] init];
WEAKIFY(self);
WEAKIFY(connection);
ci.block = ^{
STRONGIFY(self)
STRONGIFY(connection);
[connection suspend];
connection.invalidationHandler = connection.interruptionHandler = ^{
if (self.invalidationHandler) self.invalidationHandler();
};
connection.exportedInterface = self.exportedInterface;
connection.exportedObject = self.exportedObject;
[connection resume];
// The connection is now established.
if (self.acceptedHandler) self.acceptedHandler();
};
connection.exportedInterface = self.validationInterface;
connection.exportedObject = ci;
[connection resume];
return YES;
}
- (id)remoteObjectProxy {
if (self.currentConnection.remoteObjectInterface &&
self.currentConnection.remoteObjectInterface != self.validationInterface) {
return [self.currentConnection remoteObjectProxyWithErrorHandler:^(NSError *error) {
[self.currentConnection invalidate];
}];
}
return nil;
}
#pragma mark Connection tear-down
- (void)invalidate {
if (self.currentConnection) {
[self.currentConnection invalidate];
self.currentConnection = nil;
} else if (self.listenerObject) {
[self.listenerObject invalidate];
}
}
@end

View File

@@ -12,98 +12,48 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@import Foundation;
#import <MOLCertificate/MOLCertificate.h>
#import "SNTCachedDecision.h"
#import "SNTCommonEnums.h"
#import "SNTKernelCommon.h"
#import "SNTXPCBundleServiceInterface.h"
@class SNTRule;
@class SNTStoredEvent;
@class SNTXPCConnection;
#import "Source/common/SNTXPCUnprivilegedControlInterface.h"
///
/// Protocol implemented by santad and utilized by santactl
/// Protocol implemented by santad and utilized by santactl (privileged operations)
///
@protocol SNTDaemonControlXPC
@protocol SNTDaemonControlXPC <SNTUnprivilegedDaemonControlXPC>
///
/// Kernel ops
///
- (void)cacheCounts:(void (^)(uint64_t rootCache, uint64_t nonRootCache))reply;
- (void)flushCache:(void (^)(BOOL))reply;
- (void)checkCacheForVnodeID:(uint64_t)vnodeID withReply:(void (^)(santa_action_t))reply;
///
/// Database ops
///
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate))reply;
- (void)databaseRuleAddRules:(NSArray *)rules
cleanSlate:(BOOL)cleanSlate
reply:(void (^)(NSError *error))reply;
- (void)databaseEventCount:(void (^)(int64_t count))reply;
- (void)databaseEventsPending:(void (^)(NSArray *events))reply;
- (void)databaseRemoveEventsWithIDs:(NSArray *)ids;
- (void)databaseRuleForBinarySHA256:(NSString *)binarySHA256
certificateSHA256:(NSString *)certificateSHA256
reply:(void (^)(SNTRule *))reply;
///
/// Decision ops
///
///
/// @param filePath A Path to the file, can be nil.
/// @param fileSHA256 The pre-calculated SHA256 hash for the file, can be nil. If nil the hash will
/// be calculated by this method from the filePath.
/// @param certificateSHA256 A SHA256 hash of the signing certificate, can be nil.
/// @note If fileInfo and signingCertificate are both passed in, the most specific rule will be
/// returned. Binary rules take precedence over cert rules.
///
- (void)decisionForFilePath:(NSString *)filePath
fileSHA256:(NSString *)fileSHA256
certificateSHA256:(NSString *)certificateSHA256
reply:(void (^)(SNTEventState))reply;
///
/// Config ops
///
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply;
- (void)clientMode:(void (^)(SNTClientMode))reply;
- (void)setClientMode:(SNTClientMode)mode reply:(void (^)())reply;
- (void)xsrfToken:(void (^)(NSString *))reply;
- (void)setXsrfToken:(NSString *)token reply:(void (^)())reply;
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply;
- (void)setFullSyncLastSuccess:(NSDate *)date reply:(void (^)())reply;
- (void)ruleSyncLastSuccess:(void (^)(NSDate *))reply;
- (void)setRuleSyncLastSuccess:(NSDate *)date reply:(void (^)())reply;
- (void)syncCleanRequired:(void (^)(BOOL))reply;
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)())reply;
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)())reply;
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)())reply;
- (void)bundlesEnabled:(void (^)(BOOL))reply;
- (void)setBundlesEnabled:(BOOL)bundlesEnabled reply:(void (^)())reply;
///
/// GUI Ops
///
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener;
- (void)setBundleNotificationListener:(NSXPCListenerEndpoint *)listener;
- (void)setClientMode:(SNTClientMode)mode reply:(void (^)(void))reply;
- (void)setXsrfToken:(NSString *)token reply:(void (^)(void))reply;
- (void)setFullSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply;
- (void)setRuleSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply;
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)(void))reply;
- (void)setAllowedPathRegex:(NSString *)pattern reply:(void (^)(void))reply;
- (void)setBlockedPathRegex:(NSString *)pattern reply:(void (^)(void))reply;
- (void)setEnableBundles:(BOOL)bundlesEnabled reply:(void (^)(void))reply;
- (void)setEnableTransitiveRules:(BOOL)enabled reply:(void (^)(void))reply;
///
/// Syncd Ops
///
- (void)setSyncdListener:(NSXPCListenerEndpoint *)listener;
- (void)pushNotifications:(void (^)(BOOL))reply;
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message reply:(void (^)())reply;
///
/// Bundle Ops
///
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event reply:(SNTBundleHashBlock)reply;
- (void)syncBundleEvent:(SNTStoredEvent *)event relatedEvents:(NSArray<SNTStoredEvent *> *)events;
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message reply:(void (^)(void))reply;
@end
@@ -112,18 +62,23 @@
///
/// Returns the MachService ID for this service.
///
+ (NSString *)serviceId;
+ (NSString *)serviceID;
///
/// Returns an initialized NSXPCInterface for the SNTDaemonControlXPC protocol.
/// Returns the SystemExtension ID for this service.
///
+ (NSString *)systemExtensionID;
///
/// Returns an initialized NSXPCInterface for the SNTUnprivilegedDaemonControlXPC protocol.
/// Ensures any methods that accept custom classes as arguments are set-up before returning
///
+ (NSXPCInterface *)controlInterface;
///
/// Retrieve a pre-configured SNTXPCConnection for communicating with santad.
/// Retrieve a pre-configured MOLXPCConnection for communicating with santad.
/// Connections just needs any handlers set and then can be resumed and used.
///
+ (SNTXPCConnection *)configuredConnection;
+ (MOLXPCConnection *)configuredConnection;
@end

View File

@@ -12,21 +12,35 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTXPCControlInterface.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "SNTRule.h"
#import "SNTStoredEvent.h"
#import "SNTXPCConnection.h"
#import <MOLCodesignChecker/MOLCodesignChecker.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/common/SNTCommonEnums.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTRule.h"
#import "Source/common/SNTStoredEvent.h"
NSString *const kBundleID = @"com.google.santa.daemon";
@implementation SNTXPCControlInterface
+ (NSString *)serviceId {
return @"SantaXPCControl";
+ (NSString *)serviceID {
if ([[SNTConfigurator configurator] enableSystemExtension]) {
MOLCodesignChecker *cs = [[MOLCodesignChecker alloc] initWithSelf];
// "teamid.com.google.santa.daemon.xpc"
NSString *t = cs.signingInformation[@"teamid"];
return [NSString stringWithFormat:@"%@.%@.xpc", t, kBundleID];
}
return kBundleID;
}
+ (NSXPCInterface *)controlInterface {
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTDaemonControlXPC)];
+ (NSString *)systemExtensionID {
return kBundleID;
}
+ (void)initializeControlInterface:(NSXPCInterface *)r {
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
forSelector:@selector(databaseEventsPending:)
argumentIndex:0
@@ -36,22 +50,17 @@
forSelector:@selector(databaseRuleAddRules:cleanSlate:reply:)
argumentIndex:0
ofReply:NO];
}
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
forSelector:@selector(hashBundleBinariesForEvent:reply:)
argumentIndex:1
ofReply:YES];
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
forSelector:@selector(syncBundleEvent:relatedEvents:)
argumentIndex:1
ofReply:NO];
+ (NSXPCInterface *)controlInterface {
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTDaemonControlXPC)];
[self initializeControlInterface:r];
return r;
}
+ (SNTXPCConnection *)configuredConnection {
SNTXPCConnection *c = [[SNTXPCConnection alloc] initClientWithName:[self serviceId]
+ (MOLXPCConnection *)configuredConnection {
MOLXPCConnection *c = [[MOLXPCConnection alloc] initClientWithName:[self serviceID]
privileged:YES];
c.remoteInterface = [self controlInterface];
return c;

View File

@@ -12,10 +12,10 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@import Foundation;
#import <Foundation/Foundation.h>
#import "SNTCommonEnums.h"
#import "SNTXPCBundleServiceInterface.h"
#import "Source/common/SNTCommonEnums.h"
#import "Source/common/SNTXPCBundleServiceInterface.h"
@class SNTStoredEvent;
@@ -24,16 +24,10 @@
- (void)postBlockNotification:(SNTStoredEvent *)event withCustomMessage:(NSString *)message;
- (void)postClientModeNotification:(SNTClientMode)clientmode;
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message;
@end
/// Protocol implemented by SantaGUI and utilized by santabs
@protocol SNTBundleNotifierXPC
- (void)updateCountsForEvent:(SNTStoredEvent *)event
binaryCount:(uint64_t)binaryCount
fileCount:(uint64_t)fileCount
hashedCount:(uint64_t)hashedCount;
- (void)setBundleServiceListener:(NSXPCListenerEndpoint *)listener;
@end
@interface SNTXPCNotifierInterface : NSObject
@@ -44,10 +38,4 @@
///
+ (NSXPCInterface *)notifierInterface;
///
/// @return an initialized NSXPCInterface for the SNTBundleNotifierXPC protocol.
/// Ensures any methods that accept custom classes as arguments are set-up before returning
///
+ (NSXPCInterface *)bundleNotifierInterface;
@end

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTXPCNotifierInterface.h"
#import "Source/common/SNTXPCNotifierInterface.h"
@implementation SNTXPCNotifierInterface
@@ -20,8 +20,4 @@
return [NSXPCInterface interfaceWithProtocol:@protocol(SNTNotifierXPC)];
}
+ (NSXPCInterface *)bundleNotifierInterface {
return [NSXPCInterface interfaceWithProtocol:@protocol(SNTBundleNotifierXPC)];
}
@end

View File

@@ -0,0 +1,56 @@
/// Copyright 2020 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 <Foundation/Foundation.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/common/SNTCommonEnums.h"
@class SNTStoredEvent;
/// A block that reports the number of rules processed.
/// TODO(bur): Add more details about the sync.
typedef void (^SNTFullSyncReplyBlock)(NSNumber *rulesProcessed);
/// Protocol implemented by syncservice and utilized by daemon and ctl for communication with a sync server.
@protocol SNTSyncServiceXPC
- (void)postEventsToSyncServer:(NSArray<SNTStoredEvent *> *)events fromBundle:(BOOL)fromBundle;
- (void)postBundleEventToSyncServer:(SNTStoredEvent *)event
reply:(void (^)(SNTBundleEventAction))reply;
- (void)isFCMListening:(void (^)(BOOL))reply;
- (void)performFullSyncWithReply:(SNTFullSyncReplyBlock)reply;
@end
@interface SNTXPCSyncServiceInterface : NSObject
///
/// Returns an initialized NSXPCInterface for the SNTSyncServiceXPC protocol.
/// Ensures any methods that accept custom classes as arguments are set-up before returning.
///
+ (NSXPCInterface *)syncServiceInterface;
///
/// Returns the MachService ID for this service.
///
+ (NSString *)serviceID;
///
/// Retrieve a pre-configured MOLXPCConnection for communicating with syncservice.
/// Connections just needs any handlers set and then can be resumed and used.
///
+ (MOLXPCConnection *)configuredConnection;
@end

View File

@@ -0,0 +1,43 @@
/// Copyright 2020 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 "Source/common/SNTXPCSyncServiceInterface.h"
#import "Source/common/SNTStoredEvent.h"
@implementation SNTXPCSyncServiceInterface
+ (NSXPCInterface *)syncServiceInterface {
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTSyncServiceXPC)];
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
forSelector:@selector(postEventsToSyncServer:fromBundle:)
argumentIndex:0
ofReply:NO];
return r;
}
+ (NSString *)serviceID {
return @"com.google.santa.syncservice";
}
+ (MOLXPCConnection *)configuredConnection {
MOLXPCConnection *c = [[MOLXPCConnection alloc] initClientWithName:[self serviceID]
privileged:YES];
c.remoteInterface = [self syncServiceInterface];
return c;
}
@end

View File

@@ -12,9 +12,9 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@import Foundation;
#import <Foundation/Foundation.h>
#import "SNTCommonEnums.h"
#import "Source/common/SNTCommonEnums.h"
@class SNTStoredEvent;

View File

@@ -12,9 +12,9 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTXPCSyncdInterface.h"
#import "Source/common/SNTXPCSyncdInterface.h"
#import "SNTStoredEvent.h"
#import "Source/common/SNTStoredEvent.h"
@implementation SNTXPCSyncdInterface

View File

@@ -0,0 +1,106 @@
/// 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 <Foundation/Foundation.h>
#import <MOLCertificate/MOLCertificate.h>
#import "Source/common/SNTCommonEnums.h"
#import "Source/common/SNTKernelCommon.h"
@class SNTRule;
@class SNTStoredEvent;
@class MOLXPCConnection;
///
/// Protocol implemented by santad and utilized by santactl (unprivileged operations)
///
@protocol SNTUnprivilegedDaemonControlXPC
///
/// Kernel ops
///
- (void)cacheCounts:(void (^)(uint64_t rootCache, uint64_t nonRootCache))reply;
- (void)cacheBucketCount:(void (^)(NSArray *))reply;
- (void)checkCacheForVnodeID:(santa_vnode_id_t)vnodeID withReply:(void (^)(santa_action_t))reply;
- (void)driverConnectionEstablished:(void (^)(BOOL))reply;
///
/// Database ops
///
- (void)databaseRuleCounts:(void (^)(int64_t binary,
int64_t certificate,
int64_t compiler,
int64_t transitive))reply;
- (void)databaseEventCount:(void (^)(int64_t count))reply;
///
/// Decision ops
///
///
/// @param filePath A Path to the file, can be nil.
/// @param fileSHA256 The pre-calculated SHA256 hash for the file, can be nil. If nil the hash will
/// be calculated by this method from the filePath.
/// @param certificateSHA256 A SHA256 hash of the signing certificate, can be nil.
/// @note If fileInfo and signingCertificate are both passed in, the most specific rule will be
/// returned. Binary rules take precedence over cert rules.
///
- (void)decisionForFilePath:(NSString *)filePath
fileSHA256:(NSString *)fileSHA256
certificateSHA256:(NSString *)certificateSHA256
reply:(void (^)(SNTEventState))reply;
///
/// Config ops
///
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply;
- (void)xsrfToken:(void (^)(NSString *))reply;
- (void)clientMode:(void (^)(SNTClientMode))reply;
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply;
- (void)ruleSyncLastSuccess:(void (^)(NSDate *))reply;
- (void)syncCleanRequired:(void (^)(BOOL))reply;
- (void)enableBundles:(void (^)(BOOL))reply;
- (void)enableTransitiveRules:(void (^)(BOOL))reply;
///
/// GUI Ops
///
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener;
///
/// Syncd Ops
///
- (void)pushNotifications:(void (^)(BOOL))reply;
///
/// Bundle Ops
///
- (void)syncBundleEvent:(SNTStoredEvent *)event relatedEvents:(NSArray<SNTStoredEvent *> *)events;
@end
@interface SNTXPCUnprivilegedControlInterface : NSObject
///
/// Returns an initialized NSXPCInterface for the SNTUnprivilegedDaemonControlXPC protocol.
/// Ensures any methods that accept custom classes as arguments are set-up before returning
///
+ (NSXPCInterface *)controlInterface;
///
/// Internal method used to initialize the control interface
///
+ (void)initializeControlInterface:(NSXPCInterface *)r;
@end

View File

@@ -0,0 +1,38 @@
/// 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 "Source/common/SNTXPCUnprivilegedControlInterface.h"
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/common/SNTRule.h"
#import "Source/common/SNTStoredEvent.h"
@implementation SNTXPCUnprivilegedControlInterface
+ (void)initializeControlInterface:(NSXPCInterface *)r {
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
forSelector:@selector(syncBundleEvent:relatedEvents:)
argumentIndex:1
ofReply:NO];
}
+ (NSXPCInterface *)controlInterface {
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTUnprivilegedDaemonControlXPC)];
[self initializeControlInterface:r];
return r;
}
@end

View File

@@ -20,7 +20,7 @@
#include <stdint.h>
#include <sys/cdefs.h>
#include "SNTKernelCommon.h"
#include "Source/common/SNTKernelCommon.h"
#ifdef KERNEL
#include <IOKit/IOLib.h>
@@ -30,19 +30,31 @@
#include <cstdlib>
#include <cstring>
#define panic(args...) printf(args); printf("\n"); abort()
#define IOMalloc malloc
#define IOMallocAligned(sz, alignment) malloc(sz);
#define IOFree(addr, sz) free(addr)
#define IOFreeAligned(addr, sz) free(addr)
#define OSTestAndSet OSAtomicTestAndSet
#define OSTestAndClear(bit, addr) OSAtomicTestAndClear(bit, addr) == 0
#define OSIncrementAtomic(addr) OSAtomicIncrement64((volatile int64_t *)addr)
#define OSDecrementAtomic(addr) OSAtomicDecrement64((volatile int64_t *)addr)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif // KERNEL
/**
A type to specialize to help SantaCache with its hashing.
The default works for numeric types with a multiplicative hash
using a prime near to the golden ratio, per Knuth.
*/
template<typename T> uint64_t SantaCacheHasher(T const& t) {
return t * 11400714819323198549UL;
};
/**
A somewhat simple, concurrent linked-list hash table intended for use in IOKit kernel extensions.
Maps 64-bit unsigned integer keys to values.
The type used for keys must overload the == operator and a specialization of
SantaCacheHasher must exist for it.
Enforces a maximum size by clearing all entries if a new value
is added that would go over the maximum size declared at creation.
@@ -50,7 +62,7 @@
The number of buckets is calculated as `maximum_size` / `per_bucket`
rounded up to the next power of 2. Locking is done per-bucket.
*/
template<class T> class SantaCache {
template<typename KeyT, typename ValueT> class SantaCache {
public:
/**
Initialize a newly created cache.
@@ -62,12 +74,13 @@ template<class T> class SantaCache {
Cannot be higher than 64 to try and ensure buckets don't overflow.
*/
SantaCache(uint64_t maximum_size = 10000, uint8_t per_bucket = 5) {
if (unlikely(per_bucket > maximum_size)) per_bucket = maximum_size;
if (unlikely(per_bucket < 1)) per_bucket = 1;
if (unlikely(per_bucket > 64)) per_bucket = 64;
max_size_ = maximum_size;
bucket_count_ = 1 << (32 - __builtin_clz(
((uint32_t)max_size_ / per_bucket) - 1));
buckets_ = (struct bucket *)IOMalloc(bucket_count_ * sizeof(struct bucket));
bucket_count_ = (1 << (32 - __builtin_clz((((uint32_t)max_size_ / per_bucket) - 1) ?: 1)));
if (unlikely(bucket_count_ > UINT32_MAX)) bucket_count_ = UINT32_MAX;
buckets_ = (struct bucket *)IOMallocAligned(bucket_count_ * sizeof(struct bucket), 2);
bzero(buckets_, bucket_count_ * sizeof(struct bucket));
}
@@ -76,19 +89,19 @@ template<class T> class SantaCache {
*/
~SantaCache() {
clear();
IOFree(buckets_, bucket_count_ * sizeof(struct bucket));
IOFreeAligned(buckets_, bucket_count_ * sizeof(struct bucket));
}
/**
Get an element from the cache. Returns zero_ if item doesn't exist.
*/
T get(uint64_t key) {
ValueT get(KeyT key) {
struct bucket *bucket = &buckets_[hash(key)];
lock(bucket);
struct entry *entry = (struct entry *)((uintptr_t)bucket->head - 1);
while (entry != nullptr) {
if (entry->key == key) {
T val = entry->value;
ValueT val = entry->value;
unlock(bucket);
return val;
}
@@ -102,103 +115,39 @@ template<class T> class SantaCache {
Set an element in the cache.
@note If the cache is full when this is called, this will
empty the cache before inserting the new value.
empty the cache before inserting the new value.
@param key, The key
@param value, The value with parameterized type
@param previous_value, If the has_prev_value parameter is true the new
value will only be set if this parameter is equal to the provided value.
This allows set to become a CAS operation.
@param has_prev_value, Pass true if previous_value should be used.
@param key The key.
@param value The value with parameterized type.
@return the previous value (which may be zero_)
@return true if the value was set.
*/
T set(uint64_t key, T value, T previous_value, bool has_prev_value) {
struct bucket *bucket = &buckets_[hash(key)];
lock(bucket);
struct entry *entry = (struct entry *)((uintptr_t)bucket->head - 1);
struct entry *previous_entry = nullptr;
while (entry != nullptr) {
if (entry->key == key) {
T existing_value = entry->value;
if (has_prev_value && previous_value != existing_value) {
unlock(bucket);
return existing_value;
}
entry->value = value;
if (value == zero_) {
if (previous_entry != nullptr) {
previous_entry->next = entry->next;
} else {
bucket->head = (struct entry *)((uintptr_t)entry->next + 1);
}
IOFreeAligned(entry, sizeof(struct entry));
OSDecrementAtomic(&count_);
}
unlock(bucket);
return existing_value;
}
previous_entry = entry;
entry = entry->next;
}
// If value is zero_, we're clearing but there's nothing to clear
// so we don't need to do anything else. Alternatively, if has_prev_value
// is true and is not zero_ we don't want to set a value.
if (value == zero_ || (has_prev_value && previous_value != zero_)) {
unlock(bucket);
return zero_;
}
// Check that adding this new item won't take the cache
// over its maximum size.
if (count_ + 1 > max_size_) {
unlock(bucket);
lock(&clear_bucket_);
// Check again in case clear has already run while waiting for lock
if (count_ + 1 > max_size_) {
clear();
}
lock(bucket);
unlock(&clear_bucket_);
}
// Allocate a new entry, set the key and value, then put this new entry at
// the head of this bucket's linked list.
struct entry *new_entry = (struct entry *)IOMallocAligned(
sizeof(struct entry), 2);
new_entry->key = key;
new_entry->value = value;
new_entry->next = (struct entry *)((uintptr_t)bucket->head - 1);
bucket->head = (struct entry *)((uintptr_t)new_entry + 1);
OSIncrementAtomic(&count_);
unlock(bucket);
return zero_;
}
/**
Overload to allow setting without providing a previous value
*/
T set(uint64_t key, T value) {
bool set(const KeyT& key, const ValueT& value) {
return set(key, value, {}, false);
}
/**
Overload to allow setting while providing a previous value
Set an element in the cache.
@note If the cache is full when this is called, this will
empty the cache before inserting the new value.
@param key The key.
@param value The value with parameterized type.
@param previous_value the new value will only be set if this
parameter is equal to the existing value in the cache.
This allows set to become a CAS operation.
@return true if the value was set
*/
T set(uint64_t key, T value, T previous_value) {
bool set(const KeyT& key, const ValueT& value, const ValueT& previous_value) {
return set(key, value, previous_value, true);
}
/**
An alias for `set(key, zero_)`
*/
inline void remove(uint64_t key) {
inline void remove(const KeyT& key) {
set(key, zero_);
}
@@ -238,10 +187,47 @@ template<class T> class SantaCache {
return count_;
}
/**
Fill in the per_bucket_counts array with the number of entries in each bucket.
The per_buckets_count array will contain the per-bucket counts, up to the number
in array_size. The start_bucket parameter will determine which bucket to start off
with and upon return will contain either 0 if no buckets are remaining or the next
bucket to begin with when called again.
*/
void bucket_counts(uint16_t *per_bucket_counts, uint16_t *array_size, uint64_t *start_bucket) {
if (per_bucket_counts == nullptr || array_size == nullptr || start_bucket == nullptr) return;
uint64_t start = *start_bucket;
if (start >= bucket_count_) {
*start_bucket = 0;
return;
}
uint16_t size = *array_size;
if (start + size > bucket_count_) size = bucket_count_ - start;
for (uint16_t i = 0; i < size; ++i) {
uint16_t count = 0;
struct bucket *bucket = &buckets_[start++];
lock(bucket);
struct entry *entry = (struct entry *)((uintptr_t)bucket->head - 1);
while (entry != nullptr) {
if (entry->value != zero_) ++count;
entry = entry->next;
}
unlock(bucket);
per_bucket_counts[i] = count;
}
*array_size = size;
*start_bucket = (start >= bucket_count_) ? 0 : start;
}
private:
struct entry {
uint64_t key;
T value;
KeyT key;
ValueT value;
struct entry *next;
};
@@ -251,6 +237,90 @@ template<class T> class SantaCache {
struct entry *head;
};
/**
Set an element in the cache.
@note If the cache is full when this is called, this will
empty the cache before inserting the new value.
@param key The key
@param value The value with parameterized type
@param previous_value If has_prev_value is true, the new value will only
be set if this parameter is equal to the existing value in the cache.
This allows set to become a CAS operation.
@param has_prev_value Pass true if previous_value should be used.
@return true if the entry was set, false if it was not
*/
bool set(const KeyT& key, const ValueT& value,
const ValueT& previous_value, bool has_prev_value) {
struct bucket *bucket = &buckets_[hash(key)];
lock(bucket);
struct entry *entry = (struct entry *)((uintptr_t)bucket->head - 1);
struct entry *previous_entry = nullptr;
while (entry != nullptr) {
if (entry->key == key) {
ValueT existing_value = entry->value;
if (has_prev_value && previous_value != existing_value) {
unlock(bucket);
return false;
}
entry->value = value;
if (value == zero_) {
if (previous_entry != nullptr) {
previous_entry->next = entry->next;
} else {
bucket->head = (struct entry *)((uintptr_t)entry->next + 1);
}
IOFreeAligned(entry, sizeof(struct entry));
OSDecrementAtomic(&count_);
}
unlock(bucket);
return true;
}
previous_entry = entry;
entry = entry->next;
}
// If value is zero_, we're clearing but there's nothing to clear
// so we don't need to do anything else. Alternatively, if has_prev_value
// is true and is not zero_ we don't want to set a value.
if (value == zero_ || (has_prev_value && previous_value != zero_)) {
unlock(bucket);
return false;
}
// Check that adding this new item won't take the cache
// over its maximum size.
if (count_ + 1 > max_size_) {
unlock(bucket);
lock(&clear_bucket_);
// Check again in case clear has already run while waiting for lock
if (count_ + 1 > max_size_) {
clear();
}
lock(bucket);
unlock(&clear_bucket_);
}
// Allocate a new entry, set the key and value, then put this new entry at
// the head of this bucket's linked list.
struct entry *new_entry = (struct entry *)IOMallocAligned(sizeof(struct entry), 2);
bzero(new_entry, sizeof(struct entry));
new_entry->key = key;
new_entry->value = value;
new_entry->next = (struct entry *)((uintptr_t)bucket->head - 1);
bucket->head = (struct entry *)((uintptr_t)new_entry + 1);
OSIncrementAtomic(&count_);
unlock(bucket);
return true;
}
/**
Lock a bucket. Spins until the lock is acquired.
*/
@@ -277,7 +347,7 @@ template<class T> class SantaCache {
/**
Holder for a 'zero' entry for the current type
*/
const T zero_ = {};
const ValueT zero_ = {};
/**
Special bucket used when automatically clearing due to size
@@ -288,14 +358,14 @@ template<class T> class SantaCache {
/**
Hash a key to determine which bucket it belongs in.
Multiplicative hash using a prime near to the golden ratio, per Knuth.
This seems to have good bucket distribution generally and for the range of
values we expect to see.
*/
inline uint64_t hash(uint64_t input) const {
return (input * 11400714819323198549ul) % bucket_count_;
inline uint64_t hash(KeyT input) const {
return SantaCacheHasher<KeyT>(input) % bucket_count_;
}
};
#ifndef KERNEL
#pragma clang diagnostic pop
#endif
#endif // SANTA__SANTA_DRIVER__SANTACACHE_H

View File

@@ -0,0 +1,286 @@
/// Copyright 2016 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>
#include <numeric>
#include <string>
#include <vector>
#include "Source/common/SantaCache.h"
@interface SantaCacheTest : XCTestCase
@end
@implementation SantaCacheTest
- (void)setUp {
self.continueAfterFailure = NO;
}
- (void)testSetAndGet {
auto sut = SantaCache<uint64_t, uint64_t>();
sut.set(72057611258548992llu, 10000192);
XCTAssertEqual(sut.get(72057611258548992llu), 10000192);
}
- (void)testCacheRemove {
auto sut = SantaCache<uint64_t, uint64_t>();
sut.set(0xDEADBEEF, 42);
sut.remove(0xDEADBEEF);
XCTAssertEqual(sut.get(0xDEADBEEF), 0);
}
- (void)testBucketGrowCopy {
auto sut = SantaCache<uint64_t, uint64_t>();
sut.set(386, 42);
sut.set(2434, 42);
XCTAssertEqual(sut.get(386), 42);
XCTAssertEqual(sut.get(2434), 42);
}
- (void)testBucketShrinkCopy {
auto sut = SantaCache<uint64_t, uint64_t>(100, 1);
sut.set(386, 42);
sut.set(2434, 42);
sut.set(4482, 42);
sut.remove(2434);
XCTAssertEqual(sut.get(386), 42);
XCTAssertEqual(sut.get(2434), 0);
XCTAssertEqual(sut.get(4482), 42);
}
- (void)testCacheResetAtLimit {
auto sut = SantaCache<uint64_t, uint64_t>(5);
sut.set(1, 42);
sut.set(2, 42);
sut.set(3, 42);
sut.set(4, 42);
sut.set(5, 42);
XCTAssertEqual(sut.get(3), 42);
sut.set(6, 42);
XCTAssertEqual(sut.get(3), 0);
XCTAssertEqual(sut.get(6), 42);
}
// Helper to test bucket distributions for uint64_t/uint64_t combinations.
- (void)distributionTestHelper:(SantaCache<uint64_t, uint64_t> *)sut bucketRatio:(int)br {
uint16_t count[512];
uint16_t array_size = 512;
uint64_t start_bucket = 0;
std::vector<uint16_t> per_bucket;
do {
sut->bucket_counts(count, &array_size, &start_bucket);
for (int i = 0; i < array_size; ++i) {
per_bucket.push_back(count[i]);
}
} while (start_bucket > 0);
// Calculate mean
double mean = std::accumulate(per_bucket.begin(), per_bucket.end(), 0.0) / per_bucket.size();
XCTAssertLessThanOrEqual(mean, br, @"Mean per-bucket count is greater than %d", br);
// Calculate stdev
double accum = 0.0;
std::for_each(per_bucket.begin(), per_bucket.end(), [&](const double d) {
accum += (d - mean) * (d - mean);
});
double stddev = sqrt(accum / (per_bucket.size() - 1));
double maxStdDev = (double)br / 2;
XCTAssertLessThanOrEqual(stddev, maxStdDev,
@"Standard deviation between buckets is greater than %f", maxStdDev);
}
- (void)testDistributionRandomKeys {
const int bucket_ratio = 5;
auto sut = new SantaCache<uint64_t, uint64_t>(5000, bucket_ratio);
// Fill the cache with random keys, all set to 1.
for (int i = 0; i < 4000; ++i) {
sut->set((uint64_t)arc4random() << 32 | arc4random(), 1);
}
[self distributionTestHelper:sut bucketRatio:bucket_ratio];
}
- (void)testDistributionMontonicKeys {
const int bucket_ratio = 5;
auto sut = new SantaCache<uint64_t, uint64_t>(5000, bucket_ratio);
// Fill the cache with monotonic keys, all set to 1.
for (int i = 0; i < 4000; ++i) {
sut->set(i, 1);
}
[self distributionTestHelper:sut bucketRatio:bucket_ratio];
}
- (void)testThreading {
auto sut = new SantaCache<uint64_t, uint64_t>();
for (int x = 0; x < 200; ++x) {
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
for (int i = 0; i < 5000; ++i) sut->set(i, 10000-i);
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
for (int i = 5000; i < 10000; ++i) sut->set(i, 10000-i);
dispatch_group_leave(group);
});
if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
XCTFail("Timed out while setting values for test");
}
for (int i = 0; i < 10000; ++i) XCTAssertEqual(sut->get(i), 10000 - i);
}
delete sut;
}
- (void)testCount {
auto sut = SantaCache<uint64_t, int>();
XCTAssertEqual(sut.count(), 0);
sut.set(4012, 42);
sut.set(42, 0);
sut.set(0x8BADF00D, 40010);
XCTAssertEqual(sut.count(), 2);
}
- (void)testDoubles {
auto sut = SantaCache<double, double>();
sut.set(3.14, 2.718281);
sut.set(1.41429, 2.5029);
sut.set(4.6692, 1.2020569);
sut.set(1.61803398, 0.57721);
XCTAssertEqual(sut.count(), 4);
XCTAssertEqual(sut.get(3.14), 2.718281);
XCTAssertEqual(sut.get(1.41429), 2.5029);
XCTAssertEqual(sut.get(4.6692), 1.2020569);
XCTAssertEqual(sut.get(1.61803398), 0.57721);
XCTAssertEqual(sut.get(5.5555), 0);
XCTAssertEqual(sut.get(3.1459124), 0);
}
template<> uint64_t SantaCacheHasher<std::string>(std::string const& s) {
return std::hash<std::string>{}(s);
}
- (void)testStrings {
auto sut = SantaCache<std::string, std::string>();
std::string s1 = "foo";
std::string s2 = "bar";
sut.set(s1, "deadbeef");
sut.set(s2, "feedface");
XCTAssertEqual(sut.count(), 2);
XCTAssertEqual(sut.get(s1), "deadbeef");
XCTAssertEqual(sut.get(s2), "feedface");
sut.remove(s2);
XCTAssertTrue(sut.get(s2).empty());
}
- (void)testCompareAndSwap {
auto sut = SantaCache<uint64_t, uint64_t>(100, 2);
sut.set(1, 42);
sut.set(1, 666, 1);
sut.set(1, 666, 0);
XCTAssertEqual(sut.get(1), 42);
sut.set(1, 0);
XCTAssertEqual(sut.get(1), 0);
sut.set(1, 42, 1);
XCTAssertEqual(sut.get(1), 0);
sut.set(1, 42, 0);
XCTAssertEqual(sut.get(1), 42);
sut.set(1, 0, 666);
XCTAssertEqual(sut.get(1), 42);
sut.set(1, 0, 42);
XCTAssertEqual(sut.get(1), 0);
}
struct S {
uint64_t first_val;
uint64_t second_val;
bool operator==(const S& rhs) {
return first_val == rhs.first_val && second_val == rhs.second_val;
}
};
template<> uint64_t SantaCacheHasher<S>(S const& s) {
return SantaCacheHasher<uint64_t>(s.first_val) ^ (SantaCacheHasher<uint64_t>(s.second_val) << 1);
}
- (void)testStructKeys {
auto sut = SantaCache<S, uint64_t>(10, 2);
S s1 = {1024, 2048};
S s2 = {4096, 8192};
S s3 = {16384, 32768};
sut.set(s1, 10);
sut.set(s2, 20);
sut.set(s3, 30);
XCTAssertEqual(sut.get(s1), 10);
XCTAssertEqual(sut.get(s2), 20);
XCTAssertEqual(sut.get(s3), 30);
}
- (void)testBucketCounts {
auto sut = new SantaCache<uint64_t, uint64_t>(UINT16_MAX, 1);
// These tests verify that the bucket_counts() function can't be abused
// with integer {over,under}flow issues in the input or going out-of-bounds
// on the buckets array.
uint16_t size = 2048;
uint64_t start = (UINT64_MAX - 2047);
uint16_t per_bucket_counts[2048];
sut->bucket_counts(per_bucket_counts, &size, &start);
XCTAssertEqual(start, 0, @"Check a high start can't overflow");
size = UINT16_MAX;
start = UINT16_MAX - 1;
sut->bucket_counts(per_bucket_counts, &size, &start);
XCTAssertEqual(start, 0, @"Check a large size can't overflow");
}
@end

BIN
Source/common/testdata/32bitplist vendored Executable file

Binary file not shown.

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