Skip to content

Commit

Permalink
Unmount USB on start (#1211)
Browse files Browse the repository at this point in the history
* WIP Allow configuring Santa to unmount existing mass storage devices on startup

* WIP fixup existing tests

* Add unmount on startup tests
  • Loading branch information
mlw authored Oct 31, 2023
1 parent 275a8ed commit c5c6037
Show file tree
Hide file tree
Showing 11 changed files with 356 additions and 55 deletions.
6 changes: 6 additions & 0 deletions Source/common/SNTCommonEnums.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,12 @@ typedef NS_ENUM(NSInteger, SNTOverrideFileAccessAction) {
SNTOverrideFileAccessActionDiable,
};

typedef NS_ENUM(NSInteger, SNTDeviceManagerStartupPreferences) {
SNTDeviceManagerStartupPreferencesNone,
SNTDeviceManagerStartupPreferencesUnmount,
SNTDeviceManagerStartupPreferencesForceUnmount,
};

#ifdef __cplusplus
enum class FileAccessPolicyDecision {
kNoPolicy,
Expand Down
14 changes: 14 additions & 0 deletions Source/common/SNTConfigurator.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,20 @@
///
@property(nonatomic) NSArray<NSString *> *remountUSBMode;

///
/// If set, defines the action that should be taken on existing USB mounts when
/// Santa starts up.
///
/// Supported values are:
/// * "Unmount": Unmount mass storage devices
/// * "ForceUnmount": Force unmount mass storage devices
///
///
/// Note: Existing mounts with mount flags that are a superset of RemountUSBMode
/// are unaffected and left mounted.
///
@property(readonly, nonatomic) SNTDeviceManagerStartupPreferences onStartUSBOptions;

///
/// If set, will override the action taken when a file access rule violation
/// occurs. This setting will apply across all rules in the file access policy.
Expand Down
14 changes: 14 additions & 0 deletions Source/common/SNTConfigurator.m
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ @implementation SNTConfigurator
static NSString *const kFailClosedKey = @"FailClosed";
static NSString *const kBlockUSBMountKey = @"BlockUSBMount";
static NSString *const kRemountUSBModeKey = @"RemountUSBMode";
static NSString *const kOnStartUSBOptions = @"OnStartUSBOptions";
static NSString *const kEnableTransitiveRulesKey = @"EnableTransitiveRules";
static NSString *const kEnableTransitiveRulesKeyDeprecated = @"EnableTransitiveWhitelisting";
static NSString *const kAllowedPathRegexKey = @"AllowedPathRegex";
Expand Down Expand Up @@ -181,6 +182,7 @@ - (instancetype)init {
kBlockedPathRegexKeyDeprecated : re,
kBlockUSBMountKey : number,
kRemountUSBModeKey : array,
kOnStartUSBOptions : string,
kEnablePageZeroProtectionKey : number,
kEnableBadSignatureProtectionKey : number,
kEnableSilentModeKey : number,
Expand Down Expand Up @@ -635,6 +637,18 @@ - (void)setRemountUSBMode:(NSArray<NSString *> *)args {
return args;
}

- (SNTDeviceManagerStartupPreferences)onStartUSBOptions {
NSString *action = [self.configState[kOnStartUSBOptions] lowercaseString];

if ([action isEqualToString:@"unmount"]) {
return SNTDeviceManagerStartupPreferencesUnmount;
} else if ([action isEqualToString:@"forceunmount"]) {
return SNTDeviceManagerStartupPreferencesForceUnmount;
} else {
return SNTDeviceManagerStartupPreferencesNone;
}
}

- (NSDictionary<NSString *, SNTRule *> *)staticRules {
return self.cachedStaticRules;
}
Expand Down
2 changes: 2 additions & 0 deletions Source/santad/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ objc_library(
":Metrics",
":SNTEndpointSecurityClient",
":SNTEndpointSecurityEventHandler",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTDeviceEvent",
"//Source/common:SNTLogging",
],
Expand Down Expand Up @@ -1317,6 +1318,7 @@ santa_unit_test(
":Metrics",
":MockEndpointSecurityAPI",
":SNTEndpointSecurityDeviceManager",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDeviceEvent",
"//Source/common:TestUtils",
Expand Down
25 changes: 25 additions & 0 deletions Source/santad/EventProviders/DiskArbitrationTestUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#include <CoreFoundation/CoreFoundation.h>
#include <DiskArbitration/DiskArbitration.h>
#include <Foundation/Foundation.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/ucred.h>

NS_ASSUME_NONNULL_BEGIN

Expand All @@ -27,6 +30,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface MockDADisk : NSObject
@property(nonatomic) NSDictionary *diskDescription;
@property(nonatomic, readwrite) NSString *name;
@property(nonatomic) BOOL wasUnmounted;
@end

typedef void (^MockDADiskAppearedCallback)(DADiskRef ref);
Expand All @@ -49,6 +53,23 @@ typedef void (^MockDADiskAppearedCallback)(DADiskRef ref);
+ (instancetype _Nonnull)mockDiskArbitration;
@end

@interface MockStatfs : NSObject
@property NSString *fromName;
@property NSString *onName;
@property NSNumber *flags;

- (instancetype _Nonnull)initFrom:(NSString *)from on:(NSString *)on flags:(NSNumber *)flags;
@end

@interface MockMounts : NSObject
@property(nonatomic) NSMutableDictionary<NSString *, MockStatfs *> *mounts;

- (instancetype _Nonnull)init;
- (void)reset;
- (void)insert:(MockStatfs *)sfs;
+ (instancetype _Nonnull)mockMounts;
@end

//
// All DiskArbitration functions used in SNTEndpointSecurityDeviceManager
// and shimmed out accordingly.
Expand Down Expand Up @@ -81,5 +102,9 @@ void DARegisterDiskDescriptionChangedCallback(DASessionRef session,
void DASessionSetDispatchQueue(DASessionRef session, dispatch_queue_t __nullable queue);
DASessionRef __nullable DASessionCreate(CFAllocatorRef __nullable allocator);

void DADiskUnmount(DADiskRef disk, DADiskUnmountOptions options,
DADiskUnmountCallback __nullable callback, void *__nullable context);
int getmntinfo_r_np(struct statfs *__nullable *__nullable mntbufp, int flags);

CF_EXTERN_C_END
NS_ASSUME_NONNULL_END
72 changes: 72 additions & 0 deletions Source/santad/EventProviders/DiskArbitrationTestUtil.mm
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
#import <Foundation/Foundation.h>

#include <stdlib.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/ucred.h>

#import "Source/santad/EventProviders/DiskArbitrationTestUtil.h"

Expand Down Expand Up @@ -62,6 +65,47 @@ + (instancetype _Nonnull)mockDiskArbitration {

@end

@implementation MockStatfs
- (instancetype _Nonnull)initFrom:(NSString *)from on:(NSString *)on flags:(NSNumber *)flags {
self = [super init];
if (self) {
_fromName = from;
_onName = on;
_flags = flags;
}
return self;
}
@end

@implementation MockMounts

- (instancetype _Nonnull)init {
self = [super init];
if (self) {
_mounts = [NSMutableDictionary dictionary];
}
return self;
}

- (void)reset {
[self.mounts removeAllObjects];
}

- (void)insert:(MockStatfs *)sfs {
self.mounts[sfs.fromName] = sfs;
}

+ (instancetype _Nonnull)mockMounts {
static MockMounts *sharedMounts;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMounts = [[MockMounts alloc] init];
});
return sharedMounts;
}

@end

void DADiskMountWithArguments(DADiskRef _Nonnull disk, CFURLRef __nullable path,
DADiskMountOptions options, DADiskMountCallback __nullable callback,
void *__nullable context,
Expand Down Expand Up @@ -117,4 +161,32 @@ DASessionRef __nullable DASessionCreate(CFAllocatorRef __nullable allocator) {
return (__bridge DASessionRef)[MockDiskArbitration mockDiskArbitration];
};

void DADiskUnmount(DADiskRef disk, DADiskUnmountOptions options,
DADiskUnmountCallback __nullable callback, void *__nullable context) {
MockDADisk *mockDisk = (__bridge MockDADisk *)disk;
mockDisk.wasUnmounted = YES;

dispatch_semaphore_t sema = (__bridge dispatch_semaphore_t)context;
dispatch_semaphore_signal(sema);
}

int getmntinfo_r_np(struct statfs *__nullable *__nullable mntbufp, int flags) {
MockMounts *mockMounts = [MockMounts mockMounts];

struct statfs *sfs = (struct statfs *)calloc(mockMounts.mounts.count, sizeof(struct statfs));

__block NSUInteger i = 0;
[mockMounts.mounts
enumerateKeysAndObjectsUsingBlock:^(NSString *key, MockStatfs *mockSfs, BOOL *stop) {
strlcpy(sfs[i].f_mntfromname, mockSfs.fromName.UTF8String, sizeof(sfs[i].f_mntfromname));
strlcpy(sfs[i].f_mntonname, mockSfs.onName.UTF8String, sizeof(sfs[i].f_mntonname));
sfs[i].f_flags = [mockSfs.flags unsignedIntValue];
i++;
}];

*mntbufp = sfs;

return (int)mockMounts.mounts.count;
}

NS_ASSUME_NONNULL_END
16 changes: 11 additions & 5 deletions Source/santad/EventProviders/SNTEndpointSecurityDeviceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <DiskArbitration/DiskArbitration.h>
#import <Foundation/Foundation.h>

#import "Source/common/SNTCommonEnums.h"
#import "Source/common/SNTDeviceEvent.h"
#import "Source/santad/EventProviders/AuthResultCache.h"
#include "Source/santad/EventProviders/EndpointSecurity/EndpointSecurityAPI.h"
Expand All @@ -39,11 +40,16 @@ typedef void (^SNTDeviceBlockCallback)(SNTDeviceEvent *event);
@property(nonatomic, nullable) SNTDeviceBlockCallback deviceBlockCallback;

- (instancetype)
initWithESAPI:
(std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::santad::Metrics>)metrics
logger:(std::shared_ptr<santa::santad::logs::endpoint_security::Logger>)logger
authResultCache:(std::shared_ptr<santa::santad::event_providers::AuthResultCache>)authResultCache;
initWithESAPI:
(std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI>)
esApi
metrics:(std::shared_ptr<santa::santad::Metrics>)metrics
logger:(std::shared_ptr<santa::santad::logs::endpoint_security::Logger>)logger
authResultCache:
(std::shared_ptr<santa::santad::event_providers::AuthResultCache>)authResultCache
blockUSBMount:(BOOL)blockUSBMount
remountUSBMode:(nullable NSArray<NSString *> *)remountUSBMode
startupPreferences:(SNTDeviceManagerStartupPreferences)startupPrefs;

@end

Expand Down
Loading

0 comments on commit c5c6037

Please sign in to comment.