Compare commits

...

97 Commits

Author SHA1 Message Date
Matt W
72969a3c92 Fix crash flushing cache on unmount events (#895) 2022-09-27 21:54:35 -04:00
Matt W
d2dbed78dd Return a value from the test block (#894) 2022-09-27 15:07:20 -04:00
Matt W
8fa91e4ff0 Build deps (#893)
* Too bad we can't require explicit build deps...

* More deps
2022-09-23 13:55:48 -04:00
Matt W
551763146d Linter and BUILD deps fixups (#892)
* Minor changes to address lint issues

* Add more BUILD deps

* Include cleanup

* Even more BUILD deps

* Still more BUILD deps
2022-09-23 11:18:58 -04:00
Matt W
7a7f0cd5a8 Ingestion fixups (#891) 2022-09-22 12:30:34 -04:00
Matt W
fcb49701b3 ES and Logging Interfaces Redesign (#888)
* Initial structure for ES wrappers, enriched types, logging

* Basic working ES and logging functionality

* Add in oneTBB and thread-safe-lru deps

* Added a bunch of enriched types

* Auto-mute self when establishing ES client

* Basic auth, tamper client. Syslog of all events. Basic compiler tracking.

* Update copyright header blobs, convert some tabs to spaces

* Auth result cache. Fix getting translocation path.

* Added remaining cache methods

* Add AuthResultCache to Recorder client. Cache now operates on es_file_t.

* Hooked up SNTPrefixTree

* Fix CompilerController for RENAME. Fix AllowList logging missing path.

* Block loading Santa kext

* Added device manager client

* Properly log DiskAppear events

* Fix build to adopt new adhoc build

* Handle clearing cache on UNMOUNT events

* Ignore other ES clients if configured

* Remove SNTAllowlistInfo. Rename AllowList to Allowlist. Minor cleanup.

* Recorder now logs asynchronously. Enricher now returns shared_ptrs.

* Added File writer. Added timestamps to BasicStream serializer.

* Skip calling stat in SNTFileInfo when path given by ES.

* Fix build issue

* Address draft PR feedback

* santactl integrated, XPC works, fix file writer bug

* Integrate syncservice. Start observing some config changes.

* Add metrics service wrapper

* Add metrics config observers and metrics interval reset.

* Start better dependency control. Add Null logger support.

* Added more deps

* Added more deps

* Fix issue where metric service wasn't starting

* Add missing variant include

* Fix missing parent proc name

* Added googletest and new unit test macro

* Started expanding AuthResultCacheTest

* Properly mock EndpointSecurityAPI

* Finished AuthResultCacheTest

* bazelrc now builds all C++ as C++17. Added LoggerTest.

* Add FileTest. Abstract some File constants to Logger.

* Added Empty serializer test

* Started work on BasicStringTest. Fixed some BasicString serialization bugs.

* Added Unlink BasicString serialization test

* Added some more tests. Commonized some test code

* Finished BasicStringTest. Converted to XCTest.

* Standardize esapi variable naming

* Bubble up gTest expect failures to XCTest failures

* AuthResultCacheTest now uses XCTest. Added common TestUtils.h

* EmptyTest now uses XCTest.

* FileTest now uses XCTest

* LoggerTest now uses XCTest. Removed santa_unit_gtest bazel macro.

* Added ClientTest

* Add basic Enricher tests

* Add MessageTest. Make more TestUtils.

* Rename metrics to Metrics

* Add MetricsTest.

* Apply template pattern to Serializer

* Add SNTDecisionCacheTest.

* Add SNTCachedDecisionTest.

* Testing with coveralls debug mode

* Allow manual CI runs

* Remove unused property

* Started work on SNTEndpointSecurityClientTest.

* WIP SNTEndpointSecurityClientTest, fix test run issue

* Added more base ES client tests

* Add more base ES client tests

* Base ES client tests done. Added serializer utils/tests. Expanded basic string tests.

* Add utils test to test suite

* Add copy ctor. Add test output to bazel coverage.

* Single thread bazel coverage

* Updaload coverage file

* Updaload coverage file

* Old gen cov test

* Restructure message handlers to enable better testability

* Added enable tests for all ES clients

* Made a single MockEndpointSecurityAPI class to share everywhere

* Added most of SNTCompilerControllerTest

* Cleanup SNTCompilerControllerTest

* Started expanding Auth client test

* Finished up the Authorizer tests

* Move to using enum class for notify/auth instead of bool

* WIP for tamper resistance test. ASAN issues.

* Add OCMock patch to fix test issue on ARM Macs

* Changed patches directory name to external_patches

* Update WORKSPACE path

* Finished up Tamper Resistance tests

* Finished up Recorder tests.

* Move SNTExecutionControllerTest to ObjC++

* Initial work to port SNTExecutionControllerTest

* Finished porting SNTExecutionControllerTest.

* Added SNTExecutionControllerTest to list of unit tests

* Ported SNTEndpointSecurityDeviceManager.

* Test cleanup, use MockESAPI expectation helpers

* Verify SNTEndpointSecurityDeviceManager expectations differently

* Test cleanup, omit gTest param list where unused

* Log message cleanup

* Rename SNTApplicationTest to santad_test.mm

* Finished porting santad_test, formerly SNTApplicationTest

* Fix SNTEndpointSecurityDeviceManager issues

* Pulled in missed fixes. Updated tests.

* Renamed lowercase filenames to match rest of codebase

* Fix non-static dispatch_once_t, and noisy watching compiler log message

* WIP Started process of removing components no longer used

* WIP Continued process of removing components no longer used

* BUILD file cleanup. Proto warning. Removed unused global

* Rename SNTEventProvider to SNTEndpointSecurityEventHandler

* Rename SNTEndpointSecurityEventHandler protocol

* Remove EnableSysxCache option. Remove --quick flag used during dev.

* Ran testing/fix.sh

* Addmissing param to fix.sh that was omitting .mm files.

* clang-format

* Fix linter: find cmd missing .mm ext, git grep exclude patch files.

* Use MakeESProcess default params in tests

* Move variables to camelCase in objc classes

* More case changes

* Sanitize strings

* Change dispatch queue priorities and standardize daemon queue naming

* Exclude patch files in markdown check

* Ensure string log messages end with newline

* Fix BasicStringTest

* Disable clang-format in code producing different results in local/remote versions

* Moved to using date ranges in copyright notices as per current guidelines

* Update Source/common/SNTConfigurator.h

Suggestion adding whitespace in comment to fix clang-format mangling

Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>

* Removed santa_panic macro used in one place

* Updated comment about ES cachability

* Pin oneTBB to specific commit

* Address outstanding WORKSPACE 'canonical reproducible form' messages

* Use string append instead of ostringstream due to benchmark results

* Remove use of freind classes in EnrichedTypes.h

* Added SNTKVOManager, removed observers from SNTConfigurator.

* Fixed SNTEndpointSecurityRecorderTest class name

* Reduce usage of the auto keyword

* Each SNTKVOManager instance now adds its own observer

* Replaced more auto keywords with real types.

* Remove leftover code coverage debugging from ci.yml

* Updated comment

* Memoize SNTFileInfo sha256. Reduce some cache sizes.

* Fix issue checking for translocated paths

* Use more performant NSURL creation method

* Fix lint issue

* Address PR feedback

* Use an array literal for kvo objects

* Fix some clang tidy and import issues

* Replace third party LRU cache with SantaCache for now

* Fix clang tidy issues

* Address PR feedback

* Fix comment typo

Co-authored-by: Pete Markowsky <pmarkowsky@users.noreply.github.com>

* Added todo for when we adopt macOS 13

Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>
Co-authored-by: Pete Markowsky <pmarkowsky@users.noreply.github.com>
2022-09-22 10:18:41 -04:00
Russell Hancox
c9ef723fc5 Project: Update bazel and apple-rules (#887) 2022-08-29 17:52:27 -04:00
Pete Markowsky
dc6732ef04 Refactor the SNTApplicationTest unit tests to function correctly (#885)
* Refactor the SNTApplicationTest unit tests to function correctly.

The tests were originally written in a table style and were impacted by the lack of mocking the configurator. This caused issues with static rules to impact the unit tests.

Additionally added improved logging messages for critical binaries and a todo for macOS 13 unit tests.

Added goodbinary and rules.db test files to allstar's ignored paths.
2022-08-29 13:18:04 -04:00
Russell Hancox
a48900a4ae Allstar: Pre-emptively check-in binary_artifacts.yaml to exclude test binaries (#884) 2022-08-25 09:32:43 -04:00
Russell Hancox
bb49118d94 README: Try again, this time replacing the correct bit (#883) 2022-08-24 16:26:30 -04:00
Russell Hancox
456333d6d2 README: Fix logo link, remove coverage badge (#882) 2022-08-24 16:22:37 -04:00
Pete Markowsky
fd23a5c3b7 Fix up endTimestamp to be Monarch compliant (#879)
Fix up endTimestamp field to be Monarch compliant.
2022-08-16 22:32:29 -04:00
Russell Hancox
ec203e8796 Project: Rename Source/santa -> Source/gui (#877) 2022-08-12 14:19:01 -04:00
Russell Hancox
57ff69208d GUI: Missed a required dependency (#876) 2022-08-12 14:02:22 -04:00
Russell Hancox
f00b7d2ded GUI: Expose SNTNotificationManager.h for the test. (#875) 2022-08-12 13:46:25 -04:00
Russell Hancox
9791fdd53c Project: Add a GH action to prevent trailing whitespace (#873) 2022-08-12 12:46:11 -04:00
Russell Hancox
26e2203f1e GUI: Improve signing chain key reporting in distributed notifications. (#874)
Also add a group for GUI unit_tests and include in the overall project tests group.
2022-08-12 11:03:21 -04:00
Russell Hancox
4a47195d12 Santa: Post distributed notification when showing block UI (#870)
Fixes #869
2022-08-11 12:34:35 -04:00
Russell Hancox
4436e221df GUI: Add silent mode configuration option. (#871)
When enabled, this option disables *all* GUI notifications from Santa. This is intended for kiosk-style machines where it is not expected for users to _ever_ execute unknown binaries.

Fixes #862
2022-08-11 09:17:07 -04:00
Russell Hancox
deccc8a148 GUI: For App Store published apps, include team ID. (#872)
With this change, the publisher field for an App Store published app will be  instead of

Fixes #758
2022-08-11 08:15:42 -04:00
Henry S
06da796a4d Docs: add link to GitHub (#868) 2022-08-08 16:38:34 -04:00
Russell Hancox
7b99a76d0d Docs: Add StaticRules to example mobileconfig (#866) 2022-08-03 10:59:18 -04:00
Pete Markowsky
c2d3e99446 Sync Protocol Docs (#860)
Initial commit of sync protocol docs.
2022-07-28 17:27:43 -04:00
Russell Hancox
6db7fea8ae syncservice: Add tests for NSData+Zlib and Postflight (#864) 2022-07-26 13:05:35 -04:00
Kathryn Hancox
6fcb4cfe63 Docs: Add recommended rollout doc (#861) 2022-07-22 13:50:25 -04:00
bfreezy
8b55ee4da5 santad: only allow root read+write permissions on sync-state.plist (#858) 2022-07-18 13:32:08 -04:00
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
286 changed files with 13631 additions and 7766 deletions

View File

@@ -0,0 +1,18 @@
# Ignore reason: These crafted binaries are used in tests
ignorePaths:
- Source/common/testdata/bad_pagezero
- Source/common/testdata/missing_pagezero
- Source/common/testdata/missing_pagezero
- Source/common/testdata/missing_pagezero
- Source/common/testdata/32bitplist
- Source/common/testdata/BundleExample.app/Contents/MacOS/BundleExample
- Source/common/testdata/DirectoryBundle/Contents/MacOS/DirectoryBundle
- Source/common/testdata/DirectoryBundle/Contents/Resources/BundleExample.app/Contents/MacOS/BundleExample
- Source/santad/testdata/binaryrules/badbinary
- Source/santad/testdata/binaryrules/goodbinary
- Source/santad/testdata/binaryrules/badcert
- Source/santad/testdata/binaryrules/banned_teamid_allowed_binary
- Source/santad/testdata/binaryrules/banned_teamid
- Source/santad/testdata/binaryrules/goodcert
- Source/santad/testdata/binaryrules/noop
- Source/santad/testdata/binaryrules/rules.db

View File

@@ -3,3 +3,14 @@ build --apple_generate_dsym --define=apple.propagate_embedded_extra_outputs=yes
build --copt=-Werror
build --copt=-Wall
build --copt=-Wno-error=deprecated-declarations
build --per_file_copt=.*\.mm\$@-std=c++17
build --cxxopt=-std=c++17
build:asan --strip=never
build:asan --copt="-Wno-macro-redefined"
build:asan --copt="-D_FORTIFY_SOURCE=0"
build:asan --copt="-O1"
build:asan --copt="-fno-omit-frame-pointer"
build:asan --copt="-fsanitize=address"
build:asan --copt="-DADDRESS_SANITIZER"
build:asan --linkopt="-fsanitize=address"

1
.bazelversion Normal file
View File

@@ -0,0 +1 @@
5.3.0

View File

@@ -1,13 +1,14 @@
name: Check Markdown links
name: Check Markdown
on:
on:
pull_request:
paths:
- "**.md"
jobs:
markdown-link-check:
markdown-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: gaurav-nelson/github-action-markdown-link-check@v1
- run: "! git grep -EIn $'[ \t]+$' -- ':(exclude)*.patch'"

View File

@@ -1,49 +1,20 @@
name: CI
on:
push:
branches:
- '*'
paths:
- 'Source/**'
pull_request:
branches:
- main
paths:
- 'Source/**'
jobs:
preqs:
runs-on: ubuntu-latest
outputs:
run_build_and_tests: ${{ steps.step1.outputs.run_build_and_tests }}
steps:
- uses: actions/checkout@v2
- name: Check If We Need to Run Build/Test
id: step1
run: |
git remote add mainline https://github.com/google/santa.git
git fetch mainline main
git diff --name-only mainline/main HEAD > files.txt
echo "FILES CHANGED: $(wc -l ./files.txt)\n"
cat files.txt
build_and_run_tests=0
for file in `cat files.txt`; do
if [[ $file = Source/* ]]; then
build_and_run_test=1;
fi
done
if [[ $build_and_run_test != 0 ]]; then
echo "NEED TO RUN BUILD AND TESTS"
echo "::set-output name=run_build_and_tests::true"
else
echo "::set-output name=run_build_and_tests::false"
fi
lint:
runs-on: ubuntu-latest
needs: [preqs]
if: needs.preqs.outputs.run_build_and_tests == 'true'
steps:
- uses: actions/checkout@v2
- name: Run linters
@@ -53,32 +24,26 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [macos-10.15, macos-11]
os: [macos-10.15, macos-11, macos-12]
runs-on: ${{ matrix.os }}
needs: [preqs]
if: needs.preqs.outputs.run_build_and_tests == 'true'
steps:
- uses: actions/checkout@v2
- name: Build Userspace
run: bazel build --apple_generate_dsym -c opt :release --define=SANTA_BUILD_TYPE=ci
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]
os: [macos-10.15, macos-11, macos-12]
runs-on: ${{ matrix.os }}
needs: [preqs]
if: needs.preqs.outputs.run_build_and_tests == 'true'
steps:
- uses: actions/checkout@v2
- name: Run All Tests
run: bazel test :unit_tests --define=SANTA_BUILD_TYPE=ci
run: bazel test :unit_tests --define=SANTA_BUILD_TYPE=adhoc --test_output=errors
test_coverage:
runs-on: macos-11
needs: [preqs]
if: needs.preqs.outputs.run_build_and_tests == 'true'
steps:
- uses: actions/checkout@v2
- name: Generate test coverage
@@ -87,14 +52,5 @@ jobs:
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: ./CoverageData/info.lcov
path-to-lcov: ./bazel-out/_coverage/_coverage_report.dat
flag-name: Unit
benchmark:
runs-on: macos-11
needs: [preqs]
if: needs.preqs.outputs.run_build_and_tests == 'true'
steps:
- uses: actions/checkout@v2
- name: Run All Tests
run: ./Testing/benchmark.sh

View File

@@ -10,4 +10,4 @@ jobs:
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=ci
run: bazel test --test_strategy=exclusive --test_output=errors --runs_per_test 50 -t- :unit_tests --define=SANTA_BUILD_TYPE=adhoc

40
BUILD
View File

@@ -1,6 +1,5 @@
load("@build_bazel_rules_apple//apple:versioning.bzl", "apple_bundle_version")
load("//:helper.bzl", "run_command")
load("//:version.bzl", "SANTA_VERSION")
package(default_visibility = ["//:santa_package_group"])
@@ -11,13 +10,14 @@ exports_files(["LICENSE"])
# The version label for mac_* rules.
apple_bundle_version(
name = "version",
build_label_pattern = "{build}",
build_version = SANTA_VERSION + ".{build}",
build_label_pattern = ".*santa_{release}\\.{build}",
build_version = "{release}.{build}",
capture_groups = {
"release": "\\d{4}\\.\\d+",
"build": "\\d+",
},
fallback_build_label = "1",
short_version_string = SANTA_VERSION,
fallback_build_label = "santa_9999.1.1",
short_version_string = "{release}",
)
# Used to detect release builds
@@ -27,10 +27,11 @@ config_setting(
visibility = [":santa_package_group"],
)
# Used to detect CI builds
# Adhoc signed - provisioning profiles are not used.
# Used for CI runs and dev builds when SIP is disabled.
config_setting(
name = "ci_build",
values = {"define": "SANTA_BUILD_TYPE=ci"},
name = "adhoc_build",
values = {"define": "SANTA_BUILD_TYPE=adhoc"},
visibility = [":santa_package_group"],
)
@@ -54,6 +55,7 @@ run_command(
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
""",
)
@@ -64,6 +66,7 @@ run_command(
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
""",
)
@@ -71,14 +74,14 @@ launchctl load /Library/LaunchAgents/com.google.santa.plist
run_command(
name = "reload",
srcs = [
"//Source/santa:Santa",
"//Source/gui: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
$${BUILD_WORKSPACE_DIRECTORY}/bazel-out/*$(COMPILATION_MODE)*/bin/Source/gui/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
@@ -93,11 +96,12 @@ echo "Time to stop being naughty"
genrule(
name = "release",
srcs = [
"//Source/santa:Santa",
"//Source/gui: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",
@@ -107,7 +111,7 @@ genrule(
"Conf/Package/postinstall",
"Conf/Package/preinstall",
],
outs = ["santa-" + SANTA_VERSION + ".tar.gz"],
outs = ["santa-release.tar.gz"],
cmd = select({
"//conditions:default": """
echo "ERROR: Trying to create a release tarball without optimization."
@@ -149,6 +153,10 @@ genrule(
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
@@ -183,16 +191,10 @@ test_suite(
name = "unit_tests",
tests = [
"//Source/common:unit_tests",
"//Source/gui: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

@@ -48,6 +48,7 @@ readonly INPUT_SYSX="${INPUT_APP}/Contents/Library/SystemExtensions/com.google.s
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)"
@@ -64,7 +65,7 @@ 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_SYSX}" "${INPUT_APP}"; do
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"
@@ -113,11 +114,11 @@ echo "verifying signatures"
"${RELEASE_ROOT}/binaries/"* || die "bad signature"
echo "creating fresh release tarball"
/bin/mkdir -p "${RELEASE_ROOT}/${RELEASE_NAME}"
/bin/cp -r "${RELEASE_ROOT}/binaries" "${RELEASE_ROOT}/${RELEASE_NAME}"
/bin/cp -r "${RELEASE_ROOT}/conf" "${RELEASE_ROOT}/${RELEASE_NAME}"
/bin/cp -r "${RELEASE_ROOT}/dsym" "${RELEASE_ROOT}/${RELEASE_NAME}"
/usr/bin/tar -C "${RELEASE_ROOT}" -czvf "${TAR_PATH}" "${RELEASE_NAME}" || die "failed to create 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" \
@@ -130,6 +131,7 @@ echo "creating app pkg"
/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}/"

View File

@@ -26,6 +26,9 @@ mkdir -p /usr/local/bin
# Load com.google.santa.metricservice
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santa.metricservice.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

View File

@@ -9,6 +9,7 @@
/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
/bin/sleep 1

View File

@@ -15,8 +15,8 @@
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
<false/>
<key>KeepAlive</key>
<true/>
<false/>
</dict>
</plist>

View File

@@ -27,7 +27,10 @@ fi
# Unload metric service
/bin/launchctl remove com.google.santa.metricservice >/dev/null 2>&1
# Unload kext.
# 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
@@ -59,6 +62,7 @@ GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
/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/
@@ -74,6 +78,9 @@ GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
# 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

View File

@@ -13,6 +13,7 @@
# 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)
@@ -28,6 +29,7 @@ user=$(/usr/bin/stat -f '%u' /dev/console)
/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

View File

@@ -1,13 +1,13 @@
# 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 [![CI](https://github.com/google/santa/actions/workflows/ci.yml/badge.svg)](https://github.com/google/santa/actions/workflows/ci.yml)
<p align="center">
<img src="./Source/santa/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
<img src="https://raw.githubusercontent.com/google/santa/main/Source/gui/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
</p>
Santa is a binary authorization system for macOS. It consists of a system
extension that monitors for executions, a daemon that makes execution decisions
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
case of a block decision and a command-line utility for managing the system and
synchronizing the database with a server.
It is named Santa because it keeps track of binaries that are naughty or nice.
@@ -16,7 +16,7 @@ It is named Santa because it keeps track of binaries that are naughty or nice.
The Santa docs are stored in the
[Docs](https://github.com/google/santa/blob/main/docs) directory and published
at http://santa.dev.
at https://santa.dev.
The docs include deployment options, details on how parts of Santa work and
instructions for developing Santa itself.

View File

@@ -1,7 +1,7 @@
# 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
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.

View File

@@ -3,13 +3,12 @@ load("@rules_proto_grpc//objc:defs.bzl", "objc_proto_library")
package(
default_visibility = ["//:santa_package_group"],
features = ["layering_check"],
)
licenses(["notice"])
proto_library(
name = "log_proto",
name = "santa_proto",
srcs = ["santa.proto"],
deps = [
"@com_google_protobuf//:any_proto",
@@ -18,16 +17,15 @@ proto_library(
)
objc_proto_library(
name = "log_objc_proto",
name = "santa_objc_proto",
copts = ["-fno-objc-arc"],
non_arc_srcs = ["Santa.pbobjc.m"],
protos = [":log_proto"],
protos = [":santa_proto"],
)
cc_library(
name = "SantaCache",
hdrs = ["SantaCache.h"],
features = ["layering_check"],
deps = ["//Source/common:SNTCommon"],
)
@@ -48,15 +46,7 @@ objc_library(
":SNTConfigurator",
":SNTLogging",
":SNTStoredEvent",
],
)
objc_library(
name = "SNTDeviceEvent",
srcs = ["SNTDeviceEvent.m"],
hdrs = ["SNTDeviceEvent.h"],
deps = [
":SNTCommonEnums",
":SNTSystemInfo",
],
)
@@ -70,6 +60,7 @@ objc_library(
":SNTDeviceEvent",
":SNTLogging",
":SNTStoredEvent",
":SNTSystemInfo",
],
)
@@ -84,9 +75,12 @@ objc_library(
)
objc_library(
name = "SNTAllowlistInfo",
srcs = ["SNTAllowlistInfo.m"],
hdrs = ["SNTAllowlistInfo.h"],
name = "SNTDeviceEvent",
srcs = ["SNTDeviceEvent.m"],
hdrs = ["SNTDeviceEvent.h"],
deps = [
":SNTCommonEnums",
],
)
objc_library(
@@ -100,11 +94,29 @@ objc_library(
hdrs = ["SNTConfigurator.h"],
deps = [
":SNTCommonEnums",
":SNTRule",
":SNTStrengthify",
":SNTSystemInfo",
],
)
objc_library(
name = "SNTKVOManager",
srcs = ["SNTKVOManager.mm"],
hdrs = ["SNTKVOManager.h"],
deps = [
":SNTLogging",
],
)
santa_unit_test(
name = "SNTKVOManagerTest",
srcs = ["SNTKVOManagerTest.mm"],
deps = [
":SNTKVOManager",
],
)
objc_library(
name = "SNTDropRootPrivs",
srcs = ["SNTDropRootPrivs.m"],
@@ -116,6 +128,7 @@ objc_library(
srcs = ["SNTFileInfo.m"],
hdrs = ["SNTFileInfo.h"],
deps = [
":SNTLogging",
"@FMDB",
"@MOLCodesignChecker",
],
@@ -128,7 +141,6 @@ cc_library(
"TARGET_OS_OSX",
"TARGET_OS_MAC",
],
features = ["layering_check"],
)
objc_library(
@@ -143,7 +155,6 @@ cc_library(
srcs = ["SNTPrefixTree.cc"],
hdrs = ["SNTPrefixTree.h"],
copts = ["-std=c++11"],
features = ["layering_check"],
deps = [":SNTLogging"],
)
@@ -151,7 +162,16 @@ objc_library(
name = "SNTRule",
srcs = ["SNTRule.m"],
hdrs = ["SNTRule.h"],
deps = [":SNTCommonEnums"],
deps = [
":SNTCommonEnums",
":SNTSyncConstants",
],
)
santa_unit_test(
name = "SNTRuleTest",
srcs = ["SNTRuleTest.m"],
deps = [":SNTRule"],
)
objc_library(
@@ -167,7 +187,12 @@ objc_library(
cc_library(
name = "SNTStrengthify",
hdrs = ["SNTStrengthify.h"],
features = ["layering_check"],
)
objc_library(
name = "SNTSyncConstants",
srcs = ["SNTSyncConstants.m"],
hdrs = ["SNTSyncConstants.h"],
)
objc_library(
@@ -200,10 +225,17 @@ 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",
],
)
@@ -225,21 +257,12 @@ objc_library(
deps = [":SNTCommonEnums"],
)
objc_library(
name = "SNTXPCSyncdInterface",
srcs = ["SNTXPCSyncdInterface.m"],
hdrs = ["SNTXPCSyncdInterface.h"],
deps = [
":SNTCommonEnums",
":SNTStoredEvent",
],
)
objc_library(
name = "SNTXPCSyncServiceInterface",
srcs = ["SNTXPCSyncServiceInterface.m"],
hdrs = ["SNTXPCSyncServiceInterface.h"],
deps = [
":SNTCommonEnums",
":SNTStoredEvent",
"@MOLXPCConnection",
],
@@ -287,13 +310,40 @@ santa_unit_test(
deps = [":SNTMetricSet"],
)
santa_unit_test(
name = "SNTCachedDecisionTest",
srcs = ["SNTCachedDecisionTest.mm"],
deps = [
"//Source/common:SNTCachedDecision",
"//Source/common:TestUtils",
"@OCMock",
],
)
test_suite(
name = "unit_tests",
tests = [
":SNTCachedDecisionTest",
":SNTFileInfoTest",
":SNTKVOManagerTest",
":SNTMetricSetTest",
":SNTPrefixTreeTest",
":SNTRuleTest",
":SantaCacheTest",
],
visibility = ["//:santa_package_group"],
)
objc_library(
name = "TestUtils",
testonly = 1,
srcs = ["TestUtils.mm"],
hdrs = ["TestUtils.h"],
sdk_dylibs = [
"bsm",
],
deps = [
"@OCMock",
"@com_google_googletest//:gtest",
],
)

View File

@@ -1,32 +0,0 @@
/// 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

@@ -119,9 +119,16 @@
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%"

View File

@@ -1,4 +1,4 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2015-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.
@@ -12,10 +12,11 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import <EndpointSecurity/EndpointSecurity.h>
#import <Foundation/Foundation.h>
#import "Source/common/SNTCommonEnums.h"
#import "Source/common/SNTCommon.h"
#import "Source/common/SNTCommonEnums.h"
@class MOLCertificate;
@@ -24,6 +25,8 @@
///
@interface SNTCachedDecision : NSObject
- (instancetype)initWithEndpointSecurityFile:(const es_file_t *)esFile;
@property santa_vnode_id_t vnodeId;
@property SNTEventState decision;
@property NSString *decisionExtra;

View File

@@ -1,4 +1,4 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2015-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.
@@ -15,4 +15,14 @@
#import "Source/common/SNTCachedDecision.h"
@implementation SNTCachedDecision
- (instancetype)initWithEndpointSecurityFile:(const es_file_t *)esFile {
self = [super init];
if (self) {
_vnodeId.fsid = (uint64_t)esFile->stat.st_dev;
_vnodeId.fileid = esFile->stat.st_ino;
}
return self;
}
@end

View File

@@ -0,0 +1,36 @@
/// 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/SNTCachedDecision.h"
#include "Source/common/TestUtils.h"
@interface SNTCachedDecisionTest : XCTestCase
@end
@implementation SNTCachedDecisionTest
- (void)testSNTCachedDecisionInit {
// Ensure the vnodeId field is properly set from the es_file_t
struct stat sb = MakeStat(1234, 5678);
es_file_t file = MakeESFile("foo", sb);
SNTCachedDecision *cd = [[SNTCachedDecision alloc] initWithEndpointSecurityFile:&file];
XCTAssertEqual(sb.st_ino, cd.vnodeId.fileid);
XCTAssertEqual(sb.st_dev, cd.vnodeId.fsid);
}
@end

View File

@@ -1,4 +1,4 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2015-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.
@@ -26,44 +26,23 @@
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
// Enum defining actions that can be passed down the IODataQueue and in
// response methods.
typedef enum {
ACTION_UNSET = 0,
ACTION_UNSET,
// REQUESTS
ACTION_REQUEST_SHUTDOWN = 10,
ACTION_REQUEST_BINARY = 11,
// If an operation is awaiting a cache decision from a similar operation
// currently being processed, it will poll about every 5 ms for an answer.
ACTION_REQUEST_BINARY,
// 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,
ACTION_RESPOND_ALLOW,
ACTION_RESPOND_DENY,
ACTION_RESPOND_ALLOW_COMPILER,
} 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)
x == ACTION_RESPOND_ALLOW_COMPILER)
// Struct to manage vnode IDs
typedef struct santa_vnode_id_t {
@@ -74,38 +53,7 @@ typedef struct santa_vnode_id_t {
bool operator==(const santa_vnode_id_t &rhs) const {
return fsid == rhs.fsid && fileid == rhs.fileid;
}
// This _must not_ be used for anything security-sensitive. It exists solely
// to make the msleep/wakeup calls easier.
uint64_t unsafe_simple_id() const {
return (((uint64_t)fsid << 32) | fileid);
}
#endif
} santa_vnode_id_t;
// Message struct that is sent down the IODataQueue.
typedef struct {
santa_action_t action;
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];
// 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];
// For messages that originate from EndpointSecurity, this points to a copy of
// the message.
void *es_message;
// For messages that originate from EndpointSecurity, this points to an
// NSArray of the arguments.
void *args_array;
} santa_message_t;
#endif // SANTA__COMMON__COMMON_H

View File

@@ -1,4 +1,4 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2015-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.
@@ -57,6 +57,7 @@ typedef NS_ENUM(NSInteger, SNTEventState) {
SNTEventStateBlockCertificate = 1 << 18,
SNTEventStateBlockScope = 1 << 19,
SNTEventStateBlockTeamID = 1 << 20,
SNTEventStateBlockLongPath = 1 << 21,
// Bits 24-31 store allow decision types
SNTEventStateAllowUnknown = 1 << 24,
@@ -93,6 +94,22 @@ typedef NS_ENUM(NSInteger, SNTEventLogType) {
SNTEventLogTypeSyslog,
SNTEventLogTypeFilelog,
SNTEventLogTypeProtobuf,
SNTEventLogTypeNull,
};
// 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) {
@@ -104,5 +121,4 @@ typedef NS_ENUM(NSInteger, SNTMetricFormatType) {
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

@@ -1,4 +1,4 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2015-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.
@@ -16,6 +16,8 @@
#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.
@@ -46,6 +48,33 @@
///
@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.
///
@@ -151,9 +180,10 @@
///
/// Defines how event logs are stored. Options are:
/// SNTEventLogTypeSyslog: Sent to ASL or ULS (if built with the 10.12 SDK or later).
/// SNTEventLogTypeFilelog: Sent to a file on disk. Use eventLogPath to specify a path.
/// SNTEventLogTypeProtobuf: (BETA) Sent to a file on disk, using maildir format. Use
/// 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.
@@ -215,17 +245,18 @@
///
@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
///
/// When silent mode is enabled, Santa will never show notifications for
/// blocked processes.
///
/// This can be a very confusing experience for users, use with caution.
///
/// Defaults to NO.
///
@property(readonly, nonatomic) BOOL enableSilentMode;
///
/// The text to display when opening Santa.app.
/// If unset, the default text will be displayed.
@@ -411,6 +442,22 @@
///
@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.
///

View File

@@ -1,4 +1,4 @@
/// Copyright 2021 Google Inc. All rights reserved.
/// Copyright 2014-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.
@@ -16,6 +16,7 @@
#include <sys/stat.h>
#import "Source/common/SNTRule.h"
#import "Source/common/SNTStrengthify.h"
#import "Source/common/SNTSystemInfo.h"
@@ -28,11 +29,15 @@
@property(readonly, nonatomic) NSDictionary *forcedConfigKeyTypes;
/// Holds the configurations from a sync server and mobileconfig.
@property NSMutableDictionary *syncState;
@property NSDictionary *syncState;
@property NSMutableDictionary *configState;
/// Was --debug passed as an argument to this process?
@property(readonly, nonatomic) BOOL debugFlag;
/// Holds the last processed hash of the static rules list.
@property(atomic) NSDictionary *cachedStaticRules;
@end
@implementation SNTConfigurator
@@ -44,8 +49,10 @@ NSString *const kSyncStateFilePath = @"/var/db/santa/sync-state.plist";
static NSString *const kMobileConfigDomain = @"com.google.santa";
/// The keys managed by a mobileconfig.
static NSString *const kStaticRules = @"StaticRules";
static NSString *const kSyncBaseURLKey = @"SyncBaseURL";
static NSString *const kSyncProxyConfigKey = @"SyncProxyConfiguration";
static NSString *const kSyncEnableCleanSyncEventUpload = @"SyncEnableCleanSyncEventUpload";
static NSString *const kClientAuthCertificateFileKey = @"ClientAuthCertificateFile";
static NSString *const kClientAuthCertificatePasswordKey = @"ClientAuthCertificatePassword";
static NSString *const kClientAuthCertificateCNKey = @"ClientAuthCertificateCN";
@@ -60,7 +67,8 @@ static NSString *const kMachineOwnerPlistKeyKey = @"MachineOwnerKey";
static NSString *const kMachineIDPlistFileKey = @"MachineIDPlist";
static NSString *const kMachineIDPlistKeyKey = @"MachineIDKey";
static NSString *const kAboutText = @"AboutText";
static NSString *const kEnableSilentModeKey = @"EnableSilentMode";
static NSString *const kAboutTextKey = @"AboutText";
static NSString *const kMoreInfoURLKey = @"MoreInfoURL";
static NSString *const kEventDetailURLKey = @"EventDetailURL";
static NSString *const kEventDetailTextKey = @"EventDetailText";
@@ -87,8 +95,6 @@ static NSString *const kMailDirectoryEventMaxFlushTimeSec = @"MailDirectoryEvent
static NSString *const kEnableMachineIDDecoration = @"EnableMachineIDDecoration";
static NSString *const kEnableSysxCache = @"EnableSysxCache";
static NSString *const kEnableForkAndExitLogging = @"EnableForkAndExitLogging";
static NSString *const kIgnoreOtherEndpointSecurityClients = @"IgnoreOtherEndpointSecurityClients";
static NSString *const kEnableDebugLogging = @"EnableDebugLogging";
@@ -111,6 +117,8 @@ static NSString *const kAllowedPathRegexKey = @"AllowedPathRegex";
static NSString *const kAllowedPathRegexKeyDeprecated = @"WhitelistRegex";
static NSString *const kBlockedPathRegexKey = @"BlockedPathRegex";
static NSString *const kBlockedPathRegexKeyDeprecated = @"BlacklistRegex";
static NSString *const kEnableAllEventUploadKey = @"EnableAllEventUpload";
static NSString *const kDisableUnknownEventUploadKey = @"DisableUnknownEventUpload";
// TODO(markowsky): move these to sync server only.
static NSString *const kMetricFormat = @"MetricFormat";
@@ -146,7 +154,8 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kRemountUSBModeKey : array,
kFullSyncLastSuccess : date,
kRuleSyncLastSuccess : date,
kSyncCleanRequired : number
kSyncCleanRequired : number,
kEnableAllEventUploadKey : number,
};
_forcedConfigKeyTypes = @{
kClientModeKey : number,
@@ -163,7 +172,8 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kRemountUSBModeKey : array,
kEnablePageZeroProtectionKey : number,
kEnableBadSignatureProtectionKey : number,
kAboutText : string,
kEnableSilentModeKey : string,
kAboutTextKey : string,
kMoreInfoURLKey : string,
kEventDetailURLKey : string,
kEventDetailTextKey : string,
@@ -173,6 +183,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kRemountUSBBlockMessage : string,
kModeNotificationMonitor : string,
kModeNotificationLockdown : string,
kStaticRules : array,
kSyncBaseURLKey : string,
kSyncProxyConfigKey : dictionary,
kClientAuthCertificateFileKey : string,
@@ -194,7 +205,6 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kMailDirectorySizeThresholdMB : number,
kMailDirectoryEventMaxFlushTimeSec : number,
kEnableMachineIDDecoration : number,
kEnableSysxCache : number,
kEnableForkAndExitLogging : number,
kIgnoreOtherEndpointSecurityClients : number,
kEnableDebugLogging : number,
@@ -207,10 +217,13 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kMetricExportInterval : number,
kMetricExportTimeout : number,
kMetricExtraLabels : dictionary,
kEnableAllEventUploadKey : number,
kDisableUnknownEventUploadKey : number,
};
_defaults = [NSUserDefaults standardUserDefaults];
[_defaults addSuiteNamed:@"com.google.santa"];
_configState = [self readForcedConfig];
[self cacheStaticRules];
_syncState = [self readSyncStateFromDisk] ?: [NSMutableDictionary dictionary];
_debugFlag = [[NSProcessInfo processInfo].arguments containsObject:@"--debug"];
[self startWatchingDefaults];
@@ -278,6 +291,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingStaticRules {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingSyncBaseURL {
return [self configStateSet];
}
@@ -286,6 +303,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableSilentMode {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingAboutText {
return [self configStateSet];
}
@@ -394,8 +415,12 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableSysxCache {
return [self configStateSet];
+ (NSSet *)keyPathsForValuesAffectingEnableAllEventUpload {
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingDisableUnknownEventUpload {
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableForkAndExitLogging {
@@ -434,6 +459,26 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingBlockUSBMount {
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingBannedUSBBlockMessage {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingRemountUSBMode {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingRemountUSBBlockMessage {
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingUsbBlockMessage {
return [self syncAndConfigStateSet];
}
#pragma mark Public Interface
- (SNTClientMode)clientMode {
@@ -541,6 +586,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return args;
}
- (NSDictionary<NSString *, SNTRule *> *)staticRules {
return self.cachedStaticRules;
}
- (NSURL *)syncBaseURL {
NSString *urlString = self.configState[kSyncBaseURLKey];
if (![urlString hasSuffix:@"/"]) urlString = [urlString stringByAppendingString:@"/"];
@@ -562,8 +611,13 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return number ? [number boolValue] : NO;
}
- (BOOL)enableSilentMode {
NSNumber *number = self.configState[kEnableSilentModeKey];
return number ? [number boolValue] : NO;
}
- (NSString *)aboutText {
return self.configState[kAboutText];
return self.configState[kAboutTextKey];
}
- (NSURL *)moreInfoURL {
@@ -693,6 +747,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return SNTEventLogTypeProtobuf;
} else if ([logType isEqualToString:@"syslog"]) {
return SNTEventLogTypeSyslog;
} else if ([logType isEqualToString:@"null"]) {
return SNTEventLogTypeNull;
} else if ([logType isEqualToString:@"file"]) {
return SNTEventLogTypeFilelog;
} else {
return SNTEventLogTypeFilelog;
}
@@ -729,9 +787,31 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return number ? [number boolValue] : NO;
}
- (BOOL)enableSysxCache {
NSNumber *number = self.configState[kEnableSysxCache];
return number ? [number boolValue] : YES;
- (BOOL)enableCleanSyncEventUpload {
NSNumber *number = self.configState[kSyncEnableCleanSyncEventUpload];
return number ? [number boolValue] : NO;
}
- (BOOL)enableAllEventUpload {
NSNumber *n = self.syncState[kEnableAllEventUploadKey];
if (n) return [n boolValue];
return [self.configState[kEnableAllEventUploadKey] boolValue];
}
- (void)setEnableAllEventUpload:(BOOL)enabled {
[self updateSyncStateForKey:kEnableAllEventUploadKey value:@(enabled)];
}
- (BOOL)disableUnknownEventUpload {
NSNumber *n = self.syncState[kDisableUnknownEventUploadKey];
if (n) return [n boolValue];
return [self.configState[kDisableUnknownEventUploadKey] boolValue];
}
- (void)setDisableUnknownEventUpload:(BOOL)enabled {
[self updateSyncStateForKey:kDisableUnknownEventUploadKey value:@(enabled)];
}
- (BOOL)enableForkAndExitLogging {
@@ -879,7 +959,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
syncState[kAllowedPathRegexKey] = [syncState[kAllowedPathRegexKey] pattern];
syncState[kBlockedPathRegexKey] = [syncState[kBlockedPathRegexKey] pattern];
[syncState writeToFile:kSyncStateFilePath atomically:YES];
[[NSFileManager defaultManager] setAttributes:@{NSFilePosixPermissions : @0644}
[[NSFileManager defaultManager] setAttributes:@{NSFilePosixPermissions : @0600}
ofItemAtPath:kSyncStateFilePath
error:NULL];
}
@@ -936,6 +1016,24 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
///
- (void)handleChange {
self.configState = [self readForcedConfig];
[self cacheStaticRules];
}
///
/// Processes the StaticRules key to create SNTRule objects and caches them for quick use
///
- (void)cacheStaticRules {
NSArray *staticRules = self.configState[kStaticRules];
if (![staticRules isKindOfClass:[NSArray class]]) return;
NSMutableDictionary<NSString *, SNTRule *> *rules =
[NSMutableDictionary dictionaryWithCapacity:staticRules.count];
for (id rule in staticRules) {
if (![rule isKindOfClass:[NSDictionary class]]) return;
SNTRule *r = [[SNTRule alloc] initWithDictionary:rule];
rules[r.identifier] = r;
}
self.cachedStaticRules = [rules copy];
}
@end

View File

@@ -1,4 +1,4 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2015-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.
@@ -12,6 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import <EndpointSecurity/EndpointSecurity.h>
#import <Foundation/Foundation.h>
@class MOLCodesignChecker;
@@ -32,6 +33,14 @@
///
- (instancetype)initWithPath:(NSString *)path error:(NSError **)error;
///
/// Convenience initializer.
///
/// @param esFile Pointer to an es_file_t provided by the EndpointSecurity framework.
/// Assumes that the path is a resolved path.
///
- (instancetype)initWithEndpointSecurityFile:(const es_file_t *)esFile error:(NSError **)error;
///
/// Convenience initializer.
///

View File

@@ -1,4 +1,4 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2015-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.
@@ -25,6 +25,8 @@
#include <sys/stat.h>
#include <sys/xattr.h>
#import "Source/common/SNTLogging.h"
// Simple class to hold the data of a mach_header and the offset within the file
// in which that header was found.
@interface MachHeaderWithOffset : NSObject
@@ -48,6 +50,7 @@
@property NSFileHandle *fileHandle;
@property NSUInteger fileSize;
@property NSString *fileOwnerHomeDir;
@property NSString *sha256Storage;
// Cached properties
@property NSBundle *bundleRef;
@@ -63,6 +66,26 @@
extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
- (instancetype)initWithResolvedPath:(NSString *)path error:(NSError **)error {
struct stat fileStat;
if (path.length) {
lstat(path.UTF8String, &fileStat);
}
return [self initWithResolvedPath:path stat:&fileStat error:error];
}
- (instancetype)initWithEndpointSecurityFile:(const es_file_t *)esFile error:(NSError **)error {
return [self initWithResolvedPath:@(esFile->path.data) stat:&esFile->stat error:error];
}
- (instancetype)initWithResolvedPath:(NSString *)path
stat:(const struct stat *)fileStat
error:(NSError **)error {
if (!fileStat) {
// This is a programming error. Bail.
LOGE(@"NULL stat buffer unsupported");
exit(EXIT_FAILURE);
}
self = [super init];
if (self) {
_path = path;
@@ -76,9 +99,7 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
return nil;
}
struct stat fileStat;
lstat(_path.UTF8String, &fileStat);
if (!((S_IFMT & fileStat.st_mode) == S_IFREG)) {
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"
@@ -88,12 +109,12 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
return nil;
}
_fileSize = fileStat.st_size;
_fileSize = fileStat->st_size;
if (_fileSize == 0) return nil;
if (fileStat.st_uid != 0) {
struct passwd *pwd = getpwuid(fileStat.st_uid);
if (fileStat->st_uid != 0) {
struct passwd *pwd = getpwuid(fileStat->st_uid);
if (pwd) {
_fileOwnerHomeDir = @(pwd->pw_dir);
}
@@ -214,9 +235,13 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
}
- (NSString *)SHA256 {
NSString *sha256;
[self hashSHA1:NULL SHA256:&sha256];
return sha256;
// Memoize the value
if (!self.sha256Storage) {
NSString *sha256;
[self hashSHA1:NULL SHA256:&sha256];
self.sha256Storage = sha256;
}
return self.sha256Storage;
}
#pragma mark File Type Info

View File

@@ -0,0 +1,34 @@
/// Copyright 2022 Google LLC
///
/// 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
///
/// https://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>
// The callback type when KVO notifications are received for observed key paths.
// The first parameter is the previous value, the second paramter is the new value.
typedef void (^KVOCallback)(id oldValue, id newValue);
@interface SNTKVOManager : NSObject
// Add an observer for the selector on the given object. When a KVO notification
// is received, the callback is called. If the notification contains objects that
// are not of the expectedType, nil is passed as the argument to the callback.
// The observer is removed when the returned instance is deallocated.
- (instancetype)initWithObject:(id)object
selector:(SEL)selector
type:(Class)expectedType
callback:(KVOCallback)callback;
- (instancetype)init NS_UNAVAILABLE;
@end

View File

@@ -0,0 +1,72 @@
/// Copyright 2022 Google LLC
///
/// 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
///
/// https://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/SNTKVOManager.h"
#import "Source/common/SNTLogging.h"
@interface SNTKVOManager ()
@property KVOCallback callback;
@property Class expectedType;
@property NSString *keyPath;
@property id object;
@end
@implementation SNTKVOManager
- (instancetype)initWithObject:(id)object
selector:(SEL)selector
type:(Class)expectedType
callback:(KVOCallback)callback {
self = [super self];
if (self) {
NSString *selectorName = NSStringFromSelector(selector);
if (![object respondsToSelector:selector]) {
LOGE(@"Attempt to add observer for an unknown selector (%@) for object (%@)", selectorName,
[object class]);
return nil;
}
_object = object;
_keyPath = selectorName;
_expectedType = expectedType;
_callback = callback;
[object addObserver:self
forKeyPath:selectorName
options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
context:NULL];
}
return self;
}
- (void)dealloc {
[self.object removeObserver:self forKeyPath:self.keyPath context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSString *, id> *)change
context:(void *)context {
id oldValue = [change[NSKeyValueChangeOldKey] isKindOfClass:self.expectedType]
? change[NSKeyValueChangeOldKey]
: nil;
id newValue = [change[NSKeyValueChangeNewKey] isKindOfClass:self.expectedType]
? change[NSKeyValueChangeNewKey]
: nil;
self.callback(oldValue, newValue);
}
@end

View File

@@ -0,0 +1,129 @@
/// Copyright 2022 Google LLC
///
/// 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
///
/// https://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/SNTKVOManager.h"
@interface Foo : NSObject
@property NSNumber *propNumber;
@property NSArray *propArray;
@property id propId;
@end
@implementation Foo
@end
@interface SNTKVOManagerTest : XCTestCase
@end
@implementation SNTKVOManagerTest
- (void)testInvalidSelector {
Foo *foo = [[Foo alloc] init];
SNTKVOManager *kvo = [[SNTKVOManager alloc] initWithObject:foo
selector:NSSelectorFromString(@"doesNotExist")
type:[NSNumber class]
callback:^(id, id){
}];
XCTAssertNil(kvo);
}
- (void)testNormalOperation {
Foo *foo = [[Foo alloc] init];
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
int origVal = 123;
int update1 = 456;
int update2 = 789;
foo.propNumber = @(origVal);
// Store the values from the callback to test against expected values
__block int oldVal;
__block int newVal;
SNTKVOManager *kvo =
[[SNTKVOManager alloc] initWithObject:foo
selector:@selector(propNumber)
type:[NSNumber class]
callback:^(NSNumber *oldValue, NSNumber *newValue) {
oldVal = [oldValue intValue];
newVal = [newValue intValue];
dispatch_semaphore_signal(sema);
}];
XCTAssertNotNil(kvo);
// Ensure an update to the observed property triggers the callback
foo.propNumber = @(update1);
XCTAssertEqual(0,
dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC)),
"Failed waiting for first observable update");
XCTAssertEqual(oldVal, origVal);
XCTAssertEqual(newVal, update1);
// One more time why not
foo.propNumber = @(update2);
XCTAssertEqual(0,
dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC)),
"Failed waiting for second observable update");
XCTAssertEqual(oldVal, update1);
XCTAssertEqual(newVal, update2);
}
- (void)testUnexpectedTypes {
Foo *foo = [[Foo alloc] init];
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
NSString *origVal = @"any_val";
NSString *update = @"new_val";
foo.propId = origVal;
__block id oldVal;
__block id newVal;
SNTKVOManager *kvo = [[SNTKVOManager alloc] initWithObject:foo
selector:@selector(propId)
type:[NSString class]
callback:^(id oldValue, id newValue) {
oldVal = oldValue;
newVal = newValue;
dispatch_semaphore_signal(sema);
}];
XCTAssertNotNil(kvo);
// Update to an unexpected type (here, NSNumber instead of NSString)
foo.propId = @(123);
XCTAssertEqual(0,
dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC)),
"Failed waiting for first observable update");
XCTAssertEqualObjects(oldVal, origVal);
XCTAssertNil(newVal);
// Update again with an expected type, ensure oldVal is now nil
foo.propId = update;
XCTAssertEqual(0,
dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC)),
"Failed waiting for first observable update");
XCTAssertNil(oldVal);
XCTAssertEqualObjects(newVal, update);
}
@end

View File

@@ -49,6 +49,9 @@ 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__)
/// Get the logging level for this process.
LogLevel EffectiveLogLevel();
#ifdef __cplusplus
} // extern C
#endif

View File

@@ -19,12 +19,6 @@
#import <asl.h>
#import <pthread.h>
#ifdef DEBUG
static LogLevel logLevel = LOG_LEVEL_DEBUG;
#else
static LogLevel logLevel = LOG_LEVEL_INFO; // default to info
#endif
void syslogClientDestructor(void *arg) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@@ -32,6 +26,21 @@ void syslogClientDestructor(void *arg) {
#pragma clang diagnostic pop
}
LogLevel EffectiveLogLevel() {
#ifdef DEBUG
static LogLevel logLevel = LOG_LEVEL_DEBUG;
#else
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 NSString *binaryName;
@@ -41,10 +50,6 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
dispatch_once(&pred, ^{
binaryName = [[NSProcessInfo processInfo] processName];
if ([SNTConfigurator configurator].enableDebugLogging) {
logLevel = LOG_LEVEL_DEBUG;
}
// If requested, redirect output to syslog.
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--syslog"] ||
[binaryName isEqualToString:@"com.google.santa.daemon"]) {
@@ -53,7 +58,7 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
}
});
if (logLevel < level) return;
if (EffectiveLogLevel() < level) return;
va_list args;
va_start(args, format);

View File

@@ -108,6 +108,11 @@ NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType);
*/
+ (instancetype)sharedInstance;
/**
* Resets all the metrics in this set. Intended only for testing.
*/
- (void)reset;
/**
* Add a root label to the MetricSet.
*/

View File

@@ -27,9 +27,9 @@ NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType) {
case SNTMetricTypeGaugeInt64: typeStr = @"SNTMetricTypeGaugeInt64"; break;
case SNTMetricTypeGaugeDouble: typeStr = @"SNTMetricTypeGaugeDouble"; break;
case SNTMetricTypeCounter: typeStr = @"SNTMetricTypeCounter"; break;
default: typeStr = @"SNTMetricTypeUnknown"; break;
default: typeStr = [NSString stringWithFormat:@"SNTMetricTypeUnknown %ld", metricType]; break;
}
return [NSString stringWithFormat:@"%@ %ld", typeStr, metricType];
return typeStr;
}
/**
@@ -485,6 +485,10 @@ NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType) {
return self;
}
- (void)reset {
_metrics = [[NSMutableDictionary alloc] init];
}
- (void)addRootLabel:(NSString *)label value:(NSString *)value {
@synchronized(self) {
_rootLabels[label] = value;

View File

@@ -45,10 +45,10 @@
XCTAssertNotNil(c, @"Expected returned SNTMetricCounter to not be nil");
[c incrementForFieldValues:@[ @"certificate" ]];
XCTAssertEqual(1, [c getCountForFieldValues:@[ @"certificate" ]],
@"Counter not incremendted by 1");
@"Counter not incremented by 1");
[c incrementBy:3 forFieldValues:@[ @"certificate" ]];
XCTAssertEqual(4, [c getCountForFieldValues:@[ @"certificate" ]],
@"Counter not incremendted by 3");
@"Counter not incremented by 3");
}
- (void)testExportNSDictionary {
@@ -630,39 +630,39 @@
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeConstantBool],
@"expected" : @"SNTMetricTypeConstantBool 1"
@"expected" : @"SNTMetricTypeConstantBool"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeConstantString],
@"expected" : @"SNTMetricTypeConstantString 2"
@"expected" : @"SNTMetricTypeConstantString"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeConstantInt64],
@"expected" : @"SNTMetricTypeConstantInt64 3"
@"expected" : @"SNTMetricTypeConstantInt64"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeConstantDouble],
@"expected" : @"SNTMetricTypeConstantDouble 4"
@"expected" : @"SNTMetricTypeConstantDouble"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeGaugeBool],
@"expected" : @"SNTMetricTypeGaugeBool 5"
@"expected" : @"SNTMetricTypeGaugeBool"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeGaugeString],
@"expected" : @"SNTMetricTypeGaugeString 6"
@"expected" : @"SNTMetricTypeGaugeString"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeGaugeInt64],
@"expected" : @"SNTMetricTypeGaugeInt64 7"
@"expected" : @"SNTMetricTypeGaugeInt64"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeGaugeDouble],
@"expected" : @"SNTMetricTypeGaugeDouble 8"
@"expected" : @"SNTMetricTypeGaugeDouble"
},
@{
@"input" : [NSNumber numberWithInt:SNTMetricTypeCounter],
@"expected" : @"SNTMetricTypeCounter 9"
@"expected" : @"SNTMetricTypeCounter"
}
];

View File

@@ -1,4 +1,4 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2015-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.
@@ -51,18 +51,23 @@
/// Designated initializer.
///
- (instancetype)initWithIdentifier:(NSString *)identifier
state:(SNTRuleState)state
type:(SNTRuleType)type
customMsg:(NSString *)customMsg
timestamp:(NSUInteger)timestamp;
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;
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.

View File

@@ -13,6 +13,7 @@
/// limitations under the License.
#import "Source/common/SNTRule.h"
#import "Source/common/SNTSyncConstants.h"
@interface SNTRule ()
@property(readwrite) NSUInteger timestamp;
@@ -48,6 +49,60 @@
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
#pragma clang diagnostic push

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

@@ -1,4 +1,4 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2015-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.
@@ -95,6 +95,11 @@
///
@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.
///

View File

@@ -49,6 +49,7 @@
ENCODE(self.fileBundleVersionString, @"fileBundleVersionString");
ENCODE(self.signingChain, @"signingChain");
ENCODE(self.teamID, @"teamID");
ENCODE(self.executingUser, @"executingUser");
ENCODE(self.occurrenceDate, @"occurrenceDate");
@@ -93,6 +94,7 @@
_fileBundleVersionString = DECODE(NSString, @"fileBundleVersionString");
_signingChain = DECODEARRAY(MOLCertificate, @"signingChain");
_teamID = DECODE(NSString, @"teamID");
_executingUser = DECODE(NSString, @"executingUser");
_occurrenceDate = DECODE(NSDate, @"occurrenceDate");

View File

@@ -1,4 +1,4 @@
/// Copyright 2016 Google Inc. All rights reserved.
/// Copyright 2016-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.
@@ -12,10 +12,14 @@
/// 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); \
// clang-format off
#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);
// clang-format on

View File

@@ -50,6 +50,8 @@ 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;
@@ -62,10 +64,12 @@ 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;
@@ -89,6 +93,7 @@ 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;
@@ -132,5 +137,5 @@ extern const NSUInteger kDefaultEventBatchSize;
/// Are represented in seconds
///
extern const NSUInteger kDefaultFullSyncInterval;
extern const NSUInteger kDefaultFCMFullSyncInterval;
extern const NSUInteger kDefaultFCMGlobalRuleSyncDeadline;
extern const NSUInteger kDefaultPushNotificationsFullSyncInterval;
extern const NSUInteger kDefaultPushNotificationsGlobalRuleSyncDeadline;

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTSyncConstants.h"
#import "Source/common/SNTSyncConstants.h"
NSString *const kXSRFToken = @"X-XSRF-TOKEN";
@@ -51,6 +51,8 @@ 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";
@@ -63,10 +65,12 @@ 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";
@@ -90,6 +94,7 @@ 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";
@@ -125,5 +130,5 @@ NSString *const kLogSync = @"log_sync";
const NSUInteger kDefaultEventBatchSize = 50;
const NSUInteger kDefaultFullSyncInterval = 600;
const NSUInteger kDefaultFCMFullSyncInterval = 14400;
const NSUInteger kDefaultFCMGlobalRuleSyncDeadline = 600;
const NSUInteger kDefaultPushNotificationsFullSyncInterval = 14400;
const NSUInteger kDefaultPushNotificationsGlobalRuleSyncDeadline = 600;

View File

@@ -1,4 +1,4 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2015-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.
@@ -18,8 +18,11 @@
@implementation SNTSystemInfo
+ (NSString *)serialNumber {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
io_service_t platformExpert =
IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
#pragma clang diagnostic pop
if (!platformExpert) return nil;
NSString *serial = CFBridgingRelease(IORegistryEntryCreateCFProperty(
@@ -31,8 +34,11 @@
}
+ (NSString *)hardwareUUID {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
io_service_t platformExpert =
IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
#pragma clang diagnostic pop
if (!platformExpert) return nil;
NSString *uuid = CFBridgingRelease(IORegistryEntryCreateCFProperty(

View File

@@ -20,7 +20,7 @@
@protocol SNTDaemonControlXPC <SNTUnprivilegedDaemonControlXPC>
///
/// Kernel ops
/// Cache ops
///
- (void)flushCache:(void (^)(BOOL))reply;
@@ -41,7 +41,6 @@
/// Config ops
///
- (void)setClientMode:(SNTClientMode)mode reply:(void (^)(void))reply;
- (void)setXsrfToken:(NSString *)token reply:(void (^)(void))reply;
- (void)setFullSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply;
- (void)setRuleSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply;
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)(void))reply;
@@ -51,11 +50,12 @@
- (void)setRemountUSBMode:(NSArray *)remountUSBMode reply:(void (^)(void))reply;
- (void)setEnableBundles:(BOOL)bundlesEnabled reply:(void (^)(void))reply;
- (void)setEnableTransitiveRules:(BOOL)enabled reply:(void (^)(void))reply;
- (void)setEnableAllEventUpload:(BOOL)enabled reply:(void (^)(void))reply;
- (void)setDisableUnknownEventUpload:(BOOL)enabled reply:(void (^)(void))reply;
///
/// Syncd Ops
///
- (void)setSyncdListener:(NSXPCListenerEndpoint *)listener;
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message reply:(void (^)(void))reply;
@end

View File

@@ -27,10 +27,16 @@ NSString *const kBundleID = @"com.google.santa.daemon";
@implementation SNTXPCControlInterface
+ (NSString *)serviceID {
#ifdef SANTAADHOC
// The mach service for an adhoc signed ES sysx uses the "endpoint-security" prefix instead of
// the teamid. In Santa's case it will be endpoint-security.com.google.santa.daemon.xpc.
return [NSString stringWithFormat:@"endpoint-security.%@.xpc", kBundleID];
#else
MOLCodesignChecker *cs = [[MOLCodesignChecker alloc] initWithSelf];
// "teamid.com.google.santa.daemon.xpc"
NSString *t = cs.signingInformation[@"teamid"];
return [NSString stringWithFormat:@"%@.%@.xpc", t, kBundleID];
#endif
}
+ (NSString *)systemExtensionID {

View File

@@ -20,18 +20,37 @@
@class SNTStoredEvent;
/// A block that reports the number of rules processed.
/// TODO(bur): Add more details about the sync.
typedef void (^SNTFullSyncReplyBlock)(NSNumber *rulesProcessed);
///
/// Protocol implemented by syncservice and utilized by daemon and ctl for communication with a
/// sync server.
///
@protocol SNTSyncServiceXPC
- (void)postEventsToSyncServer:(NSArray<SNTStoredEvent *> *)events fromBundle:(BOOL)fromBundle;
- (void)postBundleEventToSyncServer:(SNTStoredEvent *)event
reply:(void (^)(SNTBundleEventAction))reply;
- (void)isFCMListening:(void (^)(BOOL))reply;
- (void)performFullSyncWithReply:(SNTFullSyncReplyBlock)reply;
// The syncservice regularly syncs with a configured sync server. Use this method to sync out of
// band. The syncservice ensures syncs do not run concurrently.
//
// Pass an NSXPCListenerEndpoint whose associated NSXPCListener exports an object that implements
// the SNTSyncServiceLogReceiverXPC protocol. The caller will receive sync logs over this listener.
// This is required.
//
// Syncs are enqueued in order and executed serially. kMaxEnqueuedSyncs limits the number of syncs
// in the queue. If the queue is full calls to this method will be dropped and
// SNTSyncStatusTypeTooManySyncsInProgress will be passed into the reply block.
//
// Pass true to isClean to perform a clean sync, defaults to false.
//
- (void)syncWithLogListener:(NSXPCListenerEndpoint *)logListener
isClean:(BOOL)cleanSync
reply:(void (^)(SNTSyncStatusType))reply;
// Spindown the syncservice. The syncservice will not automatically start back up.
// A new connection to the syncservice will bring it back up. This allows us to avoid running
// the syncservice needlessly when there is no configured sync server.
- (void)spindown;
@end
@interface SNTXPCSyncServiceInterface : NSObject
@@ -54,3 +73,11 @@ typedef void (^SNTFullSyncReplyBlock)(NSNumber *rulesProcessed);
+ (MOLXPCConnection *)configuredConnection;
@end
///
/// Protocol implemented by santactl sync and used to receive log messages from
/// the syncservice during a user initiated sync.
///
@protocol SNTSyncServiceLogReceiverXPC
- (void)didReceiveLog:(NSString *)log;
@end

View File

@@ -1,37 +0,0 @@
/// 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 <Foundation/Foundation.h>
#import "Source/common/SNTCommonEnums.h"
@class SNTStoredEvent;
/// Protocol implemented by santactl and utilized by santad
@protocol SNTSyncdXPC
- (void)postEventsToSyncServer:(NSArray<SNTStoredEvent *> *)events isFromBundle:(BOOL)isFromBundle;
- (void)postBundleEventToSyncServer:(SNTStoredEvent *)event
reply:(void (^)(SNTBundleEventAction))reply;
- (void)isFCMListening:(void (^)(BOOL))reply;
@end
@interface SNTXPCSyncdInterface : NSObject
///
/// Returns an initialized NSXPCInterface for the SNTSyncdXPC protocol.
/// Ensures any methods that accept custom classes as arguments are set-up before returning
///
+ (NSXPCInterface *)syncdInterface;
@end

View File

@@ -1,4 +1,4 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2015-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.
@@ -15,8 +15,8 @@
#import <Foundation/Foundation.h>
#import <MOLCertificate/MOLCertificate.h>
#import "Source/common/SNTCommonEnums.h"
#import "Source/common/SNTCommon.h"
#import "Source/common/SNTCommonEnums.h"
@class SNTRule;
@class SNTStoredEvent;
@@ -28,12 +28,10 @@
@protocol SNTUnprivilegedDaemonControlXPC
///
/// Kernel ops
/// Cache Ops
///
- (void)cacheCounts:(void (^)(uint64_t rootCache, uint64_t nonRootCache))reply;
- (void)cacheBucketCount:(void (^)(NSArray *))reply;
- (void)checkCacheForVnodeID:(santa_vnode_id_t)vnodeID withReply:(void (^)(santa_action_t))reply;
- (void)driverConnectionEstablished:(void (^)(BOOL))reply;
///
/// Database ops
@@ -41,6 +39,7 @@
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate, int64_t compiler,
int64_t transitive, int64_t teamID))reply;
- (void)databaseEventCount:(void (^)(int64_t count))reply;
- (void)staticRuleCount:(void (^)(int64_t count))reply;
///
/// Decision ops
@@ -64,7 +63,6 @@
/// Config ops
///
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply;
- (void)xsrfToken:(void (^)(NSString *))reply;
- (void)clientMode:(void (^)(SNTClientMode))reply;
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply;
- (void)ruleSyncLastSuccess:(void (^)(NSDate *))reply;

View File

@@ -1,4 +1,4 @@
/// Copyright 2016 Google Inc. All rights reserved.
/// Copyright 2016-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.
@@ -17,6 +17,7 @@
#include <libkern/OSAtomic.h>
#include <libkern/OSTypes.h>
#include <os/log.h>
#include <stdint.h>
#include <sys/cdefs.h>
@@ -26,11 +27,6 @@
#include "Source/common/SNTCommon.h"
#define panic(args...) \
printf(args); \
printf("\n"); \
abort()
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@@ -334,7 +330,9 @@ class SantaCache {
inline void unlock(struct bucket *bucket) const {
if (unlikely(OSAtomicTestAndClear(7, (volatile uint8_t *)&bucket->head) ==
0)) {
panic("SantaCache::unlock(): Tried to unlock an unlocked lock");
os_log_error(OS_LOG_DEFAULT,
"SantaCache::unlock(): Tried to unlock an unlocked lock");
abort();
}
}

60
Source/common/TestUtils.h Normal file
View File

@@ -0,0 +1,60 @@
/// 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.
#ifndef SANTA__COMMON__TESTUTILS_H
#define SANTA__COMMON__TESTUTILS_H
#include <EndpointSecurity/EndpointSecurity.h>
#import <XCTest/XCTest.h>
#include <bsm/libbsm.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <sys/stat.h>
#define NOBODY_UID ((unsigned int)-2)
#define NOBODY_GID ((unsigned int)-2)
// Bubble up googletest expectation failures to XCTest failures
#define XCTBubbleMockVerifyAndClearExpectations(mock) \
XCTAssertTrue(::testing::Mock::VerifyAndClearExpectations(mock), \
"Expected calls were not properly mocked")
// Pretty print C string match errors
#define XCTAssertCStringEqual(got, want) \
XCTAssertTrue(strcmp((got), (want)) == 0, @"\nMismatched strings.\n\t got: %s\n\twant: %s", \
(got), (want))
// Pretty print C++ string match errors
#define XCTAssertCppStringEqual(got, want) XCTAssertCStringEqual((got).c_str(), (want).c_str())
// Helper to ensure at least `ms` milliseconds are slept, even if the sleep
// function returns early due to interrupts.
void SleepMS(long ms);
enum class ActionType {
Auth,
Notify,
};
// Helpers to construct various ES structs
audit_token_t MakeAuditToken(pid_t pid, pid_t pidver);
struct stat MakeStat(ino_t ino, dev_t devno = 0);
es_string_token_t MakeESStringToken(const char *s);
es_file_t MakeESFile(const char *path, struct stat sb = {});
es_process_t MakeESProcess(es_file_t *file, audit_token_t tok = {}, audit_token_t parent_tok = {});
es_message_t MakeESMessage(es_event_type_t et, es_process_t *proc,
ActionType action_type = ActionType::Notify,
uint64_t future_deadline_ms = 100000);
#endif

107
Source/common/TestUtils.mm Normal file
View File

@@ -0,0 +1,107 @@
/// 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.
#include "Source/common/TestUtils.h"
#include <EndpointSecurity/ESTypes.h>
#include <dispatch/dispatch.h>
#include <mach/mach_time.h>
#include <time.h>
audit_token_t MakeAuditToken(pid_t pid, pid_t pidver) {
return audit_token_t{
.val =
{
0,
NOBODY_UID,
NOBODY_GID,
NOBODY_UID,
NOBODY_GID,
(unsigned int)pid,
0,
(unsigned int)pidver,
},
};
}
struct stat MakeStat(ino_t ino, dev_t devno) {
return (struct stat){
.st_dev = devno,
.st_ino = ino,
};
}
es_string_token_t MakeESStringToken(const char *s) {
return es_string_token_t{
.length = strlen(s),
.data = s,
};
}
es_file_t MakeESFile(const char *path, struct stat sb) {
return es_file_t{
.path = MakeESStringToken(path),
.path_truncated = false,
.stat = sb,
};
}
es_process_t MakeESProcess(es_file_t *file, audit_token_t tok, audit_token_t parent_tok) {
return es_process_t{
.audit_token = tok,
.ppid = audit_token_to_pid(parent_tok),
.original_ppid = audit_token_to_pid(parent_tok),
.executable = file,
.parent_audit_token = parent_tok,
};
}
static uint64_t AddMillisToMachTime(uint64_t ms, uint64_t machTime) {
static dispatch_once_t onceToken;
static mach_timebase_info_data_t timebase;
dispatch_once(&onceToken, ^{
mach_timebase_info(&timebase);
});
// Convert given machTime to nanoseconds
uint64_t nanoTime = machTime * timebase.numer / timebase.denom;
// Add the ms offset
nanoTime += (ms * NSEC_PER_MSEC);
// Convert back to machTime
return nanoTime * timebase.denom / timebase.numer;
}
es_message_t MakeESMessage(es_event_type_t et, es_process_t *proc, ActionType action_type,
uint64_t future_deadline_ms) {
return es_message_t{
.deadline = AddMillisToMachTime(future_deadline_ms, mach_absolute_time()),
.process = proc,
.action_type =
(action_type == ActionType::Notify) ? ES_ACTION_TYPE_NOTIFY : ES_ACTION_TYPE_AUTH,
.event_type = et,
};
}
void SleepMS(long ms) {
struct timespec ts {
.tv_sec = ms / 1000, .tv_nsec = (long)((ms % 1000) * NSEC_PER_MSEC),
};
while (nanosleep(&ts, &ts) != 0) {
XCTAssertEqual(errno, EINTR);
}
}

View File

@@ -1,6 +1,7 @@
//
// !!! WARNING !!!
// This proto is in beta format and subject to change.
// This proto is for demonstration purposes only and will be changing.
// Do not rely on this format.
//
syntax = "proto3";
@@ -79,6 +80,7 @@ message Execution {
optional string original_path = 11;
repeated string args = 12;
optional string machine_id = 13;
optional string team_id = 14;
}
message DiskAppeared {

View File

@@ -1,7 +1,12 @@
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_application")
load("//:helper.bzl", "santa_unit_test")
licenses(["notice"])
package(
default_visibility = ["//:santa_package_group"],
)
exports_files([
"Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-256.png",
])
@@ -27,6 +32,9 @@ objc_library(
"SNTNotificationManager.m",
"main.m",
],
hdrs = [
"SNTNotificationManager.h",
],
data = [
"Resources/AboutWindow.xib",
"Resources/DeviceMessageWindow.xib",
@@ -36,13 +44,19 @@ objc_library(
"IOKit",
"SecurityInterface",
"SystemExtensions",
"UserNotifications",
],
deps = [
"//Source/common:SNTBlockMessage_SantaGUI",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDeviceEvent",
"//Source/common:SNTLogging",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTStrengthify",
"//Source/common:SNTSyncConstants",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCNotifierInterface",
"@MOLCertificate",
"@MOLCodesignChecker",
"@MOLXPCConnection",
],
@@ -54,6 +68,7 @@ macos_application(
"//Source/santactl": "MacOS",
"//Source/santabundleservice": "MacOS",
"//Source/santametricservice": "MacOS",
"//Source/santasyncservice": "MacOS",
"//Source/santad:com.google.santa.daemon": "Library/SystemExtensions",
},
app_icons = glob(["Resources/Images.xcassets/**"]),
@@ -64,14 +79,41 @@ macos_application(
"--force",
"--options library,kill,runtime",
],
entitlements = "Santa.app.entitlements",
entitlements = select({
"//:adhoc_build": "Santa.app-adhoc.entitlements",
# Non-adhoc builds get thier entitlements from the provisioning profile.
"//conditions:default": None,
}),
infoplists = ["Info.plist"],
minimum_os_version = "10.9",
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//conditions:default": "Santa_Dev.provisionprofile",
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",
visibility = ["//:santa_package_group"],
deps = [":SantaGUI_lib"],
)
santa_unit_test(
name = "SNTNotificationManagerTest",
srcs = [
"SNTNotificationManagerTest.m",
],
sdk_frameworks = [
"Cocoa",
],
deps = [
":SantaGUI_lib",
"//Source/common:SNTStoredEvent",
"@OCMock",
],
)
test_suite(
name = "unit_tests",
tests = [
":SNTNotificationManagerTest",
],
visibility = ["//:santa_package_group"],
)

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19455" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19455"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -17,7 +17,7 @@
<window title="Santa" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<rect key="contentRect" x="196" y="240" width="480" height="200"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<rect key="screenRect" x="0.0" y="0.0" width="1728" height="1079"/>
<view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="480" height="200"/>
<autoresizingMask key="autoresizingMask"/>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19455" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19455"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -15,7 +15,7 @@
<window title="Santa Blocked Execution" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" visibleAtLaunch="NO" animationBehavior="none" id="9Bq-yh-54f" customClass="SNTMessageWindow">
<windowStyleMask key="styleMask" utility="YES"/>
<rect key="contentRect" x="167" y="107" width="515" height="318"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="875"/>
<rect key="screenRect" x="0.0" y="0.0" width="1728" height="1079"/>
<view key="contentView" id="Iwq-Lx-rLv">
<rect key="frame" x="0.0" y="0.0" width="515" height="318"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19455" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19455"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -21,13 +21,13 @@
<window title="Santa Blocked Execution" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" visibleAtLaunch="NO" animationBehavior="none" id="9Bq-yh-54f" customClass="SNTMessageWindow">
<windowStyleMask key="styleMask" utility="YES"/>
<rect key="contentRect" x="167" y="107" width="540" height="479"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<view key="contentView" misplaced="YES" id="Iwq-Lx-rLv">
<rect key="frame" x="0.0" y="0.0" width="540" height="479"/>
<rect key="screenRect" x="0.0" y="0.0" width="1728" height="1079"/>
<view key="contentView" id="Iwq-Lx-rLv">
<rect key="frame" x="0.0" y="0.0" width="540" height="462"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kiB-jZ-69S">
<rect key="frame" x="16" y="451" width="37" height="32"/>
<rect key="frame" x="16" y="434" width="37" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Hidden Button" alternateTitle="This button exists so neither of the other two buttons is pre-selected when the dialog opens." bezelStyle="rounded" alignment="center" borderStyle="border" focusRingType="none" transparent="YES" imageScaling="proportionallyDown" inset="2" id="XGa-Sl-F4t">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@@ -240,17 +240,16 @@
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="7ua-5a-uSd">
<rect key="frame" x="153" y="35" width="114" height="23"/>
<rect key="frame" x="147" y="30" width="126" height="32"/>
<constraints>
<constraint firstAttribute="width" priority="900" constant="112" id="Pec-Pa-4aZ"/>
</constraints>
<buttonCell key="cell" type="roundTextured" title="Open Event..." bezelStyle="texturedRounded" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="X1b-TF-1TL">
<buttonCell key="cell" type="push" title="Open Event..." bezelStyle="rounded" alignment="center" 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"/>
@@ -285,18 +284,17 @@ DQ
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BbV-3h-mmL" userLabel="Dismiss Button">
<rect key="frame" x="277" y="33" width="112" height="25"/>
<rect key="frame" x="271" y="28" width="124" height="34"/>
<constraints>
<constraint firstAttribute="width" constant="110" id="6Uh-Bd-N64"/>
<constraint firstAttribute="height" constant="22" id="GH6-nw-6rD"/>
</constraints>
<buttonCell key="cell" type="roundTextured" title="Ignore" bezelStyle="texturedRounded" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="XR6-Xa-gP4">
<buttonCell key="cell" type="push" title="Ignore" bezelStyle="rounded" alignment="center" 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
Gw
</string>
<modifierMask key="keyEquivalentModifierMask" shift="YES"/>
</buttonCell>
<accessibility description="Dismiss Dialog"/>
<connections>

View File

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

View File

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

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTAppDelegate.h"
#import "Source/gui/SNTAppDelegate.h"
#import <MOLXPCConnection/MOLXPCConnection.h>
@@ -20,8 +20,8 @@
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStrengthify.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santa/SNTAboutWindowController.h"
#import "Source/santa/SNTNotificationManager.h"
#import "Source/gui/SNTAboutWindowController.h"
#import "Source/gui/SNTNotificationManager.h"
@interface SNTAppDelegate ()
@property SNTAboutWindowController *aboutWindowController;

View File

@@ -14,7 +14,7 @@
#import <Cocoa/Cocoa.h>
#import "Source/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
@class SNTStoredEvent;

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTBinaryMessageWindowController.h"
#import "Source/gui/SNTBinaryMessageWindowController.h"
#import <MOLCertificate/MOLCertificate.h>
#import <SecurityInterface/SFCertificatePanel.h>
@@ -20,7 +20,7 @@
#import "Source/common/SNTBlockMessage.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/santa/SNTMessageWindow.h"
#import "Source/gui/SNTMessageWindow.h"
@interface SNTBinaryMessageWindowController ()
/// The custom message to display for this event
@@ -139,7 +139,9 @@
- (NSString *)publisherInfo {
MOLCertificate *leafCert = [self.event.signingChain firstObject];
if (leafCert.commonName && leafCert.orgName) {
if ([leafCert.commonName isEqualToString:@"Apple Mac OS Application Signing"]) {
return [NSString stringWithFormat:@"App Store (Team ID: %@)", self.event.teamID];
} else if (leafCert.commonName && leafCert.orgName) {
return [NSString stringWithFormat:@"%@ - %@", leafCert.orgName, leafCert.commonName];
} else if (leafCert.commonName) {
return leafCert.commonName;

View File

@@ -14,7 +14,7 @@
#import <Cocoa/Cocoa.h>
#import "Source/common/SNTDeviceEvent.h"
#import "Source/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
NS_ASSUME_NONNULL_BEGIN

View File

@@ -12,12 +12,12 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTDeviceMessageWindowController.h"
#import "Source/gui/SNTDeviceMessageWindowController.h"
#import "Source/common/SNTBlockMessage.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTDeviceEvent.h"
#import "Source/santa/SNTMessageWindow.h"
#import "Source/gui/SNTMessageWindow.h"
NS_ASSUME_NONNULL_BEGIN

View File

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

View File

@@ -1,6 +1,6 @@
#import "Source/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
#import "Source/santa/SNTMessageWindow.h"
#import "Source/gui/SNTMessageWindow.h"
@implementation SNTMessageWindowController
@@ -28,7 +28,8 @@
}
- (NSString *)messageHash {
return @"";
[self doesNotRecognizeSelector:_cmd];
return nil;
}
@end

View File

@@ -15,9 +15,9 @@
#import <Cocoa/Cocoa.h>
#import "Source/common/SNTXPCNotifierInterface.h"
#import "Source/santa/SNTBinaryMessageWindowController.h"
#import "Source/santa/SNTDeviceMessageWindowController.h"
#import "Source/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTBinaryMessageWindowController.h"
#import "Source/gui/SNTDeviceMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
///
/// Keeps track of pending notifications and ensures only one is presented to the user at a time.

View File

@@ -12,9 +12,11 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTNotificationManager.h"
#import "Source/gui/SNTNotificationManager.h"
#import <MOLCertificate/MOLCertificate.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import <UserNotifications/UserNotifications.h>
#import "Source/common/SNTBlockMessage.h"
#import "Source/common/SNTConfigurator.h"
@@ -22,8 +24,9 @@
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/common/SNTStrengthify.h"
#import "Source/common/SNTSyncConstants.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
@interface SNTNotificationManager ()
@@ -58,7 +61,7 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
[self.pendingNotifications removeObject:self.currentWindowController];
self.currentWindowController = nil;
if ([self.pendingNotifications count]) {
if (self.pendingNotifications.count) {
[self showQueuedWindow];
} else {
MOLXPCConnection *bc = [SNTXPCBundleServiceInterface configuredConnection];
@@ -83,9 +86,7 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
- (BOOL)notificationAlreadyQueued:(SNTMessageWindowController *)pendingMsg {
for (SNTMessageWindowController *msg in self.pendingNotifications) {
if ([msg messageHash] == [pendingMsg messageHash]) {
return YES;
}
if ([[msg messageHash] isEqual:[pendingMsg messageHash]]) return YES;
}
return NO;
}
@@ -113,12 +114,59 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
pendingMsg.delegate = self;
[self.pendingNotifications addObject:pendingMsg];
[self postDistributedNotification:pendingMsg];
if (!self.currentWindowController) {
[self showQueuedWindow];
}
}
// For blocked execution notifications, post an NSDistributedNotificationCenter
// notification with the important details from the stored event. Distributed
// notifications are system-wide broadcasts that can be sent by apps and observed
// from separate processes. This allows users of Santa to write tools that
// perform actions when we block execution, such as trigger management tools or
// display an enterprise-specific UI (which is particularly useful when combined
// with the EnableSilentMode configuration option, to disable Santa's standard UI).
- (void)postDistributedNotification:(SNTMessageWindowController *)pendingMsg {
if (![pendingMsg isKindOfClass:[SNTBinaryMessageWindowController class]]) {
return;
}
SNTBinaryMessageWindowController *wc = (SNTBinaryMessageWindowController *)pendingMsg;
NSDistributedNotificationCenter *dc = [NSDistributedNotificationCenter defaultCenter];
NSMutableArray<NSDictionary *> *signingChain =
[NSMutableArray arrayWithCapacity:wc.event.signingChain.count];
for (MOLCertificate *cert in wc.event.signingChain) {
[signingChain addObject:@{
kCertSHA256 : cert.SHA256 ?: @"",
kCertCN : cert.commonName ?: @"",
kCertOrg : cert.orgName ?: @"",
kCertOU : cert.orgUnit ?: @"",
kCertValidFrom : @([cert.validFrom timeIntervalSince1970]) ?: @0,
kCertValidUntil : @([cert.validUntil timeIntervalSince1970]) ?: @0,
}];
}
NSDictionary *userInfo = @{
kFileSHA256 : wc.event.fileSHA256 ?: @"",
kFilePath : wc.event.filePath ?: @"",
kFileBundleName : wc.event.fileBundleName ?: @"",
kFileBundleID : wc.event.fileBundleID ?: @"",
kFileBundleVersion : wc.event.fileBundleVersion ?: @"",
kFileBundleShortVersionString : wc.event.fileBundleVersionString ?: @"",
kTeamID : wc.event.teamID ?: @"",
kExecutingUser : wc.event.executingUser ?: @"",
kExecutionTime : @([wc.event.occurrenceDate timeIntervalSince1970]) ?: @0,
kPID : wc.event.pid ?: @0,
kPPID : wc.event.ppid ?: @0,
kParentName : wc.event.parentName ?: @"",
kSigningChain : signingChain,
};
[dc postNotificationName:@"com.google.santa.notification.blockedeexecution"
object:@"com.google.santa"
userInfo:userInfo];
}
- (void)showQueuedWindow {
// Notifications arrive on a background thread but UI updates must happen on the main thread.
// This includes making windows.
@@ -156,6 +204,7 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
// Otherwise abandon bundle hashing and display the blockable event.
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
[withController updateBlockNotification:event withBundleHash:nil];
LOGE(@"Timeout connecting to bundle service");
return;
}
@@ -208,31 +257,66 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
#pragma mark SNTNotifierXPC protocol methods
- (void)postClientModeNotification:(SNTClientMode)clientmode {
NSUserNotification *un = [[NSUserNotification alloc] init];
un.title = @"Santa";
un.hasActionButton = NO;
NSString *customMsg;
if ([SNTConfigurator configurator].enableSilentMode) return;
UNUserNotificationCenter *un = [UNUserNotificationCenter currentNotificationCenter];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = @"Santa";
switch (clientmode) {
case SNTClientModeMonitor:
un.informativeText = @"Switching into Monitor mode";
customMsg = [[SNTConfigurator configurator] modeNotificationMonitor];
case SNTClientModeMonitor: {
content.body = @"Switching into Monitor mode";
NSString *customMsg = [[SNTConfigurator configurator] modeNotificationMonitor];
if (!customMsg) break;
// If a custom message is added but as an empty string, disable notifications.
if (!customMsg.length) return;
un.informativeText = [SNTBlockMessage stringFromHTML:customMsg];
content.body = [SNTBlockMessage stringFromHTML:customMsg];
break;
case SNTClientModeLockdown:
un.informativeText = @"Switching into Lockdown mode";
customMsg = [[SNTConfigurator configurator] modeNotificationLockdown];
}
case SNTClientModeLockdown: {
content.body = @"Switching into Lockdown mode";
NSString *customMsg = [[SNTConfigurator configurator] modeNotificationLockdown];
if (!customMsg) break;
// If a custom message is added but as an empty string, disable notifications.
if (!customMsg.length) return;
un.informativeText = [SNTBlockMessage stringFromHTML:customMsg];
content.body = [SNTBlockMessage stringFromHTML:customMsg];
break;
}
default: return;
}
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:un];
UNNotificationRequest *req =
[UNNotificationRequest requestWithIdentifier:@"clientModeNotification"
content:content
trigger:nil];
[un addNotificationRequest:req withCompletionHandler:nil];
}
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message {
if ([SNTConfigurator configurator].enableSilentMode) return;
UNUserNotificationCenter *un = [UNUserNotificationCenter currentNotificationCenter];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = @"Santa";
content.body = message ?: @"Requested application can now be run";
NSString *identifier = [NSString stringWithFormat:@"ruleSyncNotification_%@", content.body];
UNNotificationRequest *req = [UNNotificationRequest requestWithIdentifier:identifier
content:content
trigger:nil];
[un addNotificationRequest:req withCompletionHandler:nil];
}
- (void)postBlockNotification:(SNTStoredEvent *)event withCustomMessage:(NSString *)message {
if ([SNTConfigurator configurator].enableSilentMode) return;
if (!event) {
LOGI(@"Error: Missing event object in message received from daemon!");
return;
@@ -244,15 +328,9 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
[self queueMessage:pendingMsg];
}
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message {
NSUserNotification *un = [[NSUserNotification alloc] init];
un.title = @"Santa";
un.hasActionButton = NO;
un.informativeText = message ?: @"Requested application can now be run";
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:un];
}
- (void)postUSBBlockNotification:(SNTDeviceEvent *)event withCustomMessage:(NSString *)message {
if ([SNTConfigurator configurator].enableSilentMode) return;
if (!event) {
LOGI(@"Error: Missing event object in message received from daemon!");
return;

View File

@@ -0,0 +1,76 @@
/// 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 <MOLCertificate/MOLCertificate.h>
// #import <MOLCodesignChecker/MOLCodesignChecker.h>
#import <OCMock/OCMock.h>
#import <XCTest/XCTest.h>
#import "Source/gui/SNTNotificationManager.h"
#import "Source/common/SNTStoredEvent.h"
@class SNTBinaryMessageWindowController;
@interface SNTNotificationManager (Testing)
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event
withController:(SNTBinaryMessageWindowController *)controller;
@end
@interface SNTNotificationManagerTest : XCTestCase
@end
@implementation SNTNotificationManagerTest
- (void)setUp {
[super setUp];
fclose(stdout);
}
- (void)testPostBlockNotificationSendsDistributedNotification {
SNTStoredEvent *ev = [[SNTStoredEvent alloc] init];
ev.fileSHA256 = @"the-sha256";
ev.filePath = @"/Applications/Safari.app/Contents/MacOS/Safari";
ev.fileBundleName = @"Safari";
ev.fileBundlePath = @"/Applications/Safari.app";
ev.fileBundleID = @"com.apple.Safari";
ev.fileBundleVersion = @"18614.1.14.1.15";
ev.fileBundleVersionString = @"16.0";
ev.executingUser = @"rah";
ev.occurrenceDate = [NSDate dateWithTimeIntervalSince1970:1660221048];
ev.decision = SNTEventStateBlockBinary;
ev.pid = @84156;
ev.ppid = @1;
ev.parentName = @"launchd";
SNTNotificationManager *sut = OCMPartialMock([[SNTNotificationManager alloc] init]);
OCMStub([sut hashBundleBinariesForEvent:OCMOCK_ANY withController:OCMOCK_ANY]).andDo(nil);
id dncMock = OCMClassMock([NSDistributedNotificationCenter class]);
OCMStub([dncMock defaultCenter]).andReturn(dncMock);
[sut postBlockNotification:ev withCustomMessage:@""];
OCMVerify([dncMock postNotificationName:@"com.google.santa.notification.blockedeexecution"
object:@"com.google.santa"
userInfo:[OCMArg checkWithBlock:^BOOL(NSDictionary *userInfo) {
XCTAssertEqualObjects(userInfo[@"file_sha256"], @"the-sha256");
XCTAssertEqualObjects(userInfo[@"pid"], @84156);
XCTAssertEqualObjects(userInfo[@"ppid"], @1);
XCTAssertEqualObjects(userInfo[@"execution_time"], @1660221048);
return YES;
}]]);
}
@end

View File

@@ -0,0 +1,8 @@
<?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>com.apple.developer.system-extension.install</key>
<true/>
</dict>
</plist>

View File

@@ -17,7 +17,7 @@
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santa/SNTAppDelegate.h"
#import "Source/gui/SNTAppDelegate.h"
@interface SNTSystemExtensionDelegate : NSObject <OSSystemExtensionRequestDelegate>
@end

View File

@@ -1,16 +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>
<key>com.apple.application-identifier</key>
<string>EQHXZ8M8AV.com.google.santa</string>
<key>com.apple.developer.system-extension.install</key>
<true/>
<key>com.apple.developer.team-identifier</key>
<string>EQHXZ8M8AV</string>
<key>keychain-access-groups</key>
<array>
<string>EQHXZ8M8AV.com.google.santa</string>
</array>
</dict>
</plist>

View File

@@ -2,6 +2,10 @@ load("@build_bazel_rules_apple//apple:macos.bzl", "macos_command_line_applicatio
licenses(["notice"])
package(
default_visibility = ["//:santa_package_group"],
)
objc_library(
name = "santabs_lib",
srcs = [
@@ -12,6 +16,7 @@ objc_library(
deps = [
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTXPCBundleServiceInterface",
"//Source/common:SNTXPCNotifierInterface",
"@FMDB",
@@ -23,8 +28,17 @@ objc_library(
macos_command_line_application(
name = "santabundleservice",
bundle_id = "com.google.santa.bundleservice",
codesignopts = [
"--timestamp",
"--force",
"--options library,kill,runtime",
],
infoplists = ["Info.plist"],
minimum_os_version = "10.9",
minimum_os_version = "10.15",
provisioning_profile = select({
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",
visibility = ["//:santa_package_group"],
deps = [":santabs_lib"],

View File

@@ -5,7 +5,6 @@ licenses(["notice"])
package(
default_visibility = ["//:santa_package_group"],
features = ["layering_check"],
)
objc_library(
@@ -27,7 +26,6 @@ objc_library(
"//:opt_build": [],
"//conditions:default": [
"Commands/SNTCommandBundleInfo.m",
"Commands/SNTCommandCacheHistogram.m",
"Commands/SNTCommandCheckCache.m",
"Commands/SNTCommandFlushCache.m",
],
@@ -49,9 +47,11 @@ objc_library(
"//Source/common:SNTSystemInfo",
"//Source/common:SNTXPCBundleServiceInterface",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCSyncServiceInterface",
"//Source/common:SNTXPCUnprivilegedControlInterface",
"//Source/santasyncservice:sync_lib",
"@FMDB",
"@MOLCertificate",
"@MOLCodesignChecker",
"@MOLXPCConnection",
],
@@ -66,10 +66,10 @@ macos_command_line_application(
"--options library,kill,runtime",
],
infoplists = ["Info.plist"],
minimum_os_version = "10.9",
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//conditions:default": "Santa_Dev.provisionprofile",
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",
deps = [":santactl_lib"],
@@ -89,6 +89,7 @@ santa_unit_test(
"//Source/common:SNTCachedDecision",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTXPCControlInterface",
"@MOLCertificate",
"@MOLCodesignChecker",
@@ -99,12 +100,22 @@ santa_unit_test(
santa_unit_test(
name = "SNTCommandMetricsTest",
srcs = ["Commands/SNTCommandMetricsTest.m"],
srcs = [
"Commands/SNTCommandMetrics.h",
"Commands/SNTCommandMetricsTest.m",
"SNTCommand.h",
"SNTCommandController.h",
],
structured_resources = glob(["Commands/testdata/*"]),
visibility = ["//:santa_package_group"],
deps = [
":santactl_lib",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTMetricSet",
"//Source/common:SNTXPCControlInterface",
"//Source/santametricservice/Formats:SNTMetricFormatTestHelper",
"@MOLXPCConnection",
"@OCMock",
],
)

View File

@@ -1,79 +0,0 @@
/// 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.
#ifdef DEBUG
#import <Foundation/Foundation.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santactl/SNTCommand.h"
#import "Source/santactl/SNTCommandController.h"
@interface SNTCommandCacheHistogram : SNTCommand <SNTCommandProtocol>
@end
@implementation SNTCommandCacheHistogram
REGISTER_COMMAND_NAME(@"cachehistogram")
+ (BOOL)requiresRoot {
return YES;
}
+ (BOOL)requiresDaemonConn {
return YES;
}
+ (NSString *)shortHelpText {
return @"Print a cache distribution histogram.";
}
+ (NSString *)longHelpText {
return (@"Prints a histogram of each bucket of the in-kernel cache\n"
@" Use -g to get 'graphical' output\n"
@"Only available in DEBUG builds.");
}
- (void)runWithArguments:(NSArray *)arguments {
[[self.daemonConn remoteObjectProxy] cacheBucketCount:^(NSArray *counts) {
NSMutableDictionary<NSNumber *, NSNumber *> *d = [NSMutableDictionary dictionary];
[counts enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
d[obj] = @([d[obj] intValue] + 1);
}];
printf("There are %llu empty buckets\n", [d[@0] unsignedLongLongValue]);
for (NSNumber *key in [d.allKeys sortedArrayUsingSelector:@selector(compare:)]) {
if ([key isEqual:@0]) continue;
uint64_t k = [key unsignedLongLongValue];
uint64_t v = [d[key] unsignedLongLongValue];
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"-g"]) {
printf("%4llu: ", k);
for (uint64_t y = 0; y < v; ++y) {
printf("#");
}
printf("\n");
} else {
printf("%4llu bucket[s] have %llu %s\n", v, k, k > 1 ? "entries" : "entry");
}
}
exit(0);
}];
}
@end
#endif

View File

@@ -1,4 +1,4 @@
/// Copyright 2016 Google Inc. All rights reserved.
/// Copyright 2016-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.
@@ -62,9 +62,6 @@ REGISTER_COMMAND_NAME(@"checkcache")
} else if (action == ACTION_RESPOND_ALLOW_COMPILER) {
LOGI(@"File exists in [allowlist compiler] kernel cache");
exit(0);
} else if (action == ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE) {
LOGI(@"File exists in [allowlist pending_transitive] kernel cache");
exit(0);
} else if (action == ACTION_UNSET) {
LOGE(@"File does not exist in cache");
exit(1);

View File

@@ -1,4 +1,4 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2015-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.
@@ -385,6 +385,7 @@ REGISTER_COMMAND_NAME(@"fileinfo")
case SNTEventStateBlockScope: [output appendString:@" (Scope)"]; break;
case SNTEventStateAllowCompiler: [output appendString:@" (Compiler)"]; break;
case SNTEventStateAllowTransitive: [output appendString:@" (Transitive)"]; break;
case SNTEventStateBlockLongPath: [output appendString:@" (Long Path)"]; break;
default: output = @"None".mutableCopy; break;
}

View File

@@ -19,4 +19,5 @@
@interface SNTCommandMetrics : SNTCommand <SNTCommandProtocol>
- (void)prettyPrintMetrics:(NSDictionary *)metircs asJSON:(BOOL)exportJSON;
- (NSDictionary *)filterMetrics:(NSDictionary *)metrics withArguments:(NSArray *)args;
@end

View File

@@ -41,6 +41,7 @@ REGISTER_COMMAND_NAME(@"metrics")
+ (NSString *)longHelpText {
return (@"Provides metrics about Santa's operation while it's running.\n"
@"Pass prefixes to filter list of metrics, if desired.\n"
@" Use --json to output in JSON format");
}
@@ -118,10 +119,30 @@ REGISTER_COMMAND_NAME(@"metrics")
printf(">>> Root Labels\n");
[self prettyPrintRootLabels:normalizedMetrics[@"root_labels"]];
printf("\n");
printf(">>> Metrics \n");
printf(">>> Metrics\n");
[self prettyPrintMetricValues:normalizedMetrics[@"metrics"]];
}
- (NSDictionary *)filterMetrics:(NSDictionary *)metrics withArguments:(NSArray *)args {
NSMutableDictionary *outer = [metrics mutableCopy];
NSMutableDictionary *inner = [NSMutableDictionary dictionary];
__block BOOL hadFilter = NO;
[metrics[@"metrics"] enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
for (NSString *arg in args) {
if ([arg hasPrefix:@"-"]) continue;
hadFilter = YES;
if ([key hasPrefix:arg]) {
inner[key] = value;
}
}
}];
outer[@"metrics"] = inner;
return hadFilter ? outer : metrics;
}
- (void)runWithArguments:(NSArray *)arguments {
__block NSDictionary *metrics;
@@ -138,6 +159,8 @@ REGISTER_COMMAND_NAME(@"metrics")
fprintf(stderr, "Failed to retrieve metrics from daemon\n\n");
}
metrics = [self filterMetrics:metrics withArguments:arguments];
[self prettyPrintMetrics:metrics asJSON:[arguments containsObject:@"--json"]];
exit(0);
}

View File

@@ -131,4 +131,26 @@
@"Metrics command command did not produce expected output");
}
- (void)testFiltering {
SNTCommandMetrics *metricsCmd = [[SNTCommandMetrics alloc] init];
NSDictionary *metricDict = [SNTMetricFormatTestHelper createValidMetricsDictionary];
NSDictionary *filtered;
filtered = [metricsCmd filterMetrics:metricDict withArguments:@[]];
XCTAssertEqualObjects(metricDict[@"metrics"], filtered[@"metrics"], @"No filtering with no args");
filtered = [metricsCmd filterMetrics:metricDict withArguments:@[ @"--json" ]];
XCTAssertEqualObjects(metricDict[@"metrics"], filtered[@"metrics"],
@"No filtering with no metric args");
filtered = [metricsCmd filterMetrics:metricDict withArguments:@[ @"--json", @"/santa" ]];
XCTAssertEqual(((NSDictionary *)filtered[@"metrics"]).count, 3,
@"Expected filter of metrics with /santa to return 3 metrics");
filtered = [metricsCmd filterMetrics:metricDict withArguments:@[ @"/build", @"/santa" ]];
XCTAssertEqual(((NSDictionary *)filtered[@"metrics"]).count, 4,
@"Expected filter of metrics with /build and /santa to return 4 metrics");
}
@end

View File

@@ -74,14 +74,15 @@ REGISTER_COMMAND_NAME(@"rule")
- (void)runWithArguments:(NSArray *)arguments {
SNTConfigurator *config = [SNTConfigurator configurator];
// DEBUG builds add a --force flag to allow manually adding/removing rules during testing.
if ((config.syncBaseURL || config.staticRules.count) &&
![arguments containsObject:@"--check"]
#ifdef DEBUG
if ([config syncBaseURL] && ![arguments containsObject:@"--check"] &&
![arguments containsObject:@"--force"]) {
// DEBUG builds add a --force flag to allow manually adding/removing rules during testing.
&& ![arguments containsObject:@"--force"]) {
#else
if ([config syncBaseURL] && ![arguments containsObject:@"--check"]) {
) {
#endif
printf("SyncBaseURL is set, rules are managed centrally.\n");
printf("(SyncBaseURL/StaticRules is set, rules are managed centrally.)");
exit(1);
}

View File

@@ -1,4 +1,4 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2015-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.
@@ -48,16 +48,10 @@ REGISTER_COMMAND_NAME(@"status")
dispatch_group_t group = dispatch_group_create();
// Daemon status
__block BOOL driverConnected;
__block NSString *clientMode;
__block uint64_t cpuEvents, ramEvents;
__block double cpuPeak, ramPeak;
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] driverConnectionEstablished:^(BOOL connected) {
driverConnected = connected;
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] clientMode:^(SNTClientMode cm) {
switch (cm) {
case SNTClientModeMonitor: clientMode = @"Monitor"; break;
@@ -80,18 +74,14 @@ REGISTER_COMMAND_NAME(@"status")
SNTConfigurator *configurator = [SNTConfigurator configurator];
BOOL cachingEnabled = [configurator enableSysxCache];
// Kext status
// Cache status
__block uint64_t rootCacheCount = -1, nonRootCacheCount = -1;
if (cachingEnabled) {
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] cacheCounts:^(uint64_t rootCache, uint64_t nonRootCache) {
rootCacheCount = rootCache;
nonRootCacheCount = nonRootCache;
dispatch_group_leave(group);
}];
}
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] cacheCounts:^(uint64_t rootCache, uint64_t nonRootCache) {
rootCacheCount = rootCache;
nonRootCacheCount = nonRootCache;
dispatch_group_leave(group);
}];
// Database counts
__block int64_t eventCount = -1, binaryRuleCount = -1, certRuleCount = -1, teamIDRuleCount = -1;
@@ -113,6 +103,14 @@ REGISTER_COMMAND_NAME(@"status")
dispatch_group_leave(group);
}];
// Static rule count
__block int64_t staticRuleCount = -1;
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] staticRuleCount:^(int64_t count) {
staticRuleCount = count;
dispatch_group_leave(group);
}];
// Sync status
__block NSDate *fullSyncLastSuccess;
dispatch_group_enter(group);
@@ -172,16 +170,16 @@ REGISTER_COMMAND_NAME(@"status")
NSString *ruleSyncLastSuccessStr =
[dateFormatter stringFromDate:ruleSyncLastSuccess] ?: fullSyncLastSuccessStr;
NSString *syncURLStr = [[[SNTConfigurator configurator] syncBaseURL] absoluteString];
NSString *syncURLStr = configurator.syncBaseURL.absoluteString;
BOOL exportMetrics = [[SNTConfigurator configurator] exportMetrics];
NSURL *metricsURLStr = [[SNTConfigurator configurator] metricURL];
NSUInteger metricExportInterval = [[SNTConfigurator configurator] metricExportInterval];
BOOL exportMetrics = configurator.exportMetrics;
NSURL *metricsURLStr = configurator.metricURL;
NSUInteger metricExportInterval = configurator.metricExportInterval;
if ([arguments containsObject:@"--json"]) {
NSMutableDictionary *stats = [@{
@"daemon" : @{
@"driver_connected" : @(driverConnected),
@"driver_connected" : @(YES),
@"mode" : clientMode ?: @"null",
@"file_logging" : @(fileLogging),
@"watchdog_cpu_events" : @(cpuEvents),
@@ -200,6 +198,9 @@ REGISTER_COMMAND_NAME(@"status")
@"transitive_rules" : @(transitiveRuleCount),
@"events_pending_upload" : @(eventCount),
},
@"static_rules" : @{
@"rule_count" : @(staticRuleCount),
},
@"sync" : @{
@"server" : syncURLStr ?: @"null",
@"clean_required" : @(syncCleanReqd),
@@ -210,12 +211,12 @@ REGISTER_COMMAND_NAME(@"status")
@"transitive_rules" : @(enableTransitiveRules),
},
} mutableCopy];
if (cachingEnabled) {
stats[@"cache"] = @{
@"root_cache_count" : @(rootCacheCount),
@"non_root_cache_count" : @(nonRootCacheCount),
};
}
stats[@"cache"] = @{
@"root_cache_count" : @(rootCacheCount),
@"non_root_cache_count" : @(nonRootCacheCount),
};
NSData *statsData = [NSJSONSerialization dataWithJSONObject:stats
options:NSJSONWritingPrettyPrinted
error:nil];
@@ -223,21 +224,19 @@ REGISTER_COMMAND_NAME(@"status")
printf("%s\n", [statsStr UTF8String]);
} else {
printf(">>> Daemon Info\n");
printf(" %-25s | %s\n", "Driver Connected", driverConnected ? "Yes" : "No");
printf(" %-25s | %s\n", "Mode", [clientMode UTF8String]);
printf(" %-25s | %s\n", "File Logging", (fileLogging ? "Yes" : "No"));
printf(" %-25s | %lld (Peak: %.2f%%)\n", "Watchdog CPU Events", cpuEvents, cpuPeak);
printf(" %-25s | %lld (Peak: %.2fMB)\n", "Watchdog RAM Events", ramEvents, ramPeak);
printf(" %-25s | %s\n", "USB Blocking", (configurator.blockUSBMount ? "Yes" : "No"));
if (configurator.blockUSBMount && configurator.remountUSBMode.count > 0) {
printf(" %-25s | %s\n", "USB Remounting Mode:",
[[configurator.remountUSBMode componentsJoinedByString:@", "] UTF8String]);
}
printf(" %-25s | %lld (Peak: %.2f%%)\n", "Watchdog CPU Events", cpuEvents, cpuPeak);
printf(" %-25s | %lld (Peak: %.2fMB)\n", "Watchdog RAM Events", ramEvents, ramPeak);
if (cachingEnabled) {
printf(">>> Cache Info\n");
printf(" %-25s | %lld\n", "Root cache count", rootCacheCount);
}
printf(">>> Cache Info\n");
printf(" %-25s | %lld\n", "Root cache count", rootCacheCount);
printf(" %-25s | %lld\n", "Non-root cache count", nonRootCacheCount);
printf(">>> Database Info\n");
printf(" %-25s | %lld\n", "Binary Rules", binaryRuleCount);
@@ -247,6 +246,11 @@ REGISTER_COMMAND_NAME(@"status")
printf(" %-25s | %lld\n", "Transitive Rules", transitiveRuleCount);
printf(" %-25s | %lld\n", "Events Pending Upload", eventCount);
if ([SNTConfigurator configurator].staticRules.count) {
printf(">>> Static Rules\n");
printf(" %-25s | %lld\n", "Rules", staticRuleCount);
}
if (syncURLStr) {
printf(">>> Sync Info\n");
printf(" %-25s | %s\n", "Sync Server", [syncURLStr UTF8String]);

View File

@@ -18,14 +18,11 @@
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTDropRootPrivs.h"
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/common/SNTXPCSyncServiceInterface.h"
#import "Source/santactl/SNTCommand.h"
#import "Source/santactl/SNTCommandController.h"
#import "Source/santasyncservice/SNTSyncManager.h"
@interface SNTCommandSync : SNTCommand <SNTCommandProtocol>
@property MOLXPCConnection *listener;
@property SNTSyncManager *syncManager;
@interface SNTCommandSync : SNTCommand <SNTCommandProtocol, SNTSyncServiceLogReceiverXPC>
@end
@implementation SNTCommandSync
@@ -38,9 +35,8 @@ REGISTER_COMMAND_NAME(@"sync")
return YES;
}
// Connect to santad while we are root, so that we pass the XPC authentication.
+ (BOOL)requiresDaemonConn {
return YES;
return NO; // We talk directly with the syncservice.
}
+ (NSString *)shortHelpText {
@@ -66,53 +62,38 @@ REGISTER_COMMAND_NAME(@"sync")
LOGE(@"Missing SyncBaseURL. Exiting.");
exit(1);
}
MOLXPCConnection *ss = [SNTXPCSyncServiceInterface configuredConnection];
ss.invalidationHandler = ^(void) {
LOGE(@"Failed to connect to the sync service.");
exit(1);
};
[ss resume];
BOOL daemon = [arguments containsObject:@"--daemon"];
self.syncManager = [[SNTSyncManager alloc] initWithDaemonConnection:self.daemonConn
isDaemon:daemon];
NSXPCListener *logListener = [NSXPCListener anonymousListener];
MOLXPCConnection *lr = [[MOLXPCConnection alloc] initServerWithListener:logListener];
lr.exportedObject = self;
lr.unprivilegedInterface =
[NSXPCInterface interfaceWithProtocol:@protocol(SNTSyncServiceLogReceiverXPC)];
[lr resume];
BOOL isClean = [NSProcessInfo.processInfo.arguments containsObject:@"--clean"];
[[ss remoteObjectProxy]
syncWithLogListener:logListener.endpoint
isClean:isClean
reply:^(SNTSyncStatusType status) {
if (status == SNTSyncStatusTypeTooManySyncsInProgress) {
[self didReceiveLog:@"Too many syncs in progress, try again later."];
}
exit((int)status);
}];
// Dropping root privileges to the 'nobody' user causes the default NSURLCache to throw
// sandbox errors, which are benign but annoying. This line disables the cache entirely.
[NSURLCache setSharedURLCache:[[NSURLCache alloc] initWithMemoryCapacity:0
diskCapacity:0
diskPath:nil]];
if (!self.syncManager.daemon) return [self.syncManager fullSync];
[self syncdWithDaemonConnection:self.daemonConn];
// Do not return from this scope.
[[NSRunLoop mainRunLoop] run];
}
#pragma mark daemon methods
- (void)syncdWithDaemonConnection:(MOLXPCConnection *)daemonConn {
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
// Create listener for return connection from daemon.
NSXPCListener *listener = [NSXPCListener anonymousListener];
self.listener = [[MOLXPCConnection alloc] initServerWithListener:listener];
self.listener.privilegedInterface = [SNTXPCSyncdInterface syncdInterface];
self.listener.exportedObject = self.syncManager;
self.listener.acceptedHandler = ^{
LOGD(@"santad <--> santactl connections established");
dispatch_semaphore_signal(sema);
};
self.listener.invalidationHandler = ^{
// If santad is unloaded kill santactl
LOGD(@"exiting");
exit(0);
};
[self.listener resume];
// Tell daemon to connect back to the above listener.
[[daemonConn remoteObjectProxy] setSyncdListener:listener.endpoint];
// Now wait for the connection to come in.
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
self.listener.invalidationHandler = nil;
[self.listener invalidate];
[self performSelectorInBackground:@selector(syncdWithDaemonConnection:) withObject:daemonConn];
}
[self.syncManager fullSyncSecondsFromNow:15];
/// Implement the SNTSyncServiceLogReceiverXPC protocol.
- (void)didReceiveLog:(NSString *)log {
printf("%s\n", log.UTF8String);
fflush(stdout);
}
@end

View File

@@ -7,10 +7,10 @@
hostname | testHost
username | testUser
>>> Metrics
>>> Metrics
Metric Name | /santa/rules
Description | Number of rules
Type | SNTMetricTypeGaugeInt64 7
Type | SNTMetricTypeGaugeInt64
Field | rule_type=binary
Created | 2021-09-16T21:07:34.826Z
Last Updated | 2021-09-16T21:07:34.826Z
@@ -22,14 +22,14 @@
Metric Name | /proc/memory/resident_size
Description | The resident set size of this process
Type | SNTMetricTypeGaugeInt64 7
Type | SNTMetricTypeGaugeInt64
Created | 2021-09-16T21:07:34.826Z
Last Updated | 2021-09-16T21:07:34.826Z
Data | 123456789
Metric Name | /santa/events
Description | Count of process exec events on the host
Type | SNTMetricTypeCounter 9
Type | SNTMetricTypeCounter
Field | rule_type=binary
Created | 2021-09-16T21:07:34.826Z
Last Updated | 2021-09-16T21:07:34.826Z
@@ -41,28 +41,28 @@
Metric Name | /santa/using_endpoint_security_framework
Description | Is santad using the endpoint security framework
Type | SNTMetricTypeConstantBool 1
Type | SNTMetricTypeConstantBool
Created | 2021-09-16T21:07:34.826Z
Last Updated | 2021-09-16T21:07:34.826Z
Data | 1
Metric Name | /proc/birth_timestamp
Description | Start time of this santad instance, in microseconds since epoch
Type | SNTMetricTypeConstantInt64 3
Type | SNTMetricTypeConstantInt64
Created | 2021-09-16T21:07:34.826Z
Last Updated | 2021-09-16T21:07:34.826Z
Data | 1250999830800
Metric Name | /proc/memory/virtual_size
Description | The virtual memory size of this process
Type | SNTMetricTypeGaugeInt64 7
Type | SNTMetricTypeGaugeInt64
Created | 2021-09-16T21:07:34.826Z
Last Updated | 2021-09-16T21:07:34.826Z
Data | 987654321
Metric Name | /build/label
Description | Software version running
Type | SNTMetricTypeConstantString 2
Type | SNTMetricTypeConstantString
Created | 2021-09-16T21:07:34.826Z
Last Updated | 2021-09-16T21:07:34.826Z
Data | 20210809.0.1

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,7 @@
#import "Source/santad/DataLayer/SNTRuleTable.h"
#import <EndpointSecurity/EndpointSecurity.h>
#import <MOLCertificate/MOLCertificate.h>
#import <MOLCodesignChecker/MOLCodesignChecker.h>
@@ -29,6 +30,45 @@ static const NSUInteger kTransitiveRuleCullingThreshold = 500000;
// Consider transitive rules out of date if they haven't been used in six months.
static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
static void addPathsFromDefaultMuteSet(NSMutableSet *criticalPaths) API_AVAILABLE(macos(12.0)) {
// Note: This function uses API introduced in macOS 12, but we want to continue to support
// building in older environments. API Availability checks do not help for this use case,
// instead we use the following preprocessor macros to conditionally compile these API. The
// drawback here is that if a pre-macOS 12 SDK is used to build Santa and it is then deployed
// on macOS 12 or later, the dynamic mute set will not be computed.
#if defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0
// Create a temporary ES client in order to grab the default set of muted paths.
// TODO(mlw): Reorganize this code so that a temporary ES client doesn't need to be created
es_client_t *client = NULL;
es_new_client_result_t ret = es_new_client(&client, ^(es_client_t *c, const es_message_t *m){
// noop
});
if (ret != ES_NEW_CLIENT_RESULT_SUCCESS) {
// Creating the client failed, so we cannot grab the current default mute set.
LOGE(@"Failed to create client to grab default muted paths");
return;
}
es_muted_paths_t *mps = NULL;
if (es_muted_paths_events(client, &mps) != ES_RETURN_SUCCESS) {
LOGE(@"Failed to obtain list of default muted paths.");
es_delete_client(client);
return;
}
for (size_t i = 0; i < mps->count; i++) {
// Only add literal paths, prefix paths would require recursive directory search
if (mps->paths[i].type == ES_MUTE_PATH_TYPE_LITERAL) {
[criticalPaths addObject:@(mps->paths[i].path.data)];
}
}
es_release_muted_paths(mps);
es_delete_client(client);
#endif
}
@interface SNTRuleTable ()
@property MOLCodesignChecker *santadCSInfo;
@property MOLCodesignChecker *launchdCSInfo;
@@ -42,38 +82,67 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
// ES on Monterey now has a default mute set of paths that are automatically applied to each ES
// client. This mute set contains most (not all) AUTH event types for some paths that were deemed
// system critical.
// Retain this list for < 12.0 versions of ES, but we should be able to rely on the paths muted by
// default (visible with es_muted_paths_events any time after connecting a new client and before
// modifying any of the mute state).
+ (NSArray *)criticalSystemBinaryPaths {
return @[
@"/usr/libexec/trustd",
@"/usr/libexec/xpcproxy",
@"/usr/libexec/amfid",
@"/usr/libexec/opendirectoryd",
@"/usr/libexec/runningboardd",
@"/usr/libexec/syspolicyd",
@"/usr/libexec/watchdogd",
@"/usr/libexec/cfprefsd",
@"/usr/sbin/securityd",
@"/System/Library/PrivateFrameworks/TCC.framework/Versions/A/Resources/tccd",
@"/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/Resources/WindowServer",
@"/usr/sbin/ocspd",
@"/usr/lib/dyld",
@"/Applications/Santa.app/Contents/MacOS/Santa",
@"/Applications/Santa.app/Contents/MacOS/santactl",
@"/Applications/Santa.app/Contents/MacOS/santabundleservice",
// This entry is for on <10.15 - on 10.15+ the binary is actually executed
// from a system-controlled path but will only ever be executed by
// the OS anyway.
@"/Applications/Santa.app/Contents/Library/SystemExtensions/com.google.santa.daemon.systemextension/Contents/MacOS/com.google.santa.daemon",
];
static dispatch_once_t onceToken;
static NSArray *criticalPaths = nil;
dispatch_once(&onceToken, ^{
// These paths have previously existed in the ES default mute set. They are hardcoded
// here in case grabbing the current default mute set fails, or if Santa is running on
// an OS that did not yet support this feature.
NSSet *fallbackDefaultMuteSet = [[NSSet alloc] initWithArray:@[
@"/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/Resources/WindowServer",
@"/System/Library/PrivateFrameworks/TCC.framework/Support/tccd",
@"/System/Library/PrivateFrameworks/TCC.framework/Versions/A/Resources/tccd",
@"/usr/sbin/cfprefsd",
@"/usr/sbin/securityd",
@"/usr/libexec/opendirectoryd",
@"/usr/libexec/sandboxd",
@"/usr/libexec/syspolicyd",
@"/usr/libexec/runningboardd",
@"/usr/libexec/amfid",
@"/usr/libexec/watchdogd",
]];
// This is a Santa-curated list of paths to check on startup. This list will be merged
// with the set of default muted paths from ES.
NSSet *santaDefinedCriticalPaths = [NSSet setWithArray:@[
@"/usr/libexec/trustd",
@"/usr/lib/dyld",
@"/usr/libexec/xpcproxy",
@"/usr/sbin/ocspd",
@"/Applications/Santa.app/Contents/MacOS/Santa",
@"/Applications/Santa.app/Contents/MacOS/santactl",
@"/Applications/Santa.app/Contents/MacOS/santabundleservice",
@"/Applications/Santa.app/Contents/MacOS/santametricservice",
@"/Applications/Santa.app/Contents/MacOS/santasyncservice",
]];
// Combine the fallback default mute set and Santa-curated set
NSMutableSet *superSet = [NSMutableSet setWithSet:fallbackDefaultMuteSet];
[superSet unionSet:santaDefinedCriticalPaths];
if (@available(macOS 12.0, *)) {
// Attempt to add the real default mute set
addPathsFromDefaultMuteSet(superSet);
}
criticalPaths = [superSet allObjects];
});
return criticalPaths;
}
- (void)setupSystemCriticalBinaries {
NSMutableDictionary *bins = [NSMutableDictionary dictionary];
for (NSString *path in [SNTRuleTable criticalSystemBinaryPaths]) {
SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:path];
if (!binInfo.SHA256) {
// If there isn't a hash, no need to compute the other info here.
// Just continue on to the next binary.
LOGW(@"Unable to compute hash for critical system binary %@.", path);
continue;
}
MOLCodesignChecker *csInfo = [binInfo codesignCheckerWithError:NULL];
// Make sure the critical system binary is signed by the same chain as launchd/self
@@ -81,9 +150,9 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
if ([csInfo signingInformationMatches:self.launchdCSInfo]) {
systemBin = YES;
} else if (![csInfo signingInformationMatches:self.santadCSInfo]) {
LOGE(@"Unable to validate critical system binary. "
LOGW(@"Unable to validate critical system binary %@. "
@"pid 1: %@, santad: %@ and %@: %@ do not match.",
self.launchdCSInfo.leafCertificate, self.santadCSInfo.leafCertificate, path,
path, self.launchdCSInfo.leafCertificate, self.santadCSInfo.leafCertificate, path,
csInfo.leafCertificate);
continue;
}
@@ -215,6 +284,19 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
teamID:(NSString *)teamID {
__block SNTRule *rule;
// Look for a static rule that matches.
NSDictionary *staticRules = [[SNTConfigurator configurator] staticRules];
if (staticRules.count) {
rule = staticRules[binarySHA256];
if (rule.type == SNTRuleTypeBinary) return rule;
rule = staticRules[certificateSHA256];
if (rule.type == SNTRuleTypeCertificate) return rule;
rule = staticRules[teamID];
if (rule.type == SNTRuleTypeTeamID) return rule;
}
// Now query the database.
//
// NOTE: This code is written with the intention that the binary rule is searched for first
// as Santa is designed to go with the most-specific rule possible.
//

View File

@@ -0,0 +1,75 @@
/// 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.
#ifndef SANTA__SANTAD__EVENTPROVIDERS_AUTHRESULTCACHE_H
#define SANTA__SANTAD__EVENTPROVIDERS_AUTHRESULTCACHE_H
#include <EndpointSecurity/EndpointSecurity.h>
#import <Foundation/Foundation.h>
#include <dispatch/dispatch.h>
#include <sys/stat.h>
#include <memory>
#import "Source/common/SNTCommon.h"
#include "Source/common/SantaCache.h"
#include "Source/santad/EventProviders/EndpointSecurity/EndpointSecurityAPI.h"
namespace santa::santad::event_providers {
enum class FlushCacheMode {
kNonRootOnly,
kAllCaches,
};
class AuthResultCache {
public:
// Santa currently only flushes caches when new DENY rules are added, not
// ALLOW rules. This means this value should be low enough so that if a
// previously denied binary is allowed, it can be re-executed by the user in a
// timely manner. But the value should be high enough to allow the cache to be
// effective in the event the binary is executed in rapid succession.
AuthResultCache(
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi,
uint64_t cache_deny_time_ms = 1500);
virtual ~AuthResultCache();
AuthResultCache(AuthResultCache &&other) = delete;
AuthResultCache &operator=(AuthResultCache &&rhs) = delete;
AuthResultCache(const AuthResultCache &other) = delete;
AuthResultCache &operator=(const AuthResultCache &other) = delete;
virtual bool AddToCache(const es_file_t *es_file, santa_action_t decision);
virtual void RemoveFromCache(const es_file_t *es_file);
virtual santa_action_t CheckCache(const es_file_t *es_file);
virtual santa_action_t CheckCache(santa_vnode_id_t vnode_id);
virtual void FlushCache(FlushCacheMode mode);
virtual NSArray<NSNumber *> *CacheCounts();
private:
virtual SantaCache<santa_vnode_id_t, uint64_t> *CacheForVnodeID(santa_vnode_id_t vnode_id);
SantaCache<santa_vnode_id_t, uint64_t> *root_cache_;
SantaCache<santa_vnode_id_t, uint64_t> *nonroot_cache_;
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi_;
uint64_t root_devno_;
uint64_t cache_deny_time_ns_;
dispatch_queue_t q_;
};
} // namespace santa::santad::event_providers
#endif

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