Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include SNTEndpointSecurityManagerTest in the main test_suite #566

Merged
merged 3 commits into from
Aug 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -232,5 +232,6 @@ test_suite(
"//Source/santad:SNTEventTableTest",
"//Source/santad:SNTExecutionControllerTest",
"//Source/santad:SNTRuleTableTest",
"//Source/santad:SNTEndpointSecurityManagerTest",
],
)
8 changes: 8 additions & 0 deletions Source/santad/EventProviders/EndpointSecurityTestUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,11 @@ API_AVAILABLE(macos(10.15))
API_UNAVAILABLE(ios, tvos, watchos)
es_return_t es_subscribe(es_client_t *_Nonnull client, const es_event_type_t *_Nonnull events,
uint32_t event_count);

API_AVAILABLE(macos(10.15))
API_UNAVAILABLE(ios, tvos, watchos) es_return_t es_delete_client(es_client_t *_Nullable client);

API_AVAILABLE(macos(10.15))
API_UNAVAILABLE(ios, tvos, watchos) es_return_t
es_unsubscribe(es_client_t *_Nonnull client, const es_event_type_t *_Nonnull events,
uint32_t event_count);
14 changes: 14 additions & 0 deletions Source/santad/EventProviders/EndpointSecurityTestUtil.mm
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ es_new_client_result_t es_new_client(es_client_t *_Nullable *_Nonnull client,
return ES_NEW_CLIENT_RESULT_SUCCESS;
};

API_AVAILABLE(macos(10.15))
API_UNAVAILABLE(ios, tvos, watchos) es_return_t es_delete_client(es_client_t *_Nullable client) {
[[MockEndpointSecurity mockEndpointSecurity] reset];
return ES_RETURN_SUCCESS;
};

API_AVAILABLE(macos(10.15))
API_UNAVAILABLE(ios, tvos, watchos)
es_respond_result_t es_respond_auth_result(es_client_t *_Nonnull client,
Expand All @@ -132,3 +138,11 @@ es_return_t es_subscribe(es_client_t *_Nonnull client, const es_event_type_t *_N
[MockEndpointSecurity mockEndpointSecurity].subscribed = YES;
return ES_RETURN_SUCCESS;
}
API_AVAILABLE(macos(10.15))
API_UNAVAILABLE(ios, tvos, watchos)
es_return_t es_unsubscribe(es_client_t *_Nonnull client, const es_event_type_t *_Nonnull events,
uint32_t event_count) {
[MockEndpointSecurity mockEndpointSecurity].subscribed = NO;

return ES_RETURN_SUCCESS;
};
156 changes: 115 additions & 41 deletions Source/santad/EventProviders/SNTEndpointSecurityManagerTest.mm
Original file line number Diff line number Diff line change
Expand Up @@ -33,50 +33,109 @@ - (void)setUp {
fclose(stdout);
}

- (void)testDenyOnTimeout {
// There should be two events: an early uncached DENY as the consequence for not
// meeting the decision deadline and an actual cached decision from our message
// handler.
__block int wantNumResp = 2;

MockEndpointSecurity *mockES = [MockEndpointSecurity mockEndpointSecurity];
[mockES reset];
SNTEndpointSecurityManager *snt = [[SNTEndpointSecurityManager alloc] init];

XCTestExpectation *expectation =
[self expectationWithDescription:@"Wait for santa's Auth dispatch queue"];

__block NSMutableArray<ESResponse *> *events = [NSMutableArray array];
[mockES registerResponseCallback:^(ESResponse *r) {
@synchronized(self) {
[events addObject:r];
}

if (events.count >= wantNumResp) {
[expectation fulfill];
}
}];

es_file_t dbFile = {.path = MakeStringToken(kEventsDBPath)};
es_file_t otherBinary = {.path = MakeStringToken(@"somebinary")};
es_process_t proc = {
.executable = &otherBinary,
.is_es_client = false,
.codesigning_flags = 570509313,
.session_id = 12345,
.group_id = 12345,
.ppid = 12345,
.original_ppid = 12345,
.is_platform_binary = false,
};
es_event_unlink_t unlink_event = {.target = &dbFile};
es_events_t event = {.unlink = unlink_event};
es_message_t m = {
.version = 4,
.event_type = ES_EVENT_TYPE_AUTH_UNLINK,
.event = event,
.mach_time = 1234,
.action_type = ES_ACTION_TYPE_AUTH,
.deadline = 1234,
.process = &proc,
.seq_num = 1337,
};

[mockES triggerHandler:&m];

[self waitForExpectationsWithTimeout:10.0
handler:^(NSError *error) {
if (error) {
XCTFail(@"Santa auth test timed out without receiving two "
@"events. Instead, had error: %@",
error);
}
}];

for (ESResponse *resp in events) {
XCTAssertEqual(
resp.result, ES_AUTH_RESULT_DENY,
@"Failed to automatically deny on timeout and also the malicious event afterwards");
}
}

- (void)testDeleteRulesDB {
for (const NSString *testPath in @[ kEventsDBPath, kRulesDBPath ]) {
// There should be two events: an early uncached DENY as the consequence for not
// meeting the decision deadline and an actual cached decision from our message
// handler.
__block int wantNumResp = 2;

MockEndpointSecurity *mockES = [MockEndpointSecurity mockEndpointSecurity];
[mockES reset];
SNTEndpointSecurityManager *snt = [[SNTEndpointSecurityManager alloc] init];

snt.logCallback = ^(santa_message_t m) {
};
snt.decisionCallback = ^(santa_message_t m) {
};

XCTestExpectation *expectation =
[self expectationWithDescription:@"Wait for santa's Auth dispatch queue"];

__block NSMutableArray<ESResponse *> *events = [NSMutableArray array];
XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for response from ES"];
__block ESResponse *got = nil;
[mockES registerResponseCallback:^(ESResponse *r) {
@synchronized(self) {
[events addObject:r];
}

if (events.count >= wantNumResp) {
[expectation fulfill];
}
got = r;
[expectation fulfill];
}];

es_file_t dbFile = {.path = MakeStringToken(testPath)};
es_file_t otherBinary = {.path = MakeStringToken(@"somebinary")};
es_process_t proc = {
.executable = &otherBinary,
.is_es_client = false,
.codesigning_flags = 570509313,
.session_id = 12345,
.group_id = 12345,
.ppid = 12345,
.original_ppid = 12345,
.is_platform_binary = false,
};
es_event_unlink_t unlink_event = {.target = &dbFile};
es_events_t event = {.unlink = unlink_event};
es_message_t m = {
.version = 4,
.event_type = ES_EVENT_TYPE_AUTH_UNLINK,
.event = event,
.mach_time = DISPATCH_TIME_NOW,
.action_type = ES_ACTION_TYPE_AUTH,
.deadline = DISPATCH_TIME_NOW + NSEC_PER_SEC * 60,
.deadline = DISPATCH_TIME_FOREVER,
.process = &proc,
.seq_num = 1337,
};
[mockES triggerHandler:&m];

Expand All @@ -87,12 +146,8 @@ - (void)testDeleteRulesDB {
}
}];

bool wasCached = false;
for (ESResponse *resp in events) {
XCTAssertEqual(resp.result, ES_AUTH_RESULT_DENY, @"Failed to deny deletion of %@", testPath);
wasCached = wasCached | resp.shouldCache;
}
XCTAssertTrue(wasCached, @"Failed to cache deletion decision of %@", testPath);
XCTAssertEqual(got.result, ES_AUTH_RESULT_DENY, @"Failed to deny deletion of %@", testPath);
XCTAssertTrue(got.shouldCache, @"Failed to cache deletion decision of %@", testPath);
}
}

Expand All @@ -101,29 +156,48 @@ - (void)testSkipOtherESEvents {
[mockES reset];
SNTEndpointSecurityManager *snt = [[SNTEndpointSecurityManager alloc] init];

snt.logCallback = ^(santa_message_t m) {
};
snt.decisionCallback = ^(santa_message_t m) {
};
XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for response from ES"];

__block NSMutableArray<ESResponse *> *events = [NSMutableArray array];
__block ESResponse *got = nil;
[mockES registerResponseCallback:^(ESResponse *r) {
@synchronized(self) {
[events addObject:r];
}
got = r;
[expectation fulfill];
}];

es_file_t dbFile = {.path = MakeStringToken(@"/some/other/path")};
es_file_t otherBinary = {.path = MakeStringToken(@"somebinary")};
es_process_t proc = {.executable = &otherBinary, .is_es_client = true};
es_process_t proc = {
.executable = &otherBinary,
.is_es_client = true,
.codesigning_flags = 570509313,
.session_id = 12345,
.group_id = 12345,
.ppid = 12345,
.original_ppid = 12345,
.is_platform_binary = false,
};
es_event_unlink_t unlink_event = {.target = &dbFile};
es_events_t event = {.unlink = unlink_event};
es_message_t m = {
.version = 4,
.event_type = ES_EVENT_TYPE_AUTH_UNLINK,
.event = event,
.mach_time = DISPATCH_TIME_NOW,
.action_type = ES_ACTION_TYPE_AUTH,
.deadline = DISPATCH_TIME_FOREVER,
.process = &proc,
.seq_num = 1337,
};
[mockES triggerHandler:&m];

XCTAssertEqual(events.count, 1);
XCTAssertEqual(events[0].result, ES_AUTH_RESULT_ALLOW);
XCTAssertEqual(events[0].shouldCache, false);
[mockES triggerHandler:&m];
[self waitForExpectationsWithTimeout:10.0
handler:^(NSError *error) {
if (error) {
XCTFail(@"Santa auth test timed out with error: %@", error);
}
}];

XCTAssertEqual(got.result, ES_AUTH_RESULT_ALLOW);
}

@end