From e044fe3601488d859b80a7beee48045c304df245 Mon Sep 17 00:00:00 2001 From: Eric Case Date: Fri, 24 Jun 2022 11:34:32 -0700 Subject: [PATCH 01/20] Readme: http -> https link (#829) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 650a12ff3..bb4e054b9 100644 --- a/README.md +++ b/README.md @@ -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. From b7421e4499cdd6aedca63e163a42894ec7fc57e2 Mon Sep 17 00:00:00 2001 From: np5 Date: Fri, 24 Jun 2022 20:00:55 +0000 Subject: [PATCH 02/20] Add team ID to synced events (#827) --- Source/common/SNTStoredEvent.h | 6 ++++++ Source/common/SNTStoredEvent.m | 2 ++ Source/santad/SNTExecutionController.m | 1 + Source/santasyncservice/SNTSyncConstants.h | 1 + Source/santasyncservice/SNTSyncConstants.m | 1 + Source/santasyncservice/SNTSyncEventUpload.m | 1 + Source/santasyncservice/SNTSyncTest.m | 2 ++ .../testdata/sync_eventupload_input_basic.plist | 6 ++++++ docs/details/events.md | 1 + 9 files changed, 21 insertions(+) diff --git a/Source/common/SNTStoredEvent.h b/Source/common/SNTStoredEvent.h index 7b114c986..d0a7a4aa6 100644 --- a/Source/common/SNTStoredEvent.h +++ b/Source/common/SNTStoredEvent.h @@ -95,6 +95,12 @@ /// @property NSArray *signingChain; + +/// +/// If the executed file was signed, this is the Team ID if present in the signature information. +/// +@property NSString *teamID; + /// /// The user who executed the binary. /// diff --git a/Source/common/SNTStoredEvent.m b/Source/common/SNTStoredEvent.m index c87f2c644..2a0907c48 100644 --- a/Source/common/SNTStoredEvent.m +++ b/Source/common/SNTStoredEvent.m @@ -49,6 +49,7 @@ - (void)encodeWithCoder:(NSCoder *)coder { ENCODE(self.fileBundleVersionString, @"fileBundleVersionString"); ENCODE(self.signingChain, @"signingChain"); + ENCODE(self.teamID, @"teamID"); ENCODE(self.executingUser, @"executingUser"); ENCODE(self.occurrenceDate, @"occurrenceDate"); @@ -93,6 +94,7 @@ - (instancetype)initWithCoder:(NSCoder *)decoder { _fileBundleVersionString = DECODE(NSString, @"fileBundleVersionString"); _signingChain = DECODEARRAY(MOLCertificate, @"signingChain"); + _teamID = DECODE(NSString, @"teamID"); _executingUser = DECODE(NSString, @"executingUser"); _occurrenceDate = DECODE(NSDate, @"occurrenceDate"); diff --git a/Source/santad/SNTExecutionController.m b/Source/santad/SNTExecutionController.m index a4e062f04..216d15dbe 100644 --- a/Source/santad/SNTExecutionController.m +++ b/Source/santad/SNTExecutionController.m @@ -207,6 +207,7 @@ - (void)validateBinaryWithMessage:(santa_message_t)message { se.decision = cd.decision; se.signingChain = cd.certChain; + se.teamID = cd.teamID; se.pid = @(message.pid); se.ppid = @(message.ppid); se.parentName = @(message.pname); diff --git a/Source/santasyncservice/SNTSyncConstants.h b/Source/santasyncservice/SNTSyncConstants.h index 63b6cb5ed..fb2a235b4 100644 --- a/Source/santasyncservice/SNTSyncConstants.h +++ b/Source/santasyncservice/SNTSyncConstants.h @@ -92,6 +92,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; diff --git a/Source/santasyncservice/SNTSyncConstants.m b/Source/santasyncservice/SNTSyncConstants.m index 2419cff1e..1d161d005 100644 --- a/Source/santasyncservice/SNTSyncConstants.m +++ b/Source/santasyncservice/SNTSyncConstants.m @@ -93,6 +93,7 @@ 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"; diff --git a/Source/santasyncservice/SNTSyncEventUpload.m b/Source/santasyncservice/SNTSyncEventUpload.m index bddada55c..58cb35110 100644 --- a/Source/santasyncservice/SNTSyncEventUpload.m +++ b/Source/santasyncservice/SNTSyncEventUpload.m @@ -149,6 +149,7 @@ - (NSDictionary *)dictionaryForEvent:(SNTStoredEvent *)event { [signingChain addObject:certDict]; } newEvent[kSigningChain] = signingChain; + ADDKEY(newEvent, kTeamID, event.teamID); return newEvent; #undef ADDKEY diff --git a/Source/santasyncservice/SNTSyncTest.m b/Source/santasyncservice/SNTSyncTest.m index 121fa8423..51b5066ff 100644 --- a/Source/santasyncservice/SNTSyncTest.m +++ b/Source/santasyncservice/SNTSyncTest.m @@ -340,6 +340,8 @@ - (void)testEventUploadBasic { XCTAssertEqualObjects(cert[kCertValidFrom], @(1365806075)); XCTAssertEqualObjects(cert[kCertValidUntil], @(1618266875)); + XCTAssertEqualObjects(event[kTeamID], @"012345678910"); + event = events[1]; XCTAssertEqualObjects(event[kFileName], @"hub"); XCTAssertEqualObjects(event[kExecutingUser], @"foouser"); diff --git a/Source/santasyncservice/testdata/sync_eventupload_input_basic.plist b/Source/santasyncservice/testdata/sync_eventupload_input_basic.plist index ff7981d8d..d219401c3 100644 --- a/Source/santasyncservice/testdata/sync_eventupload_input_basic.plist +++ b/Source/santasyncservice/testdata/sync_eventupload_input_basic.plist @@ -91,6 +91,11 @@ CF$UID 6 + teamID + + CF$UID + 39 + 14887 ff98fa0c0a1095fedcbe4d388a9760e71399a5c3c017a847ffa545663b57929a @@ -393,6 +398,7 @@ + 012345678910 $top diff --git a/docs/details/events.md b/docs/details/events.md index 345a54b1b..75a1009eb 100644 --- a/docs/details/events.md +++ b/docs/details/events.md @@ -71,6 +71,7 @@ JSON blob. Here is an example of Firefox being blocked and sent for upload: "sha256": "b0b1730ecbc7ff4505142c49f1295e6eda6bcaed7e2c68c5be91b5a11001f024" } ], + "team_id": "43AQ936H96", "file_bundle_name": "Firefox", "executing_user": "bur", "ppid": 1, From 7df209ed3f08b28bb3bc209d5878cff826bcd13b Mon Sep 17 00:00:00 2001 From: Russell Hancox Date: Tue, 28 Jun 2022 14:23:47 -0400 Subject: [PATCH 03/20] Project: Upgrade bazel rules_apple to 1.0.1 release (#830) --- WORKSPACE | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index feb10bfed..b86270c6e 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -5,14 +5,14 @@ load( "git_repository", "new_git_repository", ) +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -git_repository( +http_archive( name = "build_bazel_rules_apple", - commit = "7115f0188d141d57d64a6875735847c975956dae", # 0.34.0 - remote = "https://github.com/bazelbuild/rules_apple.git", + sha256 = "36072d4f3614d309d6a703da0dfe48684ec4c65a89611aeb9590b45af7a3e592", # 1.0.1 + urls = ["https://github.com/bazelbuild/rules_apple/releases/download/1.0.1/rules_apple.1.0.1.tar.gz"], ) -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "rules_proto_grpc", From d4a0d77cb97343dc9b65f15a17a0dbf245a4ce83 Mon Sep 17 00:00:00 2001 From: Russell Hancox Date: Fri, 1 Jul 2022 11:06:16 -0400 Subject: [PATCH 04/20] 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. --- docs/.gitignore | 6 ++++++ docs/Gemfile | 13 +++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 docs/.gitignore create mode 100644 docs/Gemfile diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..918de83ed --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,6 @@ +_site +.sass-cache +.jekyll-cache +.jekyll-metadata +vendor +Gemfile.lock diff --git a/docs/Gemfile b/docs/Gemfile new file mode 100644 index 000000000..433fd2cba --- /dev/null +++ b/docs/Gemfile @@ -0,0 +1,13 @@ +source "https://rubygems.org" +# Hello! This is where you manage which Jekyll version is used to run. +# When you want to use a different version, change it below, save the +# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: +# +# bundle exec jekyll serve +# +gem "github-pages", group: :jekyll_plugins + +# If you have any plugins, put them here! +group :jekyll_plugins do + gem "jekyll-feed", "~> 0.12" +end From aec1c74fab0854fc7e55e621ff0b779a82b14360 Mon Sep 17 00:00:00 2001 From: Matt W <436037+mlw@users.noreply.github.com> Date: Wed, 6 Jul 2022 21:51:02 -0400 Subject: [PATCH 05/20] Use the message copy in the dispatch blocks (#839) --- Source/santad/EventProviders/SNTDeviceManager.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/santad/EventProviders/SNTDeviceManager.mm b/Source/santad/EventProviders/SNTDeviceManager.mm index 065ebb865..b4c0a078e 100644 --- a/Source/santad/EventProviders/SNTDeviceManager.mm +++ b/Source/santad/EventProviders/SNTDeviceManager.mm @@ -313,21 +313,21 @@ - (void)handleESMessageWithTimeout:(const es_message_t *)m dispatch_semaphore_signal(processingSema); dispatch_semaphore_t deadlineExpiredSema = dispatch_semaphore_create(0); - if (m->action_type == ES_ACTION_TYPE_AUTH) { + if (mc->action_type == ES_ACTION_TYPE_AUTH) { dispatch_after(timeout, self.esAuthQueue, ^(void) { if (dispatch_semaphore_wait(processingSema, DISPATCH_TIME_NOW) != 0) { // Handler already responded, nothing to do. return; } LOGE(@"SNTDeviceManager: deadline reached: deny pid=%d ret=%d", - audit_token_to_pid(m->process->audit_token), - es_respond_auth_result(c, m, ES_AUTH_RESULT_DENY, false)); + audit_token_to_pid(mc->process->audit_token), + es_respond_auth_result(c, mc, ES_AUTH_RESULT_DENY, false)); dispatch_semaphore_signal(deadlineExpiredSema); }); } dispatch_async(self.esAuthQueue, ^{ - [self handleESMessage:m withClient:c]; + [self handleESMessage:mc withClient:c]; if (dispatch_semaphore_wait(processingSema, DISPATCH_TIME_NOW) != 0) { // Deadline expired, wait for deadline block to finish. dispatch_semaphore_wait(deadlineExpiredSema, DISPATCH_TIME_FOREVER); From 64096f5d08767eeb89900597e77dde930b7c85af Mon Sep 17 00:00:00 2001 From: Tom Burgin Date: Thu, 7 Jul 2022 17:09:53 -0400 Subject: [PATCH 06/20] adhoc build and run santa (#840) * adhoc build and run santa * fold ci into adhoc * review updates Co-authored-by: Tom Burgin --- .github/workflows/ci.yml | 4 +- .github/workflows/continuous.yml | 2 +- BUILD | 7 +- Source/common/BUILD | 4 + Source/common/SNTXPCControlInterface.m | 6 + Source/santa/BUILD | 8 +- Source/santa/Santa.app-adhoc.entitlements | 8 ++ Source/santa/Santa.app.entitlements | 16 --- Source/santabundleservice/BUILD | 2 +- Source/santactl/BUILD | 8 +- Source/santad/BUILD | 27 ++-- ....daemon.systemextension-adhoc.entitlements | 8 ++ ....santa.daemon.systemextension.entitlements | 16 --- Source/santametricservice/BUILD | 4 +- Source/santasyncservice/BUILD | 2 +- Testing/benchmark.sh | 2 +- docs/development/building.md | 124 +++++++----------- 17 files changed, 109 insertions(+), 139 deletions(-) create mode 100644 Source/santa/Santa.app-adhoc.entitlements delete mode 100644 Source/santa/Santa.app.entitlements create mode 100644 Source/santad/com.google.santa.daemon.systemextension-adhoc.entitlements delete mode 100644 Source/santad/com.google.santa.daemon.systemextension.entitlements diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e21c4738f..9600be0d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,7 +60,7 @@ jobs: 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: @@ -73,7 +73,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Run All Tests - run: bazel test :unit_tests --define=SANTA_BUILD_TYPE=ci --test_output=errors + run: bazel test :unit_tests --define=SANTA_BUILD_TYPE=adhoc --test_output=errors test_coverage: runs-on: macos-11 diff --git a/.github/workflows/continuous.yml b/.github/workflows/continuous.yml index 6e48111eb..813855267 100644 --- a/.github/workflows/continuous.yml +++ b/.github/workflows/continuous.yml @@ -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 diff --git a/BUILD b/BUILD index abe1cba7e..966c0402a 100644 --- a/BUILD +++ b/BUILD @@ -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"], ) diff --git a/Source/common/BUILD b/Source/common/BUILD index 57a910f22..4c7104f7b 100644 --- a/Source/common/BUILD +++ b/Source/common/BUILD @@ -197,6 +197,10 @@ objc_library( name = "SNTXPCControlInterface", srcs = ["SNTXPCControlInterface.m"], hdrs = ["SNTXPCControlInterface.h"], + defines = select({ + "//:adhoc_build": ["SANTAADHOC"], + "//conditions:default": None, + }), deps = [ ":SNTCommonEnums", ":SNTConfigurator", diff --git a/Source/common/SNTXPCControlInterface.m b/Source/common/SNTXPCControlInterface.m index 6168a6901..5d6929490 100644 --- a/Source/common/SNTXPCControlInterface.m +++ b/Source/common/SNTXPCControlInterface.m @@ -27,10 +27,16 @@ @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 { diff --git a/Source/santa/BUILD b/Source/santa/BUILD index aa2855dea..746dd7513 100644 --- a/Source/santa/BUILD +++ b/Source/santa/BUILD @@ -74,11 +74,15 @@ 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.15", provisioning_profile = select({ - "//:ci_build": None, + "//:adhoc_build": None, "//conditions:default": "//profiles:santa_dev", }), version = "//:version", diff --git a/Source/santa/Santa.app-adhoc.entitlements b/Source/santa/Santa.app-adhoc.entitlements new file mode 100644 index 000000000..8b98f922f --- /dev/null +++ b/Source/santa/Santa.app-adhoc.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.developer.system-extension.install + + + diff --git a/Source/santa/Santa.app.entitlements b/Source/santa/Santa.app.entitlements deleted file mode 100644 index 920ecb6ae..000000000 --- a/Source/santa/Santa.app.entitlements +++ /dev/null @@ -1,16 +0,0 @@ - - - - - com.apple.application-identifier - EQHXZ8M8AV.com.google.santa - com.apple.developer.system-extension.install - - com.apple.developer.team-identifier - EQHXZ8M8AV - keychain-access-groups - - EQHXZ8M8AV.com.google.santa - - - diff --git a/Source/santabundleservice/BUILD b/Source/santabundleservice/BUILD index 1f4b2b8c6..d04f50a98 100644 --- a/Source/santabundleservice/BUILD +++ b/Source/santabundleservice/BUILD @@ -36,7 +36,7 @@ macos_command_line_application( infoplists = ["Info.plist"], minimum_os_version = "10.15", provisioning_profile = select({ - "//:ci_build": None, + "//:adhoc_build": None, "//conditions:default": "//profiles:santa_dev", }), version = "//:version", diff --git a/Source/santactl/BUILD b/Source/santactl/BUILD index 3faa0181e..5aec32e7f 100644 --- a/Source/santactl/BUILD +++ b/Source/santactl/BUILD @@ -69,7 +69,7 @@ macos_command_line_application( infoplists = ["Info.plist"], minimum_os_version = "10.15", provisioning_profile = select({ - "//:ci_build": None, + "//:adhoc_build": None, "//conditions:default": "//profiles:santa_dev", }), version = "//:version", @@ -102,10 +102,10 @@ santa_unit_test( santa_unit_test( name = "SNTCommandMetricsTest", srcs = [ - "SNTCommand.h", - "SNTCommandController.h", "Commands/SNTCommandMetrics.h", "Commands/SNTCommandMetricsTest.m", + "SNTCommand.h", + "SNTCommandController.h", ], structured_resources = glob(["Commands/testdata/*"]), visibility = ["//:santa_package_group"], @@ -116,8 +116,8 @@ santa_unit_test( "//Source/common:SNTMetricSet", "//Source/common:SNTXPCControlInterface", "//Source/santametricservice/Formats:SNTMetricFormatTestHelper", - "@OCMock", "@MOLXPCConnection", + "@OCMock", ], ) diff --git a/Source/santad/BUILD b/Source/santad/BUILD index 384d75d6e..9a7c8bf57 100644 --- a/Source/santad/BUILD +++ b/Source/santad/BUILD @@ -127,8 +127,8 @@ objc_library( "//Source/common:SNTCachedDecision", "//Source/common:SNTCommon", "//Source/common:SNTConfigurator", - "//Source/common:SNTStoredEvent", "//Source/common:SNTLogging", + "//Source/common:SNTStoredEvent", ], ) @@ -150,13 +150,13 @@ objc_library( objc_library( name = "event_logs", + hdrs = ["Logs/SNTEventLog.h"], deps = [ ":file_event_logs", ":protobuf_event_logs", ":syslog_event_logs", "//Source/common:SNTCommon", ], - hdrs = ["Logs/SNTEventLog.h"], ) objc_library( @@ -197,11 +197,10 @@ objc_library( "IOKit", ], deps = [ + ":SNTApplicationCoreMetrics", ":database_controller", ":endpoint_security_manager", ":event_logs", - ":SNTApplicationCoreMetrics", - "//Source/common:SantaCache", "//Source/common:SNTAllowlistInfo", "//Source/common:SNTBlockMessage", "//Source/common:SNTCachedDecision", @@ -222,6 +221,7 @@ objc_library( "//Source/common:SNTXPCNotifierInterface", "//Source/common:SNTXPCSyncServiceInterface", "//Source/common:SNTXPCUnprivilegedControlInterface", + "//Source/common:SantaCache", "@FMDB", "@MOLCertificate", "@MOLCodesignChecker", @@ -287,11 +287,16 @@ macos_bundle( "--force", "--options library,kill,runtime", ], + entitlements = select({ + "//:adhoc_build": "com.google.santa.daemon.systemextension-adhoc.entitlements", + # Non-adhoc builds get their entitlements from the provisioning profile. + "//conditions:default": None, + }), infoplists = ["Info.plist"], linkopts = ["-execute"], minimum_os_version = "10.15", provisioning_profile = select({ - "//:ci_build": None, + "//:adhoc_build": None, "//conditions:default": "//profiles:daemon_dev", }), version = "//:version", @@ -302,11 +307,11 @@ macos_bundle( santa_unit_test( name = "SNTExecutionControllerTest", srcs = [ - "SNTExecutionController.h", "DataLayer/SNTDatabaseTable.h", "DataLayer/SNTEventTable.h", "DataLayer/SNTRuleTable.h", "EventProviders/SNTEventProvider.h", + "SNTExecutionController.h", "SNTExecutionControllerTest.m", ], sdk_dylibs = [ @@ -364,6 +369,9 @@ santa_unit_test( "DataLayer/SNTRuleTable.m", "DataLayer/SNTRuleTableTest.m", ], + sdk_dylibs = [ + "EndpointSecurity", + ], deps = [ "//Source/common:SNTCachedDecision", "//Source/common:SNTCommonEnums", @@ -375,9 +383,6 @@ santa_unit_test( "@MOLCertificate", "@MOLCodesignChecker", ], - sdk_dylibs = [ - "EndpointSecurity", - ], ) santa_unit_test( @@ -395,8 +400,8 @@ santa_unit_test( ], deps = [ ":EndpointSecurityTestLib", - "//Source/common:SNTConfigurator", "//Source/common:SNTCommon", + "//Source/common:SNTConfigurator", "//Source/common:SNTLogging", "//Source/common:SNTPrefixTree", "//Source/common:SantaCache", @@ -443,6 +448,7 @@ santa_unit_test( "EndpointSecurity", "bsm", ], + tags = ["exclusive"], deps = [ ":EndpointSecurityTestLib", ":santad_lib", @@ -453,7 +459,6 @@ santa_unit_test( "@MOLXPCConnection", "@OCMock", ], - tags = ["exclusive"], ) santa_unit_test( diff --git a/Source/santad/com.google.santa.daemon.systemextension-adhoc.entitlements b/Source/santad/com.google.santa.daemon.systemextension-adhoc.entitlements new file mode 100644 index 000000000..46a73e743 --- /dev/null +++ b/Source/santad/com.google.santa.daemon.systemextension-adhoc.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.developer.endpoint-security.client + + + diff --git a/Source/santad/com.google.santa.daemon.systemextension.entitlements b/Source/santad/com.google.santa.daemon.systemextension.entitlements deleted file mode 100644 index 7ec26d3bf..000000000 --- a/Source/santad/com.google.santa.daemon.systemextension.entitlements +++ /dev/null @@ -1,16 +0,0 @@ - - - - - com.apple.application-identifier - EQHXZ8M8AV.com.google.santa.daemon - com.apple.developer.endpoint-security.client - - com.apple.developer.team-identifier - EQHXZ8M8AV - keychain-access-groups - - EQHXZ8M8AV.com.google.santa.daemon - - - diff --git a/Source/santametricservice/BUILD b/Source/santametricservice/BUILD index b39449bef..25b69b6de 100644 --- a/Source/santametricservice/BUILD +++ b/Source/santametricservice/BUILD @@ -42,8 +42,8 @@ santa_unit_test( "//Source/common:SNTConfigurator", "//Source/common:SNTMetricSet", "//Source/santametricservice/Formats:SNTMetricFormatTestHelper", - "@OCMock", "@MOLAuthenticatingURLSession", + "@OCMock", ], ) @@ -67,7 +67,7 @@ macos_command_line_application( infoplists = ["Info.plist"], minimum_os_version = "10.15", provisioning_profile = select({ - "//:ci_build": None, + "//:adhoc_build": None, "//conditions:default": "//profiles:santa_dev", }), version = "//:version", diff --git a/Source/santasyncservice/BUILD b/Source/santasyncservice/BUILD index 3a7ae0daa..69a5b7933 100644 --- a/Source/santasyncservice/BUILD +++ b/Source/santasyncservice/BUILD @@ -162,7 +162,7 @@ macos_command_line_application( infoplists = ["Info.plist"], minimum_os_version = "10.15", provisioning_profile = select({ - "//:ci_build": None, + "//:adhoc_build": None, "//conditions:default": "//profiles:santa_dev", }), version = "//:version", diff --git a/Testing/benchmark.sh b/Testing/benchmark.sh index 6eff7e165..6d165abae 100755 --- a/Testing/benchmark.sh +++ b/Testing/benchmark.sh @@ -1,4 +1,4 @@ #!/bin/sh # TODO: Pull benchmarks from previous commit to check for regression -bazel test //:benchmarks --define=SANTA_BUILD_TYPE=ci +bazel test //:benchmarks --define=SANTA_BUILD_TYPE=adhoc diff --git a/docs/development/building.md b/docs/development/building.md index 3ca4f99ba..4ef444eb1 100644 --- a/docs/development/building.md +++ b/docs/development/building.md @@ -23,7 +23,7 @@ built from tagged commits, so if you wanted to build, run or test a specific release you can checkout that tag: ```sh -git checkout 0.9.33 +git checkout 2022.5 ``` If you want to list all the tags in reverse order: @@ -37,123 +37,89 @@ git tag --sort=-creatordate Build a debug version of Santa: ```sh -bazel build //Source/santa_driver +bazel build //Source/santa:Santa ``` -Build a release (optimized) version of Santa: +For developers who do not have access to Google's code signing certificate and +provisioning profiles, use the `--define=SANTA_BUILD_TYPE=adhoc` flag. This will +adhoc sign Santa and does not require provisioning profiles. + +Note: In order to run an adhoc signed Santa SIP must be disabled. See the +running section below. ```sh -bazel build //Source/santa_driver -c opt +bazel build //Source/santa:Santa --define=SANTA_BUILD_TYPE=adhoc ``` -The output for these commands will be a `santa-driver.zip` file under -`bazel-bin` which, when extracted, will contain all of Santa and should be -installed under `/Library/Extensions`. However, if you're working on Santa and -want a quick way to reload everything, see the next section. - #### Running When working on Santa, it's useful to have a way to quickly reload all of the Santa components. For this reason, there's a special rule in the Santa BUILD -file that will compile a new santa-driver, unload Santa if it's running, install -the new Santa in the right place and attempt to load it. +file that will build Santa, unload Santa if it's running, install the new +Santa in the right place and attempt to load it. + +Non-adhoc debug builds of Santa can only be run by Google developers. This is +because of bundle id and provisioning profile restrictions bound to Apple +developer accounts. + +```sh +bazel run :reload +``` -On macOS 10.11+ System Integrity Protection (SIP) prevents loading of kernel -extensions that are not signed by an Apple KEXT signing certificate. To be able -to load and test a non-release version of Santa, SIP will have to be configured -to allow non-Apple KEXT signing certificates. +Non-Google developers can use an adhoc build to run development builds of Santa. +System Integrity Protection (SIP) will need to be disabled in order to run an +adhoc build. -__This is only to be done a machine that is actively developing, unloading and -loading kernel extensions.__ +**This is only to be done a machine that is actively developing Santa.** -1. Boot into Recovery Mode: Reboot and hold down `command+r` +1. Boot into Recovery Mode: + * For Intel Macs reboot and hold down `command+r`. + * For Apple Silicon Macs press and hold the power button until “Loading + startup options” appears. Click Options, then click Continue. If asked, + select a volume to recover, then click Next. 2. From the utilities menu select `Terminal` -3. Disable the KEXT feature of SIP: `csrutil enable --without kext` +3. Disable the KEXT feature of SIP. The kext wording is legacy but the command + still works well for loading adhoc signed system extensions: `csrutil enable + --without kext` 4. Reboot You should now be able to load and run a non-release version of Santa. -Build and run a debug version of Santa. +Build and run an adhoc debug version of Santa. ```sh -bazel run :reload +bazel run :reload --define=SANTA_BUILD_TYPE=adhoc ``` -Build and run a release version of Santa. +Note: if you are currently running a release or non-adhoc dev build of Santa, +this new adhoc build will show up as a second instance of Santa. Remove the +non-adhoc instance like so: ```sh -bazel run :reload -c opt -``` - -#### Using Xcode - -While Bazel is a very convenient and powerful build system, it can still be -useful to use Xcode for actually working on the code. If you'd like to use Xcode -you can use [Tulsi](https://tulsi.bazel.build) to generate an `.xcodeproj` from -the BUILD file which will use Bazel for actually doing the builds. - -```sh -generate_xcodeproj.sh santa.tulsiproj +systemextensionsctl uninstall EQHXZ8M8AV com.google.santa.daemon ``` #### Debugging -Xcode and lldb can be used to debug Santa, similarly to any other project, with -some exceptions. Instead of clicking the play button to launch and attach to a -process, you can attach to an already running, or soon to by running, component -of Santa. To do this select the Debug menu and choose `Attach to Process by PID -or Name…`. Below are the four components of Santa and who to debug the process -as. - -Note: santa-driver (the kernel extension) cannot be debugged by attaching with -Xcode. - -Note: Xcode can attach to santad without interruption, however any breakpoints -in the decision making codepath can deadlock the machine. Using lldb directly to -attach to santad will deadlock the machine. - -process | user --------- | ---- -santad | root -Santa* | me -santactl | me -santabs | root - -Xcode will then wait for the process to start. Issue this command to restart all -the Santa processes in debug mode. - -*The Santa (GUI) process is the only component of Santa that can be launched and -debugged from Xcode directly. All the other components are launched with -privileges and/or are scoped to an XPC service that launchd scopes to a hosting -bundle. Thus the need for the `Attach to Process by PID or Name…` technique. See -the [ipc](../details/ipc.md) document for for details. - -```sh -bazel run :reload -``` - -Now the process is attached in Xcode and you can debug your day away. +lldb can be used to debug Santa, similarly to any other project, with some +exceptions. lldb can attach to com.google.santa.daemon, however any breakpoints +in the decision making codepath can deadlock the machine. #### Tests Run all the logic / unit tests ```sh -bazel test :unit_tests -``` - -Run all of santa-driver kernel extension tests - -```sh -bazel run //Source/santa_driver:kernel_tests +bazel test :unit_tests --define=SANTA_BUILD_TYPE=adhoc --test_output=errors ``` #### Releases Creates a release build of Santa with a version based of the newest tag. Also saves the dsym files for each component of Santa. This makes debugging and -interpreting future crashes or kernel panics much easier. +interpreting future crashes much easier. Releases are handled by Google internal +infrastructure. ```sh -bazel build :release -``` +bazel build --apple_generate_dsym -c opt :release +``` \ No newline at end of file From 7e2b2911224371ad3db57007683f70d8a009758f Mon Sep 17 00:00:00 2001 From: Kathryn Hancox <44557882+kathancox@users.noreply.github.com> Date: Fri, 8 Jul 2022 15:53:16 -0400 Subject: [PATCH 07/20] Docs: Updated home page with README files & nav changes (#841) --- docs/Gemfile | 1 + docs/{details => binaries}/block.png | Bin docs/binaries/index.md | 5 ++ docs/{details => binaries}/push.png | Bin docs/{details => binaries}/santa-gui.md | 2 +- docs/{details => binaries}/santabs.md | 2 +- docs/{details => binaries}/santactl.md | 4 +- docs/{details => binaries}/santad.md | 2 +- docs/{details => concepts}/events.md | 2 +- docs/{details => concepts}/index.md | 4 +- docs/{details => concepts}/ipc.md | 2 +- docs/{details => concepts}/logs.md | 2 +- docs/{details => concepts}/mode.md | 2 +- docs/{details => concepts}/rules.md | 2 +- docs/{details => concepts}/santa_ipc.png | Bin docs/{details => concepts}/scopes.md | 2 +- docs/deployment/troubleshooting.md | 8 +- docs/development/index.md | 2 +- docs/index.md | 71 ++++++++++-------- .../binary-authorization-overview.md | 4 +- docs/known-limitations.md | 10 +++ 21 files changed, 75 insertions(+), 52 deletions(-) rename docs/{details => binaries}/block.png (100%) create mode 100644 docs/binaries/index.md rename docs/{details => binaries}/push.png (100%) rename docs/{details => binaries}/santa-gui.md (95%) rename docs/{details => binaries}/santabs.md (99%) rename docs/{details => binaries}/santactl.md (99%) rename docs/{details => binaries}/santad.md (99%) rename docs/{details => concepts}/events.md (99%) rename docs/{details => concepts}/index.md (65%) rename docs/{details => concepts}/ipc.md (99%) rename docs/{details => concepts}/logs.md (98%) rename docs/{details => concepts}/mode.md (99%) rename docs/{details => concepts}/rules.md (99%) rename docs/{details => concepts}/santa_ipc.png (100%) rename docs/{details => concepts}/scopes.md (98%) create mode 100644 docs/known-limitations.md diff --git a/docs/Gemfile b/docs/Gemfile index 433fd2cba..f96736c74 100644 --- a/docs/Gemfile +++ b/docs/Gemfile @@ -11,3 +11,4 @@ gem "github-pages", group: :jekyll_plugins group :jekyll_plugins do gem "jekyll-feed", "~> 0.12" end +gem "webrick", "~> 1.7" diff --git a/docs/details/block.png b/docs/binaries/block.png similarity index 100% rename from docs/details/block.png rename to docs/binaries/block.png diff --git a/docs/binaries/index.md b/docs/binaries/index.md new file mode 100644 index 000000000..1ff33711d --- /dev/null +++ b/docs/binaries/index.md @@ -0,0 +1,5 @@ +--- +title: Binaries +has_children: true +nav_order: 5 +--- \ No newline at end of file diff --git a/docs/details/push.png b/docs/binaries/push.png similarity index 100% rename from docs/details/push.png rename to docs/binaries/push.png diff --git a/docs/details/santa-gui.md b/docs/binaries/santa-gui.md similarity index 95% rename from docs/details/santa-gui.md rename to docs/binaries/santa-gui.md index 1a99642e8..20331abdc 100644 --- a/docs/details/santa-gui.md +++ b/docs/binaries/santa-gui.md @@ -1,5 +1,5 @@ --- -parent: Details +parent: Binaries --- # Santa GUI diff --git a/docs/details/santabs.md b/docs/binaries/santabs.md similarity index 99% rename from docs/details/santabs.md rename to docs/binaries/santabs.md index d5e6c1307..1b88d911d 100644 --- a/docs/details/santabs.md +++ b/docs/binaries/santabs.md @@ -1,5 +1,5 @@ --- -parent: Details +parent: Binaries --- # santabs diff --git a/docs/details/santactl.md b/docs/binaries/santactl.md similarity index 99% rename from docs/details/santactl.md rename to docs/binaries/santactl.md index 62d97e950..9d898f71a 100644 --- a/docs/details/santactl.md +++ b/docs/binaries/santactl.md @@ -1,5 +1,5 @@ --- -parent: Details +parent: Binaries --- # santactl @@ -367,7 +367,7 @@ Recursive lookups of an application or directory is a soon to be added feature ##### rule -The rule command is covered in the [Rules](rules.md) document. +The rule command is covered in the [Rules](../concepts/rules.md) document. ##### sync diff --git a/docs/details/santad.md b/docs/binaries/santad.md similarity index 99% rename from docs/details/santad.md rename to docs/binaries/santad.md index 7671f3996..701e37593 100644 --- a/docs/details/santad.md +++ b/docs/binaries/santad.md @@ -1,5 +1,5 @@ --- -parent: Details +parent: Binaries --- # santad diff --git a/docs/details/events.md b/docs/concepts/events.md similarity index 99% rename from docs/details/events.md rename to docs/concepts/events.md index 75a1009eb..4998e6d37 100644 --- a/docs/details/events.md +++ b/docs/concepts/events.md @@ -1,5 +1,5 @@ --- -parent: Details +parent: Concepts --- # Events diff --git a/docs/details/index.md b/docs/concepts/index.md similarity index 65% rename from docs/details/index.md rename to docs/concepts/index.md index 0263a914d..1712ed472 100644 --- a/docs/details/index.md +++ b/docs/concepts/index.md @@ -1,5 +1,5 @@ --- -title: Details +title: Concepts has_children: true nav_order: 4 ---- +--- \ No newline at end of file diff --git a/docs/details/ipc.md b/docs/concepts/ipc.md similarity index 99% rename from docs/details/ipc.md rename to docs/concepts/ipc.md index 4452779c9..5cc89cd42 100644 --- a/docs/details/ipc.md +++ b/docs/concepts/ipc.md @@ -1,5 +1,5 @@ --- -parent: Details +parent: Concepts --- # Interprocess Communication (IPC) diff --git a/docs/details/logs.md b/docs/concepts/logs.md similarity index 98% rename from docs/details/logs.md rename to docs/concepts/logs.md index dfa55bd97..bd79c15f7 100644 --- a/docs/details/logs.md +++ b/docs/concepts/logs.md @@ -1,5 +1,5 @@ --- -parent: Details +parent: Concepts --- # Logs diff --git a/docs/details/mode.md b/docs/concepts/mode.md similarity index 99% rename from docs/details/mode.md rename to docs/concepts/mode.md index db0986fde..6833ba77e 100644 --- a/docs/details/mode.md +++ b/docs/concepts/mode.md @@ -1,5 +1,5 @@ --- -parent: Details +parent: Concepts --- # Mode diff --git a/docs/details/rules.md b/docs/concepts/rules.md similarity index 99% rename from docs/details/rules.md rename to docs/concepts/rules.md index 3af765ebc..bc9273b0a 100644 --- a/docs/details/rules.md +++ b/docs/concepts/rules.md @@ -1,5 +1,5 @@ --- -parent: Details +parent: Concepts --- # Rules diff --git a/docs/details/santa_ipc.png b/docs/concepts/santa_ipc.png similarity index 100% rename from docs/details/santa_ipc.png rename to docs/concepts/santa_ipc.png diff --git a/docs/details/scopes.md b/docs/concepts/scopes.md similarity index 98% rename from docs/details/scopes.md rename to docs/concepts/scopes.md index 79203916e..d9851d2c4 100644 --- a/docs/details/scopes.md +++ b/docs/concepts/scopes.md @@ -1,5 +1,5 @@ --- -parent: Details +parent: Concepts --- # Scopes diff --git a/docs/deployment/troubleshooting.md b/docs/deployment/troubleshooting.md index bee098264..d28c9f673 100644 --- a/docs/deployment/troubleshooting.md +++ b/docs/deployment/troubleshooting.md @@ -10,7 +10,7 @@ this page will cover troublshooting the system extension and related topics. ## Confirming Status -While there's an entire page on [santactl](../details/santactl.md), it's one of the best ways to start +While there's an entire page on [santactl](../binaries/santactl.md), it's one of the best ways to start determining the cause of an issue: ```sh @@ -49,11 +49,11 @@ Santa as well as its details and live connection state ## Confirming Actions -Looking into [logs](../details/logs.md) would be instructive for the majority -of how Santa is operating, and the pages on [scopes](../details/scopes.md) and [rules](../details/rules.md) would assist in +Looking into [logs](../concepts/logs.md) would be instructive for the majority +of how Santa is operating, and the pages on [scopes](../concepts/scopes.md) and [rules](../concepts/rules.md) would assist in determining precendence and why decisions are made. Most helpful is the output of `/usr/local/bin/santactl`'s `fileinfo` verb when called with the path/binary in -question as described on the [santactl](../details/santactl.md) page. +question as described on the [santactl](../binaries/santactl.md) page. Depending on the presence or implementation details of a sync server, there may be queues and a process for allowing binaries or updated developer certificates. diff --git a/docs/development/index.md b/docs/development/index.md index 99e9c983b..98c686eca 100644 --- a/docs/development/index.md +++ b/docs/development/index.md @@ -1,5 +1,5 @@ --- title: Development has_children: true -nav_order: 5 +nav_order: 6 --- diff --git a/docs/index.md b/docs/index.md index a9d89d6f7..2bde756af 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,51 +3,58 @@ title: Home nav_order: 1 --- -# Welcome +# Welcome to the Santa documentation -Santa is a binary authorization system for macOS. Here you will find the -documentation for understanding how Santa works, how to deploy it and how to -contribute. +Santa is a binary authorization system for macOS. It consists of a system extension that allows or denies attempted executions using a set of rules stored in a local database, a GUI agent that notifies the user in case of a block decision, a sync daemon responsible for syncing the database and a server, and a command-line utility for managing the system. -#### Introduction +It is named Santa because it keeps track of binaries that are naughty or nice. -The following documents give an overview of how Santa accomplishes binary -authorization at the enterprise scale. +## Features -* [Binary Authorization](introduction/binary-authorization-overview.md): How Santa makes allow or deny decisions for any `execve()` taking place. -* [Syncing](introduction/syncing-overview.md): How configuration and rules are applied from a sync server. +* [**Multiple modes:**](concepts/mode.md) In the default `MONITOR` mode, all binaries except those marked as blocked will be allowed to run, whilst being logged and recorded in the events database. In `LOCKDOWN` mode, only listed binaries are allowed to run. +* [**Event logging:**](concepts/events.md) All binary launches are logged. When in either mode, all unknown or denied binaries are stored in the database to enable later aggregation. +* [**Certificate-based rules, with override levels:**](concepts/rules.md) Instead of relying on a binary's hash (or 'fingerprint'), executables can be allowed/blocked by their signing certificate. You can therefore allow/block all binaries by a given publisher that were signed with that cert across version updates. A binary can only be allowed by its certificate if its signature validates correctly but a rule for a binary's fingerprint will override a decision for a certificate; i.e. you can allowlist a certificate while blocking a binary signed with that certificate, or vice-versa. +* **Path-based rules (via NSRegularExpression/ICU):** Binaries can be allowed/blocked based on the path they are launched from by matching against a configurable regex. +* [**Failsafe cert rules:**](concepts/rules.md#built-in-rules) You cannot put in a deny rule that would block the certificate used to sign launchd, a.k.a. pid 1, and therefore all components used in macOS. The binaries in every OS update (and in some cases entire new versions) are therefore automatically allowed. This does not affect binaries from Apple's App Store, which use various certs that change regularly for common apps. Likewise, you cannot block Santa itself. +* [**Components validate each other:**](binaries/index.md) Each of the components (the daemons, the GUI agent, and the command-line utility) communicate with each other using XPC and check that their signing certificates are identical before any communication is accepted. +* **Caching:** Allowed binaries are cached so the processing required to make a request is only done if the binary isn't already cached. -#### Deployment +## Documentation overview -* [Configuration](deployment/configuration.md): The local and sync server configuration options, along with example needed mobileconfig files. -* [Troubleshooting](deployment/troubleshooting.md): While there are numerous pages with details on Santa, admins may appreciate a central place to branch off from with common practical issues. +### Introduction -#### Development +The following pages give an overview of how Santa accomplishes authorization at enterprise scale. -* [Building Santa](development/building.md): How to build and load Santa for testing on a development machine. -* [Contributing](development/contributing.md): How to contribute a bug fix or new feature to Santa. +* [Binary Authorization](introduction/binary-authorization-overview.md): How Santa makes allow or deny decisions for any execution taking place. +* [Syncing](introduction/syncing-overview.md): How configuration and rules are applied from a sync server. + +### Deployment + +* [Configuration](deployment/configuration.md): The local and sync server configuration options, along with example needed mobileconfig files. +* [Troubleshooting](deployment/troubleshooting.md): How to troubleshoot issues with your Santa deployment. -#### Details +### Concepts -For those who want even more details on how Santa works under the hood, this section is for you. +Additional documentation on the concepts that support the operation of the main components: -###### Binaries +* [mode](concepts/mode.md): An operating mode, either Monitor or Lockdown. +* [events](concepts/events.md): Represents an `execve()` that was blocked, or would have been blocked, depending on the mode. +* [rules](concepts/rules.md): Represents allow or deny decisions for a given `execve()`. Can either be a binary's SHA-256 hash or a leaf code-signing certificate's SHA-256 hash. +* [scopes](concepts/scopes.md): The level at which an `execve()` was allowed or denied from taking place. +* [ipc](concepts/ipc.md): How all the components of Santa communicate. + duction/syncing-overview. +* [logs](concepts/logs.md): What and where Santa logs. -There are five main components that make up Santa whose core functionality is described in snippets below. For additional detail on each component, visit their respective pages. These quick descriptions do not encompass all the jobs performed by each component, but do provide a quick look at the basic functionality utilized to achieve the goal of binary authorization. +### Binaries -* [santad](details/santad.md): A user-land root daemon that makes decisions. -* [santactl](details/santactl.md): A user-land anonymous daemon that communicates with a sync server for configurations and policies. santactl can also be used by a user to manually configure Santa when using the local configuration. -* [santa-gui](details/santa-gui.md): A user-land GUI daemon that displays notifications when an `execve()` is blocked. -* [santabs](details/santabs.md): A user-land root daemon that finds Mach-O binaries within a bundle and creates events for them. +The following pages describe the main components that make up Santa: -###### Concepts +* [santad](binaries/santad.md): A root daemon that makes decisions. +* [santactl](binaries/santactl.md): A command-line utility for inspecting the state and managing local configuration of Santa. +* [santa-gui](binaries/santa-gui.md): A GUI daemon that displays notifications when an execution is blocked. +* [santabs](binaries/santabs.md): A root daemon that finds binaries within a bundle to allow for easier rule-creation of bundled applications. -Additional documentation on the concepts that support the operation of the main components: +### Development -* [mode](details/mode.md): An operating mode, either Monitor or Lockdown. -* [events](details/events.md): Represents an `execve()` that was blocked, or would have been blocked, depending on the mode. -* [rules](details/rules.md): Represents allow or deny decisions for a given `execve()`. Can either be a binary's SHA-256 hash or a leaf code-signing certificate's SHA-256 hash. -* [scopes](details/scopes.md): The level at which an `execve()` was allowed or denied from taking place. -* [ipc](details/ipc.md): How all the components of Santa communicate. - duction/syncing-overview. -* [logs](details/logs.md): What and where Santa logs. +* [Building Santa](development/building.md): How to build and load Santa for testing on a development machine. +* [Contributing](development/contributing.md): How to contribute a bug fix or new feature to Santa. \ No newline at end of file diff --git a/docs/introduction/binary-authorization-overview.md b/docs/introduction/binary-authorization-overview.md index 3734f42c5..1573832f0 100644 --- a/docs/introduction/binary-authorization-overview.md +++ b/docs/introduction/binary-authorization-overview.md @@ -24,7 +24,7 @@ directly or indirectly, on the operations being performed. This is a high level overview of the binary authorization decision process. For a more detailed account of each part, see the respective documentation. This flow does not cover the logging component of Santa, see the -[logs.md](../details/logs.md) documentation for more info. +[logs.md](../concepts/logs.md) documentation for more info. ###### Kernel Space @@ -62,7 +62,7 @@ documentation. This flow does not cover the logging component of Santa, see the a decision, extra care is taken to be as performant as possible. 2. santad uses the information it has gathered to make a decision to allow or deny the `execve()`. There are more details on how these decisions are made - in the [rules.md](../details/rules.md) and [scopes.md](../details/scopes.md) + in the [rules.md](../concepts/rules.md) and [scopes.md](../concepts/scopes.md) documents. 3. The decision is posted back to santa-driver. 4. If there was a deny decision, a message is sent to Santa GUI to display a diff --git a/docs/known-limitations.md b/docs/known-limitations.md new file mode 100644 index 000000000..9ca573eb2 --- /dev/null +++ b/docs/known-limitations.md @@ -0,0 +1,10 @@ +--- +title: Known Limitations +nav_order: 7 +--- + +## Known limitations + +- Santa only blocks execution (execve and variants), it doesn't protect against dynamic libraries loaded with dlopen, libraries on disk that have been replaced, or libraries loaded using `DYLD_INSERT_LIBRARIES`. + +- Scripts: Santa is currently written to ignore any execution that isn't a binary. After weighing the administration cost versus the benefit, we found it wasn't worthwhile to manage the execution of scripts. Additionally, a number of applications make use of temporary generated scripts and blocking these could cause problems. We're happy to revisit this (or at least make it an option) if it would be useful to others. \ No newline at end of file From 6d911e9d6e8dfa542158158b4b92dd9c0c2049ad Mon Sep 17 00:00:00 2001 From: Russell Hancox Date: Fri, 8 Jul 2022 16:03:30 -0400 Subject: [PATCH 08/20] CI: Make CI workflow only run on source changes (#843) --- .github/workflows/ci.yml | 47 +++++----------------------------------- 1 file changed, 5 insertions(+), 42 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9600be0d1..42f4d3057 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 @@ -55,8 +26,6 @@ jobs: matrix: 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 @@ -68,8 +37,6 @@ jobs: matrix: 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 @@ -77,8 +44,6 @@ jobs: 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 @@ -92,8 +57,6 @@ jobs: 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 From ba1ace56f0b7403255000f845152c7bb0106901a Mon Sep 17 00:00:00 2001 From: Russell Hancox Date: Tue, 12 Jul 2022 13:53:57 -0400 Subject: [PATCH 09/20] Project: Delete tulsiproj, add basic doc about hedron (#845) --- docs/development/building.md | 15 ++- santa.tulsiproj/Configs/Santa.tulsigen | 123 ------------------------- santa.tulsiproj/project.tulsiconf | 24 ----- 3 files changed, 14 insertions(+), 148 deletions(-) delete mode 100644 santa.tulsiproj/Configs/Santa.tulsigen delete mode 100644 santa.tulsiproj/project.tulsiconf diff --git a/docs/development/building.md b/docs/development/building.md index 4ef444eb1..6a505e1bf 100644 --- a/docs/development/building.md +++ b/docs/development/building.md @@ -99,6 +99,19 @@ non-adhoc instance like so: systemextensionsctl uninstall EQHXZ8M8AV com.google.santa.daemon ``` +#### IDE Setup + +We don't generally use Xcode when working on Santa but it's very useful to be +able to use an IDE when developing. We generally use clangd for this, using a +tool that will extract the appropriate compile commands automatically from our +Bazel build rules. To use this: + +1) Run `bazel run @hedron_compile_commands//:refresh_all` to generate the + `compile_commands.json` file. + +2) Follow the [instructions](https://github.com/hedronvision/bazel-compile-commands-extractor#editor-setup--for-autocomplete-based-on-compile_commandsjson) + for setting up your editor. + #### Debugging lldb can be used to debug Santa, similarly to any other project, with some @@ -122,4 +135,4 @@ infrastructure. ```sh bazel build --apple_generate_dsym -c opt :release -``` \ No newline at end of file +``` diff --git a/santa.tulsiproj/Configs/Santa.tulsigen b/santa.tulsiproj/Configs/Santa.tulsigen deleted file mode 100644 index e85a637c2..000000000 --- a/santa.tulsiproj/Configs/Santa.tulsigen +++ /dev/null @@ -1,123 +0,0 @@ -{ - "additionalFilePaths" : [ - "Source/santa/BUILD", - "Source/santactl/BUILD", - "Source/santabundleservice/BUILD", - "Source/santa_driver/BUILD", - "Source/common/BUILD", - "Source/santad/BUILD", - "/BUILD", - "Source/santasyncservice/BUILD" - ], - "buildTargets" : [ - "//:benchmarks", - "//:unit_tests", - "//Source/santa:Santa", - "//Source/santa_driver:kernel_tests_bin", - "//Source/santabundleservice:santabundleservice", - "//Source/santactl:santactl", - "//Source/santad:com.google.santa.daemon", - "//Source/santasyncservice:santasyncservice" - ], - "optionSet" : { - "BazelBuildOptionsDebug" : { - "p" : "$(inherited)" - }, - "BazelBuildOptionsRelease" : { - "p" : "$(inherited)" - }, - "BazelBuildStartupOptionsDebug" : { - "p" : "$(inherited)" - }, - "BazelBuildStartupOptionsRelease" : { - "p" : "$(inherited)" - }, - "BuildActionPostActionScript" : { - "p" : "$(inherited)" - }, - "BuildActionPreActionScript" : { - "p" : "$(inherited)" - }, - "CommandlineArguments" : { - "p" : "$(inherited)" - }, - "EnvironmentVariables" : { - "p" : "$(inherited)" - }, - "LaunchActionPostActionScript" : { - "p" : "$(inherited)" - }, - "LaunchActionPreActionScript" : { - "p" : "$(inherited)" - }, - "ProjectGenerationBazelStartupOptions" : { - "p" : "$(inherited)" - }, - "TestActionPostActionScript" : { - "p" : "$(inherited)" - }, - "TestActionPreActionScript" : { - "p" : "$(inherited)" - } - }, - "projectName" : "santa", - "sourceFilters" : [ - "Source/...", - "Source/common", - "Source/santa", - "Source/santa/Resources", - "Source/santabundleservice", - "Source/santactl", - "Source/santactl/Commands", - "Source/santactl/Commands/sync", - "Source/santad", - "Source/santad/DataLayer", - "Source/santad/EventProviders", - "Source/santad/Logs", - "external/...", - "external/FMDB", - "external/FMDB/src", - "external/FMDB/src/fmdb", - "external/MOLAuthenticatingURLSession", - "external/MOLAuthenticatingURLSession/Source", - "external/MOLAuthenticatingURLSession/Source/MOLAuthenticatingURLSession", - "external/MOLCertificate", - "external/MOLCertificate/Source", - "external/MOLCertificate/Source/MOLCertificate", - "external/MOLCodesignChecker", - "external/MOLCodesignChecker/Source", - "external/MOLCodesignChecker/Source/MOLCodesignChecker", - "external/MOLXPCConnection", - "external/MOLXPCConnection/Source", - "external/MOLXPCConnection/Source/MOLXPCConnection", - "external/bazel_skylib", - "external/bazel_skylib/lib", - "external/bazel_skylib/rules", - "external/bazel_tools", - "external/bazel_tools/tools", - "external/bazel_tools/tools/build_defs", - "external/bazel_tools/tools/build_defs/cc", - "external/build_bazel_apple_support", - "external/build_bazel_apple_support/lib", - "external/build_bazel_rules_apple", - "external/build_bazel_rules_apple/apple", - "external/build_bazel_rules_apple/apple/internal", - "external/build_bazel_rules_apple/apple/internal/aspects", - "external/build_bazel_rules_apple/apple/internal/partials", - "external/build_bazel_rules_apple/apple/internal/partials/support", - "external/build_bazel_rules_apple/apple/internal/resource_actions", - "external/build_bazel_rules_apple/apple/internal/resource_rules", - "external/build_bazel_rules_apple/apple/internal/testing", - "external/build_bazel_rules_apple/apple/internal/utils", - "external/build_bazel_rules_swift", - "external/build_bazel_rules_swift/swift", - "external/build_bazel_rules_swift/swift/internal", - "external/rules_cc", - "external/rules_cc/cc", - "external/rules_cc/cc/private", - "external/rules_cc/cc/private/rules_impl", - "external/rules_proto", - "external/rules_proto/proto", - "external/rules_proto/proto/private" - ] -} diff --git a/santa.tulsiproj/project.tulsiconf b/santa.tulsiproj/project.tulsiconf deleted file mode 100644 index 618bc6439..000000000 --- a/santa.tulsiproj/project.tulsiconf +++ /dev/null @@ -1,24 +0,0 @@ -{ - "configDefaults" : { - "optionSet" : { - "ALWAYS_SEARCH_USER_PATHS" : { - "p" : "YES" - }, - "GenerateRunfiles" : { - "p" : "YES" - } - } - }, - "packages" : [ - "", - "Source/common", - "Source/santa", - "Source/santa_driver", - "Source/santabundleservice", - "Source/santactl", - "Source/santad", - "Source/santasyncservice" - ], - "projectName" : "santa", - "workspaceRoot" : ".." -} From 1cc40d59d82912aa4b8d51d2baf4326503229320 Mon Sep 17 00:00:00 2001 From: Russell Hancox Date: Wed, 13 Jul 2022 17:58:13 -0400 Subject: [PATCH 10/20] santad: Allow configuring a static set of rules via configuration profile (#846) --- Source/common/BUILD | 18 ++- Source/common/SNTConfigurator.h | 28 +++++ Source/common/SNTConfigurator.m | 26 ++++ Source/common/SNTRule.h | 5 + Source/common/SNTRule.m | 53 ++++++++ Source/common/SNTRuleTest.m | 116 ++++++++++++++++++ .../SNTSyncConstants.h | 0 .../SNTSyncConstants.m | 2 +- Source/santactl/Commands/SNTCommandRule.m | 11 +- Source/santactl/Commands/SNTCommandStatus.m | 16 ++- Source/santad/DataLayer/SNTRuleTable.m | 13 ++ Source/santametricservice/Writers/BUILD | 2 +- Source/santasyncservice/BUILD | 6 +- .../santasyncservice/SNTPushNotifications.m | 2 +- .../SNTPushNotificationsTracker.m | 2 +- Source/santasyncservice/SNTSyncEventUpload.m | 2 +- Source/santasyncservice/SNTSyncManager.m | 2 +- Source/santasyncservice/SNTSyncPostflight.m | 2 +- Source/santasyncservice/SNTSyncPreflight.m | 2 +- Source/santasyncservice/SNTSyncRuleDownload.h | 1 - Source/santasyncservice/SNTSyncRuleDownload.m | 59 +-------- Source/santasyncservice/SNTSyncStage.m | 2 +- Source/santasyncservice/SNTSyncTest.m | 2 +- WORKSPACE | 3 +- profiles/BUILD | 8 +- 25 files changed, 299 insertions(+), 84 deletions(-) create mode 100644 Source/common/SNTRuleTest.m rename Source/{santasyncservice => common}/SNTSyncConstants.h (100%) rename Source/{santasyncservice => common}/SNTSyncConstants.m (99%) diff --git a/Source/common/BUILD b/Source/common/BUILD index 4c7104f7b..aaae5ffee 100644 --- a/Source/common/BUILD +++ b/Source/common/BUILD @@ -100,6 +100,7 @@ objc_library( hdrs = ["SNTConfigurator.h"], deps = [ ":SNTCommonEnums", + ":SNTRule", ":SNTStrengthify", ":SNTSystemInfo", ], @@ -149,7 +150,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,6 +177,12 @@ cc_library( hdrs = ["SNTStrengthify.h"], ) +objc_library( + name = "SNTSyncConstants", + srcs = ["SNTSyncConstants.m"], + hdrs = ["SNTSyncConstants.h"], +) + objc_library( name = "SNTSystemInfo", srcs = ["SNTSystemInfo.m"], diff --git a/Source/common/SNTConfigurator.h b/Source/common/SNTConfigurator.h index eb69a8825..78b6426ff 100644 --- a/Source/common/SNTConfigurator.h +++ b/Source/common/SNTConfigurator.h @@ -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,32 @@ /// @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: +/// +/// StaticRules +/// +/// +/// identifier +/// binary sha256, certificate sha256, team ID +/// rule_type +/// BINARY (one of BINARY, CERTIFICATE or TEAMID) +/// policy +/// BLOCKLIST (one of ALLOWLIST, ALLOWLIST_COMPILER, BLOCKLIST, SILENT_BLOCKLIST) +/// +/// +/// +/// 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 *staticRules; + /// /// The regex of allowed paths. Regexes are specified in ICU format. /// diff --git a/Source/common/SNTConfigurator.m b/Source/common/SNTConfigurator.m index 25ea12ebe..576543947 100644 --- a/Source/common/SNTConfigurator.m +++ b/Source/common/SNTConfigurator.m @@ -16,6 +16,7 @@ #include +#import "Source/common/SNTRule.h" #import "Source/common/SNTStrengthify.h" #import "Source/common/SNTSystemInfo.h" @@ -33,6 +34,10 @@ @interface SNTConfigurator () /// 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 NSUInteger cachedStaticRulesHash; +@property NSDictionary *cachedStaticRules; @end @implementation SNTConfigurator @@ -44,6 +49,7 @@ @implementation SNTConfigurator 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"; @@ -176,6 +182,7 @@ - (instancetype)init { kRemountUSBBlockMessage : string, kModeNotificationMonitor : string, kModeNotificationLockdown : string, + kStaticRules : array, kSyncBaseURLKey : string, kSyncProxyConfigKey : dictionary, kClientAuthCertificateFileKey : string, @@ -282,6 +289,10 @@ + (NSSet *)keyPathsForValuesAffectingFileChangesPrefixFiltersKey { return [self configStateSet]; } ++ (NSSet *)keyPathsForValuesAffectingStaticRules { + return [self configStateSet]; +} + + (NSSet *)keyPathsForValuesAffectingSyncBaseURL { return [self configStateSet]; } @@ -549,6 +560,21 @@ - (void)setRemountUSBMode:(NSArray *)args { return args; } +- (NSDictionary *)staticRules { + NSArray *currentRules = self.configState[kStaticRules]; + if (currentRules.hash != self.cachedStaticRulesHash) { + NSMutableDictionary *rules = [NSMutableDictionary dictionaryWithCapacity:currentRules.count]; + for (id rule in currentRules) { + if (![rule isKindOfClass:[NSDictionary class]]) return self.cachedStaticRules; + SNTRule *r = [[SNTRule alloc] initWithDictionary:rule]; + rules[r.identifier] = r; + } + self.cachedStaticRules = [rules copy]; + self.cachedStaticRulesHash = currentRules.hash; + } + return self.cachedStaticRules; +} + - (NSURL *)syncBaseURL { NSString *urlString = self.configState[kSyncBaseURLKey]; if (![urlString hasSuffix:@"/"]) urlString = [urlString stringByAppendingString:@"/"]; diff --git a/Source/common/SNTRule.h b/Source/common/SNTRule.h index 0f1e27e90..0407f2ce7 100644 --- a/Source/common/SNTRule.h +++ b/Source/common/SNTRule.h @@ -64,6 +64,11 @@ 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. /// diff --git a/Source/common/SNTRule.m b/Source/common/SNTRule.m index d9afe068f..fb3387fc3 100644 --- a/Source/common/SNTRule.m +++ b/Source/common/SNTRule.m @@ -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,58 @@ - (instancetype)initWithIdentifier:(NSString *)identifier 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.length) _identifier = dict[kRuleSHA256]; + if (!_identifier.length) { + return nil; + } + + NSString *policyString = dict[kRulePolicy]; + 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 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.length) { + _customMsg = customMsg; + } + } + return self; +} + #pragma mark NSSecureCoding #pragma clang diagnostic push diff --git a/Source/common/SNTRuleTest.m b/Source/common/SNTRuleTest.m new file mode 100644 index 000000000..6d1d3de82 --- /dev/null +++ b/Source/common/SNTRuleTest.m @@ -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 + +#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 diff --git a/Source/santasyncservice/SNTSyncConstants.h b/Source/common/SNTSyncConstants.h similarity index 100% rename from Source/santasyncservice/SNTSyncConstants.h rename to Source/common/SNTSyncConstants.h diff --git a/Source/santasyncservice/SNTSyncConstants.m b/Source/common/SNTSyncConstants.m similarity index 99% rename from Source/santasyncservice/SNTSyncConstants.m rename to Source/common/SNTSyncConstants.m index 1d161d005..e7caa2479 100644 --- a/Source/santasyncservice/SNTSyncConstants.m +++ b/Source/common/SNTSyncConstants.m @@ -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"; diff --git a/Source/santactl/Commands/SNTCommandRule.m b/Source/santactl/Commands/SNTCommandRule.m index ae8ad411d..5a1d2a188 100644 --- a/Source/santactl/Commands/SNTCommandRule.m +++ b/Source/santactl/Commands/SNTCommandRule.m @@ -74,14 +74,15 @@ + (NSString *)longHelpText { - (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); } diff --git a/Source/santactl/Commands/SNTCommandStatus.m b/Source/santactl/Commands/SNTCommandStatus.m index c2033f7a9..83351165e 100644 --- a/Source/santactl/Commands/SNTCommandStatus.m +++ b/Source/santactl/Commands/SNTCommandStatus.m @@ -166,11 +166,11 @@ - (void)runWithArguments:(NSArray *)arguments { 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 = [@{ @@ -194,6 +194,9 @@ - (void)runWithArguments:(NSArray *)arguments { @"transitive_rules" : @(transitiveRuleCount), @"events_pending_upload" : @(eventCount), }, + @"static_rules" : @{ + @"rule_count" : @(configurator.staticRules.count), + }, @"sync" : @{ @"server" : syncURLStr ?: @"null", @"clean_required" : @(syncCleanReqd), @@ -241,6 +244,11 @@ - (void)runWithArguments:(NSArray *)arguments { 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 | %lu\n", "Rules", (unsigned long)configurator.staticRules.count); + } + if (syncURLStr) { printf(">>> Sync Info\n"); printf(" %-25s | %s\n", "Sync Server", [syncURLStr UTF8String]); diff --git a/Source/santad/DataLayer/SNTRuleTable.m b/Source/santad/DataLayer/SNTRuleTable.m index 334f39e03..d77704f9c 100644 --- a/Source/santad/DataLayer/SNTRuleTable.m +++ b/Source/santad/DataLayer/SNTRuleTable.m @@ -277,6 +277,19 @@ - (SNTRule *)ruleForBinarySHA256:(NSString *)binarySHA256 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. // diff --git a/Source/santametricservice/Writers/BUILD b/Source/santametricservice/Writers/BUILD index b866c35f5..b04a0ae24 100644 --- a/Source/santametricservice/Writers/BUILD +++ b/Source/santametricservice/Writers/BUILD @@ -62,8 +62,8 @@ santa_unit_test( deps = [ ":SNTMetricHTTPWriter", "//Source/common:SNTConfigurator", - "@OCMock", "@MOLAuthenticatingURLSession", + "@OCMock", ], ) diff --git a/Source/santasyncservice/BUILD b/Source/santasyncservice/BUILD index 69a5b7933..40d617392 100644 --- a/Source/santasyncservice/BUILD +++ b/Source/santasyncservice/BUILD @@ -26,8 +26,6 @@ objc_library( "SNTPushNotifications.m", "SNTPushNotificationsTracker.h", "SNTPushNotificationsTracker.m", - "SNTSyncConstants.h", - "SNTSyncConstants.m", "SNTSyncEventUpload.h", "SNTSyncEventUpload.m", "SNTSyncLogging.h", @@ -57,6 +55,7 @@ objc_library( "//Source/common:SNTRule", "//Source/common:SNTStoredEvent", "//Source/common:SNTStrengthify", + "//Source/common:SNTSyncConstants", "//Source/common:SNTSystemInfo", "//Source/common:SNTXPCControlInterface", "//Source/common:SNTXPCSyncServiceInterface", @@ -78,8 +77,6 @@ santa_unit_test( "SNTPushNotifications.m", "SNTPushNotificationsTracker.h", "SNTPushNotificationsTracker.m", - "SNTSyncConstants.h", - "SNTSyncConstants.m", "SNTSyncEventUpload.h", "SNTSyncEventUpload.m", "SNTSyncLogging.h", @@ -113,6 +110,7 @@ santa_unit_test( "//Source/common:SNTRule", "//Source/common:SNTStoredEvent", "//Source/common:SNTStrengthify", + "//Source/common:SNTSyncConstants", "//Source/common:SNTSystemInfo", "//Source/common:SNTXPCControlInterface", "@MOLAuthenticatingURLSession", diff --git a/Source/santasyncservice/SNTPushNotifications.m b/Source/santasyncservice/SNTPushNotifications.m index ffdde81c4..ba34e3138 100644 --- a/Source/santasyncservice/SNTPushNotifications.m +++ b/Source/santasyncservice/SNTPushNotifications.m @@ -17,8 +17,8 @@ #import "Source/common/SNTConfigurator.h" #import "Source/common/SNTLogging.h" #import "Source/common/SNTStrengthify.h" +#import "Source/common/SNTSyncConstants.h" #import "Source/santasyncservice/SNTPushNotificationsTracker.h" -#import "Source/santasyncservice/SNTSyncConstants.h" #import "Source/santasyncservice/SNTSyncFCM.h" #import "Source/santasyncservice/SNTSyncState.h" diff --git a/Source/santasyncservice/SNTPushNotificationsTracker.m b/Source/santasyncservice/SNTPushNotificationsTracker.m index 8ea9def36..57fe6657c 100644 --- a/Source/santasyncservice/SNTPushNotificationsTracker.m +++ b/Source/santasyncservice/SNTPushNotificationsTracker.m @@ -15,7 +15,7 @@ #import "Source/santasyncservice/SNTPushNotificationsTracker.h" #import "Source/common/SNTLogging.h" -#import "Source/santasyncservice/SNTSyncConstants.h" +#import "Source/common/SNTSyncConstants.h" @interface SNTPushNotificationsTracker () diff --git a/Source/santasyncservice/SNTSyncEventUpload.m b/Source/santasyncservice/SNTSyncEventUpload.m index 58cb35110..0fa763878 100644 --- a/Source/santasyncservice/SNTSyncEventUpload.m +++ b/Source/santasyncservice/SNTSyncEventUpload.m @@ -21,9 +21,9 @@ #import "Source/common/SNTFileInfo.h" #import "Source/common/SNTLogging.h" #import "Source/common/SNTStoredEvent.h" +#import "Source/common/SNTSyncConstants.h" #import "Source/common/SNTXPCControlInterface.h" #import "Source/santasyncservice/NSData+Zlib.h" -#import "Source/santasyncservice/SNTSyncConstants.h" #import "Source/santasyncservice/SNTSyncLogging.h" #import "Source/santasyncservice/SNTSyncState.h" diff --git a/Source/santasyncservice/SNTSyncManager.m b/Source/santasyncservice/SNTSyncManager.m index 6bf86b8fe..8d6a2f4b8 100644 --- a/Source/santasyncservice/SNTSyncManager.m +++ b/Source/santasyncservice/SNTSyncManager.m @@ -23,9 +23,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/santasyncservice/SNTPushNotifications.h" -#import "Source/santasyncservice/SNTSyncConstants.h" #import "Source/santasyncservice/SNTSyncEventUpload.h" #import "Source/santasyncservice/SNTSyncLogging.h" #import "Source/santasyncservice/SNTSyncPostflight.h" diff --git a/Source/santasyncservice/SNTSyncPostflight.m b/Source/santasyncservice/SNTSyncPostflight.m index a796521c2..a53b24655 100644 --- a/Source/santasyncservice/SNTSyncPostflight.m +++ b/Source/santasyncservice/SNTSyncPostflight.m @@ -16,8 +16,8 @@ #import +#import "Source/common/SNTSyncConstants.h" #import "Source/common/SNTXPCControlInterface.h" -#import "Source/santasyncservice/SNTSyncConstants.h" #import "Source/santasyncservice/SNTSyncState.h" @implementation SNTSyncPostflight diff --git a/Source/santasyncservice/SNTSyncPreflight.m b/Source/santasyncservice/SNTSyncPreflight.m index 53319a42d..a1f051b48 100644 --- a/Source/santasyncservice/SNTSyncPreflight.m +++ b/Source/santasyncservice/SNTSyncPreflight.m @@ -19,9 +19,9 @@ #import "Source/common/SNTCommon.h" #import "Source/common/SNTConfigurator.h" #import "Source/common/SNTLogging.h" +#import "Source/common/SNTSyncConstants.h" #import "Source/common/SNTSystemInfo.h" #import "Source/common/SNTXPCControlInterface.h" -#import "Source/santasyncservice/SNTSyncConstants.h" #import "Source/santasyncservice/SNTSyncLogging.h" #import "Source/santasyncservice/SNTSyncState.h" diff --git a/Source/santasyncservice/SNTSyncRuleDownload.h b/Source/santasyncservice/SNTSyncRuleDownload.h index d84d32d62..067a261b2 100644 --- a/Source/santasyncservice/SNTSyncRuleDownload.h +++ b/Source/santasyncservice/SNTSyncRuleDownload.h @@ -19,5 +19,4 @@ @class SNTRule; @interface SNTSyncRuleDownload : SNTSyncStage -- (SNTRule *)ruleFromDictionary:(NSDictionary *)dict; @end diff --git a/Source/santasyncservice/SNTSyncRuleDownload.m b/Source/santasyncservice/SNTSyncRuleDownload.m index cd71120bb..d67deb80e 100644 --- a/Source/santasyncservice/SNTSyncRuleDownload.m +++ b/Source/santasyncservice/SNTSyncRuleDownload.m @@ -18,9 +18,9 @@ #import #import "Source/common/SNTRule.h" +#import "Source/common/SNTSyncConstants.h" #import "Source/common/SNTXPCControlInterface.h" #import "Source/santasyncservice/SNTPushNotificationsTracker.h" -#import "Source/santasyncservice/SNTSyncConstants.h" #import "Source/santasyncservice/SNTSyncLogging.h" #import "Source/santasyncservice/SNTSyncState.h" @@ -92,8 +92,9 @@ - (BOOL)sync { uint32_t count = 0; for (NSDictionary *ruleDict in response[kRules]) { - SNTRule *rule = [self ruleFromDictionary:ruleDict]; + SNTRule *rule = [[SNTRule alloc] initWithDictionary:ruleDict]; if (rule) { + [self processBundleNotificationsForRule:rule fromDictionary:ruleDict]; [newRules addObject:rule]; count++; } @@ -128,60 +129,14 @@ - (void)announceUnblockingRules:(NSArray *)newRules { [tracker removeNotificationsForHashes:processed]; } -// 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). -- (SNTRule *)ruleFromDictionary:(NSDictionary *)dict { - if (![dict isKindOfClass:[NSDictionary class]]) return nil; - - SNTRule *newRule = [[SNTRule alloc] init]; - newRule.identifier = dict[kRuleIdentifier]; - if (newRule.identifier == nil) { - newRule.identifier = dict[kRuleSHA256]; - } - - NSString *policyString = dict[kRulePolicy]; - if ([policyString isEqual:kRulePolicyAllowlist] || - [policyString isEqual:kRulePolicyAllowlistDeprecated]) { - newRule.state = SNTRuleStateAllow; - } else if ([policyString isEqual:kRulePolicyAllowlistCompiler] || - [policyString isEqual:kRulePolicyAllowlistCompilerDeprecated]) { - newRule.state = SNTRuleStateAllowCompiler; - } else if ([policyString isEqual:kRulePolicyBlocklist] || - [policyString isEqual:kRulePolicyBlocklistDeprecated]) { - newRule.state = SNTRuleStateBlock; - } else if ([policyString isEqual:kRulePolicySilentBlocklist] || - [policyString isEqual:kRulePolicySilentBlocklistDeprecated]) { - newRule.state = SNTRuleStateSilentBlock; - } else if ([policyString isEqual:kRulePolicyRemove]) { - newRule.state = SNTRuleStateRemove; - } else { - return nil; - } - - NSString *ruleTypeString = dict[kRuleType]; - if ([ruleTypeString isEqual:kRuleTypeBinary]) { - newRule.type = SNTRuleTypeBinary; - } else if ([ruleTypeString isEqual:kRuleTypeCertificate]) { - newRule.type = SNTRuleTypeCertificate; - } else if ([ruleTypeString isEqual:kRuleTypeTeamID]) { - newRule.type = SNTRuleTypeTeamID; - } else { - return nil; - } - - NSString *customMsg = dict[kRuleCustomMsg]; - if (customMsg.length) { - newRule.customMsg = customMsg; - } - +- (void)processBundleNotificationsForRule:(SNTRule *)rule fromDictionary:(NSDictionary *)dict { // Check rule for extra notification related info. - if (newRule.state == SNTRuleStateAllow || newRule.state == SNTRuleStateAllowCompiler) { + if (rule.state == SNTRuleStateAllow || rule.state == SNTRuleStateAllowCompiler) { // primaryHash is the bundle hash if there was a bundle hash included in the rule, otherwise // it is simply the binary hash. NSString *primaryHash = dict[kFileBundleHash]; if (primaryHash.length != 64) { - primaryHash = newRule.identifier; + primaryHash = rule.identifier; } // As we read in rules, we update the "remaining count" information. This count represents the @@ -190,8 +145,6 @@ - (SNTRule *)ruleFromDictionary:(NSDictionary *)dict { decrementPendingRulesForHash:primaryHash totalRuleCount:dict[kFileBundleBinaryCount]]; } - - return newRule; } @end diff --git a/Source/santasyncservice/SNTSyncStage.m b/Source/santasyncservice/SNTSyncStage.m index 42c682d57..fda255ac2 100644 --- a/Source/santasyncservice/SNTSyncStage.m +++ b/Source/santasyncservice/SNTSyncStage.m @@ -17,9 +17,9 @@ #import #import "Source/common/SNTLogging.h" +#import "Source/common/SNTSyncConstants.h" #import "Source/common/SNTXPCControlInterface.h" #import "Source/santasyncservice/NSData+Zlib.h" -#import "Source/santasyncservice/SNTSyncConstants.h" #import "Source/santasyncservice/SNTSyncLogging.h" #import "Source/santasyncservice/SNTSyncState.h" diff --git a/Source/santasyncservice/SNTSyncTest.m b/Source/santasyncservice/SNTSyncTest.m index 51b5066ff..c12a788a9 100644 --- a/Source/santasyncservice/SNTSyncTest.m +++ b/Source/santasyncservice/SNTSyncTest.m @@ -20,8 +20,8 @@ #import "Source/common/SNTCommonEnums.h" #import "Source/common/SNTRule.h" #import "Source/common/SNTStoredEvent.h" +#import "Source/common/SNTSyncConstants.h" #import "Source/common/SNTXPCControlInterface.h" -#import "Source/santasyncservice/SNTSyncConstants.h" #import "Source/santasyncservice/SNTSyncEventUpload.h" #import "Source/santasyncservice/SNTSyncPostflight.h" #import "Source/santasyncservice/SNTSyncPreflight.h" diff --git a/WORKSPACE b/WORKSPACE index b86270c6e..6f7677b63 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -9,11 +9,10 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "build_bazel_rules_apple", - sha256 = "36072d4f3614d309d6a703da0dfe48684ec4c65a89611aeb9590b45af7a3e592", # 1.0.1 + sha256 = "36072d4f3614d309d6a703da0dfe48684ec4c65a89611aeb9590b45af7a3e592", # 1.0.1 urls = ["https://github.com/bazelbuild/rules_apple/releases/download/1.0.1/rules_apple.1.0.1.tar.gz"], ) - http_archive( name = "rules_proto_grpc", sha256 = "28724736b7ff49a48cb4b2b8cfa373f89edfcb9e8e492a8d5ab60aa3459314c8", diff --git a/profiles/BUILD b/profiles/BUILD index 9a242b21a..179073a5c 100644 --- a/profiles/BUILD +++ b/profiles/BUILD @@ -5,11 +5,11 @@ package( licenses(["notice"]) filegroup( - name="santa_dev", - srcs=["Santa_Dev.provisionprofile"] + name = "santa_dev", + srcs = ["Santa_Dev.provisionprofile"], ) filegroup( - name="daemon_dev", - srcs=["Santa_Daemon_Dev.provisionprofile"], + name = "daemon_dev", + srcs = ["Santa_Daemon_Dev.provisionprofile"], ) From 9db00d143d1b6b9d7c5445f2ba3b50d4d0ffff2e Mon Sep 17 00:00:00 2001 From: Russell Hancox Date: Thu, 14 Jul 2022 10:50:30 -0400 Subject: [PATCH 11/20] 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. --- Source/common/SNTConfigurator.m | 33 ++++++++++++++++++++------------- Source/common/SNTRule.m | 10 ++++++---- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/Source/common/SNTConfigurator.m b/Source/common/SNTConfigurator.m index 576543947..f88cc6ea4 100644 --- a/Source/common/SNTConfigurator.m +++ b/Source/common/SNTConfigurator.m @@ -36,8 +36,7 @@ @interface SNTConfigurator () @property(readonly, nonatomic) BOOL debugFlag; /// Holds the last processed hash of the static rules list. -@property NSUInteger cachedStaticRulesHash; -@property NSDictionary *cachedStaticRules; +@property(atomic) NSDictionary *cachedStaticRules; @end @implementation SNTConfigurator @@ -561,17 +560,6 @@ - (void)setRemountUSBMode:(NSArray *)args { } - (NSDictionary *)staticRules { - NSArray *currentRules = self.configState[kStaticRules]; - if (currentRules.hash != self.cachedStaticRulesHash) { - NSMutableDictionary *rules = [NSMutableDictionary dictionaryWithCapacity:currentRules.count]; - for (id rule in currentRules) { - if (![rule isKindOfClass:[NSDictionary class]]) return self.cachedStaticRules; - SNTRule *r = [[SNTRule alloc] initWithDictionary:rule]; - rules[r.identifier] = r; - } - self.cachedStaticRules = [rules copy]; - self.cachedStaticRulesHash = currentRules.hash; - } return self.cachedStaticRules; } @@ -960,6 +948,9 @@ - (NSMutableDictionary *)readForcedConfig { NSString *pattern = [obj isKindOfClass:[NSString class]] ? obj : nil; forcedConfig[key] = [self expressionForPattern:pattern]; } + if ([key isEqualToString:kStaticRules]) { + [self cacheStaticRules]; + } } return forcedConfig; } @@ -992,4 +983,20 @@ - (void)handleChange { self.configState = [self readForcedConfig]; } +/// +/// 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 *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 diff --git a/Source/common/SNTRule.m b/Source/common/SNTRule.m index fb3387fc3..9294a5b02 100644 --- a/Source/common/SNTRule.m +++ b/Source/common/SNTRule.m @@ -58,12 +58,13 @@ - (instancetype)initWithDictionary:(NSDictionary *)dict { self = [super init]; if (self) { _identifier = dict[kRuleIdentifier]; - if (!_identifier.length) _identifier = dict[kRuleSHA256]; - if (!_identifier.length) { - return nil; + 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; @@ -83,6 +84,7 @@ - (instancetype)initWithDictionary:(NSDictionary *)dict { } NSString *ruleTypeString = dict[kRuleType]; + if (![ruleTypeString isKindOfClass:[NSString class]]) return nil; if ([ruleTypeString isEqual:kRuleTypeBinary]) { _type = SNTRuleTypeBinary; } else if ([ruleTypeString isEqual:kRuleTypeCertificate]) { @@ -94,7 +96,7 @@ - (instancetype)initWithDictionary:(NSDictionary *)dict { } NSString *customMsg = dict[kRuleCustomMsg]; - if (customMsg.length) { + if ([customMsg isKindOfClass:[NSString class]] && customMsg.length) { _customMsg = customMsg; } } From fbefbc59103e43a7b9b4fdbc8c7715772fdb5d77 Mon Sep 17 00:00:00 2001 From: Russell Hancox Date: Fri, 15 Jul 2022 11:52:43 -0400 Subject: [PATCH 12/20] santasyncservice: Keep XSRF token in memory, don't send to daemon (#851) --- Source/common/SNTXPCControlInterface.h | 1 - Source/common/SNTXPCUnprivilegedControlInterface.h | 1 - Source/santad/SNTDaemonControlController.m | 10 ---------- Source/santasyncservice/SNTSyncManager.m | 14 +++++++------- Source/santasyncservice/SNTSyncStage.m | 3 --- 5 files changed, 7 insertions(+), 22 deletions(-) diff --git a/Source/common/SNTXPCControlInterface.h b/Source/common/SNTXPCControlInterface.h index de414555f..d090e47f3 100644 --- a/Source/common/SNTXPCControlInterface.h +++ b/Source/common/SNTXPCControlInterface.h @@ -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; diff --git a/Source/common/SNTXPCUnprivilegedControlInterface.h b/Source/common/SNTXPCUnprivilegedControlInterface.h index de987da50..86e3e5f05 100644 --- a/Source/common/SNTXPCUnprivilegedControlInterface.h +++ b/Source/common/SNTXPCUnprivilegedControlInterface.h @@ -63,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; diff --git a/Source/santad/SNTDaemonControlController.m b/Source/santad/SNTDaemonControlController.m index 51727d92e..6eb6dcec9 100644 --- a/Source/santad/SNTDaemonControlController.m +++ b/Source/santad/SNTDaemonControlController.m @@ -43,7 +43,6 @@ double watchdogRAMPeak = 0; @interface SNTDaemonControlController () -@property NSString *_syncXsrfToken; @property SNTPolicyProcessor *policyProcessor; @property id eventProvider; @property SNTNotificationQueue *notQueue; @@ -169,15 +168,6 @@ - (void)setClientMode:(SNTClientMode)mode reply:(void (^)(void))reply { reply(); } -- (void)xsrfToken:(void (^)(NSString *))reply { - reply(self._syncXsrfToken); -} - -- (void)setXsrfToken:(NSString *)token reply:(void (^)(void))reply { - self._syncXsrfToken = token; - reply(); -} - - (void)fullSyncLastSuccess:(void (^)(NSDate *))reply { reply([[SNTConfigurator configurator] fullSyncLastSuccess]); } diff --git a/Source/santasyncservice/SNTSyncManager.m b/Source/santasyncservice/SNTSyncManager.m index 8d6a2f4b8..363d9af73 100644 --- a/Source/santasyncservice/SNTSyncManager.m +++ b/Source/santasyncservice/SNTSyncManager.m @@ -53,6 +53,8 @@ @interface SNTSyncManager () { @property NSUInteger eventBatchSize; +@property NSString *xsrfToken; + @end // Called when the network state changes @@ -120,6 +122,7 @@ - (void)postEventsToSyncServer:(NSArray *)events fromBundle:(B [[SNTConfigurator configurator] syncBaseURL].absoluteString); [self startReachability]; } + self.xsrfToken = syncState.xsrfToken; } - (void)postBundleEventToSyncServer:(SNTStoredEvent *)event @@ -152,6 +155,7 @@ - (void)postBundleEventToSyncServer:(SNTStoredEvent *)event [[SNTConfigurator configurator] syncBaseURL].absoluteString); [self startReachability]; } + self.xsrfToken = syncState.xsrfToken; } - (void)isFCMListening:(void (^)(BOOL))reply { @@ -222,6 +226,7 @@ - (void)ruleSyncImpl { SNTSyncRuleDownload *p = [[SNTSyncRuleDownload alloc] initWithState:syncState]; BOOL ret = [p sync]; LOGD(@"Rule download %@", ret ? @"complete" : @"failed"); + self.xsrfToken = syncState.xsrfToken; }); } @@ -246,6 +251,7 @@ - (SNTSyncStatusType)preflightOnly:(BOOL)preflightOnly { SNTSyncPreflight *p = [[SNTSyncPreflight alloc] initWithState:syncState]; if ([p sync]) { SLOGD(@"Preflight complete"); + self.xsrfToken = syncState.xsrfToken; // Clean up reachability if it was started for a non-network error [self stopReachability]; @@ -346,12 +352,7 @@ - (SNTSyncState *)createSyncStateWithStatus:(SNTSyncStatusType *)status { SLOGW(@"Missing Machine Owner."); } - dispatch_group_t group = dispatch_group_create(); - dispatch_group_enter(group); - [[self.daemonConn remoteObjectProxy] xsrfToken:^(NSString *token) { - syncState.xsrfToken = token; - dispatch_group_leave(group); - }]; + syncState.xsrfToken = self.xsrfToken; NSURLSessionConfiguration *sessConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; sessConfig.connectionProxyDictionary = [[SNTConfigurator configurator] syncProxyConfig]; @@ -394,7 +395,6 @@ - (SNTSyncState *)createSyncStateWithStatus:(SNTSyncStatusType *)status { syncState.pushNotificationsToken = self.pushNotifications.token; - dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)); return syncState; } diff --git a/Source/santasyncservice/SNTSyncStage.m b/Source/santasyncservice/SNTSyncStage.m index fda255ac2..ee45856c7 100644 --- a/Source/santasyncservice/SNTSyncStage.m +++ b/Source/santasyncservice/SNTSyncStage.m @@ -199,9 +199,6 @@ - (BOOL)fetchXSRFToken { [self performRequest:request timeout:10 response:&response error:NULL]; if (response.statusCode == 200) { NSDictionary *headers = [response allHeaderFields]; - [[self.daemonConn remoteObjectProxy] setXsrfToken:headers[kXSRFToken] - reply:^{ - }]; self.syncState.xsrfToken = headers[kXSRFToken]; SLOGD(@"Retrieved new XSRF token"); success = YES; From fdb01928a00d05b2f70e16c5b4fa61088bb63021 Mon Sep 17 00:00:00 2001 From: Russell Hancox Date: Fri, 15 Jul 2022 11:53:17 -0400 Subject: [PATCH 13/20] 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 --- Source/santad/SNTApplication.m | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/santad/SNTApplication.m b/Source/santad/SNTApplication.m index ef902e41d..3a4ac819c 100644 --- a/Source/santad/SNTApplication.m +++ b/Source/santad/SNTApplication.m @@ -318,9 +318,14 @@ - (void)establishSyncServiceConnection { // This will handle retying connection establishment if there are issues with the service // during initialization (missing binary, malformed plist, bad code signature, etc.). // Once those issues are resolved the connection will establish. - // This will also handle reestablishment if the service crashes or is killed. + // This will also handle re-establishment if the service crashes or is killed. + WEAKIFY(self); ss.invalidationHandler = ^(void) { - [self establishSyncServiceConnection]; + STRONGIFY(self); + self.syncdQueue.syncConnection.invalidationHandler = nil; + [self performSelectorOnMainThread:@selector(establishSyncServiceConnection) + withObject:nil + waitUntilDone:YES]; }; [ss resume]; // If there are issues establishing the connection resume will block for 2 seconds. self.syncdQueue.syncConnection = ss; From 39f3ffe8fc39c999dbe52aa915090de89d13c6c8 Mon Sep 17 00:00:00 2001 From: Russell Hancox Date: Fri, 15 Jul 2022 11:53:38 -0400 Subject: [PATCH 14/20] santactl/status: Fix printing of static rules (#848) --- Source/common/SNTConfigurator.m | 8 ++++---- Source/common/SNTXPCControlInterface.h | 2 +- Source/common/SNTXPCUnprivilegedControlInterface.h | 1 + Source/santactl/Commands/SNTCommandStatus.m | 12 ++++++++++-- Source/santad/SNTDaemonControlController.m | 4 ++++ 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Source/common/SNTConfigurator.m b/Source/common/SNTConfigurator.m index f88cc6ea4..cb6986610 100644 --- a/Source/common/SNTConfigurator.m +++ b/Source/common/SNTConfigurator.m @@ -221,6 +221,7 @@ - (instancetype)init { _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]; @@ -948,9 +949,6 @@ - (NSMutableDictionary *)readForcedConfig { NSString *pattern = [obj isKindOfClass:[NSString class]] ? obj : nil; forcedConfig[key] = [self expressionForPattern:pattern]; } - if ([key isEqualToString:kStaticRules]) { - [self cacheStaticRules]; - } } return forcedConfig; } @@ -981,6 +979,7 @@ - (void)defaultsChanged:(void *)v { /// - (void)handleChange { self.configState = [self readForcedConfig]; + [self cacheStaticRules]; } /// @@ -990,7 +989,8 @@ - (void)cacheStaticRules { NSArray *staticRules = self.configState[kStaticRules]; if (![staticRules isKindOfClass:[NSArray class]]) return; - NSMutableDictionary *rules = [NSMutableDictionary dictionaryWithCapacity:staticRules.count]; + NSMutableDictionary *rules = + [NSMutableDictionary dictionaryWithCapacity:staticRules.count]; for (id rule in staticRules) { if (![rule isKindOfClass:[NSDictionary class]]) return; SNTRule *r = [[SNTRule alloc] initWithDictionary:rule]; diff --git a/Source/common/SNTXPCControlInterface.h b/Source/common/SNTXPCControlInterface.h index d090e47f3..d9bdecd45 100644 --- a/Source/common/SNTXPCControlInterface.h +++ b/Source/common/SNTXPCControlInterface.h @@ -20,7 +20,7 @@ @protocol SNTDaemonControlXPC /// -/// Kernel ops +/// Cache ops /// - (void)flushCache:(void (^)(BOOL))reply; diff --git a/Source/common/SNTXPCUnprivilegedControlInterface.h b/Source/common/SNTXPCUnprivilegedControlInterface.h index 86e3e5f05..f515ed31b 100644 --- a/Source/common/SNTXPCUnprivilegedControlInterface.h +++ b/Source/common/SNTXPCUnprivilegedControlInterface.h @@ -40,6 +40,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 diff --git a/Source/santactl/Commands/SNTCommandStatus.m b/Source/santactl/Commands/SNTCommandStatus.m index 83351165e..e5f1a6e03 100644 --- a/Source/santactl/Commands/SNTCommandStatus.m +++ b/Source/santactl/Commands/SNTCommandStatus.m @@ -107,6 +107,14 @@ - (void)runWithArguments:(NSArray *)arguments { 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); @@ -195,7 +203,7 @@ - (void)runWithArguments:(NSArray *)arguments { @"events_pending_upload" : @(eventCount), }, @"static_rules" : @{ - @"rule_count" : @(configurator.staticRules.count), + @"rule_count" : @(staticRuleCount), }, @"sync" : @{ @"server" : syncURLStr ?: @"null", @@ -246,7 +254,7 @@ - (void)runWithArguments:(NSArray *)arguments { if ([SNTConfigurator configurator].staticRules.count) { printf(">>> Static Rules\n"); - printf(" %-25s | %lu\n", "Rules", (unsigned long)configurator.staticRules.count); + printf(" %-25s | %lld\n", "Rules", staticRuleCount); } if (syncURLStr) { diff --git a/Source/santad/SNTDaemonControlController.m b/Source/santad/SNTDaemonControlController.m index 6eb6dcec9..fdd26449b 100644 --- a/Source/santad/SNTDaemonControlController.m +++ b/Source/santad/SNTDaemonControlController.m @@ -139,6 +139,10 @@ - (void)databaseRuleForBinarySHA256:(NSString *)binarySHA256 teamID:teamID]); } +- (void)staticRuleCount:(void (^)(int64_t count))reply { + reply([SNTConfigurator configurator].staticRules.count); +} + #pragma mark Decision Ops - (void)decisionForFilePath:(NSString *)filePath From 21220f14990e68179a56cbccb410c30a333007ac Mon Sep 17 00:00:00 2001 From: Russell Hancox Date: Fri, 15 Jul 2022 12:30:20 -0400 Subject: [PATCH 15/20] santad: Add DisableUnknownEventUpload option. (#852) --- Source/common/SNTConfigurator.h | 5 ++ Source/common/SNTConfigurator.m | 17 +++++++ Source/common/SNTMetricSet.h | 5 ++ Source/common/SNTMetricSet.m | 4 ++ Source/common/SNTSyncConstants.h | 1 + Source/common/SNTSyncConstants.m | 1 + Source/common/SNTXPCControlInterface.h | 1 + Source/santad/SNTDaemonControlController.m | 9 ++++ Source/santad/SNTExecutionController.m | 8 ++-- Source/santad/SNTExecutionControllerTest.m | 53 ++++++++++++++++++---- Source/santasyncservice/SNTSyncPreflight.m | 8 ++++ docs/deployment/configuration.md | 1 + 12 files changed, 99 insertions(+), 14 deletions(-) diff --git a/Source/common/SNTConfigurator.h b/Source/common/SNTConfigurator.h index 78b6426ff..e2931ae2d 100644 --- a/Source/common/SNTConfigurator.h +++ b/Source/common/SNTConfigurator.h @@ -451,6 +451,11 @@ /// @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. /// diff --git a/Source/common/SNTConfigurator.m b/Source/common/SNTConfigurator.m index cb6986610..18d11d9c7 100644 --- a/Source/common/SNTConfigurator.m +++ b/Source/common/SNTConfigurator.m @@ -118,6 +118,7 @@ @implementation SNTConfigurator 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"; @@ -217,6 +218,7 @@ - (instancetype)init { kMetricExportTimeout : number, kMetricExtraLabels : dictionary, kEnableAllEventUploadKey : number, + kDisableUnknownEventUploadKey : number, }; _defaults = [NSUserDefaults standardUserDefaults]; [_defaults addSuiteNamed:@"com.google.santa"]; @@ -413,6 +415,10 @@ + (NSSet *)keyPathsForValuesAffectingEnableAllEventUpload { return [self syncAndConfigStateSet]; } ++ (NSSet *)keyPathsForValuesAffectingDisableUnknownEventUpload { + return [self syncAndConfigStateSet]; +} + + (NSSet *)keyPathsForValuesAffectingEnableSysxCache { return [self configStateSet]; } @@ -777,6 +783,17 @@ - (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 { NSNumber *number = self.configState[kEnableForkAndExitLogging]; return number ? [number boolValue] : NO; diff --git a/Source/common/SNTMetricSet.h b/Source/common/SNTMetricSet.h index d6c6a98d6..6463a34ad 100644 --- a/Source/common/SNTMetricSet.h +++ b/Source/common/SNTMetricSet.h @@ -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. */ diff --git a/Source/common/SNTMetricSet.m b/Source/common/SNTMetricSet.m index 4ca5ee50e..d68d99ef0 100644 --- a/Source/common/SNTMetricSet.m +++ b/Source/common/SNTMetricSet.m @@ -485,6 +485,10 @@ - (instancetype)initWithHostname:(NSString *)hostname username:(NSString *)usern return self; } +- (void)reset { + _metrics = [[NSMutableDictionary alloc] init]; +} + - (void)addRootLabel:(NSString *)label value:(NSString *)value { @synchronized(self) { _rootLabels[label] = value; diff --git a/Source/common/SNTSyncConstants.h b/Source/common/SNTSyncConstants.h index fb2a235b4..31cb25ec4 100644 --- a/Source/common/SNTSyncConstants.h +++ b/Source/common/SNTSyncConstants.h @@ -51,6 +51,7 @@ 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; diff --git a/Source/common/SNTSyncConstants.m b/Source/common/SNTSyncConstants.m index e7caa2479..291005414 100644 --- a/Source/common/SNTSyncConstants.m +++ b/Source/common/SNTSyncConstants.m @@ -52,6 +52,7 @@ 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"; diff --git a/Source/common/SNTXPCControlInterface.h b/Source/common/SNTXPCControlInterface.h index d9bdecd45..733179b55 100644 --- a/Source/common/SNTXPCControlInterface.h +++ b/Source/common/SNTXPCControlInterface.h @@ -51,6 +51,7 @@ - (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 diff --git a/Source/santad/SNTDaemonControlController.m b/Source/santad/SNTDaemonControlController.m index fdd26449b..ae64ecde4 100644 --- a/Source/santad/SNTDaemonControlController.m +++ b/Source/santad/SNTDaemonControlController.m @@ -251,6 +251,15 @@ - (void)setEnableAllEventUpload:(BOOL)enabled reply:(void (^)(void))reply { reply(); } +- (void)disableUnknownEventUpload:(void (^)(BOOL))reply { + reply([SNTConfigurator configurator].disableUnknownEventUpload); +} + +- (void)setDisableUnknownEventUpload:(BOOL)enabled reply:(void (^)(void))reply { + [[SNTConfigurator configurator] setDisableUnknownEventUpload:enabled]; + reply(); +} + #pragma mark Metrics Ops - (void)metrics:(void (^)(NSDictionary *))reply { diff --git a/Source/santad/SNTExecutionController.m b/Source/santad/SNTExecutionController.m index 216d15dbe..b4d63263e 100644 --- a/Source/santad/SNTExecutionController.m +++ b/Source/santad/SNTExecutionController.m @@ -195,11 +195,9 @@ - (void)validateBinaryWithMessage:(santa_message_t)message { [self incrementEventCounters:cd.decision]; // Log to database if necessary. - if ([SNTConfigurator configurator].enableAllEventUpload || - (cd.decision != SNTEventStateAllowBinary && cd.decision != SNTEventStateAllowCompiler && - cd.decision != SNTEventStateAllowTransitive && - cd.decision != SNTEventStateAllowCertificate && cd.decision != SNTEventStateAllowTeamID && - cd.decision != SNTEventStateAllowScope)) { + if (config.enableAllEventUpload || + (cd.decision == SNTEventStateAllowUnknown && !config.disableUnknownEventUpload) || + (cd.decision & SNTEventStateAllow) == 0) { SNTStoredEvent *se = [[SNTStoredEvent alloc] init]; se.occurrenceDate = [[NSDate alloc] init]; se.fileSHA256 = cd.sha256; diff --git a/Source/santad/SNTExecutionControllerTest.m b/Source/santad/SNTExecutionControllerTest.m index 51db2bca6..74434a64c 100644 --- a/Source/santad/SNTExecutionControllerTest.m +++ b/Source/santad/SNTExecutionControllerTest.m @@ -54,6 +54,8 @@ - (void)setUp { fclose(stdout); + [[SNTMetricSet sharedInstance] reset]; + self.mockCodesignChecker = OCMClassMock([MOLCodesignChecker class]); OCMStub([self.mockCodesignChecker alloc]).andReturn(self.mockCodesignChecker); @@ -111,7 +113,7 @@ - (void)checkMetricCounters:(const NSString *)expectedFieldValueName break; } - if (!foundField) { + if (!foundField && expectedValue.intValue != 0) { XCTFail(@"failed to find %@ field value", expectedFieldValueName); } } @@ -129,7 +131,7 @@ - (void)testBinaryAllowRule { [self.sut validateBinaryWithMessage:[self getMessage]]; OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]); - [self checkMetricCounters:@"AllowBinary" expected:@2]; + [self checkMetricCounters:@"AllowBinary" expected:@1]; } - (void)testBinaryBlockRule { @@ -241,7 +243,7 @@ - (void)testBinaryAllowTransitiveRule { OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]); - [self checkMetricCounters:@"AllowBinary" expected:@2]; + [self checkMetricCounters:kAllowTransitive expected:@1]; } - (void)testBinaryAllowTransitiveRuleDisabled { @@ -262,8 +264,8 @@ - (void)testBinaryAllowTransitiveRuleDisabled { OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]); OCMVerifyAllWithDelay(self.mockEventDatabase, 1); - [self checkMetricCounters:kAllowBinary expected:@2]; - [self checkMetricCounters:kAllowTransitive expected:@1]; + [self checkMetricCounters:kAllowBinary expected:@0]; + [self checkMetricCounters:kAllowTransitive expected:@0]; } - (void)testDefaultDecision { @@ -281,7 +283,7 @@ - (void)testDefaultDecision { OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]); OCMVerifyAllWithDelay(self.mockEventDatabase, 1); - [self checkMetricCounters:kBlockUnknown expected:@2]; + [self checkMetricCounters:kBlockUnknown expected:@1]; [self checkMetricCounters:kAllowUnknown expected:@1]; } @@ -298,7 +300,7 @@ - (void)testUnreadableFailOpenLockdown { OCMStub([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown); [self.sut validateBinaryWithMessage:[self getMessage]]; OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]); - [self checkMetricCounters:kAllowNoFileInfo expected:@2]; + [self checkMetricCounters:kAllowNoFileInfo expected:@1]; } - (void)testUnreadableFailClosedLockdown { @@ -344,7 +346,7 @@ - (void)testOutOfScope { OCMStub([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown); [self.sut validateBinaryWithMessage:[self getMessage]]; OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]); - [self checkMetricCounters:kAllowScope expected:@2]; + [self checkMetricCounters:kAllowScope expected:@1]; } - (void)testPageZero { @@ -354,7 +356,40 @@ - (void)testPageZero { [self.sut validateBinaryWithMessage:[self getMessage]]; OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]); OCMVerifyAllWithDelay(self.mockEventDatabase, 1); - [self checkMetricCounters:kBlockUnknown expected:@3]; + [self checkMetricCounters:kBlockUnknown expected:@1]; +} + +- (void)testAllEventUpload { + OCMStub([self.mockFileInfo isMachO]).andReturn(YES); + OCMStub([self.mockFileInfo SHA256]).andReturn(@"a"); + + OCMExpect([self.mockConfigurator clientMode]).andReturn(SNTClientModeMonitor); + OCMExpect([self.mockConfigurator enableAllEventUpload]).andReturn(YES); + OCMExpect([self.mockEventDatabase addStoredEvent:OCMOCK_ANY]); + + SNTRule *rule = [[SNTRule alloc] init]; + rule.state = SNTRuleStateAllow; + rule.type = SNTRuleTypeBinary; + OCMStub([self.mockRuleDatabase ruleForBinarySHA256:@"a" certificateSHA256:nil teamID:nil]) + .andReturn(rule); + + [self.sut validateBinaryWithMessage:[self getMessage]]; + OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]); + OCMVerifyAllWithDelay(self.mockEventDatabase, 1); +} + +- (void)testDisableUnknownEventUpload { + OCMStub([self.mockFileInfo isMachO]).andReturn(YES); + OCMStub([self.mockFileInfo SHA256]).andReturn(@"a"); + + OCMExpect([self.mockConfigurator clientMode]).andReturn(SNTClientModeMonitor); + OCMExpect([self.mockConfigurator enableAllEventUpload]).andReturn(NO); + OCMExpect([self.mockConfigurator disableUnknownEventUpload]).andReturn(YES); + + [self.sut validateBinaryWithMessage:[self getMessage]]; + OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]); + OCMVerify(never(), [self.mockEventDatabase addStoredEvent:OCMOCK_ANY]); + [self checkMetricCounters:kAllowUnknown expected:@1]; } @end diff --git a/Source/santasyncservice/SNTSyncPreflight.m b/Source/santasyncservice/SNTSyncPreflight.m index a1f051b48..57f7a4f71 100644 --- a/Source/santasyncservice/SNTSyncPreflight.m +++ b/Source/santasyncservice/SNTSyncPreflight.m @@ -117,6 +117,14 @@ - (BOOL)sync { dispatch_group_leave(group); }]; + dispatch_group_enter(group); + NSNumber *disableUnknownEventUpload = resp[kDisableUnknownEventUpload]; + [[self.daemonConn remoteObjectProxy] + setDisableUnknownEventUpload:[disableUnknownEventUpload boolValue] + reply:^{ + dispatch_group_leave(group); + }]; + self.syncState.eventBatchSize = [resp[kBatchSize] unsignedIntegerValue] ?: kDefaultEventBatchSize; // Don't let these go too low diff --git a/docs/deployment/configuration.md b/docs/deployment/configuration.md index 9cbba34b9..fa4b0bee3 100644 --- a/docs/deployment/configuration.md +++ b/docs/deployment/configuration.md @@ -65,6 +65,7 @@ also known as mobileconfig files, which are in an Apple-specific XML format. | MetricExportTimeout | Integer | Number of seconds to wait before a timeout occurs when exporting metrics. Defaults to 30. | | MetricExtraLabels | Dictionary | A map of key value pairs to add to all metric root labels. (e.g. a=b,c=d) defaults to @{}). If a previously set key (e.g. host_name is set to "" then the key is remove from the metric root labels. Alternatively if a value is set for an existing key then the new value will override the old. | | EnableAllEventUpload | Bool | If YES, the client will upload all execution events to the sync server, including those that were explicitly allowed. | +| DisableUnknownEventUpload | Bool | If YES, the client will *not* upload events for executions of unknown binaries allowed in monitor mode | \*overridable by the sync server: run `santactl status` to check the current From 743c567bf8a663dce6485d1703ddfe35079fda87 Mon Sep 17 00:00:00 2001 From: Russell Hancox Date: Fri, 15 Jul 2022 12:41:56 -0400 Subject: [PATCH 16/20] santad: Log team ID in execution logs, where available (#850) --- Source/common/santa.proto | 1 + Source/santad/Logs/SNTProtobufEventLog.m | 1 + Source/santad/Logs/SNTSyslogEventLog.m | 6 +++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Source/common/santa.proto b/Source/common/santa.proto index 0f97d42fe..bd8b5eb84 100644 --- a/Source/common/santa.proto +++ b/Source/common/santa.proto @@ -79,6 +79,7 @@ message Execution { optional string original_path = 11; repeated string args = 12; optional string machine_id = 13; + optional string team_id = 14; } message DiskAppeared { diff --git a/Source/santad/Logs/SNTProtobufEventLog.m b/Source/santad/Logs/SNTProtobufEventLog.m index 3d7ae6b34..27548a551 100644 --- a/Source/santad/Logs/SNTProtobufEventLog.m +++ b/Source/santad/Logs/SNTProtobufEventLog.m @@ -226,6 +226,7 @@ - (void)logExecution:(santa_message_t)message withDecision:(SNTCachedDecision *) exec.argsArray = [(__bridge NSArray *)message.args_array mutableCopy]; exec.machineId = [[SNTConfigurator configurator] enableMachineIDDecoration] ? self.machineID : nil; + exec.teamId = cd.teamID; [self logWithSantaMessage:&message wrapper:^(SNTPBSantaMessage *sm) { diff --git a/Source/santad/Logs/SNTSyslogEventLog.m b/Source/santad/Logs/SNTSyslogEventLog.m index d54654347..5e5c0653a 100644 --- a/Source/santad/Logs/SNTSyslogEventLog.m +++ b/Source/santad/Logs/SNTSyslogEventLog.m @@ -170,11 +170,15 @@ - (void)logExecution:(santa_message_t)message withDecision:(SNTCachedDecision *) [outLog appendFormat:@"|sha256=%@", cd.sha256]; - if (cd.certSHA256) { + if (cd.certSHA256.length) { [outLog appendFormat:@"|cert_sha256=%@|cert_cn=%@", cd.certSHA256, [self sanitizeString:cd.certCommonName]]; } + if (cd.teamID.length) { + [outLog appendFormat:@"|teamid=%@", cd.teamID]; + } + if (cd.quarantineURL) { [outLog appendFormat:@"|quarantine_url=%@", [self sanitizeString:cd.quarantineURL]]; } From 234f81ea7cea1b2aed7aa795f11a463fe4603d85 Mon Sep 17 00:00:00 2001 From: Pete Markowsky Date: Fri, 15 Jul 2022 15:13:55 -0400 Subject: [PATCH 17/20] Ensure KVO works for USB config options (#853) Ensure KVO works for USB config options. --- Source/common/SNTConfigurator.m | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Source/common/SNTConfigurator.m b/Source/common/SNTConfigurator.m index 18d11d9c7..8cda9c8da 100644 --- a/Source/common/SNTConfigurator.m +++ b/Source/common/SNTConfigurator.m @@ -459,6 +459,26 @@ + (NSSet *)keyPathsForValuesAffectingEnableBadSignatureProtection { 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 { From 2c06c39c8203156e2a273f3230f6f303a18a8f7e Mon Sep 17 00:00:00 2001 From: Kathryn Hancox <44557882+kathancox@users.noreply.github.com> Date: Fri, 15 Jul 2022 15:23:33 -0400 Subject: [PATCH 18/20] Added quick getting started page for deployments (#855) --- docs/deployment/configuration.md | 1 + docs/deployment/getting-started.md | 27 +++++++++++++++++++++++++++ docs/deployment/troubleshooting.md | 1 + docs/index.md | 1 + 4 files changed, 30 insertions(+) create mode 100644 docs/deployment/getting-started.md diff --git a/docs/deployment/configuration.md b/docs/deployment/configuration.md index fa4b0bee3..71f009f81 100644 --- a/docs/deployment/configuration.md +++ b/docs/deployment/configuration.md @@ -1,6 +1,7 @@ --- title: Configuration parent: Deployment +nav_order: 2 --- # Configuration diff --git a/docs/deployment/getting-started.md b/docs/deployment/getting-started.md new file mode 100644 index 000000000..f72fd4498 --- /dev/null +++ b/docs/deployment/getting-started.md @@ -0,0 +1,27 @@ +--- +title: Getting Started +parent: Deployment +nav_order: 1 +--- + +# Getting Started + +This page shows you the process to get started with your deployment of Santa. + +**Note:** You can combine each of the profiles listed in the following steps into a single profile containing the different payloads: configuration, TCC, system extension, and notifications. + +1. (Optional) Set up a [sync server](../introduction/syncing-overview.md). Without a sync server, [`santactl`](../binaries/santactl.md) can configure rules locally. + + +1. Create and install your Santa configuration profile to customize your deployment of Santa. See [Configuration](configuration.md) for a reference list of the available options and an [example profile](https://github.com/google/santa/blob/main/docs/deployment/com.google.santa.example.mobileconfig). + +1. Install the TCC and system extension configuration profiles: + + - The TCC profile provides Santa the access it requires to read files anywhere on disk. See an [example TCC profile](https://github.com/google/santa/blob/main/docs/deployment/tcc.configuration-profile-policy.santa.example.mobileconfig). + - The system extension profile allows Santa to run without approval from the user. See an [example system extension profile](https://github.com/google/santa/blob/main/docs/deployment/system-extension-policy.santa.example.mobileconfig). + +1. (Optional) Customize and install the notification settings profile. This allows you to set up notifications to alert when Santa is switching [modes](../concepts/mode.md). See an [example notification settings profile](https://github.com/google/santa/blob/main/docs/deployment/notificationsettings.santa.example.mobileconfig). + + The notifications modified through this profile are different to the main Santa GUI pop-ups. To configure the [Santa GUI](../binaries/santa-gui.md) notifications, use the [configuration profile](configuration.md) (in step 2). + +1. Install the latest Santa package from [GitHub](https://github.com/google/santa/releases) (where you can also find release notes). The package is distributed as a `PKG` wrapped inside a `DMG`, both of which are properly signed and can be validated. \ No newline at end of file diff --git a/docs/deployment/troubleshooting.md b/docs/deployment/troubleshooting.md index d28c9f673..6dae592dd 100644 --- a/docs/deployment/troubleshooting.md +++ b/docs/deployment/troubleshooting.md @@ -1,6 +1,7 @@ --- title: Troubleshooting parent: Deployment +nav_order: 3 --- # Troubleshooting diff --git a/docs/index.md b/docs/index.md index 2bde756af..91ff13f86 100644 --- a/docs/index.md +++ b/docs/index.md @@ -30,6 +30,7 @@ The following pages give an overview of how Santa accomplishes authorization at ### Deployment +* [Getting Started](deployment/getting-started.md): A quick guide to setting up your deployment. * [Configuration](deployment/configuration.md): The local and sync server configuration options, along with example needed mobileconfig files. * [Troubleshooting](deployment/troubleshooting.md): How to troubleshoot issues with your Santa deployment. From a49a59b109de86b154ce6675f0e0143d40d6b178 Mon Sep 17 00:00:00 2001 From: Kathryn Hancox <44557882+kathancox@users.noreply.github.com> Date: Fri, 15 Jul 2022 16:19:17 -0400 Subject: [PATCH 19/20] Docs: Add sync server list (#856) --- docs/deployment/configuration.md | 2 +- docs/deployment/getting-started.md | 3 +-- docs/deployment/sync-servers.md | 20 ++++++++++++++++++++ docs/deployment/troubleshooting.md | 2 +- docs/index.md | 1 + 5 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 docs/deployment/sync-servers.md diff --git a/docs/deployment/configuration.md b/docs/deployment/configuration.md index 71f009f81..ed1f84a0f 100644 --- a/docs/deployment/configuration.md +++ b/docs/deployment/configuration.md @@ -38,7 +38,7 @@ also known as mobileconfig files, which are in an Apple-specific XML format. | BannedBlockMessage | String | This is the message shown to the user when a binary is blocked because of a rule if that rule doesn't provide a custom message. If this is not configured a reasonable default is provided. | | ModeNotificationMonitor | String | The notification text to display when the client goes into Monitor mode. Defaults to "Switching into Monitor mode". | | ModeNotificationLockdown | String | The notification text to display when the client goes into Lockdown mode. Defaults to "Switching into Lockdown mode". | -| SyncBaseURL | String | The base URL of the sync server. | +| SyncBaseURL | String | The base URL of the sync server. | | SyncProxyConfiguration | Dictionary | The proxy configuration to use when syncing. See the [Apple Documentation](https://developer.apple.com/documentation/cfnetwork/global_proxy_settings_constants) for details on the keys that can be used in this dictionary. | | SyncEnableCleanSyncEventUpload | Bool | If true, events will be uploaded to the sync server even if a clean sync is requested. Defaults to false. | | ClientAuthCertificateFile | String | If set, this contains the location of a PKCS#12 certificate to be used for sync authentication. | diff --git a/docs/deployment/getting-started.md b/docs/deployment/getting-started.md index f72fd4498..0ebc2f174 100644 --- a/docs/deployment/getting-started.md +++ b/docs/deployment/getting-started.md @@ -10,8 +10,7 @@ This page shows you the process to get started with your deployment of Santa. **Note:** You can combine each of the profiles listed in the following steps into a single profile containing the different payloads: configuration, TCC, system extension, and notifications. -1. (Optional) Set up a [sync server](../introduction/syncing-overview.md). Without a sync server, [`santactl`](../binaries/santactl.md) can configure rules locally. - +1. (Optional) Set up a [sync server](../introduction/syncing-overview.md). For a list of open-source sync servers, see [Sync Servers](sync-servers.md). Without a sync server, [`santactl`](../binaries/santactl.md) can configure rules locally. 1. Create and install your Santa configuration profile to customize your deployment of Santa. See [Configuration](configuration.md) for a reference list of the available options and an [example profile](https://github.com/google/santa/blob/main/docs/deployment/com.google.santa.example.mobileconfig). diff --git a/docs/deployment/sync-servers.md b/docs/deployment/sync-servers.md new file mode 100644 index 000000000..99d23e317 --- /dev/null +++ b/docs/deployment/sync-servers.md @@ -0,0 +1,20 @@ +--- +title: Sync Servers +parent: Deployment +nav_order: 3 +--- + +# Sync Servers + +Santa's [SyncBaseURL](configuration.md#sync-base-url) configuration flag allows you to synchronize with a management server, which uploads events that have occurred on the machine and downloads new rules. + +There are several open-source servers you can sync with: + +* [Moroz](https://github.com/groob/moroz): A simple golang server that serves hard-coded rules from configuration files. +* [Rudolph](https://github.com/airbnb/rudolph): An AWS-based serverless sync service primarily built on API GW, DynamoDB, and Lambda components to reduce operational burden. Rudolph is designed to be fast, easy-to-use, and cost-efficient. +* [Zentral](https://github.com/zentralopensource/zentral/wiki): A centralized service that pulls data from multiple sources and deploys configurations to multiple services. +* [Zercurity](https://github.com/zercurity/zercurity): A dockerized service for managing and monitoring applications across a large fleet using Santa + Osquery. + +Alternatively, `santactl` can configure rules locally without a sync server. + +See the [Syncing Overview](../introduction/syncing-overview.md) page for an explanation of how syncing works in Santa. \ No newline at end of file diff --git a/docs/deployment/troubleshooting.md b/docs/deployment/troubleshooting.md index 6dae592dd..f667e63cf 100644 --- a/docs/deployment/troubleshooting.md +++ b/docs/deployment/troubleshooting.md @@ -1,7 +1,7 @@ --- title: Troubleshooting parent: Deployment -nav_order: 3 +nav_order: 4 --- # Troubleshooting diff --git a/docs/index.md b/docs/index.md index 91ff13f86..a378aa411 100644 --- a/docs/index.md +++ b/docs/index.md @@ -32,6 +32,7 @@ The following pages give an overview of how Santa accomplishes authorization at * [Getting Started](deployment/getting-started.md): A quick guide to setting up your deployment. * [Configuration](deployment/configuration.md): The local and sync server configuration options, along with example needed mobileconfig files. +* [Sync Servers](deployment/sync-servers.md): A list of open-source sync servers. * [Troubleshooting](deployment/troubleshooting.md): How to troubleshoot issues with your Santa deployment. ### Concepts From cc3177502c1729530f134769a9dcc223c3db3680 Mon Sep 17 00:00:00 2001 From: Russell Hancox Date: Fri, 15 Jul 2022 18:03:34 -0400 Subject: [PATCH 20/20] Tests: Fix un-needed expectation in SNTExecutionControllerTest.allEventUpload (#857) --- Source/santad/SNTExecutionControllerTest.m | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/santad/SNTExecutionControllerTest.m b/Source/santad/SNTExecutionControllerTest.m index 74434a64c..81832f5f1 100644 --- a/Source/santad/SNTExecutionControllerTest.m +++ b/Source/santad/SNTExecutionControllerTest.m @@ -363,7 +363,6 @@ - (void)testAllEventUpload { OCMStub([self.mockFileInfo isMachO]).andReturn(YES); OCMStub([self.mockFileInfo SHA256]).andReturn(@"a"); - OCMExpect([self.mockConfigurator clientMode]).andReturn(SNTClientModeMonitor); OCMExpect([self.mockConfigurator enableAllEventUpload]).andReturn(YES); OCMExpect([self.mockEventDatabase addStoredEvent:OCMOCK_ANY]);