Skip to content

Commit

Permalink
Ignore TeamID and SigningID rules for dev signed code (#1241)
Browse files Browse the repository at this point in the history
* Ignore TID/SID rules for dev signed code

* Handle code paths from santactl

* Don't bother evaluating isProdSignedCallback if not necessary

* PR feedback. Link to docs.
  • Loading branch information
mlw committed Nov 27, 2023
1 parent f499654 commit 818518b
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 77 deletions.
1 change: 0 additions & 1 deletion Source/santad/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,6 @@ objc_library(
"//Source/common:SNTDeepCopy",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTMetricSet",
"//Source/common:SNTRule",
"@FMDB",
"@MOLCertificate",
Expand Down
124 changes: 48 additions & 76 deletions Source/santad/SNTPolicyProcessor.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#import "Source/santad/SNTPolicyProcessor.h"
#include <Foundation/Foundation.h>

#include <Availability.h>
#include <Kernel/kern/cs_blobs.h>
#import <MOLCodesignChecker/MOLCodesignChecker.h>
#import <Security/SecCode.h>
Expand All @@ -26,22 +25,12 @@
#import "Source/common/SNTDeepCopy.h"
#import "Source/common/SNTFileInfo.h"
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTMetricSet.h"
#import "Source/common/SNTRule.h"
#import "Source/santad/DataLayer/SNTRuleTable.h"

NSArray<NSString *> *FieldValuesForProperties(BOOL csDevFlagSet, BOOL validationCategoryThree,
BOOL oidsSet) {
#define SNT_BOOL_STR(b) (b) ? @"True" : @"False"
return
@[ SNT_BOOL_STR(csDevFlagSet), SNT_BOOL_STR(validationCategoryThree), SNT_BOOL_STR(oidsSet) ];
#undef SNT_BOOL_STR
}

@interface SNTPolicyProcessor ()
@property SNTRuleTable *ruleTable;
@property SNTConfigurator *configurator;
@property SNTMetricCounter *experimentalDevMetrics;
@end

@implementation SNTPolicyProcessor
Expand All @@ -51,11 +40,6 @@ - (instancetype)initWithRuleTable:(SNTRuleTable *)ruleTable {
if (self) {
_ruleTable = ruleTable;
_configurator = [SNTConfigurator configurator];

_experimentalDevMetrics = [[SNTMetricSet sharedInstance]
counterWithName:@"/santa/tmp_signing_info_experiment"
fieldNames:@[ @"CSDevFlagSet", @"ValidationCategory", @"DevOIDSet" ]
helpText:@"Temporary experiment for dev signed code"];
}
return self;
}
Expand All @@ -65,7 +49,7 @@ - (nonnull SNTCachedDecision *)decisionForFileInfo:(nonnull SNTFileInfo *)fileIn
certificateSHA256:(nullable NSString *)certificateSHA256
teamID:(nullable NSString *)teamID
signingID:(nullable NSString *)signingID
targetProcess:(nullable const es_process_t *)targetProc
isProdSignedCallback:(BOOL (^_Nonnull)())isProdSignedCallback
entitlementsFilterCallback:
(NSDictionary *_Nullable (^_Nullable)(
NSDictionary *_Nullable entitlements))entitlementsFilterCallback {
Expand Down Expand Up @@ -126,57 +110,23 @@ - (nonnull SNTCachedDecision *)decisionForFileInfo:(nonnull SNTFileInfo *)fileIn
cd.entitlements = [entitlements sntDeepCopy];
cd.entitlementsFiltered = NO;
}

#if defined(MAC_OS_VERSION_13_3) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_13_3
if (@available(macOS 13.0, *)) {
// Temporary experiment code...
if (targetProc != NULL && fileInfo.path != nil) {
// Doing a second SecStaticCodeCreate to not need to worry about modifying the
// dependency...
SecStaticCodeRef codeRef = NULL;
OSStatus status = SecStaticCodeCreateWithPath(
(__bridge CFURLRef)[NSURL fileURLWithPath:fileInfo.path], kSecCSDefaultFlags, &codeRef);
if (status == errSecSuccess) {
CFDictionaryRef cfSigningInfo = NULL;
SecCodeCopySigningInformation(
codeRef, kSecCSSigningInformation | kSecCSRequirementInformation, &cfSigningInfo);
NSDictionary *signingInfo = CFBridgingRelease(cfSigningInfo);

// Taken from Security framework: LWCRHelper.mm
NSString *reqVaidationCategryKey = @"validation-category";
// Taken from Security framework:
NSArray *keys = @[ @"1.2.840.113635.100.6.1.2", @"1.2.840.113635.100.6.1.12" ];
NSDictionary *lwCodeReq = signingInfo[(
__bridge NSString *)kSecCodeInfoDefaultDesignatedLightweightCodeRequirement];

NSDictionary *vals = CFBridgingRelease(SecCertificateCopyValues(
csInfo.leafCertificate.certRef, (__bridge CFArrayRef)keys, NULL));

BOOL validationCategoryThree = [lwCodeReq[reqVaidationCategryKey] intValue] == 3;
BOOL csDevFlagSet = ((targetProc->codesigning_flags & CS_DEV_CODE) != 0);
BOOL oidsSet = vals.count > 0;

[self.experimentalDevMetrics
incrementForFieldValues:FieldValuesForProperties(csDevFlagSet,
validationCategoryThree, oidsSet)];

if (!(csDevFlagSet == validationCategoryThree && csDevFlagSet == oidsSet)) {
NSDictionary *certVals = CFBridgingRelease(
SecCertificateCopyValues(csInfo.leafCertificate.certRef, NULL, NULL));
LOGI(@"EXPERIMENTAL Unexpected state difference: %@ | Flags(%d): 0x%08x, VC(%d): %d, "
@"oids(%d): %@",
fileInfo.path, csDevFlagSet, targetProc->codesigning_flags,
validationCategoryThree, [lwCodeReq[reqVaidationCategryKey] intValue], oidsSet,
[certVals allKeys]);
}
}
}
}
#endif
}
}
cd.quarantineURL = fileInfo.quarantineDataURL;

// Do not evaluate TeamID/SigningID rules for dev-signed code based on the
// assumption that orgs are generally more relaxed about dev signed cert
// protections and users can more easily produce dev-signed code that
// would otherwise be inadvertently allowed.
// Note: Only perform the check if the SigningID is still set, otherwise
// it is unsigned or had issues above that already cleared the values.
if (cd.signingID && !isProdSignedCallback()) {
LOGD(@"Ignoring TeamID and SigningID rules for code not signed with production cert: %@",
cd.signingID);
cd.teamID = nil;
cd.signingID = nil;
}

SNTRule *rule = [self.ruleTable ruleForBinarySHA256:cd.sha256
signingID:cd.signingID
certificateSHA256:cd.certSHA256
Expand Down Expand Up @@ -328,14 +278,16 @@ - (nonnull SNTCachedDecision *)decisionForFileInfo:(nonnull SNTFileInfo *)fileIn
}

return [self decisionForFileInfo:fileInfo
fileSHA256:nil
certificateSHA256:nil
teamID:teamID
signingID:signingID
targetProcess:targetProc
entitlementsFilterCallback:^NSDictionary *(NSDictionary *entitlements) {
return entitlementsFilterCallback(entitlementsFilterTeamID, entitlements);
}];
fileSHA256:nil
certificateSHA256:nil
teamID:teamID
signingID:signingID
isProdSignedCallback:^BOOL {
return ((targetProc->codesigning_flags & CS_DEV_CODE) == 0);
}
entitlementsFilterCallback:^NSDictionary *(NSDictionary *entitlements) {
return entitlementsFilterCallback(entitlementsFilterTeamID, entitlements);
}];
}

// Used by `$ santactl fileinfo`.
Expand All @@ -344,16 +296,36 @@ - (nonnull SNTCachedDecision *)decisionForFilePath:(nonnull NSString *)filePath
certificateSHA256:(nullable NSString *)certificateSHA256
teamID:(nullable NSString *)teamID
signingID:(nullable NSString *)signingID {
SNTFileInfo *fileInfo;
MOLCodesignChecker *csInfo;
NSError *error;
fileInfo = [[SNTFileInfo alloc] initWithPath:filePath error:&error];
if (!fileInfo) LOGW(@"Failed to read file %@: %@", filePath, error.localizedDescription);

SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:filePath error:&error];
if (!fileInfo) {
LOGW(@"Failed to read file %@: %@", filePath, error.localizedDescription);
} else {
csInfo = [fileInfo codesignCheckerWithError:&error];
if (error) {
LOGW(@"Failed to get codesign ingo for file %@: %@", filePath, error.localizedDescription);
}
}

return [self decisionForFileInfo:fileInfo
fileSHA256:fileSHA256
certificateSHA256:certificateSHA256
teamID:teamID
signingID:signingID
targetProcess:NULL
isProdSignedCallback:^BOOL {
if (csInfo) {
// Development OID values defined by Apple and used by the Security Framework
// https://images.apple.com/certificateauthority/pdf/Apple_WWDR_CPS_v1.31.pdf
NSArray *keys = @[ @"1.2.840.113635.100.6.1.2", @"1.2.840.113635.100.6.1.12" ];
NSDictionary *vals = CFBridgingRelease(SecCertificateCopyValues(
csInfo.leafCertificate.certRef, (__bridge CFArrayRef)keys, NULL));
return vals.count == 0;
} else {
return NO;
}
}
entitlementsFilterCallback:nil];
}

Expand Down

0 comments on commit 818518b

Please sign in to comment.