Skip to content

Commit

Permalink
Standalone Mode (Beta) (1/3) (#86)
Browse files Browse the repository at this point in the history
This PR creates a new Santa operating mode -- standalone

This adds a new operating mode to Santa called standalone mode.

When running in standalone mode TouchID can be used to approved
binaries. If a
binary is properly signed a SigningID rule is generated otherwise a
SHA256 rule
is generated. 

Standalone mode won't override explicit block rules, but will allow a user to approve anything blocked by default.

Also the sync protocol changes are saved for a future PR. 

Signed-off-by: Pete Markowsky <[email protected]>
Co-authored-by: Russell Hancox <[email protected]>
  • Loading branch information
pmarkowsky and russellhancox authored Dec 4, 2024
1 parent 59a19ab commit 7d73fd6
Show file tree
Hide file tree
Showing 41 changed files with 551 additions and 52 deletions.
1 change: 1 addition & 0 deletions Source/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ objc_library(

objc_library(
name = "SNTCommonEnums",
module_name = "santa_common_SNTCommonEnums",
textual_hdrs = ["SNTCommonEnums.h"],
)

Expand Down
6 changes: 5 additions & 1 deletion Source/common/SNTCommonEnums.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,15 @@ typedef NS_ENUM(NSInteger, SNTRuleState) {

SNTRuleStateAllowCompiler = 5,
SNTRuleStateAllowTransitive = 6,
SNTRuleStateAllowLocalBinary = 7,
SNTRuleStateAllowLocalSigningID = 8,
};

typedef NS_ENUM(NSInteger, SNTClientMode) {
SNTClientModeUnknown,

SNTClientModeMonitor = 1,
SNTClientModeLockdown = 2,
SNTClientModeStandalone = 3,
};

typedef NS_ENUM(uint64_t, SNTEventState) {
Expand Down Expand Up @@ -98,6 +100,8 @@ typedef NS_ENUM(uint64_t, SNTEventState) {
SNTEventStateAllowTeamID = 1ULL << 47,
SNTEventStateAllowSigningID = 1ULL << 48,
SNTEventStateAllowCDHash = 1ULL << 49,
SNTEventStateAllowLocalBinary = 1ULL << 50,
SNTEventStateAllowLocalSigningID = 1ULL << 51,

// Block and Allow masks
SNTEventStateBlock = 0xFFFFFFULL << 16,
Expand Down
6 changes: 6 additions & 0 deletions Source/common/SNTConfigurator.h
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,12 @@
///
@property(nullable, readonly, nonatomic) NSString *modeNotificationLockdown;

///
/// The notification text to display when the client goes into STANDALONE mode.
/// Defaults to "Switching into Standalone mode"
///
@property(nullable, readonly, nonatomic) NSString *modeNotificationStandalone;

///
/// If this is set to true, the UI will use different fonts on April 1st, May 4th and October 31st.
///
Expand Down
17 changes: 13 additions & 4 deletions Source/common/SNTConfigurator.m
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ @implementation SNTConfigurator

static NSString *const kModeNotificationMonitor = @"ModeNotificationMonitor";
static NSString *const kModeNotificationLockdown = @"ModeNotificationLockdown";
static NSString *const kModeNotificationStandalone = @"ModeNotificationStandalone";
static NSString *const kFunFontsOnSpecificDays = @"FunFontsOnSpecificDays";

static NSString *const kEnablePageZeroProtectionKey = @"EnablePageZeroProtection";
Expand Down Expand Up @@ -238,6 +239,7 @@ - (instancetype)initWithSyncStateFile:(NSString *)syncStateFilePath
kRemountUSBBlockMessage : string,
kModeNotificationMonitor : string,
kModeNotificationLockdown : string,
kModeNotificationStandalone : string,
kFunFontsOnSpecificDays : number,
kStaticRules : array,
kSyncBaseURLKey : string,
Expand Down Expand Up @@ -633,27 +635,30 @@ + (NSSet *)keyPathsForValuesAffectingTelemetry {

- (SNTClientMode)clientMode {
SNTClientMode cm = [self.syncState[kClientModeKey] longLongValue];
if (cm == SNTClientModeMonitor || cm == SNTClientModeLockdown) {
if (cm == SNTClientModeMonitor || cm == SNTClientModeLockdown || cm == SNTClientModeStandalone) {
return cm;
}

cm = [self.configState[kClientModeKey] longLongValue];
if (cm == SNTClientModeMonitor || cm == SNTClientModeLockdown) {
if (cm == SNTClientModeMonitor || cm == SNTClientModeLockdown || cm == SNTClientModeStandalone) {
return cm;
}

return SNTClientModeMonitor;
}

- (void)setSyncServerClientMode:(SNTClientMode)newMode {
if (newMode == SNTClientModeMonitor || newMode == SNTClientModeLockdown) {
if (newMode == SNTClientModeMonitor || newMode == SNTClientModeLockdown ||
newMode == SNTClientModeStandalone) {
[self updateSyncStateForKey:kClientModeKey value:@(newMode)];
}
}

- (BOOL)failClosed {
NSNumber *n = self.configState[kFailClosedKey];
return [n boolValue] && self.clientMode == SNTClientModeLockdown;
BOOL runningInLockdownClientMode =
self.clientMode == SNTClientModeLockdown || self.clientMode == SNTClientModeStandalone;
return [n boolValue] && runningInLockdownClientMode;
}

- (BOOL)enableTransitiveRules {
Expand Down Expand Up @@ -842,6 +847,10 @@ - (NSString *)modeNotificationLockdown {
return self.configState[kModeNotificationLockdown];
}

- (NSString *)modeNotificationStandalone {
return self.configState[kModeNotificationStandalone];
}

- (BOOL)funFontsOnSpecificDays {
return [self.configState[kFunFontsOnSpecificDays] boolValue];
}
Expand Down
7 changes: 7 additions & 0 deletions Source/common/SNTRule.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2024 North Pole Security, Inc.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -166,6 +167,10 @@ - (instancetype)initWithDictionary:(NSDictionary *)dict {
} else if ([policyString isEqual:kRulePolicyAllowlistCompiler] ||
[policyString isEqual:kRulePolicyAllowlistCompilerDeprecated]) {
state = SNTRuleStateAllowCompiler;
} else if ([policyString isEqual:kRulePolicyAllowlistLocalBinary]) {
state = SNTRuleStateAllowLocalBinary;
} else if ([policyString isEqual:kRulePolicyAllowlistLocalSigningID]) {
state = SNTRuleStateAllowLocalSigningID;
} else if ([policyString isEqual:kRulePolicyBlocklist] ||
[policyString isEqual:kRulePolicyBlocklistDeprecated]) {
state = SNTRuleStateBlock;
Expand Down Expand Up @@ -254,6 +259,8 @@ - (NSString *)ruleStateToPolicyString:(SNTRuleState)state {
case SNTRuleStateSilentBlock: return kRulePolicySilentBlocklist;
case SNTRuleStateRemove: return kRulePolicyRemove;
case SNTRuleStateAllowTransitive: return @"AllowTransitive";
case SNTRuleStateAllowLocalBinary: return kRulePolicyAllowlistLocalBinary;
case SNTRuleStateAllowLocalSigningID: return kRulePolicyAllowlistLocalSigningID;
// This should never be hit. But is here for completion.
default: return @"Unknown";
}
Expand Down
2 changes: 2 additions & 0 deletions Source/common/SNTSyncConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ extern NSString *const kRuleSHA256;
extern NSString *const kRuleIdentifier;
extern NSString *const kRulePolicy;
extern NSString *const kRulePolicyAllowlist;
extern NSString *const kRulePolicyAllowlistLocalBinary;
extern NSString *const kRulePolicyAllowlistLocalSigningID;
extern NSString *const kRulePolicyAllowlistDeprecated;
extern NSString *const kRulePolicyAllowlistCompiler;
extern NSString *const kRulePolicyAllowlistCompilerDeprecated;
Expand Down
2 changes: 2 additions & 0 deletions Source/common/SNTSyncConstants.m
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@
NSString *const kRuleIdentifier = @"identifier";
NSString *const kRulePolicy = @"policy";
NSString *const kRulePolicyAllowlist = @"ALLOWLIST";
NSString *const kRulePolicyAllowlistLocalBinary = @"ALLOWLIST_LOCAL_BINARY";
NSString *const kRulePolicyAllowlistLocalSigningID = @"ALLOWLIST_LOCAL_SIGNINGID";
NSString *const kRulePolicyAllowlistDeprecated = @"WHITELIST";
NSString *const kRulePolicyAllowlistCompiler = @"ALLOWLIST_COMPILER";
NSString *const kRulePolicyAllowlistCompilerDeprecated = @"WHITELIST_COMPILER";
Expand Down
4 changes: 3 additions & 1 deletion Source/common/SNTXPCNotifierInterface.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2024 North Pole Security, Inc.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
Expand All @@ -25,7 +26,8 @@
@protocol SNTNotifierXPC
- (void)postBlockNotification:(SNTStoredEvent *)event
withCustomMessage:(NSString *)message
andCustomURL:(NSString *)url;
customURL:(NSString *)url
andReply:(void (^)(BOOL authenticated))reply;
- (void)postUSBBlockNotification:(SNTDeviceEvent *)event;
- (void)postFileAccessBlockNotification:(SNTFileAccessEvent *)event
customMessage:(NSString *)message
Expand Down
1 change: 1 addition & 0 deletions Source/common/santa.proto
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ message Execution {
MODE_UNKNOWN = 0;
MODE_LOCKDOWN = 1;
MODE_MONITOR = 2;
MODE_STANDALONE = 3;
}
optional Mode mode = 11;

Expand Down
2 changes: 2 additions & 0 deletions Source/gui/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ swift_library(
deps = [
":SNTMessageView",
"//Source/common:SNTBlockMessage_SantaGUI",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTStoredEvent",
],
Expand Down Expand Up @@ -84,6 +85,7 @@ objc_library(
"SNTNotificationManager.h",
],
sdk_frameworks = [
"LocalAuthentication",
"IOKit",
"SecurityInterface",
"SystemExtensions",
Expand Down
9 changes: 9 additions & 0 deletions Source/gui/Resources/de.lproj/Localizable.strings
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
/* The default message to show the user when access to a file is blocked */
"Access to a file has been denied" = "Der Zugriff auf eine Datei wurde verweigert";

/* No comment provided by engineer. */
"Approve" = "Genehmigen";

/* No comment provided by engineer. */
"Application" = "Applikation";

/* No comment provided by engineer. */
"authorize execution of " = "genehmigen die Ausführung von ";

/* No comment provided by engineer. */
"authorize execution of the application " = "Die Ausführung der Anwendung autorisieren ";

/* No comment provided by engineer. */
"Bundle Hash" = "Bundle-Hash";

Expand Down
10 changes: 10 additions & 0 deletions Source/gui/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
/* No comment provided by engineer. */
"Application" = "Application";

/* Default text for Approve */
"Approve" = "Approve";

/* File path
Signing ID */
"authorize execution of " = "authorize execution of ";

/* Bundle name */
"authorize execution of the application " = "authorize execution of the application ";

/* No comment provided by engineer. */
"Bundle Hash" = "Bundle Hash";

Expand Down
9 changes: 9 additions & 0 deletions Source/gui/Resources/ru.lproj/Localizable.strings
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
/* The default message to show the user when access to a file is blocked */
"Access to a file has been denied" = "Доступ к файлу был запрещен";

/* No comment provided by engineer. */
"Approve" = "Утвердить";

/* No comment provided by engineer. */
"Application" = "Приложение";

/* No comment provided by engineer. */
"authorize execution of " = "разрешить исполнение ";

/* No comment provided by engineer. */
"authorize execution of the application " = "разрешить выполнение заявки ";

/* No comment provided by engineer. */
"Bundle Hash" = "Бандл-хэш";

Expand Down
9 changes: 9 additions & 0 deletions Source/gui/Resources/uk.lproj/Localizable.strings
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
/* No comment provided by engineer. */
"Approve" = "Затвердити";

/* No comment provided by engineer. */
"Application" = "Заявка";

/* No Comment provided by engineer. */
"authorize execution of " = "дозволити виконання ";

/* No Comment provided by engineer. */
"authorize execution of the application " = "дозволити виконання заявки ";

/* No comment provided by engineer. */
"Bundle Hash" = "Пакетний хеш";

Expand Down
8 changes: 7 additions & 1 deletion Source/gui/SNTBinaryMessageWindowController.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@

- (instancetype)initWithEvent:(SNTStoredEvent *)event
customMsg:(NSString *)message
customURL:(NSString *)url;
customURL:(NSString *)url
reply:(void (^)(BOOL authenticated))replyBlock;

- (void)updateBlockNotification:(SNTStoredEvent *)event withBundleHash:(NSString *)bundleHash;

Expand All @@ -49,6 +50,11 @@
///
@property(readonly) SNTStoredEvent *event;

///
/// The reply block to call when the user has made a decision in standalone
/// mode.
@property(readonly, nonatomic) void (^replyBlock)(BOOL authenticated);

///
/// The root progress object. Child nodes are vended to santad to report on work being done.
///
Expand Down
9 changes: 7 additions & 2 deletions Source/gui/SNTBinaryMessageWindowController.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
#import "Source/gui/SNTBinaryMessageWindowView-Swift.h"

#include <AppKit/AppKit.h>
#import <LocalAuthentication/LocalAuthentication.h>
#import <MOLCertificate/MOLCertificate.h>
#import <SecurityInterface/SFCertificatePanel.h>
#include <dispatch/dispatch.h>

#import "Source/common/CertificateHelpers.h"
#import "Source/common/SNTBlockMessage.h"
Expand All @@ -39,12 +41,14 @@ @implementation SNTBinaryMessageWindowController

- (instancetype)initWithEvent:(SNTStoredEvent *)event
customMsg:(NSString *)message
customURL:(NSString *)url {
customURL:(NSString *)url
reply:(void (^)(BOOL))replyBlock {
self = [super init];
if (self) {
_event = event;
_customMessage = message;
_customURL = url;
_replyBlock = replyBlock;
_progress = [NSProgress discreteProgressWithTotalUnitCount:1];
[_progress addObserver:self
forKeyPath:@"fractionCompleted"
Expand Down Expand Up @@ -97,7 +101,8 @@ - (void)showWindow:(id)sender {
bundleProgress:self.bundleProgress
uiStateCallback:^(NSTimeInterval preventNotificationsPeriod) {
self.silenceFutureNotificationsPeriod = preventNotificationsPeriod;
}];
}
replyCallback:self.replyBlock];

self.window.delegate = self;

Expand Down
Loading

0 comments on commit 7d73fd6

Please sign in to comment.