diff --git a/Podfile.lock b/Podfile.lock index 388952a9c..fe917ee81 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -2,8 +2,8 @@ PODS: - FMDB (2.6.2): - FMDB/standard (= 2.6.2) - FMDB/standard (2.6.2) - - MOLAuthenticatingURLSession (1.8): - - MOLCertificate (~> 1.3) + - MOLAuthenticatingURLSession (2.1): + - MOLCertificate (~> 1.5) - MOLCertificate (1.5) - MOLCodesignChecker (1.5): - MOLCertificate (~> 1.3) @@ -18,7 +18,7 @@ DEPENDENCIES: SPEC CHECKSUMS: FMDB: 854a0341b4726e53276f2a8996f06f1b80f9259a - MOLAuthenticatingURLSession: d04d93e7fe209533befb3d0e70a6675aa7f21d5a + MOLAuthenticatingURLSession: 2f0fd35f641bc857ee1b026021dbd759955adaa3 MOLCertificate: c39cae866d24d36fbc78032affff83d401b5384a MOLCodesignChecker: fc9c64147811d7b0d0739127003e0630dff9213a OCMock: f3f61e6eaa16038c30caa5798c5e49d3307b6f22 diff --git a/Santa.xcodeproj/project.pbxproj b/Santa.xcodeproj/project.pbxproj index d28225877..021481618 100644 --- a/Santa.xcodeproj/project.pbxproj +++ b/Santa.xcodeproj/project.pbxproj @@ -180,6 +180,8 @@ C714F8B21D8044FE00700EDF /* SNTCommandController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D35BDAB18FD7CFD00921A21 /* SNTCommandController.m */; }; C72E8D941D7F399900C86DD3 /* SNTCommandFileInfoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C72E8D931D7F399900C86DD3 /* SNTCommandFileInfoTest.m */; }; C76614EC1D142D3C00D150C1 /* SNTCommandCheckCache.m in Sources */ = {isa = PBXBuildFile; fileRef = C76614EB1D142D3C00D150C1 /* SNTCommandCheckCache.m */; }; + C795ED901D80A5BE007CFF42 /* SNTPolicyProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = C795ED8F1D80A5BE007CFF42 /* SNTPolicyProcessor.m */; }; + C795ED911D80B66B007CFF42 /* SNTPolicyProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = C795ED8F1D80A5BE007CFF42 /* SNTPolicyProcessor.m */; }; EFD8E30D32F6128B9E833D64 /* libPods-LogicTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 873978BCE4B0DBD2A89C99D1 /* libPods-LogicTests.a */; }; /* End PBXBuildFile section */ @@ -416,6 +418,8 @@ BE53E1EAE84D54E7FCB22FD5 /* libPods-santactl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-santactl.a"; sourceTree = BUILT_PRODUCTS_DIR; }; C72E8D931D7F399900C86DD3 /* SNTCommandFileInfoTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandFileInfoTest.m; sourceTree = ""; }; C76614EB1D142D3C00D150C1 /* SNTCommandCheckCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandCheckCache.m; sourceTree = ""; }; + C795ED8E1D80A5BE007CFF42 /* SNTPolicyProcessor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTPolicyProcessor.h; sourceTree = ""; }; + C795ED8F1D80A5BE007CFF42 /* SNTPolicyProcessor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTPolicyProcessor.m; sourceTree = ""; }; D227889DF327E7D3532FE00B /* Pods-Santa.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santa.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Santa/Pods-Santa.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -738,6 +742,8 @@ 0D8E18CC19107B56000F89B8 /* SNTDaemonControlController.m */, 0D63DD5A1906FCB400D346C4 /* SNTDatabaseController.h */, 0D63DD5B1906FCB400D346C4 /* SNTDatabaseController.m */, + C795ED8E1D80A5BE007CFF42 /* SNTPolicyProcessor.h */, + C795ED8F1D80A5BE007CFF42 /* SNTPolicyProcessor.m */, 0D7D01851774F93A005DBAB4 /* SNTDriverManager.h */, 0D7D01861774F93A005DBAB4 /* SNTDriverManager.m */, 0D536ED91B94E9230039A26D /* SNTEventLog.h */, @@ -1305,6 +1311,7 @@ 0D202D1A1CDD464B00A88F16 /* SNTCommandSyncPreflight.m in Sources */, 0D10BE891A0AAF6700C0C944 /* SNTDropRootPrivs.m in Sources */, 0DEFB7C61ACDE5F600B92AAE /* SNTFileWatcher.m in Sources */, + C795ED911D80B66B007CFF42 /* SNTPolicyProcessor.m in Sources */, C72E8D941D7F399900C86DD3 /* SNTCommandFileInfoTest.m in Sources */, 0DEFB7C81ACF0BFE00B92AAE /* SNTFileWatcherTest.m in Sources */, 0D28D53819D9F5910015C5EB /* SNTConfigurator.m in Sources */, @@ -1421,6 +1428,7 @@ 0D37C10F18F6029A0069BC61 /* SNTDatabaseTable.m in Sources */, 0D42D2B819D2042900955F08 /* SNTConfigurator.m in Sources */, 0DCD605519115D17006B445C /* SNTXPCControlInterface.m in Sources */, + C795ED901D80A5BE007CFF42 /* SNTPolicyProcessor.m in Sources */, 0D536EDB1B94E9230039A26D /* SNTEventLog.m in Sources */, 0DCD604F19115A06006B445C /* SNTXPCNotifierInterface.m in Sources */, 0DE5B54B1C926E3300C00603 /* SNTNotificationQueue.m in Sources */, diff --git a/Source/common/SNTCommonEnums.h b/Source/common/SNTCommonEnums.h index 2a6fb2c85..cf4c76182 100644 --- a/Source/common/SNTCommonEnums.h +++ b/Source/common/SNTCommonEnums.h @@ -41,19 +41,25 @@ typedef NS_ENUM(NSInteger, SNTClientMode) { }; typedef NS_ENUM(NSInteger, SNTEventState) { - SNTEventStateUnknown, + // Bits 0-15 bits store non-decision types + SNTEventStateUnknown = 0, + SNTEventStateBundleBinary = 1, - SNTEventStateAllowUnknown = 1, - SNTEventStateAllowBinary = 2, - SNTEventStateAllowCertificate = 3, - SNTEventStateAllowScope = 4, + // Bits 16-23 store deny decision types + SNTEventStateBlockUnknown = 1 << 16, + SNTEventStateBlockBinary = 1 << 17, + SNTEventStateBlockCertificate = 1 << 18, + SNTEventStateBlockScope = 1 << 19, - SNTEventStateBlockUnknown = 5, - SNTEventStateBlockBinary = 6, - SNTEventStateBlockCertificate = 7, - SNTEventStateBlockScope = 8, + // Bits 24-31 store allow decision types + SNTEventStateAllowUnknown = 1 << 24, + SNTEventStateAllowBinary = 1 << 25, + SNTEventStateAllowCertificate = 1 << 26, + SNTEventStateAllowScope = 1 << 27, - SNTEventStateBundleBinary = 9, + // Block and Allow masks + SNTEventStateBlock = 0xFF << 16, + SNTEventStateAllow = 0xFF << 24 }; typedef NS_ENUM(NSInteger, SNTRuleTableError) { diff --git a/Source/common/SNTKernelCommon.h b/Source/common/SNTKernelCommon.h index 89a1bf923..c11677abd 100644 --- a/Source/common/SNTKernelCommon.h +++ b/Source/common/SNTKernelCommon.h @@ -28,6 +28,10 @@ #define USERCLIENT_CLASS "com_google_SantaDriver" #define USERCLIENT_ID "com.google.santa-driver" +// Branch prediction +#define likely(x) __builtin_expect((x), 1) +#define unlikely(x) __builtin_expect((x), 0) + // List of methods supported by the driver. enum SantaDriverMethods { kSantaUserClientOpen, diff --git a/Source/common/SNTXPCControlInterface.h b/Source/common/SNTXPCControlInterface.h index 51ebd0d26..b634a944a 100644 --- a/Source/common/SNTXPCControlInterface.h +++ b/Source/common/SNTXPCControlInterface.h @@ -12,6 +12,9 @@ /// See the License for the specific language governing permissions and /// limitations under the License. +#import + +#import "SNTCachedDecision.h" #import "SNTCommonEnums.h" #import "SNTKernelCommon.h" @@ -46,6 +49,22 @@ - (void)databaseRuleForBinarySHA256:(NSString *)binarySHA256 certificateSHA256:(NSString *)certificateSHA256 reply:(void (^)(SNTRule *))reply; +/// +/// Decision ops +/// + +/// +/// @param filePath A Path to the file, can be nil. +/// @param fileSHA256 The pre-calculated SHA256 hash for the file, can be nil. If nil the hash will +/// be calculated by this method from the filePath. +/// @param signingCertificate A MOLCertificate object, can be nil. +/// @note If fileInfo and signingCertificate are both passed in, the most specific rule will be +/// returned. Binary rules take precedence over cert rules. +/// +- (void)decisionForFilePath:(NSString *)filePath + fileSHA256:(NSString *)fileSHA256 + signingCertificate:(MOLCertificate *)signingCertificate + reply:(void (^)(SNTEventState))reply; /// /// Config ops diff --git a/Source/santa-driver/SantaCache.h b/Source/santa-driver/SantaCache.h index 4206bf0e5..8303facfe 100644 --- a/Source/santa-driver/SantaCache.h +++ b/Source/santa-driver/SantaCache.h @@ -22,9 +22,6 @@ #include "SNTKernelCommon.h" -#define likely(x) __builtin_expect((x), 1) -#define unlikely(x) __builtin_expect((x), 0) - #ifdef KERNEL #include #else // KERNEL diff --git a/Source/santactl/Commands/SNTCommandFileInfo.m b/Source/santactl/Commands/SNTCommandFileInfo.m index 0a4edb7c0..4b238cee5 100644 --- a/Source/santactl/Commands/SNTCommandFileInfo.m +++ b/Source/santactl/Commands/SNTCommandFileInfo.m @@ -14,11 +14,12 @@ #import "SNTCommandController.h" -#include "SNTLogging.h" +#import +#import -#import "MOLCertificate.h" -#import "MOLCodesignChecker.h" +#import "SNTCachedDecision.h" #import "SNTFileInfo.h" +#import "SNTLogging.h" #import "SNTRule.h" #import "SNTXPCConnection.h" #import "SNTXPCControlInterface.h" @@ -261,51 +262,60 @@ - (SNTAttributeBlock)codeSigned { - (SNTAttributeBlock)rule { return ^id (SNTCommandFileInfo *fi) { - __block SNTRule *r; - dispatch_group_t group = dispatch_group_create(); + __block SNTEventState s; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [fi.daemonConn resume]; }); - dispatch_group_enter(group); + dispatch_semaphore_t sema = dispatch_semaphore_create(0); if (!fi.csc) { NSError *error; fi.csc = [[MOLCodesignChecker alloc] initWithBinaryPath:fi.filePath error:&error]; } - NSString *leafCertSHA = [[fi.csc.certificates firstObject] SHA256]; - [[fi.daemonConn remoteObjectProxy] databaseRuleForBinarySHA256:fi.fileInfo.SHA256 - certificateSHA256:leafCertSHA - reply:^(SNTRule *rule) { - if (rule) r = rule; - dispatch_group_leave(group); + [[fi.daemonConn remoteObjectProxy] decisionForFilePath:fi.filePath + fileSHA256:fi.propertyMap[kSHA256](fi) + signingCertificate:fi.csc.leafCertificate + reply:^(SNTEventState state) { + if (state) s = state; + dispatch_semaphore_signal(sema); }]; - if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) { + if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) { return @"Cannot communicate with daemon"; } else { - NSString *output; - switch (r.state) { - case SNTRuleStateWhitelist: - output = @"Whitelisted"; - if (isatty(STDOUT_FILENO) && !fi.jsonOutput) { - output = @"\033[32mWhitelisted\033[0m"; - } - return output; + NSMutableString *output = + (SNTEventStateAllow & s) ? @"Whitelisted".mutableCopy : @"Blacklisted".mutableCopy; + switch (s) { + case SNTEventStateAllowUnknown: + case SNTEventStateBlockUnknown: + [output appendString:@" (Unknown)"]; break; - case SNTRuleStateBlacklist: - case SNTRuleStateSilentBlacklist: - output = @"Blacklisted"; - if (isatty(STDOUT_FILENO) && !fi.jsonOutput) { - output = @"\033[31mBlacklisted\033[0m"; - } - return output; + case SNTEventStateAllowBinary: + case SNTEventStateBlockBinary: + [output appendString:@" (Binary)"]; + break; + case SNTEventStateAllowCertificate: + case SNTEventStateBlockCertificate: + [output appendString:@" (Certificate)"]; + break; + case SNTEventStateAllowScope: + case SNTEventStateBlockScope: + [output appendString:@" (Scope)"]; break; default: - output = @"None"; - if (isatty(STDOUT_FILENO) && !fi.jsonOutput) { - output = @"\033[33mNone\033[0m"; - } - return output; + output = @"None".mutableCopy; + break; + } + if ((SNTEventStateAllow & s) && isatty(STDOUT_FILENO) && !fi.jsonOutput) { + [output insertString:@"\033[32m" atIndex:0]; + [output appendString:@"\033[0m"]; + } else if ((SNTEventStateBlock & s) && isatty(STDOUT_FILENO) && !fi.jsonOutput) { + [output insertString:@"\033[31m" atIndex:0]; + [output appendString:@"\033[0m"]; + } else if (isatty(STDOUT_FILENO) && !fi.jsonOutput) { + [output insertString:@"\033[33m" atIndex:0]; + [output appendString:@"\033[0m"]; } + return output.copy; } }; } diff --git a/Source/santad/SNTDaemonControlController.m b/Source/santad/SNTDaemonControlController.m index e10d3ddc7..07837a452 100644 --- a/Source/santad/SNTDaemonControlController.m +++ b/Source/santad/SNTDaemonControlController.m @@ -14,6 +14,7 @@ #import "SNTDaemonControlController.h" +#import "SNTCachedDecision.h" #import "SNTConfigurator.h" #import "SNTDatabaseController.h" #import "SNTDriverManager.h" @@ -21,6 +22,7 @@ #import "SNTEventTable.h" #import "SNTLogging.h" #import "SNTNotificationQueue.h" +#import "SNTPolicyProcessor.h" #import "SNTRule.h" #import "SNTRuleTable.h" #import "SNTXPCConnection.h" @@ -35,6 +37,7 @@ @interface SNTDaemonControlController () @property NSString *_syncXsrfToken; @property dispatch_source_t syncTimer; +@property SNTPolicyProcessor *policyProcessor; @end @implementation SNTDaemonControlController @@ -44,6 +47,8 @@ - (instancetype)init { if (self) { _syncTimer = [self createSyncTimer]; [self rescheduleSyncSecondsFromNow:30]; + _policyProcessor = [[SNTPolicyProcessor alloc] initWithRuleTable: + [SNTDatabaseController ruleTable]]; } return self; } @@ -141,6 +146,17 @@ - (void)databaseRuleForBinarySHA256:(NSString *)binarySHA256 certificateSHA256:certificateSHA256]); } +#pragma mark Decision Ops + +- (void)decisionForFilePath:(NSString *)filePath + fileSHA256:(NSString *)fileSHA256 + signingCertificate:(MOLCertificate *)signingCertificate + reply:(void (^)(SNTEventState))reply { + reply([self.policyProcessor decisionForFilePath:filePath + fileSHA256:fileSHA256 + signingCertificate:signingCertificate].decision); +} + #pragma mark Config Ops - (void)clientMode:(void (^)(SNTClientMode))reply { diff --git a/Source/santad/SNTExecutionController.h b/Source/santad/SNTExecutionController.h index 567532f98..1c2c751c6 100644 --- a/Source/santad/SNTExecutionController.h +++ b/Source/santad/SNTExecutionController.h @@ -23,10 +23,8 @@ @class SNTRuleTable; /// -/// SNTExecutionController is responsible for everything that happens when a request to execute -/// a binary occurs: -/// + Making a decision about whether to allow or deny this binary based on any existing rules -/// for that specific binary, its signing certificate and the operating mode of santad. +/// SNTExecutionController is responsible for handling binary execution requests: +/// + Uses SNTPolicyProcessor to make a decision about whether to allow or deny the binary. /// + Sending the decision to the kernel as soon as possible /// + (If denied or unknown) Storing details about the execution event to the database /// for upload and spwaning santactl to quickly try and send that to the server. diff --git a/Source/santad/SNTExecutionController.m b/Source/santad/SNTExecutionController.m index 70624b6ca..b022658a7 100644 --- a/Source/santad/SNTExecutionController.m +++ b/Source/santad/SNTExecutionController.m @@ -32,6 +32,7 @@ #import "SNTEventTable.h" #import "SNTFileInfo.h" #import "SNTNotificationQueue.h" +#import "SNTPolicyProcessor.h" #import "SNTRule.h" #import "SNTRuleTable.h" #import "SNTStoredEvent.h" @@ -41,6 +42,7 @@ @interface SNTExecutionController () @property SNTEventLog *eventLog; @property SNTEventTable *eventTable; @property SNTNotificationQueue *notifierQueue; +@property SNTPolicyProcessor *policyProcessor; @property SNTRuleTable *ruleTable; @property NSMutableDictionary *uploadBackoff; @@ -63,6 +65,7 @@ - (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager _eventTable = eventTable; _notifierQueue = notifierQueue; _eventLog = eventLog; + _policyProcessor = [[SNTPolicyProcessor alloc] initWithRuleTable:_ruleTable]; _uploadBackoff = [NSMutableDictionary dictionaryWithCapacity:128]; _eventQueue = dispatch_queue_create("com.google.santad.event_upload", DISPATCH_QUEUE_SERIAL); @@ -76,64 +79,17 @@ - (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager #pragma mark Binary Validation -- (SNTEventState)makeDecision:(SNTCachedDecision *)cd binaryInfo:(SNTFileInfo *)fi { - SNTRule *rule = [_ruleTable ruleForBinarySHA256:cd.sha256 certificateSHA256:cd.certSHA256]; - if (rule) { - switch (rule.type) { - case SNTRuleTypeBinary: - switch (rule.state) { - case SNTRuleStateWhitelist: - return SNTEventStateAllowBinary; - case SNTRuleStateSilentBlacklist: - cd.silentBlock = YES; - case SNTRuleStateBlacklist: - cd.customMsg = rule.customMsg; - return SNTEventStateBlockBinary; - default: break; - } - break; - case SNTRuleTypeCertificate: - switch (rule.state) { - case SNTRuleStateWhitelist: - return SNTEventStateAllowCertificate; - case SNTRuleStateSilentBlacklist: - cd.silentBlock = YES; - case SNTRuleStateBlacklist: - cd.customMsg = rule.customMsg; - return SNTEventStateBlockCertificate; - default: break; - } - break; - default: - break; - } - } - - NSString *msg = [self fileIsScopeBlacklisted:fi]; - if (msg) { - cd.decisionExtra = msg; - return SNTEventStateBlockScope; - } - - msg = [self fileIsScopeWhitelisted:fi]; - if (msg) { - cd.decisionExtra = msg; - return SNTEventStateAllowScope; - } - - switch ([[SNTConfigurator configurator] clientMode]) { - case SNTClientModeMonitor: return SNTEventStateAllowUnknown; - case SNTClientModeLockdown: return SNTEventStateBlockUnknown; - default: return SNTEventStateBlockUnknown; - } -} - - (void)validateBinaryWithMessage:(santa_message_t)message { // Get info about the file. If we can't get this info, allow execution and log an error. + if (unlikely(message.path == NULL)) { + LOGE(@"Path for vnode_id is NULL: %llu", message.vnode_id); + [_driverManager postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:message.vnode_id]; + return; + } NSError *fileInfoError; SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:@(message.path) error:&fileInfoError]; - if (!binInfo) { - LOGW(@"Failed to read file %@: %@", binInfo.path, fileInfoError.localizedDescription); + if (unlikely(!binInfo)) { + LOGE(@"Failed to read file %@: %@", @(message.path), fileInfoError.localizedDescription); [_driverManager postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:message.vnode_id]; return; } @@ -145,16 +101,16 @@ - (void)validateBinaryWithMessage:(santa_message_t)message { MOLCodesignChecker *csInfo = [[MOLCodesignChecker alloc] initWithBinaryPath:binInfo.path]; // Actually make the decision. - SNTCachedDecision *cd = [[SNTCachedDecision alloc] init]; - cd.sha256 = binInfo.SHA256; - cd.certCommonName = csInfo.leafCertificate.commonName; - cd.certSHA256 = csInfo.leafCertificate.SHA256; + SNTCachedDecision *cd = [self.policyProcessor decisionForFileInfo:binInfo + fileSHA256:nil + signingCertificate:csInfo.leafCertificate]; cd.vnodeId = message.vnode_id; - cd.quarantineURL = binInfo.quarantineDataURL; - cd.decision = [self makeDecision:cd binaryInfo:binInfo]; + + // Formulate an action from the decision + santa_action_t action = + (SNTEventStateAllow & cd.decision) ? ACTION_RESPOND_ALLOW : ACTION_RESPOND_DENY; // Save decision details for logging the execution later. - santa_action_t action = [self actionForEventState:cd.decision]; if (action == ACTION_RESPOND_ALLOW) [_eventLog saveDecisionDetails:cd]; // Send the decision to the kernel. @@ -236,45 +192,6 @@ - (void)validateBinaryWithMessage:(santa_message_t)message { } } -/// -/// Checks whether the file at @c path is in-scope for checking with Santa. -/// -/// Files that are out of scope: -/// + Non Mach-O files that are not part of an installer package. -/// + Files in whitelisted path. -/// -/// @return @c YES if file is in scope, @c NO otherwise. -/// -- (NSString *)fileIsScopeWhitelisted:(SNTFileInfo *)fi { - // Determine if file is within a whitelisted path - NSRegularExpression *re = [[SNTConfigurator configurator] whitelistPathRegex]; - if ([re numberOfMatchesInString:fi.path options:0 range:NSMakeRange(0, fi.path.length)]) { - return @"Whitelist Regex"; - } - - // If file is not a Mach-O file, we're not interested unless it's part of an install package. - // TODO(rah): Consider adding an option to check all scripts. - // TODO(rah): Consider adding an option to disable package script checks. - if (!fi.isMachO && ![fi.path hasPrefix:@"/private/tmp/PKInstallSandbox."]) { - return @"Not a Mach-O"; - } - - return nil; -} - -- (NSString *)fileIsScopeBlacklisted:(SNTFileInfo *)fi { - NSRegularExpression *re = [[SNTConfigurator configurator] blacklistPathRegex]; - if ([re numberOfMatchesInString:fi.path options:0 range:NSMakeRange(0, fi.path.length)]) { - return @"Blacklist Regex"; - } - - if ([[SNTConfigurator configurator] enablePageZeroProtection] && fi.isMissingPageZero) { - return @"Missing __PAGEZERO"; - } - - return nil; -} - /** Workaround for issue with PrinterProxy.app. @@ -351,24 +268,6 @@ - (void)initiateEventUploadForEvent:(SNTStoredEvent *)event { } } -- (santa_action_t)actionForEventState:(SNTEventState)state { - switch (state) { - case SNTEventStateAllowBinary: - case SNTEventStateAllowCertificate: - case SNTEventStateAllowScope: - case SNTEventStateAllowUnknown: - return ACTION_RESPOND_ALLOW; - case SNTEventStateBlockBinary: - case SNTEventStateBlockCertificate: - case SNTEventStateBlockScope: - case SNTEventStateBlockUnknown: - return ACTION_RESPOND_DENY; - default: - LOGW(@"Invalid event state %ld", state); - return ACTION_RESPOND_DENY; - } -} - - (void)printMessage:(NSString *)msg toTTYForPID:(pid_t)pid { if (pid < 2) return; // don't bother even looking for launchd. diff --git a/Source/santad/SNTPolicyProcessor.h b/Source/santad/SNTPolicyProcessor.h new file mode 100644 index 000000000..bc700c69a --- /dev/null +++ b/Source/santad/SNTPolicyProcessor.h @@ -0,0 +1,58 @@ +/// Copyright 2015 Google Inc. All rights reserved. +/// +/// 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 +/// +/// http://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 "SNTCommonEnums.h" +#import "SNTKernelCommon.h" + +#import + +@class SNTCachedDecision; +@class SNTFileInfo; +@class SNTRuleTable; + +/// +/// Creates SNTCachedDecision objects from a SNTFileInfo object or a file path. Decisions are based +/// on any existing rules for that specific binary, its signing certificate and the operating mode +/// of santad. +/// +@interface SNTPolicyProcessor : NSObject + +/// +/// @param ruleTable The rule table to be used for every decision +/// +- (nullable instancetype)initWithRuleTable:(nonnull SNTRuleTable *)ruleTable; + +/// +/// @param fileInfo A SNTFileInfo object. +/// @param fileSHA256 The pre-calculated SHA256 hash for the file, can be nil. If nil the hash will +/// be calculated by this method from the filePath. +/// @param signingCertificate A MOLCertificate object, can be nil. +/// @note If fileInfo and signingCertificate are both passed in, the most specific rule will be +/// returned. Binary rules take precedence over cert rules. +/// +- (nonnull SNTCachedDecision *)decisionForFileInfo:(nonnull SNTFileInfo *)fileInfo + fileSHA256:(nullable NSString *)fileSHA256 + signingCertificate:(nullable MOLCertificate *)signingCertificate; + +/// +/// A wrapper for decisionForFileInfo:fileSHA256:signingCertificate:. This method is slower as it +/// has to create the SNTFileInfo object. This is mainly used by the santactl binary because +/// SNTFileInfo is not SecureCoding compliant. If the SHA256 hash of the file has already been +/// calculated, use the fileSHA256 parameter to save a second calculation of the hash. +/// +- (nonnull SNTCachedDecision *)decisionForFilePath:(nonnull NSString *)filePath + fileSHA256:(nullable NSString *)fileSHA256 + signingCertificate:(nullable MOLCertificate *)signingCertificate; + +@end diff --git a/Source/santad/SNTPolicyProcessor.m b/Source/santad/SNTPolicyProcessor.m new file mode 100644 index 000000000..424ed7e9a --- /dev/null +++ b/Source/santad/SNTPolicyProcessor.m @@ -0,0 +1,166 @@ +/// Copyright 2015 Google Inc. All rights reserved. +/// +/// 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 +/// +/// http://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 "SNTPolicyProcessor.h" + +#include "SNTLogging.h" + +#import "SNTCachedDecision.h" +#import "SNTConfigurator.h" +#import "SNTFileInfo.h" +#import "SNTRule.h" +#import "SNTRuleTable.h" + +@interface SNTPolicyProcessor() +@property SNTRuleTable *ruleTable; +@end + +@implementation SNTPolicyProcessor + +- (instancetype)initWithRuleTable:(SNTRuleTable *)ruleTable { + self = [super init]; + if (self) { + _ruleTable = ruleTable; + } + return self; +} + +- (SNTCachedDecision *)decisionForFileInfo:(SNTFileInfo *)fileInfo + fileSHA256:(NSString *)fileSHA256 + signingCertificate:(MOLCertificate *)signingCertificate { + SNTCachedDecision *cd = [[SNTCachedDecision alloc] init]; + if (fileInfo) { + cd.sha256 = fileSHA256 ?: fileInfo.SHA256; + cd.quarantineURL = fileInfo.quarantineDataURL; + } + if (signingCertificate) { + cd.certCommonName = signingCertificate.commonName; + cd.certSHA256 = signingCertificate.SHA256; + } + + SNTRule *rule = [self.ruleTable ruleForBinarySHA256:cd.sha256 + certificateSHA256:cd.certSHA256]; + if (rule) { + switch (rule.type) { + case SNTRuleTypeBinary: + switch (rule.state) { + case SNTRuleStateWhitelist: + cd.decision = SNTEventStateAllowBinary; + return cd; + case SNTRuleStateSilentBlacklist: + cd.silentBlock = YES; + case SNTRuleStateBlacklist: + cd.customMsg = rule.customMsg; + cd.decision = SNTEventStateBlockBinary; + return cd; + default: break; + } + break; + case SNTRuleTypeCertificate: + switch (rule.state) { + case SNTRuleStateWhitelist: + cd.decision = SNTEventStateAllowCertificate; + return cd; + case SNTRuleStateSilentBlacklist: + cd.silentBlock = YES; + case SNTRuleStateBlacklist: + cd.customMsg = rule.customMsg; + cd.decision = SNTEventStateBlockCertificate; + return cd; + default: break; + } + break; + default: + break; + } + } + + NSString *msg = [self fileIsScopeBlacklisted:fileInfo]; + if (msg) { + cd.decisionExtra = msg; + cd.decision = SNTEventStateBlockScope; + return cd; + } + + msg = [self fileIsScopeWhitelisted:fileInfo]; + if (msg) { + cd.decisionExtra = msg; + cd.decision = SNTEventStateAllowScope; + return cd; + } + + switch ([[SNTConfigurator configurator] clientMode]) { + case SNTClientModeMonitor: + cd.decision = SNTEventStateAllowUnknown; + return cd; + case SNTClientModeLockdown: + cd.decision = SNTEventStateBlockUnknown; + return cd; + default: + cd.decision = SNTEventStateBlockUnknown; + return cd; + } +} + +- (SNTCachedDecision *)decisionForFilePath:(NSString *)filePath + fileSHA256:(NSString *)fileSHA256 + signingCertificate:(MOLCertificate *)signingCertificate { + NSError *error; + SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:filePath error:&error]; + if (!fileInfo) LOGW(@"Failed to read file %@: %@", filePath, error.localizedDescription); + return [self decisionForFileInfo:fileInfo + fileSHA256:fileSHA256 + signingCertificate:signingCertificate]; +} + +/// +/// Checks whether the file at @c path is in-scope for checking with Santa. +/// +/// Files that are out of scope: +/// + Non Mach-O files that are not part of an installer package. +/// + Files in whitelisted path. +/// +/// @return @c YES if file is in scope, @c NO otherwise. +/// +- (NSString *)fileIsScopeWhitelisted:(SNTFileInfo *)fi { + // Determine if file is within a whitelisted path + NSRegularExpression *re = [[SNTConfigurator configurator] whitelistPathRegex]; + if ([re numberOfMatchesInString:fi.path options:0 range:NSMakeRange(0, fi.path.length)]) { + return @"Whitelist Regex"; + } + + // If file is not a Mach-O file, we're not interested unless it's part of an install package. + // TODO(rah): Consider adding an option to check all scripts. + // TODO(rah): Consider adding an option to disable package script checks. + if (!fi.isMachO && ![fi.path hasPrefix:@"/private/tmp/PKInstallSandbox."]) { + return @"Not a Mach-O"; + } + + return nil; +} + +- (NSString *)fileIsScopeBlacklisted:(SNTFileInfo *)fi { + NSRegularExpression *re = [[SNTConfigurator configurator] blacklistPathRegex]; + if ([re numberOfMatchesInString:fi.path options:0 range:NSMakeRange(0, fi.path.length)]) { + return @"Blacklist Regex"; + } + + if ([[SNTConfigurator configurator] enablePageZeroProtection] && fi.isMissingPageZero) { + return @"Missing __PAGEZERO"; + } + + return nil; +} + +@end diff --git a/Tests/LogicTests/Resources/sync_eventupload_input_basic.plist b/Tests/LogicTests/Resources/sync_eventupload_input_basic.plist index 14d2a5d02..ff7981d8d 100644 --- a/Tests/LogicTests/Resources/sync_eventupload_input_basic.plist +++ b/Tests/LogicTests/Resources/sync_eventupload_input_basic.plist @@ -136,43 +136,7 @@ 9 NS.data - - MIIFOzCCBCOgAwIBAgIIKtpxuqe9F58wDQYJKoZIhvcNAQEFBQAw - fzELMAkGA1UEBhMCVVMxEzARBgNVBAoMCkFwcGxlIEluYy4xJjAk - BgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTMw - MQYDVQQDDCpBcHBsZSBDb2RlIFNpZ25pbmcgQ2VydGlmaWNhdGlv - biBBdXRob3JpdHkwHhcNMTMwNDEyMjIzNDM1WhcNMjEwNDEyMjIz - NDM1WjBWMQswCQYDVQQGEwJVUzETMBEGA1UECgwKQXBwbGUgSW5j - LjEXMBUGA1UECwwOQXBwbGUgU29mdHdhcmUxGTAXBgNVBAMMEFNv - ZnR3YXJlIFNpZ25pbmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw - ggEKAoIBAQC/MLh0mE+uBguklG4xVG0J0TyjsDkQqdDmqmAiXdPk - hKJAQZBkxmA9kWHaUqhFJ54sZMvkHqgkClI6s9PsFkF4wZ7RBuZ4 - JWMI89/KQeYd/jXpUVwTFYvp0Z1xe9HJqkuemdqPwCm4L5BvpLtl - j4Bq1z1obeR4wqUSL/gy6X7JXVyMPhYgG9denRuGLQj3vBmkTQ5B - pErbaxqARVAEqUyNFQfqaie9u4iePD+yUjmX47fI61RSmIovI1Zl - 5ekq2VG0I/oE3ffroN/VmvJeCPFfh/CxR2x1sbGM0RPjesHsYkF0 - poM08fladGQ5P1luzyzAYIMpPOfeT18N85M5XzCNAgMBAAGjggHi - MIIB3jAdBgNVHQ4EFgQUxu0+Svsu6D8T1aAVs13Z57P3aDUwDAYD - VR0TAQH/BAIwADAfBgNVHSMEGDAWgBSOaabEd0JOBKVWQpxRH4ba - 0iCPCTCCARwGA1UdIASCARMwggEPMIIBCwYJKoZIhvdjZAUBMIH9 - MDUGCCsGAQUFBwIBFilodHRwOi8vd3d3LmFwcGxlLmNvbS9jZXJ0 - aWZpY2F0ZWF1dGhvcml0eTCBwwYIKwYBBQUHAgIwgbYMgbNSZWxp - YW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh - c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs - ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2Us - IGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlmaWNhdGlvbiBw - cmFjdGljZSBzdGF0ZW1lbnRzLjA1BgNVHR8ELjAsMCqgKKAmhiRo - dHRwOi8vY3JsLmFwcGxlLmNvbS9jb2Rlc2lnbmluZy5jcmwwDgYD - VR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMDMA8G - CSqGSIb3Y2QGFgQCBQAwDQYJKoZIhvcNAQEFBQADggEBAFfUxSFX - GxTaEjIsEQUMBA+VqtTi+vLEbWjeUlINInPIhXMd71FO8IpJsGiU - ZVEi3/1AjzW0aEBSuyWOzPrOfBJW2MDQVQW1SrG1YfyVfJFectEo - tB0rbdpLZ58F/ObnWUpDXh97hDe+/rqKKzMFlFCDuP6a2wO7jWLy - GW13k+N1zzZZMV4IbV0BHGVTUpN2eJwXCxAelLw2kFZLRC6Y2aYx - ofAcZpSZVHMTtVE4vCSioDA7emWHrMC8FfReMMedoyoE38TPR4Rt - n/3/RcOgGaw8u62PlPm5x8hxNhHt6AG6tHVIgqQqUxoFBZudxkcb - 9eggcqAbS+W+ZPw4DZr/Q0E= - + MIIFOzCCBCOgAwIBAgIIKtpxuqe9F58wDQYJKoZIhvcNAQEFBQAwfzELMAkGA1UEBhMCVVMxEzARBgNVBAoMCkFwcGxlIEluYy4xJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTMwMQYDVQQDDCpBcHBsZSBDb2RlIFNpZ25pbmcgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTMwNDEyMjIzNDM1WhcNMjEwNDEyMjIzNDM1WjBWMQswCQYDVQQGEwJVUzETMBEGA1UECgwKQXBwbGUgSW5jLjEXMBUGA1UECwwOQXBwbGUgU29mdHdhcmUxGTAXBgNVBAMMEFNvZnR3YXJlIFNpZ25pbmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/MLh0mE+uBguklG4xVG0J0TyjsDkQqdDmqmAiXdPkhKJAQZBkxmA9kWHaUqhFJ54sZMvkHqgkClI6s9PsFkF4wZ7RBuZ4JWMI89/KQeYd/jXpUVwTFYvp0Z1xe9HJqkuemdqPwCm4L5BvpLtlj4Bq1z1obeR4wqUSL/gy6X7JXVyMPhYgG9denRuGLQj3vBmkTQ5BpErbaxqARVAEqUyNFQfqaie9u4iePD+yUjmX47fI61RSmIovI1Zl5ekq2VG0I/oE3ffroN/VmvJeCPFfh/CxR2x1sbGM0RPjesHsYkF0poM08fladGQ5P1luzyzAYIMpPOfeT18N85M5XzCNAgMBAAGjggHiMIIB3jAdBgNVHQ4EFgQUxu0+Svsu6D8T1aAVs13Z57P3aDUwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBSOaabEd0JOBKVWQpxRH4ba0iCPCTCCARwGA1UdIASCARMwggEPMIIBCwYJKoZIhvdjZAUBMIH9MDUGCCsGAQUFBwIBFilodHRwOi8vd3d3LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhvcml0eTCBwwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjA1BgNVHR8ELjAsMCqgKKAmhiRodHRwOi8vY3JsLmFwcGxlLmNvbS9jb2Rlc2lnbmluZy5jcmwwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMDMA8GCSqGSIb3Y2QGFgQCBQAwDQYJKoZIhvcNAQEFBQADggEBAFfUxSFXGxTaEjIsEQUMBA+VqtTi+vLEbWjeUlINInPIhXMd71FO8IpJsGiUZVEi3/1AjzW0aEBSuyWOzPrOfBJW2MDQVQW1SrG1YfyVfJFectEotB0rbdpLZ58F/ObnWUpDXh97hDe+/rqKKzMFlFCDuP6a2wO7jWLyGW13k+N1zzZZMV4IbV0BHGVTUpN2eJwXCxAelLw2kFZLRC6Y2aYxofAcZpSZVHMTtVE4vCSioDA7emWHrMC8FfReMMedoyoE38TPR4Rtn/3/RcOgGaw8u62PlPm5x8hxNhHt6AG6tHVIgqQqUxoFBZudxkcb9eggcqAbS+W+ZPw4DZr/Q0E= $classes @@ -212,35 +176,7 @@ 9 NS.data - - MIIEDjCCAvagAwIBAgIBITANBgkqhkiG9w0BAQUFADBiMQswCQYD - VQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMd - QXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMT - DUFwcGxlIFJvb3QgQ0EwHhcNMTExMDI0MTczOTQxWhcNMjYxMDI0 - MTczOTQxWjB/MQswCQYDVQQGEwJVUzETMBEGA1UECgwKQXBwbGUg - SW5jLjEmMCQGA1UECwwdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRo - b3JpdHkxMzAxBgNVBAMMKkFwcGxlIENvZGUgU2lnbmluZyBDZXJ0 - aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQAD - ggEPADCCAQoCggEBAKKoEXH/DvkLa/glDZiBXWtZvVobibPn5e7O - OZgjNTlInyGrJ9nunCDwZDgIawynz9xQth0GxFvxXRqbVGWGcy9i - 5Ti9ARBkcm18aUdhnBAFJuPrhcIsJNxqwj+I/MysKUyhSXkRmnV2 - 5R640NIJtExTePvfGHahj6SpMsqRp7b6l705qs0bUBGIq2rt62bK - IEusOy3vqufWyYgtacKkKmEv24cC86EhuUyfDvj52S3KcgR/Ha5u - +j+Is8yjQO4XhxhRlrzP5C2twulZTl0cZTMnA6pno5Mkh8eHeQK5 - XZizDu7NaQg+jEiSJLJt1zC+z9jkyKeXgdAeI9w4mV9h/oUCAwEA - AaOBsTCBrjAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYB - BQUHAwMwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjmmmxHdC - TgSlVkKcUR+G2tIgjwkwHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40u - QKb3R01/CF4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL3d3dy5h - cHBsZS5jb20vYXBwbGVjYS9yb290LmNybDANBgkqhkiG9w0BAQUF - AAOCAQEAcHOt9lIVarcVGN6pKtGddpsesmmWx8LD4SvQ7wddcPja - PFpIR9s5bIDKc95iG7c6yqNaHuOH2iVKk5vvcxCTc13j9J1+3g+B - 9qmZwVhunPSJAL7PT/8C0w789fP0choysconDt6o05mPauaZ+2HJ - T/IXsRhn8DDAxgruyESBpIm78XlBw+6uyGtnfMxsSYZMAtPTam4Y - nPhcOMgwh5ow2mcouOKaedqfpTsfUWI7IvF+U3waC8PwTdxJRPKI - iM46W7md6bK3W1KnxtVYiXK32MyzqBgdUJc/Hdpqrji/e3kxvmO5 - 94WFF+ltisTiGJQv129SpZmx3USbB3CSiCZ32w== - + MIIEDjCCAvagAwIBAgIBITANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMTExMDI0MTczOTQxWhcNMjYxMDI0MTczOTQxWjB/MQswCQYDVQQGEwJVUzETMBEGA1UECgwKQXBwbGUgSW5jLjEmMCQGA1UECwwdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMzAxBgNVBAMMKkFwcGxlIENvZGUgU2lnbmluZyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKKoEXH/DvkLa/glDZiBXWtZvVobibPn5e7OOZgjNTlInyGrJ9nunCDwZDgIawynz9xQth0GxFvxXRqbVGWGcy9i5Ti9ARBkcm18aUdhnBAFJuPrhcIsJNxqwj+I/MysKUyhSXkRmnV25R640NIJtExTePvfGHahj6SpMsqRp7b6l705qs0bUBGIq2rt62bKIEusOy3vqufWyYgtacKkKmEv24cC86EhuUyfDvj52S3KcgR/Ha5u+j+Is8yjQO4XhxhRlrzP5C2twulZTl0cZTMnA6pno5Mkh8eHeQK5XZizDu7NaQg+jEiSJLJt1zC+z9jkyKeXgdAeI9w4mV9h/oUCAwEAAaOBsTCBrjAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjmmmxHdCTgSlVkKcUR+G2tIgjwkwHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL3d3dy5hcHBsZS5jb20vYXBwbGVjYS9yb290LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAcHOt9lIVarcVGN6pKtGddpsesmmWx8LD4SvQ7wddcPjaPFpIR9s5bIDKc95iG7c6yqNaHuOH2iVKk5vvcxCTc13j9J1+3g+B9qmZwVhunPSJAL7PT/8C0w789fP0choysconDt6o05mPauaZ+2HJT/IXsRhn8DDAxgruyESBpIm78XlBw+6uyGtnfMxsSYZMAtPTam4YnPhcOMgwh5ow2mcouOKaedqfpTsfUWI7IvF+U3waC8PwTdxJRPKIiM46W7md6bK3W1KnxtVYiXK32MyzqBgdUJc/Hdpqrji/e3kxvmO594WFF+ltisTiGJQv129SpZmx3USbB3CSiCZ32w== $class @@ -261,40 +197,7 @@ 9 NS.data - - MIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQswCQYD - VQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMd - QXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMT - DUFwcGxlIFJvb3QgQ0EwHhcNMDYwNDI1MjE0MDM2WhcNMzUwMjA5 - MjE0MDM2WjBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUg - SW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRo - b3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqG - SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmE - Les2oldMVeyLGYne+Uts9QerIjAC6Bg++FAJ039BqJj50cpmnCRr - EdCju+QbKsMflZ56DKRHi1vUFjczy8QPTc4UadHJGXL1XQ7Vf1+b - 8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQZ48ItCD3y6ws - IG9wtj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCS - C7EhFi501TwN22IWq6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CW - QYnEdGILEINBhzOKgbEwWOxaBDKMaLOPHd5lc/9nXmW8Sdh2nzMU - ZaF3lMktAgMBAAGjggF6MIIBdjAOBgNVHQ8BAf8EBAMCAQYwDwYD - VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9BpR5R2Cf70a40uQKb3 - R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4w - ggERBgNVHSAEggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggr - BgEFBQcCARYeaHR0cHM6Ly93d3cuYXBwbGUuY29tL2FwcGxlY2Ev - MIHDBggrBgEFBQcCAjCBthqBs1JlbGlhbmNlIG9uIHRoaXMgY2Vy - dGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5j - ZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1z - IGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9s - aWN5IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVu - dHMuMA0GCSqGSIb3DQEBBQUAA4IBAQBcNplMLXi37Yyb3PN3m/J2 - 0ncwT8EfhYOFG5k9RzfyqZtAjizUsZAS2L70c5vu0mQPy3lPNNii - Pvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJfBdAVhEedNO3 - iyM7R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr - 1KIkIxH3oayPc4FgxhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP - 3uujL/lTaltkwGMzd/c6ByxW69oPIQ7aunMZT7XZNn/Bh1XZp5m5 - MkL72NVxnn6hUrcbvZNCJBIqxw8dtk2cXmPIS4AXUKqK1drk/NAJ - BzewdXUh - + MIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMDYwNDI1MjE0MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne+Uts9QerIjAC6Bg++FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjczy8QPTc4UadHJGXL1XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQZ48ItCD3y6wsIG9wtj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCSC7EhFi501TwN22IWq6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINBhzOKgbEwWOxaBDKMaLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIBdjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9BpR5R2Cf70a40uQKb3R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wggERBgNVHSAEggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcCARYeaHR0cHM6Ly93d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCBthqBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3DQEBBQUAA4IBAQBcNplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizUsZAS2L70c5vu0mQPy3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJfBdAVhEedNO3iyM7R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr1KIkIxH3oayPc4FgxhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltkwGMzd/c6ByxW69oPIQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIqxw8dtk2cXmPIS4AXUKqK1drk/NAJBzewdXUh $classes @@ -314,7 +217,7 @@ 18 NS.time - 485894498.53763503 + 485894498.537635 $classes @@ -325,7 +228,7 @@ $classname NSDate - 6 + 131072 11196 10760 bash @@ -454,7 +357,7 @@ 18 NS.time - 485894568.92822498 + 485894568.928225 1 11427