diff --git a/Source/common/SNTConfigurator.m b/Source/common/SNTConfigurator.m index ec99b2c55..8c3ce2fee 100644 --- a/Source/common/SNTConfigurator.m +++ b/Source/common/SNTConfigurator.m @@ -1059,7 +1059,8 @@ - (SNTOverrideFileAccessAction)overrideFileAccessAction { } } - if ([action isEqualToString:@"auditonly"]) { + // Note: `auditonly` without an underscore is a deprecated, but still accepted form. + if ([action isEqualToString:@"audit_only"] || [action isEqualToString:@"auditonly"]) { return SNTOverrideFileAccessActionAuditOnly; } else if ([action isEqualToString:@"disable"]) { return SNTOverrideFileAccessActionDiable; diff --git a/Source/santasyncservice/SNTSyncPreflight.mm b/Source/santasyncservice/SNTSyncPreflight.mm index 6d1cda409..649c61b5f 100644 --- a/Source/santasyncservice/SNTSyncPreflight.mm +++ b/Source/santasyncservice/SNTSyncPreflight.mm @@ -33,6 +33,11 @@ using santa::NSStringToUTF8String; using santa::StringToNSString; +// Ignoring warning regarding deprecated declarations because of large number of +// reported issues due to checking deprecated proto fields. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + /* Clean Sync Implementation Notes @@ -40,30 +45,30 @@ The clean sync implementation seems a bit complex at first glance, but boils down to the following rules: -1. If the server says to do a "clean" sync, a "clean" sync is performed, unless the - client specified a "clean all" sync, in which case "clean all" is performed. -2. If the server responded that it is performing a "clean all" sync, a "clean all" is performed. -3. All other server responses result in a "normal" sync. +1. If the server says to do a "CLEAN" sync, a "CLEAN" sync is performed, unless the + client specified a "CLEAN_ALL" sync, in which case "CLEAN_ALL" is performed. +2. If the server responded that it is performing a "CLEAN_ALL" sync, a "CLEAN_ALL" is performed. +3. All other server responses result in a "NORMAL" sync. The following table expands upon the above logic to list most of the permutations: | Client Sync State | Clean Sync Request? | Server Response | Sync Type Performed | | ----------------- | ------------------- | ------------------ | ------------------- | -| normal | No | normal OR | normal | -| normal | No | clean | clean | -| normal | No | clean_all | clean_all | -| normal | No | clean_sync (dep) | clean | -| normal | Yes | New AND Dep Key | Dep key ignored | -| clean | Yes | normal OR | normal | -| clean | Yes | clean | clean | -| clean | Yes | clean_all | clean_all | -| clean | Yes | clean_sync (dep) | clean | -| clean | Yes | New AND Dep Key | Dep key ignored | -| clean_all | Yes | normal OR | normal | -| clean_all | Yes | clean | clean_all | -| clean_all | Yes | clean_all | clean_all | -| clean_all | Yes | clean_sync (dep) | clean_all | -| clean_all | Yes | New AND Dep Key | Dep key ignored | +| NORMAL | No | NORMAL OR | NORMAL | +| NORMAL | No | CLEAN | CLEAN | +| NORMAL | No | CLEAN_ALL | CLEAN_ALL | +| NORMAL | No | clean_sync (dep) | CLEAN | +| NORMAL | Yes | New AND Dep Key | Dep key ignored | +| CLEAN | Yes | NORMAL OR | NORMAL | +| CLEAN | Yes | CLEAN | CLEAN | +| CLEAN | Yes | CLEAN_ALL | CLEAN_ALL | +| CLEAN | Yes | clean_sync (dep) | CLEAN | +| CLEAN | Yes | New AND Dep Key | Dep key ignored | +| CLEAN_ALL | Yes | NORMAL OR | NORMAL | +| CLEAN_ALL | Yes | CLEAN | CLEAN_ALL | +| CLEAN_ALL | Yes | CLEAN_ALL | CLEAN_ALL | +| CLEAN_ALL | Yes | clean_sync (dep) | CLEAN_ALL | +| CLEAN_ALL | Yes | New AND Dep Key | Dep key ignored | */ @implementation SNTSyncPreflight @@ -130,12 +135,27 @@ - (BOOL)sync { return NO; } - self.syncState.enableBundles = @(resp.enable_bundles() || resp.deprecated_bundles_enabled()); - self.syncState.enableTransitiveRules = - @(resp.enable_transitive_rules() || resp.deprecated_enabled_transitive_whitelisting() || - resp.deprecated_transitive_whitelisting_enabled()); - self.syncState.enableAllEventUpload = @(resp.enable_all_event_upload()); - self.syncState.disableUnknownEventUpload = @(resp.disable_unknown_event_upload()); + if (resp.has_enable_bundles()) { + self.syncState.enableBundles = @(resp.enable_bundles()); + } else if (resp.has_deprecated_bundles_enabled()) { + self.syncState.enableBundles = @(resp.deprecated_bundles_enabled()); + } + + if (resp.has_enable_transitive_rules()) { + self.syncState.enableTransitiveRules = @(resp.enable_transitive_rules()); + } else if (resp.has_deprecated_enabled_transitive_whitelisting()) { + self.syncState.enableTransitiveRules = @(resp.deprecated_enabled_transitive_whitelisting()); + } else if (resp.has_deprecated_transitive_whitelisting_enabled()) { + self.syncState.enableTransitiveRules = @(resp.deprecated_transitive_whitelisting_enabled()); + } + + if (resp.has_enable_all_event_upload()) { + self.syncState.enableAllEventUpload = @(resp.enable_all_event_upload()); + } + + if (resp.has_disable_unknown_event_upload()) { + self.syncState.disableUnknownEventUpload = @(resp.disable_unknown_event_upload()); + } self.syncState.eventBatchSize = kDefaultEventBatchSize; if (resp.batch_size() > 0) { @@ -179,50 +199,58 @@ - (BOOL)sync { if (resp.has_block_usb_mount()) { self.syncState.blockUSBMount = @(resp.block_usb_mount()); - } - self.syncState.remountUSBMode = [NSMutableArray array]; - for (const std::string &mode : resp.remount_usb_mode()) { - [(NSMutableArray *)self.syncState.remountUSBMode addObject:StringToNSString(mode)]; + self.syncState.remountUSBMode = [NSMutableArray array]; + for (const std::string &mode : resp.remount_usb_mode()) { + [(NSMutableArray *)self.syncState.remountUSBMode addObject:StringToNSString(mode)]; + } } if (resp.has_override_file_access_action()) { - self.syncState.overrideFileAccessAction = StringToNSString(resp.override_file_access_action()); + switch (resp.override_file_access_action()) { + case ::pbv1::NONE: self.syncState.overrideFileAccessAction = @"NONE"; break; + case ::pbv1::AUDIT_ONLY: self.syncState.overrideFileAccessAction = @"AUDIT_ONLY"; break; + case ::pbv1::DISABLE: self.syncState.overrideFileAccessAction = @"DISABLE"; break; + case ::pbv1::FILE_ACCESS_ACTION_UNSPECIFIED: // Intentional fallthrough + default: self.syncState.overrideFileAccessAction = nil; break; + } } // Default sync type is SNTSyncTypeNormal // // Logic overview: // The requested sync type (clean or normal) is merely informative. The server - // can choose to respond with a normal, clean or clean_all. + // can choose to respond with a NORMAL, CLEAN or CLEAN_ALL. // // If the server responds that it will perform a clean sync, santa will - // treat it as either a clean or clean_all depending on which was requested. + // treat it as either a clean or CLEAN_ALL depending on which was requested. // // The server can also "override" the requested clean operation. If a normal // sync was requested, but the server responded that it was doing a clean or - // clean_all sync, that will take precedence. Similarly, if only a clean sync - // was requested, the server can force a "clean_all" operation to take place. - self.syncState.syncType = SNTSyncTypeNormal; + // CLEAN_ALL sync, that will take precedence. Similarly, if only a clean sync + // was requested, the server can force a "CLEAN_ALL" operation to take place. // If kSyncType response key exists, it overrides the kCleanSyncDeprecated value // First check if the kSyncType reponse key exists. If so, it takes precedence // over the kCleanSyncDeprecated key. - std::string responseSyncType = resp.sync_type(); - if (!responseSyncType.empty()) { - // If the client wants to Clean All, this takes precedence. The server - // cannot override the client wanting to remove all rules. - if (responseSyncType == "clean") { - SLOGD(@"Clean sync requested by server"); - if (requestSyncType == SNTSyncTypeCleanAll) { - self.syncState.syncType = SNTSyncTypeCleanAll; - } else { - self.syncState.syncType = SNTSyncTypeClean; - } - } else if (responseSyncType == "clean_all") { - self.syncState.syncType = SNTSyncTypeCleanAll; + if (resp.has_sync_type()) { + switch (resp.sync_type()) { + case ::pbv1::CLEAN: + // If the client wants to Clean All, this takes precedence. The server + // cannot override the client wanting to remove all rules. + SLOGD(@"Clean sync requested by server"); + if (requestSyncType == SNTSyncTypeCleanAll) { + self.syncState.syncType = SNTSyncTypeCleanAll; + } else { + self.syncState.syncType = SNTSyncTypeClean; + } + break; + case ::pbv1::CLEAN_ALL: self.syncState.syncType = SNTSyncTypeCleanAll; break; + case ::pbv1::SYNC_TYPE_UNSPECIFIED: // Intentional fallthrough + case ::pbv1::NORMAL: // Intentional fallthrough + default: self.syncState.syncType = SNTSyncTypeNormal; break; } - } else if (resp.deprecated_clean_sync()) { + } else if (resp.has_deprecated_clean_sync()) { // If the deprecated key is set, the type of sync clean performed should be // the type that was requested. This must be set appropriately so that it // can be propagated during the Rule Download stage so SNTRuleTable knows @@ -233,9 +261,14 @@ - (BOOL)sync { } else { self.syncState.syncType = SNTSyncTypeClean; } + } else { + // Fallback if unspecified is a normal sync + self.syncState.syncType = SNTSyncTypeNormal; } return YES; } @end + +#pragma clang diagnostic pop diff --git a/Source/santasyncservice/SNTSyncRuleDownload.mm b/Source/santasyncservice/SNTSyncRuleDownload.mm index 2e60e3cb1..586ce2ad6 100644 --- a/Source/santasyncservice/SNTSyncRuleDownload.mm +++ b/Source/santasyncservice/SNTSyncRuleDownload.mm @@ -145,6 +145,7 @@ - (SNTRule *)ruleFromProtoRule:(::pbv1::Rule)rule { r.identifier = StringToNSString(rule.identifier()); if (!r.identifier.length) r.identifier = StringToNSString(rule.deprecated_sha256()); + if (!r.identifier.length) return nil; SNTRuleState state; switch (rule.policy()) { diff --git a/Source/santasyncservice/SNTSyncTest.mm b/Source/santasyncservice/SNTSyncTest.mm index eb4b13aca..884ddc265 100644 --- a/Source/santasyncservice/SNTSyncTest.mm +++ b/Source/santasyncservice/SNTSyncTest.mm @@ -326,7 +326,7 @@ - (void)testPreflightOverrideFileAccessAction { [self stubRequestBody:respData response:nil error:nil validateBlock:nil]; XCTAssertTrue([sut sync]); - XCTAssertEqualObjects(self.syncState.overrideFileAccessAction, @"AuditOnly"); + XCTAssertEqualObjects(self.syncState.overrideFileAccessAction, @"AUDIT_ONLY"); } - (void)testPreflightOverrideFileAccessActionAbsent { @@ -418,27 +418,48 @@ - (void)testPreflightStateNormalRequestEmptyResponseEmpty { response:@{}]; } -- (void)testPreflightStateNormalRequestEmptyResponseNormal { +- (void)testPreflightStateNormalRequestEmptyResponseNormalDeprecated { [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeNormal expectcleanSyncRequest:NO expectedSyncType:SNTSyncTypeNormal response:@{kSyncType : @"normal"}]; } -- (void)testPreflightStateNormalRequestEmptyResponseClean { +- (void)testPreflightStateNormalRequestEmptyResponseNormal { + [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeNormal + expectcleanSyncRequest:NO + expectedSyncType:SNTSyncTypeNormal + response:@{kSyncType : @"NORMAL"}]; +} + +- (void)testPreflightStateNormalRequestEmptyResponseCleanDeprecated { [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeNormal expectcleanSyncRequest:NO expectedSyncType:SNTSyncTypeClean response:@{kSyncType : @"clean"}]; } -- (void)testPreflightStateNormalRequestEmptyResponseCleanAll { +- (void)testPreflightStateNormalRequestEmptyResponseClean { + [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeNormal + expectcleanSyncRequest:NO + expectedSyncType:SNTSyncTypeClean + response:@{kSyncType : @"CLEAN"}]; +} + +- (void)testPreflightStateNormalRequestEmptyResponseCleanAllDeprecated { [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeNormal expectcleanSyncRequest:NO expectedSyncType:SNTSyncTypeCleanAll response:@{kSyncType : @"clean_all"}]; } +- (void)testPreflightStateNormalRequestEmptyResponseCleanAll { + [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeNormal + expectcleanSyncRequest:NO + expectedSyncType:SNTSyncTypeCleanAll + response:@{kSyncType : @"CLEAN_ALL"}]; +} + - (void)testPreflightStateNormalRequestEmptyResponseCleanDep { [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeNormal expectcleanSyncRequest:NO @@ -453,27 +474,48 @@ - (void)testPreflightStateCleanRequestCleanResponseEmpty { response:@{}]; } -- (void)testPreflightStateCleanRequestCleanResponseNormal { +- (void)testPreflightStateCleanRequestCleanResponseNormalDeprecated { [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeClean expectcleanSyncRequest:YES expectedSyncType:SNTSyncTypeNormal response:@{kSyncType : @"normal"}]; } -- (void)testPreflightStateCleanRequestCleanResponseClean { +- (void)testPreflightStateCleanRequestCleanResponseNormal { + [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeClean + expectcleanSyncRequest:YES + expectedSyncType:SNTSyncTypeNormal + response:@{kSyncType : @"NORMAL"}]; +} + +- (void)testPreflightStateCleanRequestCleanResponseCleanDeprecated { [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeClean expectcleanSyncRequest:YES expectedSyncType:SNTSyncTypeClean response:@{kSyncType : @"clean"}]; } -- (void)testPreflightStateCleanRequestCleanResponseCleanAll { +- (void)testPreflightStateCleanRequestCleanResponseClean { + [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeClean + expectcleanSyncRequest:YES + expectedSyncType:SNTSyncTypeClean + response:@{kSyncType : @"CLEAN"}]; +} + +- (void)testPreflightStateCleanRequestCleanResponseCleanAllDeprecated { [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeClean expectcleanSyncRequest:YES expectedSyncType:SNTSyncTypeCleanAll response:@{kSyncType : @"clean_all"}]; } +- (void)testPreflightStateCleanRequestCleanResponseCleanAll { + [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeClean + expectcleanSyncRequest:YES + expectedSyncType:SNTSyncTypeCleanAll + response:@{kSyncType : @"CLEAN_ALL"}]; +} + - (void)testPreflightStateCleanRequestCleanResponseCleanDep { [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeClean expectcleanSyncRequest:YES @@ -488,27 +530,48 @@ - (void)testPreflightStateCleanAllRequestCleanResponseEmpty { response:@{}]; } -- (void)testPreflightStateCleanAllRequestCleanResponseNormal { +- (void)testPreflightStateCleanAllRequestCleanResponseNormalDeprecated { [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeCleanAll expectcleanSyncRequest:YES expectedSyncType:SNTSyncTypeNormal response:@{kSyncType : @"normal"}]; } -- (void)testPreflightStateCleanAllRequestCleanResponseClean { +- (void)testPreflightStateCleanAllRequestCleanResponseNormal { + [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeCleanAll + expectcleanSyncRequest:YES + expectedSyncType:SNTSyncTypeNormal + response:@{kSyncType : @"NORMAL"}]; +} + +- (void)testPreflightStateCleanAllRequestCleanResponseCleanDeprecated { [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeCleanAll expectcleanSyncRequest:YES expectedSyncType:SNTSyncTypeCleanAll response:@{kSyncType : @"clean"}]; } -- (void)testPreflightStateCleanAllRequestCleanResponseCleanAll { +- (void)testPreflightStateCleanAllRequestCleanResponseClean { + [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeCleanAll + expectcleanSyncRequest:YES + expectedSyncType:SNTSyncTypeCleanAll + response:@{kSyncType : @"CLEAN"}]; +} + +- (void)testPreflightStateCleanAllRequestCleanResponseCleanAllDeprecated { [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeCleanAll expectcleanSyncRequest:YES expectedSyncType:SNTSyncTypeCleanAll response:@{kSyncType : @"clean_all"}]; } +- (void)testPreflightStateCleanAllRequestCleanResponseCleanAll { + [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeCleanAll + expectcleanSyncRequest:YES + expectedSyncType:SNTSyncTypeCleanAll + response:@{kSyncType : @"CLEAN_ALL"}]; +} + - (void)testPreflightStateCleanAllRequestCleanResponseCleanDep { [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeCleanAll expectcleanSyncRequest:YES @@ -528,7 +591,7 @@ - (void)testPreflightStateCleanAllRequestCleanResponseTypeAndDepMismatch { [self cleanSyncPreflightRequiredSyncType:SNTSyncTypeCleanAll expectcleanSyncRequest:YES expectedSyncType:SNTSyncTypeNormal - response:@{kSyncType : @"normal", kCleanSyncDeprecated : @YES}]; + response:@{kSyncType : @"NORMAL", kCleanSyncDeprecated : @YES}]; } - (void)testPreflightLockdown { @@ -549,8 +612,15 @@ - (void)testEventUploadBasic { SNTSyncEventUpload *sut = [[SNTSyncEventUpload alloc] initWithState:self.syncState]; self.syncState.eventBatchSize = 50; + NSSet *allowedClasses = [NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]; + NSData *eventData = [self dataFromFixture:@"sync_eventupload_input_basic.plist"]; - NSArray *events = [NSKeyedUnarchiver unarchiveObjectWithData:eventData]; + + NSError *err; + NSArray *events = [NSKeyedUnarchiver unarchivedObjectOfClasses:allowedClasses + fromData:eventData + error:&err]; + XCTAssertNil(err); OCMStub([self.daemonConnRop databaseEventsPending:([OCMArg invokeBlockWithArgs:events, nil])]); @@ -611,8 +681,16 @@ - (void)testEventUploadBundleAndQuarantineData { SNTSyncEventUpload *sut = [[SNTSyncEventUpload alloc] initWithState:self.syncState]; sut = OCMPartialMock(sut); + NSSet *allowedClasses = [NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]; + NSData *eventData = [self dataFromFixture:@"sync_eventupload_input_quarantine.plist"]; - NSArray *events = [NSKeyedUnarchiver unarchiveObjectWithData:eventData]; + + NSError *err; + NSArray *events = [NSKeyedUnarchiver unarchivedObjectOfClasses:allowedClasses + fromData:eventData + error:&err]; + XCTAssertNil(err); + OCMStub([self.daemonConnRop databaseEventsPending:([OCMArg invokeBlockWithArgs:events, nil])]); [self stubRequestBody:nil @@ -647,8 +725,16 @@ - (void)testEventUploadBatching { self.syncState.eventBatchSize = 1; sut = OCMPartialMock(sut); + NSSet *allowedClasses = [NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]; + NSData *eventData = [self dataFromFixture:@"sync_eventupload_input_basic.plist"]; - NSArray *events = [NSKeyedUnarchiver unarchiveObjectWithData:eventData]; + + NSError *err; + NSArray *events = [NSKeyedUnarchiver unarchivedObjectOfClasses:allowedClasses + fromData:eventData + error:&err]; + XCTAssertNil(err); + OCMStub([self.daemonConnRop databaseEventsPending:([OCMArg invokeBlockWithArgs:events, nil])]); __block int requestCount = 0; diff --git a/Source/santasyncservice/syncv1.proto b/Source/santasyncservice/syncv1.proto index 4bd7e4e76..b5a18e0f4 100644 --- a/Source/santasyncservice/syncv1.proto +++ b/Source/santasyncservice/syncv1.proto @@ -1,3 +1,5 @@ +// LINT: LEGACY_NAMES + syntax = "proto3"; package santa.sync.v1; @@ -56,6 +58,44 @@ message PreflightRequest { string machine_id = 18; } +enum SyncType { + option allow_alias = true; + + // An unspecified SyncType will fallback to default "NORMAL" handling + SYNC_TYPE_UNSPECIFIED = 0; + + // Standard, progressive sync + NORMAL = 1; + normal = 1 [deprecated=true]; + + // Delete all non-transitive, previously received rules before applying the newly received rules. + CLEAN = 2; + clean = 2 [deprecated=true]; + + // Delete all previously received rules before applying the newly received rules. + CLEAN_ALL = 3; + clean_all = 3 [deprecated=true]; +} + +enum FileAccessAction { + option allow_alias = true; + + // An unspecified FileAccessAction will fallback to not changing any settings on the host + FILE_ACCESS_ACTION_UNSPECIFIED = 0; + + // The policy will be applied as written + NONE = 1; + None = 1 [deprecated=true]; + + // Actions that would be denied are logged but allowed + AUDIT_ONLY = 2; + AuditOnly = 2 [deprecated=true]; + + // No action will be taken + DISABLE = 3; + Disable = 3 [deprecated=true]; +} + message PreflightResponse { // The client mode that the client should move into at the end of this sync. // The mode does not change until the Postflight request has been made, to @@ -63,32 +103,26 @@ message PreflightResponse { // into Lockdown. ClientMode client_mode = 1; - // Possible values are "normal" (default if unspecified), "clean", or "clean_all". - // "normal" is a standard progressive sync - // "clean" deletes all previously received rules before applying the newly received rules. - // "clean_all" deletes all rules, including transitive rules, before applying the newly received rules. - // This should be an enum but we won't match lowercase values if specified as an enum unless the - // enum values are also lowercase (which is odd), and this is the behavior from the pre-proto protocol. - string sync_type = 2; + optional SyncType sync_type = 2; // Controls how many events Santa should upload in a single EventUpload request. // If the server doesn't specify, the default is 50. uint32 batch_size = 3; // Enable bundle hashing and bundle rules. - bool enable_bundles = 4; + optional bool enable_bundles = 4; // Enable transitive (ALLOWLIST_COMPILER) rules. // Without this enabled, any received ALLOWLIST_COMPILER rules will be treated as ALLOWLIST. - bool enable_transitive_rules = 5; + optional bool enable_transitive_rules = 5; // Ordinarily, Santa will only upload events about executions that are denied or would be denied if the machine // were in LOCKDOWN mode. With this enabled, Santa will upload details about all events. - bool enable_all_event_upload = 6; + optional bool enable_all_event_upload = 6; // Ordinarily, Santa will only upload events about executions that are denied or would be denied if the machine // were in LOCKDOWN mode. With this enabled, Santa will NOT upload events for binaries that would have been blocked in LOCKDOWN. - bool disable_unknown_event_upload = 7; + optional bool disable_unknown_event_upload = 7; // Specifies the time interval in seconds between full syncs. Defaults to 600 (10 minutes). Cannot be set lower than 60. uint64 full_sync_interval_seconds = 8 [json_name="full_sync_interval"]; @@ -119,7 +153,7 @@ message PreflightResponse { // `disable`: No action will be taken // `auditonly`: Actions that would be denied are logged but allowed // `none`: The policy will be applied as written - optional string override_file_access_action = 15; + optional FileAccessAction override_file_access_action = 15; // These fields are deprecated forms of other fields and exist here solely for backwards compatibility optional bool deprecated_enabled_transitive_whitelisting = 1000 [json_name="enabled_transitive_whitelisting", deprecated=true]; @@ -130,8 +164,8 @@ message PreflightResponse { optional string deprecated_whitelist_regex = 1005 [json_name="whitelist_regex", deprecated=true]; optional string deprecated_blacklist_regex = 1006 [json_name="blacklist_regex", deprecated=true]; - // Deprecated but still supported key that acts like sync_type was set to "clean" unless - // the client had requested a clean sync, in which case it acts like "clean_all" + // Deprecated but still supported key that acts like sync_type was set to "CLEAN" unless + // the client had requested a clean sync, in which case it acts like "CLEAN_ALL" optional bool deprecated_clean_sync = 1007 [json_name="clean_sync", deprecated=true]; } diff --git a/docs/deployment/configuration.md b/docs/deployment/configuration.md index 00ec3c505..d881ad376 100644 --- a/docs/deployment/configuration.md +++ b/docs/deployment/configuration.md @@ -82,7 +82,7 @@ also known as mobileconfig files, which are in an Apple-specific XML format. | FileAccessPolicy | Dictionary | A complete file access configuration policy embedded in the main Santa config. If set, `FileAccessPolicyPlist` will be ignored. | | FileAccessPolicyUpdateIntervalSec | Integer | Number of seconds between re-reading the file access policy config and policies/monitored paths updated. | | FileAccessBlockMessage | String | This is the message shown to the user when a access to a file is blocked because of a rule defined by `FileAccessPolicy` if that rule doesn't provide a custom message. If this is not configured a reasonable default is provided. | -| OverrideFileAccessAction | String | Defines a global override policy that applies to the enforcement of all `FileAccessPolicy` rules. Allowed values are: `auditonly` (no access will be blocked, only logged), `disabled` (no access will be blocked or logged), `none` (enforce policy as defined in each rule). Defaults to `none`. | +| OverrideFileAccessAction | String | Defines a global override policy that applies to the enforcement of all `FileAccessPolicy` rules. Allowed values are: `AUDIT_ONLY` (no access will be blocked, only logged), `DISABLE` (no access will be blocked or logged), `none` (enforce policy as defined in each rule). Defaults to `NONE`. Note: `AUDITONLY` without an underscore is deprecated. | | SyncClientContentEncoding | String | Sets the Content-Encoding header for requests sent to the sync service. Acceptable values are "deflate", "gzip", "none". Defaults to deflate. | | SyncExtraHeaders | Dictionary | Dictionary of additional headers to include in all requests made to the sync server. System managed headers such as Content-Length, Host, WWW-Authenticate etc will be ignored. | | EnableDebugLogging | Bool | If true, the client will log additional debug messages to the Apple Unified Log. For example, transitive rule creation logs can be viewed with `log stream --predicate 'sender=="com.google.santa.daemon"'`. Defaults to false. | @@ -258,24 +258,31 @@ ways to install configuration profiles: ## Sync Server Provided Configuration +The following configuration keys can be set by the sync server and override the +local configuration value. Unless otherwise noted to be held only in memory, the +values set by the sync server are persisted across restarted of the Santa +daemon. For additional information regarding default values and action taken +by the daemon if the key is not set, please refer to the +[Sync Protocol Preflight Response](../development/sync-protocol.md#preflight-response) +documentation. + | Key | Value Type | Description | | ----------------------------------- | ---------- | ---------------------------------------- | -| client\_mode | String | MONITOR or LOCKDOWN, defaults to MONITOR. | -| clean\_sync\*\* | Bool | If set to `True` Santa will clear all local rules and download a fresh copy from the sync server. Defaults to `False`. | -| batch\_size | Integer | The number of rules to download or events to upload per request. Multiple requests will be made if there is more work than can fit in single request. Defaults to 50. | -| upload\_logs\_url\*\* | String | If set, the endpoint to send Santa's current logs. No default. | -| allowed\_path\_regex | String | Same as the "Local Configuration" AllowedPathRegex. No default. | -| blocked\_path\_regex | String | Same as the "Local Configuration" BlockedPathRegex. No default. | -| full\_sync\_interval\* | Integer | The max time to wait before performing a full sync with the server. Defaults to 600 secs (10 minutes) if not set. | +| client\_mode | String | MONITOR or LOCKDOWN. | +| clean\_sync\*\* | Bool | If set to `True` Santa will clear all local rules and download a fresh copy from the sync server. | +| batch\_size | Integer | The number of rules to download or events to upload per request. Multiple requests will be made if there is more work than can fit in single request. | +| allowed\_path\_regex | String | Same as the "Local Configuration" AllowedPathRegex. | +| blocked\_path\_regex | String | Same as the "Local Configuration" BlockedPathRegex. | +| full\_sync\_interval\* | Integer | The max time to wait before performing a full sync with the server. | | fcm\_token\*† | String | The FCM token used by Santa to listen for FCM messages. Unique for every machine. No default. | | fcm\_full\_sync\_interval\*† | Integer | The full sync interval if a fcm\_token is set. Defaults to 14400 secs (4 hours). | | fcm\_global\_rule\_sync\_deadline\*†| Integer | The max time to wait before performing a rule sync when a global rule sync FCM message is received. This allows syncing to be staggered for global events to avoid spikes in server load. Defaults to 600 secs (10 min). | -| enable\_bundles\* | Bool | If set to `True` the bundle scanning feature is enabled. Defaults to `False`. | -| enable\_transitive\_rules | Bool | If set to `True` the transitive rule feature is enabled. Defaults to `False`. | +| enable\_bundles\* | Bool | If set to `True` the bundle scanning feature is enabled. | +| enable\_transitive\_rules | Bool | If set to `True` the transitive rule feature is enabled. | | enable\_all\_event\_upload | Bool | If set to `True` the client will upload events for all executions, including those that are explicitly allowed. | -| block\_usb\_mount | Bool | If set to 'True' blocking USB Mass storage feature is enabled. Defaults to `False`. | -| remount\_usb\_mode | Array | Array of strings for arguments to pass to mount -o (any of "rdonly", "noexec", "nosuid", "nobrowse", "noowners", "nodev", "async", "-j"). when forcibly remounting devices. No default. | -| override\_file\_access\_action | String | Defines a global override policy that applies to the enforcement of all `FileAccessPolicy` rules. Allowed values are: `auditonly` (no access will be blocked, only logged), `disabled` (no access will be blocked or logged), `none` (enforce policy as defined in each rule). Defaults to `none`. | +| block\_usb\_mount | Bool | If set to 'True' blocking USB Mass storage feature is enabled. | +| remount\_usb\_mode | Array | Array of strings for arguments to pass to mount -o (any of "rdonly", "noexec", "nosuid", "nobrowse", "noowners", "nodev", "async", "-j"). when forcibly remounting devices. | +| override\_file\_access\_action | String | Defines a global override policy that applies to the enforcement of all `FileAccessPolicy` rules. Allowed values are: `AUDIT_ONLY` (no access will be blocked, only logged), `DISABLE` (no access will be blocked or logged), `NONE` (enforce policy as defined in each rule). | *Held only in memory. Not persistent upon process restart. diff --git a/docs/development/sync-protocol.md b/docs/development/sync-protocol.md index b8a77c6e0..8df5b1637 100644 --- a/docs/development/sync-protocol.md +++ b/docs/development/sync-protocol.md @@ -128,23 +128,26 @@ The request consists of the following JSON keys: ### `preflight` Response -If all of the data is well formed, the server responds with an HTTP status code of 200 and provides a JSON response. +If all of the data is well formed, the server responds with an HTTP status code +of 200 and provides a JSON response. While none of the preflight response keys +are required, if not set it will result in the listed action being taken by the +client. The JSON object has the following keys: -| Key | Required | Type | Meaning | Example Value | -|---|---|---|---|---| -| enable_bundles | NO | boolean | Enable bundle scanning | true | -| enable_transitive_rules | NO | boolean | Whether or not to enable transitive allowlisting | true | -| batch_size | YES | integer | Number of events to upload at a time | 128 | -| full_sync_interval | YES | integer | Number of seconds between full syncs | 600 | -| client_mode | YES | string | Operating mode to set for the client | either "MONITOR" or "LOCKDOWN" | -| allowed_path_regex | NO | string | Regular expression to allow a binary to execute from a path | "/Users/markowsk/foo/.\*" | -| blocked_path_regex | NO | string | Regular expression to block a binary from executing by path | "/tmp/" | -| block_usb_mount | NO | boolean | Block USB mass storage devices | true | -| remount_usb_mode | NO | string | Force USB mass storage devices to be remounted with the following permissions (see [configuration](../deployment/configuration.md)) | | -| sync_type | NO | string | If set, the type of sync that the client should perform. Must be one of:
1.) `normal` (or not set) The server intends only to send new rules. The client will not drop any existing rules.
2.) `clean` Instructs the client to drop all non-transitive rules. The server intends to entirely sync all rules.
3.) `clean_all` Instructs the client to drop all rules. The server intends to entirely sync all rules.
See [Clean Syncs](#clean-syncs) for more info. | `normal`, `clean` or `clean_all` | -| override_file_access_action | NO | string | Override file access config policy action. Must be:
1.) "Disable" to not log or block any rule violations.
2.) "AuditOnly" to only log violations, not block anything.
3.) "" (empty string) or "None" to not override the config | "Disable", or "AuditOnly", or "" (empty string) | +| Key | If Not Set | Type | Meaning | Example Value | +| --------------------------- | ------------------------------------------- | ------- | ------- | ------------------------------------ | +| enable_bundles | Use previous setting | boolean | Enable bundle scanning | true | +| enable_transitive_rules | Use previous setting | boolean | Whether or not to enable transitive allowlisting | true | +| batch_size | Use a Santa-defined default value | integer | Number of events to upload at a time | 128 | +| full_sync_interval | Defaults to 600 seconds | integer | Number of seconds between full syncs. Note: Santa enforces a minimum value of 60. The default value will be used if a smaller value is provided. | 600 | +| client_mode | Use previous setting | string | Operating mode to set for the client | either `MONITOR` or `LOCKDOWN` | +| allowed_path_regex | Use previous setting | string | Regular expression to allow a binary to execute from a path | "/Users/markowsk/foo/.\*" | +| blocked_path_regex | Use previous setting | string | Regular expression to block a binary from executing by path | "/tmp/" | +| block_usb_mount | Use previous setting | boolean | Block USB mass storage devices | true | +| remount_usb_mode | No attempt to mount with flags will be made | string | Force USB mass storage devices to be remounted with the given permissions (see [configuration](../deployment/configuration.md)). Note that `block_usb_mount` field must also be set for Santa to use this field. | `noexec,rdonly` | +| sync_type | A `NORMAL` sync is assumed | string | If set, the type of sync that the client should perform. Must be one of:
1.) `NORMAL` (or not set) The server intends only to send new rules. The client will not drop any existing rules.
2.) `CLEAN` Instructs the client to drop all non-transitive rules. The server intends to entirely sync all rules.
3.) `CLEAN_ALL` Instructs the client to drop all rules. The server intends to entirely sync all rules.
See [Clean Syncs](#clean-syncs) for more info. | `NORMAL`, `CLEAN` or `CLEAN_ALL` | +| override_file_access_action | Use previous setting | string | Override file access config policy action. Must be:
1.) `DISABLE` to not log or block any rule violations.
2.) `AUDIT_ONLY` to only log violations, not block anything.
3.) `NONE` to not override the config | `DISABLE`, `AUDIT_ONLY`, or `NONE` | #### Example Preflight Response Payload @@ -165,18 +168,18 @@ The JSON object has the following keys: Clean syncs will result in rules being deleted from the host before applying the newly synced rule set from the server. When the server indicates it is performing a clean sync, it means it intends to sync all current rules to the client. -The client maintains a "sync type state" that controls the type of sync it wants to perform (i.e. `normal`, `clean` or `clean_all`). This is typically set by using `santactl sync`, `santactl sync --clean`, or `santactl sync --clean-all` respectively. Either clean sync type state being set will result in the `request_clean_sync` key being set to true in the [Preflight Request](#preflight-request). +The client maintains a "sync type state" that controls the type of sync it wants to perform (i.e. `NORMAL`, `CLEAN` or `CLEAN_ALL`). This is typically set by using `santactl sync`, `santactl sync --clean`, or `santactl sync --clean-all` respectively. Either clean sync type state being set will result in the `request_clean_sync` key being set to true in the [Preflight Request](#preflight-request). -There are three types of syncs the server can set: `normal`, `clean`, and `clean_all`. The server indicates the type of sync it wants to perform by setting the `sync_type` key in the [Preflight Response](#preflight-response). When a sever performs a normal sync, it only intends to send new rules to the client. When a server performs either a `clean` or `clean_all` sync, it intends to send all rules and the client should delete appropriate rules (non-transitive, or all). The server should try to honor the `request_clean_sync` key if set to true in the [Preflight Request](#preflight-request) by setting the `sync_type` to `clean` (or possibly `clean_all` if desired). +There are three types of syncs the server can set: `NORMAL`, `CLEAN`, and `CLEAN_ALL`. The server indicates the type of sync it wants to perform by setting the `sync_type` key in the [Preflight Response](#preflight-response). When a sever performs a `NORMAL` sync, it only intends to send new rules to the client. When a server performs either a `CLEAN` or `CLEAN_ALL` sync, it intends to send all rules and the client should delete appropriate rules (non-transitive, or all). The server should try to honor the `request_clean_sync` key if set to true in the [Preflight Request](#preflight-request) by setting the `sync_type` to `CLEAN` (or possibly `CLEAN_ALL` if desired). The rules for resolving the type of sync that will be performed are as follows: -1. If the server responds with a `sync_type` of `clean`, a clean sync is performed (regardless of whether or not it was requested by the client), unless the client sync type state was `clean_all`, in which case a `clean_all` sync type is performed. -2. If the server responded that it is performing a `clean_all` sync, a `clean all` is performed (regardless of whether or not it was requested by the client) +1. If the server responds with a `sync_type` of `CLEAN`, a clean sync is performed (regardless of whether or not it was requested by the client), unless the client sync type state was `CLEAN_ALL`, in which case a `CLEAN_ALL` sync type is performed. +2. If the server responded that it is performing a `CLEAN_ALL` sync, a `CLEAN_ALL` is performed (regardless of whether or not it was requested by the client) 3. Otherwise, a normal sync is performed -A client that has a `clean` or `clean_all` sync type state set will continue to request a clean sync until it is satisfied by the server. If a client has requested a clean sync, but the server has not responded that it will perform a clean sync, then the client will not delete any rules before applying the new rules received from the server. +A client that has a `CLEAN` or `CLEAN_ALL` sync type state set will continue to request a clean sync until it is satisfied by the server. If a client has requested a clean sync, but the server has not responded that it will perform a clean sync, then the client will not delete any rules before applying the new rules received from the server. -If the deprecated [Preflight Response](#preflight-response) key `clean_sync` is set, it is treated as if the `sync_type` key were set to `clean`. This is a change in behavior to what was previously performed in that not all rules are dropped anymore, only non-transitive rules. Servers should stop using the `clean_sync` key and migrate to using the `sync_type` key. +If the deprecated [Preflight Response](#preflight-response) key `clean_sync` is set, it is treated as if the `sync_type` key were set to `CLEAN`. This is a change in behavior to what was previously performed in that not all rules are dropped anymore, only non-transitive rules. Servers should stop using the `clean_sync` key and migrate to using the `sync_type` key. ## EventUpload