Skip to content

Commit 541201b

Browse files
authored
Apply Machine ID config updates in real time (#377)
Support lockless updates to the MachineID value.
1 parent ece4af7 commit 541201b

8 files changed

Lines changed: 144 additions & 70 deletions

File tree

Source/santad/Logs/EndpointSecurity/Logger.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ class Logger {
7272

7373
inline bool ShouldLog(TelemetryEvent event) { return ((event & telemetry_mask_) == event); }
7474

75+
void UpdateMachineIDLogging() const;
76+
7577
friend class santa::LoggerPeer;
7678

7779
private:

Source/santad/Logs/EndpointSecurity/Logger.mm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,8 @@
127127
writer_->Flush();
128128
}
129129

130+
void Logger::UpdateMachineIDLogging() const {
131+
serializer_->UpdateMachineID();
132+
}
133+
130134
} // namespace santa

Source/santad/Logs/EndpointSecurity/Serializers/BasicString.mm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,9 @@ static inline void AppendSocketAddress(std::string &str, es_address_type_t type,
285285
}
286286

287287
std::vector<uint8_t> BasicString::FinalizeString(std::string &str) {
288-
if (EnabledMachineID()) {
288+
if (EnableMachineIDDecoration()) {
289289
str.append("|machineid=");
290-
str.append(MachineID());
290+
str.append(*MachineID());
291291
}
292292
str.append("\n");
293293

Source/santad/Logs/EndpointSecurity/Serializers/Protobuf.mm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,8 +399,8 @@ static inline void EncodeCertificateInfo(::pbv1::CertificateInfo *pb_cert_info,
399399
struct timespec processed_time) {
400400
::pbv1::SantaMessage *santa_msg = Arena::Create<::pbv1::SantaMessage>(arena);
401401

402-
if (EnabledMachineID()) {
403-
EncodeString([santa_msg] { return santa_msg->mutable_machine_id(); }, MachineID());
402+
if (EnableMachineIDDecoration()) {
403+
EncodeString([santa_msg] { return santa_msg->mutable_machine_id(); }, *MachineID());
404404
}
405405
EncodeTimestamp(santa_msg->mutable_event_time(), event_time);
406406
EncodeTimestamp(santa_msg->mutable_processed_time(), processed_time);

Source/santad/Logs/EndpointSecurity/Serializers/Serializer.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define SANTA__SANTAD__LOGS_ENDPOINTSECURITY_SERIALIZERS_SERIALIZER_H
1818

1919
#import <Foundation/Foundation.h>
20+
#include <atomic>
2021

2122
#include <functional>
2223
#include <memory>
@@ -47,8 +48,9 @@ class Serializer {
4748
msg->GetEnrichedMessage());
4849
}
4950

50-
bool EnabledMachineID();
51-
std::string_view MachineID();
51+
bool EnableMachineIDDecoration() const;
52+
std::shared_ptr<std::string> MachineID() const;
53+
void UpdateMachineID();
5254

5355
virtual std::vector<uint8_t> SerializeMessage(const santa::EnrichedClose &) = 0;
5456
virtual std::vector<uint8_t> SerializeMessage(const santa::EnrichedExchange &) = 0;
@@ -123,9 +125,11 @@ class Serializer {
123125
return SerializeMessage(msg);
124126
}
125127

126-
bool enabled_machine_id_ = false;
127-
std::string machine_id_;
128128
SNTDecisionCache *decision_cache_;
129+
std::atomic<bool> enabled_machine_id_{false};
130+
std::shared_ptr<std::string> machine_id_;
131+
// Used to ensure a reference sticks around while no vended copies exists
132+
std::shared_ptr<std::string> saved_machine_id_;
129133
XXH3_state_t *common_hash_state_;
130134
};
131135

Source/santad/Logs/EndpointSecurity/Serializers/Serializer.mm

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@
3030
namespace santa {
3131

3232
Serializer::Serializer(SNTDecisionCache *decision_cache) : decision_cache_(decision_cache) {
33-
if ([[SNTConfigurator configurator] enableMachineIDDecoration]) {
34-
enabled_machine_id_ = true;
35-
machine_id_ = [[[SNTConfigurator configurator] machineID] UTF8String] ?: "";
36-
}
33+
machine_id_ = std::make_shared<std::string>("");
34+
saved_machine_id_ = machine_id_;
35+
36+
UpdateMachineID();
3737

3838
// Prime the xxHash state with invariant data
3939
common_hash_state_ = XXH3_createState();
@@ -46,13 +46,34 @@
4646
XXH3_freeState(common_hash_state_);
4747
}
4848

49-
bool Serializer::EnabledMachineID() {
50-
return enabled_machine_id_;
49+
void Serializer::UpdateMachineID() {
50+
bool should_enable = [[SNTConfigurator configurator] enableMachineIDDecoration];
51+
52+
if (should_enable) {
53+
NSString *configured_machine_id = [[SNTConfigurator configurator] machineID] ?: @"";
54+
auto new_machine_id = std::make_shared<std::string>([configured_machine_id UTF8String]);
55+
56+
// Atomically update the shared_ptr - relaxed ordering is sufficient
57+
// because we separately synchronize with the enabled_machine_id_ flag
58+
std::atomic_store_explicit(&machine_id_, new_machine_id, std::memory_order_relaxed);
59+
60+
// Keep a reference to avoid deallocation
61+
saved_machine_id_ = new_machine_id;
62+
63+
// Use release ordering to establish happens-before relationship with readers
64+
enabled_machine_id_.store(true, std::memory_order_release);
65+
} else {
66+
enabled_machine_id_.store(false, std::memory_order_release);
67+
}
5168
}
5269

53-
std::string_view Serializer::MachineID() {
54-
return std::string_view(machine_id_);
55-
};
70+
bool Serializer::EnableMachineIDDecoration() const {
71+
return enabled_machine_id_.load(std::memory_order_acquire);
72+
}
73+
74+
std::shared_ptr<std::string> Serializer::MachineID() const {
75+
return std::atomic_load_explicit(&machine_id_, std::memory_order_relaxed);
76+
}
5677

5778
std::vector<uint8_t> Serializer::SerializeMessageTemplate(const santa::EnrichedExec &msg) {
5879
SNTCachedDecision *cd;

Source/santad/Santad.mm

Lines changed: 89 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@
5656
using santa::Unit;
5757
using santa::WatchItems;
5858

59+
static NSString *ClientModeName(SNTClientMode mode) {
60+
switch (mode) {
61+
case SNTClientModeMonitor: return @"Monitor";
62+
case SNTClientModeLockdown: return @"Lockdown";
63+
case SNTClientModeStandalone: return @"Standalone";
64+
default: return @"Unknown";
65+
}
66+
}
67+
5968
void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logger> logger,
6069
std::shared_ptr<Metrics> metrics, std::shared_ptr<santa::WatchItems> watch_items,
6170
std::shared_ptr<Enricher> enricher,
@@ -199,18 +208,20 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
199208
SNTClientMode clientMode = (SNTClientMode)[newValue longLongValue];
200209

201210
switch (clientMode) {
202-
case SNTClientModeLockdown:
203-
LOGI(@"Changed client mode to Lockdown, flushing cache.");
204-
auth_result_cache->FlushCache(FlushCacheMode::kAllCaches,
205-
FlushCacheReason::kClientModeChanged);
206-
break;
211+
case SNTClientModeLockdown: [[fallthrough]];
207212
case SNTClientModeStandalone:
208-
LOGI(@"Changed client mode to Standalone, flushing cache.");
213+
LOGI(@"ClientMode changed: %@ -> %@. Flushing caches.",
214+
ClientModeName((SNTClientMode)[oldValue integerValue]),
215+
ClientModeName((SNTClientMode)[newValue integerValue]));
209216
auth_result_cache->FlushCache(FlushCacheMode::kAllCaches,
210217
FlushCacheReason::kClientModeChanged);
211218
break;
212-
case SNTClientModeMonitor: LOGI(@"Changed client mode to Monitor."); break;
213-
default: LOGW(@"Changed client mode to unknown value."); break;
219+
case SNTClientModeMonitor: [[fallthrough]];
220+
default:
221+
LOGI(@"ClientMode changed: %@ -> %@",
222+
ClientModeName((SNTClientMode)[oldValue integerValue]),
223+
ClientModeName((SNTClientMode)[newValue integerValue]));
224+
break;
214225
}
215226

216227
[[notifier_queue.notifierConnection remoteObjectProxy]
@@ -226,8 +237,7 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
226237
return;
227238
}
228239

229-
LOGI(@"SyncBaseURL %s.",
230-
(!oldValue && newValue) ? "added" : (newValue ? "changed" : "removed"));
240+
LOGI(@"SyncBaseURL changed: %@ -> %@", oldValue, newValue);
231241

232242
[syncd_queue reassessSyncServiceConnection];
233243
}],
@@ -238,8 +248,8 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
238248
BOOL oldBool = [oldValue boolValue];
239249
BOOL newBool = [newValue boolValue];
240250
if (oldBool != newBool) {
241-
LOGI(@"EnableStatsCollection changed %s -> %s.",
242-
oldBool ? "NO" : "YES", newBool ? "NO" : "YES");
251+
LOGI(@"EnableStatsCollection changed: %d -> %d", oldBool,
252+
newBool);
243253
[syncd_queue reassessSyncServiceConnection];
244254
}
245255
}],
@@ -250,13 +260,15 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
250260
callback:^(NSString *oldValue, NSString *newValue) {
251261
if ((!newValue && !oldValue) || ([newValue isEqualToString:oldValue])) {
252262
return;
253-
} else if (oldValue && newValue) {
254-
// Sync service should've already been started. Nothing to do here.
255-
LOGI(@"StatsOrganizationID changed.");
256-
return;
257263
} else {
258-
LOGI(@"StatsOrganizationID %s.", newValue ? "added" : "removed");
259-
[syncd_queue reassessSyncServiceConnection];
264+
LOGI(@"StatsOrganizationID changed: %@ -> %@", oldValue, newValue);
265+
// If either the new or old value was missing, we must
266+
// reassess the connection. If they both exist, it means the
267+
// value changed, but the sync service should already be
268+
// running and there is nothing to do.
269+
if (!oldValue || !newValue) {
270+
[syncd_queue reassessSyncServiceConnection];
271+
}
260272
}
261273
}],
262274
[[SNTKVOManager alloc]
@@ -267,10 +279,12 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
267279
BOOL oldBool = [oldValue boolValue];
268280
BOOL newBool = [newValue boolValue];
269281
if (oldBool == NO && newBool == YES) {
270-
LOGI(@"metricsExport changed NO -> YES, starting to export metrics");
282+
LOGI(@"ExportMetrics changed: %d -> %d. Starting to export metrics.", oldBool,
283+
newBool);
271284
metrics->StartPoll();
272285
} else if (oldBool == YES && newBool == NO) {
273-
LOGI(@"metricsExport changed YES -> NO, stopping export of metrics");
286+
LOGI(@"ExportMetrics changed: %d -> %d. Stopping export of metrics", oldBool,
287+
newBool);
274288
metrics->StopPoll();
275289
}
276290
}],
@@ -281,8 +295,8 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
281295
callback:^(NSNumber *oldValue, NSNumber *newValue) {
282296
uint64_t oldInterval = [oldValue unsignedIntValue];
283297
uint64_t newInterval = [newValue unsignedIntValue];
284-
LOGI(@"MetricExportInterval changed from %llu to %llu restarting export",
285-
oldInterval, newInterval);
298+
LOGI(@"MetricExportInterval changed: %llu -> %llu. Restarting export.", oldInterval,
299+
newInterval);
286300
metrics->SetInterval(newInterval);
287301
}],
288302
[[SNTKVOManager alloc]
@@ -295,7 +309,7 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
295309
return;
296310
}
297311

298-
LOGI(@"Changed allowlist regex, flushing cache");
312+
LOGI(@"AllowedPathRegex changed. Flushing caches.");
299313
auth_result_cache->FlushCache(FlushCacheMode::kAllCaches,
300314
FlushCacheReason::kPathRegexChanged);
301315
}],
@@ -309,7 +323,7 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
309323
return;
310324
}
311325

312-
LOGI(@"Changed denylist regex, flushing cache");
326+
LOGI(@"BlockedPathRegex changed. Flushing caches.");
313327
auth_result_cache->FlushCache(FlushCacheMode::kAllCaches,
314328
FlushCacheReason::kPathRegexChanged);
315329
}],
@@ -365,7 +379,7 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
365379
return;
366380
}
367381

368-
LOGI(@"StaticRules set has changed, flushing cache.");
382+
LOGI(@"StaticRules changed. Flushing caches.");
369383
auth_result_cache->FlushCache(
370384
FlushCacheMode::kAllCaches,
371385
FlushCacheReason::kStaticRulesChanged);
@@ -382,7 +396,7 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
382396
return;
383397
}
384398

385-
LOGW(@"EventLogType config changed (%ld --> %ld). Restarting...", oldLogType,
399+
LOGW(@"EventLogType config changed: %ld -> %ld. Restarting...", oldLogType,
386400
newLogType);
387401

388402
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
@@ -409,7 +423,7 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
409423
return;
410424
}
411425

412-
LOGI(@"EntitlementsTeamIDFilter changed. '%@' --> '%@'. Flushing caches.", oldValue,
426+
LOGI(@"EntitlementsTeamIDFilter changed: '%@' -> '%@'. Flushing caches.", oldValue,
413427
newValue);
414428

415429
// Get the value from the configurator since that method ensures proper structure
@@ -432,7 +446,7 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
432446
return;
433447
}
434448

435-
LOGI(@"EntitlementsPrefixFilter changed. '%@' --> '%@'. Flushing caches.", oldValue,
449+
LOGI(@"EntitlementsPrefixFilter changed: '%@' -> '%@'. Flushing caches.", oldValue,
436450
newValue);
437451

438452
// Get the value from the configurator since that method ensures proper structure
@@ -484,7 +498,7 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
484498
return;
485499
}
486500

487-
LOGI(@"enableForkAndExitLogging changed %d -> %d", oldBool,
501+
LOGI(@"EnableForkAndExitLogging changed: %d -> %d", oldBool,
488502
newBool);
489503
logger->SetTelemetryMask(santa::TelemetryConfigToBitmask(
490504
configurator.telemetry, newBool));
@@ -500,40 +514,67 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
500514
return;
501515
}
502516

503-
LOGI(@"enableSilentTTYMode changed %d -> %d", oldBool, newBool);
517+
LOGI(@"EnableSilentTTYMode changed: %d -> %d", oldBool, newBool);
504518

505519
tty_writer->EnableSilentTTYMode(newBool);
506520
}],
521+
[[SNTKVOManager alloc] initWithObject:configurator
522+
selector:@selector(enableMachineIDDecoration)
523+
type:[NSNumber class]
524+
callback:^(NSNumber *oldValue, NSNumber *newValue) {
525+
BOOL oldBool = [oldValue boolValue];
526+
BOOL newBool = [newValue boolValue];
527+
528+
if (oldBool == newBool) {
529+
return;
530+
}
531+
532+
LOGI(@"EnableMachineIDDecoration changed: %d -> %d", oldBool,
533+
newBool);
534+
535+
logger->UpdateMachineIDLogging();
536+
}],
537+
[[SNTKVOManager alloc] initWithObject:configurator
538+
selector:@selector(machineID)
539+
type:[NSString class]
540+
callback:^(NSString *oldValue, NSString *newValue) {
541+
if ((!newValue && !oldValue) ||
542+
([newValue isEqualToString:oldValue])) {
543+
return;
544+
}
545+
546+
LOGI(@"MachineID changed: %@ -> %@", oldValue, newValue);
547+
548+
logger->UpdateMachineIDLogging();
549+
}],
507550
]];
508551

509552
if (@available(macOS 13.0, *)) {
510553
// Only watch file access auth keys on mac 13 and newer
511554
[kvoObservers addObjectsFromArray:@[
512-
[[SNTKVOManager alloc]
513-
initWithObject:configurator
514-
selector:@selector(fileAccessPolicyPlist)
515-
type:[NSString class]
516-
callback:^(NSString *oldValue, NSString *newValue) {
517-
if ([configurator fileAccessPolicy]) {
518-
// Ignore any changes to this key if fileAccessPolicy is set
519-
return;
520-
}
555+
[[SNTKVOManager alloc] initWithObject:configurator
556+
selector:@selector(fileAccessPolicyPlist)
557+
type:[NSString class]
558+
callback:^(NSString *oldValue, NSString *newValue) {
559+
if ([configurator fileAccessPolicy]) {
560+
// Ignore any changes to this key if fileAccessPolicy is set
561+
return;
562+
}
521563

522-
if ((oldValue && !newValue) ||
523-
(newValue && ![oldValue isEqualToString:newValue])) {
524-
LOGI(@"Filesystem monitoring policy config path changed: %@ -> %@", oldValue,
525-
newValue);
526-
watch_items->SetConfigPath(newValue);
527-
}
528-
}],
564+
if ((oldValue && !newValue) ||
565+
(newValue && ![oldValue isEqualToString:newValue])) {
566+
LOGI(@"FileAccessPolicyPlist changed: %@ -> %@", oldValue,
567+
newValue);
568+
watch_items->SetConfigPath(newValue);
569+
}
570+
}],
529571
[[SNTKVOManager alloc] initWithObject:configurator
530572
selector:@selector(fileAccessPolicy)
531573
type:[NSDictionary class]
532574
callback:^(NSDictionary *oldValue, NSDictionary *newValue) {
533575
if ((oldValue && !newValue) ||
534576
(newValue && ![oldValue isEqualToDictionary:newValue])) {
535-
LOGI(
536-
@"Filesystem monitoring policy embedded config changed");
577+
LOGI(@"FileAccessPolicy changed");
537578
watch_items->SetConfig(newValue);
538579
}
539580
}],

0 commit comments

Comments
 (0)