Skip to content

Commit

Permalink
Idempotent config states (pt1) (#221)
Browse files Browse the repository at this point in the history
This is part of a series of PRs that will move Santa to using
configuration "views" that provide a snapshot of configuration state
that can be used while processing some event or flow so that config
changes occur "atomically" and never partially apply during event
processing.

This first part sets up some initial infrastructure needed to support
this change. Namely it creates the new `SNTConfigState` object which
initially only captures `clientMode`. This also begins some of the work
of getting this object passed around to various components that will
need it in the future.

Finally, this PR addresses a UI-only issue that could result in the
wrong buttons being displayed if the client mode was changed to or from
Standalone mode via the sync server
  • Loading branch information
mlw authored Jan 23, 2025
1 parent e51158f commit 420e1a7
Show file tree
Hide file tree
Showing 18 changed files with 217 additions and 12 deletions.
18 changes: 18 additions & 0 deletions Source/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,11 @@ objc_library(
hdrs = ["SNTRuleIdentifiers.h"],
)

objc_library(
name = "CoderMacros",
hdrs = ["CoderMacros.h"],
)

objc_library(
name = "SNTStoredEvent",
srcs = ["SNTStoredEvent.m"],
Expand All @@ -390,6 +395,18 @@ objc_library(
],
)

objc_library(
name = "SNTConfigState",
srcs = ["SNTConfigState.m"],
hdrs = ["SNTConfigState.h"],
module_name = "santa_common_SNTConfigState",
deps = [
":CoderMacros",
":SNTConfigurator",
":SNTCommonEnums",
],
)

cc_library(
name = "SNTStrengthify",
hdrs = ["SNTStrengthify.h"],
Expand Down Expand Up @@ -459,6 +476,7 @@ objc_library(
hdrs = ["SNTXPCNotifierInterface.h"],
deps = [
":SNTCommonEnums",
":SNTConfigState",
":SNTXPCBundleServiceInterface",
],
)
Expand Down
56 changes: 56 additions & 0 deletions Source/common/CoderMacros.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/// Copyright 2025 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.
/// You may obtain a copy of the License at
///
/// https://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.

#ifndef SANTA__COMMON__CODERMACROS_H
#define SANTA__COMMON__CODERMACROS_H

// Encode the property keyed by the property name.
#define ENCODE(c, o) \
do { \
if (self.o) { \
[c encodeObject:self.o forKey:@(#o)]; \
} \
} while (0)

// Encode the property (by first boxing the value) keyed
// by the property name.
#define ENCODE_BOXABLE(c, o) \
do { \
id local_obj__ = @(self.o); \
[c encodeObject:local_obj__ forKey:@(#o)]; \
} while (0)

// Decode a property of a given type and assign the value to
// the named property.
#define DECODE(d, o, c) \
do { \
_##o = [d decodeObjectOfClass:[c class] forKey:@(#o)]; \
} while (0)

// Decode a property of a given type and calls a method on that
// type before assigning the value to the named property
#define DECODE_SELECTOR(d, o, c, s) \
do { \
_##o = [[d decodeObjectOfClass:[c class] forKey:@(#o)] s]; \
} while (0)

// Decode a property of an array of objects of the given type
// and assign the value to the named property.
#define DECODEARRAY(d, o, c) \
do { \
_##o = [d decodeObjectOfClasses:[NSSet setWithObjects:[NSArray class], [c class], nil] \
forKey:@(#o)]; \
} while (0)

#endif
29 changes: 29 additions & 0 deletions Source/common/SNTConfigState.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/// Copyright 2025 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.
/// You may obtain a copy of the License at
///
/// https://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.

#import <Foundation/Foundation.h>

#import "Source/common/SNTCommonEnums.h"
#import "Source/common/SNTConfigurator.h"

@interface SNTConfigState : NSObject <NSSecureCoding>

//
// Properties here mirror the SNTConfigurator at a point in time
//
@property(readonly) SNTClientMode clientMode;

- (instancetype)initWithConfig:(SNTConfigurator *)config;

@end
46 changes: 46 additions & 0 deletions Source/common/SNTConfigState.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/// Copyright 2025 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.
/// You may obtain a copy of the License at
///
/// https://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.

#import <Foundation/Foundation.h>

#import "Source/common/CoderMacros.h"
#import "Source/common/SNTConfigState.h"

@implementation SNTConfigState

- (instancetype)initWithConfig:(SNTConfigurator *)config {
self = [super init];
if (self) {
_clientMode = config.clientMode;
}
return self;
}

+ (BOOL)supportsSecureCoding {
return YES;
}

- (void)encodeWithCoder:(NSCoder *)coder {
ENCODE_BOXABLE(coder, clientMode);
}

- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self) {
DECODE_SELECTOR(decoder, clientMode, NSNumber, integerValue);
}
return self;
};

@end
2 changes: 2 additions & 0 deletions Source/common/SNTXPCNotifierInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#import <Foundation/Foundation.h>

#import "Source/common/SNTCommonEnums.h"
#import "Source/common/SNTConfigState.h"
#import "Source/common/SNTXPCBundleServiceInterface.h"

@class SNTDeviceEvent;
Expand All @@ -27,6 +28,7 @@
- (void)postBlockNotification:(SNTStoredEvent *)event
withCustomMessage:(NSString *)message
customURL:(NSString *)url
configState:(SNTConfigState *)configState
andReply:(void (^)(BOOL authenticated))reply;
- (void)postUSBBlockNotification:(SNTDeviceEvent *)event;
- (void)postFileAccessBlockNotification:(SNTFileAccessEvent *)event
Expand Down
2 changes: 2 additions & 0 deletions Source/gui/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ swift_library(
":SNTMessageView",
"//Source/common:SNTBlockMessage_SantaGUI",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigState",
"//Source/common:SNTConfigurator",
"//Source/common:SNTStoredEvent",
],
Expand Down Expand Up @@ -101,6 +102,7 @@ objc_library(
"//Source/common:SNTBlockMessage_SantaGUI",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDeviceEvent",
"//Source/common:SNTConfigState",
"//Source/common:SNTFileAccessEvent",
"//Source/common:SNTLogging",
"//Source/common:SNTStoredEvent",
Expand Down
7 changes: 7 additions & 0 deletions Source/gui/SNTBinaryMessageWindowController.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#import <Cocoa/Cocoa.h>

#import "Source/common/SNTConfigState.h"
#import "Source/gui/SNTMessageWindowController.h"

@class SNTStoredEvent;
Expand All @@ -28,6 +29,7 @@
- (instancetype)initWithEvent:(SNTStoredEvent *)event
customMsg:(NSString *)message
customURL:(NSString *)url
configState:(SNTConfigState *)configState
reply:(void (^)(BOOL authenticated))replyBlock;

- (void)updateBlockNotification:(SNTStoredEvent *)event withBundleHash:(NSString *)bundleHash;
Expand All @@ -45,6 +47,11 @@
///
@property(readonly) SNTBundleProgress *bundleProgress;

///
/// Snapshot of configuration used for processing the event.
///
@property(readonly) SNTConfigState *configState;

///
/// The execution event that this window is for
///
Expand Down
3 changes: 3 additions & 0 deletions Source/gui/SNTBinaryMessageWindowController.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@ @implementation SNTBinaryMessageWindowController
- (instancetype)initWithEvent:(SNTStoredEvent *)event
customMsg:(NSString *)message
customURL:(NSString *)url
configState:(SNTConfigState *)configState
reply:(void (^)(BOOL))replyBlock {
self = [super init];
if (self) {
_event = event;
_customMessage = message;
_customURL = url;
_configState = configState;
_replyBlock = replyBlock;
_progress = [NSProgress discreteProgressWithTotalUnitCount:1];
[_progress addObserver:self
Expand Down Expand Up @@ -98,6 +100,7 @@ - (void)showWindow:(id)sender {
event:self.event
customMsg:self.customMessage
customURL:self.customURL
configState:self.configState
bundleProgress:self.bundleProgress
uiStateCallback:^(NSTimeInterval preventNotificationsPeriod) {
self.silenceFutureNotificationsPeriod = preventNotificationsPeriod;
Expand Down
10 changes: 7 additions & 3 deletions Source/gui/SNTBinaryMessageWindowView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
import SwiftUI

import santa_common_SNTBlockMessage
import santa_common_SNTConfigurator
import santa_common_SNTCommonEnums
import santa_common_SNTConfigState
import santa_common_SNTConfigurator
import santa_common_SNTStoredEvent
import santa_gui_SNTMessageView

Expand All @@ -34,6 +35,7 @@ import santa_gui_SNTMessageView
event: SNTStoredEvent,
customMsg: NSString?,
customURL: NSString?,
configState: SNTConfigState,
bundleProgress: SNTBundleProgress,
uiStateCallback: ((TimeInterval) -> Void)?,
replyCallback: ((Bool) -> Void)?
Expand All @@ -44,6 +46,7 @@ import santa_gui_SNTMessageView
event: event,
customMsg: customMsg,
customURL: customURL,
configState: configState,
bundleProgress: bundleProgress,
uiStateCallback: uiStateCallback,
replyCallback: replyCallback
Expand Down Expand Up @@ -251,6 +254,7 @@ struct SNTBinaryMessageWindowView: View {
let event: SNTStoredEvent?
let customMsg: NSString?
let customURL: NSString?
let configState: SNTConfigState
@StateObject var bundleProgress: SNTBundleProgress
let uiStateCallback: ((TimeInterval) -> Void)?
let replyCallback: ((Bool) -> Void)?
Expand Down Expand Up @@ -283,7 +287,7 @@ struct SNTBinaryMessageWindowView: View {
}

// Display the standalone error message to the user if one is provided.
if c.clientMode == .standalone {
if configState.clientMode == .standalone {
let (canAuthz, err) = CanAuthorizeWithTouchID()
if !canAuthz {
if let errMsg = err {
Expand All @@ -310,7 +314,7 @@ struct SNTBinaryMessageWindowView: View {
}

func shouldAddStandaloneButton() -> Bool {
var shouldDisplay = c.clientMode == .standalone
var shouldDisplay = configState.clientMode == .standalone

let (canAuthz, _) = CanAuthorizeWithTouchID()
if !canAuthz {
Expand Down
3 changes: 3 additions & 0 deletions Source/gui/SNTNotificationManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#import <UserNotifications/UserNotifications.h>

#import "Source/common/SNTBlockMessage.h"
#import "Source/common/SNTConfigState.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTDeviceEvent.h"
#import "Source/common/SNTLogging.h"
Expand Down Expand Up @@ -363,6 +364,7 @@ - (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message {
- (void)postBlockNotification:(SNTStoredEvent *)event
withCustomMessage:(NSString *)message
customURL:(NSString *)url
configState:(SNTConfigState *)configState
andReply:(void (^)(BOOL))replyBlock {
if (!event) {
LOGI(@"Error: Missing event object in message received from daemon!");
Expand All @@ -373,6 +375,7 @@ - (void)postBlockNotification:(SNTStoredEvent *)event
[[SNTBinaryMessageWindowController alloc] initWithEvent:event
customMsg:message
customURL:url
configState:configState
reply:replyBlock];

[self queueMessage:pendingMsg];
Expand Down
1 change: 1 addition & 0 deletions Source/gui/SNTNotificationManagerTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ - (void)testPostBlockNotificationSendsDistributedNotification {
[sut postBlockNotification:ev
withCustomMessage:@""
customURL:@""
configState:nil
andReply:^(BOOL authenticated){
}];

Expand Down
4 changes: 4 additions & 0 deletions Source/santad/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ objc_library(
hdrs = ["SNTNotificationQueue.h"],
deps = [
"//Source/common:RingBuffer",
"//Source/common:SNTConfigState",
"//Source/common:SNTLogging",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTStrengthify",
Expand All @@ -191,6 +192,7 @@ santa_unit_test(
srcs = ["SNTNotificationQueueTest.mm"],
deps = [
":SNTNotificationQueue",
"//Source/common:SNTConfigState",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTXPCNotifierInterface",
"//Source/common:TestUtils",
Expand Down Expand Up @@ -221,6 +223,7 @@ objc_library(
"//Source/common:CertificateHelpers",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigState",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDeepCopy",
"//Source/common:SNTFileInfo",
Expand Down Expand Up @@ -292,6 +295,7 @@ objc_library(
"//Source/common:SNTConfigurator",
"//Source/common:SNTDeepCopy",
"//Source/common:SNTDropRootPrivs",
"//Source/common:SNTConfigState",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTMetricSet",
Expand Down
Loading

0 comments on commit 420e1a7

Please sign in to comment.