|
25 | 25 | #include <memory> |
26 | 26 | #include <set> |
27 | 27 |
|
| 28 | +#import "Source/common/Platform.h" |
28 | 29 | #import "Source/common/SNTConfigurator.h" |
29 | 30 | #include "Source/common/TestUtils.h" |
30 | 31 | #include "Source/common/es/Client.h" |
@@ -90,7 +91,8 @@ - (void)testEnable { |
90 | 91 | [[SNTEndpointSecurityTamperResistance alloc] initWithESAPI:mockESApi |
91 | 92 | metrics:nullptr |
92 | 93 | logger:nullptr |
93 | | - antiSuspendSigningIDs:nil]; |
| 94 | + antiSuspendSigningIDs:nil |
| 95 | + allowDelegatedSignals:NO]; |
94 | 96 | id mockTamperClient = OCMPartialMock(tamperClient); |
95 | 97 |
|
96 | 98 | [mockTamperClient enable]; |
@@ -136,7 +138,8 @@ - (void)testEnableWithAntiSuspendSigningIDs { |
136 | 138 | initWithESAPI:mockESApi |
137 | 139 | metrics:nullptr |
138 | 140 | logger:nullptr |
139 | | - antiSuspendSigningIDs:@[ @"ABCDE12345:com.example.protected" ]]; |
| 141 | + antiSuspendSigningIDs:@[ @"ABCDE12345:com.example.protected" ] |
| 142 | + allowDelegatedSignals:NO]; |
140 | 143 | id mockTamperClient = OCMPartialMock(tamperClient); |
141 | 144 |
|
142 | 145 | [mockTamperClient enable]; |
@@ -178,7 +181,8 @@ - (void)testSetAntiSuspendSigningIDsAfterEnable { |
178 | 181 | [[SNTEndpointSecurityTamperResistance alloc] initWithESAPI:mockESApi |
179 | 182 | metrics:nullptr |
180 | 183 | logger:nullptr |
181 | | - antiSuspendSigningIDs:nil]; |
| 184 | + antiSuspendSigningIDs:nil |
| 185 | + allowDelegatedSignals:NO]; |
182 | 186 | id mockTamperClient = OCMPartialMock(tamperClient); |
183 | 187 |
|
184 | 188 | [mockTamperClient enable]; |
@@ -237,7 +241,8 @@ - (void)testHandleMessage { |
237 | 241 | [[SNTEndpointSecurityTamperResistance alloc] initWithESAPI:mockESApi |
238 | 242 | metrics:nullptr |
239 | 243 | logger:nullptr |
240 | | - antiSuspendSigningIDs:nil]; |
| 244 | + antiSuspendSigningIDs:nil |
| 245 | + allowDelegatedSignals:NO]; |
241 | 246 |
|
242 | 247 | id mockTamperClient = OCMPartialMock(tamperClient); |
243 | 248 |
|
@@ -528,9 +533,80 @@ - (void)testHandleMessage { |
528 | 533 |
|
529 | 534 | XCTAssertSemaTrue(semaMetrics, 5, "Metrics not recorded within expected window"); |
530 | 535 | XCTAssertEqual(gotAuthResult, kv.second); |
531 | | - XCTAssertEqual(gotCachable, kv.second == ES_AUTH_RESULT_ALLOW); |
| 536 | + XCTAssertFalse(gotCachable); |
| 537 | + } |
| 538 | + } |
| 539 | + |
| 540 | + // Check SIGNAL tamper events with v9 instigator field |
| 541 | +#if HAVE_MACOS_15_5 |
| 542 | + { |
| 543 | + esMsg.event_type = ES_EVENT_TYPE_AUTH_SIGNAL; |
| 544 | + esMsg.version = 9; |
| 545 | + |
| 546 | + struct InstigatorCase { |
| 547 | + const char* desc; |
| 548 | + bool allowDelegatedSignals; |
| 549 | + bool hasInstigator; |
| 550 | + bool instigatorIsPlatform; |
| 551 | + const char* instigatorTeamID; |
| 552 | + const char* instigatorSigningID; |
| 553 | + es_auth_result_t expected; |
| 554 | + }; |
| 555 | + |
| 556 | + static const InstigatorCase cases[] = { |
| 557 | + // Direct signal from launchd (no instigator) -> ALLOW (shutdown path). |
| 558 | + {"direct from launchd", /*allow=*/false, /*hasInst=*/false, |
| 559 | + /*plat=*/false, "", "", ES_AUTH_RESULT_ALLOW}, |
| 560 | + // Delegated by launchd itself -> ALLOW (baseline). |
| 561 | + {"baseline launchd", false, true, true, "", "com.apple.xpc.launchd", ES_AUTH_RESULT_ALLOW}, |
| 562 | + // Delegated by smd -> ALLOW (baseline). |
| 563 | + {"baseline smd", false, true, true, "", "com.apple.xpc.smd", ES_AUTH_RESULT_ALLOW}, |
| 564 | + // Unlisted platform binary with AllowDelegatedSignals=NO -> DENY. |
| 565 | + {"unlisted platform, AllowDelegatedSignals=NO", false, true, true, "", "com.apple.unknown", |
| 566 | + ES_AUTH_RESULT_DENY}, |
| 567 | + // Same instigator with AllowDelegatedSignals=YES -> ALLOW. |
| 568 | + {"unlisted platform, AllowDelegatedSignals=YES", true, true, true, "", "com.apple.unknown", |
| 569 | + ES_AUTH_RESULT_ALLOW}, |
| 570 | + // Third-party-signed instigator with AllowDelegatedSignals=YES -> DENY |
| 571 | + // (the config only relaxes when is_platform_binary == true). |
| 572 | + {"third party, AllowDelegatedSignals=YES", true, true, false, "ABCDE12345", "com.evil.app", |
| 573 | + ES_AUTH_RESULT_DENY}, |
| 574 | + }; |
| 575 | + |
| 576 | + for (const auto& c : cases) { |
| 577 | + [tamperClient setAllowDelegatedSignals:c.allowDelegatedSignals]; |
| 578 | + Message msg(mockESApi, &esMsg); |
| 579 | + |
| 580 | + es_process_t target_proc = MakeESProcess(&file); |
| 581 | + target_proc.audit_token = MakeAuditToken(getpid(), 42); |
| 582 | + esMsg.event.signal.target = &target_proc; |
| 583 | + esMsg.process->audit_token = MakeAuditToken(1, 42); // sender = launchd |
| 584 | + |
| 585 | + es_file_t instigatorFile = MakeESFile("/usr/libexec/instigator"); |
| 586 | + es_process_t instigator_proc = MakeESProcess(&instigatorFile); |
| 587 | + instigator_proc.team_id = MakeESStringToken(c.instigatorTeamID); |
| 588 | + instigator_proc.signing_id = MakeESStringToken(c.instigatorSigningID); |
| 589 | + instigator_proc.is_platform_binary = c.instigatorIsPlatform; |
| 590 | + esMsg.event.signal.instigator = c.hasInstigator ? &instigator_proc : NULL; |
| 591 | + |
| 592 | + [mockTamperClient handleMessage:std::move(msg) |
| 593 | + recordEventMetrics:^(EventDisposition d) { |
| 594 | + XCTAssertEqual(d, |
| 595 | + c.expected == ES_AUTH_RESULT_DENY ? EventDisposition::kProcessed |
| 596 | + : EventDisposition::kDropped, |
| 597 | + @"%s", c.desc); |
| 598 | + dispatch_semaphore_signal(semaMetrics); |
| 599 | + }]; |
| 600 | + |
| 601 | + XCTAssertSemaTrue(semaMetrics, 5, "Metrics not recorded within expected window"); |
| 602 | + XCTAssertEqual(gotAuthResult, c.expected, @"%s", c.desc); |
532 | 603 | } |
| 604 | + |
| 605 | + // Reset shared state for downstream test blocks. |
| 606 | + esMsg.event.signal.instigator = NULL; |
| 607 | + esMsg.version = MaxSupportedESMessageVersionForCurrentOS(); |
533 | 608 | } |
| 609 | +#endif // HAVE_MACOS_15_5 |
534 | 610 |
|
535 | 611 | // Check PROC_SUSPEND_RESUME tamper events - EnableAntiTamperProcessSuspendResume = NO |
536 | 612 | { |
@@ -742,7 +818,8 @@ - (void)testHandleMessageTruncatedPath { |
742 | 818 | [[SNTEndpointSecurityTamperResistance alloc] initWithESAPI:mockESApi |
743 | 819 | metrics:nullptr |
744 | 820 | logger:nullptr |
745 | | - antiSuspendSigningIDs:nil]; |
| 821 | + antiSuspendSigningIDs:nil |
| 822 | + allowDelegatedSignals:NO]; |
746 | 823 |
|
747 | 824 | id mockTamperClient = OCMPartialMock(tamperClient); |
748 | 825 |
|
|
0 commit comments