Skip to content

Commit

Permalink
UI For Blocked File Access (#1174)
Browse files Browse the repository at this point in the history
* WIP: UI: open cert modal, hookup silence checkbox. Add cert helper funcs.

* Popup dialog on file access violation. Support config-based and custom messages.

* Send message to TTY on file access rule violation

* TTYWriter Write now takes an es_process_t. Fix async data lifespan issue.

* Dedupe TTY message printing per process per rule

* Some minor swift beautification

* Remove main app from dock when showing file access dialog

* Update header docs

* Remove define guards for ObjC header file

* Update Source/common/CertificateHelpers.h

Co-authored-by: Russell Hancox <[email protected]>

* Fix comment typo

Co-authored-by: Russell Hancox <[email protected]>

* Use #import for ObjC headers

* Use #import for ObjC header

Co-authored-by: Russell Hancox <[email protected]>

* lint

* Comment use of escape sequences

---------

Co-authored-by: Russell Hancox <[email protected]>
  • Loading branch information
mlw and russellhancox authored Sep 13, 2023
1 parent d2e5aec commit 3be45fd
Show file tree
Hide file tree
Showing 23 changed files with 434 additions and 148 deletions.
32 changes: 24 additions & 8 deletions Source/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -85,28 +85,43 @@ objc_library(
)

objc_library(
name = "SNTBlockMessage",
srcs = ["SNTBlockMessage.m"],
hdrs = ["SNTBlockMessage.h"],
name = "CertificateHelpers",
srcs = ["CertificateHelpers.m"],
hdrs = ["CertificateHelpers.h"],
deps = [
"@MOLCertificate",
],
)

# Note: `srcs`/`hdrs` are left unspecified here because `defines` from the
# `SNTBlockMessage_SantaGUI` target do not propagate to `deps`.
objc_library(
name = "SNTBlockMessage_CommonDeps",
deps = [
":SNTConfigurator",
":SNTFileAccessEvent",
":SNTLogging",
":SNTStoredEvent",
":SNTSystemInfo",
],
)

objc_library(
name = "SNTBlockMessage",
srcs = ["SNTBlockMessage.m"],
hdrs = ["SNTBlockMessage.h"],
deps = [
":SNTBlockMessage_CommonDeps",
],
)

objc_library(
name = "SNTBlockMessage_SantaGUI",
srcs = ["SNTBlockMessage.m"],
hdrs = ["SNTBlockMessage.h"],
defines = ["SANTAGUI"],
deps = [
":SNTConfigurator",
":SNTDeviceEvent",
":SNTLogging",
":SNTStoredEvent",
":SNTSystemInfo",
":SNTBlockMessage_CommonDeps",
],
)

Expand Down Expand Up @@ -142,6 +157,7 @@ objc_library(
"Foundation",
],
deps = [
":CertificateHelpers",
"@MOLCertificate",
],
)
Expand Down
43 changes: 43 additions & 0 deletions Source/common/CertificateHelpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/// Copyright 2023 Google LLC
///
/// 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 <MOLCertificate/MOLCertificate.h>
#include <sys/cdefs.h>

__BEGIN_DECLS

/**
Return a string representing publisher info from the provided certs
@param certs A certificate chain
@param teamID A team ID to be displayed for apps from the App Store
@return A string that tries to be more helpful to users by extracting
appropriate information from the certificate chain.
*/
NSString *Publisher(NSArray<MOLCertificate *> *certs, NSString *teamID);

/**
Return an array of the underlying SecCertificateRef's for the given array
of MOLCertificates.
@param certs An array of MOLCertificates
@return An array of SecCertificateRefs. WARNING: If the refs need to be used
for a long time be careful to properly CFRetain/CFRelease the returned items.
*/
NSArray<id> *CertificateChain(NSArray<MOLCertificate *> *certs);

__END_DECLS
42 changes: 42 additions & 0 deletions Source/common/CertificateHelpers.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/// Copyright 2023 Google LLC
///
/// 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 "Source/common/CertificateHelpers.h"

#include <Security/SecCertificate.h>

NSString *Publisher(NSArray<MOLCertificate *> *certs, NSString *teamID) {
MOLCertificate *leafCert = [certs firstObject];

if ([leafCert.commonName isEqualToString:@"Apple Mac OS Application Signing"]) {
return [NSString stringWithFormat:@"App Store (Team ID: %@)", teamID];
} else if (leafCert.commonName && leafCert.orgName) {
return [NSString stringWithFormat:@"%@ - %@", leafCert.orgName, leafCert.commonName];
} else if (leafCert.commonName) {
return leafCert.commonName;
} else if (leafCert.orgName) {
return leafCert.orgName;
} else {
return nil;
}
}

NSArray<id> *CertificateChain(NSArray<MOLCertificate *> *certs) {
NSMutableArray *certArray = [NSMutableArray arrayWithCapacity:[certs count]];
for (MOLCertificate *cert in certs) {
[certArray addObject:(id)cert.certRef];
}

return certArray;
}
6 changes: 6 additions & 0 deletions Source/common/SNTBlockMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#import <Foundation/Foundation.h>
#endif

#import "Source/common/SNTFileAccessEvent.h"
#import "Source/common/SNTStoredEvent.h"

@class SNTStoredEvent;

@interface SNTBlockMessage : NSObject
Expand All @@ -38,6 +41,9 @@
+ (NSAttributedString *)attributedBlockMessageForEvent:(SNTStoredEvent *)event
customMessage:(NSString *)customMessage;

+ (NSAttributedString *)attributedBlockMessageForFileAccessEvent:(SNTFileAccessEvent *)event
customMessage:(NSString *)customMessage;

///
/// Return a URL generated from the EventDetailURL configuration key
/// after replacing templates in the URL with values from the event.
Expand Down
12 changes: 12 additions & 0 deletions Source/common/SNTBlockMessage.m
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ + (NSAttributedString *)attributedBlockMessageForEvent:(SNTStoredEvent *)event
return [SNTBlockMessage formatMessage:message];
}

+ (NSAttributedString *)attributedBlockMessageForFileAccessEvent:(SNTFileAccessEvent *)event
customMessage:(NSString *)customMessage {
NSString *message = customMessage;
if (!message.length) {
message = [[SNTConfigurator configurator] fileAccessBlockMessage];
if (!message.length) {
message = @"Access to a file has been denied.";
}
}
return [SNTBlockMessage formatMessage:message];
}

+ (NSString *)stringFromHTML:(NSString *)html {
NSError *error;
NSXMLDocument *xml = [[NSXMLDocument alloc] initWithXMLString:html options:0 error:&error];
Expand Down
10 changes: 10 additions & 0 deletions Source/common/SNTConfigurator.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,16 @@
///
@property(readonly, nonatomic) NSString *fileAccessPolicyPlist;

///
/// This is the message shown to the user when access to a file is blocked
/// by a binary due to some rule in the current File Access policy if that rule
/// doesn't provide a custom message. If this is not configured, a reasonable
/// default is provided.
///
/// @note: This property is KVO compliant.
///
@property(readonly, nonatomic) NSString *fileAccessBlockMessage;

///
/// If fileAccessPolicyPlist is set, fileAccessPolicyUpdateIntervalSec
/// sets the number of seconds between times that the configuration file is
Expand Down
10 changes: 10 additions & 0 deletions Source/common/SNTConfigurator.m
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ @implementation SNTConfigurator

static NSString *const kFileAccessPolicy = @"FileAccessPolicy";
static NSString *const kFileAccessPolicyPlist = @"FileAccessPolicyPlist";
static NSString *const kFileAccessBlockMessage = @"FileAccessBlockMessage";
static NSString *const kFileAccessPolicyUpdateIntervalSec = @"FileAccessPolicyUpdateIntervalSec";

static NSString *const kEnableMachineIDDecoration = @"EnableMachineIDDecoration";
Expand Down Expand Up @@ -219,6 +220,7 @@ - (instancetype)init {
kSpoolDirectoryEventMaxFlushTimeSec : number,
kFileAccessPolicy : dictionary,
kFileAccessPolicyPlist : string,
kFileAccessBlockMessage : string,
kFileAccessPolicyUpdateIntervalSec : number,
kEnableMachineIDDecoration : number,
kEnableForkAndExitLogging : number,
Expand Down Expand Up @@ -441,6 +443,10 @@ + (NSSet *)keyPathsForValuesAffectingFileAccessPolicyPlist {
return [self configStateSet];
}

+ (NSSet *)keyPathsForValuesAffectingFileAccessBlockMessage {
return [self configStateSet];
}

+ (NSSet *)keyPathsForValuesAffectingFileAccessPolicyUpdateIntervalSec {
return [self configStateSet];
}
Expand Down Expand Up @@ -861,6 +867,10 @@ - (NSString *)fileAccessPolicyPlist {
}
}

- (NSString *)fileAccessBlockMessage {
return self.configState[kFileAccessBlockMessage];
}

- (uint32_t)fileAccessPolicyUpdateIntervalSec {
return self.configState[kFileAccessPolicyUpdateIntervalSec]
? [self.configState[kFileAccessPolicyUpdateIntervalSec] unsignedIntValue]
Expand Down
20 changes: 18 additions & 2 deletions Source/common/SNTFileAccessEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,23 @@
///
@property NSString *parentName;

// TODO(mlw): Store signing chain info
// @property NSArray<MOLCertificate*> *signingChain;
///
/// If the executed file was signed, this is an NSArray of MOLCertificate's
/// representing the signing chain.
///
@property NSArray<MOLCertificate *> *signingChain;

///
/// A string representing the publisher based on the signingChain
///
@property(readonly) NSString *publisherInfo;

///
/// Return an array of the underlying SecCertificateRef's of the signingChain
///
/// WARNING: If the refs need to be used for a long time be careful to properly
/// CFRetain/CFRelease the returned items.
///
@property(readonly) NSArray *signingChainCertRefs;

@end
18 changes: 18 additions & 0 deletions Source/common/SNTFileAccessEvent.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

#import "Source/common/SNTFileAccessEvent.h"

#import "Source/common/CertificateHelpers.h"

@implementation SNTFileAccessEvent

#define ENCODE(o) \
Expand All @@ -28,6 +30,12 @@ @implementation SNTFileAccessEvent
_##o = [decoder decodeObjectOfClass:[c class] forKey:@(#o)]; \
} while (0)

#define DECODEARRAY(o, c) \
do { \
_##o = [decoder decodeObjectOfClasses:[NSSet setWithObjects:[NSArray class], [c class], nil] \
forKey:@(#o)]; \
} while (0)

- (instancetype)init {
self = [super init];
if (self) {
Expand All @@ -51,6 +59,7 @@ - (void)encodeWithCoder:(NSCoder *)coder {
ENCODE(pid);
ENCODE(ppid);
ENCODE(parentName);
ENCODE(signingChain);
}

- (instancetype)initWithCoder:(NSCoder *)decoder {
Expand All @@ -67,6 +76,7 @@ - (instancetype)initWithCoder:(NSCoder *)decoder {
DECODE(pid, NSNumber);
DECODE(ppid, NSNumber);
DECODE(parentName, NSString);
DECODEARRAY(signingChain, MOLCertificate);
}
return self;
}
Expand All @@ -76,4 +86,12 @@ - (NSString *)description {
stringWithFormat:@"SNTFileAccessEvent: Accessed: %@, By: %@", self.accessedPath, self.filePath];
}

- (NSString *)publisherInfo {
return Publisher(self.signingChain, self.teamID);
}

- (NSArray *)signingChainCertRefs {
return CertificateChain(self.signingChain);
}

@end
10 changes: 10 additions & 0 deletions Source/common/String.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <Foundation/Foundation.h>

#include <optional>
#include <string>
#include <string_view>

Expand All @@ -38,6 +39,15 @@ static inline NSString *StringToNSString(const char *str) {
return [NSString stringWithUTF8String:str];
}

static inline NSString *OptionalStringToNSString(const std::optional<std::string> &optional_str) {
std::string str = optional_str.value_or("");
if (str.length() == 0) {
return nil;
} else {
return StringToNSString(str);
}
}

} // namespace santa::common

#endif
1 change: 1 addition & 0 deletions Source/gui/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ objc_library(
":SNTAboutWindowView",
":SNTDeviceMessageWindowView",
":SNTFileAccessMessageWindowView",
"//Source/common:CertificateHelpers",
"//Source/common:SNTBlockMessage_SantaGUI",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDeviceEvent",
Expand Down
22 changes: 3 additions & 19 deletions Source/gui/SNTBinaryMessageWindowController.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#import <MOLCertificate/MOLCertificate.h>
#import <SecurityInterface/SFCertificatePanel.h>

#import "Source/common/CertificateHelpers.h"
#import "Source/common/SNTBlockMessage.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTStoredEvent.h"
Expand Down Expand Up @@ -117,16 +118,11 @@ - (NSString *)messageHash {

- (IBAction)showCertInfo:(id)sender {
// SFCertificatePanel expects an NSArray of SecCertificateRef's
NSMutableArray *certArray = [NSMutableArray arrayWithCapacity:[self.event.signingChain count]];
for (MOLCertificate *cert in self.event.signingChain) {
[certArray addObject:(id)cert.certRef];
}

[[[SFCertificatePanel alloc] init] beginSheetForWindow:self.window
modalDelegate:nil
didEndSelector:nil
contextInfo:nil
certificates:certArray
certificates:CertificateChain(self.event.signingChain)
showGroup:YES];
}

Expand All @@ -148,19 +144,7 @@ + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
}

- (NSString *)publisherInfo {
MOLCertificate *leafCert = [self.event.signingChain firstObject];

if ([leafCert.commonName isEqualToString:@"Apple Mac OS Application Signing"]) {
return [NSString stringWithFormat:@"App Store (Team ID: %@)", self.event.teamID];
} else if (leafCert.commonName && leafCert.orgName) {
return [NSString stringWithFormat:@"%@ - %@", leafCert.orgName, leafCert.commonName];
} else if (leafCert.commonName) {
return leafCert.commonName;
} else if (leafCert.orgName) {
return leafCert.orgName;
} else {
return nil;
}
return Publisher(self.event.signingChain, self.event.teamID);
}

- (NSAttributedString *)attributedCustomMessage {
Expand Down
Loading

0 comments on commit 3be45fd

Please sign in to comment.