Compare commits

...

789 Commits

Author SHA1 Message Date
Russell Hancox
cc3177502c Tests: Fix un-needed expectation in SNTExecutionControllerTest.allEventUpload (#857) 2022-07-15 18:03:34 -04:00
Kathryn Hancox
a49a59b109 Docs: Add sync server list (#856) 2022-07-15 16:19:17 -04:00
Kathryn Hancox
2c06c39c82 Added quick getting started page for deployments (#855) 2022-07-15 15:23:33 -04:00
Pete Markowsky
234f81ea7c Ensure KVO works for USB config options (#853)
Ensure KVO works for USB config options.
2022-07-15 15:13:55 -04:00
Russell Hancox
743c567bf8 santad: Log team ID in execution logs, where available (#850) 2022-07-15 12:41:56 -04:00
Russell Hancox
21220f1499 santad: Add DisableUnknownEventUpload option. (#852) 2022-07-15 12:30:20 -04:00
Russell Hancox
39f3ffe8fc santactl/status: Fix printing of static rules (#848) 2022-07-15 11:53:38 -04:00
Russell Hancox
fdb01928a0 santad: Fix re-establishment of syncservice connection (#849)
* santad: Fix re-establishment of syncservice connection

The previous version could lead to santad having lots of threads stuck waiting for connections
2022-07-15 11:53:17 -04:00
Russell Hancox
fbefbc5910 santasyncservice: Keep XSRF token in memory, don't send to daemon (#851) 2022-07-15 11:52:43 -04:00
Russell Hancox
9db00d143d santad: Improve caching of static rules (#847)
In #846 I forgot that  is only a count of the entries so if the config changes but the number of rules remains the same we would never update the cache. This PR moves the processing of the raw config into the KVO handler code so it is not at all in the hot-path.
2022-07-14 10:50:30 -04:00
Russell Hancox
1cc40d59d8 santad: Allow configuring a static set of rules via configuration profile (#846) 2022-07-13 17:58:13 -04:00
Russell Hancox
ba1ace56f0 Project: Delete tulsiproj, add basic doc about hedron (#845) 2022-07-12 13:53:57 -04:00
Russell Hancox
6d911e9d6e CI: Make CI workflow only run on source changes (#843) 2022-07-08 16:03:30 -04:00
Kathryn Hancox
7e2b291122 Docs: Updated home page with README files & nav changes (#841) 2022-07-08 15:53:16 -04:00
Tom Burgin
64096f5d08 adhoc build and run santa (#840)
* adhoc build and run santa

* fold ci into adhoc

* review updates

Co-authored-by: Tom Burgin <bur@chromium.org>
2022-07-07 17:09:53 -04:00
Matt W
aec1c74fab Use the message copy in the dispatch blocks (#839) 2022-07-06 21:51:02 -04:00
Russell Hancox
d4a0d77cb9 Docs: Add gemfile for running jekyll locally. (#834)
This lets us test docs site changes by running `bundle exec jekyll serve` from inside the docs folder.
2022-07-01 11:06:16 -04:00
Russell Hancox
7df209ed3f Project: Upgrade bazel rules_apple to 1.0.1 release (#830) 2022-06-28 14:23:47 -04:00
np5
b7421e4499 Add team ID to synced events (#827) 2022-06-24 20:00:55 +00:00
Eric Case
e044fe3601 Readme: http -> https link (#829) 2022-06-24 14:34:32 -04:00
Russell Hancox
a67801d5ed santactl/status: Remove driver connected, re-org USB blocking status (#826) 2022-06-22 14:59:46 -04:00
Russell Hancox
3d37a3a5ae santad: Update assert usage to avoid a string-to-bool conversion (#825) 2022-06-22 12:55:57 -04:00
Russell Hancox
bfae5dc828 fix some style issues (#824) 2022-06-22 10:41:05 -04:00
Pete Markowsky
fde5f52a11 Added handling for Remount events to USB mass storage blocking (#818)
* Added handling for Remount events to USB mass storage blocking.
2022-06-22 09:39:20 -04:00
Russell Hancox
01bd1bfdca santad: Use multiple semaphores to avoid freeing ES message before use of it has ended. (#822)
This slightly complex solution is necessary because while on macOS 11+ there are retain/release methods that can be used on ES messages, on 10.15 the only option is a copy which is comparatively expensive (and on 11+ the copy/free methods are aliases for retain/release)

Fixes #821
2022-06-08 11:21:40 -04:00
Matt W
ae13900676 Mute self to reduce message volume. Remove noisy log message. (#820)
* Mute self to reduce message volume. Remove noisy log message.

* Bail if self muting failed. Remove selfPid.

* Fix tests by mocking es_mute_process
2022-05-31 11:36:35 -04:00
Matt W
a65c91874b Copy new PrinterProxy file instead of overwriting (#819)
* Copy new PrinterProxy file instead of overwriting

* Update log type for error message

* Update log message severity
2022-05-27 13:08:25 -04:00
Matt W
6a3fda069c Remove unused testing scripts (#816)
* Remvoe unused testing scripts

Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>
2022-05-27 11:03:10 -04:00
Khalid Jamal Abdulnasser
4d34099142 santad: log decision when failing to read file (#817) 2022-05-27 09:52:06 -04:00
Russell Hancox
e639574973 Project: Fix layering for tests (#813) 2022-05-12 16:52:11 -04:00
Russell Hancox
636f9ea873 Project: Layering, missed a dependency (#812) 2022-05-12 14:49:18 -04:00
Russell Hancox
9099409915 Project: Enable layering check, fix all dependency violations (#811) 2022-05-12 14:26:08 -04:00
Russell Hancox
976f483a99 syncservice: Fix SNTSyncTest (#810)
Failing preflight early if the daemonConn doesn't return a response the tests. This fix is a bit awkward, I tried to add the defaults in setUp but then you can't overwrite the stubs in methods that need to do it
2022-05-12 09:54:00 -04:00
Tom Burgin
8a32b7a56b preflight sync: fix dispatch_group_wait return polarity (#809)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-11 14:55:42 -04:00
Tom Burgin
7eeb06b406 preflight sync: stop the sync if we cannot communicate with the daemon (#808)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-11 18:45:58 +00:00
Tom Burgin
4540a1c656 SNTConfigurator: remove mutability from sync state dict (#807)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-11 14:35:43 -04:00
Russell Hancox
acc7b32b24 GUI: Switch to UserNotification.framework notifications (#806) 2022-05-11 12:32:08 -04:00
Russell Hancox
b92d513f5d GUI: Fix message queuing (#805) 2022-05-11 09:59:38 -04:00
Tom Burgin
3458fccd4e santasyncservice: handle loading and unloading of the service in the pkg (#804)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-10 14:59:34 -04:00
Russell Hancox
fdfb00368c GUI: Update keys for EventDetailURL. (#802)
The previous change here (#797) was not backward compatible and would be difficult to roll out. This change restores the previously used key and adds 2 new ones for migration. The previous key is marked deprecated and will be removed in the future.
2022-05-09 13:46:13 -04:00
Tom Burgin
6bd369cfb2 santad: remove sema from sync service queue (#803)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-09 13:32:28 -04:00
Pete Markowsky
0df26c6214 Fix ES Mock Client Subscription issues (#801)
Fixes an issue with the ES mock where it was deleting all clients on an unsubscribe.
2022-05-06 14:34:42 -04:00
Russell Hancox
6e22da1d97 santad: Add 'null' event logger. Fixes #754 (#799) 2022-05-06 12:22:04 -04:00
Russell Hancox
1725809335 Add config to allow uploading all events (#800)
* Add config to allow uploading all events

This config can be enabled locally or by a sync server and causes the
client to upload all events, not just those for binaries that are or
would be blocked.

Fixes #689
2022-05-06 11:45:53 -04:00
Pete Markowsky
3eff49feda Added macos-12 to the build matrix. (#798) 2022-05-03 21:14:15 -04:00
Pete Markowsky
5caedebb06 Created a profiles package so provisioning profiles only need to be in one place. (#794) 2022-05-03 17:14:02 -04:00
Russell Hancox
d823028b72 Sync: Add option to enable event upload despite clean sync. (#796)
Related to #789
2022-05-03 15:15:42 -04:00
Russell Hancox
49b2d6e22a GUI: Add %bundle_or_file_sha% translation key (#797)
* GUI: Add %bundle_or_file_sha% translation key

This mimics the current behavior that %file_sha% previously had and
moves %file_sha% to the expected behavior or just showing the file's
SHA.

Related to #795
2022-05-03 14:59:01 -04:00
Russell Hancox
4236d57e96 Project: Update packaging script to do tarball creation in a scratch dir (#793) 2022-04-28 15:25:48 -04:00
Russell Hancox
36d463a1dc Project: Include syncservice.plist in release builds and loads (#792) 2022-04-28 14:42:19 -04:00
Tom Burgin
adbafd6bab syncservice: sign and package (#790)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-04-28 13:31:20 -04:00
Tom Burgin
b5ebe1259c syncservice: implementation and migration (#775)
* review updates

* fix test

* review updates

* log level cleanup

Co-authored-by: Tom Burgin <bur@chromium.org>
Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>
2022-04-27 14:54:56 -04:00
Ryan Diers
e0ae0f481b santa/gui: Update buttons to push style to better stand out (#788) 2022-04-19 20:08:35 -04:00
Matt W
8037c79fc0 Populate critical paths from the ES default mute set (#786)
* Populate critical paths from the ES default mute set

* Attempt to fix build on older macos

* Link ES to build SNTRuleTableTest

* Workflow test

* Use preprocessor macros to support building on older SDKs

* Add API availability
2022-04-18 15:11:43 -04:00
Walter Lee
892d303de1 Disable layering check for Objective-C, part two (#787) 2022-04-18 12:15:08 -04:00
Russell Hancox
ff3979263e santad: Use TTY path provided by ES (#785) 2022-04-15 12:48:06 -04:00
np5
01afefd3d4 santactl/sync: Fix event team ID decision value (#784) 2022-04-15 10:27:48 -04:00
Kent Ma
830627e7bc Docs: Add "Team ID" to description on AllowedPathRegex (#782) 2022-04-14 13:13:51 -04:00
Walter Lee
601d726fcc Disable layering check for Objective-C (#781) 2022-04-12 09:06:55 -04:00
Tom Burgin
0be1ca0199 ES_EVENT_TYPE_NOTIFY_UNMOUNT: flush the cache off the ES handler thread (#778)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-04-06 12:07:08 -04:00
Kent Ma
8602593149 Fix dead link (#774) 2022-03-25 13:33:08 -04:00
Matt W
9bca601ce6 Modified build target names for santa proto (#772) 2022-03-25 13:07:57 -04:00
Kent Ma
c73acd59d4 Update logo image of Santa (#773) 2022-03-25 12:46:34 -04:00
Russell Hancox
3c334e8882 Project: Fix coverage collection (#770) 2022-03-24 11:33:46 -04:00
Russell Hancox
5f811cadf8 Project: Update apple_rules dep, add .bazelversion for bazelisk users (#769) 2022-03-23 17:34:04 -04:00
Russell Hancox
4252475de0 Project: Fix fallback version (#767) 2022-03-23 15:13:30 -04:00
Kent Ma
45f1822681 Exclude bazel-out from test coverage generation (#768) 2022-03-23 15:10:46 -04:00
Russell Hancox
498a23d907 Project: Make versioning dynamic through bazel's --embed-label. (#766)
The apple_rules allow versioning using an apple_bundle_version rule that extracts elements from an embedded label. We haven't been able to use this until now because the kernel extension needed access to the version in a define.
2022-03-23 14:53:51 -04:00
Russell Hancox
5dff8a18f4 santad: Split ES cache into root/non-root varieties (#765) 2022-03-23 09:43:14 -04:00
Russell Hancox
676c02626d santactl/metrics: Allow filtering metrics (#763) 2022-03-22 18:12:14 +00:00
Russell Hancox
64950d0a99 Project: Show test errors in output from CI (#764) 2022-03-22 11:39:01 -04:00
Kent Ma
16f74cb85c Remove references to santa-driver and the kernel extension from parts of the docs (#762) 2022-03-21 11:33:45 -04:00
Russell Hancox
aadc961429 santad: Clear caches when disks are unmounted. (#760)
This restores behavior that was recently removed
2022-03-18 13:38:35 -04:00
Russell Hancox
be66fd92f4 santactl/status: Re-org output in status re: USB Blocking. (#759) 2022-03-18 09:57:34 -04:00
Russell Hancox
feea349f25 Project: Remove kext signing/packaging (#755) 2022-03-16 17:08:59 -04:00
Kent Ma
1c04c3a257 Remove code guarded by #ifdef kernel macros (#752)
* Remove code guarded by #ifdef kernel macros
2022-03-15 14:38:40 -04:00
np5
818d3f645f santactl/sync: Add model identifier to preflight request (#751) 2022-03-15 14:24:05 +00:00
Pete Markowsky
15d6bb1f14 Made santad an early boot client to prevent racing other pids. (#750)
Make santad an early boot Endpoint Security Framework Client.
2022-03-15 10:16:40 -04:00
Kent Ma
211dbd123f Remove the Santa kernel extension. (#749)
This includes:

* All of the code in Source/santa_driver containing the kernel extension
* The SNTDriverManager event provider
* All workflows in our CI related to testing if the driver builds
* Installation of the driver in install.sh. Note that code uninstalling existing instances of the driver is still intentionally kept present.
* Kernel extension-specific build rules
* Renames SNTKernelCommon to SNTCommon
* Driver version output from santactl version
* The [SNTConfigurator EnableSystemExtension] configuration key
2022-03-14 18:17:02 -04:00
Matt W
c67364fe76 Protobuf support, maildir format logging (#731)
* Initial protobuf support, maildir logging

Fix build issues in the integration test

Deduped some test code

Formatting

Address feedback from draft PR

Removed legacy labels. Updated docs.

Add in metrics. Fix protobuf logging test.

* Now use the Any proto for the LogBatch wrapper

* Changes based on PR feedback

* Added gauge metrics for spool dir

* Formatting

* Add event time to proto

* Fix build issue after rebase

* Update BUILD rules

* Updated language around protobuf logging to mark as beta
2022-03-14 15:46:52 -04:00
Pete Markowsky
2043983f69 Fix typo in SNTDeviceManager tests & ensure they run in the CI. (#746) 2022-03-14 12:57:07 -04:00
Russell Hancox
2f408936a0 Project: Disable bazel layering_check feature for most rules (#742) 2022-03-10 10:07:15 -05:00
Russell Hancox
02c1d0f267 Project: Bump version to 2022.3 (#745) 2022-03-10 09:35:44 -05:00
Pete Markowsky
4728c346cc Fix uninstall.sh to remove the metric & bundle services. (#743) 2022-03-09 18:00:45 -05:00
Pete Markowsky
9588dd8a0e Fix: Issue with SNTMetricHTTPWriter Timeouts (#741)
Fix issue with santametricservice timing out due to incorrect timeout argument.
2022-03-08 14:12:57 -05:00
Russell Hancox
e3e48aed1b Packaging: Keep package versions simple (#737) 2022-03-02 10:36:39 -05:00
Russell Hancox
e60f9cf6c5 Project: Add build version (#736) 2022-02-28 14:18:18 -05:00
Kent Ma
c7e309ccb1 Add a USB device blocking popup. (#728)
* Add a USB device blocking popup.
* Refactor SNTNotificationManager and SNTMessageWindowController to make
  for generalized notification logic
* Add the configuration keys for custom block messages and resize window
2022-02-28 13:30:56 -05:00
Russell Hancox
ad8aafbd07 Project: Bump version to 2022.2 (#734) 2022-02-17 11:36:21 -05:00
Russell Hancox
9e671c3dee Project: Add arm64 to hostArchitectures for productbuild (#733)
This should avoid prompts to install Rosetta when installing the .pkg
2022-02-16 20:02:12 -05:00
Russell Hancox
d97abe36f2 santad: Fix fail open tests in SNTExecutionControllerTest (#730) 2022-02-14 20:13:08 -05:00
Pete Markowsky
faa8946056 Fix: remediate a crash in santametricservice (#729)
* Fix issue with task cancelation.

* Make export timeouts configurable.

This allows an export timeout to be set via configuration and eases testing.
2022-02-14 13:51:29 -05:00
Kent Ma
8b2b1f0bfc Report USB blocking status with santactl status (#727)
* Report USB blocking status with santactl status
2022-02-10 16:02:40 -05:00
Kent Ma
16678cd5a0 Update version of bazel rules_apple to fix broken 12.3 builds (#726) 2022-02-10 14:32:43 -05:00
Matt W
0bd6a199a3 Fix additional strlcpy issue, simplify call paths (#723)
* Fix additional strlcpy issue, simplify call paths

* Remove unused interface from header
2022-02-03 12:53:22 -05:00
Russell Hancox
58e2b7e1b8 santad: Add fail-closed mode (#722) 2022-01-28 18:29:18 -05:00
Tom Burgin
b824a8e3e0 santad: only store events if there is a sync server configured (#721)
* santad: only store events if there is a sync server configured

* SNTExecutionControllerTest stub sync server

Co-authored-by: Tom Burgin <bur@chromium.org>
2022-01-27 15:55:51 -05:00
Kent Ma
25bf2a93e4 Add DiskArbitrationTestUtil to shim out DiskArbitration for unit testing (#720) 2022-01-25 13:45:03 -05:00
Russell Hancox
f1ea1b369f santactl/fileinfo: Switch certIndex to an NSNumber (#719)
* santactl/fileinfo: Switch certIndex to an NSNumber
2022-01-25 12:50:04 -05:00
Tom Burgin
5503a88308 rule download: return early on daemon timeout (#718)
* rule download: return early on daemon timeout

* wording update

Co-authored-by: Tom Burgin <bur@chromium.org>
2022-01-21 17:19:44 +00:00
Kent Ma
8cf0f8217d Add clang_analyzer generation (#717)
Fix warnings for unused variables. The other warnings are more
nontrivial & require some light refactoring to fix, and will come in a followup PR.
2022-01-21 17:14:44 +00:00
Russell Hancox
22799ffc2a Conf: Delete and clean-up ASL conf, enable signaling on newsyslog.conf. (#716)
* Conf: Delete and clean-up ASL conf, enable signaling on newsyslog.conf.

The ASL config is a remnant from when Santa did all logging via ASL before Apple deprecated ASL and replaced it with ULS, which doesn't allow redirecting messages to a file. The old config wasn't causing any problems except that it was handling battling newsyslog for rotation and had different parameters.

The signal change in the newsyslog.conf causes newsyslog to fallback on sending a (harmless) SIGHUP to syslogd, which has no effect on Santa except it also triggers a 10s sleep inside newsyslog between renaming the old file and beginning the compression, which is plenty of time for santad to notice the rename and start writing new logs to the newer file.
2022-01-19 11:29:39 -05:00
Pete Markowsky
cb61d0cc99 Create test suites for each component (#702)
Create test suites for each component.
2022-01-18 17:00:44 -05:00
Pete Markowsky
fb7447ceba Fix off-by one error in strlcpy. (#715) 2022-01-18 15:31:30 -05:00
Russell Hancox
45e51e9c09 santactl/fileinfo: Clarify valid index for cert-index (#714) 2022-01-13 14:35:30 -05:00
Russell Hancox
b0f0cdd4e6 santactl/fileinfo: Update --cert-index usage (#713)
* santactl/fileinfo: Update --cert-index usage.

Fixes #710
2022-01-13 13:04:38 -05:00
Kent Ma
65090d3ef2 Support rule downloading of Team ID rules (#709)
* Support syncing Team ID rules and using 'identifier' instead of 'sha256' in sync rules
2022-01-13 10:55:14 -05:00
Russell Hancox
9c80f79d82 Sync: Allow configuring proxies (#708)
* Sync: Allow configuring proxies

Fixes #672
2022-01-13 15:04:11 +00:00
Kent Ma
93adaea81e Add clang annotation for fallthrough (#712) 2022-01-12 13:56:47 -05:00
Russell Hancox
a125b340a5 santad: Don't use proc_pidpath when using ES (#707) 2022-01-11 20:32:29 -05:00
Kent Ma
fbd0de3d48 Add test coverage for syncing USB mounting options (#711) 2022-01-11 17:13:37 -05:00
Russell Hancox
6f2ae62bce Project: Explicitly set calendar on ISO8601 dates (#706) 2022-01-06 09:33:04 -05:00
Christopher Sauer
da29b20473 Update hedron_compile_commands (#704) 2021-12-30 07:59:35 -05:00
Kent Ma
197109a8ee USB mass storage blocking and remounting (#685)
* USB mass storage blocking.

* Add the sync service and config key for enabling mass USB storage blocking
* Update docs with the sync service key
* Add ability to forcibly remount USBs with different flags
* update EndpointSecurityTestUtil and tests that use it to properly handle multiple ES clients
2021-12-16 13:38:48 -05:00
Kent Ma
91f3168c7a Update santactl rule text to have accurate text for team IDs (#701) 2021-12-14 11:42:53 -05:00
Russell Hancox
a00ec41518 Project: Bump version to 2022.1 (#700) 2021-12-13 13:28:16 -05:00
Russell Hancox
c32248aaf7 santad: Fix PrinterProxy workaround for Monterey+ (#698) 2021-12-13 15:24:58 +00:00
Pete Markowsky
afd97bdf3e Removed the check for export metrics in santad. (#697)
Remove the check for export metrics in santad

Metrics are always collected but only exported to a monitoring system when all of the necessary config options are set. Since they're always collected santactl metrics should always return metrics data.
2021-12-13 10:23:05 -05:00
Tom Burgin
73c4875b1f santasyncservice: move sync code to the santasyncservice dir (#696)
* sync: move sync code from santactl dir to santasyncservice dir

* clang-format

* fix tests

* s/SNTCommandSync/SNTSync

* s/SNTCommandSync/SNTSync on content
2021-12-08 18:11:56 -05:00
Pete Markowsky
916fc8c0e6 Add a simple event counter to SNTExecutionController (#694)
Add a simple event counter for events per response.
2021-12-08 17:35:37 -05:00
Kent Ma
e59e6105f3 Update the Santa version number to 2021.9 (#695) 2021-12-08 17:01:43 -05:00
Pete Markowsky
216ac811eb Fix issue with reregistering metrics. (#693) 2021-12-07 15:32:15 -05:00
Pete Markowsky
48f92f5913 Ignore VSCode directories (#692) 2021-12-07 14:23:13 -05:00
Russell Hancox
6bb08d0490 Project: Add bazel commands extractor for VSCode integration (#690) 2021-12-06 13:39:23 -05:00
Pete Markowsky
82b71c0f20 Add a metrics command to santactl (#687)
Add a metrics command to santactl.
2021-12-02 14:30:39 -05:00
Allister Banks
10ccee9e4c Docs: EnableSysxCache docs, etc (#684)
* Add more Conf references, EnableSysxCache key, etc
* Updated link (even though previous config profile explainer link redirects accordingly) to profile spec.
* Added brief explanation of TCC/PPPC and made reference to the non-setting example MDM mobileconfig files in the repo
* Add sysext log stream example, update wording
* Pointed at events and configuration pages for details about logging
* New troubleshooting section
* Standardized on asterisks for page link markup in the TOC index page
2021-11-22 22:22:49 -05:00
Pete Markowsky
acbbb9e7b0 Add a configuration option for users to add their own root labels (#683)
Add an option for users to add their own root labels.

Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>
2021-11-19 10:34:18 -05:00
Kent Ma
3939ad9813 Add santametricservice information to santactl status (#679) 2021-11-16 16:04:59 -05:00
Kent Ma
d20455252d Update santactl fileinfo, sync, and status to show teamID info (#678)
* Update santactl fileinfo, sync, and status to show teamID info
2021-11-16 14:57:02 -05:00
Pete Markowsky
5cd901034f Fixed up typo related to hostname vs. host_name (#676)
Fixed up typo related to hostname vs. host_name.
2021-11-15 15:28:41 -05:00
Kent Ma
4e82392370 Update cli flag for --teamid in santactl (#675) 2021-11-11 15:56:18 +00:00
np5
19710f7233 Do not store Allow TeamID events in the database (#674) 2021-11-11 10:44:39 -05:00
Russell Hancox
27e32bd9ff Tests: Update SNTMetricHTTPWriterTest (#673) 2021-11-11 08:59:14 -05:00
Kent Ma
c268ad4f9a Change SNTEventLog to be a singleton emit a singleton Logger object (#670)
* Change SNTEventLog to be a singleton emit a singleton Logger object
2021-11-10 17:23:01 -05:00
Russell Hancox
f7a1a4cb39 Tests: Fix MetricServiceTest compatible with public OCMock (#669) 2021-11-08 15:53:57 -05:00
Russell Hancox
ad6e03e6cc Tests: Stop using NSInvocation with OCMock's .andDo() (#667) 2021-11-08 12:19:20 -05:00
Russell Hancox
8ecc3f879a Tests: Fix some flaky tests. (#666)
1. OCMock objects don't need stopMocking to be called - it's only necessary to call that in cases where the original object behavior must be restored before the end of the test. Otherwise the mock automatically restores during deallocation.
2. SNTMetricRawJSONFormat still used a plain NSDateFormatter and so was applying timezone calculations. In tests we've switched to using NSISO8601DateFormatter but this requires 10.13 and our deployment target is still 10.9 so I've stuck to applying the UTC timezone to the formatter instead.
2021-11-05 18:03:57 -04:00
Pete Markowsky
d51093501c Fix Flaky Execution Controller Tests (#665)
* Fix up some issues with flaky tests.
2021-11-05 13:51:04 -04:00
np5
05dd1b6215 Add AboutText option for the Santa.app (#662) 2021-11-04 22:02:23 -04:00
Pete Markowsky
8c3320e3e9 Change NSDateFormatter to NSISO8601DateFormatter (#661)
Change NSDateFormatter to NSISO8601DateFormatter.
2021-11-02 13:11:51 -04:00
Tom Burgin
369dc9a63c Add KVO binding for EnableBadSignatureProtection (#659) 2021-10-28 17:34:56 -04:00
Pete Markowsky
7adc55007c Change to NSISO8601DateFormatter to ensure UTC timestamps in unit tests (#658)
Change to NSISO8601DateFormatter to ensure UTC timestamps in unit tests.
2021-10-28 15:34:31 -04:00
Edward Marczak
fe6be921d3 Add EnableBadSignatureProtection key (#656)
Add EnableBadSignatureProtection key and description into the configuration.md doc.
2021-10-28 10:02:24 -04:00
Pete Markowsky
23b31ec413 Add build matrix for build / test steps to shake out OS nuances (#654)
Add build matrix for build / test steps to shake out OS nuances.

Remove macos-latest from build matrix.
2021-10-26 16:14:24 -04:00
Pete Markowsky
727b009a1c Fixed one set of tests. (#652) 2021-10-26 15:36:54 -04:00
Pete Markowsky
1c42f06135 Add Metrics and Metrics Service to Santa (#641)
Add santametricservice and basic metrics to Santad.

This PR adds the santametricservice, and adds basic metrics to santad.  It also updates the SNTMetricSet to have and updates packaging scripts to include the santametricservice (aka metric service) in the final bundle.

Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>
2021-10-26 09:25:10 -04:00
Kent Ma
e1cf8e70a3 Add continuous workflow job for checking for flakes (#650)
Co-authored-by: Pete Markowsky <pmarkowsky@users.noreply.github.com>
2021-10-25 10:23:42 -04:00
Russell Hancox
7a500b8135 Packaging: Fix syntax error in package_and_sign.sh (#651) 2021-10-22 09:15:20 -04:00
Pete Markowsky
3702af0309 Add description to SNTMetricSet and Fix issues with SNTMetricMonarchJSONFormat (#649)
* Added description to SNTMetricSet and fixed typos in SNTMonarchJSONFormat.
2021-10-21 16:41:24 -04:00
Russell Hancox
697cd29a0a Project: Include package files in release tarball (#648) 2021-10-20 21:31:33 +00:00
Kent Ma
5735a12424 Update list of critical system binaries and include comment about Monterrey behavior (#647) 2021-10-20 16:45:42 -04:00
Russell Hancox
07b8f2121d Project: Include new packaging files in release tarball (#646) 2021-10-20 12:54:00 -04:00
Russell Hancox
78a1a929fd Project: Check-in packaging and signing script. (#645)
This is largely a copy of what we've been using so far but with previously hardcoded stuff replaced with environment variables.
2021-10-20 11:47:30 -04:00
Russell Hancox
9163417b54 santad: enable sysx cache by default (#644)
We've had this enabled long enough now to know that it works correctly and helps performance considerably, so let's have it on by default.
2021-10-18 18:17:11 -04:00
Kent Ma
fa6630a31a Rename shasum to identifier in database (#643) 2021-10-18 13:27:36 -04:00
Kent Ma
1f2b82fc58 Allow banning of team IDs. (#640) 2021-10-18 09:52:56 -04:00
Kent Ma
b77b0142af Add microbenchmark for execs on SNTApplication (#639) 2021-10-15 15:57:04 -04:00
Russell Hancox
2f80a42845 Project: Build driver if files in Source/common/* change (#637) 2021-10-15 15:03:16 +00:00
Russell Hancox
67db370492 Common/Kernel: Add some missing defines to libs included in driver (#638) 2021-10-14 13:05:33 -04:00
Russell Hancox
a0319ecf52 Project: Bump to 2021.8 (#636)
Co-authored-by: Kent Ma <tnek@google.com>
2021-10-13 14:37:44 -04:00
Pete Markowsky
16d0bd6db6 Add Support for Formatting metrics for Monarch (#633)
* Initial commit of a Format that converts SNTMetricSet dictionaries to a format consumable by Monarch tooling.

Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>
2021-10-11 16:01:50 -04:00
Kent Ma
9e3943ec68 Add error on lint failure and include a fix.sh (#632)
Add error on lint failure, include a fix.sh, and fix existing linter errors.
2021-10-11 11:33:10 -04:00
Kent Ma
e461b4bfbc Use direct path in integration_tests.sh instead of relative path (#631) 2021-10-07 13:07:14 -04:00
Russell Hancox
8f836afe86 * Project: Update README and docs/details/santactl (#630)
Re-organized some sections, removed some obsolete statements, fixed a few links.
2021-10-06 17:12:53 -04:00
Russell Hancox
04ad1c34ba Project: Update entitlements files (#629) 2021-10-06 11:36:58 -04:00
Pete Markowsky
c3042e21dc Add a workflow for checking links in Markdown files. (#628) 2021-10-05 19:28:03 -04:00
Russell Hancox
3ede20a121 Project: Fix issues link in README (#626) 2021-10-05 15:49:39 -04:00
Russell Hancox
976118cce4 santactl/sync: Fix tests for santactl/sync (#625)
This test has been around since early 2016 but has been un-runnable since early 2019.
2021-10-05 13:17:50 -04:00
Pete Markowsky
ea85f0f539 Initial commit of an HTTP writer for SNTMetricSets (#624)
* Initial commit of an HTTP writer for SNTMetricSets.

This PR adds support for shipping serialized SNTMetricSets to an HTTP server via POSTs.

Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>
2021-10-04 19:49:40 -04:00
Russell Hancox
d193b05057 Tests: ensure SNTPrefixTree test finishes executing at appropriate time (#623) 2021-10-04 15:41:14 -04:00
Russell Hancox
9fb4f2e171 README: sync servers; remove upvote, sort the rest alphabetically. (#622) 2021-10-04 11:48:13 -04:00
Kent Ma
58cec5819a Add linter step with clang-format and buildifier. (#620)
Also lint our files accordingly
2021-10-01 16:51:06 -04:00
Kent Ma
6ba5831f2d Run buildifier (#619) 2021-10-01 15:18:33 -04:00
Kent Ma
a22e3ead83 Add regular execution integration tests (#618) 2021-10-01 15:07:56 -04:00
Kent Ma
2611b551ce Add provisioningprofile for santactl so that it's properly signed (#617) 2021-10-01 13:00:12 -04:00
Kent Ma
023f96f5c8 Detect existence of a provisionprofile and use that instead in build_and_sign.sh (#616) 2021-10-01 10:07:54 -04:00
Kent Ma
1523d58429 Remove use of entitlements field for the santad build rule (#615)
* Remove use of entitlements field for santad

* Create a local keychain instead of using the system keychain and drop sudo from most of the build stages
2021-09-28 12:48:09 -04:00
Kent Ma
81049db170 Deflake SNTApplicationTest by tracking subscriptions to specific event types (#614)
* Switch to waitForExpectations in tests

* Change mock es_subscribe to note specific events we're ready for
2021-09-27 10:40:15 -04:00
Russell Hancox
c110245701 Project: fix exporting of SantaCache header (#612) 2021-09-23 12:32:41 -04:00
Russell Hancox
d7a56b9bd4 Project: fix some BUILD file lint (#611) 2021-09-23 12:03:23 -04:00
Russell Hancox
4bb5804a6f santactl/sync: Catch rare crash in FCM parsing (#609) 2021-09-23 10:56:57 -04:00
Russell Hancox
e68fb7235a Metric: Fix formatting of SNTMetricFormatTestHelper (#608) 2021-09-23 06:53:06 -04:00
Pete Markowsky
f93e7ef879 Refactored metric service tests to use a common helper. (#607)
This refactors the SNTFormat tests to use the SNTMetricSet to generate the
test data. This keeps the metric service and the SNTMetricSet in sync and
reduces repeated data.
2021-09-22 16:56:57 -04:00
Kent Ma
f472f4821c Create block builder for ES Messages and clean up tests (#606)
Create block builder for ES Messages and clean up tests
2021-09-22 15:48:24 -04:00
Pete Markowsky
1c97761038 Initial commit of santametricservice. (#605)
Initial commit of santametricservice.

The santametricservice is an XPC helper service to write metrics. It consists of Formatters and Writers. This initial commit only has support for the rawJSON format and writing to a file.

This is a new daemon to be included. Docs and packaging will be updated in future PRs.

Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>
2021-09-22 14:49:30 -04:00
Pete Markowsky
e569a684b7 Add initial configuration options for barebones metric service (#604)
Add initial configuration options for barebones metric service.
2021-09-21 13:36:02 -04:00
Pete Markowsky
66c32dc526 Added an XPC interface for the Metric service. (#603) 2021-09-20 18:14:39 -04:00
Kent Ma
075d3cbc11 Include an integration test setup and fixture with moroz (#602) 2021-09-20 11:35:12 -04:00
Adam Sindelar
340326df8a Remove the hiring banner (#600) 2021-09-10 08:09:06 -04:00
Kent Ma
f52edd2a76 Explicitly include the TeamIdentifierPrefix to santad's entitlement (#599) 2021-09-09 11:24:49 -04:00
Kent Ma
11c247e33a Add entitlements to the santad and Santa build rules (#598) 2021-09-09 09:36:42 -04:00
Ryan Diers
a859b9b341 Docs: Add Rudolph to sync server list (#597) 2021-09-08 19:41:27 -04:00
Kent Ma
c190f1f52d Add tulsi project (#596) 2021-09-08 14:50:57 -04:00
Pete Markowsky
87dc191494 Rewrote ci.yml workflow to parallelize steps. (#593)
Build and test steps were taking more than 5 minutes to run so this cleans up
the workflow and parallelizes it.
2021-09-02 17:21:36 -04:00
Pete Markowsky
3a19591822 Made CI build and test steps conditional. (#592)
This changes the workflow steps to execute conditionally e.g. if you only modify
documentation it won't run the build, test, and coverage steps.
2021-09-01 16:47:19 -04:00
Kent Ma
b225c0740e Raise the timeout delay for tests (#591) 2021-08-25 16:46:26 -04:00
Russell Hancox
d1fffb4636 README: Remove ReadTheDocs link (#589) 2021-08-25 12:50:07 -04:00
Kent Ma
9d7ca62e46 Pin to 0a2c39c020 (#588)
This allows us to run tests on ARM.
2021-08-19 12:01:45 -04:00
Kent Ma
2a6073a9a1 Upgrade bazel rules_apple to 0.31.3 (#587) 2021-08-18 17:04:49 -04:00
Russell Hancox
296f06582b Project: delete obsolete ReadTheDocs config (#586)
We haven't used ReadTheDocs for >6m
2021-08-18 15:40:34 -04:00
Russell Hancox
0e27dab4c6 Project: remove xcode projects, they're stale (#584) 2021-08-18 15:05:55 -04:00
Russell Hancox
256836d7f8 Docs: Switch themes, update config, fix redirect (#583) 2021-08-18 10:58:12 -04:00
Russell Hancox
b117d8106e Project: Update deps in WORKSPACE to be reproducible. (#582)
This removes a lot of warnings when starting in a clean workspace
2021-08-17 17:20:30 -04:00
Russell Hancox
c980223215 Project: remove py2 forcing, drop deprecation warnings back to warning (#581) 2021-08-17 16:59:27 -04:00
Russell Hancox
635b33ebf9 Project: Update coverage link in README (#580) 2021-08-17 15:53:07 -04:00
Kent Ma
b6f35c9b9f Add actual test binaries to the repo (#579) 2021-08-17 15:02:14 -04:00
Kent Ma
796109cc60 Adding cert rules and testdata (#578) 2021-08-17 14:26:09 -04:00
Russell Hancox
38f580de72 CI: Re-org CI workflows, ignore /Applications in coverage (#577) 2021-08-17 14:08:02 -04:00
Kent Ma
c7a58c77e7 Add missing ES Auth response to AUTH_RENAME (#576)
* Add missing ES Auth response to AUTH_RENAME
* Added unit test cases for benign paths
2021-08-17 12:10:43 -04:00
Kent Ma
9a4fe782d7 Bump version -> 2021.7 (#575) 2021-08-16 16:04:24 -04:00
Kent Ma
fbb5f3728f Include license in BUILD file (#574) 2021-08-16 12:55:01 -04:00
Pete Markowsky
24b96c4798 Added types for recording and storing performance metrics. (#567)
* Added types for recording and storing performance metrics.

This adds SNTMetricSet and various gauge and counter types to allow for
exporting metrics such as CPU,Memory usage and other properties that are useful
for tracking reliability and debugging.

This is the first commit of a series to add support for monitoring systems.

Co-authored-by: Kent Ma <tnek@google.com>
2021-08-13 13:26:45 -04:00
Kent Ma
1edf6d9200 Enable -Werror -Wall on our build rules (#572)
* Reorder init lists for -Wreorder-init-lists

* Add nullability annotations to the rest of EndpointSecurityTestUtil

* Added fake uses for -Wunused-variable

* Corrected signed/unsigned int conversions in SNTPrefixTree

* Explicitly convert implicit conversions in Santacache

* Set bazelrc to -Werror -Wall
2021-08-13 11:31:41 -04:00
Kent Ma
ac1f8ea1b8 Add an extra return on the rename case (#570) 2021-08-12 13:48:38 -04:00
Kent Ma
9923f601b6 Prevent Overwrite of Santa Databases (#569) 2021-08-12 10:38:27 -04:00
Kent Ma
471ae89406 Switch the CI build steps to run all unit tests and coverage first (#568) 2021-08-11 14:03:05 -04:00
Kent Ma
54d6653973 Include SNTEndpointSecurityManagerTest in the main test_suite (#566)
* Include SNTEndpointSecurityManagerTest in the main test_suite and clean it up.

This commit (1) adds es_unsubscribe and es_delete_client to our ESF shim
to fix the test segfaulting, and (2) cleans up the unit tests themselves by
breaking out the timeout test from the regular unlink test
2021-08-11 11:42:55 -04:00
Kent Ma
27ee66597b Correctly calculate coverage by calling blaze coverage on individual tests (#565) 2021-08-11 09:17:27 -04:00
Kent Ma
10f2d852f5 Add functional test for executing a binary on SNTApplication. (#562)
This adds a full functional test for starting up an SNTApplication
(with as few mocks as possible) and executing it with a directly
recorded & collected EndpointSecurity event.

This also fixes a potential race condition and segfault on Santa startup: due
to es_subscribe being called first, it's possible for an es event to arrive
before listenForDecisionRequests or listenForLogRequests are called,
causing the SNTEndpointSecurityManager callbacks to call a nil pointer.
2021-08-10 14:42:21 -04:00
Kent Ma
1fcb63dc92 Add coveralls CI rule and lcov generator (#564)
Adding coveralls so that test coverage can be easily displayed and checked
2021-08-10 14:41:14 -04:00
Kent Ma
7944f681f8 Test util library for mocking EndpointSecurity. (#560)
* Test util library for mocking EndpointSecurity.
2021-07-14 10:49:10 -04:00
Tom Burgin
e3aedc92ba bump version (#559) 2021-06-16 13:20:10 -04:00
Kent Ma
d2b6c2b6c2 Update .clang-format and apply to existing files. (#558) 2021-06-15 09:47:31 -04:00
Tom Burgin
d026989dfb santactl: remove extra check (#557) 2021-06-11 15:43:08 -04:00
Tom Burgin
e7a8e9b6ac santactl: use host instead of http address for reachability (#556) 2021-06-11 15:13:50 -04:00
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
Tom Burgin
d4ba4b082f codesign check: verify all architectures (#239)
* fileinfo rule: don't use certs that have codesigning errors

* pods: MOLCodesignChecker --> 1.8
2018-02-22 14:41:47 -05:00
Tom Burgin
cce43829eb use MOLFCMClient v1.5 (#238) 2018-02-16 14:35:52 -05:00
johnl
c1bfbac2fe Various small fixes to README.md (#237)
* Various small fixes to README.md

* Apply changes
2018-02-13 11:06:28 -05:00
Tom Burgin
fc87cde668 config: use KVO (#234)
* config: atomically update config

* config: add an explanation for sleep usage

* config: use mobileconfig in the getters

* config: cleanup file watcher

* config: spell

* config: clear or reload sync state on sync base url change

* config: Use KVO and Dependent Keys

* config: remove debug log

* config: review updates

* config: update rule sync getter and setter names

* config: get logical
2018-02-07 13:59:00 -05:00
Tom Burgin
400c413029 config: add option to disable mode change notifications (#235)
* config: add option to disable mode change notifications

* config: don't do extra work

* config: handle none, default and custom

* config: cleaner
2018-02-02 12:01:51 -05:00
Tom Burgin
0e6eb45732 santa-driver: add an acknowledge feature to allow timeouts (#220)
* santa-driver: Add an acknowledge feature to allow timeouts for lost requests

* project: cocoapods 1.3.1 update

* review updates
2018-01-26 11:33:54 -05:00
Tom Burgin
7ca2028c19 santabs: don't try to lookup nil bundle paths (#233) 2018-01-26 11:33:04 -05:00
Tom Burgin
08144b54a7 docs: updated configuration details (#232)
* docs: updated configuration details

* config: add example mobileconfig
2018-01-24 21:07:48 -05:00
Russell Hancox
103137498b santa-driver: Deny execs with names over MAXPATHLEN with appropriate errno (#231) 2018-01-24 14:02:05 -05:00
Tom Burgin
8e57e3709d SNTConfigurator: use mobileconfigs (#222)
* SNTConfigurator: use mobileconfigs

* use proper key groups

* remove state

* review updates

* review updates

* SNTConfigurator: Revert any out-of-band changes to the sync state file.

* SNTConfigurator move the file watcher to santad only
2018-01-08 12:56:24 -05:00
nguyen-phillip
bd6bd66946 santactl: Added -h and --help as synonyms for help (#225) 2017-12-05 14:16:04 -08:00
nguyen-phillip
6973dd0ec2 log the events generated by bundle hashing with action=BUNDLE (#207)
* log the events generated by bundle hashing with action=BUNDLE

* change EventLog to eventLog in SNTDaemonControlController init signature
2017-12-04 10:03:04 -08:00
Tom Burgin
2e8b08cd9e keep style fixes (#221) 2017-11-28 11:48:12 -05:00
Russell Hancox
edc8f43f42 Style fixes 2017-11-15 20:35:53 -05:00
Russell Hancox
133814cd73 santa-driver: Prevent possible infinite loop if decision requests fail to be retrieved
When enqueue'ing on the decision data queue, if the queue is full the new message will overwrite the oldest. In this scenario it's possible for that overwritten request to get stuck in an infinite loop - as far as the driver is concerned there's a request pending that the driver should be picking up and responding to but the daemon has never actually received the request. The only way out of this loop is for the file being executed to be written to. This change adds an expiration to pending requests (of 5s) so that if this scenario were to happen the pending request would be removed, breaking out of the inner decision loop to the outer loop where the request is sent to the daemon.

This change also removes a pointless dequeue in the log queue, it was intended to try and help reduce the queue size to get logs flowing again but it doesn't really help.
2017-11-15 20:35:53 -05:00
Russell Hancox
57213ee31b [santactl] Ensure reachability is released properly 2017-10-26 15:45:28 -04:00
Tom Burgin
b4fa2a394b Update .gitignore (#211)
Track files in the santa-driver dir
2017-10-16 15:20:43 -04:00
Tom Burgin
0c39342d53 santad: SNTPolicyProcessor fix nil scope check (#208)
Fixes `santactl rule --check` returning `Whitelisted (Scope)` incorrectly
2017-10-06 13:07:48 -04:00
Tom Burgin
1c95e8e25c santad: Stop ignoring CSInfoPlistFailed (#204)
It is too broad a check for the few false positive events we have seen.
2017-09-14 12:45:07 -04:00
nguyen-phillip
ff5a92772b sync: start reachability handler to retry to upload blocked event when initial upload fails (#202)
* sync: start reachability handler to retry to upload blocked event when initial upload fails

* fix indentation

* store related bundle events when server connection fails

* revert SNTBundleEventAction to BOOL

* go back to using SNTBundleEventAction in reply; make sure to send reply to avoid leaks

* fix indentation

* fix indentation
2017-09-14 12:44:32 -04:00
nguyen-phillip
bc2a17f70f santactl: added filtering to fileinfo command via --filter flag (#201)
* implemented simple filtering with fileinfo command

* Use regex filters instead of substring matching

* remove unnecessary match variable

* Update SNTCommandFileInfo.m
2017-09-14 11:21:08 -04:00
nguyen-phillip
f2e909e578 Minor refactor to merge similar methods into one: (#200)
* Merged similar methods into one:

* SNTSyncdQueue addBundleEvents: and addEvent: became addEvents:isFromBundle:
* SNTSyncdQueue backoffForBundleHash: and backoffForEvent: became backoffForPrimaryHash:
* SNTCommandSyncManager postBundleEventsToSyncServer: and postEventToSyncServer: became postEventsToSyncServer:isFromBundle:

* fix style issue

* simplify condition
2017-09-08 09:18:13 -04:00
nguyen-phillip
c3385a808c Bundle Notifications (#197)
* stub code for bundle notifications with new rules info

* get bundle rule count info from each rule, rather than initial FCM message

* Replace string literals with constants

* only update pendingNotifications for whitelist rules

* use pre-existing string constants as dictionary keys

* Remove processed entries from the notifications dictionary after we're done with them.

* fix indentation

* replace kRuleBundleHash with kFileBundleHash

* enforce serial access to the whitelistNotifications dictionary

* clarify comment

* fix queue spelling and better comments
2017-09-05 15:35:35 -04:00
Tom Burgin
8d480331ff Add Read the Docs to README (#196)
* Update README.md

* Update README.md
2017-08-18 13:29:43 -04:00
nguyen-phillip
5216f0989c santactl: Recursive fileinfo command (#191)
* temporarily gutted SNTCommandFileInfo. Added SNTCommand base class for all
of the SNTCommand* classes to inherit from.  Changed commands so that they
are consistently instantiated before being run, with a common init method.

* Put most of SNTCommandFileInfo functionality back in

* follow symlinks

* added -r and --recursive flags and updated help text

* moved humanReadableFileType to SNTFileInfo

* added back JSON output

* Fixed bundle info. Grab directory color from ENV variable.

* fixed indentation, moved stuff around

* Added SNTCommandFileInfo * back as parameter to property getters so that rule getter
doesn't have to be a special case any more.

* fixed code review issues

* added SNTCommand.h and SNTCommand.m to project

* added SNTCommand.m to build phases

* removed trailing spaces

* fixed tests for SNTCommandFileInfo and added a few more

* fix end-of-line comment spacing to conform to style guide

* Use NSBundle instead of NSWorkspace to determine if path is a bundle.

* added autorelease pool inside recursive search loop to fix bug where file listing
would abruptly stop after so many files with mach header related keys.

* removed directory headers. don't separate entries with newline when printing single key. format output based on max key length.

* an attempt at speeding things up.  also halfway fixed broken cert-index key.

* speedups via caching MOLCodeSignChecker & not using NSMutableString append*

* fix json ouput with cert-index, single key output, & cache SHA values

* reverted back to NSMutableString for building up output, since it seems slightly better
or at least no worse than using an NSMutableArray

* Don't print empty JSON objects

* fixed non-thread-safe JSON commas

* made the print dispatch group a property so it doesn't have to be passed around

* Fixed certIndex indexing bug & better error checking when parsing --cert-index argument

* prevent unsigned int overflow

* fixed logic tests broken by objc_setAssociatedObject with nil SNTFileInfo argument

* send error output to the serial print queue

* NSBundle bundleWithPath: returns an object even for non-bundle directories, so need to also check that there's a valid bundle identifier.

* Added TODO comment and fixed formatting issues

* added cached codeSignChecker property to SNTFileInfo

* rewrote SNTFileInfo's codesignChecker method to include an error reference parameter & removed @synchronized

* Removed caching of SHA values from SNTFileInfo

* use property getter/setter to access codesignCheckerError

* Change nil NSError ** arguments to NULL

* Don't try to create a new codesignChecker if there was previously an error

* Fix NSDirectoryEnumerator memory usage & don't retain self in rule getter.

The NSStrings grabbed from the directory enumerator needed a chance to be freed.

* fixed colon alignment
2017-08-18 09:56:37 -04:00
Tom Burgin
4238553a2e Docs: Start of Santa Docs (#192)
* Docs: Start of Santa Docs

* Docs: /exec()/execve()/

* Docs: /sync-server/sync server/

* Docs: review updates
2017-08-17 16:01:59 -04:00
nguyen-phillip
79662d0dcf santad/SNTEventLog: log original path of translocated apps (#194)
* log original path of translocated apps

* made handle a local variable & fixed capitalization

* Removed superfluous CFError
2017-08-17 11:09:46 -04:00
Russell Hancox
ff095bc53d KernelTests: Fix cache performance test
It was previously calculating CPU use rather than walltime which isn't really what we want to measure.
2017-08-16 16:13:45 -04:00
Russell Hancox
eefd70b2de santa-driver: Fix race condition by adding CAS op to SantaCache
Change the signature of the set method in SantaCache so that it takes an
optional previous-value parameter (and a bool indicating that this value
has been provided). If previous-value is provided, set becomes a
compare-and-swap. Also provide 2 overloads for a cleaner interface, one
with and without the previous-value parameter.
2017-08-16 16:13:45 -04:00
Russell Hancox
9b3eab67a2 santa-driver: Determine root FSID more safely
Only calculate root FSID during daemon connection. If daemon is running
there must be a root filesystem. Also check return values just in case.

Check vnode_id has been determined in VnodeCallback and SantaDriverClient
methods so that it doesn't need to be checked anywhere else.
2017-08-16 12:07:44 -04:00
Russell Hancox
54def2deb7 santa-driver: Reverse ClearCache() non_root_only default parameter 2017-08-16 12:07:44 -04:00
Russell Hancox
cd12744726 santad/santactl/santa-driver: Make status command return size of both caches 2017-08-16 12:07:44 -04:00
Russell Hancox
616fd9570f santa-driver: Split cache for root/non-root volume
Split the kernel-land cache into 2 separate caches, one for the root
volume and one for secondary volumes. When an unmount happens, clear
the non-root cache to ensure no overlap with filesystem IDs.
2017-08-16 12:07:44 -04:00
Russell Hancox
0544011ee0 [santad] Remove broken check and obsolete TODOs (#190) 2017-08-03 15:14:02 -04:00
nguyen-phillip
51920c7045 santad: modified execution log format to show path & args at end (#189)
Fixed problem where extremely long path/args obscured other log info.
2017-08-02 14:27:39 -04:00
Russell Hancox
6f417a1775 common: Remove EventDetailBundleURL key (#187)
The changes to bundle scanning mean this key isn't really necessary anymore - if a server supports bundles it tells the client during preflight, this in turn causes bundle hashes to be generated and these are used in place of the file hash when generating a detail URL. Keying bundles off the ID and version was never really a good idea anyway.
2017-08-01 12:16:37 -04:00
Russell Hancox
51034a24c6 SNTXPCConnection: Prevent crash if caller releases instance during resume (#183) 2017-07-18 16:50:32 -04:00
Tom Burgin
f631f219b0 santactl/sync: fixed exception when file_name is None / NSNull (#180)
* santactl/sync: fixed exception when file_name is None / NSNull

* review updates
2017-07-06 11:52:49 -04:00
Tom Burgin
aacae020b8 logs: add DAAppearanceTime to the DISKAPPEAR logs (#179)
* logs: add DAAppearanceTime to the DISKAPPEAR logs

* review updates

* discussion updates
2017-07-02 16:27:40 -04:00
Tom Burgin
7c426e0eec santactl/sync: upload file bundle executable relative path for bundle events (#178) 2017-06-28 11:55:21 -04:00
Tom Burgin
363826502f santabs: de-dupe generated events before upload (#177)
* santabs: de-dupe generated events before upload and remove locks

* review updates

* error updates
2017-06-22 17:46:04 -04:00
Russell Hancox
1cfadae068 SantaGUI: Don't show pop-up notifications for empty filenames (#176) 2017-06-12 11:28:32 -07:00
Tom Burgin
d3b3d722b4 santabs: use the ancestor bundle when searching for binaries (#175)
* santabs: use the ancestor bundle when searching for binaries

* review updates

* bundle tests
2017-06-09 17:04:23 -04:00
Tom Burgin
a82428958b santactl/rule: Add the ability to check the status of arbitrary sha256 hashes without on-disk artifacts. (#172) 2017-05-30 13:07:47 -04:00
Tom Burgin
b185632bda santad/SantaGUI: Add needsBundleHash property to SNTStoredEvent && (#170)
santactl/sync:sync server enables/disables client bundle support
2017-05-30 13:04:08 -04:00
Tom Burgin
e7a0c3d25b santactl/sync: Sync Server to set FCM interval and deadline (#168)
* santactl/sync: Sync Server to set FCM interval and deadline

* rename default constants

* review updates
2017-05-22 11:50:37 -04:00
Tom Burgin
ab33de2c15 project/config: Move /var/log/santa.log to /var/db/santa/santa.log (#173) 2017-05-22 11:10:10 -04:00
Tom Burgin
a1031cdc27 protect wakeup() from being called with 0 (#167)
* don't call wakeup on 0

* project: "share" the santabs target

* Project: Update CocoaPods to 1.2.1

* Project: pod deintegrate - pod install
2017-04-14 16:13:45 -04:00
Tom Burgin
e3ab3ca506 Update SNTCommandSyncEventUpload.m (#162) 2017-04-13 16:58:25 -04:00
Matthew Suozzo
b4cd1ccbee santa-driver: Fix a typo 2017-04-13 14:27:05 -04:00
Matthew Suozzo
14573a5714 santa-driver: Refactor cache expiration calculation 2017-04-12 22:35:08 -04:00
Tom Burgin
96150a9668 Bundle Events (#145)
* santabs: Create Santa Bundle Service

* common: SNTXPCConnection add initClientWithServiceName:

* santad: add logic for blocked bundles

* SantaGUI: add ui elements and xpc connections to / from santabs

* santactl/sync: add api features for syncing bundle events

* santactl/bundleinfo: add bundleinfo command for debug builds

* common: prefer bundle hash over file hash for event urls

* common: remove syncBackoff property - this is now handled in santactl sync

* common: add properties to support the bundle event api

* common: find a bundle from a nested binary

* review updates

* sane bundle hash time outs

* post rebase updates

* post review updates
2017-04-07 15:31:56 -04:00
Russell Hancox
c10c1303ed SantaGUI: Add preprocessor flag to import Cocoa for SNTBlockMessage 2017-04-05 14:19:50 -04:00
Tom Burgin
7852e69685 SantaCache fix 0 init (#158) 2017-03-22 09:38:27 -04:00
Russell Hancox
094880af50 Project: Add DevelopmentTeam configuration (#157)
This is a generated xcconfig in the Rakefile which gets included by the project
to set the DEVELOPMENT_TEAM key to keep Xcode 8 happy. The development team is
figured based on the available “Mac Developer” certificate.

Also update the way SantaCache declares a ‘zero’ value, update the
OCMock pod and add a few missing includes.
2017-03-20 16:34:59 -04:00
Tom Burgin
c3db518aca santactl/sync: use the new fcm-stream format (#156) 2017-03-20 14:42:29 -04:00
Tom Burgin
41ee0c5fdb Running without a config fixes (#154)
* common: capture fileSystemRepresentation in a local variable

* santactl/status: check for instant notification status only when there is a sync url

* s/FALSE/NO
2017-03-17 12:12:41 -04:00
Tom Burgin
ae178bc146 create default config if one does not exist (#153) 2017-03-10 17:17:52 -05:00
Tom Burgin
a2a660d483 config update and modules (#152)
* santactl/sync: https://github.com/google/santa/issues/150

* pch to modules
2017-03-09 13:02:02 -08:00
Tom Burgin
8684cc34f7 santactl/sync: use hostname for reachability (#149)
* Revert "SNTXPCConnection: make XPC debugging easier (#141)"

This reverts commit a2d6338400.

* santactl/sync: use hostname for reachability

* style update
2017-03-08 07:55:35 -08:00
Tom Burgin
0aba8b78ba disable bundle scans (#146)
* config: update to cocoapods-1.2.0 and molfcmclient 1.2

* santactl/sync: disable sync server bundle scan requests
2017-03-01 09:02:00 -08:00
Russell Hancox
5e735aa8d5 santad: Clear cache when regexes change. (#143)
When white/black-list regexes are changed clear the kernel cache so the regexes are able to take effect immediately. Fixes #142
2017-02-03 11:00:32 -05:00
Tom Burgin
a2d6338400 SNTXPCConnection: make XPC debugging easier (#141) 2017-01-31 15:36:09 -05:00
Russell Hancox
5e4b8350ab SNTXPCConnection: allow redefining invalidationHandler after connections are established (#140) 2017-01-23 11:10:13 -05:00
Tom Burgin
4a65b646df santactl status: add last successful rule sync date (#139)
* santactl status: add last successful rule sync date
2017-01-11 15:52:07 -05:00
Tom Burgin
24c715aae9 santactl sync: reachability and notification updates santad: syncd xpc updates (#138)
* santactl sync: post a notification for every matching rule and fcm message

* santactl sync: if full sync fails, retry when reachable

* santad: only allow one syncd connection at any given time
2017-01-10 16:14:15 -05:00
Tom Burgin
9ab85768bd Update Podfile.lock to use MOLFCMClient v1.1 (#136) 2017-01-03 11:10:15 -05:00
Tom Burgin
16458d96e7 Notification verbage update (#135) 2016-12-14 14:41:20 -05:00
Tom Burgin
b307dd17af Use machine ids as the targeted sync indicator (#134)
* Use machine ids as the targeted sync indicator

* remove unused constant
2016-12-12 16:53:24 -05:00
Tom Burgin
313552352c Display the binary name when a local rule is synced from a push notification (#133) 2016-12-07 17:40:11 -05:00
Tom Burgin
543ac7c649 push notifications with FCM (#132)
* push notifications with FCM

* Don't display rule count in notifications. Get FCM broadcast topic from sync server.
2016-12-06 16:04:34 -05:00
Tom Burgin
dacff76694 run santactl as a sync daemon (#129)
* run santactl as a sync daemon
2016-11-16 14:41:12 -05:00
Russell Hancox
c134169ea1 santad: Drop AUTOINCREMENT on event table (#130) 2016-11-01 11:14:51 -04:00
Russell Hancox
e252945047 santactl/fileinfo: Send resolved path to santad for processing (#128) 2016-10-26 16:04:27 -04:00
Russell Hancox
f8cfcaab20 Package/Conf: Fix typo in uninstall.sh (#126) 2016-10-25 15:05:36 -04:00
Tom Burgin
528237a239 santactl status: check non-boxed vars when building json output (#125) 2016-10-24 12:14:56 -04:00
Russell Hancox
91aefe25c4 santad: Fix printer-proxy workaround (#120) 2016-10-13 15:30:08 -04:00
Russell Hancox
a8c11097d9 Project: Use NSSet instead of NSDictionary for uniqueness in collections (#119) 2016-10-13 15:20:19 -04:00
Russell Hancox
92ba4a3ae9 santactl/sync: Debug log when clean sync requested (#118) 2016-10-13 15:20:12 -04:00
Russell Hancox
7c5d382010 santactl/sync: Fix bundle searching, make concurrent. (#115)
* santactl/sync: Fix bundle searching, make concurrent.
2016-10-13 15:14:35 -04:00
Russell Hancox
f8fbaefd86 Tests: Fix XPC connection tests (#116)
Also disable LTO in debug builds.
2016-10-13 12:43:26 -04:00
Russell Hancox
181b37296a santactl/sync Tests: Use constants (#117) 2016-10-13 12:43:14 -04:00
Tom Burgin
2ab61cfa12 SNTCommandFileInfo: Fixed retain cycle. Added locking for a NSMutableArray when accessed on multiple threads (#114) 2016-10-13 11:38:33 -04:00
Tom Burgin
1b0e9b14ef Global json bool shared between class and instance methods. https://github.com/google/santa/issues/112 (#113) 2016-10-12 14:35:27 -04:00
Russell Hancox
2aacc9266f Revert changes for building with Xcode 8 (#111)
* Partial Revert of "Project: Update project files for Xcode 8 (#105)"

Building with Xcode 8 (and specifically the 10.12 SDK) breaks logging on
10.12 and on top of that some tests don't pass while working perfectly
fine on 10.11. For now, we'll just continue building with 7.3.1.

* README: Add note about building with Xcode 7.3.1
2016-10-10 14:24:14 -04:00
Russell Hancox
d648d477bb santa-driver: Fix deadlocking on Sierra (#107)
1. Don't RemoveFromCache for advisory access by santad itself.
2. wakeup sleeping threads when removing from cache
3. Move the vnode type check earlier in the process for the vnode scope
2016-09-28 16:36:23 -04:00
Russell Hancox
6f91c1a1d3 Project: Update project files for Xcode 8 (#105) 2016-09-28 16:11:22 -04:00
Russell Hancox
aa1aca24b7 Common: Don't crash if ClientMode key is not an integer. (#106)
NSString has longLongValue but not longValue, so switch to that then cast down. Check that the receiver responds to longLongValue before calling it just in case someone tries to set it to an NSData or something.
2016-09-26 11:53:51 -04:00
Tom Burgin
6a0867172f Mocking for MOLCodesignChecker initWithBinaryPath:error: (#104) 2016-09-23 15:40:37 -04:00
Russell Hancox
f025a4b2fb santad: In required rule protection, handle case where there are multiple rules for the required certs (#101) 2016-09-22 16:17:59 -04:00
Russell Hancox
8871f36a92 santa-driver: FetchDecision - use a loop rather than recursing. (#100) 2016-09-22 15:58:53 -04:00
Russell Hancox
f17490edad santad: Handle UTF-8 in process args. (#99)
While appendFormat with %s is slightly faster (~1üs) it doesn't handle UTF-8 properly.
2016-09-22 15:38:00 -04:00
Russell Hancox
b360e782c6 santad: Start ignoring errSecCSInfoPlistFailed (-67030) (#98) 2016-09-22 15:36:35 -04:00
Russell Hancox
8d94324dd6 santad: Update SNTFileWatcher to fix broken dispatch source. (#97)
I'm not certain if this is a Sierra change or just that it was more rare before but changing a cancel handler on a dispatch source no longer seems to have any effect. This meant the file descriptor for the currently-active source was being closed instead of the one for the source that was just cancelled. It wasn't actually necessary to get the file handle from the source, we can just rely on capturing it in the block, which works just as well.
2016-09-22 15:36:26 -04:00
Russell Hancox
2818609412 santactl/sync: Fix bundle event upload (#96) 2016-09-20 12:37:12 -04:00
Russell Hancox
270a2e69d4 Project: Add bundler caching to travis build (#95) 2016-09-19 07:19:15 -04:00
Russell Hancox
d1d9762e29 santa-driver: Don't filter advisory vnode_write notifications (#94) 2016-09-15 10:17:18 -04:00
Russell Hancox
1666e8b127 Move some NSMutableDictionary uses to NSCache, log client connection (#93)
* santa-driver: Log when client connects (we already log disconnect)
* santad: Move a couple of NSMutableDictionary uses over to NSCache, add type info.
2016-09-14 17:09:04 -04:00
Tom Burgin
08dfad208b Move decision making to SNTPolicyProcessor (#91)
Move SNTEventState to a mixed bit field enum
SNTCommandFileInfo now handles all rule states
2016-09-14 12:34:42 -04:00
Russell Hancox
b5921f95f3 santa-driver: Remove the static wrappers in SantaDriverClient (#90)
SantaDriverClient was implemented to have static functions that call instance
methods passing appropriate arguments. While this works and is 'technically correct' (best kind),
it's a bit messy and hard to read.
2016-09-12 10:14:38 -04:00
Russell Hancox
2063bc3db3 Update pods, check length of EventDetailBundleURL, add text above URL in TTY (#89)
* SantaGUI: Check EventDetailBundleURL length rather than just existence

* santad: Add title above detail URL in TTY

* Project: Update pods
2016-09-09 16:11:40 -04:00
Tom Burgin
4380016d52 Compile SNTCommandController and SNTCommandFileInfo in the LogicTests target (#86) 2016-09-07 10:56:15 -04:00
Tom Burgin
5e3ceabe46 SNTCommandFileInfo Tests (#85) 2016-09-06 14:21:37 -04:00
Tom Burgin
8e7936275b Merge pull request #84 from russellhancox/fix-70
santactl/rule: Handle bad path properly (dir, non-file)
2016-09-06 13:31:57 -04:00
Russell Hancox
4b967239fa santactl/rule: Handle bad path properly (dir, non-file)
Fixes #70
2016-09-06 13:29:05 -04:00
Tom Burgin
92945c384c Merge pull request #83 from russellhancox/fix-82
Package: Ensure /usr/local/bin exists before making symlinks in it.
2016-09-06 13:10:44 -04:00
Russell Hancox
79d93c4ecf Package: Ensure /usr/local/bin exists before making symlinks in it.
Fixes #82
2016-09-06 13:03:49 -04:00
Allister Banks
76b6f25b0c uninstall.sh typo
typo
2016-09-01 11:20:44 -04:00
Allister Banks
aadce4890a Add uninstall script (#77)
Leaves configs, performs no checks about current state but should be
relatively idempotent (can't unload/rm stuff that's not there)
2016-08-30 11:41:20 -04:00
Tom Burgin
0e95a98fc2 santactl fileinfo sha1 & sha256 simultaneous hashing (#67) 2016-08-23 15:40:01 -04:00
Tom Burgin
9483437e8f Merge pull request #66 from russellhancox/master
santad: Database access optimizations
2016-08-23 14:29:40 -04:00
Russell Hancox
59542f8aef santad: Drop binrules/certrules views in rules database. 2016-08-23 12:48:41 -04:00
Russell Hancox
e29f7332f5 santad: Avoid creating multiple SNT*Table objects, as initializing them can be slow. 2016-08-23 12:48:41 -04:00
Russell Hancox
f8640feafe Project: Include xcodebuild clean in rake clean 2016-08-22 14:49:18 -04:00
Russell Hancox
e94e9e2be4 Project: Clean up CocoaPods project cruft 2016-08-22 14:46:56 -04:00
Tom Burgin
4053aac365 Merge pull request #65 from russellhancox/master
santactl/fileinfo: Recognize bundle/plugin mach-o files.
2016-08-22 14:07:43 -04:00
Russell Hancox
a5fa6c7aef santactl/fileinfo: Recognize bundle/plugin mach-o files. 2016-08-22 14:05:22 -04:00
Russell Hancox
97263894d1 santactl/sync: Send existing client mode in preflight request 2016-08-19 15:10:50 -04:00
Russell Hancox
1885580958 Project: pod update 2016-08-19 15:10:50 -04:00
Tom Burgin
1167b470bb santactl/fileinfo: Fix arg parsing, better cert printing
* arg parse fixes

* More parse fixes
2016-08-19 14:53:33 -04:00
Russell Hancox
7600506d6d santad: Include client mode in execution logs. 2016-08-18 14:44:40 -04:00
Russell Hancox
86bad866a0 santad: Unify CERT vs CERTIFICATE in logs. 2016-08-18 14:13:36 -04:00
Russell Hancox
2f1a15cf7e SantaGUI: Fix bundle version URLs 2016-08-18 14:11:42 -04:00
Tom Burgin
52b0e1870f Squashed binary and cert rule fetching down to one call. (#62) 2016-08-17 17:06:51 -04:00
Tom Burgin
9b181c1e0d santactl fileinfo updates (#61)
* Added --json output option. Added --key output option.
* Added multi-file processing
* Added threading
* \r to cleanup during really quick runs
2016-08-17 15:55:03 -04:00
Tom Burgin
100f2dc45e Merge pull request #60 from russellhancox/master
Performance improvements, GUI bundle handling
2016-08-12 16:42:00 -04:00
Russell Hancox
b247c3d477 santa-driver: Try to prevent logspam when dropping log queue messages
Both PostTo*Queue methods use mutexes, so access to the failed_*_queue_requests_ variables don't need to be atomic.
2016-08-12 16:08:23 -04:00
Russell Hancox
76ee82b258 santad: Limit log queue to 15 threads
To counteract the increased likelihood of dropped messages, double the maximum
log queue size.
2016-08-12 15:04:21 -04:00
Russell Hancox
e8fcd29669 santa-driver: If a request for a given vnode is pending, don't repeat request. 2016-08-12 15:04:21 -04:00
Russell Hancox
8dd16ecea4 santa-driver: Remove references to vnode_id_str
These should have been culled when moving to SantaCache but were missed.
2016-08-12 15:04:21 -04:00
Russell Hancox
e9c0bcd877 SantaGUI: Handle bundles having version instead of short version string 2016-08-12 15:04:21 -04:00
Allister Banks
75ed4b52a6 revise readme (#57)
* overall readme revise

admin-specific vs. security/performance features split up, add details
about path-based functionality, PAGEZERO feature, failsafe cert
whitelisting, explicitly say default mode is MONITOR

* process feedback

sticking with talking about binary launches while kext is loaded,
integrated all other feedback
2016-08-10 15:53:55 -04:00
Tom Burgin
71635c00df Merge pull request #58 from russellhancox/master
Performance improvements
2016-08-10 15:53:00 -04:00
Russell Hancox
1810af5483 SantaGUI: Change Dismiss button to Ignore 2016-08-10 15:18:22 -04:00
Russell Hancox
b07835dfd5 santad: Cache user/group id->name lookups. 2016-08-10 15:18:22 -04:00
Russell Hancox
4c33aa2aae santad: Improve loggedInUsers:sessions: 2016-08-09 16:51:23 -04:00
Russell Hancox
3c255640cb santad: Speed up TTY message creation 2016-08-09 16:51:23 -04:00
Russell Hancox
3d08ba9ebc santa-driver: Use msleep/wakeup instead of IOSleep.
This brings the average cache-miss decision making time down by 66%. Previously the minimum decision time was 10ms, now it's <1ms.
2016-08-09 16:51:23 -04:00
Russell Hancox
f64482500e santa-driver: Add debug logging of decision times to GetFromDaemon 2016-08-09 16:51:20 -04:00
Russell Hancox
215902f192 SantaCache: Extract entry value before unlocking bucket. 2016-07-19 16:28:35 -04:00
Russell Hancox
3e9c3a069d Project: Pod update 2016-07-19 14:51:01 -04:00
Russell Hancox
841fb48479 santa-driver: Only send file mod notifications to queue if client is connected. 2016-07-14 13:45:13 -04:00
Russell Hancox
df8e41925f SNTFileInfo: Check NSURLQuarantinePropertiesKey is usable 2016-07-13 17:29:53 -04:00
Russell Hancox
6b0994a990 santad: Avoid properties in critical path 2016-07-13 12:44:48 -04:00
Russell Hancox
7dd616e891 santa-driver: Switch SantaCache from an array to a linked list 2016-07-12 14:54:43 -04:00
Russell Hancox
c672edbe4d Whitespace clean-up 2016-07-12 14:51:10 -04:00
Russell Hancox
687ecc7097 santad: Close more file descriptors on exec 2016-07-11 16:23:38 -04:00
Russell Hancox
b8882b4826 santactl/fileinfo: Wait longer for daemon response. 2016-07-11 15:59:30 -04:00
Russell Hancox
51de0b38a4 santad: Change watchdog thread interval to 30s 2016-07-11 15:59:30 -04:00
Russell Hancox
e0309c0482 SantaGUI: In keyPathsForValuesAffectingValueForKey, return an empty set rather than nil 2016-07-11 15:53:04 -04:00
Russell Hancox
5dbe86869d santad: Move event storage out of the high priority decision queue
As event storage needs to happen before attempting upload, use the same serial queue.
2016-07-01 17:56:52 -04:00
Russell Hancox
14a11279c7 Project: Remove activesupport from travis settings.
It no longer appears to be needed for CocoaPods and causes errors.
2016-07-01 17:56:52 -04:00
Russell Hancox
df0ce42377 Merge pull request #54 from georgekola/gk-avoid-string-copy
Avoid two string copies
2016-07-01 17:09:54 -04:00
George Kola
4c03411405 Avoid two string copies 2016-07-01 14:07:23 -07:00
Russell Hancox
f020e18238 Project: Update to MOLCertificate 1.5 2016-07-01 13:02:07 -04:00
Russell Hancox
629bd4aff9 santad: argsForPid: Don't replace last NULL but still count up to it 2016-07-01 12:48:50 -04:00
Russell Hancox
f20825a66c Project: Increase optimization level for Pods 2016-06-30 14:36:16 -04:00
Russell Hancox
f098ca0d02 santad: Update argsForPid to append to a given string. 2016-06-30 09:41:26 -04:00
Russell Hancox
1f96f74f4d Merge pull request #52 from georgekola/gk-pread
Correctly use pread
2016-06-29 14:27:22 -04:00
George Kola
7a3a98c27a Correctly use pread
pread can return less than the chunk size (e.g. signal caught in the
middle) and hence we need to handle it. This change also cleans up the
hash function and makes it more performant.
2016-06-29 11:21:56 -07:00
Russell Hancox
1130448cb9 Merge pull request #53 from georgekola/gk-cacheCalls
Cache method call
2016-06-29 08:20:59 -04:00
George Kola
d388e99c0e Cache method call
Minor optimization. Cache objc method call in local variable to avoid a
second call
2016-06-28 21:26:35 -07:00
Russell Hancox
2baea9a6b4 Project: Xcode recommended updates. 2016-06-28 17:34:58 -04:00
Russell Hancox
0629625a9a santad: Move log queue down to BACKGROUND priority. 2016-06-28 17:21:07 -04:00
Russell Hancox
a2d0acc761 santad: sanitizeString: Use cached length value, use lengthOfBytesUsingEncoding: instead of length. 2016-06-28 17:02:37 -04:00
Russell Hancox
28a6bce90f santad: sanitizeString: Only allocate buffer if necessary. 2016-06-28 16:35:50 -04:00
Russell Hancox
9058192ffe santad: Use memcpy instead of strncpy where appropriate 2016-06-28 16:23:06 -04:00
Russell Hancox
465b358271 SantaCache: Initialize count_ to 0. 2016-06-28 15:01:57 -04:00
Russell Hancox
7de585fe1d santad: Replace sanitizeString with simple loop.
This is ~70% faster on average and is faster in all cases compared to the regex and the previous method.
2016-06-28 14:54:31 -04:00
Russell Hancox
8479730c95 SNTFileInfo: Catch potential NULL-pointer deref in isScript and isXARArchive. 2016-06-28 14:54:21 -04:00
Russell Hancox
7102e2df4c SNTFileInfo: More speed-ups in hashing, use RDAHEAD, don't use NOCACHE, catch EINTR. 2016-06-28 14:52:28 -04:00
Russell Hancox
c3bd99ff93 santad: Use serial queues instead of NSLock 2016-06-28 14:51:27 -04:00
Russell Hancox
c560405a46 SNTFileInfo: Speed up hashing - increase chunksize, read directly, use fcntl
- Use fcntl to disable cache and issue an advisory read
- Increase default chunk size from 4KB to 256KB
- Use pread to read from file descriptor, rather than make NSData objects

This is ~15% faster.
2016-06-27 17:38:41 -04:00
Russell Hancox
0c0fb28ccc santad: Make argsForPid more reliable and 33% faster 2016-06-27 15:55:18 -04:00
Russell Hancox
a33fce942c santad: Use regex to sanitize incoming strings, which is ~6x faster. 2016-06-27 13:11:15 -04:00
Russell Hancox
369cd40ee5 santad: Also optimize file logging by using NSMutableString 2016-06-27 12:51:29 -04:00
Russell Hancox
577b431a41 santad: Put locks around NSMutableDictionary in SNTEventLog and SNTExecutionController. 2016-06-27 12:48:36 -04:00
Russell Hancox
75cf8acd33 Project: Enable more compiler optimizations.
Specifically, switch from -Os to -Ofast and enable link-time optimization.
2016-06-27 10:08:38 -04:00
Russell Hancox
d70983962b Merge pull request #50 from georgekola/gk-optimize-log1
Optimize logging by using one pre-allocated NSMutableString
2016-06-27 10:05:46 -04:00
Russell Hancox
ff440984b0 Merge pull request #51 from georgekola/gk-optimize-fprintf
Using fwrite as we know the length of string
2016-06-27 09:48:53 -04:00
George Kola
c631155be7 Using fwrite as we know the length of string
It is better to use fwrite as it is generally faster and we are not
using any fprintf feature
2016-06-25 18:50:03 -07:00
George Kola
6038930755 Optimize logging by using one pre-allocated NSMutableString 2016-06-25 18:47:05 -07:00
Tom Burgin
9edc119c62 Merge pull request #49 from russellhancox/kernel-hashtable
santa-driver: Re-implement caching to avoid OSDictionary.
2016-06-20 13:16:09 -04:00
Russell Hancox
269a94bf03 SantaCache: Updates from PR:
+ Added check that per_bucket is >=1 and reduced max from 126 to 64.
+ Added note about cache reset above set method
+ Moved modulo into the hash function
2016-06-20 13:14:50 -04:00
Russell Hancox
7f3e4d7468 santa-driver: Re-implement caching to avoid OSDictionary.
OSDictionary is not well-suited to our needs and locking is quite expensive.
This commit:

  + Replaces all uses of OSDictionary with a new SantaCache class, which
    is a size-limited array hash table with per-bucket locking. It works with
    uint64_t keys, which is perfect for our needs.
  + Adds a unit test for SantaCache.
  + Removes SantaCachedDecision and SantaPIDAndPPID, which only existed
    because OSDictionary can only store OSObject subclasses.
  + Removes a lot of locking logic from SantaDecisionManager as the
    locking is now handled inside SantaCache and is therefore and is
    much more granular.
  + Removes the timed cache expiration for ALLOW decisions. This was
    originally to ensure executions were logged regularly but as we're
    logging all executions nowadays this is longer particularly useful.

SantaCache's configured load factor and hashing function may need tweaking
over-time but this is already a little faster and uses less memory
than what existed before.
2016-06-17 16:39:39 -04:00
Russell Hancox
eb89891cdd Merge pull request #48 from tburgin/tom
Add checkcache command to santactl
2016-06-17 16:17:38 -04:00
Tom Burgin
038b068370 u_int64_t --> uint64_t. CacheCheck --> RemoveFromCache. 2016-06-17 15:53:54 -04:00
Tom Burgin
d2017a59de Get back file status from the kernel cache 2016-06-17 12:45:51 -04:00
Tom Burgin
3435b56a84 Add checkcache command to santactl. It will check to see if the vnode id of a file is in the kernel cache 2016-06-17 12:03:26 -04:00
Russell Hancox
a812558d2d santad: Remove hashes from file write logs 2016-06-16 17:31:40 -04:00
Russell Hancox
aefd85455e Project: s/OS X/macOS/g 2016-06-16 17:31:40 -04:00
Russell Hancox
e42f1347b7 santad: Use IORegistryEntryFromPath instead of IORegistryEntryCopyFromPath.
The latter was only introduced in 10.11. Fixes #47
2016-06-16 17:31:40 -04:00
Russell Hancox
c7442a03d1 santa-driver: Use KAUTH_VNODE_WRITE_DATA instead of KAUTH_FILEOP_CLOSE to catch writes
It turns out that the KAUTH_FILEOP_CLOSE action is not used when the kernel automatically closes file descriptors for exiting processes. Some things, like dd, don't close their file descriptors and let the kernel do it for them which we were previously missing.
2016-06-16 17:31:40 -04:00
Russell Hancox
1eda8bdd9d KernelTests: Add test for overwritten file that was auto-closed 2016-06-16 17:31:37 -04:00
Russell Hancox
c4d0628bdb santad: Increase detail in TTY messages. 2016-06-13 12:38:55 -04:00
Russell Hancox
d51ae66242 santactl: Only resume in CommandContoller when its required, otherwise leave it to individual command 2016-06-10 12:48:54 -04:00
Russell Hancox
121dde6b8b KernelTests: Add cache speed test and secondary client rejection test 2016-06-10 12:48:54 -04:00
Russell Hancox
98081b067d Merge pull request #45 from clburlison/patch-1
Update style guide links
2016-06-09 16:53:54 -04:00
Clayton Burlison
8cc9345b42 Update style guide links 2016-06-09 15:52:48 -05:00
Russell Hancox
f7528365b0 Project: Have rake dist make the correct folder name from the version tag 2016-06-07 12:05:17 -04:00
Russell Hancox
7baa1a345e SNTFileWatcher: Don't call handler on main thread, sleep between handler invocations 2016-06-07 11:40:12 -04:00
Russell Hancox
acf7f4fd52 SantaGUI: Don't reload config file if attributes change (as it will trigger an attribute change) 2016-06-07 11:38:48 -04:00
Russell Hancox
f43e8680b8 santad: Improve SNTFileWatcher, update config file permissions if they change 2016-06-06 16:15:28 -04:00
Russell Hancox
545a6c1b36 santad: Ensure config file reloading is handled on main thread 2016-06-06 11:31:36 -04:00
Russell Hancox
f01fd8c850 Project: Try and fix CocoaPods on Travis 2016-06-03 14:12:03 -04:00
Russell Hancox
c9ec69b0b5 Tests: Fix OCMock misuse in testPreflightDatabaseCounts.
The block expects int64_t, not NSNumber. For some reason this didn't fail in Xcode but does from the command-line. Using OCMOCK_VALUE works properly.
2016-06-03 12:43:03 -04:00
Russell Hancox
3640e2c5f0 santad: Add a workaround for PrinterProxy 2016-06-03 11:32:55 -04:00
Russell Hancox
b3659cb456 santad: Don't spawn an event upload if one for this hash happened in the last 10 minutes 2016-06-01 17:20:16 -04:00
Russell Hancox
76284a2916 santad: Log disk mount/unmount events 2016-06-01 17:20:16 -04:00
Russell Hancox
40b1e011bd SantaGUI/santad: Add option to send bundled binaries to a different detail URL 2016-06-01 17:13:11 -04:00
Russell Hancox
e0bebecd59 santactl/sync: Switch bundle binary uploading
Only upload bundle related events when the server asks for it. Do the search inside a bundle for longer
2016-06-01 17:13:11 -04:00
Russell Hancox
8ac0cf6831 santad: Catch exceptions writing to TTY 2016-06-01 17:13:10 -04:00
Russell Hancox
992163206d Project: Switch to MOLAuthenticatingURLSession Pod. 2016-06-01 17:13:10 -04:00
Russell Hancox
86dd5d8078 santactl/sync: Refactor to reduce repetition, support XSRF tokens and add tests.
Move common request generating and performing code into a common
superclass.
Add code to handle XSSI in JSON responses and support XSRF
tokens via headers.
Adds tests, finally.
Changes preflight hostname to be long instead of short
2016-06-01 17:13:02 -04:00
Russell Hancox
932aa9d052 santad: For single-event syncs, use syslog logging 2016-05-25 17:52:53 -04:00
Russell Hancox
5f7f5204ec santad: Flush cache when switching into lockdown mode 2016-05-25 11:04:53 -04:00
Russell Hancox
a154d23637 SantaGUI: Add customizable notifications when client switches modes. 2016-05-25 11:04:35 -04:00
Russell Hancox
ac2bb9d362 SNTBlockMessage: Move HTML stripping to separate method 2016-05-24 16:32:25 -04:00
Russell Hancox
b918958bfa santactl/fileinfo: Don't fail if santad isn't running, colorize rule output on a TTY. 2016-05-19 19:08:52 -04:00
Russell Hancox
215df4ffa6 santactl: Always try to get daemonConn but only log and exit if it's marked as required 2016-05-19 19:08:52 -04:00
Russell Hancox
bb28bc5875 SNTXPCConnection: Ensure validation completes before returning remoteObjectProxy 2016-05-19 19:08:52 -04:00
Russell Hancox
a82bc3f712 SNTXPCConnection: Don't track accepted connections, it isn't useful. 2016-05-19 19:08:52 -04:00
Russell Hancox
b3a507014b Project: Update to CocoaPods 1.0 2016-05-19 19:08:52 -04:00
Russell Hancox
49c5e35a14 santad: Improve TTY message output.
Bold Santa title, replace <br/> with \n, add link to EventDetailURL
2016-05-19 19:08:03 -04:00
Russell Hancox
869ed33bd4 santactl/fileinfo: Show when code signature is adhoc 2016-05-03 14:15:27 -04:00
Russell Hancox
0c4a9be482 santad: Write message to TTY when blocking execution
Sometimes the GUI isn't running. Sometimes the user is using SSH. Either way, printing a message to the TTY of the parent of the just denied process is user-friendly.
2016-04-28 16:07:36 -04:00
Russell Hancox
4410ec575a santactl/fileinfo: Include rule state info 2016-04-28 16:07:24 -04:00
Russell Hancox
e3b92fc948 santactl/sync: Upload rule counts in preflight 2016-04-28 16:07:04 -04:00
Russell Hancox
4ca4692a67 santactl/flushcache: Disable flushcache in release builds.
It really isn't a useful command outside of development and its existence
seems to confuse people.
2016-04-28 15:00:10 -04:00
Russell Hancox
c1284d3c23 Project: Re-organize file structure, again 2016-04-28 14:11:50 -04:00
Russell Hancox
c8c0eadf72 santactl/fileinfo: Make file type output more accurate for executables 2016-04-28 10:54:54 -04:00
Russell Hancox
f4bbc8abc7 santactl/sync: Log successful stages as debug 2016-04-27 14:41:50 -04:00
Russell Hancox
a0f6ea57f8 SantaGUI: If SilencedNotifications key doesn't exist, create it 2016-04-27 14:19:25 -04:00
Russell Hancox
88d21a07ac santad, santactl/sync: Include Bundle Path in event upload data. 2016-04-26 17:35:29 -04:00
Russell Hancox
88e3a606a0 SNTFileInfo: Use CFBundleDisplayName if available 2016-04-26 17:34:29 -04:00
Russell Hancox
fff693c3f0 santad: Close the FMResultSet left after locking db to prevent spurious messages. 2016-04-26 17:33:43 -04:00
Russell Hancox
1e8d792d39 santa-driver: Flush vnode-pid map periodically. 2016-04-15 17:10:14 -04:00
Russell Hancox
dfb149ac6a santa-driver: Try to get uid/gid from credential if available 2016-04-15 17:05:50 -04:00
Russell Hancox
b5cfc92261 santactl/sync: Check that singleevent took an argument 2016-04-11 17:52:03 -04:00
Russell Hancox
079f3e3868 santactl/sync: Re-organize 'main' to bail earlier if config is invalid 2016-04-11 17:51:47 -04:00
Russell Hancox
15a6d58785 santactl/sync: Add long help, document --clean flag 2016-04-11 17:51:08 -04:00
Russell Hancox
a404498f8a santactl help: If command doesn't have long help, use short help. 2016-04-11 17:40:58 -04:00
Russell Hancox
0d133e2df6 Project: Enable code coverage for "All" test target 2016-04-11 17:40:00 -04:00
Russell Hancox
488b28bfd5 SantaGUI: Log to syslog 2016-04-11 15:15:03 -04:00
Russell Hancox
0fceb7b2e1 SantaGUI: Post notifications to main thread using dispatch_async 2016-04-11 15:14:52 -04:00
Russell Hancox
a79d1a98e7 santactl/fileinfo: Only print "Signing Chain" header if certificates array isn't empty 2016-04-08 16:21:15 -04:00
Russell Hancox
43434fd445 santactl/fileinfo: Don't crash on <512b files 2016-04-08 16:20:49 -04:00
Russell Hancox
492e523884 Project: Move enums in SNTCommonEnums to NS_ENUM, part 2 2016-04-08 15:41:26 -04:00
Russell Hancox
3d1fdb7a2b Project: Move enums in SNTCommonEnums to NS_ENUM, part 1 2016-04-08 15:17:32 -04:00
Russell Hancox
95a4bf0ec7 santad: Ensure launchd/santad rules are whitelisted on every startup
If they weren't already and the database is not new, log an error.
2016-04-08 15:16:12 -04:00
Russell Hancox
0d4f261e14 santad: Have SNTRuleTable return NSError when failing to add rules so user can see why 2016-04-08 15:07:43 -04:00
Russell Hancox
e96288b41b santad: Exclusive-lock rules.db when opening 2016-04-08 12:11:08 -04:00
Russell Hancox
deda1abcf7 SantaGUI: Detect value type for silenced notifications to prevent crashes from bad plist 2016-04-08 10:32:48 -04:00
Russell Hancox
ee79d75483 santad: Set ThrottleInterval to 1s. 2016-04-07 17:03:49 -04:00
Russell Hancox
0e9e445ddf SantaGUI: Reconnect when listener loses a client.
Also move WEAKIFY/STRONGIFY macros into their own header.
2016-04-07 17:03:09 -04:00
Russell Hancox
e64720bcd9 Project: Tell Travis to use xcode7 2016-04-07 15:33:10 -04:00
Russell Hancox
6e27590b57 SantaGUI: Add 'dismiss for a day' checkbox.
Fixes #39.
2016-04-07 14:40:33 -04:00
Russell Hancox
916c3c7a2a SNTXPCConnection: Re-add forced establishment of clients, better tests.
Previously SNTXPCConnection had two-way validation which, due to the method of
implementation, forced a client to connect to a server straight away. Once that
was removed, it meant invalidationHandlers aren't called if either end dies
before the connection is established.

This also puts back the acceptedHandler, which can be used to know when the
connection has finished being established (particularly useful on the server
side), updates the __weak stuff to use WEAKIFY/STRONGIFY macros (and now
actually switch them back to strong within each block) and make the
tests a lot better by using in-process anonymousListener's rather than
lots of mocking.
2016-04-06 23:25:55 -04:00
Russell Hancox
8a5fde8ceb LogicTests: Stop instrumenting program flow, it causes 100s of log lines during tests 2016-04-06 17:30:08 -04:00
Russell Hancox
f5bd9bde7f SantaGUI: Use ultralight system font for title of message window 2016-04-06 15:23:18 -04:00
Russell Hancox
b987f61924 SantaGUI: Fix centering constraint for publisher label 2016-04-06 15:22:21 -04:00
Russell Hancox
482b51a2f9 santactl/sync: Fix userAgent not being set 2016-04-05 14:52:48 -04:00
Russell Hancox
93f2078eda SantaGUI: Fix some constraint bugs in MessageWindow 2016-04-04 18:15:20 -04:00
Russell Hancox
158ae11e61 Tests: Remove old stuff from XPCConnectionTest 2016-04-01 17:53:37 -04:00
Russell Hancox
d282388266 santactl/sync: Release certificate after use 2016-04-01 17:31:24 -04:00
Russell Hancox
6ecdfcba38 santactl/sync: If unable to find client identity, let default handling occur. 2016-04-01 09:54:15 -04:00
Russell Hancox
88dc8a547e README: Add video of block event, as an example 2016-03-31 13:51:35 -04:00
Russell Hancox
58e24b3c11 santagui: Remove old comment, don't activateIgnoringOtherApps twice. 2016-03-31 09:27:38 -04:00
Russell Hancox
5f1b3a2284 santad: Initialize ppath to (null) before calling proc_pidpath 2016-03-30 16:12:52 -04:00
Russell Hancox
31be2584f2 Conf: Use facility instead of sender for santad/santactl logs 2016-03-29 18:00:30 -04:00
Russell Hancox
a2311e5128 santad: Attempt to load santa-driver before connecting to it 2016-03-29 14:08:29 -04:00
Russell Hancox
e94d42187b santactl/sync: Don't log successful stages
Currently a standard succesful sync will print 4 success lines, one for
each stage that was run, followed by a line that the entire sync was
successful. As each stage will also log if it did anything, these
success messages aren't useful. Instead, just log if they failed.
2016-03-25 16:44:06 -04:00
Russell Hancox
2b99cc3f62 Logging: strncmp for binaryName 2016-03-25 14:20:46 -04:00
Russell Hancox
cb7f782893 santad: Fix typo in cert protection error 2016-03-23 17:45:08 -04:00
Russell Hancox
d5a0f8a74b Logging: Remove extraneous ; 2016-03-23 16:46:38 -04:00
Russell Hancox
2ebd71df24 santactl/sync: Fix single-event upload with extra arguments 2016-03-23 16:46:21 -04:00
Russell Hancox
479203f47c santa-driver: Style and type cleanups, inlining some small functions 2016-03-22 15:38:48 -04:00
Russell Hancox
022b9209d9 LogicTests: Delete resources that aren't used anymore. 2016-03-22 15:36:56 -04:00
Russell Hancox
771c2c868f SantaGUI: Increase contrast of user-defined block window messages. 2016-03-21 18:00:02 -04:00
Russell Hancox
5285a728b1 santa-driver: Don't record fileop events from santad 2016-03-21 16:15:20 -04:00
Russell Hancox
41e6583920 SantaGUI: Improve accessiblity of message dialog
+ VoiceOver: add more useful label descriptions
+ VoiceOver: skip some fields
+ Color: increase contrast
2016-03-21 13:49:50 -04:00
Russell Hancox
cbb60b3a05 SantaGUI: Have daemon reply when setting notification listener so GUI can ensure it connected 2016-03-17 17:55:31 -04:00
Russell Hancox
cf1d1e3557 santa-driver: Better handle secondary volumes 2016-03-15 15:10:41 -04:00
Russell Hancox
8f05ee7d79 santa-driver: Rename some action types 2016-03-15 12:53:44 -04:00
Russell Hancox
641bd07c0b Project: New icon 2016-03-14 16:38:07 -04:00
Russell Hancox
7d9dc0a853 Tests: Fix kernel tests 2016-03-14 16:13:28 -04:00
Russell Hancox
e0a46be1b7 santactl/fileinfo: When resolving path, store bundle ref if possible. 2016-03-14 12:55:20 -04:00
Russell Hancox
fd82c67b56 santactl/fileinfo: Add disk image file type 2016-03-14 12:55:20 -04:00
Russell Hancox
f0a83b6f19 santactl/fileinfo: Add simultaneous hashing. 2016-03-14 12:52:25 -04:00
Russell Hancox
736b45bb46 SNTXPCConnection: Remove client validation of server
Now that santad<->SantaGUI work more like the client/server they are,
having an SNTXPCConnection 'client' validate its server is no longer necessary.
Having the validation in the 'server' only simplifies the code.
2016-03-11 17:06:43 -05:00
Russell Hancox
8eae9b7cb7 santad/SantaGUI: Refactor GUI<>santad connection logic and add queuing.
Instead of having santad create a listener for SantaGUI to connect to
and then reverse the client-server relationship, have SantaGUI create an
anonymous listener that it sends to santad using the control interface.

Also add a queue for notifications so that blocks that occur while
SantaGUI isn't running will show up once it starts.
2016-03-11 14:58:12 -05:00
Russell Hancox
0aa2d2c613 santactl/fileinfo: Print useful info when codesign validation fails 2016-03-10 18:23:21 -05:00
Russell Hancox
ad43db10f2 Tests: Attempt to fix FileWatcher tests 2016-03-10 17:17:02 -05:00
Russell Hancox
606f507422 Project: Update CocoaPods 2016-03-10 16:34:08 -05:00
Russell Hancox
36b7778883 LogicTests: Fix SNTXPCConnection test 2016-03-10 15:53:40 -05:00
Russell Hancox
7b032a6a73 Project: Travis, build in local dir instead of DerivedData 2016-03-10 15:53:27 -05:00
Russell Hancox
0e00237e44 Project: Add clang-format file, apply most of the fixes it suggested 2016-03-10 15:53:06 -05:00
Russell Hancox
e9ec9a7d7f santad: Log quarantine URL if one exists.
Fixes #34
2016-03-10 13:24:31 -05:00
Russell Hancox
6834507f3a XPC: Allow multiple XPC clients to a server 2016-03-10 12:21:49 -05:00
Russell Hancox
90e99255b1 santa-driver/santad: Split decision making and logging onto 2 data queues
This resolves an issue where the data queue can be overwhelmed by logging requests and fail to respond to decisions for an extended period of time.
2016-03-10 12:21:17 -05:00
Russell Hancox
b6487000a3 SNTFileInfo: Use NSBundle to find executable path in bundles.
Fixes #37
2016-03-10 12:19:52 -05:00
Russell Hancox
18ce2f72ed Config: Fix config reloading 2016-03-10 12:18:05 -05:00
Russell Hancox
8a2d04bf69 santactl/rule: Fix print error 2016-03-09 15:41:27 -05:00
Russell Hancox
a210ffecec Logging: Create one ASL client per-thread. 2016-03-07 17:31:31 -05:00
Russell Hancox
aff96e8144 Config: Warn if SyncBaseURL is an invalid URL 2016-03-07 12:36:00 -05:00
Russell Hancox
3d4c639bb4 santactl/sync: Fix logic when auto-detecting certificates.
Now, instead of assuming an identity can be found that the server asked
for, look for a chain of certs resulting in an identity that matches the
server's request.
2016-03-07 12:32:32 -05:00
Russell Hancox
d507e79505 santad: Fix quarantine data collection.
This previously didn't work for root (santactl fileinfo was fine)
because quarantine data is per-user.
2016-03-07 12:30:36 -05:00
Russell Hancox
d3e242ff42 Project: Update Travis settings 2016-02-05 19:37:11 -05:00
Russell Hancox
df7616403d SantaGUI: Show entire SHA-256 in fixed-width font 2016-01-14 16:51:29 -05:00
Russell Hancox
962b15517a SantaGUI: Add a transparent button to be the first responder, so tabbing the dialog works. 2015-12-28 17:24:29 -05:00
Russell Hancox
d295f2391f santactl/sync: In --debug log the full NSError for failed requests 2015-12-15 12:36:07 -05:00
Russell Hancox
c042222eea santad: Add user/group info to file changelogs also 2015-12-14 22:32:59 -05:00
Russell Hancox
63f6596bc2 santactl: Rename binaryinfo -> fileinfo. 2015-12-14 18:09:40 -05:00
Russell Hancox
d8a8aba0ea SNTFileInfo: Move machoType method to binaryinfo command, add XAR archive detection. 2015-12-14 17:25:32 -05:00
Russell Hancox
d9d9682029 santactl/sync: Let related-binary search take up to 5s 2015-12-14 16:37:19 -05:00
Russell Hancox
4a27a8ac70 Rakefile: Use Xcode to figure out where built products went, to avoid relying on particular Xcode settings. 2015-12-14 16:36:11 -05:00
Russell Hancox
32857ff304 Project: Apply latest Xcode recommendations 2015-12-14 16:35:34 -05:00
Russell Hancox
375bfd3862 santa-driver: Put locks around vnode_pid_map, use an OSObject subclass to store PID/PPID.
Put a R/W lock around vnode_pid_map_ to prevent use-after-free.
Create SantaPIDAndPPID to use instead of creating and then scanning strings.
Also rename SantaMessage -> SantaCachedDecision, as that's what it is.
2015-12-14 16:34:38 -05:00
Russell Hancox
9430c41b8a santad: Include user and group names in execution logs 2015-12-11 12:58:09 -05:00
Russell Hancox
9b342e146a santactl/sync: Include code sign info with related executables and encode to dict. 2015-12-10 17:37:22 -05:00
Russell Hancox
e5685f2959 santad: Don't try to add empty argument to array when processing execution arguments 2015-12-10 17:02:11 -05:00
Russell Hancox
4150feece2 santactl/sync: When uploading events for bundles, look for other bundled executables.
Many application bundles have related helper tools, which will individually need to be whitelisted unless they're covered by a certificate. To help make user's lives easier, when an event is triggered for a binary inside a bundle look for other executables in the same bundle and upload an event for those too (with an obvious tag) so that the server can let the user vote to whitelist all the binaries together.
2015-12-10 17:01:49 -05:00
Russell Hancox
6879ec5deb santa-driver: in DecisionManager free locks before anything else 2015-12-10 16:56:13 -05:00
Russell Hancox
28ad00ffad SantaGUI: Split block messages into unknown and banned.
This is so that a message can be configured for banned executables without having to provide a custom message for every single one.
2015-12-10 12:13:52 -05:00
Russell Hancox
bf51049fbf santa-driver: Save pid/ppid from VFS context when decision making for use when logging
Previously the execution logging from fileop didn't work when using posix_spawn as proc_selfpid/proc_selfppid still refer to the process calling posix_spawn. We can get the correct pid/ppid from the vfs_context in the vnode scope but we can't log executions from there as the arguments end up being wrong. Instead, save the vnode_id->pid/ppid mapping in the vnode scope and use that in the fileop scope for logging.
2015-12-10 12:12:38 -05:00
Russell Hancox
36189e9122 santad: Update SNTFileInfo to always get strings from bundle Info.plist data.
Also perform a one-time update of any events created before this change.
2015-12-04 13:09:56 -05:00
Russell Hancox
4c747463ac santad: Separate execution requests and logging into separate queues with appropriate priorities. 2015-12-04 12:39:26 -05:00
Russell Hancox
b4b1fbb9e6 santad: Run watchdog thread loop once before sleeping 2015-10-31 14:01:44 -04:00
Russell Hancox
209eaff3c6 SNTFileInfo: Embed SHA hashing loop in an autoreleasepool to avoid temporary RAM spikes 2015-10-31 13:45:47 -04:00
Russell Hancox
c3f70703fd santactl/status: Expose peak CPU/RAM use from santad. 2015-10-29 16:20:57 -04:00
Russell Hancox
f2967e7b94 santad: Switch watchdog CPU counter from rusage to task_info, capture peak CPU/RAM use. 2015-10-29 16:20:25 -04:00
Russell Hancox
77c46b5c43 SNTFileInfo: switch from NSData to NSFileHandle.
This seems to work much better than NSData with either mapped (SIGBUS when file is deleted) or uncached (ballooning memory use) reading.
2015-10-29 16:17:12 -04:00
Russell Hancox
5fda5bc081 santactl/binaryinfo: Only print bundle lines if bundle info is present 2015-10-29 12:35:27 -04:00
Russell Hancox
33a7b38c6a SNTFileInfo: check for NULL ptrs when parsing for embedded plist 2015-10-27 18:35:11 -04:00
Russell Hancox
2a7c0bd58c SNTFileInfo: Go back to using mmap, uncached read balloons memory use 2015-10-27 18:08:16 -04:00
Russell Hancox
86e4d0db0f santactl: Use yyyy instead of YYYY in NSDateFormatter 2015-10-27 17:58:23 -04:00
420 changed files with 31898 additions and 11596 deletions

5
.bazelrc Normal file
View File

@@ -0,0 +1,5 @@
build --apple_generate_dsym --define=apple.propagate_embedded_extra_outputs=yes
build --copt=-Werror
build --copt=-Wall
build --copt=-Wno-error=deprecated-declarations

1
.bazelversion Normal file
View File

@@ -0,0 +1 @@
5.0.0

32
.clang-format Normal file
View File

@@ -0,0 +1,32 @@
Language: ObjC
BasedOnStyle: Google
IndentWidth: 2
ObjCBlockIndentWidth: 2
ContinuationIndentWidth: 2
# For ObjC, the line limit is 100
ColumnLimit: 100
# Allow short case statements to be on a single line
AllowShortCaseLabelsOnASingleLine: true
AllowShortLoopsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
# Allow spaces in NSArray/NSDictionary literals @[ and @{
SpacesInContainerLiterals: true
# For pointers, always put the * next to the variable name.
DerivePointerAlignment: false
PointerAlignment: Right
---
Language: Cpp
Standard: Cpp11
BasedOnStyle: Google
# For C++, the line limit is 80
ColumnLimit: 80

View File

@@ -0,0 +1,13 @@
name: Check Markdown links
on:
pull_request:
paths:
- "**.md"
jobs:
markdown-link-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: gaurav-nelson/github-action-markdown-link-check@v1

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

@@ -0,0 +1,63 @@
name: CI
on:
push:
branches:
- '*'
paths:
- 'Source/**'
pull_request:
branches:
- main
paths:
- 'Source/**'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run linters
run: ./Testing/lint.sh
build_userspace:
strategy:
fail-fast: false
matrix:
os: [macos-10.15, macos-11, macos-12]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Build Userspace
run: bazel build --apple_generate_dsym -c opt :release --define=SANTA_BUILD_TYPE=adhoc
unit_tests:
strategy:
fail-fast: false
matrix:
os: [macos-10.15, macos-11, macos-12]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Run All Tests
run: bazel test :unit_tests --define=SANTA_BUILD_TYPE=adhoc --test_output=errors
test_coverage:
runs-on: macos-11
steps:
- uses: actions/checkout@v2
- name: Generate test coverage
run: sh ./generate_cov.sh
- name: Coveralls
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: ./bazel-out/_coverage/_coverage_report.dat
flag-name: Unit
benchmark:
runs-on: macos-11
steps:
- uses: actions/checkout@v2
- name: Run All Tests
run: ./Testing/benchmark.sh

13
.github/workflows/continuous.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
name: continuous
on:
schedule:
- cron: '* 10 * * *' # Every day at 10:00 UTC
workflow_dispatch: # Allows you to run this workflow manually from the Actions tab
jobs:
preqs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: Checks for flaky tests
run: bazel test --test_strategy=exclusive --test_output=errors --runs_per_test 50 -t- :unit_tests --define=SANTA_BUILD_TYPE=adhoc

24
.gitignore vendored
View File

@@ -1,8 +1,20 @@
.DS_Store
Build
Dist
*.profraw
*.provisionprofile
bazel-*
Pods
Santa.xcodeproj/xcuserdata
Santa.xcodeproj/project.xcworkspace
Santa.xcworkspace/xcuserdata
Santa.xcworkspace/xcshareddata
Santa.xcodeproj/*
Santa.xcworkspace/*
CoverageData/*
*.tulsiconf-user
xcuserdata
tulsigen-*
*.crt
*.key
*.pem
*.p12
*.keychain
*.swp
compile_commands.json
.cache/
.vscode/*

View File

@@ -1,9 +0,0 @@
---
language: objective-c
cache: cocoapods
before_install:
- gem install cocoapods xcpretty
script:
- xcodebuild -workspace Santa.xcworkspace -scheme All build test CODE_SIGN_IDENTITY='' | xcpretty -sc && exit ${PIPESTATUS[0]}

206
BUILD Normal file
View File

@@ -0,0 +1,206 @@
load("@build_bazel_rules_apple//apple:versioning.bzl", "apple_bundle_version")
load("//:helper.bzl", "run_command")
package(default_visibility = ["//:santa_package_group"])
licenses(["notice"])
exports_files(["LICENSE"])
# The version label for mac_* rules.
apple_bundle_version(
name = "version",
build_label_pattern = ".*santa_{release}\\.{build}",
build_version = "{release}.{build}",
capture_groups = {
"release": "\\d{4}\\.\\d+",
"build": "\\d+",
},
fallback_build_label = "santa_9999.1.1",
short_version_string = "{release}",
)
# Used to detect release builds
config_setting(
name = "release_build",
values = {"define": "SANTA_BUILD_TYPE=release"},
visibility = [":santa_package_group"],
)
# Adhoc signed - provisioning profiles are not used.
# Used for CI runs and dev builds when SIP is disabled.
config_setting(
name = "adhoc_build",
values = {"define": "SANTA_BUILD_TYPE=adhoc"},
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 launchctl unload /Library/LaunchDaemons/com.google.santa.metricservice.plist 2>/dev/null
sudo launchctl unload /Library/LaunchDaemons/com.google.santa.syncservice.plist 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
sudo launchctl load /Library/LaunchDaemons/com.google.santa.metricservice.plist
sudo launchctl load /Library/LaunchDaemons/com.google.santa.syncservice.plist
launchctl load /Library/LaunchAgents/com.google.santa.plist
""",
)
run_command(
name = "reload",
srcs = [
"//Source/santa:Santa",
],
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/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.santa.metricservice.plist",
"Conf/com.google.santa.syncservice.plist",
"Conf/com.google.santad.plist",
"Conf/com.google.santa.plist",
"Conf/com.google.santa.newsyslog.conf",
"Conf/Package/Distribution.xml",
"Conf/Package/notarization_tool.sh",
"Conf/Package/package_and_sign.sh",
"Conf/Package/postinstall",
"Conf/Package/preinstall",
],
outs = ["santa-release.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 -H $${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
;;
*santametricservice.dSYM*Info.plist)
mkdir -p $(@D)/dsym
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santametricservice.dSYM
;;
*santasyncservice.dSYM*Info.plist)
mkdir -p $(@D)/dsym
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santasyncservice.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,
)
test_suite(
name = "unit_tests",
tests = [
"//Source/common:unit_tests",
"//Source/santactl:unit_tests",
"//Source/santad:unit_tests",
"//Source/santametricservice:unit_tests",
"//Source/santasyncservice:unit_tests",
],
)
test_suite(
name = "benchmarks",
tests = [
"//Source/santad:SNTApplicationBenchmark",
],
)

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](http://google-styleguide.googlecode.com/svn/trunk/objcguide.xml)
or the [Google C++ Style Guide](http://google-styleguide.googlecode.com/svn/trunk/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

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<installer-gui-script minSpecVersion="1">
<title>Santa</title>
<options customize="never" allow-external-scripts="no" hostArchitectures="x86_64,arm64" />
<choices-outline>
<line choice="default" />
</choices-outline>
<choice id="default">
<pkg-ref id="com.google.santa"/>
</choice>
<pkg-ref id="com.google.santa">app.pkg</pkg-ref>
</installer-gui-script>

View File

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

View File

@@ -0,0 +1,6 @@
#!/bin/bash
# Example NOTARIZATION_TOOL wrapper.
/usr/bin/xcrun altool --notarize-app "${2}" --primary-bundle-id "${4}" \
-u "${NOTARIZATION_USERNAME}" -p "${NOTARIZATION_PASSWORD}"

185
Conf/Package/package_and_sign.sh Executable file
View File

@@ -0,0 +1,185 @@
#!/bin/bash
# This script signs all of Santa's components, verifies the signatures,
# notarizes all of the components, staples them, packages them up, signs the
# package, notarizes the package, puts the package in a DMG and notarizes the
# DMG. It also outputs a single release tarball.
# All of the following environment variables are required.
# RELEASE_ROOT is a required environment variable that points to the root
# of an extracted release tarball produced with the :release and :release_driver
# rules in Santa's main BUILD file.
[[ -n "${RELEASE_ROOT}" ]] || die "RELEASE_ROOT unset"
# SIGNING_IDENTITY, SIGNING_TEAMID and SIGNING_KEYCHAIN are required environment
# variables specifying the identity and keychain to pass to the codesign tool
# and the team ID to use for verification.
[[ -n "${SIGNING_IDENTITY}" ]] || die "SIGNING_IDENTITY unset"
[[ -n "${SIGNING_TEAMID}" ]] || die "SIGNING_TEAMID unset"
[[ -n "${SIGNING_KEYCHAIN}" ]] || die "SIGNING_KEYCHAIN unset"
# INSTALLER_SIGNING_IDENTITY and INSTALLER_SIGNING_KEYCHAIN are required
# environment variables specifying the identity and keychain to use when signing
# the distribution package.
[[ -n "${INSTALLER_SIGNING_IDENTITY}" ]] || die "INSTALLER_SIGNING_IDENTITY unset"
[[ -n "${INSTALLER_SIGNING_KEYCHAIN}" ]] || die "INSTALLER_SIGNING_KEYCHAIN unset"
# NOTARIZATION_TOOL is a required environment variable pointing to a wrapper
# tool around the tool to use for notarization. The tool must take 2 flags:
# --file
# - pointing at a zip file containing the artifact to notarize
# --primary-bundle-id
# - specifying the CFBundleID of the artifact being notarized
[[ -n "${NOTARIZATION_TOOL}" ]] || die "NOTARIZATION_TOOL unset"
# ARTIFACTS_DIR is a required environment variable pointing at a directory to
# place the output artifacts in.
[[ -n "${ARTIFACTS_DIR}" ]] || die "ARTIFACTS_DIR unset"
################################################################################
function die {
echo "${@}"
exit 2
}
readonly INPUT_APP="${RELEASE_ROOT}/binaries/Santa.app"
readonly INPUT_SYSX="${INPUT_APP}/Contents/Library/SystemExtensions/com.google.santa.daemon.systemextension"
readonly INPUT_SANTACTL="${INPUT_APP}/Contents/MacOS/santactl"
readonly INPUT_SANTABS="${INPUT_APP}/Contents/MacOS/santabundleservice"
readonly INPUT_SANTAMS="${INPUT_APP}/Contents/MacOS/santametricservice"
readonly INPUT_SANTASS="${INPUT_APP}/Contents/MacOS/santasyncservice"
readonly RELEASE_NAME="santa-$(/usr/bin/defaults read "${INPUT_APP}/Contents/Info.plist" CFBundleShortVersionString)"
readonly SCRATCH=$(/usr/bin/mktemp -d "${TMPDIR}/santa-"XXXXXX)
readonly APP_PKG_ROOT="${SCRATCH}/app_pkg_root"
readonly APP_PKG_SCRIPTS="${SCRATCH}/pkg_scripts"
readonly ENTITLEMENTS="${SCRATCH}/entitlements"
readonly SCRIPT_PATH="$(/usr/bin/dirname -- ${BASH_SOURCE[0]})"
/bin/mkdir -p "${APP_PKG_ROOT}" "${APP_PKG_SCRIPTS}" "${ENTITLEMENTS}"
readonly DMG_PATH="${ARTIFACTS_DIR}/${RELEASE_NAME}.dmg"
readonly TAR_PATH="${ARTIFACTS_DIR}/${RELEASE_NAME}.tar.gz"
# Sign all of binaries/bundles. Maintain inside-out ordering where necessary
for ARTIFACT in "${INPUT_SANTACTL}" "${INPUT_SANTABS}" "${INPUT_SANTAMS}" "${INPUT_SANTASS}" "${INPUT_SYSX}" "${INPUT_APP}"; do
BN=$(/usr/bin/basename "${ARTIFACT}")
EN="${ENTITLEMENTS}/${BN}.entitlements"
echo "extracting ${BN} entitlements"
/usr/bin/codesign -d --entitlements "${EN}" "${ARTIFACT}"
if [[ -s "${EN}" ]]; then
EN="--entitlements ${EN}"
else
EN=""
fi
echo "codesigning ${BN}"
/usr/bin/codesign --sign "${SIGNING_IDENTITY}" --keychain "${SIGNING_KEYCHAIN}" \
${EN} --timestamp --force --generate-entitlement-der \
--options library,kill,runtime "${ARTIFACT}"
done
# Notarize all the bundles
for ARTIFACT in "${INPUT_SYSX}" "${INPUT_APP}"; do
BN=$(/usr/bin/basename "${ARTIFACT}")
echo "zipping ${BN}"
/usr/bin/zip -9r "${SCRATCH}/${BN}.zip" "${ARTIFACT}"
echo "notarizing ${BN}"
PBID=$(/usr/bin/defaults read "${ARTIFACT}/Contents/Info.plist" CFBundleIdentifier)
"${NOTARIZATION_TOOL}" --file "${SCRATCH}/${BN}.zip" --primary-bundle-id "${PBID}"
done
# Staple the App.
for ARTIFACT in "${INPUT_APP}"; do
BN=$(/usr/bin/basename "${ARTIFACT}")
echo "stapling ${BN}"
/usr/bin/xcrun stapler staple "${ARTIFACT}"
done
# Ensure _CodeSignature/CodeResources files have 0644 permissions so they can
# be verified without using sudo.
/usr/bin/find "${RELEASE_ROOT}/binaries" -type f -name CodeResources -exec chmod 0644 {} \;
/usr/bin/find "${RELEASE_ROOT}/binaries" -type d -exec chmod 0755 {} \;
/usr/bin/find "${RELEASE_ROOT}/conf" -type f -name "com.google.santa*" -exec chmod 0644 {} \;
echo "verifying signatures"
/usr/bin/codesign -vv -R="certificate leaf[subject.OU] = ${SIGNING_TEAMID}" \
"${RELEASE_ROOT}/binaries/"* || die "bad signature"
echo "creating fresh release tarball"
/bin/mkdir -p "${SCRATCH}/tar_root/${RELEASE_NAME}"
/bin/cp -r "${RELEASE_ROOT}/binaries" "${SCRATCH}/tar_root/${RELEASE_NAME}"
/bin/cp -r "${RELEASE_ROOT}/conf" "${SCRATCH}/tar_root/${RELEASE_NAME}"
/bin/cp -r "${RELEASE_ROOT}/dsym" "${SCRATCH}/tar_root/${RELEASE_NAME}"
/usr/bin/tar -C "${SCRATCH}/tar_root" -czvf "${TAR_PATH}" "${RELEASE_NAME}" || die "failed to create release tarball"
echo "creating app pkg"
/bin/mkdir -p "${APP_PKG_ROOT}/Applications" \
"${APP_PKG_ROOT}/Library/LaunchAgents" \
"${APP_PKG_ROOT}/Library/LaunchDaemons" \
"${APP_PKG_ROOT}/private/etc/asl" \
"${APP_PKG_ROOT}/private/etc/newsyslog.d"
/bin/cp -vXR "${RELEASE_ROOT}/binaries/Santa.app" "${APP_PKG_ROOT}/Applications/"
/bin/cp -vX "${RELEASE_ROOT}/conf/com.google.santad.plist" "${APP_PKG_ROOT}/Library/LaunchDaemons/"
/bin/cp -vX "${RELEASE_ROOT}/conf/com.google.santa.plist" "${APP_PKG_ROOT}/Library/LaunchAgents/"
/bin/cp -vX "${RELEASE_ROOT}/conf/com.google.santa.bundleservice.plist" "${APP_PKG_ROOT}/Library/LaunchDaemons/"
/bin/cp -vX "${RELEASE_ROOT}/conf/com.google.santa.metricservice.plist" "${APP_PKG_ROOT}/Library/LaunchDaemons/"
/bin/cp -vX "${RELEASE_ROOT}/conf/com.google.santa.syncservice.plist" "${APP_PKG_ROOT}/Library/LaunchDaemons/"
/bin/cp -vX "${RELEASE_ROOT}/conf/com.google.santa.asl.conf" "${APP_PKG_ROOT}/private/etc/asl/"
/bin/cp -vX "${RELEASE_ROOT}/conf/com.google.santa.newsyslog.conf" "${APP_PKG_ROOT}/private/etc/newsyslog.d/"
/bin/cp -vXL "${SCRIPT_PATH}/preinstall" "${APP_PKG_SCRIPTS}/"
/bin/cp -vXL "${SCRIPT_PATH}/postinstall" "${APP_PKG_SCRIPTS}/"
/bin/chmod +x "${APP_PKG_SCRIPTS}/"*
# Disable bundle relocation.
/usr/bin/pkgbuild --analyze --root "${APP_PKG_ROOT}" "${SCRATCH}/component.plist"
/usr/bin/plutil -replace BundleIsRelocatable -bool NO "${SCRATCH}/component.plist"
/usr/bin/plutil -replace BundleIsVersionChecked -bool NO "${SCRATCH}/component.plist"
/usr/bin/plutil -replace BundleOverwriteAction -string upgrade "${SCRATCH}/component.plist"
/usr/bin/plutil -replace ChildBundles -json "[]" "${SCRATCH}/component.plist"
# Build app package
/usr/bin/pkgbuild --identifier "com.google.santa" \
--version "$(echo "${RELEASE_NAME}" | cut -d - -f2)" \
--root "${APP_PKG_ROOT}" \
--component-plist "${SCRATCH}/component.plist" \
--scripts "${APP_PKG_SCRIPTS}" \
"${SCRATCH}/app.pkg"
# Build signed distribution package
echo "productbuild pkg"
/bin/mkdir -p "${SCRATCH}/${RELEASE_NAME}"
/usr/bin/productbuild \
--distribution "${SCRIPT_PATH}/Distribution.xml" \
--package-path "${SCRATCH}" \
--sign "${INSTALLER_SIGNING_IDENTITY}" --keychain "${INSTALLER_SIGNING_KEYCHAIN}" \
"${SCRATCH}/${RELEASE_NAME}/${RELEASE_NAME}.pkg"
echo "verifying pkg signature"
/usr/sbin/pkgutil --check-signature "${SCRATCH}/${RELEASE_NAME}/${RELEASE_NAME}.pkg" || die "bad pkg signature"
echo "notarizing pkg"
"${NOTARIZATION_TOOL}" --file "${SCRATCH}/${RELEASE_NAME}/${RELEASE_NAME}.pkg" \
--primary-bundle-id "com.google.santa"
echo "stapling pkg"
/usr/bin/xcrun stapler staple "${SCRATCH}/${RELEASE_NAME}/${RELEASE_NAME}.pkg" || die "failed to staple pkg"
echo "wrapping pkg in dmg"
/usr/bin/hdiutil create -fs HFS+ -format UDZO \
-volname "${RELEASE_NAME}" \
-ov -imagekey zlib-level=9 \
-srcfolder "${SCRATCH}/${RELEASE_NAME}/" "${DMG_PATH}" || die "failed to wrap pkg in dmg"
echo "notarizing dmg"
"${NOTARIZATION_TOOL}" --file "${DMG_PATH}" --primary-bundle-id "com.google.santa"
echo "stapling dmg"
/usr/bin/xcrun stapler staple "${DMG_PATH}" || die "failed to staple dmg"

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,19 +9,28 @@
# Restart syslogd to pick up ASL configuration change
/usr/bin/killall -HUP syslogd
/sbin/kextload /Library/Extensions/santa-driver.kext
# Create hopefully useful symlink for santactl
mkdir -p /usr/local/bin
/bin/ln -sf /Applications/Santa.app/Contents/MacOS/santactl /usr/local/bin/santactl
sleep 1
# Remove the kext before com.google.santa.daemon loads if the SystemExtension is already present.
/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
sleep 1
# Load com.google.santa.bundleservice
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santa.bundleservice.plist
# Create hopefully useful symlink for santactl
/bin/ln -sf /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin
# Load com.google.santa.metricservice
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santa.metricservice.plist
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
# Load com.google.santa.syncservice
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santa.syncservice.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,28 @@
[[ $3 != "/" ]] && exit 0
/bin/launchctl remove com.google.santad
/bin/launchctl remove com.google.santad || true
/bin/launchctl remove com.google.santa.bundleservice || true
/bin/launchctl remove com.google.santa.metricservice || true
/bin/launchctl remove com.google.santa.syncservice || 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

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

View File

@@ -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,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.metricservice</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/Santa.app/Contents/MacOS/santametricservice</string>
<string>--syslog</string>
</array>
<key>MachServices</key>
<dict>
<key>com.google.santa.metricservice</key>
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</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 * Z

View File

@@ -3,10 +3,11 @@
<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>
<string>--syslog</string>
</array>
<key>RunAtLoad</key>
<true/>

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>
<false/>
<key>KeepAlive</key>
<false/>
</dict>
</plist>

View File

@@ -1,25 +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>
</array>
<key>MachServices</key>
<dict>
<key>SantaXPCNotifications</key>
<true/>
<key>SantaXPCControl</key>
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true />
<key>ProcessType</key>
<string>Interactive</string>
<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,54 +5,84 @@ 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 metric service
/bin/launchctl remove com.google.santa.metricservice >/dev/null 2>&1
# Unload sync service
/bin/launchctl remove com.google.santa.syncservice >/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
/bin/rm /etc/asl/com.google.santa.asl.conf
# Copy new files.
/bin/cp -r ${SOURCE}/binaries/santa-driver.kext /Library/Extensions
/bin/cp -r ${SOURCE}/binaries/Santa.app /Applications
/bin/ln -s /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin
/bin/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
/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.santa.metricservice.plist /Library/LaunchDaemons
/bin/cp ${CONF}/com.google.santa.syncservice.plist /Library/LaunchDaemons
/bin/cp ${CONF}/com.google.santad.plist /Library/LaunchDaemons
/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 com.google.santa.metricservice
/bin/launchctl load /Library/LaunchDaemons/com.google.santa.metricservice.plist
# Load com.google.santa.syncservice
/bin/launchctl load /Library/LaunchDaemons/com.google.santa.syncservice.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

40
Conf/uninstall.sh Executable file
View File

@@ -0,0 +1,40 @@
#!/bin/bash
# Uninstalls Santa from the boot volume, clearing up everything but logs/configs.
# Unloads the kernel extension, services, and deletes component files.
# If a user is logged in, also unloads the GUI agent.
[ "$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
# remove helper XPC services
/bin/launchctl remove com.google.santa.bundleservice
/bin/launchctl remove com.google.santa.metricservice
/bin/launchctl remove com.google.santa.syncservice
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 /Library/LaunchDaemons/com.google.santa.metricservice.plist
/bin/rm -f /Library/LaunchDaemons/com.google.santa.syncservice.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*
exit 0

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,43 @@
/// 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 <cstdint>
#include <iostream>
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 <cstdint>
#include <iostream>
#include <vector>
#include <SNTRule.h>
#include <SNTSyncConstants.h>
#include <SNTSyncRuleDownload.h>
#include <SNTSyncState.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;
}
SNTSyncState *state = [[SNTSyncState alloc] init];
if (!state) {
return 0;
}
SNTSyncRuleDownload *obj = [[SNTSyncRuleDownload 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,58 @@
/// 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 <cstdint>
#include <iostream>
#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 <cstdint>
#include <iostream>
#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 <cstdint>
#include <iostream>
#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.identifier = @(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.identifier UTF8String]);
} else {
printf("Added rule for SHA-256: %s.\n", [newRule.identifier UTF8String]);
}
}
}];
return 0;
}

39
Podfile
View File

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

View File

@@ -1,24 +0,0 @@
PODS:
- FMDB (2.5):
- FMDB/standard (= 2.5)
- FMDB/common (2.5)
- FMDB/standard (2.5):
- FMDB/common
- MOLCertificate (1.1)
- MOLCodesignChecker (1.2):
- MOLCertificate (~> 1.1)
- OCMock (3.2)
DEPENDENCIES:
- FMDB
- MOLCertificate
- MOLCodesignChecker
- OCMock
SPEC CHECKSUMS:
FMDB: 96e8f1bcc1329e269330f99770ad4285d9003e52
MOLCertificate: 57fb88b33e83008d45c75644937fca61ed9f63d3
MOLCodesignChecker: e887eeeb7cd87c9b808d1682187c8a27f8ff1100
OCMock: 28def049ef47f996b515a8eeea958be7ccab2dbb
COCOAPODS: 0.39.0

221
README.md
View File

@@ -1,149 +1,144 @@
Santa [![Build Status](https://travis-ci.org/google/santa.png?branch=master)](https://travis-ci.org/google/santa)
=====
# Santa [![CI](https://github.com/google/santa/actions/workflows/ci.yml/badge.svg)](https://github.com/google/santa/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/github/google/santa/badge.svg?branch=main)](https://coveralls.io/github/google/santa?branch=main)
Santa is a binary whitelisting/blacklisting system for OS X. It consists of
a kernel extension that monitors for executions, a userland daemon that makes
execution decisions based on the contents of a SQLite database, a GUI agent that
notifies the user in case of a block decision and a command-line utility for
managing the system and synchronizing the database with a server.
<p align="center">
<img src="https://raw.githubusercontent.com/google/santa/main/Source/santa/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
</p>
Santa is not yet a 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 system
extension that monitors for executions, a daemon that makes execution decisions
based on the contents of a local 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 named because it keeps track of binaries that are naughty and nice.
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
Features
========
The Santa docs are stored in the
[Docs](https://github.com/google/santa/blob/main/docs) directory and published
at https://santa.dev.
* Multiple modes: MONITOR and LOCKDOWN. In MONITOR mode all binaries except
those marked as blacklisted will be allowed to run, whilst being logged and
recorded in the database. In LOCKDOWN mode, only whitelisted binaries are
allowed to run.
The docs include deployment options, details on how parts of Santa work and
instructions for developing Santa itself.
* Codesign listing: Binaries can be whitelisted/blacklisted by their signing
certificate, so you can trust/block all binaries by a given publisher. The
binary will only be whitelisted by certificate if its signature validates
correctly. However, a decision for a binary will override a decision for a
certificate; i.e. you can whitelist a certificate while blacklisting a binary
signed by that certificate or vice-versa.
# Get Help
* 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.
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/issues) 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.
# 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.
* 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.
* Event logging: all executions processed by the userland agent are logged and
all unknown or denied binaries are also stored in the database for upload to a
server.
* Caching: allowed binaries are cached so the processing required to make a
request is only done if the binary isn't already cached.
* 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.
# 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
allowing unknown software to exfiltrate more data about your system. As a
centrally managed component, Santa can help stop the spread of malware among a
larger fleet of machines. Additionally, Santa can aid in analyzing what is
running in your fleet.
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
========
# Security and Performance-Related Features
If you have questions or need help getting started, the
[santa-dev](https://groups.google.com/forum/#!forum/santa-dev) group is the
best place to start.
Known Issues
============
Santa is not yet a 1.0 and we have some known issues to be aware of:
# 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`. We are working on also protecting
against these avenues of attack.
* Kext communication security: the kext will only accept a connection from a
single client at a time and said client must be running as root. We haven't yet
found a good way to ensure the kext only accepts connections from a valid client.
* 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 command-line client includes a command to synchronize with a
management server, including the uploading of events that have occurred on the
machine and to download 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!)
dynamic libraries loaded with dlopen, libraries on disk that have been
replaced, or libraries loaded using `DYLD_INSERT_LIBRARIES`.
* 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: There currently isn't any.
# 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:
Building
========
```sh
git clone https://github.com/google/santa
cd santa
* [Moroz](https://github.com/groob/moroz) - A simple golang server that
serves hardcoded rules from simple configuration files.
* [Rudolph](https://github.com/airbnb/rudolph) - An AWS-based serverless sync service
primarily built on API GW, DynamoDB, and Lambda components to reduce operational burden.
Rudolph is designed to be fast, easy-to-use, and cost-efficient.
* [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.
# 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
```
* Alternatively, `santactl` can configure rules locally (without a sync
server).
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.
# Screenshots
Kext Signing
============
Kernel extensions on OS X 10.9 and later must be signed using an Apple-provided
Developer ID certificate with a kernel extension flag. Without it, the only way
to load an extension is to enable kext-dev-mode or disable SIP, depending on the
OS version.
A tool like Santa doesn't really lend itself to screenshots, so here's a video
instead.
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.
<p align="center"> <img src="https://thumbs.gfycat.com/MadFatalAmphiuma-small.gif" alt="Santa Block Video" /> </p>
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://santa.dev/development/contributing) doc.
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.

201
Rakefile
View File

@@ -1,201 +0,0 @@
WORKSPACE = 'Santa.xcworkspace'
DEFAULT_SCHEME = 'All'
OUTPUT_PATH = 'Build'
DIST_PATH = 'Dist'
BINARIES = ['Santa.app', 'santa-driver.kext']
DSYMS = ['Santa.app.dSYM', 'santa-driver.kext.dSYM', 'santad.dSYM', 'santactl.dSYM']
XCPRETTY_DEFAULTS = '-sc'
XCODEBUILD_DEFAULTS = "-workspace #{WORKSPACE} -derivedDataPath #{OUTPUT_PATH} -parallelizeTargets"
$DISABLE_XCPRETTY = false
task :default do
system("rake -sT")
end
def xcodebuild(opts)
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
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
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)
FileUtils.rm_rf(DIST_PATH)
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'
Rake::Task['build:build'].invoke(config)
puts "Installing with configuration: #{config}"
Rake::Task['remove_existing'].invoke()
system "sudo cp -r #{OUTPUT_PATH}/Products/#{config}/santa-driver.kext /Library/Extensions"
system "sudo cp -r #{OUTPUT_PATH}/Products/#{config}/Santa.app /Applications"
end
end
# Dist
task :dist do
desc "Create distribution folder"
Rake::Task['clean'].invoke()
Rake::Task['build:build'].invoke("Release")
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("#{OUTPUT_PATH}/Products/Release/#{x}", "#{DIST_PATH}/binaries")
end
DSYMS.each do |x|
FileUtils.cp_r("#{OUTPUT_PATH}/Products/Release/#{x}", "#{DIST_PATH}/dsym")
end
Dir.glob("Conf/*") {|x| File.directory?(x) or FileUtils.cp(x, "#{DIST_PATH}/conf")}
puts "Distribution folder created"
end
# 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 #{Dir.pwd}/#{OUTPUT_PATH}/Products/Debug/KernelTests"
rescue Exception
ensure
puts "\033[?25h\033[12l\n\n" # unhide cursor
FileUtils.rm_rf("/tmp/santa_kerneltests_tmp")
Rake::Task['unload_kext'].execute
end
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,78 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
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
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D260DAB18B68E12002A0B55"
BuildableName = "LogicTests.xctest"
BlueprintName = "LogicTests"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D91BCDC174E8AE600131A7D"
BuildableName = "All"
BlueprintName = "All"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

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

View File

@@ -1,68 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
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
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
</Testables>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D91BCB3174E8A7E00131A7D"
BuildableName = "santa-driver.kext"
BlueprintName = "santa-driver"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

View File

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

View File

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

View File

@@ -1,155 +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 "SNTMessageWindowController.h"
#import <SecurityInterface/SFCertificatePanel.h>
#import "MOLCertificate.h"
#import "SNTConfigurator.h"
#import "SNTFileInfo.h"
#import "SNTMessageWindow.h"
#import "SNTStoredEvent.h"
@implementation SNTMessageWindowController
- (instancetype)initWithEvent:(SNTStoredEvent *)event andMessage:(NSString *)message {
self = [super initWithWindowNibName:@"MessageWindow"];
if (self) {
_event = event;
_customMessage = (message != (NSString *)[NSNull null] ? message : nil);
}
return self;
}
- (void)loadWindow {
[super loadWindow];
[self.window setLevel:NSPopUpMenuWindowLevel];
[self.window setMovableByWindowBackground:YES];
if (![[SNTConfigurator configurator] eventDetailURL]) {
[self.openEventButton removeFromSuperview];
} else {
NSString *eventDetailText = [[SNTConfigurator configurator] eventDetailText];
if (eventDetailText) {
[self.openEventButton setTitle:eventDetailText];
}
}
if (!self.event.fileBundleName) {
[self.applicationNameLabel removeFromSuperview];
}
}
- (IBAction)showWindow:(id)sender {
[(SNTMessageWindow *)self.window fadeIn:sender];
}
- (IBAction)closeWindow:(id)sender {
[(SNTMessageWindow *)self.window fadeOut:sender];
}
- (void)windowWillClose:(NSNotification *)notification {
if (self.delegate) [self.delegate windowDidClose];
}
- (IBAction)showCertInfo:(id)sender {
// SFCertificatePanel expects an NSArray of SecCertificateRef's
NSMutableArray *certArray = [NSMutableArray arrayWithCapacity:[self.event.signingChain count]];
for (MOLCertificate *cert in self.event.signingChain) {
[certArray addObject:(id)cert.certRef];
}
[[[SFCertificatePanel alloc] init] beginSheetForWindow:self.window
modalDelegate:nil
didEndSelector:nil
contextInfo:nil
certificates:certArray
showGroup:YES];
}
- (IBAction)openEventDetails:(id)sender {
SNTConfigurator *config = [SNTConfigurator configurator];
NSString *formatStr = config.eventDetailURL;
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%file_sha%"
withString:self.event.fileSHA256];
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%username%"
withString:self.event.executingUser];
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%machine_id%"
withString:config.machineID];
[self closeWindow:sender];
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:formatStr]];
}
#pragma mark Generated properties
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
if (![key isEqualToString:@"event"]) {
return [NSSet setWithObject:@"event"];
} else {
return nil;
}
}
- (NSString *)shortenedHash {
return [self.event.fileSHA256 substringWithRange:NSMakeRange(0, 10)];
}
- (NSString *)publisherInfo {
MOLCertificate *leafCert = [self.event.signingChain firstObject];
if (leafCert.commonName && leafCert.orgName) {
return [NSString stringWithFormat:@"%@ - %@", leafCert.orgName, leafCert.commonName];
} else if (leafCert.commonName) {
return leafCert.commonName;
} else if (leafCert.orgName) {
return leafCert.orgName;
} else {
return nil;
}
}
- (NSAttributedString *)attributedCustomMessage {
NSString *htmlHeader = @"<html><head><style>"
@"body {"
@" font-family: 'Lucida Grande', 'Helvetica', sans-serif;"
@" font-size: 13px;"
@" color: #AAA;"
@" text-align: center;"
@"}"
@"</style></head><body>";
NSString *htmlFooter = @"</body></html>";
NSString *message;
if ([self.customMessage length] > 0) {
message = self.customMessage;
} else {
message = [[SNTConfigurator configurator] defaultBlockMessage];
if (!message) {
message = @"The following application has been blocked from executing<br />"
@"because its trustworthiness cannot be determined.";
}
}
NSString *fullHTML = [NSString stringWithFormat:@"%@%@%@", htmlHeader, message, htmlFooter];
NSData *htmlData = [fullHTML dataUsingEncoding:NSUTF8StringEncoding];
NSAttributedString *returnStr = [[NSAttributedString alloc] initWithHTML:htmlData
documentAttributes:NULL];
return returnStr;
}
@end

View File

@@ -1,96 +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 "SNTNotificationManager.h"
#import "SNTStoredEvent.h"
@interface SNTNotificationManager ()
///
/// The currently displayed notification
///
@property SNTMessageWindowController *currentWindowController;
///
/// The queue of pending notifications
///
@property(readonly) NSMutableArray *pendingNotifications;
@end
@implementation SNTNotificationManager
- (instancetype)init {
self = [super init];
if (self) {
_pendingNotifications = [[NSMutableArray alloc] init];
}
return self;
}
- (void)windowDidClose {
[self.pendingNotifications removeObject:self.currentWindowController];
self.currentWindowController = nil;
if ([self.pendingNotifications count]) {
self.currentWindowController = [self.pendingNotifications firstObject];
[self.currentWindowController showWindow:self];
} else {
[NSApp hide:self];
}
}
#pragma mark SNTNotifierXPC protocol methods
- (void)postBlockNotification:(SNTStoredEvent *)event withCustomMessage:(NSString *)message {
// See if this binary is already in the list of pending notifications.
NSPredicate *predicate =
[NSPredicate predicateWithFormat:@"event.fileSHA256==%@", event.fileSHA256];
if ([[self.pendingNotifications filteredArrayUsingPredicate:predicate] count]) return;
if (!event) {
NSLog(@"Error: Missing event object in message received from daemon!");
return;
}
if (!message) message = (NSString *)[NSNull null];
// Notifications arrive on a background thread but UI updates must happen on the main thread.
// This includes making windows.
[self performSelectorOnMainThread:@selector(postBlockNotificationMainThread:)
withObject:@{ @"event": event, @"custommsg": message }
waitUntilDone:NO];
}
- (void)postBlockNotificationMainThread:(NSDictionary *)dict {
SNTStoredEvent *event = dict[@"event"];
NSString *msg = dict[@"custommsg"];
// Create message window
SNTMessageWindowController *pendingMsg = [[SNTMessageWindowController alloc] initWithEvent:event
andMessage:msg];
pendingMsg.delegate = self;
[self.pendingNotifications addObject:pendingMsg];
// If a notification isn't currently being displayed, display the incoming one.
if (!self.currentWindowController) {
self.currentWindowController = pendingMsg;
[NSApp activateIgnoringOtherApps:YES];
// It's quite likely that we're currently on a background thread, and GUI code should always be
// on main thread. Open the window on the main thread so any code it runs is also.
[pendingMsg showWindow:nil];
}
}
@end

310
Source/common/BUILD Normal file
View File

@@ -0,0 +1,310 @@
load("//:helper.bzl", "santa_unit_test")
load("@rules_proto_grpc//objc:defs.bzl", "objc_proto_library")
package(
default_visibility = ["//:santa_package_group"],
)
licenses(["notice"])
proto_library(
name = "santa_proto",
srcs = ["santa.proto"],
deps = [
"@com_google_protobuf//:any_proto",
"@com_google_protobuf//:timestamp_proto",
],
)
objc_proto_library(
name = "santa_objc_proto",
copts = ["-fno-objc-arc"],
non_arc_srcs = ["Santa.pbobjc.m"],
protos = [":santa_proto"],
)
cc_library(
name = "SantaCache",
hdrs = ["SantaCache.h"],
deps = ["//Source/common:SNTCommon"],
)
santa_unit_test(
name = "SantaCacheTest",
srcs = [
"SantaCache.h",
"SantaCacheTest.mm",
],
deps = ["//Source/common:SNTCommon"],
)
objc_library(
name = "SNTBlockMessage",
srcs = ["SNTBlockMessage.m"],
hdrs = ["SNTBlockMessage.h"],
deps = [
":SNTConfigurator",
":SNTLogging",
":SNTStoredEvent",
":SNTSystemInfo",
],
)
objc_library(
name = "SNTBlockMessage_SantaGUI",
srcs = ["SNTBlockMessage.m"],
hdrs = ["SNTBlockMessage.h"],
defines = ["SANTAGUI"],
deps = [
":SNTConfigurator",
":SNTDeviceEvent",
":SNTLogging",
":SNTStoredEvent",
":SNTSystemInfo",
],
)
objc_library(
name = "SNTCachedDecision",
srcs = ["SNTCachedDecision.m"],
hdrs = ["SNTCachedDecision.h"],
deps = [
":SNTCommon",
":SNTCommonEnums",
],
)
objc_library(
name = "SNTDeviceEvent",
srcs = ["SNTDeviceEvent.m"],
hdrs = ["SNTDeviceEvent.h"],
deps = [
":SNTCommonEnums",
],
)
objc_library(
name = "SNTAllowlistInfo",
srcs = ["SNTAllowlistInfo.m"],
hdrs = ["SNTAllowlistInfo.h"],
)
objc_library(
name = "SNTCommonEnums",
hdrs = ["SNTCommonEnums.h"],
)
objc_library(
name = "SNTConfigurator",
srcs = ["SNTConfigurator.m"],
hdrs = ["SNTConfigurator.h"],
deps = [
":SNTCommonEnums",
":SNTRule",
":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 = "SNTCommon",
hdrs = ["SNTCommon.h"],
defines = [
"TARGET_OS_OSX",
"TARGET_OS_MAC",
],
)
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"],
)
objc_library(
name = "SNTRule",
srcs = ["SNTRule.m"],
hdrs = ["SNTRule.h"],
deps = [
":SNTCommonEnums",
":SNTSyncConstants",
],
)
santa_unit_test(
name = "SNTRuleTest",
srcs = ["SNTRuleTest.m"],
deps = [":SNTRule"],
)
objc_library(
name = "SNTStoredEvent",
srcs = ["SNTStoredEvent.m"],
hdrs = ["SNTStoredEvent.h"],
deps = [
":SNTCommonEnums",
"@MOLCertificate",
],
)
cc_library(
name = "SNTStrengthify",
hdrs = ["SNTStrengthify.h"],
)
objc_library(
name = "SNTSyncConstants",
srcs = ["SNTSyncConstants.m"],
hdrs = ["SNTSyncConstants.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 = "SNTXPCMetricServiceInterface",
srcs = ["SNTXPCMetricServiceInterface.m"],
hdrs = ["SNTXPCMetricServiceInterface.h"],
deps = [
"@MOLXPCConnection",
],
)
objc_library(
name = "SNTXPCControlInterface",
srcs = ["SNTXPCControlInterface.m"],
hdrs = ["SNTXPCControlInterface.h"],
defines = select({
"//:adhoc_build": ["SANTAADHOC"],
"//conditions:default": None,
}),
deps = [
":SNTCommonEnums",
":SNTConfigurator",
":SNTRule",
":SNTStoredEvent",
":SNTXPCUnprivilegedControlInterface",
"@MOLCodesignChecker",
"@MOLXPCConnection",
],
)
objc_library(
name = "SNTXPCNotifierInterface",
srcs = ["SNTXPCNotifierInterface.m"],
hdrs = ["SNTXPCNotifierInterface.h"],
deps = [
":SNTCommonEnums",
":SNTXPCBundleServiceInterface",
],
)
objc_library(
name = "SNTMetricSet",
srcs = ["SNTMetricSet.m"],
hdrs = ["SNTMetricSet.h"],
deps = [":SNTCommonEnums"],
)
objc_library(
name = "SNTXPCSyncServiceInterface",
srcs = ["SNTXPCSyncServiceInterface.m"],
hdrs = ["SNTXPCSyncServiceInterface.h"],
deps = [
":SNTCommonEnums",
":SNTStoredEvent",
"@MOLXPCConnection",
],
)
objc_library(
name = "SNTXPCUnprivilegedControlInterface",
srcs = ["SNTXPCUnprivilegedControlInterface.m"],
hdrs = ["SNTXPCUnprivilegedControlInterface.h"],
deps = [
":SNTCommon",
":SNTCommonEnums",
":SNTRule",
":SNTStoredEvent",
":SNTXPCBundleServiceInterface",
"@MOLCertificate",
"@MOLXPCConnection",
],
)
santa_unit_test(
name = "SNTFileInfoTest",
srcs = ["SNTFileInfoTest.m"],
resources = [
"testdata/32bitplist",
"testdata/bad_pagezero",
"testdata/missing_pagezero",
],
structured_resources = glob([
"testdata/BundleExample.app/**",
"testdata/DirectoryBundle/**",
]),
deps = [":SNTFileInfo"],
)
santa_unit_test(
name = "SNTPrefixTreeTest",
srcs = ["SNTPrefixTreeTest.mm"],
deps = [":SNTPrefixTree"],
)
santa_unit_test(
name = "SNTMetricSetTest",
srcs = ["SNTMetricSetTest.m"],
deps = [":SNTMetricSet"],
)
test_suite(
name = "unit_tests",
tests = [
":SNTFileInfoTest",
":SNTMetricSetTest",
":SNTPrefixTreeTest",
":SantaCacheTest",
],
visibility = ["//:santa_package_group"],
)

View File

@@ -0,0 +1,32 @@
/// Copyright 2021 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>
///
/// Store information about new allowlist rules for later logging.
///
@interface SNTAllowlistInfo : NSObject
@property pid_t pid;
@property int pidversion;
@property NSString *targetPath;
@property NSString *sha256;
- (instancetype)initWithPid:(pid_t)pid
pidversion:(int)pidver
targetPath:(NSString*)targetPath
sha256:(NSString*)hash;
@end

View File

@@ -0,0 +1,32 @@
/// Copyright 2021 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/SNTAllowlistInfo.h"
@implementation SNTAllowlistInfo
- (instancetype)initWithPid:(pid_t)pid
pidversion:(int)pidver
targetPath:(NSString *)targetPath
sha256:(NSString *)hash {
self = [super init];
if (self) {
_pid = pid;
_pidversion = pidver;
_targetPath = targetPath;
_sha256 = hash;
}
return self;
}
@end

View File

@@ -0,0 +1,52 @@
/// 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.
#ifdef SANTAGUI
#import <Cocoa/Cocoa.h>
#else
#import <Foundation/Foundation.h>
#endif
@class SNTStoredEvent;
@interface SNTBlockMessage : NSObject
///
/// Return a message suitable for presenting to the user.
///
/// In SantaGUI this will return an NSAttributedString with links and formatting included
/// while for santad all HTML will be properly stripped.
///
+ (NSAttributedString *)formatMessage:(NSString *)message;
///
/// Uses either the configured message depending on the event type or a custom message
/// if the rule that blocked this file included one, formatted using
/// +[SNTBlockMessage formatMessage].
///
+ (NSAttributedString *)attributedBlockMessageForEvent:(SNTStoredEvent *)event
customMessage:(NSString *)customMessage;
///
/// Return a URL generated from the EventDetailURL configuration key
/// after replacing templates in the URL with values from the event.
///
+ (NSURL *)eventDetailURLForEvent:(SNTStoredEvent *)event;
///
/// Strip HTML from a string, replacing <br /> with newline.
///
+ (NSString *)stringFromHTML:(NSString *)html;
@end

View File

@@ -0,0 +1,154 @@
/// 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 "Source/common/SNTBlockMessage.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/common/SNTSystemInfo.h"
@implementation SNTBlockMessage
+ (NSAttributedString *)formatMessage:(NSString *)message {
NSString *htmlHeader =
@"<html><head><style>"
@"body {"
@" font-family: 'Lucida Grande', 'Helvetica', sans-serif;"
@" font-size: 13px;"
@" 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 *fullHTML = [NSString stringWithFormat:@"%@%@%@", htmlHeader, message, htmlFooter];
#ifdef SANTAGUI
NSData *htmlData = [fullHTML dataUsingEncoding:NSUTF8StringEncoding];
return [[NSAttributedString alloc] initWithHTML:htmlData documentAttributes:NULL];
#else
NSString *strippedHTML = [self stringFromHTML:fullHTML];
if (!strippedHTML) {
return [[NSAttributedString alloc] initWithString:@"This binary has been blocked."];
}
return [[NSAttributedString alloc] initWithString:strippedHTML];
#endif
}
+ (NSAttributedString *)attributedBlockMessageForEvent:(SNTStoredEvent *)event
customMessage:(NSString *)customMessage {
NSString *message;
if (customMessage.length) {
message = customMessage;
} else if (event.decision == SNTEventStateBlockUnknown) {
message = [[SNTConfigurator configurator] unknownBlockMessage];
if (!message) {
message = @"The following application has been blocked from executing<br />"
@"because its trustworthiness cannot be determined.";
}
} else {
message = [[SNTConfigurator configurator] bannedBlockMessage];
if (!message) {
message = @"The following application has been blocked from executing<br />"
@"because it has been deemed malicious.";
}
}
return [SNTBlockMessage formatMessage:message];
}
+ (NSString *)stringFromHTML:(NSString *)html {
NSError *error;
NSXMLDocument *xml = [[NSXMLDocument alloc] initWithXMLString:html options:0 error:&error];
if (!xml && error.code == NSXMLParserEmptyDocumentError) {
html = [NSString stringWithFormat:@"<html><body>%@</body></html>", html];
xml = [[NSXMLDocument alloc] initWithXMLString:html options:0 error:&error];
if (!xml) return html;
}
// Strip any HTML tags out of the message. Also remove any content inside <style> tags and
// replace <br> elements with a newline.
NSString *stripXslt =
@"<?xml version='1.0' encoding='utf-8'?>"
@"<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'"
@" xmlns:xhtml='http://www.w3.org/1999/xhtml'>"
@"<xsl:output method='text'/>"
@"<xsl:template match='br'><xsl:text>\n</xsl:text></xsl:template>"
@"<xsl:template match='style'/>"
@"</xsl:stylesheet>";
NSData *data = [xml objectByApplyingXSLTString:stripXslt arguments:NULL error:&error];
if (error || ![data isKindOfClass:[NSData class]]) {
return html;
}
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
+ (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;
if (event.fileSHA256) {
// This key is deprecated, use %file_identifier% or %bundle_or_file_identifier%
formatStr =
[formatStr stringByReplacingOccurrencesOfString:@"%file_sha%"
withString:event.fileBundleHash ?: event.fileSHA256];
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%file_identifier%"
withString:event.fileSHA256];
formatStr =
[formatStr stringByReplacingOccurrencesOfString:@"%bundle_or_file_identifier%"
withString:event.fileBundleHash ?: event.fileSHA256];
}
if (event.executingUser) {
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%username%"
withString:event.executingUser];
}
if (config.machineID) {
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];
}
@end

View File

@@ -12,19 +12,29 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
#import <Foundation/Foundation.h>
#import "Source/common/SNTCommonEnums.h"
#import "Source/common/SNTCommon.h"
@class MOLCertificate;
///
/// Store information about executions from decision making for later logging.
///
@interface SNTCachedDecision : NSObject
@property uint64_t vnodeId;
@property santa_eventstate_t decision;
@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 *teamID;
@property NSString *quarantineURL;
@property NSString *customMsg;
@property BOOL silentBlock;

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

102
Source/common/SNTCommon.h Normal file
View File

@@ -0,0 +1,102 @@
/// 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.
///
/// Common defines between daemon <-> client
///
#ifndef SANTA__COMMON__COMMON_H
#define SANTA__COMMON__COMMON_H
#include <stdint.h>
#include <sys/param.h>
// Branch prediction
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
typedef enum {
ACTION_UNSET = 0,
// REQUESTS
ACTION_REQUEST_SHUTDOWN = 10,
ACTION_REQUEST_BINARY = 11,
// RESPONSES
ACTION_RESPOND_ALLOW = 20,
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,
ACTION_NOTIFY_RENAME = 32,
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_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;
}
#endif
} santa_vnode_id_t;
typedef struct {
santa_action_t action;
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];
char ttypath[MAXPATHLEN];
// For file events, this is the process name.
// For exec requests, this is the parent process name.
// While process names can technically be 4*MAXPATHLEN, that never
// actually happens, so only take MAXPATHLEN and throw away any excess.
char pname[MAXPATHLEN];
// This points to a copy of the original ES message.
void *es_message;
// This points to an NSArray of the process arguments.
void *args_array;
} santa_message_t;
#endif // SANTA__COMMON__COMMON_H

View File

@@ -12,61 +12,113 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#ifndef SANTA__COMMON__COMMONENUMS_H
#define SANTA__COMMON__COMMONENUMS_H
#import <Foundation/Foundation.h>
///
/// These enums are used in various places throughout the Santa client code.
/// The integer values are also stored in the database and so shouldn't be changed.
///
typedef enum {
RULETYPE_UNKNOWN,
typedef NS_ENUM(NSInteger, SNTRuleType) {
SNTRuleTypeUnknown,
RULETYPE_BINARY = 1,
RULETYPE_CERT = 2,
SNTRuleTypeBinary = 1,
SNTRuleTypeCertificate = 2,
SNTRuleTypeTeamID = 3,
};
RULETYPE_MAX
} santa_ruletype_t;
typedef NS_ENUM(NSInteger, SNTRuleState) {
SNTRuleStateUnknown,
typedef enum {
RULESTATE_UNKNOWN,
SNTRuleStateAllow = 1,
SNTRuleStateBlock = 2,
SNTRuleStateSilentBlock = 3,
SNTRuleStateRemove = 4,
RULESTATE_WHITELIST = 1,
RULESTATE_BLACKLIST = 2,
RULESTATE_SILENT_BLACKLIST = 3,
RULESTATE_REMOVE = 4,
SNTRuleStateAllowCompiler = 5,
SNTRuleStateAllowTransitive = 6,
};
RULESTATE_MAX
} santa_rulestate_t;
typedef NS_ENUM(NSInteger, SNTClientMode) {
SNTClientModeUnknown,
typedef enum {
CLIENTMODE_UNKNOWN,
SNTClientModeMonitor = 1,
SNTClientModeLockdown = 2,
};
CLIENTMODE_MONITOR = 1,
CLIENTMODE_LOCKDOWN = 2,
typedef NS_ENUM(NSInteger, SNTEventState) {
// Bits 0-15 bits store non-decision types
SNTEventStateUnknown = 0,
SNTEventStateBundleBinary = 1,
CLIENTMODE_MAX
} santa_clientmode_t;
// Bits 16-23 store deny decision types
SNTEventStateBlockUnknown = 1 << 16,
SNTEventStateBlockBinary = 1 << 17,
SNTEventStateBlockCertificate = 1 << 18,
SNTEventStateBlockScope = 1 << 19,
SNTEventStateBlockTeamID = 1 << 20,
typedef enum {
EVENTSTATE_UNKNOWN,
// Bits 24-31 store allow decision types
SNTEventStateAllowUnknown = 1 << 24,
SNTEventStateAllowBinary = 1 << 25,
SNTEventStateAllowCertificate = 1 << 26,
SNTEventStateAllowScope = 1 << 27,
SNTEventStateAllowCompiler = 1 << 28,
SNTEventStateAllowTransitive = 1 << 29,
SNTEventStateAllowPendingTransitive = 1 << 30,
SNTEventStateAllowTeamID = 1 << 31,
EVENTSTATE_ALLOW_UNKNOWN = 1,
EVENTSTATE_ALLOW_BINARY = 2,
EVENTSTATE_ALLOW_CERTIFICATE = 3,
EVENTSTATE_ALLOW_SCOPE = 4,
// Block and Allow masks
SNTEventStateBlock = 0xFF << 16,
SNTEventStateAllow = 0xFF << 24
};
EVENTSTATE_BLOCK_UNKNOWN = 5,
EVENTSTATE_BLOCK_BINARY = 6,
EVENTSTATE_BLOCK_CERTIFICATE = 7,
EVENTSTATE_BLOCK_SCOPE = 8,
typedef NS_ENUM(NSInteger, SNTRuleTableError) {
SNTRuleTableErrorEmptyRuleArray,
SNTRuleTableErrorInsertOrReplaceFailed,
SNTRuleTableErrorInvalidRule,
SNTRuleTableErrorRemoveFailed
};
EVENTSTATE_MAX
} santa_eventstate_t;
// This enum type is used to indicate what should be done with the related bundle events that are
// generated when an initiating blocked bundle event occurs.
typedef NS_ENUM(NSInteger, SNTBundleEventAction) {
SNTBundleEventActionDropEvents,
SNTBundleEventActionStoreEvents,
SNTBundleEventActionSendEvents,
};
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";
// Indicates where to store event logs.
typedef NS_ENUM(NSInteger, SNTEventLogType) {
SNTEventLogTypeSyslog,
SNTEventLogTypeFilelog,
SNTEventLogTypeProtobuf,
SNTEventLogTypeNull,
};
#endif // SANTA__COMMON__COMMONENUMS_H
// The return status of a sync.
typedef NS_ENUM(NSInteger, SNTSyncStatusType) {
SNTSyncStatusTypeSuccess,
SNTSyncStatusTypePreflightFailed,
SNTSyncStatusTypeEventUploadFailed,
SNTSyncStatusTypeRuleDownloadFailed,
SNTSyncStatusTypePostflightFailed,
SNTSyncStatusTypeTooManySyncsInProgress,
SNTSyncStatusTypeMissingSyncBaseURL,
SNTSyncStatusTypeMissingMachineID,
SNTSyncStatusTypeDaemonTimeout,
SNTSyncStatusTypeSyncStarted,
SNTSyncStatusTypeUnknown,
};
typedef NS_ENUM(NSInteger, SNTMetricFormatType) {
SNTMetricFormatTypeUnknown,
SNTMetricFormatTypeRawJSON,
SNTMetricFormatTypeMonarchJSON,
};
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,23 +12,95 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
#import <Foundation/Foundation.h>
#import "Source/common/SNTCommonEnums.h"
@class SNTRule;
///
/// Singleton that provides an interface for managing configuration values on disk
/// @note This class is designed as a singleton but that is not strictly enforced.
/// @note All properties are KVO compliant.
///
@interface SNTConfigurator : NSObject
/// Default config file path
extern NSString * const kDefaultConfigFilePath;
#pragma mark - Daemon Settings
///
/// The operating mode.
/// The operating mode. Defaults to MONITOR.
///
@property(nonatomic) santa_clientmode_t clientMode;
@property(readonly, nonatomic) SNTClientMode clientMode;
///
/// Set the operating mode as received from a sync server.
///
- (void)setSyncServerClientMode:(SNTClientMode)newMode;
///
/// Enable Fail Close mode. Defaults to NO.
/// This controls Santa's behavior when a failure occurs, such as an
/// inability to read a file. By default, to prevent bugs or misconfiguration
/// from rendering a machine inoperable Santa will fail open and allow
/// execution. With this setting enabled, Santa will fail closed if the client
/// is in LOCKDOWN mode, offering a higher level of security but with a higher
/// potential for causing problems.
///
@property(readonly, nonatomic) BOOL failClosed;
///
/// A set of static rules that should always apply. These can be used as a
/// fallback set of rules for management tools that should always be allowed to
/// run even if a sync server does something unexpected. It can also be used
/// as the sole source of rules, distributed with an MDM.
///
/// The value of this key should be an array containing dictionaries. Each
/// dictionary should contain the same keys used for syncing, e.g:
///
/// <key>StaticRules</key>
/// <array>
/// <dict>
/// <key>identifier</key>
/// <string>binary sha256, certificate sha256, team ID</string>
/// <key>rule_type</key>
/// <string>BINARY</string> (one of BINARY, CERTIFICATE or TEAMID)
/// <key>policy</key>
/// <string>BLOCKLIST</string> (one of ALLOWLIST, ALLOWLIST_COMPILER, BLOCKLIST, SILENT_BLOCKLIST)
/// </dict>
/// </array>
///
/// The return of this property is a dictionary where the keys are the
/// identifiers of each rule, with the SNTRule as a value
///
@property(readonly, nonatomic) NSDictionary<NSString *, SNTRule *> *staticRules;
///
/// 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 *allowedPathRegex;
///
/// Set the regex of allowed paths as received from a sync server.
///
- (void)setSyncServerAllowedPathRegex:(NSRegularExpression *)re;
///
/// 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 *blockedPathRegex;
///
/// Set the regex of blocked paths as received from a sync server.
///
- (void)setSyncServerBlockedPathRegex:(NSRegularExpression *)re;
///
/// The regex of paths to log file changes for. Regexes are specified in ICU format.
@@ -37,25 +109,58 @@ extern NSString * const kDefaultConfigFilePath;
/// pointless as a path only ever has a single line.
/// If the regex doesn't begin with ^ to match from the beginning of the line, it will be added.
///
@property(nonatomic) NSRegularExpression *fileChangesRegex;
@property(readonly, nonatomic) NSRegularExpression *fileChangesRegex;
///
/// The regex of whitelisted paths. Regexes are specified in ICU format.
/// A list of ignore prefixes which are checked in-kernel.
/// This is more performant than FileChangesRegex when ignoring whole directory trees.
///
/// 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.
/// 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.
///
@property(nonatomic) NSRegularExpression *whitelistPathRegex;
/// By default "/." and "/dev/" are added.
///
/// The regex of blacklisted paths. Regexes are specified in ICU format.
/// 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.
///
/// 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.
/// "/private/tmp/"
/// "/private/var/"
/// "/private/new/"
///
@property(nonatomic) NSRegularExpression *blacklistPathRegex;
/// -> [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
@@ -64,8 +169,98 @@ extern NSString * const kDefaultConfigFilePath;
///
@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 "syslog": Sent to ASL or ULS (if built with the 10.12 SDK or later).
/// SNTEventLogTypeFilelog "file": Sent to a file on disk. Use eventLogPath to specify a path.
/// SNTEventLogTypeNull "null": Logs nothing
/// SNTEventLogTypeProtobuf "protobuf": (BETA) Sent to a file on disk, using maildir format. Use
/// mailDirectory to specify a path. Use mailDirectoryFileSizeThresholdKB,
/// mailDirectorySizeThresholdMB and mailDirectoryEventMaxFlushTimeSec to configure
/// additional maildir format settings.
/// 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;
///
/// If eventLogType is set to protobuf, mailDirectory will provide the base path used for
/// saving logs using the maildir format.
/// Defaults to /var/db/santa/mail.
///
/// @note: This property is KVO compliant, but should only be read once at santad startup.
///
@property(readonly, nonatomic) NSString *mailDirectory;
///
/// If eventLogType is set to protobuf, mailDirectoryFileSizeThresholdKB sets the per-file size
/// limit for files saved in the mailDirectory.
/// Defaults to 100.
///
/// @note: This property is KVO compliant, but should only be read once at santad startup.
///
@property(readonly, nonatomic) NSUInteger mailDirectoryFileSizeThresholdKB;
///
/// If eventLogType is set to protobuf, mailDirectorySizeThresholdMB sets the total size
/// limit for all files saved in the mailDirectory.
/// Defaults to 500.
///
/// @note: This property is KVO compliant, but should only be read once at santad startup.
///
@property(readonly, nonatomic) NSUInteger mailDirectorySizeThresholdMB;
///
/// If eventLogType is set to protobuf, mailDirectoryEventMaxFlushTimeSec sets the maximum amount
/// of time an event will be stored in memory before being written to disk.
/// Defaults to 5.0.
///
/// @note: This property is KVO compliant, but should only be read once at santad startup.
///
@property(readonly, nonatomic) float mailDirectoryEventMaxFlushTimeSec;
///
/// 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 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
///
/// The text to display when opening Santa.app.
/// If unset, the default text will be displayed.
///
@property(readonly, nonatomic) NSString *aboutText;
///
/// The URL to open when the user clicks "More Info..." when opening Santa.app.
/// If unset, the button will not be displayed.
@@ -75,12 +270,16 @@ extern NSString * const kDefaultConfigFilePath;
///
/// When the user gets a block notification, a button can be displayed which will
/// take them to a web page with more information about that event.
///
/// This property contains a kind of format string to be turned into the URL to send them to.
/// The following sequences will be replaced in the final URL:
///
/// %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.
///
@@ -94,10 +293,43 @@ extern NSString * const kDefaultConfigFilePath;
@property(readonly, nonatomic) NSString *eventDetailText;
///
/// For any rule that doesn't have a custom message, this setting overrides the message
/// text that is display. If unset, a reasonable default is provided.
/// In lockdown mode this is the message shown to the user when an unknown binary
/// is blocked. If this message is not configured, a reasonable default is provided.
///
@property(readonly, nonatomic) NSString *defaultBlockMessage;
@property(readonly, nonatomic) NSString *unknownBlockMessage;
///
/// This is the message shown to the user when a binary is blocked because of a rule,
/// if that rule doesn't provide a custom message. If this is not configured, a reasonable
/// default is provided.
///
@property(readonly, nonatomic) NSString *bannedBlockMessage;
///
/// This is the message shown to the user when a USB storage device's mount is denied
/// from the BlockUSB configuration setting. If not configured, a reasonable
/// default is provided.
///
@property(readonly, nonatomic) NSString *bannedUSBBlockMessage;
///
/// This is the message shown to the user when a USB storage device's mount is forcibly
/// remounted to a different set of permissions from the BlockUSB and RemountUSBMode
/// configuration settings. If not configured, a reasonable default is provided.
///
@property(readonly, nonatomic) NSString *remountUSBBlockMessage;
///
/// The notification text to display when the client goes into MONITOR mode.
/// Defaults to "Switching into Monitor mode"
///
@property(readonly, nonatomic) NSString *modeNotificationMonitor;
///
/// The notification text to display when the client goes into LOCKDOWN mode.
/// Defaults to "Switching into Lockdown mode"
///
@property(readonly, nonatomic) NSString *modeNotificationLockdown;
#pragma mark - Sync Settings
@@ -107,10 +339,12 @@ extern NSString * const kDefaultConfigFilePath;
@property(readonly, nonatomic) NSURL *syncBaseURL;
///
/// If YES, mid-execution event uploads are skipped.
/// This property is never stored on disk.
/// Proxy settings for syncing.
/// This dictionary is passed directly to NSURLSession. The allowed keys
/// are loosely documented at
/// https://developer.apple.com/documentation/cfnetwork/global_proxy_settings_constants.
///
@property BOOL syncBackOff;
@property(readonly, nonatomic) NSDictionary *syncProxyConfig;
///
/// The machine owner.
@@ -118,20 +352,57 @@ extern NSString * const kDefaultConfigFilePath;
@property(readonly, nonatomic) NSString *machineOwner;
///
/// The last date of successful sync.
/// The last date of a successful full sync.
///
@property(nonatomic) NSDate *syncLastSuccess;
@property(nonatomic) NSDate *fullSyncLastSuccess;
///
/// The last date of a successful rule sync.
///
@property(nonatomic) NSDate *ruleSyncLastSuccess;
///
/// If YES a clean sync is required.
///
@property(nonatomic) BOOL syncCleanRequired;
///
/// USB Mount Blocking. Defaults to false.
///
@property(nonatomic) BOOL blockUSBMount;
///
/// Comma-seperated `$ mount -o` arguments used for forced remounting of USB devices. Default
/// to fully allow/deny without remounting if unset.
///
@property(nonatomic) NSArray<NSString *> *remountUSBMode;
///
/// When `blockUSBMount` is set, this is the message shown to the user when a device is blocked
/// If this message is not configured, a reasonable default is provided.
///
@property(readonly, nonatomic) NSString *usbBlockMessage;
///
/// If set, this over-rides the default machine ID used for syncing.
///
@property(readonly, nonatomic) NSString *machineID;
///
/// 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 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
///
@@ -169,21 +440,106 @@ extern NSString * const kDefaultConfigFilePath;
///
@property(readonly, nonatomic) NSString *syncClientAuthCertificateIssuer;
///
/// If true, syncs will upload events when a clean sync is requested. Defaults to false.
///
@property(readonly, nonatomic) BOOL enableCleanSyncEventUpload;
///
/// If true, events will be uploaded for all executions, even those that are allowed.
/// Use with caution, this generates a lot of events. Defaults to false.
///
@property(nonatomic) BOOL enableAllEventUpload;
///
/// If true, events will *not* be uploaded for ALLOW_UNKNOWN events for clients in Monitor mode.
///
@property(nonatomic) BOOL disableUnknownEventUpload;
///
/// 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;
///
/// True if metricsFormat and metricsURL are set. False otherwise.
///
@property(readonly, nonatomic) BOOL exportMetrics;
///
/// Format to export Metrics as.
///
@property(readonly, nonatomic) SNTMetricFormatType metricFormat;
///
/// URL describing where metrics are exported, defaults to nil.
///
@property(readonly, nonatomic) NSURL *metricURL;
///
/// Extra Metric Labels to add to the metrics payloads.
///
@property(readonly, nonatomic) NSDictionary *extraMetricLabels;
///
/// Duration in seconds of how often the metrics should be exported.
///
@property(readonly, nonatomic) NSUInteger metricExportInterval;
///
/// Duration in seconds for metrics export timeout. Defaults to 30;
///
@property(readonly, nonatomic) NSUInteger metricExportTimeout;
///
/// Retrieve an initialized singleton configurator object using the default file path.
///
+ (instancetype)configurator;
///
/// Designated initializer.
/// Clear the sync server configuration from the effective configuration.
///
/// @param filePath The path to the file to use as a backing store.
///
- (instancetype)initWithFilePath:(NSString *)filePath;
///
/// Re-read config data from disk.
///
- (void)reloadConfigData;
- (void)clearSyncState;
@end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
/// Copyright 2022 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>
@interface SNTDeviceEvent : NSObject <NSSecureCoding>
- (instancetype)initWithOnName:(NSString *)mntonname fromName:(NSString *)mntfromname;
@property NSString *mntonname;
@property NSString *mntfromname;
@property NSArray<NSString *> *remountArgs;
- (NSString *)readableRemountArgs;
@end

View File

@@ -0,0 +1,63 @@
#import "Source/common/SNTDeviceEvent.h"
@implementation SNTDeviceEvent
#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) \
[decoder decodeObjectOfClasses:[NSSet setWithObjects:[NSArray class], [cls class], nil] \
forKey:key]
- (instancetype)initWithOnName:(NSString *)mntonname fromName:(NSString *)mntfromname {
self = [super init];
if (self) {
_mntonname = mntonname;
_mntfromname = mntfromname;
}
return self;
}
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(NSCoder *)coder {
ENCODE(self.mntonname, @"mntonname");
ENCODE(self.mntfromname, @"mntfromname");
ENCODE(self.remountArgs, @"remountArgs");
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self) {
_mntonname = DECODE(NSString, @"mntonname");
_mntfromname = DECODE(NSString, @"mntfromname");
_remountArgs = DECODEARRAY(NSString, @"remountArgs");
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"SNTDeviceEvent '%@' -> '%@' (with permissions: [%@]",
self.mntfromname, self.mntonname,
[self.remountArgs componentsJoinedByString:@", "]];
}
- (NSString *)readableRemountArgs {
NSMutableArray<NSString *> *readable = [NSMutableArray array];
for (NSString *arg in self.remountArgs) {
if ([arg isEqualToString:@"rdonly"]) {
[readable addObject:@"read-only"];
} else if ([arg isEqualToString:@"noexec"]) {
[readable addObject:@"block executables"];
} else {
[readable addObject:arg];
}
}
return [readable componentsJoinedByString:@", "];
}
@end

View File

@@ -12,6 +12,8 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#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,6 +12,10 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import <Foundation/Foundation.h>
@class MOLCodesignChecker;
///
/// Represents a binary on disk, providing access to details about that binary
/// such as the SHA-1, SHA-256, Info.plist and the Mach-O data.
@@ -36,11 +40,30 @@
///
- (instancetype)initWithPath:(NSString *)path;
///
/// Initializer for already resolved paths.
///
/// @param path The path of the file this instance is to represent. The path will
/// not be converted and will be used as is. If the path is not a regular file this method will
/// return nil and fill in an error.
/// @param error If an error occurred and nil is returned, this will be a pointer to an NSError
/// describing the problem.
///
- (instancetype)initWithResolvedPath:(NSString *)path error:(NSError **)error;
///
/// @return Path of this file.
///
- (NSString *)path;
///
/// Hash this file with SHA-1 and SHA-256 simultaneously.
///
/// @param sha1 If not NULL, will be filled with the SHA-1 of the file.
/// @param sha256 If not NULL, will be filled with the SHA-256 of the file.
///
- (void)hashSHA1:(NSString **)sha1 SHA256:(NSString **)sha256;
///
/// @return SHA-1 hash of this binary.
///
@@ -51,12 +74,6 @@
///
- (NSString *)SHA256;
///
/// @return The type of Mach-O file, one of:
/// Dynamic Library, Kernel Extension, Fat Binary or Thin Binary.
///
- (NSString *)machoType;
///
/// @return The architectures included in this binary (e.g. x86_64, ppc).
///
@@ -82,6 +99,11 @@
///
- (BOOL)isDylib;
///
/// @return YES if this file is a bundle executable (QuickLook/Spotlight plugin, etc.)
///
- (BOOL)isBundle;
///
/// @return YES if this file is a kernel extension.
///
@@ -92,11 +114,41 @@
///
- (BOOL)isScript;
///
/// @return YES if this file is an XAR archive.
///
- (BOOL)isXARArchive;
///
/// @return YES if this file is a disk image.
///
- (BOOL)isDMG;
///
/// @return NSString describing the kind of file (executable, bundle, script, etc.)
///
- (NSString *)humanReadableFileType;
///
/// @return YES if this file has a bad/missing __PAGEZERO .
///
- (BOOL)isMissingPageZero;
///
/// If set to YES, the bundle* and infoPlist methods will search for and use the highest NSBundle
/// found in the tree. Defaults to NO, which uses the first found bundle, if any.
///
/// @example:
/// An SNTFileInfo object that represents
/// /Applications/Photos.app/Contents/XPCServices/com.apple.Photos.librarychooserservice.xpc
/// useAncestorBundle is set to YES
/// /Applications/Photos.app will be used to get data backing all the bundle methods
///
/// @note: The NSBundle object backing the bundle* and infoPlist methods is cached once found.
/// Setting the useAncestorBundle propery will clear this cache and force a re-search.
///
@property(nonatomic) BOOL useAncestorBundle;
///
/// @return An NSBundle if this file is part of a bundle.
///
@@ -159,4 +211,16 @@
///
- (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
/// calls. You may pass in NULL for the error if you don't care to receive it.
///
- (MOLCodesignChecker *)codesignCheckerWithError:(NSError **)error;
@end

View File

@@ -12,12 +12,18 @@
/// 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 <MOLCodesignChecker/MOLCodesignChecker.h>
#import <fmdb/FMDB.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>
// Simple class to hold the data of a mach_header and the offset within the file
// in which that header was found.
@@ -39,143 +45,269 @@
@interface SNTFileInfo ()
@property NSString *path;
@property NSData *fileData;
// Dictionary of MachHeaderWithOffset objects where the keys are the architecture strings
@property NSDictionary *machHeaders;
@property NSFileHandle *fileHandle;
@property NSUInteger fileSize;
@property NSString *fileOwnerHomeDir;
// Cached properties
@property NSBundle *bundleRef;
@property NSDictionary *infoDict;
@property NSDictionary *quarantineDict;
@property NSDictionary *cachedHeaders;
@property MOLCodesignChecker *cachedCodesignChecker;
@property(nonatomic) NSError *codesignCheckerError;
@end
@implementation SNTFileInfo
extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
- (instancetype)initWithPath:(NSString *)path error:(NSError **)error {
- (instancetype)initWithResolvedPath:(NSString *)path error:(NSError **)error {
self = [super init];
if (self) {
_path = [self resolvePath:path];
if (_path.length == 0) {
_path = path;
if (!_path.length) {
if (error) {
NSString *errStr = @"Unable to resolve empty path";
if (path) errStr = [@"Unable to resolve path: " stringByAppendingString:path];
NSString *errStr = @"Unable to use empty path";
*error = [NSError errorWithDomain:@"com.google.santa.fileinfo"
code:260
userInfo:@{ NSLocalizedDescriptionKey: errStr }];
code:270
userInfo:@{NSLocalizedDescriptionKey : errStr}];
}
return nil;
}
_fileData = [NSData dataWithContentsOfFile:_path
options:NSDataReadingUncached
error:error];
if (_fileData.length == 0) return nil;
[self parseMachHeaders];
struct stat fileStat;
lstat(_path.UTF8String, &fileStat);
if (!((S_IFMT & fileStat.st_mode) == S_IFREG)) {
if (error) {
NSString *errStr = [NSString stringWithFormat:@"Non regular file: %s", strerror(errno)];
*error = [NSError errorWithDomain:@"com.google.santa.fileinfo"
code:290
userInfo:@{NSLocalizedDescriptionKey : errStr}];
}
return nil;
}
_fileSize = fileStat.st_size;
if (_fileSize == 0) return nil;
if (fileStat.st_uid != 0) {
struct passwd *pwd = getpwuid(fileStat.st_uid);
if (pwd) {
_fileOwnerHomeDir = @(pwd->pw_dir);
}
}
int fd = open([_path UTF8String], O_RDONLY | O_CLOEXEC);
if (fd < 0) {
if (error) {
NSString *errStr = [NSString stringWithFormat:@"Unable to open file: %s", strerror(errno)];
*error = [NSError errorWithDomain:@"com.google.santa.fileinfo"
code:280
userInfo:@{NSLocalizedDescriptionKey : errStr}];
}
return nil;
}
_fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:YES];
}
return self;
}
- (instancetype)initWithPath:(NSString *)path error:(NSError **)error {
NSBundle *bndl;
NSString *resolvedPath = [self resolvePath:path bundle:&bndl];
if (!resolvedPath.length) {
if (error) {
NSString *errStr = @"Unable to resolve empty path";
if (path) errStr = [@"Unable to resolve path: " stringByAppendingString:path];
*error = [NSError errorWithDomain:@"com.google.santa.fileinfo"
code:260
userInfo:@{NSLocalizedDescriptionKey : errStr}];
}
return nil;
}
self = [self initWithResolvedPath:resolvedPath error:error];
if (self && bndl) _bundleRef = bndl;
return self;
}
- (instancetype)initWithPath:(NSString *)path {
return [self initWithPath:path error:NULL];
}
- (NSString *)SHA1 {
unsigned char sha1[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(self.fileData.bytes, (unsigned int)self.fileData.length, sha1);
#pragma mark Hashing
// Convert the binary SHA into hex
NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
[buf appendFormat:@"%02x", (unsigned char)sha1[i]];
- (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 = malloc(chunkSize);
@try {
CC_SHA1_CTX c1;
CC_SHA256_CTX c256;
if (sha1) CC_SHA1_Init(&c1);
if (sha256) CC_SHA256_Init(&c256);
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;
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";
*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);
}
}
return buf;
- (NSString *)SHA1 {
NSString *sha1;
[self hashSHA1:&sha1 SHA256:NULL];
return sha1;
}
- (NSString *)SHA256 {
unsigned char sha256[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(self.fileData.bytes, (unsigned int)self.fileData.length, sha256);
NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
[buf appendFormat:@"%02x", (unsigned char)sha256[i]];
}
return buf;
NSString *sha256;
[self hashSHA1:NULL SHA256:&sha256];
return sha256;
}
- (NSString *)machoType {
if ([self isDylib]) return @"Dynamic Library";
if ([self isKext]) return @"Kernel Extension";
if ([self isFat]) return @"Fat Binary";
if ([self isMachO]) return @"Thin Binary";
if ([self isScript]) return @"Script";
return @"Unknown (not executable?)";
}
#pragma mark File Type Info
- (NSArray *)architectures {
return [self.machHeaders allKeys];
}
- (BOOL)isDylib {
- (uint32_t)machFileType {
struct mach_header *mach_header = [self firstMachHeader];
if (mach_header && mach_header->filetype == MH_DYLIB) return YES;
return NO;
if (mach_header) return mach_header->filetype;
return -1;
}
- (BOOL)isExecutable {
return [self machFileType] == MH_EXECUTE;
}
- (BOOL)isDylib {
return [self machFileType] == MH_DYLIB;
}
- (BOOL)isBundle {
return [self machFileType] == MH_BUNDLE;
}
- (BOOL)isKext {
struct mach_header *mach_header = [self firstMachHeader];
if (mach_header && mach_header->filetype == MH_KEXT_BUNDLE) return YES;
return NO;
return [self machFileType] == MH_KEXT_BUNDLE;
}
- (BOOL)isMachO {
return ([self.machHeaders count] > 0);
return (self.machHeaders.count > 0);
}
- (BOOL)isFat {
return ([self.machHeaders count] > 1);
return (self.machHeaders.count > 1);
}
- (BOOL)isScript {
const char *magic = (const char *)[[self safeSubdataWithRange:NSMakeRange(0, 2)] bytes];
return (strncmp("#!", magic, 2) == 0);
return (magic && memcmp("#!", magic, 2) == 0);
}
- (BOOL)isExecutable {
struct mach_header *mach_header = [self firstMachHeader];
if (!mach_header) return NO;
if (mach_header->filetype == MH_OBJECT || mach_header->filetype == MH_EXECUTE) return YES;
return NO;
- (BOOL)isXARArchive {
const char *magic = (const char *)[[self safeSubdataWithRange:NSMakeRange(0, 4)] bytes];
return (magic && memcmp("xar!", magic, 4) == 0);
}
- (BOOL)isDMG {
if (self.fileSize < 512) return NO;
NSUInteger last512 = self.fileSize - 512;
const char *magic = (const char *)[[self safeSubdataWithRange:NSMakeRange(last512, 4)] bytes];
return (magic && memcmp("koly", magic, 4) == 0);
}
- (NSString *)humanReadableFileType {
if ([self isExecutable]) return @"Executable";
if ([self isDylib]) return @"Dynamic Library";
if ([self isBundle]) return @"Bundle/Plugin";
if ([self isKext]) return @"Kernel Extension";
if ([self isScript]) return @"Script";
if ([self isXARArchive]) return @"XAR Archive";
if ([self isDMG]) return @"Disk Image";
return @"Unknown";
}
#pragma mark Page Zero
- (BOOL)isMissingPageZero {
// This method only checks i386 arch because the kernel enforces this for other archs
// See bsd/kern/mach_loader.c, search for enforce_hard_pagezero.
MachHeaderWithOffset *x86Header = self.machHeaders[@"i386"];
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];
if (mh->filetype != MH_EXECUTE) return NO;
NSRange range = NSMakeRange(x86Header.offset + sizeof(struct mach_header),
sizeof(struct segment_command));
NSRange range =
NSMakeRange(x86Header.offset + sizeof(struct mach_header), sizeof(struct segment_command));
NSData *lcData = [self safeSubdataWithRange:range];
if (!lcData) return NO;
// This code assumes the __PAGEZERO is always the first load-command in the file.
// Given that the OS X ABI says "the static linker creates a __PAGEZERO segment
// Given that the macOS ABI says "the static linker creates a __PAGEZERO segment
// as the first segment of an executable file." this should be OK.
struct load_command *lc = (struct load_command *)[lcData bytes];
if (lc->cmd == LC_SEGMENT) {
struct segment_command *segment = (struct segment_command *)lc;
if (segment->vmaddr == 0 && segment->vmsize != 0 &&
segment->initprot == 0 && segment->maxprot == 0 &&
strcmp("__PAGEZERO", segment->segname) == 0) {
if (segment->vmaddr == 0 && segment->vmsize != 0 && segment->initprot == 0 &&
segment->maxprot == 0 && strcmp("__PAGEZERO", segment->segname) == 0) {
return NO;
}
}
@@ -184,38 +316,62 @@ 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.
///
/// Rationale: An NSBundle has a method executablePath for discovering the main binary within a
/// bundle but provides no way to get an NSBundle object when only the executablePath is known.
/// Also a bundle can contain multiple binaries within the MacOS folder and we want any of these
/// Also a bundle can contain multiple binaries within its subdirectories and we want any of these
/// to count as being part of the bundle.
///
/// This method relies on executable bundles being laid out as follows:
/// This method walks up the path until a bundle is found, if any.
///
/// @code
/// Bundle.app/
/// Contents/
/// MacOS/
/// executable
/// @endcode
///
/// If @c self.path is the full path to @c executable above, this method would return an
/// NSBundle reference for Bundle.app.
/// @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 *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 objectForInfoDictionaryKey:@"CFBundleIdentifier"]) {
if ((!ancestor && bndl.bundlePath.pathExtension.length) ||
[[self allowedAncestorExtensions] containsObject:bndl.bundlePath.pathExtension]) {
bundle = bndl;
}
if (!ancestor) break;
}
[pathComponents removeLastObject];
}
return bundle;
}
- (NSBundle *)bundle {
if (!self.bundleRef) {
self.bundleRef = (NSBundle *)[NSNull null];
// Check that the full path is at least 4-levels deep:
// e.g: /Calendar.app/Contents/MacOS/Calendar
NSArray *pathComponents = [self.path pathComponents];
if ([pathComponents count] < 4) return nil;
pathComponents = [pathComponents subarrayWithRange:NSMakeRange(0, [pathComponents count] - 3)];
NSBundle *bndl = [NSBundle bundleWithPath:[NSString pathWithComponents:pathComponents]];
if (bndl && [bndl objectForInfoDictionaryKey:@"CFBundleIdentifier"]) self.bundleRef = bndl;
self.bundleRef =
[self findBundleWithAncestor:self.useAncestorBundle] ?: (NSBundle *)[NSNull null];
}
return self.bundleRef == (NSBundle *)[NSNull null] ? nil : self.bundleRef;
}
@@ -224,6 +380,14 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
return [self.bundle bundlePath];
}
- (void)setUseAncestorBundle:(BOOL)useAncestorBundle {
if (self.useAncestorBundle != useAncestorBundle) {
self.bundleRef = nil;
self.infoDict = nil;
}
_useAncestorBundle = useAncestorBundle;
}
- (NSDictionary *)infoPlist {
if (!self.infoDict) {
NSDictionary *d = [self embeddedPlist];
@@ -244,81 +408,87 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
}
- (NSString *)bundleIdentifier {
return [self.infoPlist objectForKey:@"CFBundleIdentifier"];
return [[self.infoPlist objectForKey:@"CFBundleIdentifier"] description];
}
- (NSString *)bundleName {
return [self.infoPlist objectForKey:@"CFBundleName"];
return [[self.infoPlist objectForKey:@"CFBundleDisplayName"] description]
?: [[self.infoPlist objectForKey:@"CFBundleName"] description];
}
- (NSString *)bundleVersion {
return [self.infoPlist objectForKey:@"CFBundleVersion"];
return [[self.infoPlist objectForKey:@"CFBundleVersion"] description];
}
- (NSString *)bundleShortVersionString {
return [self.infoPlist objectForKey:@"CFBundleShortVersionString"];
return [[self.infoPlist objectForKey:@"CFBundleShortVersionString"] description];
}
#pragma mark Quarantine Data
- (NSString *)quarantineDataURL {
NSURL *url = [self quarantineData][(__bridge NSString *)kLSQuarantineDataURLKey];
return [url absoluteString];
NSURL *dataURL = [self quarantineData][@"LSQuarantineDataURL"];
if (dataURL == (NSURL *)[NSNull null]) dataURL = nil;
return [dataURL absoluteString];
}
- (NSString *)quarantineRefererURL {
NSURL *url = [self quarantineData][(__bridge NSString *)kLSQuarantineOriginURLKey];
return [url absoluteString];
NSURL *originURL = [self quarantineData][@"LSQuarantineOriginURL"];
if (originURL == (NSURL *)[NSNull null]) originURL = nil;
return [originURL absoluteString];
}
- (NSString *)quarantineAgentBundleID {
return [self quarantineData][(__bridge NSString *)kLSQuarantineAgentBundleIdentifierKey];
NSString *agentBundle = [self quarantineData][@"LSQuarantineAgentBundleIdentifier"];
if (agentBundle == (NSString *)[NSNull null]) agentBundle = nil;
return agentBundle;
}
- (NSDate *)quarantineTimestamp {
return [self quarantineData][(__bridge NSString *)kLSQuarantineTimeStampKey];
}
- (NSUInteger)fileSize {
return self.fileData.length;
NSDate *timeStamp = [self quarantineData][@"LSQuarantineTimeStamp"];
return timeStamp;
}
#pragma mark Internal Methods
- (void)parseMachHeaders {
if (self.machHeaders) return;
- (NSDictionary *)machHeaders {
if (self.cachedHeaders) return self.cachedHeaders;
// Sanity check file length
if (self.fileData.length < sizeof(struct mach_header)) {
self.machHeaders = [NSDictionary dictionary];
return;
if (self.fileSize < sizeof(struct mach_header)) {
self.cachedHeaders = [NSDictionary dictionary];
return self.cachedHeaders;
}
NSMutableDictionary *machHeaders = [NSMutableDictionary dictionary];
NSData *machHeader = [self parseSingleMachHeader:self.fileData];
NSData *machHeader =
[self parseSingleMachHeader:[self safeSubdataWithRange:NSMakeRange(0, 4096)]];
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];
struct fat_header *fh = (struct fat_header *)[fatHeader bytes];
if (fatHeader && (fh->magic == FAT_MAGIC || fh->magic == FAT_CIGAM)) {
if (fatHeader && (fh->magic == FAT_CIGAM || fh->magic == FAT_MAGIC)) {
int nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
range = NSMakeRange(sizeof(struct fat_header), sizeof(struct fat_arch) * nfat_arch);
NSMutableData *fatArchs = [[self safeSubdataWithRange:range] mutableCopy];
if (fatArchs) {
struct fat_arch *fat_arch = (struct fat_arch *)[fatArchs mutableBytes];
for (int i = 0; i < nfat_arch; i++) {
for (int i = 0; i < nfat_arch; ++i) {
int offset = OSSwapBigToHostInt32(fat_arch[i].offset);
int size = OSSwapBigToHostInt32(fat_arch[i].size);
int cputype = OSSwapBigToHostInt(fat_arch[i].cputype);
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;
@@ -328,7 +498,8 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
}
}
self.machHeaders = [machHeaders copy];
self.cachedHeaders = [machHeaders copy];
return self.cachedHeaders;
}
- (NSData *)parseSingleMachHeader:(NSData *)inputData {
@@ -372,27 +543,54 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
offset += sz_header;
// Loop through the load commands looking for the segment named __TEXT
for (uint32_t i = 0; i < ncmds; i++) {
for (uint32_t i = 0; i < ncmds; ++i) {
NSData *cmdData = [self safeSubdataWithRange:NSMakeRange(offset, sz_segment)];
if (!cmdData) return nil;
struct segment_command_64 *lc = (struct segment_command_64 *)[cmdData bytes];
if (lc->cmd == LC_SEGMENT || lc->cmd == LC_SEGMENT_64) {
if (strncmp(lc->segname, "__TEXT", 6) == 0) {
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++) {
for (uint32_t i = 0; i < nsects; ++i) {
NSData *sectData = [self safeSubdataWithRange:NSMakeRange(offset, sz_section)];
if (!sectData) return nil;
struct section_64 *sect = (struct section_64 *)[sectData bytes];
if (strncmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
NSData *plistData = [self safeSubdataWithRange:NSMakeRange(sect->offset, sect->size)];
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
@@ -407,35 +605,91 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
}
///
/// Return one of the mach_header's in this file.
/// Return the first mach_header in this file.
///
- (struct mach_header *)firstMachHeader {
return (struct mach_header *)([[[[self.machHeaders allValues] firstObject] data] bytes]);
}
///
/// Wrap @c subdataWithRange: in a @@try/@@catch, returning nil on exception.
/// Useful for when the range is beyond the end of the file.
/// Extract a range of the file as an NSData, handling any exceptions.
/// Returns nil if the requested range is outside of the range of the file.
///
- (NSData *)safeSubdataWithRange:(NSRange)range {
@try {
return [self.fileData subdataWithRange:range];
}
@catch (NSException *e) {
if ((range.location + range.length) > self.fileSize) return nil;
[self.fileHandle seekToFileOffset:range.location];
NSData *d = [self.fileHandle readDataOfLength:range.length];
if (d.length != range.length) return nil;
return d;
} @catch (NSException *e) {
return nil;
}
}
///
/// Retrieve quarantine data for a file
/// Retrieve quarantine data for a file and caches the dictionary
/// This method attempts to handle fetching the quarantine data even if the running user
/// is not the one who downloaded the file.
///
- (NSDictionary *)quarantineData {
if (!self.quarantineDict && NSURLQuarantinePropertiesKey != NULL) {
if (!self.quarantineDict && self.fileOwnerHomeDir && NSURLQuarantinePropertiesKey) {
self.quarantineDict = (NSDictionary *)[NSNull null];
NSURL *url = [NSURL fileURLWithPath:self.path];
NSDictionary *d = [url resourceValuesForKeys:@[ NSURLQuarantinePropertiesKey ] error:NULL];
self.quarantineDict = d[NSURLQuarantinePropertiesKey];
if (!self.quarantineDict) self.quarantineDict = (NSDictionary *)[NSNull null];
if (d[NSURLQuarantinePropertiesKey]) {
d = d[NSURLQuarantinePropertiesKey];
if (d[@"LSQuarantineIsOwnedByCurrentUser"]) {
self.quarantineDict = d;
} else if (d[@"LSQuarantineEventIdentifier"]) {
NSMutableDictionary *quarantineDict = [d mutableCopy];
// If self.path is on a quarantine disk image, LSQuarantineDiskImageURL will point to the
// disk image and self.fileOwnerHomeDir will be incorrect (probably root).
NSString *fileOwnerHomeDir = self.fileOwnerHomeDir;
if (d[@"LSQuarantineDiskImageURL"]) {
struct stat fileStat;
stat([d[@"LSQuarantineDiskImageURL"] fileSystemRepresentation], &fileStat);
if (fileStat.st_uid != 0) {
struct passwd *pwd = getpwuid(fileStat.st_uid);
if (pwd) {
fileOwnerHomeDir = @(pwd->pw_dir);
}
}
}
NSURL *dbPath = [NSURL fileURLWithPathComponents:@[
fileOwnerHomeDir, @"Library", @"Preferences",
@"com.apple.LaunchServices.QuarantineEventsV2"
]];
FMDatabase *db = [FMDatabase databaseWithPath:[dbPath absoluteString]];
db.logsErrors = NO;
if ([db open]) {
FMResultSet *rs = [db executeQuery:@"SELECT * FROM LSQuarantineEvent "
@"WHERE LSQuarantineEventIdentifier=?",
d[@"LSQuarantineEventIdentifier"]];
if ([rs next]) {
NSString *agentBundleID = [rs stringForColumn:@"LSQuarantineAgentBundleIdentifier"];
NSString *dataURLString = [rs stringForColumn:@"LSQuarantineDataURLString"];
NSString *originURLString = [rs stringForColumn:@"LSQuarantineOriginURLString"];
double timeStamp = [rs doubleForColumn:@"LSQuarantineTimeStamp"];
quarantineDict[@"LSQuarantineAgentBundleIdentifier"] = agentBundleID;
quarantineDict[@"LSQuarantineDataURL"] = [NSURL URLWithString:dataURLString];
quarantineDict[@"LSQuarantineOriginURL"] = [NSURL URLWithString:originURLString];
quarantineDict[@"LSQuarantineTimestamp"] =
[NSDate dateWithTimeIntervalSinceReferenceDate:timeStamp];
self.quarantineDict = quarantineDict;
}
[rs close];
[db close];
}
}
}
}
return (self.quarantineDict == (NSDictionary *)[NSNull null]) ? nil : self.quarantineDict;
}
@@ -443,23 +697,26 @@ 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;
}
- (NSString *)resolvePath:(NSString *)path {
///
/// Resolves a given path:
/// + Follows symlinks
/// + Converts relative paths to absolute
/// + If path is a directory, checks to see if that directory is a bundle and if so
/// returns the path to that bundles CFBundleExecutable and stores a reference to the
/// bundle in the bundle out-param.
///
- (NSString *)resolvePath:(NSString *)path bundle:(NSBundle **)bundle {
// Convert to absolute, standardized path
path = [path stringByResolvingSymlinksInPath];
if (![path isAbsolutePath]) {
@@ -473,18 +730,27 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
BOOL directory;
if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&directory]) {
return nil;
} else if (directory) {
NSString *infoPath = [path stringByAppendingPathComponent:@"Contents/Info.plist"];
NSDictionary *d = [NSDictionary dictionaryWithContentsOfFile:infoPath];
if (d && d[@"CFBundleExecutable"]) {
path = [path stringByAppendingPathComponent:@"Contents/MacOS"];
return [path stringByAppendingPathComponent:d[@"CFBundleExecutable"]];
} else {
return nil;
}
} else if (directory && ![path isEqualToString:@"/"]) {
NSBundle *bndl = [NSBundle bundleWithPath:path];
if (bundle) *bundle = bndl;
return [bndl executablePath];
} else {
return path;
}
}
///
/// Cache and return a MOLCodeSignChecker for the given file. If there was an error creating the
/// code sign checker it will be returned in the passed-in error parameter.
///
- (MOLCodesignChecker *)codesignCheckerWithError:(NSError **)error {
if (!self.cachedCodesignChecker && !self.codesignCheckerError) {
NSError *e;
self.cachedCodesignChecker = [[MOLCodesignChecker alloc] initWithBinaryPath:self.path error:&e];
self.codesignCheckerError = e;
}
if (error) *error = self.codesignCheckerError;
return self.cachedCodesignChecker;
}
@end

View File

@@ -0,0 +1,243 @@
/// Copyright 2015 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import <XCTest/XCTest.h>
#import "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);
XCTAssertEqualObjects(sut.path, @"/Applications/Safari.app/Contents/MacOS/Safari");
sut = [[SNTFileInfo alloc] initWithPath:@"../../../../../../../../../../../../../../../bin/ls"];
XCTAssertEqualObjects(sut.path, @"/bin/ls");
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/sbin/DirectoryService"];
XCTAssertEqualObjects(sut.path, @"/usr/libexec/dspluginhelperd");
}
- (void)testSHA1 {
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"missing_pagezero"
ofType:@""];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
XCTAssertNotNil(sut.SHA1);
XCTAssertEqual(sut.SHA1.length, 40);
XCTAssertEqualObjects(sut.SHA1, @"3a865bf47b4ceba20496e0e66e39e4cfa101ffe6");
}
- (void)testSHA256 {
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"missing_pagezero"
ofType:@""];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
XCTAssertNotNil(sut.SHA256);
XCTAssertEqual(sut.SHA256.length, 64);
XCTAssertEqualObjects(sut.SHA256,
@"5e089b65a1e7a4696d84a34510710b6993d1de21250c41daaec63d9981083eba");
}
- (void)testExecutable {
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/sbin/launchd"];
XCTAssertTrue(sut.isMachO);
XCTAssertTrue(sut.isExecutable);
XCTAssertFalse(sut.isDylib);
XCTAssertFalse(sut.isKext);
XCTAssertFalse(sut.isScript);
}
- (void)testPageZero {
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"missing_pagezero"
ofType:@""];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
XCTAssertTrue(sut.isMissingPageZero);
path = [[NSBundle bundleForClass:[self class]] pathForResource:@"bad_pagezero" ofType:@""];
sut = [[SNTFileInfo alloc] initWithPath:path];
XCTAssertTrue(sut.isMissingPageZero);
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/sbin/bless"];
XCTAssertFalse(sut.isMissingPageZero);
}
- (void)testKext {
SNTFileInfo *sut = [[SNTFileInfo alloc]
initWithPath:@"/System/Library/Extensions/AppleAPIC.kext/Contents/MacOS/AppleAPIC"];
XCTAssertTrue(sut.isMachO);
XCTAssertTrue(sut.isKext);
XCTAssertFalse(sut.isDylib);
XCTAssertFalse(sut.isExecutable);
XCTAssertFalse(sut.isFat);
XCTAssertFalse(sut.isScript);
}
- (void)testDylibs {
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/usr/lib/system/libsystem_platform.dylib"];
XCTAssertTrue(sut.isMachO);
XCTAssertTrue(sut.isDylib);
XCTAssertTrue(sut.isFat);
XCTAssertFalse(sut.isKext);
XCTAssertFalse(sut.isExecutable);
XCTAssertFalse(sut.isScript);
}
- (void)testScript {
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/usr/bin/h2ph"];
XCTAssertTrue(sut.isScript);
XCTAssertFalse(sut.isDylib);
XCTAssertFalse(sut.isExecutable);
XCTAssertFalse(sut.isFat);
XCTAssertFalse(sut.isKext);
XCTAssertFalse(sut.isMachO);
}
- (void)testBundle {
NSString *path = [self bundleExample];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
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)testAncestorBundle {
NSString *path = [self bundleExample];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
sut.useAncestorBundle = YES;
XCTAssertNotNil([sut bundle]);
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.santa.UnitTest.SNTFileInfoTest");
XCTAssertNotNil([sut bundleVersion]);
XCTAssertNotNil([sut bundleShortVersionString]);
NSString *ancestorBundlePath = path;
for (int i = 0; i < 4; i++) {
ancestorBundlePath = [ancestorBundlePath stringByDeletingLastPathComponent];
}
XCTAssertEqualObjects([sut bundlePath], ancestorBundlePath);
}
- (void)testBundleIsAncestor {
NSString *path = [NSBundle bundleForClass:[self class]].bundlePath;
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
sut.useAncestorBundle = YES;
XCTAssertNotNil([sut bundle]);
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 = [self bundleExample];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
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);
sut.useAncestorBundle = YES;
XCTAssertNotNil([sut bundle]);
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.santa.UnitTest.SNTFileInfoTest");
XCTAssertNotNil([sut bundleVersion]);
XCTAssertNotNil([sut bundleShortVersionString]);
NSString *ancestorBundlePath = path;
for (int i = 0; i < 4; i++) {
ancestorBundlePath = [ancestorBundlePath stringByDeletingLastPathComponent];
}
XCTAssertEqualObjects([sut bundlePath], ancestorBundlePath);
}
- (void)testNonBundle {
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/usr/bin/yes"];
XCTAssertNil([sut bundle]);
sut.useAncestorBundle = YES;
XCTAssertNil([sut bundle]);
}
- (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..
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/bin/csreq"];
XCTAssertNotNil([sut infoPlist]);
}
@end

View File

@@ -1,34 +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.
///
/// Simple file watching class using dispatch sources. Will automatically
/// reload the watch if the file is deleted. Will 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.
///
/// @note Shortly after the file has been opened and monitoring has begun, the provided handler
/// will be called.
///
- (instancetype)initWithFilePath:(NSString *)filePath handler:(void (^)(void))handler;
@end

View File

@@ -1,101 +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"
@interface SNTFileWatcher ()
@property NSString *filePath;
@property dispatch_source_t monitoringSource;
@property(strong) void (^eventHandler)(void);
@property(strong) void (^internalEventHandler)(void);
@property(strong) void (^internalCancelHandler)(void);
@end
@implementation SNTFileWatcher
- (instancetype)init {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (instancetype)initWithFilePath:(NSString *)filePath handler:(void (^)(void))handler {
self = [super init];
if (self) {
_filePath = filePath;
_eventHandler = handler;
if (!_filePath || !_eventHandler) return nil;
[self beginWatchingFile];
}
return self;
}
- (void)dealloc {
[self stopWatchingFile];
}
- (void)beginWatchingFile {
__weak __typeof(self) weakSelf = self;
int mask = (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE |
DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_RENAME);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
self.internalEventHandler = ^{
unsigned long l = dispatch_source_get_data(weakSelf.monitoringSource);
if (l & DISPATCH_VNODE_DELETE || l & DISPATCH_VNODE_RENAME) {
if (weakSelf.monitoringSource) dispatch_source_cancel(weakSelf.monitoringSource);
} else {
weakSelf.eventHandler();
}
};
self.internalCancelHandler = ^{
int fd;
if (weakSelf.monitoringSource) {
fd = (int)dispatch_source_get_handle(weakSelf.monitoringSource);
close(fd);
}
const char *filePathCString = [weakSelf.filePath fileSystemRepresentation];
while ((fd = open(filePathCString, O_EVTONLY)) < 0) {
usleep(1000);
}
weakSelf.monitoringSource =
dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, mask, queue);
dispatch_source_set_event_handler(weakSelf.monitoringSource, weakSelf.internalEventHandler);
dispatch_source_set_cancel_handler(weakSelf.monitoringSource, weakSelf.internalCancelHandler);
dispatch_resume(weakSelf.monitoringSource);
weakSelf.eventHandler();
};
dispatch_async(queue, self.internalCancelHandler);
}
- (void)stopWatchingFile {
if (!self.monitoringSource) return;
int fd = (int)dispatch_source_get_handle(self.monitoringSource);
dispatch_source_set_event_handler_f(self.monitoringSource, NULL);
dispatch_source_set_cancel_handler(self.monitoringSource, ^{ close(fd); });
dispatch_source_cancel(self.monitoringSource);
self.monitoringSource = nil;
}
@end

View File

@@ -1,89 +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.
///
/// 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
// Defines the name of the userclient class and the driver bundle ID.
#define USERCLIENT_CLASS "com_google_SantaDriver"
#define USERCLIENT_ID "com.google.santa-driver"
// List of methods supported by the driver.
enum SantaDriverMethods {
kSantaUserClientOpen,
kSantaUserClientAllowBinary,
kSantaUserClientDenyBinary,
kSantaUserClientClearCache,
kSantaUserClientCacheCount,
// Any methods supported by the driver should be added above this line to
// ensure this remains the count of methods.
kSantaUserClientNMethods,
};
// Enum defining actions that can be passed down the IODataQueue and in
// response methods.
typedef enum {
ACTION_UNSET = 0,
// CHECKBW
ACTION_REQUEST_CHECKBW = 10,
ACTION_RESPOND_CHECKBW_ALLOW = 11,
ACTION_RESPOND_CHECKBW_DENY = 12,
// NOTIFY
ACTION_NOTIFY_EXEC = 20,
ACTION_NOTIFY_WRITE = 21,
ACTION_NOTIFY_RENAME = 22,
ACTION_NOTIFY_LINK = 23,
ACTION_NOTIFY_EXCHANGE = 24,
ACTION_NOTIFY_DELETE = 25,
// SHUTDOWN
ACTION_REQUEST_SHUTDOWN = 90,
// ERROR
ACTION_ERROR = 99,
} santa_action_t;
#define CHECKBW_RESPONSE_VALID(x) \
(x == ACTION_RESPOND_CHECKBW_ALLOW || x == ACTION_RESPOND_CHECKBW_DENY)
// Message struct that is sent down the IODataQueue.
typedef struct {
santa_action_t action;
uint64_t vnode_id;
uid_t uid;
gid_t gid;
pid_t pid;
pid_t ppid;
char path[MAXPATHLEN];
char newpath[MAXPATHLEN];
// For file events, this is the process name.
// For exec requests, this is the parent process name.
// While process names can technically be 4*MAXPATHLEN, that never
// actually happens, so only take MAXPATHLEN and throw away any excess.
char pname[MAXPATHLEN];
} santa_message_t;
#endif // SANTA__COMMON__KERNELCOMMON_H

View File

@@ -13,24 +13,17 @@
/// limitations under the License.
///
/// Logging definitions, for both kernel and user space.
/// Logging definitions
///
#ifndef SANTA__COMMON__LOGGING_H
#define SANTA__COMMON__LOGGING_H
#ifdef KERNEL
#ifdef __cplusplus
extern "C" {
#endif
#ifdef DEBUG
#define LOGD(...) IOLog("D santa-driver: " __VA_ARGS__); IOLog("\n");
#else // DEBUG
#define LOGD(...)
#endif // DEBUG
#define LOGI(...) IOLog("I santa-driver: " __VA_ARGS__); IOLog("\n")
#define LOGW(...) IOLog("W santa-driver: " __VA_ARGS__); IOLog("\n")
#define LOGE(...) IOLog("E santa-driver: " __VA_ARGS__); IOLog("\n")
#else // KERNEL
#import <Foundation/Foundation.h>
typedef enum : NSUInteger {
LOG_LEVEL_ERROR,
@@ -48,7 +41,7 @@ typedef enum : NSUInteger {
/// @param ... the arguments to format.
///
void logMessage(LogLevel level, FILE *destination, NSString *format, ...)
__attribute__((format(__NSString__, 3, 4)));
__attribute__((format(__NSString__, 3, 4)));
/// Simple logging macros
#define LOGD(logFormat, ...) logMessage(LOG_LEVEL_DEBUG, stdout, logFormat, ##__VA_ARGS__)
@@ -56,6 +49,11 @@ 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__)
#endif // KERNEL
/// Get the logging level for this process.
LogLevel EffectiveLogLevel();
#ifdef __cplusplus
} // extern C
#endif
#endif // SANTA__COMMON__LOGGING_H

View File

@@ -12,59 +12,101 @@
/// 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>
void syslogClientDestructor(void *arg) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
asl_close((aslclient)arg);
#pragma clang diagnostic pop
}
LogLevel EffectiveLogLevel() {
#ifdef DEBUG
static LogLevel logLevel = LOG_LEVEL_DEBUG;
static LogLevel logLevel = LOG_LEVEL_DEBUG;
#else
static LogLevel logLevel = LOG_LEVEL_INFO; // default to info
static LogLevel logLevel = LOG_LEVEL_INFO; // default to info
#endif
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if ([SNTConfigurator configurator].enableDebugLogging) {
logLevel = LOG_LEVEL_DEBUG;
}
});
return logLevel;
}
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"]) {
logLevel = LOG_LEVEL_DEBUG;
}
// If requested, redirect output to syslog.
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--syslog"] ||
strcmp(binaryName, "santad") == 0) {
useSyslog = YES;
}
// If requested, redirect output to syslog.
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--syslog"] ||
[binaryName isEqualToString:@"com.google.santa.daemon"]) {
useSyslog = YES;
pthread_key_create(&syslogKey, syslogClientDestructor);
}
});
if (logLevel < level) return;
if (EffectiveLogLevel() < level) return;
va_list args;
va_start(args, format);
NSString *s = [[NSString alloc] initWithFormat:format arguments:args];
NSMutableString *s = [[NSMutableString alloc] initWithFormat:format arguments:args];
va_end(args);
if (useSyslog) {
aslclient client = asl_open(NULL, "com.google.santa", 0);
asl_set_filter(client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
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);
}
char *levelName;
int syslogLevel = ASL_LEVEL_DEBUG;
switch (level) {
case LOG_LEVEL_ERROR: levelName = "E"; syslogLevel = ASL_LEVEL_ERR; break;
case LOG_LEVEL_WARN: levelName = "W"; syslogLevel = ASL_LEVEL_WARNING; break;
case LOG_LEVEL_INFO: levelName = "I"; syslogLevel = ASL_LEVEL_INFO; break;
case LOG_LEVEL_DEBUG: levelName = "D"; syslogLevel = ASL_LEVEL_DEBUG; break;
case LOG_LEVEL_ERROR:
levelName = "E";
syslogLevel = ASL_LEVEL_ERR;
break;
case LOG_LEVEL_WARN:
levelName = "W";
syslogLevel = ASL_LEVEL_WARNING;
break;
case LOG_LEVEL_INFO:
levelName = "I";
syslogLevel = ASL_LEVEL_NOTICE; // Maps to ULS Default
break;
case LOG_LEVEL_DEBUG:
levelName = "D";
// 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]);
asl_close(client);
#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 {
fprintf(destination, "%s\n", [s UTF8String]);
[s appendString:@"\n"];
size_t len = [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
fwrite([s UTF8String], len, 1, destination);
}
}

View File

@@ -0,0 +1,202 @@
/// Copyright 2021 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 "SNTCommonEnums.h"
/**
* Provides an abstraction for various metric systems that will be exported to
* monitoring systems via the MetricService. This is used to store internal
* counters and metrics that can be exported to an external monitoring system.
*
* `SNTMetricSet` for storing and creating metrics and counters. This is
* the externally visible interface
* class.
*
* Metric classes:
* * `SNTMetric` to store metric values broken down by "field" dimensions.
* * subclasses of `SNTMetric` with suitable setters:
* * `SNTMetricCounter`
* * `SNTMetricGaugeInt64`
* * `SNTMetricGaugeDouble`
* * `SNTMetricString`
* * `SNTMetricBool`
*/
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, SNTMetricType) {
SNTMetricTypeUnknown = 0,
SNTMetricTypeConstantBool = 1,
SNTMetricTypeConstantString = 2,
SNTMetricTypeConstantInt64 = 3,
SNTMetricTypeConstantDouble = 4,
SNTMetricTypeGaugeBool = 5,
SNTMetricTypeGaugeString = 6,
SNTMetricTypeGaugeInt64 = 7,
SNTMetricTypeGaugeDouble = 8,
SNTMetricTypeCounter = 9,
};
NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType);
@interface SNTMetric : NSObject
- (NSDictionary *)export;
@end
@interface SNTMetricCounter : SNTMetric
- (void)incrementBy:(long long)step forFieldValues:(NSArray<NSString *> *)fieldValues;
- (void)incrementForFieldValues:(NSArray<NSString *> *)fieldValues;
- (long long)getCountForFieldValues:(NSArray<NSString *> *)fieldValues;
@end
@interface SNTMetricInt64Gauge : SNTMetric
- (void)set:(long long)value forFieldValues:(NSArray<NSString *> *)fieldValues;
- (long long)getGaugeValueForFieldValues:(NSArray<NSString *> *)fieldValues;
@end
@interface SNTMetricDoubleGauge : SNTMetric
- (void)set:(double)value forFieldValues:(NSArray<NSString *> *)fieldValues;
- (double)getGaugeValueForFieldValues:(NSArray<NSString *> *)fieldValues;
@end
@interface SNTMetricStringGauge : SNTMetric
- (void)set:(NSString *)value forFieldValues:(NSArray<NSString *> *)fieldValues;
- (NSString *)getStringValueForFieldValues:(NSArray<NSString *> *)fieldValues;
@end
@interface SNTMetricBooleanGauge : SNTMetric
- (void)set:(BOOL)value forFieldValues:(NSArray<NSString *> *)fieldValues;
- (BOOL)getBoolValueForFieldValues:(NSArray<NSString *> *)fieldValues;
@end
/**
* A registry of metrics with associated fields.
*/
@interface SNTMetricSet : NSObject
- (instancetype)initWithHostname:(NSString *)hostname username:(NSString *)username;
/* Returns a counter with the given name, field names and help
* text, registered with the MetricSet.
*
* @param name The counter name, for example @"/proc/cpu".
* @param fieldNames The counter's field names, for example @[@"result"].
* @param helpText The counter's help description.
* @return A counter with the given specification registered with this root.
* The returned counter might have been created earlier with the same
* specification.
* @throw NSInternalInconsistencyException When trying to register a second
* counter with the same name but a different schema as an existing one
*/
- (SNTMetricCounter *)counterWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)text;
/**
* Returns a shared global instance with default root labels and metrics registered.
*/
+ (instancetype)sharedInstance;
/**
* Resets all the metrics in this set. Intended only for testing.
*/
- (void)reset;
/**
* Add a root label to the MetricSet.
*/
- (void)addRootLabel:(NSString *)label value:(NSString *)value;
/**
* Remove a root label from the MetricSet.
*/
- (void)removeRootLabel:(NSString *)labelName;
/**
* Returns a int64 gauge metric with the given Streamz name and help text,
* registered with this MetricSet.
*
* @param name The metric name, for example @"/memory/free".
* @param fieldNames The metric's field names, for example @[@"type"].
* @param helpText The metric's help description.
*/
- (SNTMetricInt64Gauge *)int64GaugeWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)helpText;
/**
* Returns a double gauge metric with the given name and help text,
* registered with this root.
*
* @param name The metric name, for example @"/memory/free".
* @param fieldNames The metric's field names, for example @[@"type"].
* @param helpText The metric's help description.
*/
- (SNTMetricDoubleGauge *)doubleGaugeWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)helpText;
/**
* Returns a string gauge metric with the given name and help text,
* registered with this metric set.
*
* @param name The metric name, for example @"/santa/mode".
* @param fieldNames The metric's field names, for example @[@"type"].
* @param helpText The metric's help description.
*/
- (SNTMetricStringGauge *)stringGaugeWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)helpText;
/**
* Returns a boolean gauge metric with the given name and help text,
* registered with this metric set.
*
* @param name The metric name, for example @"/memory/free".
* @param fieldNames The metric's field names, for example @[@"type"].
* @param helpText The metric's help description.
*/
- (SNTMetricBooleanGauge *)booleanGaugeWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)helpText;
/** Creates a constant metric with a string value and no fields. */
- (void)addConstantStringWithName:(NSString *)name
helpText:(NSString *)helpText
value:(NSString *)value;
/** Creates a constant metric with an integer value and no fields. */
- (void)addConstantIntegerWithName:(NSString *)name
helpText:(NSString *)helpText
value:(long long)value;
/** Creates a constant metric with an integer value and no fields. */
- (void)addConstantBooleanWithName:(NSString *)name helpText:(NSString *)helpText value:(BOOL)value;
/** Register a callback to get executed just before each export. */
- (void)registerCallback:(void (^)(void))callback;
/** Export creates an NSDictionary of the state of the metrics */
- (NSDictionary *)export;
@end
// Returns a human readble string from an SNTMetricFormat type
NSString *SNTMetricStringFromMetricFormatType(SNTMetricFormatType format);
/** Normalizes dates in an exported dictionary to be ISO8601 timestamp strings in
* UTC time.
*/
NSDictionary *SNTMetricConvertDatesToISO8601Strings(NSDictionary *metrics);
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,676 @@
/// Copyright 2021 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 "SNTMetricSet.h"
#import "SNTCommonEnums.h"
NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType) {
NSString *typeStr;
switch (metricType) {
case SNTMetricTypeConstantBool: typeStr = @"SNTMetricTypeConstantBool"; break;
case SNTMetricTypeConstantString: typeStr = @"SNTMetricTypeConstantString"; break;
case SNTMetricTypeConstantInt64: typeStr = @"SNTMetricTypeConstantInt64"; break;
case SNTMetricTypeConstantDouble: typeStr = @"SNTMetricTypeConstantDouble"; break;
case SNTMetricTypeGaugeBool: typeStr = @"SNTMetricTypeGaugeBool"; break;
case SNTMetricTypeGaugeString: typeStr = @"SNTMetricTypeGaugeString"; break;
case SNTMetricTypeGaugeInt64: typeStr = @"SNTMetricTypeGaugeInt64"; break;
case SNTMetricTypeGaugeDouble: typeStr = @"SNTMetricTypeGaugeDouble"; break;
case SNTMetricTypeCounter: typeStr = @"SNTMetricTypeCounter"; break;
default: typeStr = [NSString stringWithFormat:@"SNTMetricTypeUnknown %ld", metricType]; break;
}
return typeStr;
}
/**
* SNTMetricValue encapsulates the value of a metric along with the creation
* and update timestamps. It is thread-safe and has a separate field for each
* metric type.
*
* It is intended to only be used by SNTMetrics;
*/
@interface SNTMetricValue : NSObject
/** Increment the counter by the step value, updating timestamps appropriately. */
- (void)addInt64:(long long)step;
/** Set the Int64 value. */
- (void)setInt64:(long long)value;
/** Set the double value. */
- (void)setDouble:(double)value;
/** Set the string value. */
- (void)setString:(NSString *)value;
/** Set the BOOL string value. */
- (void)setBool:(BOOL)value;
/**
* Clears the last update timestamp.
*
* This makes the metric value always emit the current timestamp as last update timestamp.
*/
- (void)clearLastUpdateTimestamp;
/** Getters */
- (long long)getInt64Value;
- (double)getDoubleValue;
- (NSString *)getStringValue;
@end
@implementation SNTMetricValue {
/** The int64 value for the SNTMetricValue, if set. */
long long _int64Value;
/** The double value for the SNTMetricValue, if set. */
double _doubleValue;
/** The string value for the SNTMetricValue, if set. */
NSString *_stringValue;
/** The boolean value for the SNTMetricValue, if set. */
BOOL _boolValue;
/** The first time this cell got created in the current process. */
NSDate *_creationTime;
/** The last time that the counter value was changed. */
NSDate *_lastUpdate;
}
- (instancetype)init {
self = [super init];
if (self) {
_creationTime = [NSDate date];
_lastUpdate = _creationTime;
}
return self;
}
- (void)addInt64:(long long)step {
@synchronized(self) {
_int64Value += step;
_lastUpdate = [NSDate date];
}
}
- (void)setInt64:(long long)value {
@synchronized(self) {
_int64Value = value;
_lastUpdate = [NSDate date];
}
}
- (long long)getInt64Value {
@synchronized(self) {
return _int64Value;
}
}
- (void)setDouble:(double)value {
@synchronized(self) {
_doubleValue = value;
_lastUpdate = [NSDate date];
}
}
- (double)getDoubleValue {
@synchronized(self) {
return _doubleValue;
}
}
- (void)setString:(NSString *)value {
@synchronized(self) {
_stringValue = [value copy];
_lastUpdate = [NSDate date];
}
}
- (NSString *)getStringValue {
@synchronized(self) {
return [_stringValue copy];
}
}
- (void)setBool:(BOOL)value {
@synchronized(self) {
_boolValue = value;
_lastUpdate = [NSDate date];
}
}
- (BOOL)getBoolValue {
@synchronized(self) {
return _boolValue;
}
}
- (void)clearLastUpdateTimestamp {
@synchronized(self) {
_lastUpdate = nil;
}
}
- (NSDate *)getLastUpdatedTimestamp {
NSDate *updated = nil;
@synchronized(self) {
updated = [_lastUpdate copy];
}
return updated;
}
- (NSDate *)getCreatedTimestamp {
NSDate *created = nil;
@synchronized(self) {
created = [_creationTime copy];
}
return created;
}
@end
@implementation SNTMetric {
@private
/** Fully qualified metric name e.g. /ops/security/santa. */
NSString *_name;
/** A help text for the metric to be exported to be exported. **/
NSString *_help;
/** Sorted list of the fieldNames **/
NSArray<NSString *> *_fieldNames;
/** Mapping of field values to actual metric values (e.g. metric /proc/cpu_usage @"mode"=@"user"
* -> 0.89 */
NSMutableDictionary<NSArray<NSString *> *, SNTMetricValue *> *_metricsForFieldValues;
/** the type of metric this is e.g. counter, gauge etc. **/
SNTMetricType _type;
}
- (instancetype)initWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)help
type:(SNTMetricType)type {
self = [super init];
if (self) {
_name = [name copy];
_help = [help copy];
_fieldNames = [fieldNames copy];
_metricsForFieldValues = [[NSMutableDictionary alloc] init];
_type = type;
}
return self;
}
- (NSString *)name {
return _name;
}
- (BOOL)hasSameSchemaAsMetric:(SNTMetric *)other {
if (![other isKindOfClass:[self class]]) {
return NO;
}
return [_name isEqualToString:other->_name] && [_help isEqualToString:other->_help] &&
[_fieldNames isEqualTo:other->_fieldNames] && _type == other->_type;
}
/** Retrieves the SNTMetricValue for a given field value.
Creates a new SNTMetricValue if none is present. */
- (SNTMetricValue *)metricValueForFieldValues:(NSArray<NSString *> *)fieldValues {
NSParameterAssert(fieldValues.count == _fieldNames.count);
SNTMetricValue *metricValue = nil;
@synchronized(self) {
metricValue = _metricsForFieldValues[fieldValues];
if (!metricValue) {
// Deep copy to prevent mutations to the keys we store in the dictionary.
fieldValues = [fieldValues copy];
metricValue = [[SNTMetricValue alloc] init];
_metricsForFieldValues[fieldValues] = metricValue;
}
}
return metricValue;
}
- (NSDictionary *)encodeMetricValueForFieldValues:(NSArray<NSString *> *)fieldValues {
SNTMetricValue *metricValue = _metricsForFieldValues[fieldValues];
NSMutableDictionary *fieldDict = [[NSMutableDictionary alloc] init];
fieldDict[@"created"] = [metricValue getCreatedTimestamp];
fieldDict[@"last_updated"] = [metricValue getLastUpdatedTimestamp];
fieldDict[@"value"] = [fieldValues componentsJoinedByString:@","];
switch (_type) {
case SNTMetricTypeConstantBool:
case SNTMetricTypeGaugeBool:
fieldDict[@"data"] = [NSNumber numberWithBool:[metricValue getBoolValue]];
break;
case SNTMetricTypeConstantInt64:
case SNTMetricTypeCounter:
case SNTMetricTypeGaugeInt64:
fieldDict[@"data"] = [NSNumber numberWithLongLong:[metricValue getInt64Value]];
break;
case SNTMetricTypeConstantDouble:
case SNTMetricTypeGaugeDouble:
fieldDict[@"data"] = [NSNumber numberWithDouble:[metricValue getDoubleValue]];
break;
case SNTMetricTypeConstantString:
case SNTMetricTypeGaugeString: fieldDict[@"data"] = [metricValue getStringValue]; break;
default: break;
}
return fieldDict;
}
- (NSDictionary *)export {
NSMutableDictionary *metricDict = [NSMutableDictionary dictionaryWithCapacity:_fieldNames.count];
metricDict[@"type"] = [NSNumber numberWithInt:(int)_type];
metricDict[@"fields"] = [[NSMutableDictionary alloc] init];
metricDict[@"description"] = [_help copy];
if (_fieldNames.count == 0) {
metricDict[@"fields"][@""] = @[ [self encodeMetricValueForFieldValues:@[]] ];
} else {
for (NSString *fieldName in _fieldNames) {
NSMutableArray *fieldVals = [[NSMutableArray alloc] init];
for (NSArray<NSString *> *fieldValues in _metricsForFieldValues) {
[fieldVals addObject:[self encodeMetricValueForFieldValues:fieldValues]];
}
metricDict[@"fields"][fieldName] = fieldVals;
}
}
return metricDict;
}
@end
@implementation SNTMetricCounter
- (instancetype)initWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)helpText {
return [super initWithName:name
fieldNames:fieldNames
helpText:helpText
type:SNTMetricTypeCounter];
}
- (void)incrementBy:(long long)step forFieldValues:(NSArray<NSString *> *)fieldValues {
SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues];
if (!metricValue) {
return;
}
[metricValue addInt64:step];
}
- (void)incrementForFieldValues:(NSArray<NSString *> *)fieldValues {
[self incrementBy:1 forFieldValues:fieldValues];
}
- (long long)getCountForFieldValues:(NSArray<NSString *> *)fieldValues {
SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues];
if (!metricValue) {
return -1;
}
return [metricValue getInt64Value];
}
@end
@implementation SNTMetricInt64Gauge
- (instancetype)initWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)helpText {
return [super initWithName:name
fieldNames:fieldNames
helpText:helpText
type:SNTMetricTypeGaugeInt64];
}
- (void)set:(long long)value forFieldValues:(NSArray<NSString *> *)fieldValues {
SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues];
[metricValue setInt64:value];
}
- (long long)getGaugeValueForFieldValues:(NSArray<NSString *> *)fieldValues {
SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues];
if (!metricValue) {
return -1;
}
return [metricValue getInt64Value];
}
@end
@implementation SNTMetricDoubleGauge
- (instancetype)initWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)text {
return [super initWithName:name
fieldNames:fieldNames
helpText:text
type:SNTMetricTypeGaugeDouble];
}
- (void)set:(double)value forFieldValues:(NSArray<NSString *> *)fieldValues {
SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues];
[metricValue setDouble:value];
}
- (double)getGaugeValueForFieldValues:(NSArray<NSString *> *)fieldValues {
SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues];
if (!metricValue) {
return -1;
}
return [metricValue getDoubleValue];
}
@end
@implementation SNTMetricStringGauge
- (instancetype)initWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)text {
return [super initWithName:name
fieldNames:fieldNames
helpText:text
type:SNTMetricTypeGaugeString];
}
- (void)set:(NSString *)value forFieldValues:(NSArray<NSString *> *)fieldValues {
SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues];
[metricValue setString:value];
}
- (NSString *)getStringValueForFieldValues:(NSArray<NSString *> *)fieldValues {
SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues];
if (!metricValue) {
return nil;
}
return [metricValue getStringValue];
}
@end
@implementation SNTMetricBooleanGauge
- (instancetype)initWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)helpText {
return [super initWithName:name
fieldNames:fieldNames
helpText:helpText
type:SNTMetricTypeGaugeBool];
}
- (void)set:(BOOL)value forFieldValues:(NSArray<NSString *> *)fieldValues {
SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues];
[metricValue setBool:value];
}
- (BOOL)getBoolValueForFieldValues:(NSArray<NSString *> *)fieldValues {
SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues];
if (!metricValue) {
return false;
}
return [metricValue getBoolValue];
}
@end
/**
* SNTMetricSet is the top level container for all metrics and metrics value
* its is abstracted from specific implementations but is close to Google's
* Monarch and Prometheus formats.
*/
@implementation SNTMetricSet {
@private
/** Labels that are used to identify the entity to that all metrics apply to. */
NSMutableDictionary<NSString *, NSString *> *_rootLabels;
/** Registered metrics keyed by name */
NSMutableDictionary<NSString *, SNTMetric *> *_metrics;
/** Callbacks to update metric values before exporting metrics */
NSMutableArray<void (^)(void)> *_callbacks;
}
+ (instancetype)sharedInstance {
static SNTMetricSet *sharedMetrics;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMetrics = [[SNTMetricSet alloc] init];
});
return sharedMetrics;
}
- (instancetype)init {
self = [super init];
if (self) {
_rootLabels = [[NSMutableDictionary alloc] init];
_metrics = [[NSMutableDictionary alloc] init];
_callbacks = [[NSMutableArray alloc] init];
}
return self;
}
- (instancetype)initWithHostname:(NSString *)hostname username:(NSString *)username {
self = [super init];
if (self) {
_rootLabels = [[NSMutableDictionary alloc] init];
_metrics = [[NSMutableDictionary alloc] init];
_callbacks = [[NSMutableArray alloc] init];
_rootLabels[@"hostname"] = [hostname copy];
_rootLabels[@"username"] = [username copy];
}
return self;
}
- (void)reset {
_metrics = [[NSMutableDictionary alloc] init];
}
- (void)addRootLabel:(NSString *)label value:(NSString *)value {
@synchronized(self) {
_rootLabels[label] = value;
}
}
- (void)removeRootLabel:(NSString *)label {
@synchronized(self) {
[_rootLabels removeObjectForKey:label];
}
}
- (SNTMetric *)registerMetric:(nonnull SNTMetric *)metric {
@synchronized(self) {
SNTMetric *oldMetric = _metrics[[metric name]];
if ([oldMetric hasSameSchemaAsMetric:metric]) {
return oldMetric;
}
NSAssert(!oldMetric, @"metric registered twice: %@", metric.name);
_metrics[metric.name] = metric;
}
return metric;
}
- (void)registerCallback:(void (^)(void))callback {
@synchronized(self) {
[_callbacks addObject:callback];
}
}
- (SNTMetricCounter *)counterWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)helpText {
SNTMetricCounter *c = [[SNTMetricCounter alloc] initWithName:name
fieldNames:fieldNames
helpText:helpText];
return (SNTMetricCounter *)[self registerMetric:c];
}
- (SNTMetricInt64Gauge *)int64GaugeWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)helpText {
SNTMetricInt64Gauge *g = [[SNTMetricInt64Gauge alloc] initWithName:name
fieldNames:fieldNames
helpText:helpText];
return (SNTMetricInt64Gauge *)[self registerMetric:g];
}
- (SNTMetricDoubleGauge *)doubleGaugeWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)helpText {
SNTMetricDoubleGauge *g = [[SNTMetricDoubleGauge alloc] initWithName:name
fieldNames:fieldNames
helpText:helpText];
return (SNTMetricDoubleGauge *)[self registerMetric:g];
}
- (SNTMetricStringGauge *)stringGaugeWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)helpText {
SNTMetricStringGauge *s = [[SNTMetricStringGauge alloc] initWithName:name
fieldNames:fieldNames
helpText:helpText];
return (SNTMetricStringGauge *)[self registerMetric:s];
}
- (SNTMetricBooleanGauge *)booleanGaugeWithName:(NSString *)name
fieldNames:(NSArray<NSString *> *)fieldNames
helpText:(NSString *)helpText {
SNTMetricBooleanGauge *b = [[SNTMetricBooleanGauge alloc] initWithName:name
fieldNames:fieldNames
helpText:helpText];
return (SNTMetricBooleanGauge *)[self registerMetric:b];
}
- (void)addConstantStringWithName:(NSString *)name
helpText:(NSString *)helpText
value:(NSString *)value {
SNTMetric *metric = [[SNTMetric alloc] initWithName:name
fieldNames:@[]
helpText:helpText
type:SNTMetricTypeConstantString];
SNTMetricValue *metricValue = [metric metricValueForFieldValues:@[]];
[metricValue setString:value];
[self registerMetric:metric];
}
- (void)addConstantIntegerWithName:(NSString *)name
helpText:(NSString *)helpText
value:(long long)value {
SNTMetric *metric = [[SNTMetric alloc] initWithName:name
fieldNames:@[]
helpText:helpText
type:SNTMetricTypeConstantInt64];
SNTMetricValue *metricValue = [metric metricValueForFieldValues:@[]];
[metricValue setInt64:value];
[self registerMetric:metric];
}
- (void)addConstantBooleanWithName:(NSString *)name
helpText:(NSString *)helpText
value:(BOOL)value {
SNTMetric *metric = [[SNTMetric alloc] initWithName:name
fieldNames:@[]
helpText:helpText
type:SNTMetricTypeConstantBool];
SNTMetricValue *metricValue = [metric metricValueForFieldValues:@[]];
[metricValue setBool:value];
[self registerMetric:metric];
}
/** Export current state of the SNTMetricSet as an NSDictionary. */
- (NSDictionary *)export {
NSDictionary *exported = nil;
// Invoke callbacks to ensure metrics are up to date.
for (void (^cb)(void) in _callbacks) {
cb();
}
@synchronized(self) {
NSMutableDictionary *exportDict = [[NSMutableDictionary alloc] init];
exportDict[@"root_labels"] = [_rootLabels copy];
exportDict[@"metrics"] = [[NSMutableDictionary alloc] init];
// TODO(markowsky) Sort the metrics so we always get the same output.
for (NSString *metricName in _metrics) {
exportDict[@"metrics"][metricName] = [_metrics[metricName] export];
}
exported = [NSDictionary dictionaryWithDictionary:exportDict];
}
return exported;
}
// Returns a human readble string from an SNTMetricFormat type
NSString *SNTMetricStringFromMetricFormatType(SNTMetricFormatType format) {
switch (format) {
case SNTMetricFormatTypeRawJSON: return @"rawjson";
case SNTMetricFormatTypeMonarchJSON: return @"monarchjson";
default: return @"Unknown Metric Format";
}
}
NSDictionary *SNTMetricConvertDatesToISO8601Strings(NSDictionary *metrics) {
NSMutableDictionary *mutableMetrics = [metrics mutableCopy];
id formatter;
if (@available(macOS 10.13, *)) {
NSISO8601DateFormatter *isoFormatter = [[NSISO8601DateFormatter alloc] init];
isoFormatter.formatOptions =
NSISO8601DateFormatWithInternetDateTime | NSISO8601DateFormatWithFractionalSeconds;
formatter = isoFormatter;
} else {
NSDateFormatter *localFormatter = [[NSDateFormatter alloc] init];
[localFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
[localFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
formatter = localFormatter;
}
for (NSString *metricName in mutableMetrics[@"metrics"]) {
NSMutableDictionary *metric = mutableMetrics[@"metrics"][metricName];
for (NSString *field in metric[@"fields"]) {
NSMutableArray<NSMutableDictionary *> *values = metric[@"fields"][field];
[values enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
values[index][@"created"] = [formatter stringFromDate:values[index][@"created"]];
values[index][@"last_updated"] = [formatter stringFromDate:values[index][@"last_updated"]];
}];
}
}
return mutableMetrics;
}
@end

View File

@@ -0,0 +1,675 @@
#import <XCTest/XCTest.h>
#import "Source/common/SNTMetricSet.h"
@interface SNTMetricCounterTest : XCTestCase
@end
@interface SNTMetricGaugeInt64Test : XCTestCase
@end
@interface SNTMetricDoubleGaugeTest : XCTestCase
@end
@interface SNTMetricBooleanGaugeTest : XCTestCase
@end
@interface SNTMetricStringGaugeTest : XCTestCase
@end
@interface SNTMetricSetTest : XCTestCase
@end
@interface SNTMetricSetHelperFunctionsTest : XCTestCase
@end
// Stub out NSDate's date method
@implementation NSDate (custom)
+ (instancetype)date {
NSDateFormatter *formatter = NSDateFormatter.new;
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ssZZZ"];
return [formatter dateFromString:@"2021-08-05 13:00:10+0000"];
}
@end
@implementation SNTMetricCounterTest
- (void)testSimpleCounter {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricCounter *c =
[metricSet counterWithName:@"/santa/events"
fieldNames:@[ @"rule_type" ]
helpText:@"Count of exec events broken out by rule type."];
XCTAssertNotNil(c, @"Expected returned SNTMetricCounter to not be nil");
[c incrementForFieldValues:@[ @"certificate" ]];
XCTAssertEqual(1, [c getCountForFieldValues:@[ @"certificate" ]],
@"Counter not incremented by 1");
[c incrementBy:3 forFieldValues:@[ @"certificate" ]];
XCTAssertEqual(4, [c getCountForFieldValues:@[ @"certificate" ]],
@"Counter not incremented by 3");
}
- (void)testExportNSDictionary {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricCounter *c =
[metricSet counterWithName:@"/santa/events"
fieldNames:@[ @"rule_type" ]
helpText:@"Count of exec events broken out by rule type."];
XCTAssertNotNil(c);
[c incrementForFieldValues:@[ @"certificate" ]];
NSDictionary *expected = @{
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeCounter],
@"description" : @"Count of exec events broken out by rule type.",
@"fields" : @{
@"rule_type" : @[ @{
@"value" : @"certificate",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithInt:1]
} ]
}
};
XCTAssertEqualObjects([c export], expected);
}
- (void)testAddingMetricWithSameSchema {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricCounter *a = [metricSet counterWithName:@"/santa/counter"
fieldNames:@[]
helpText:@"Test counter."];
SNTMetricCounter *b = [metricSet counterWithName:@"/santa/counter"
fieldNames:@[]
helpText:@"Test counter."];
XCTAssertEqual(a, b, @"Unexpected new counter returned.");
}
@end
@implementation SNTMetricBooleanGaugeTest
- (void)testSimpleGauge {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricBooleanGauge *b = [metricSet booleanGaugeWithName:@"/santa/daemon_connected"
fieldNames:@[]
helpText:@"Is the daemon connected."];
XCTAssertNotNil(b);
[b set:true forFieldValues:@[]];
XCTAssertTrue([b getBoolValueForFieldValues:@[]]);
[b set:false forFieldValues:@[]];
XCTAssertFalse([b getBoolValueForFieldValues:@[]]);
}
- (void)testExportNSDictionary {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricBooleanGauge *b = [metricSet booleanGaugeWithName:@"/santa/daemon_connected"
fieldNames:@[]
helpText:@"Is the daemon connected."];
XCTAssertNotNil(b);
[b set:true forFieldValues:@[]];
NSDictionary *expected = @{
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeBool],
@"description" : @"Is the daemon connected.",
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithBool:true]
} ]
}
};
NSDictionary *output = [b export];
XCTAssertEqualObjects(output, expected);
}
- (void)testAddingBooleanWithSameSchema {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricBooleanGauge *a = [metricSet booleanGaugeWithName:@"/santa/daemon_connected"
fieldNames:@[]
helpText:@"Is the daemon connected."];
SNTMetricBooleanGauge *b = [metricSet booleanGaugeWithName:@"/santa/daemon_connected"
fieldNames:@[]
helpText:@"Is the daemon connected."];
XCTAssertEqual(a, b, @"Unexpected new boolean gauge returned.");
}
@end
@implementation SNTMetricGaugeInt64Test
- (void)testSimpleGauge {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricInt64Gauge *g =
[metricSet int64GaugeWithName:@"/santa/rules"
fieldNames:@[ @"rule_type" ]
helpText:@"Count of rules broken out by rule type."];
XCTAssertNotNil(g, @"Expected returned SNTMetricGaugeInt64 to not be nil");
// set from zero
[g set:250 forFieldValues:@[ @"binary" ]];
XCTAssertEqual(250, [g getGaugeValueForFieldValues:@[ @"binary" ]]);
// Increase the gauge
[g set:500 forFieldValues:@[ @"binary" ]];
XCTAssertEqual(500, [g getGaugeValueForFieldValues:@[ @"binary" ]]);
// Decrease after increase
[g set:100 forFieldValues:@[ @"binary" ]];
XCTAssertEqual(100, [g getGaugeValueForFieldValues:@[ @"binary" ]]);
// Increase after decrease
[g set:750 forFieldValues:@[ @"binary" ]];
XCTAssertEqual(750, [g getGaugeValueForFieldValues:@[ @"binary" ]]);
// TODO: export the tree to JSON and confirm the structure is correct.
}
- (void)testExportNSDictionary {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricInt64Gauge *g =
[metricSet int64GaugeWithName:@"/santa/rules"
fieldNames:@[ @"rule_type" ]
helpText:@"Count of rules broken out by rule type."];
XCTAssertNotNil(g, @"Expected returned SNTMetricGaugeInt64 to not be nil");
// set from zero
[g set:250 forFieldValues:@[ @"binary" ]];
XCTAssertEqual(250, [g getGaugeValueForFieldValues:@[ @"binary" ]]);
NSDictionary *expected = @{
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64],
@"description" : @"Count of rules broken out by rule type.",
@"fields" : @{
@"rule_type" : @[ @{
@"value" : @"binary",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithInt:250]
} ]
}
};
XCTAssertEqualObjects([g export], expected);
}
- (void)testAddingMetricWithSameSchema {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricInt64Gauge *a = [metricSet int64GaugeWithName:@"/santa/int64gauge"
fieldNames:@[]
helpText:@"Test gauge."];
SNTMetricInt64Gauge *b = [metricSet int64GaugeWithName:@"/santa/int64gauge"
fieldNames:@[]
helpText:@"Test gauge."];
XCTAssertEqual(a, b, @"Unexpected new gauge returned.");
}
@end
@implementation SNTMetricDoubleGaugeTest
- (void)testSimpleGauge {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricDoubleGauge *g = [metricSet doubleGaugeWithName:@"/proc/cpu_usage"
fieldNames:@[ @"mode" ]
helpText:@"CPU time consumed by this process."];
XCTAssertNotNil(g, @"Expected returned SNTMetricDoubleGauge to not be nil");
// set from zero
[g set:(double)0.45 forFieldValues:@[ @"user" ]];
XCTAssertEqual(0.45, [g getGaugeValueForFieldValues:@[ @"user" ]]);
// Increase the gauge
[g set:(double)0.90 forFieldValues:@[ @"user" ]];
XCTAssertEqual(0.90, [g getGaugeValueForFieldValues:@[ @"user" ]]);
// Decrease after increase
[g set:0.71 forFieldValues:@[ @"user" ]];
XCTAssertEqual(0.71, [g getGaugeValueForFieldValues:@[ @"user" ]]);
// Increase after decrease
[g set:0.75 forFieldValues:@[ @"user" ]];
XCTAssertEqual(0.75, [g getGaugeValueForFieldValues:@[ @"user" ]]);
}
- (void)testExportNSDictionary {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricDoubleGauge *g = [metricSet doubleGaugeWithName:@"/proc/cpu_usage"
fieldNames:@[ @"mode" ]
helpText:@"CPU time consumed by this process."];
XCTAssertNotNil(g, @"Expected returned SNTMetricDoubleGauge to not be nil");
// set from zero
[g set:(double)0.45 forFieldValues:@[ @"user" ]];
[g set:(double)0.90 forFieldValues:@[ @"system" ]];
NSDictionary *expected = @{
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeDouble],
@"description" : @"CPU time consumed by this process.",
@"fields" : @{
@"mode" : @[
@{
@"value" : @"user",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithDouble:0.45]
},
@{
@"value" : @"system",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithDouble:0.90]
}
]
}
};
XCTAssertEqualObjects([g export], expected);
}
- (void)testAddingMetricWithSameSchema {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricDoubleGauge *a = [metricSet doubleGaugeWithName:@"/santa/doublegauge"
fieldNames:@[]
helpText:@"Test gauge."];
SNTMetricDoubleGauge *b = [metricSet doubleGaugeWithName:@"/santa/doublegauge"
fieldNames:@[]
helpText:@"Test gauge."];
XCTAssertEqual(a, b, @"Unexpected new gauge returned.");
}
@end
@implementation SNTMetricStringGaugeTest
- (void)testSimpleGauge {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricStringGauge *s = [metricSet stringGaugeWithName:@"/santa/mode"
fieldNames:@[]
helpText:@"String description of the mode."];
XCTAssertNotNil(s);
[s set:@"testValue" forFieldValues:@[]];
XCTAssertEqualObjects([s getStringValueForFieldValues:@[]], @"testValue");
}
- (void)testExportNSDictionary {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricStringGauge *s = [metricSet stringGaugeWithName:@"/santa/mode"
fieldNames:@[]
helpText:@"String description of the mode."];
XCTAssertNotNil(s);
[s set:@"testValue" forFieldValues:@[]];
NSDictionary *expected = @{
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeString],
@"description" : @"String description of the mode.",
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : @"testValue"
} ]
}
};
XCTAssertEqualObjects([s export], expected);
}
- (void)testAddingMetricWithSameSchema {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricStringGauge *a = [metricSet stringGaugeWithName:@"/santa/stringgauge"
fieldNames:@[]
helpText:@"Test gauge."];
SNTMetricStringGauge *b = [metricSet stringGaugeWithName:@"/santa/stringgauge"
fieldNames:@[]
helpText:@"Test gauge."];
XCTAssertEqual(a, b, @"Unexpected new gauge returned.");
}
@end
@implementation SNTMetricSetTest
- (void)testRootLabels {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
[metricSet addRootLabel:@"hostname" value:@"localhost"];
NSDictionary *expected = @{@"root_labels" : @{@"hostname" : @"localhost"}, @"metrics" : @{}};
XCTAssertEqualObjects(expected, [metricSet export]);
// ensure that adding a rootLabel with the same name overwrites.
expected = @{@"root_labels" : @{@"hostname" : @"localhost2"}, @"metrics" : @{}};
[metricSet addRootLabel:@"hostname" value:@"localhost2"];
XCTAssertEqualObjects(expected, [metricSet export],
@"failed to overwrite rootLabel with second call to addRootLabel");
// ensure that removing a rootLabelWorks
expected = @{@"root_labels" : @{}, @"metrics" : @{}};
[metricSet removeRootLabel:@"hostname"];
}
- (void)testDoubleRegisteringIncompatibleMetricsFails {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricCounter *c = [metricSet counterWithName:@"/foo/bar"
fieldNames:@[ @"field" ]
helpText:@"lorem ipsum"];
XCTAssertNotNil(c);
XCTAssertThrows([metricSet counterWithName:@"/foo/bar"
fieldNames:@[ @"incompatible" ]
helpText:@"A little help text"],
@"Should raise error for incompatible field names");
XCTAssertThrows([metricSet counterWithName:@"/foo/bar"
fieldNames:@[ @"result" ]
helpText:@"INCOMPATIBLE"],
@"Should raise error for incompatible help text");
}
- (void)testRegisterCallback {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
// Register a callback metric which increments by one before export
SNTMetricInt64Gauge *gauge = [metricSet int64GaugeWithName:@"/foo/bar"
fieldNames:@[]
helpText:@"Number of callbacks done"];
__block int count = 0;
[metricSet registerCallback:^(void) {
count++;
[gauge set:count forFieldValues:@[]];
}];
// ensure the callback is called.
[metricSet export];
XCTAssertEqual([gauge getGaugeValueForFieldValues:@[]], 1);
}
- (void)testAddConstantBool {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
[metricSet addConstantBooleanWithName:@"/tautology"
helpText:@"The first rule of tautology club is the first rule"
value:YES];
NSDictionary *expected = @{
@"/tautology" : @{
@"description" : @"The first rule of tautology club is the first rule",
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantBool],
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithBool:true]
} ]
}
}
};
XCTAssertEqualObjects([metricSet export][@"metrics"], expected);
}
- (void)testAddConstantString {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
[metricSet addConstantStringWithName:@"/build/label"
helpText:@"Build label for the binary"
value:@"20210806.0.1"];
NSDictionary *expected = @{
@"/build/label" : @{
@"description" : @"Build label for the binary",
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantString],
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : @"20210806.0.1"
} ]
}
}
};
XCTAssertEqualObjects([metricSet export][@"metrics"], expected);
}
- (void)testAddConstantInt {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
[metricSet addConstantIntegerWithName:@"/deep/thought/answer"
helpText:@"Life, the universe, and everything"
value:42];
NSDictionary *expected = @{
@"/deep/thought/answer" : @{
@"description" : @"Life, the universe, and everything",
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantInt64],
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithLongLong:42]
} ]
}
}
};
XCTAssertEqualObjects([metricSet export][@"metrics"], expected);
}
- (void)testExportNSDictionary {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] initWithHostname:@"testHost"
username:@"testUser"];
// Add constants
[metricSet addConstantStringWithName:@"/build/label"
helpText:@"Software version running."
value:@"20210809.0.1"];
[metricSet addConstantBooleanWithName:@"/santa/using_endpoint_security_framework"
helpText:@"Is santad using the endpoint security framework."
value:TRUE];
[metricSet
addConstantIntegerWithName:@"/proc/birth_timestamp"
helpText:@"Start time of this santad instance, in microseconds since epoch"
value:(long long)(0x12345668910)];
// Add Metrics
SNTMetricCounter *c = [metricSet counterWithName:@"/santa/events"
fieldNames:@[ @"rule_type" ]
helpText:@"Count of events on the host"];
[c incrementForFieldValues:@[ @"binary" ]];
[c incrementBy:2 forFieldValues:@[ @"certificate" ]];
SNTMetricInt64Gauge *g = [metricSet int64GaugeWithName:@"/santa/rules"
fieldNames:@[ @"rule_type" ]
helpText:@"Number of rules."];
[g set:1 forFieldValues:@[ @"binary" ]];
[g set:3 forFieldValues:@[ @"certificate" ]];
// Add Metrics with callback
SNTMetricInt64Gauge *virtualMemoryGauge =
[metricSet int64GaugeWithName:@"/proc/memory/virtual_size"
fieldNames:@[]
helpText:@"The virtual memory size of this process."];
SNTMetricInt64Gauge *residentMemoryGauge =
[metricSet int64GaugeWithName:@"/proc/memory/resident_size"
fieldNames:@[]
helpText:@"The resident set size of this process."];
[metricSet registerCallback:^(void) {
[virtualMemoryGauge set:987654321 forFieldValues:@[]];
[residentMemoryGauge set:123456789 forFieldValues:@[]];
}];
NSDictionary *expected = @{
@"root_labels" : @{@"hostname" : @"testHost", @"username" : @"testUser"},
@"metrics" : @{
@"/build/label" : @{
@"description" : @"Software version running.",
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantString],
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : @"20210809.0.1"
} ]
}
},
@"/santa/events" : @{
@"description" : @"Count of events on the host",
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeCounter],
@"fields" : @{
@"rule_type" : @[
@{
@"value" : @"binary",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithInt:1],
},
@{
@"value" : @"certificate",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithInt:2],
},
],
},
},
@"/santa/rules" : @{
@"description" : @"Number of rules.",
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64],
@"fields" : @{
@"rule_type" : @[
@{
@"value" : @"binary",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithInt:1],
},
@{
@"value" : @"certificate",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithInt:3],
}
]
},
},
@"/santa/using_endpoint_security_framework" : @{
@"description" : @"Is santad using the endpoint security framework.",
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantBool],
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithBool:YES]
} ]
}
},
@"/proc/birth_timestamp" : @{
@"description" : @"Start time of this santad instance, in microseconds since epoch",
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantInt64],
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithLong:1250999830800]
} ]
},
},
@"/proc/memory/virtual_size" : @{
@"description" : @"The virtual memory size of this process.",
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64],
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithInt:987654321]
} ]
}
},
@"/proc/memory/resident_size" : @{
@"description" : @"The resident set size of this process.",
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64],
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : [NSDate date],
@"last_updated" : [NSDate date],
@"data" : [NSNumber numberWithInt:123456789]
} ]
},
},
}
};
XCTAssertEqualObjects([metricSet export], expected);
}
@end
@implementation SNTMetricSetHelperFunctionsTest
- (void)testMakeMetricString {
NSArray<NSDictionary *> *tests = @[
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeUnknown],
@"expected" : @"SNTMetricTypeUnknown 0"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeConstantBool],
@"expected" : @"SNTMetricTypeConstantBool"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeConstantString],
@"expected" : @"SNTMetricTypeConstantString"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeConstantInt64],
@"expected" : @"SNTMetricTypeConstantInt64"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeConstantDouble],
@"expected" : @"SNTMetricTypeConstantDouble"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeGaugeBool],
@"expected" : @"SNTMetricTypeGaugeBool"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeGaugeString],
@"expected" : @"SNTMetricTypeGaugeString"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeGaugeInt64],
@"expected" : @"SNTMetricTypeGaugeInt64"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeGaugeDouble],
@"expected" : @"SNTMetricTypeGaugeDouble"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeCounter],
@"expected" : @"SNTMetricTypeCounter"
}
];
for (NSDictionary *test in tests) {
NSString *output = SNTMetricMakeStringFromMetricType([test[@"input"] integerValue]);
XCTAssertEqualObjects(test[@"expected"], output, @"expected %@ got %@", test[@"expected"],
output);
}
}
@end

View File

@@ -0,0 +1,227 @@
/// 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"
#include <string.h>
#include <mutex>
#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()
SNTPrefixTree::SNTPrefixTree(uint32_t max_nodes) {
root_ = new SantaPrefixNode();
node_count_ = 0;
max_nodes_ = max_nodes;
pthread_rwlock_init(&spt_lock_, nullptr);
spt_add_lock_ = new std::mutex;
}
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 (size_t 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 = (uint8_t)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 = (uint8_t)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_);
pthread_rwlock_destroy(&spt_lock_);
}

View File

@@ -0,0 +1,91 @@
/// 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>
// Support for unit testing.
#include <pthread.h>
#include <stdint.h>
#include <mutex>
///
/// 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_;
pthread_rwlock_t spt_lock_;
std::mutex *spt_add_lock_;
};
#endif /* SANTA__SANTA_DRIVER__SANTAPREFIXTREE_H */

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.
#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];
}
__block BOOL stop = NO;
// Create a bunch of background noise.
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (uint64_t i = 0; i < UINT64_MAX; ++i) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
t->HasPrefix([UUIDs[i % count] UTF8String]);
});
if (stop) return;
}
});
// Fill up the tree.
dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t i) {
XCTAssertEqual(t->AddPrefix([UUIDs[i] UTF8String]), kIOReturnSuccess);
});
// Make sure every leaf byte is found.
dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t i) {
XCTAssertTrue(t->HasPrefix([UUIDs[i] UTF8String]));
});
stop = YES;
}
@end

View File

@@ -12,39 +12,66 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
#import <Foundation/Foundation.h>
#import "Source/common/SNTCommonEnums.h"
///
/// Represents a Rule.
///
@interface SNTRule : NSObject<NSSecureCoding>
@interface SNTRule : NSObject <NSSecureCoding>
///
/// The hash of the object this rule is for
///
@property(copy) NSString *shasum;
@property(copy) NSString *identifier;
///
/// The state of this rule
///
@property santa_rulestate_t state;
@property SNTRuleState state;
///
/// The type of object this rule is for (binary, certificate)
///
@property santa_ruletype_t type;
@property SNTRuleType type;
///
/// A custom message that will be displayed if this rule blocks a binary from executing
///
@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:(santa_rulestate_t)state
type:(santa_ruletype_t)type
- (instancetype)initWithIdentifier:(NSString *)identifier
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)initWithIdentifier:(NSString *)identifier
state:(SNTRuleState)state
type:(SNTRuleType)type
customMsg:(NSString *)customMsg;
///
/// Initialize with a dictionary received from a sync server.
///
- (instancetype)initWithDictionary:(NSDictionary *)dict;
///
/// Sets timestamp of rule to the current time.
///
- (void)resetTimestamp;
@end

View File

@@ -12,27 +12,103 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTRule.h"
#import "Source/common/SNTRule.h"
#import "Source/common/SNTSyncConstants.h"
@interface SNTRule ()
@property(readwrite) NSUInteger timestamp;
@end
@implementation SNTRule
- (instancetype)initWithShasum:(NSString *)shasum
state:(santa_rulestate_t)state
type:(santa_ruletype_t)type
customMsg:(NSString *)customMsg {
- (instancetype)initWithIdentifier:(NSString *)identifier
state:(SNTRuleState)state
type:(SNTRuleType)type
customMsg:(NSString *)customMsg
timestamp:(NSUInteger)timestamp {
self = [super init];
if (self) {
_shasum = shasum;
_identifier = identifier;
_state = state;
_type = type;
_customMsg = customMsg;
_timestamp = timestamp;
}
return self;
}
- (instancetype)initWithIdentifier:(NSString *)identifier
state:(SNTRuleState)state
type:(SNTRuleType)type
customMsg:(NSString *)customMsg {
self = [self initWithIdentifier:identifier 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;
}
// Converts rule information downloaded from the server into a SNTRule. Because any information
// not recorded by SNTRule is thrown away here, this method is also responsible for dealing with
// the extra bundle rule information (bundle_hash & rule_count).
- (instancetype)initWithDictionary:(NSDictionary *)dict {
if (![dict isKindOfClass:[NSDictionary class]]) return nil;
self = [super init];
if (self) {
_identifier = dict[kRuleIdentifier];
if (![_identifier isKindOfClass:[NSString class]] || !_identifier.length) {
_identifier = dict[kRuleSHA256];
}
if (![_identifier isKindOfClass:[NSString class]] || !_identifier.length) return nil;
NSString *policyString = dict[kRulePolicy];
if (![policyString isKindOfClass:[NSString class]]) return nil;
if ([policyString isEqual:kRulePolicyAllowlist] ||
[policyString isEqual:kRulePolicyAllowlistDeprecated]) {
_state = SNTRuleStateAllow;
} else if ([policyString isEqual:kRulePolicyAllowlistCompiler] ||
[policyString isEqual:kRulePolicyAllowlistCompilerDeprecated]) {
_state = SNTRuleStateAllowCompiler;
} else if ([policyString isEqual:kRulePolicyBlocklist] ||
[policyString isEqual:kRulePolicyBlocklistDeprecated]) {
_state = SNTRuleStateBlock;
} else if ([policyString isEqual:kRulePolicySilentBlocklist] ||
[policyString isEqual:kRulePolicySilentBlocklistDeprecated]) {
_state = SNTRuleStateSilentBlock;
} else if ([policyString isEqual:kRulePolicyRemove]) {
_state = SNTRuleStateRemove;
} else {
return nil;
}
NSString *ruleTypeString = dict[kRuleType];
if (![ruleTypeString isKindOfClass:[NSString class]]) return nil;
if ([ruleTypeString isEqual:kRuleTypeBinary]) {
_type = SNTRuleTypeBinary;
} else if ([ruleTypeString isEqual:kRuleTypeCertificate]) {
_type = SNTRuleTypeCertificate;
} else if ([ruleTypeString isEqual:kRuleTypeTeamID]) {
_type = SNTRuleTypeTeamID;
} else {
return nil;
}
NSString *customMsg = dict[kRuleCustomMsg];
if ([customMsg isKindOfClass:[NSString class]] && customMsg.length) {
_customMsg = customMsg;
}
}
return self;
}
#pragma mark NSSecureCoding
#define ENCODE(obj, key) if (obj) [coder encodeObject:obj forKey:key]
#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]
+ (BOOL)supportsSecureCoding {
@@ -40,45 +116,55 @@
}
- (void)encodeWithCoder:(NSCoder *)coder {
ENCODE(self.shasum, @"shasum");
ENCODE(self.identifier, @"identifier");
ENCODE(@(self.state), @"state");
ENCODE(@(self.type), @"type");
ENCODE(self.customMsg, @"custommsg");
ENCODE(@(self.timestamp), @"timestamp");
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self) {
_shasum = DECODE(NSString, @"shasum");
_identifier = DECODE(NSString, @"identifier");
_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;
if (![other isKindOfClass:[SNTRule class]]) return NO;
SNTRule *o = other;
return ([self.shasum isEqual:o.shasum] && self.state == o.state && self.type == o.type);
return ([self.identifier isEqual:o.identifier] && self.state == o.state && self.type == o.type);
}
- (NSUInteger)hash {
NSUInteger prime = 31;
NSUInteger result = 1;
result = prime * result + [self.shasum hash];
result = prime * result + [self.identifier hash];
result = prime * result + self.state;
result = prime * result + self.type;
return result;
}
- (NSString *)description {
return [NSString stringWithFormat:@"SNTRule: SHA-256: %@, State: %d, Type: %d",
self.shasum, self.state, self.type];
return [NSString
stringWithFormat:@"SNTRule: Identifier: %@, State: %ld, Type: %ld, Timestamp: %lu",
self.identifier, self.state, self.type, (unsigned long)self.timestamp];
}
#pragma mark Last-access Timestamp
- (void)resetTimestamp {
self.timestamp = (NSUInteger)[[NSDate date] timeIntervalSinceReferenceDate];
}
@end

116
Source/common/SNTRuleTest.m Normal file
View File

@@ -0,0 +1,116 @@
/// Copyright 2022 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import <XCTest/XCTest.h>
#import "Source/common/SNTRule.h"
@interface SNTRuleTest : XCTestCase
@end
@implementation SNTRuleTest
- (void)testInitWithDictionaryValid {
SNTRule *sut;
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"some-sort-of-identifier",
@"policy" : @"ALLOWLIST",
@"rule_type" : @"BINARY",
}];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
XCTAssertEqual(sut.type, SNTRuleTypeBinary);
XCTAssertEqual(sut.state, SNTRuleStateAllow);
sut = [[SNTRule alloc] initWithDictionary:@{
@"sha256" : @"some-sort-of-identifier",
@"policy" : @"BLOCKLIST",
@"rule_type" : @"CERTIFICATE",
}];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
XCTAssertEqual(sut.type, SNTRuleTypeCertificate);
XCTAssertEqual(sut.state, SNTRuleStateBlock);
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"some-sort-of-identifier",
@"policy" : @"SILENT_BLOCKLIST",
@"rule_type" : @"TEAMID",
}];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
XCTAssertEqual(sut.type, SNTRuleTypeTeamID);
XCTAssertEqual(sut.state, SNTRuleStateSilentBlock);
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"some-sort-of-identifier",
@"policy" : @"ALLOWLIST_COMPILER",
@"rule_type" : @"BINARY",
}];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
XCTAssertEqual(sut.type, SNTRuleTypeBinary);
XCTAssertEqual(sut.state, SNTRuleStateAllowCompiler);
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"some-sort-of-identifier",
@"policy" : @"REMOVE",
@"rule_type" : @"TEAMID",
}];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
XCTAssertEqual(sut.type, SNTRuleTypeTeamID);
XCTAssertEqual(sut.state, SNTRuleStateRemove);
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"some-sort-of-identifier",
@"policy" : @"ALLOWLIST",
@"rule_type" : @"TEAMID",
@"custom_msg" : @"A custom block message",
}];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
XCTAssertEqual(sut.type, SNTRuleTypeTeamID);
XCTAssertEqual(sut.state, SNTRuleStateAllow);
XCTAssertEqualObjects(sut.customMsg, @"A custom block message");
}
- (void)testInitWithDictionaryInvalid {
SNTRule *sut;
sut = [[SNTRule alloc] initWithDictionary:@{}];
XCTAssertNil(sut);
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"an-identifier",
}];
XCTAssertNil(sut);
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"an-identifier",
@"policy" : @"OTHERPOLICY",
@"rule_type" : @"BINARY",
}];
XCTAssertNil(sut);
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"an-identifier",
@"policy" : @"ALLOWLIST",
@"rule_type" : @"OTHER_RULE_TYPE",
}];
XCTAssertNil(sut);
}
@end

View File

@@ -12,15 +12,17 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
#import <Foundation/Foundation.h>
#import "Source/common/SNTCommonEnums.h"
///
/// Represents an event stored in the database.
///
@interface SNTStoredEvent : NSObject<NSSecureCoding>
@interface SNTStoredEvent : NSObject <NSSecureCoding>
///
/// An index for this event, empty unless the event came from the database.
/// An index for this event, randomly generated during initialization.
///
@property NSNumber *idx;
@@ -35,10 +37,43 @@
@property NSString *filePath;
///
/// If the executed file was part of the bundle, this is the CFBundleName.
/// Set to YES if the event is a part of a bundle. When an event is passed to SantaGUI this propery
/// will be used as an indicator to to kick off bundle hashing as necessary. Default value is NO.
///
@property BOOL needsBundleHash;
///
/// If the executed file was part of a bundle, this is the calculated hash of all the nested
/// executables within the bundle.
///
@property NSString *fileBundleHash;
///
/// If the executed file was part of a bundle, this is the time in ms it took to hash the bundle.
///
@property NSNumber *fileBundleHashMilliseconds;
///
/// If the executed file was part of a bundle, this is the total count of related mach-o binaries.
///
@property NSNumber *fileBundleBinaryCount;
///
/// If the executed file was part of the bundle, this is the CFBundleDisplayName, if it exists
/// or the CFBundleName if not.
///
@property NSString *fileBundleName;
///
/// If the executed file was part of the bundle, this is the path to the bundle.
///
@property NSString *fileBundlePath;
///
/// The relative path to the bundle's main executable.
///
@property NSString *fileBundleExecutableRelPath;
///
/// If the executed file was part of the bundle, this is the CFBundleID.
///
@@ -60,6 +95,12 @@
///
@property NSArray *signingChain;
///
/// If the executed file was signed, this is the Team ID if present in the signature information.
///
@property NSString *teamID;
///
/// The user who executed the binary.
///
@@ -73,7 +114,7 @@
///
/// The decision santad returned.
///
@property santa_eventstate_t decision;
@property SNTEventState decision;
///
/// NSArray of logged in users when the decision was made.
@@ -108,5 +149,4 @@
@property NSDate *quarantineTimestamp;
@property NSString *quarantineAgentBundleID;
@end

View File

@@ -12,17 +12,21 @@
/// 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
#define ENCODE(obj, key) if (obj) [coder encodeObject:obj forKey:key]
#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) \
[decoder decodeObjectOfClasses:[NSSet setWithObjects:[NSArray class], [cls class], nil] \
forKey:key]
#define DECODEARRAY(cls, key) \
[decoder decodeObjectOfClasses:[NSSet setWithObjects:[NSArray class], [cls class], nil] \
forKey:key]
+ (BOOL)supportsSecureCoding {
return YES;
@@ -33,12 +37,19 @@
ENCODE(self.fileSHA256, @"fileSHA256");
ENCODE(self.filePath, @"filePath");
ENCODE(@(self.needsBundleHash), @"needsBundleHash");
ENCODE(self.fileBundleHash, @"fileBundleHash");
ENCODE(self.fileBundleHashMilliseconds, @"fileBundleHashMilliseconds");
ENCODE(self.fileBundleBinaryCount, @"fileBundleBinaryCount");
ENCODE(self.fileBundleName, @"fileBundleName");
ENCODE(self.fileBundlePath, @"fileBundlePath");
ENCODE(self.fileBundleExecutableRelPath, @"fileBundleExecutableRelPath");
ENCODE(self.fileBundleID, @"fileBundleID");
ENCODE(self.fileBundleVersion, @"fileBundleVersion");
ENCODE(self.fileBundleVersionString, @"fileBundleVersionString");
ENCODE(self.signingChain, @"signingChain");
ENCODE(self.teamID, @"teamID");
ENCODE(self.executingUser, @"executingUser");
ENCODE(self.occurrenceDate, @"occurrenceDate");
@@ -52,8 +63,16 @@
ENCODE(self.quarantineDataURL, @"quarantineDataURL");
ENCODE(self.quarantineRefererURL, @"quarantineRefererURL");
ENCODE(self.quarantineTimestamp, @"quarantineTiemstamp");
ENCODE(self.quarantineAgentBundleID, @"quarantineAgentBundleID");
ENCODE(self.quarantineTimestamp, @"quarantineTimestamp");
ENCODE(self.quarantineAgentBundleID, @"quarantineAgentBundleID");
}
- (instancetype)init {
self = [super init];
if (self) {
_idx = @(arc4random());
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
@@ -63,16 +82,23 @@
_fileSHA256 = DECODE(NSString, @"fileSHA256");
_filePath = DECODE(NSString, @"filePath");
_needsBundleHash = [DECODE(NSNumber, @"needsBundleHash") boolValue];
_fileBundleHash = DECODE(NSString, @"fileBundleHash");
_fileBundleHashMilliseconds = DECODE(NSNumber, @"fileBundleHashMilliseconds");
_fileBundleBinaryCount = DECODE(NSNumber, @"fileBundleBinaryCount");
_fileBundleName = DECODE(NSString, @"fileBundleName");
_fileBundlePath = DECODE(NSString, @"fileBundlePath");
_fileBundleExecutableRelPath = DECODE(NSString, @"fileBundleExecutableRelPath");
_fileBundleID = DECODE(NSString, @"fileBundleID");
_fileBundleVersion = DECODE(NSString, @"fileBundleVersion");
_fileBundleVersionString = DECODE(NSString, @"fileBundleVersionString");
_signingChain = DECODEARRAY(MOLCertificate, @"signingChain");
_teamID = DECODE(NSString, @"teamID");
_executingUser = DECODE(NSString, @"executingUser");
_occurrenceDate = DECODE(NSDate, @"occurrenceDate");
_decision = (santa_eventstate_t)[DECODE(NSNumber, @"decision") intValue];
_decision = (SNTEventState)[DECODE(NSNumber, @"decision") intValue];
_pid = DECODE(NSNumber, @"pid");
_ppid = DECODE(NSNumber, @"ppid");
_parentName = DECODE(NSString, @"parentName");
@@ -106,7 +132,9 @@
- (NSString *)description {
return
[NSString stringWithFormat:@"SNTStoredEvent[%@] with SHA-256: %@", self.idx, self.fileSHA256];
[NSString stringWithFormat:@"SNTStoredEvent[%@] with SHA-256: %@", self.idx, self.fileSHA256];
}
#pragma clang diagnostic pop
@end

View File

@@ -0,0 +1,21 @@
/// 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.
#define STRONGIFY(var) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
__strong __typeof(var) var = (Weak_##var); \
_Pragma("clang diagnostic pop")
#define WEAKIFY(var) __weak __typeof(var) Weak_##var = (var);

View File

@@ -0,0 +1,141 @@
/// 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>
extern NSString *const kXSRFToken;
extern NSString *const kSerialNumber;
extern NSString *const kHostname;
extern NSString *const kSantaVer;
extern NSString *const kOSVer;
extern NSString *const kOSBuild;
extern NSString *const kModelIdentifier;
extern NSString *const kPrimaryUser;
extern NSString *const kRequestCleanSync;
extern NSString *const kBatchSize;
extern NSString *const kUploadLogsURL;
extern NSString *const kClientMode;
extern NSString *const kClientModeMonitor;
extern NSString *const kClientModeLockdown;
extern NSString *const kBlockUSBMount;
extern NSString *const kRemountUSBMode;
extern NSString *const kCleanSync;
extern NSString *const kAllowedPathRegex;
extern NSString *const kAllowedPathRegexDeprecated;
extern NSString *const kBlockedPathRegex;
extern NSString *const kBlockedPathRegexDeprecated;
extern NSString *const kBinaryRuleCount;
extern NSString *const kCertificateRuleCount;
extern NSString *const kCompilerRuleCount;
extern NSString *const kTransitiveRuleCount;
extern NSString *const kTeamIDRuleCount;
extern NSString *const kFullSyncInterval;
extern NSString *const kFCMToken;
extern NSString *const kFCMFullSyncInterval;
extern NSString *const kFCMGlobalRuleSyncDeadline;
extern NSString *const kEnableBundles;
extern NSString *const kEnableBundlesDeprecated;
extern NSString *const kEnableTransitiveRules;
extern NSString *const kEnableTransitiveRulesDeprecated;
extern NSString *const kEnableTransitiveRulesSuperDeprecated;
extern NSString *const kEnableAllEventUpload;
extern NSString *const kDisableUnknownEventUpload;
extern NSString *const kEvents;
extern NSString *const kFileSHA256;
extern NSString *const kFilePath;
extern NSString *const kFileName;
extern NSString *const kExecutingUser;
extern NSString *const kExecutionTime;
extern NSString *const kDecision;
extern NSString *const kDecisionAllowUnknown;
extern NSString *const kDecisionAllowBinary;
extern NSString *const kDecisionAllowCertificate;
extern NSString *const kDecisionAllowScope;
extern NSString *const kDecisionAllowTeamID;
extern NSString *const kDecisionBlockUnknown;
extern NSString *const kDecisionBlockBinary;
extern NSString *const kDecisionBlockCertificate;
extern NSString *const kDecisionBlockScope;
extern NSString *const kDecisionBlockTeamID;
extern NSString *const kDecisionUnknown;
extern NSString *const kDecisionBundleBinary;
extern NSString *const kLoggedInUsers;
extern NSString *const kCurrentSessions;
extern NSString *const kFileBundleID;
extern NSString *const kFileBundlePath;
extern NSString *const kFileBundleExecutableRelPath;
extern NSString *const kFileBundleName;
extern NSString *const kFileBundleVersion;
extern NSString *const kFileBundleShortVersionString;
extern NSString *const kFileBundleHash;
extern NSString *const kFileBundleHashMilliseconds;
extern NSString *const kFileBundleBinaryCount;
extern NSString *const kPID;
extern NSString *const kPPID;
extern NSString *const kParentName;
extern NSString *const kSigningChain;
extern NSString *const kCertSHA256;
extern NSString *const kCertCN;
extern NSString *const kCertOrg;
extern NSString *const kCertOU;
extern NSString *const kCertValidFrom;
extern NSString *const kCertValidUntil;
extern NSString *const kTeamID;
extern NSString *const kQuarantineDataURL;
extern NSString *const kQuarantineRefererURL;
extern NSString *const kQuarantineTimestamp;
extern NSString *const kQuarantineAgentBundleID;
extern NSString *const kEventUploadBundleBinaries;
extern NSString *const kRules;
extern NSString *const kRuleSHA256;
extern NSString *const kRuleIdentifier;
extern NSString *const kRulePolicy;
extern NSString *const kRulePolicyAllowlist;
extern NSString *const kRulePolicyAllowlistDeprecated;
extern NSString *const kRulePolicyAllowlistCompiler;
extern NSString *const kRulePolicyAllowlistCompilerDeprecated;
extern NSString *const kRulePolicyBlocklist;
extern NSString *const kRulePolicyBlocklistDeprecated;
extern NSString *const kRulePolicySilentBlocklist;
extern NSString *const kRulePolicySilentBlocklistDeprecated;
extern NSString *const kRulePolicyRemove;
extern NSString *const kRuleType;
extern NSString *const kRuleTypeBinary;
extern NSString *const kRuleTypeCertificate;
extern NSString *const kRuleTypeTeamID;
extern NSString *const kRuleCustomMsg;
extern NSString *const kCursor;
extern NSString *const kBackoffInterval;
extern NSString *const kFullSync;
extern NSString *const kRuleSync;
extern NSString *const kConfigSync;
extern NSString *const kLogSync;
extern const NSUInteger kDefaultEventBatchSize;
///
/// kDefaultFullSyncInterval
/// kDefaultFCMFullSyncInterval
/// kDefaultFCMGlobalRuleSyncDeadline
///
/// Are represented in seconds
///
extern const NSUInteger kDefaultFullSyncInterval;
extern const NSUInteger kDefaultPushNotificationsFullSyncInterval;
extern const NSUInteger kDefaultPushNotificationsGlobalRuleSyncDeadline;

View File

@@ -0,0 +1,134 @@
/// 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/SNTSyncConstants.h"
NSString *const kXSRFToken = @"X-XSRF-TOKEN";
NSString *const kSerialNumber = @"serial_num";
NSString *const kHostname = @"hostname";
NSString *const kSantaVer = @"santa_version";
NSString *const kOSVer = @"os_version";
NSString *const kOSBuild = @"os_build";
NSString *const kModelIdentifier = @"model_identifier";
NSString *const kPrimaryUser = @"primary_user";
NSString *const kRequestCleanSync = @"request_clean_sync";
NSString *const kBatchSize = @"batch_size";
NSString *const kUploadLogsURL = @"upload_logs_url";
NSString *const kClientMode = @"client_mode";
NSString *const kBlockUSBMount = @"block_usb_mount";
NSString *const kRemountUSBMode = @"remount_usb_mode";
NSString *const kClientModeMonitor = @"MONITOR";
NSString *const kClientModeLockdown = @"LOCKDOWN";
NSString *const kCleanSync = @"clean_sync";
NSString *const kAllowedPathRegex = @"allowed_path_regex";
NSString *const kAllowedPathRegexDeprecated = @"whitelist_regex";
NSString *const kBlockedPathRegex = @"blocked_path_regex";
NSString *const kBlockedPathRegexDeprecated = @"blacklist_regex";
NSString *const kBinaryRuleCount = @"binary_rule_count";
NSString *const kCertificateRuleCount = @"certificate_rule_count";
NSString *const kCompilerRuleCount = @"compiler_rule_count";
NSString *const kTransitiveRuleCount = @"transitive_rule_count";
NSString *const kTeamIDRuleCount = @"teamid_rule_count";
NSString *const kFullSyncInterval = @"full_sync_interval";
NSString *const kFCMToken = @"fcm_token";
NSString *const kFCMFullSyncInterval = @"fcm_full_sync_interval";
NSString *const kFCMGlobalRuleSyncDeadline = @"fcm_global_rule_sync_deadline";
NSString *const kEnableBundles = @"enable_bundles";
NSString *const kEnableBundlesDeprecated = @"bundles_enabled";
NSString *const kEnableTransitiveRules = @"enable_transitive_rules";
NSString *const kEnableTransitiveRulesDeprecated = @"enabled_transitive_whitelisting";
NSString *const kEnableTransitiveRulesSuperDeprecated = @"transitive_whitelisting_enabled";
NSString *const kEnableAllEventUpload = @"enable_all_event_upload";
NSString *const kDisableUnknownEventUpload = @"disable_unknown_event_upload";
NSString *const kEvents = @"events";
NSString *const kFileSHA256 = @"file_sha256";
NSString *const kFilePath = @"file_path";
NSString *const kFileName = @"file_name";
NSString *const kExecutingUser = @"executing_user";
NSString *const kExecutionTime = @"execution_time";
NSString *const kDecision = @"decision";
NSString *const kDecisionAllowUnknown = @"ALLOW_UNKNOWN";
NSString *const kDecisionAllowBinary = @"ALLOW_BINARY";
NSString *const kDecisionAllowCertificate = @"ALLOW_CERTIFICATE";
NSString *const kDecisionAllowScope = @"ALLOW_SCOPE";
NSString *const kDecisionAllowTeamID = @"ALLOW_TEAMID";
NSString *const kDecisionBlockUnknown = @"BLOCK_UNKNOWN";
NSString *const kDecisionBlockBinary = @"BLOCK_BINARY";
NSString *const kDecisionBlockCertificate = @"BLOCK_CERTIFICATE";
NSString *const kDecisionBlockScope = @"BLOCK_SCOPE";
NSString *const kDecisionBlockTeamID = @"BLOCK_TEAMID";
NSString *const kDecisionUnknown = @"UNKNOWN";
NSString *const kDecisionBundleBinary = @"BUNDLE_BINARY";
NSString *const kLoggedInUsers = @"logged_in_users";
NSString *const kCurrentSessions = @"current_sessions";
NSString *const kFileBundleID = @"file_bundle_id";
NSString *const kFileBundlePath = @"file_bundle_path";
NSString *const kFileBundleExecutableRelPath = @"file_bundle_executable_rel_path";
NSString *const kFileBundleName = @"file_bundle_name";
NSString *const kFileBundleVersion = @"file_bundle_version";
NSString *const kFileBundleShortVersionString = @"file_bundle_version_string";
NSString *const kFileBundleHash = @"file_bundle_hash";
NSString *const kFileBundleHashMilliseconds = @"file_bundle_hash_millis";
NSString *const kFileBundleBinaryCount = @"file_bundle_binary_count";
NSString *const kPID = @"pid";
NSString *const kPPID = @"ppid";
NSString *const kParentName = @"parent_name";
NSString *const kSigningChain = @"signing_chain";
NSString *const kCertSHA256 = @"sha256";
NSString *const kCertCN = @"cn";
NSString *const kCertOrg = @"org";
NSString *const kCertOU = @"ou";
NSString *const kCertValidFrom = @"valid_from";
NSString *const kCertValidUntil = @"valid_until";
NSString *const kTeamID = @"team_id";
NSString *const kQuarantineDataURL = @"quarantine_data_url";
NSString *const kQuarantineRefererURL = @"quarantine_referer_url";
NSString *const kQuarantineTimestamp = @"quarantine_timestamp";
NSString *const kQuarantineAgentBundleID = @"quarantine_agent_bundle_id";
NSString *const kEventUploadBundleBinaries = @"event_upload_bundle_binaries";
NSString *const kRules = @"rules";
NSString *const kRuleSHA256 = @"sha256";
NSString *const kRuleIdentifier = @"identifier";
NSString *const kRulePolicy = @"policy";
NSString *const kRulePolicyAllowlist = @"ALLOWLIST";
NSString *const kRulePolicyAllowlistDeprecated = @"WHITELIST";
NSString *const kRulePolicyAllowlistCompiler = @"ALLOWLIST_COMPILER";
NSString *const kRulePolicyAllowlistCompilerDeprecated = @"WHITELIST_COMPILER";
NSString *const kRulePolicyBlocklist = @"BLOCKLIST";
NSString *const kRulePolicyBlocklistDeprecated = @"BLACKLIST";
NSString *const kRulePolicySilentBlocklist = @"SILENT_BLOCKLIST";
NSString *const kRulePolicySilentBlocklistDeprecated = @"SILENT_BLACKLIST";
NSString *const kRulePolicyRemove = @"REMOVE";
NSString *const kRuleType = @"rule_type";
NSString *const kRuleTypeBinary = @"BINARY";
NSString *const kRuleTypeCertificate = @"CERTIFICATE";
NSString *const kRuleTypeTeamID = @"TEAMID";
NSString *const kRuleCustomMsg = @"custom_msg";
NSString *const kCursor = @"cursor";
NSString *const kBackoffInterval = @"backoff";
NSString *const kFullSync = @"full_sync";
NSString *const kRuleSync = @"rule_sync";
NSString *const kConfigSync = @"config_sync";
NSString *const kLogSync = @"log_sync";
const NSUInteger kDefaultEventBatchSize = 50;
const NSUInteger kDefaultFullSyncInterval = 600;
const NSUInteger kDefaultPushNotificationsFullSyncInterval = 14400;
const NSUInteger kDefaultPushNotificationsGlobalRuleSyncDeadline = 600;

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