diff --git a/Source/santad/BUILD b/Source/santad/BUILD index 116ba8e6e..2ac33d62b 100644 --- a/Source/santad/BUILD +++ b/Source/santad/BUILD @@ -893,8 +893,10 @@ santa_unit_test( ":Metrics", ":MockEndpointSecurityAPI", ":SNTDatabaseController", + ":SNTDecisionCache", ":SNTEndpointSecurityAuthorizer", ":SantadDeps", + "//Source/common:SNTCachedDecision", "//Source/common:SNTConfigurator", "//Source/common:TestUtils", "@MOLCertificate", diff --git a/Source/santad/SNTPolicyProcessor.m b/Source/santad/SNTPolicyProcessor.m index aa46eac65..ffb17edb5 100644 --- a/Source/santad/SNTPolicyProcessor.m +++ b/Source/santad/SNTPolicyProcessor.m @@ -82,27 +82,24 @@ - (nonnull SNTCachedDecision *)decisionForFileInfo:(nonnull SNTFileInfo *)fileIn cd.teamID = teamID ?: [csInfo.signingInformation objectForKey:(__bridge NSString *)kSecCodeInfoTeamIdentifier]; - teamID = cd.teamID; // Ensure that if no teamID exists that the signing info confirms it is a // platform binary. If not, remove the signingID. - if (!teamID && signingID) { + if (!cd.teamID && cd.signingID) { id platformID = [csInfo.signingInformation objectForKey:(__bridge NSString *)kSecCodeInfoPlatformIdentifier]; if (![platformID isKindOfClass:[NSNumber class]] || [platformID intValue] == 0) { - signingID = nil; + cd.signingID = nil; } } - - cd.signingID = signingID; } } cd.quarantineURL = fileInfo.quarantineDataURL; SNTRule *rule = [self.ruleTable ruleForBinarySHA256:cd.sha256 - signingID:signingID + signingID:cd.signingID certificateSHA256:cd.certSHA256 - teamID:teamID]; + teamID:cd.teamID]; if (rule) { switch (rule.type) { case SNTRuleTypeBinary: diff --git a/Source/santad/SantadTest.mm b/Source/santad/SantadTest.mm index e1254ba50..b5e23ad36 100644 --- a/Source/santad/SantadTest.mm +++ b/Source/santad/SantadTest.mm @@ -24,6 +24,7 @@ #include +#import "Source/common/SNTCachedDecision.h" #import "Source/common/SNTConfigurator.h" #include "Source/common/TestUtils.h" #include "Source/santad/EventProviders/EndpointSecurity/Message.h" @@ -31,6 +32,7 @@ #import "Source/santad/EventProviders/SNTEndpointSecurityAuthorizer.h" #import "Source/santad/Metrics.h" #import "Source/santad/SNTDatabaseController.h" +#import "Source/santad/SNTDecisionCache.h" #include "Source/santad/SantadDeps.h" using santa::santad::SantadDeps; @@ -41,7 +43,7 @@ static const char *kBlockedSigningID = "com.google.blocked_signing_id"; static const char *kNoRuleMatchSigningID = "com.google.no_rule_match_signing_id"; static const char *kBlockedTeamID = "EQHXZ8M8AV"; -static const char *kNoRuleMatchTeamID = "ABC1234XYZ"; +static const char *kAllowedTeamID = "TJNVEKW352"; @interface SantadTest : XCTestCase @property id mockSNTDatabaseController; @@ -62,10 +64,17 @@ - (void)tearDown { - (BOOL)checkBinaryExecution:(NSString *)binaryName wantResult:(es_auth_result_t)wantResult clientMode:(NSInteger)clientMode + cdValidator:(BOOL (^)(SNTCachedDecision *))cdValidator messageSetup:(void (^)(es_message_t *))messageSetupBlock { auto mockESApi = std::make_shared(); mockESApi->SetExpectationsESNewClient(); + id mockDecisionCache = OCMClassMock([SNTDecisionCache class]); + OCMStub([mockDecisionCache sharedCache]).andReturn(mockDecisionCache); + if (cdValidator) { + OCMExpect([mockDecisionCache cacheDecision:[OCMArg checkWithBlock:cdValidator]]); + } + id mockConfigurator = OCMClassMock([SNTConfigurator class]); OCMStub([mockConfigurator configurator]).andReturn(mockConfigurator); @@ -150,6 +159,8 @@ - (BOOL)checkBinaryExecution:(NSString *)binaryName [self waitForExpectations:@[ expectation ] timeout:10.0]; + XCTAssertTrue(OCMVerifyAll(mockDecisionCache), "Unable to verify SNTCachedDecision properties"); + XCTAssertEqual(0, dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)), "Failed waiting for message to be processed..."); @@ -159,10 +170,12 @@ - (BOOL)checkBinaryExecution:(NSString *)binaryName - (BOOL)checkBinaryExecution:(NSString *)binaryName wantResult:(es_auth_result_t)wantResult - clientMode:(NSInteger)clientMode { + clientMode:(NSInteger)clientMode + cdValidator:(BOOL (^)(SNTCachedDecision *))cdValidator { return [self checkBinaryExecution:binaryName wantResult:wantResult clientMode:clientMode + cdValidator:cdValidator messageSetup:nil]; } @@ -174,145 +187,221 @@ - (BOOL)checkBinaryExecution:(NSString *)binaryName - (void)testBinaryWithSHA256BlockRuleIsBlockedInLockdownMode { [self checkBinaryExecution:@"badbinary" wantResult:ES_AUTH_RESULT_DENY - clientMode:SNTClientModeLockdown]; + clientMode:SNTClientModeLockdown + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateBlockBinary; + }]; } - (void)testBinaryWithSHA256BlockRuleIsBlockedInMonitorMode { [self checkBinaryExecution:@"badbinary" wantResult:ES_AUTH_RESULT_DENY - clientMode:SNTClientModeMonitor]; + clientMode:SNTClientModeMonitor + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateBlockBinary; + }]; } - (void)testBinaryWithSHA256AllowRuleIsNotBlockedInLockdownMode { [self checkBinaryExecution:@"goodbinary" wantResult:ES_AUTH_RESULT_ALLOW - clientMode:SNTClientModeLockdown]; + clientMode:SNTClientModeLockdown + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateAllowBinary; + }]; } - (void)testBinaryWithSHA256AllowRuleIsNotBlockedInMonitorMode { [self checkBinaryExecution:@"goodbinary" wantResult:ES_AUTH_RESULT_ALLOW - clientMode:SNTClientModeMonitor]; + clientMode:SNTClientModeMonitor + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateAllowBinary; + }]; } - (void)testBinaryWithCertificateAllowRuleIsNotBlockedInLockdownMode { [self checkBinaryExecution:@"goodcert" wantResult:ES_AUTH_RESULT_ALLOW - clientMode:SNTClientModeLockdown]; + clientMode:SNTClientModeLockdown + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateAllowCertificate; + }]; } - (void)testBinaryWithCertificateAllowRuleIsNotBlockedInMonitorMode { [self checkBinaryExecution:@"goodcert" wantResult:ES_AUTH_RESULT_ALLOW - clientMode:SNTClientModeMonitor]; + clientMode:SNTClientModeMonitor + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateAllowCertificate; + }]; } - (void)testBinaryWithCertificateBlockRuleIsBlockedInLockdownMode { [self checkBinaryExecution:@"badcert" wantResult:ES_AUTH_RESULT_DENY - clientMode:SNTClientModeLockdown]; + clientMode:SNTClientModeLockdown + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateBlockCertificate; + }]; } -- (void)testBinaryWithCertificateBlockRuleIsNotBlockedInMonitorMode { +- (void)testBinaryWithCertificateBlockRuleIsBlockedInMonitorMode { [self checkBinaryExecution:@"badcert" wantResult:ES_AUTH_RESULT_DENY - clientMode:SNTClientModeMonitor]; + clientMode:SNTClientModeMonitor + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateBlockCertificate; + }]; } -- (void)testBinaryWithTeamIDBlockRuleIsBlockedInLockdownMode { - [self checkBinaryExecution:@"banned_teamid" - wantResult:ES_AUTH_RESULT_DENY - clientMode:SNTClientModeLockdown]; +- (void)testBinaryWithTeamIDAllowRuleAndNoSigningIDMatchIsAllowedInLockdownMode { + [self checkBinaryExecution:@"allowed_teamid" + wantResult:ES_AUTH_RESULT_ALLOW + clientMode:SNTClientModeLockdown + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateAllowTeamID; + } + messageSetup:^(es_message_t *msg) { + msg->event.exec.target->team_id = MakeESStringToken(kAllowedTeamID); + msg->event.exec.target->signing_id = MakeESStringToken(kNoRuleMatchSigningID); + }]; } -- (void)testBinaryWithTeamIDBlockRuleIsBlockedInMonitorMode { - [self checkBinaryExecution:@"banned_teamid" - wantResult:ES_AUTH_RESULT_DENY - clientMode:SNTClientModeMonitor]; +- (void)testBinaryWithTeamIDAllowRuleAndNoSigningIDMatchIsAllowedInMonitorMode { + [self checkBinaryExecution:@"allowed_teamid" + wantResult:ES_AUTH_RESULT_ALLOW + clientMode:SNTClientModeMonitor + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateAllowTeamID; + } + messageSetup:^(es_message_t *msg) { + msg->event.exec.target->team_id = MakeESStringToken(kAllowedTeamID); + msg->event.exec.target->signing_id = MakeESStringToken(kNoRuleMatchSigningID); + }]; } -- (void)testBinaryWithSigningIDBlockRuleAndCertAllowedRuleIsBlockedInMonitorMode { - [self checkBinaryExecution:@"cert_hash_allowed_signingid_blocked" - wantResult:ES_AUTH_RESULT_DENY - clientMode:SNTClientModeMonitor - messageSetup:^(es_message_t *msg) { - msg->event.exec.target->team_id = MakeESStringToken(kBlockedTeamID); - msg->event.exec.target->signing_id = MakeESStringToken(kBlockedSigningID); - }]; +- (void)testBinaryWithTeamIDBlockRuleAndNoSigningIDMatchIsBlockedInLockdownMode { + [self checkBinaryExecution:@"banned_teamid" + wantResult:ES_AUTH_RESULT_DENY + clientMode:SNTClientModeLockdown + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateBlockTeamID; + } + messageSetup:^(es_message_t *msg) { + msg->event.exec.target->team_id = MakeESStringToken(kBlockedTeamID); + msg->event.exec.target->signing_id = MakeESStringToken(kNoRuleMatchSigningID); + }]; } -- (void)testBinaryWithSigningIDNoRuleMatchAndCertAllowedRuleIsAllowedInMonitorMode { - [self checkBinaryExecution:@"cert_hash_allowed_signingid_not_matched" - wantResult:ES_AUTH_RESULT_ALLOW - clientMode:SNTClientModeMonitor - messageSetup:^(es_message_t *msg) { - msg->event.exec.target->team_id = MakeESStringToken(kBlockedTeamID); - msg->event.exec.target->signing_id = MakeESStringToken(kNoRuleMatchSigningID); - }]; +- (void)testBinaryWithTeamIDBlockRuleAndNoSigningIDMatchIsBlockedInMonitorMode { + [self checkBinaryExecution:@"banned_teamid" + wantResult:ES_AUTH_RESULT_DENY + clientMode:SNTClientModeMonitor + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateBlockTeamID; + } + messageSetup:^(es_message_t *msg) { + msg->event.exec.target->team_id = MakeESStringToken(kBlockedTeamID); + msg->event.exec.target->signing_id = MakeESStringToken(kNoRuleMatchSigningID); + }]; } -- (void)testBinaryWithSigningIDBlockRuleMatchAndCertAllowedRuleIsAllowedInMonitorMode { - [self checkBinaryExecution:@"binary_hash_allowed_signingid_blocked" - wantResult:ES_AUTH_RESULT_ALLOW - clientMode:SNTClientModeMonitor - messageSetup:^(es_message_t *msg) { - msg->event.exec.target->team_id = MakeESStringToken(kBlockedTeamID); - msg->event.exec.target->signing_id = MakeESStringToken(kBlockedSigningID); - }]; +- (void)testBinaryWithSigningIDBlockRuleIsBlockedInLockdownMode { + [self checkBinaryExecution:@"banned_signingid" + wantResult:ES_AUTH_RESULT_DENY + clientMode:SNTClientModeLockdown + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateBlockSigningID; + } + messageSetup:^(es_message_t *msg) { + msg->event.exec.target->team_id = MakeESStringToken(kBlockedTeamID); + msg->event.exec.target->signing_id = MakeESStringToken(kBlockedSigningID); + }]; } -- (void)testBinaryWithSigningIDNoRuleMatchIsAllowedInMonitorMode { - [self checkBinaryExecution:@"noop" - wantResult:ES_AUTH_RESULT_ALLOW - clientMode:SNTClientModeMonitor - messageSetup:^(es_message_t *msg) { - msg->event.exec.target->team_id = MakeESStringToken(kNoRuleMatchTeamID); - msg->event.exec.target->signing_id = MakeESStringToken(kNoRuleMatchSigningID); - }]; +- (void)testBinaryWithSigningIDBlockRuleIsBlockedInMonitorMode { + [self checkBinaryExecution:@"banned_signingid" + wantResult:ES_AUTH_RESULT_DENY + clientMode:SNTClientModeMonitor + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateBlockSigningID; + } + messageSetup:^(es_message_t *msg) { + msg->event.exec.target->team_id = MakeESStringToken(kBlockedTeamID); + msg->event.exec.target->signing_id = MakeESStringToken(kBlockedSigningID); + }]; } -- (void)testBinaryWithSigningIDNoRuleMatchIsBlockedInLockdownMode { - [self checkBinaryExecution:@"noop" - wantResult:ES_AUTH_RESULT_DENY - clientMode:SNTClientModeLockdown - messageSetup:^(es_message_t *msg) { - msg->event.exec.target->team_id = MakeESStringToken(kNoRuleMatchTeamID); - msg->event.exec.target->signing_id = MakeESStringToken(kNoRuleMatchSigningID); - }]; +- (void)testBinaryWithSigningIDAllowRuleIsAllowedInMonitorMode { + [self checkBinaryExecution:@"allowed_signingid" + wantResult:ES_AUTH_RESULT_ALLOW + clientMode:SNTClientModeMonitor + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateAllowSigningID; + } + messageSetup:^(es_message_t *msg) { + msg->event.exec.target->team_id = MakeESStringToken(kBlockedTeamID); + msg->event.exec.target->signing_id = MakeESStringToken(kAllowedSigningID); + }]; } -- (void)testBinaryWithAllowedSigningIDRuleIsAllowedInLockdownMode { - [self checkBinaryExecution:@"noop" - wantResult:ES_AUTH_RESULT_ALLOW - clientMode:SNTClientModeLockdown - messageSetup:^(es_message_t *msg) { - msg->event.exec.target->team_id = MakeESStringToken(kNoRuleMatchTeamID); - msg->event.exec.target->signing_id = MakeESStringToken(kAllowedSigningID); - }]; +- (void)testBinaryWithSigningIDAllowRuleIsAllowedInLockdownMode { + [self checkBinaryExecution:@"allowed_signingid" + wantResult:ES_AUTH_RESULT_ALLOW + clientMode:SNTClientModeMonitor + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateAllowSigningID; + } + messageSetup:^(es_message_t *msg) { + msg->event.exec.target->team_id = MakeESStringToken(kBlockedTeamID); + msg->event.exec.target->signing_id = MakeESStringToken(kAllowedSigningID); + }]; } - (void)testBinaryWithSHA256AllowRuleAndBlockedTeamIDRuleIsAllowedInLockdownMode { [self checkBinaryExecution:@"banned_teamid_allowed_binary" - wantResult:ES_AUTH_RESULT_ALLOW - clientMode:SNTClientModeLockdown]; + wantResult:ES_AUTH_RESULT_ALLOW + clientMode:SNTClientModeLockdown + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateAllowBinary; + } + messageSetup:^(es_message_t *msg) { + msg->event.exec.target->team_id = MakeESStringToken(kBlockedTeamID); + msg->event.exec.target->signing_id = MakeESStringToken(kNoRuleMatchSigningID); + }]; } - (void)testBinaryWithSHA256AllowRuleAndBlockedTeamIDRuleIsAllowedInMonitorMode { [self checkBinaryExecution:@"banned_teamid_allowed_binary" - wantResult:ES_AUTH_RESULT_ALLOW - clientMode:SNTClientModeMonitor]; + wantResult:ES_AUTH_RESULT_ALLOW + clientMode:SNTClientModeMonitor + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateAllowBinary; + } + messageSetup:^(es_message_t *msg) { + msg->event.exec.target->team_id = MakeESStringToken(kBlockedTeamID); + msg->event.exec.target->signing_id = MakeESStringToken(kNoRuleMatchSigningID); + }]; } -- (void)testBinaryWithoutBlockOrAllowRuleIsAllowedInLockdownMode { +- (void)testBinaryWithoutBlockOrAllowRuleIsBlockedInLockdownMode { [self checkBinaryExecution:@"noop" wantResult:ES_AUTH_RESULT_DENY - clientMode:SNTClientModeLockdown]; + clientMode:SNTClientModeLockdown + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateBlockUnknown; + }]; } - (void)testBinaryWithoutBlockOrAllowRuleIsAllowedInMonitorMode { [self checkBinaryExecution:@"noop" wantResult:ES_AUTH_RESULT_ALLOW - clientMode:SNTClientModeMonitor]; + clientMode:SNTClientModeMonitor + cdValidator:^BOOL(SNTCachedDecision *cd) { + return cd.decision == SNTEventStateAllowUnknown; + }]; } @end diff --git a/Source/santad/testdata/binaryrules/allowed_signingid b/Source/santad/testdata/binaryrules/allowed_signingid new file mode 100755 index 000000000..63c767762 Binary files /dev/null and b/Source/santad/testdata/binaryrules/allowed_signingid differ diff --git a/Source/santad/testdata/binaryrules/allowed_teamid b/Source/santad/testdata/binaryrules/allowed_teamid new file mode 100644 index 000000000..5219783eb Binary files /dev/null and b/Source/santad/testdata/binaryrules/allowed_teamid differ diff --git a/Source/santad/testdata/binaryrules/banned_signingid b/Source/santad/testdata/binaryrules/banned_signingid new file mode 100755 index 000000000..b5aedbfba Binary files /dev/null and b/Source/santad/testdata/binaryrules/banned_signingid differ diff --git a/Source/santad/testdata/binaryrules/binary_hash_allowed_signingid_blocked b/Source/santad/testdata/binaryrules/binary_hash_allowed_signingid_blocked deleted file mode 100755 index 6267b3a6d..000000000 Binary files a/Source/santad/testdata/binaryrules/binary_hash_allowed_signingid_blocked and /dev/null differ diff --git a/Source/santad/testdata/binaryrules/rules.db b/Source/santad/testdata/binaryrules/rules.db index 1d2a7ed48..162a51a89 100644 Binary files a/Source/santad/testdata/binaryrules/rules.db and b/Source/santad/testdata/binaryrules/rules.db differ