Compare commits

...

14 Commits
0.9.2 ... 0.9.3

Author SHA1 Message Date
Russell Hancox
61c817c9cb Tests: Fix SNTRuleTable tests 2015-10-09 15:14:15 -04:00
Russell Hancox
2ed384f677 santactl/sync: Only update client mode at end of sync 2015-10-09 13:12:25 -04:00
Russell Hancox
7a851cb080 santad: Typo in comment 2015-10-08 19:54:23 -04:00
Russell Hancox
13aa889633 SNTFileInfo: Add fileSize method, use it in SNTEventLog 2015-10-08 17:57:02 -04:00
Russell Hancox
5c3fba5f41 santad: Prevent user/server from accidentally deleting rules that would kill the system. 2015-10-08 17:45:39 -04:00
Russell Hancox
145d9216bf Project: Don't bother with "xcodebuild clean" for Rakefile clean rule 2015-10-08 17:43:59 -04:00
Russell Hancox
84f46de940 Driver/Daemon: Collect process name in-kernel for file events, parent name for exec requests. For file events log process name and path, if possible. 2015-10-05 17:09:33 -04:00
Russell Hancox
cb9a5b6fbe santactl: Add --json option to both status and version commands. 2015-10-05 14:15:10 -04:00
Russell Hancox
d9718faba4 SNTFileInfo: Return non-embedded dict if locating embedded fails 2015-10-05 14:13:40 -04:00
Russell Hancox
5472ff41f0 santactl/status: Show timezone as UTF offset rather than name 2015-10-05 13:00:55 -04:00
Russell Hancox
4f94c3b310 santactl/status: Use fixed format for sync date output but still include TZ. 2015-10-03 19:57:19 -04:00
Russell Hancox
420f1efa50 santad: For file write events, print process name as well as pid. 2015-10-03 18:16:06 -04:00
Russell Hancox
5d2ce17817 santactl/status: When printing last sync date, use local timezone and locale settings 2015-10-03 18:15:41 -04:00
Russell Hancox
053cb823a1 santa-driver: Change C++ std to C++11
This is mostly just to quiet the warning about override not being set on getMetaClass, which is part of the OSDeclareDefaultStructors macro.
2015-10-03 18:15:11 -04:00
20 changed files with 205 additions and 86 deletions

View File

@@ -44,7 +44,6 @@ end
desc "Clean"
task :clean => :init do
puts "Cleaning"
xcodebuild("-scheme All clean")
FileUtils.rm_rf(OUTPUT_PATH)
FileUtils.rm_rf(DIST_PATH)
end

View File

@@ -1647,7 +1647,10 @@
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_WARN_CXX0X_EXTENSIONS = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
GCC_C_LANGUAGE_STANDARD = c99;
MACOSX_DEPLOYMENT_TARGET = 10.9;
ONLY_ACTIVE_ARCH = YES;
PROVISIONING_PROFILE = "";
@@ -1667,8 +1670,11 @@
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_WARN_CXX0X_EXTENSIONS = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = c99;
MACOSX_DEPLOYMENT_TARGET = 10.9;
PROVISIONING_PROFILE = "";
RUN_CLANG_STATIC_ANALYZER = YES;

View File

@@ -140,4 +140,9 @@
///
- (NSArray *)downloadURLs;
///
/// @return The size of the file in bytes.
///
- (NSUInteger)fileSize;
@end

View File

@@ -235,10 +235,10 @@
// This could (and used to) use CFBundleCopyInfoDictionaryForURL but that uses mmap to read
// the file and so can cause SIGBUS if the file is deleted/truncated while it's working.
MachHeaderWithOffset *mhwo = [[self.machHeaders allValues] firstObject];
if (!mhwo) return nil;
if (!mhwo) return self.infoDict;
struct mach_header *mh = (struct mach_header *)mhwo.data.bytes;
if (mh->filetype != MH_EXECUTE) return nil;
if (mh->filetype != MH_EXECUTE) return self.infoDict;
BOOL is64 = (mh->magic == MH_MAGIC_64 || mh->magic == MH_CIGAM_64);
uint32_t ncmds = mh->ncmds;
uint32_t nsects = 0;
@@ -253,7 +253,7 @@
// Loop through the load commands looking for the segment named __TEXT
for (uint32_t i = 0; i < ncmds; i++) {
NSData *cmdData = [self safeSubdataWithRange:NSMakeRange(offset, sz_segment)];
if (!cmdData) return nil;
if (!cmdData) return self.infoDict;
struct segment_command_64 *lc = (struct segment_command_64 *)[cmdData bytes];
if (lc->cmd == LC_SEGMENT || lc->cmd == LC_SEGMENT_64) {
if (strncmp(lc->segname, "__TEXT", 6) == 0) {
@@ -268,18 +268,18 @@
// Loop through the sections in the __TEXT segment looking for an __info_plist section.
for (uint32_t i = 0; i < nsects; i++) {
NSData *sectData = [self safeSubdataWithRange:NSMakeRange(offset, sz_section)];
if (!sectData) return nil;
if (!sectData) return self.infoDict;
struct section_64 *sect = (struct section_64 *)[sectData bytes];
if (strncmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
NSData *plistData = [self safeSubdataWithRange:NSMakeRange(sect->offset, sect->size)];
if (!plistData) return nil;
if (!plistData) return self.infoDict;
NSDictionary *plist;
plist = [NSPropertyListSerialization propertyListWithData:plistData
options:NSPropertyListImmutable
format:NULL
error:NULL];
if (plist) self.infoDict = plist;
break;
return self.infoDict;
}
offset += sz_section;
}
@@ -328,6 +328,10 @@
return nil;
}
- (NSUInteger)fileSize {
return self.fileData.length;
}
#pragma mark Internal Methods
- (void)parseMachHeaders {

View File

@@ -79,6 +79,11 @@ typedef struct {
pid_t ppid;
char path[MAXPATHLEN];
char newpath[MAXPATHLEN];
// For file events, this is the process name.
// For exec requests, this is the parent process name.
// While process names can technically be 4*MAXPATHLEN, that never
// actually happens, so only take MAXPATHLEN and throw away any excess.
char pname[MAXPATHLEN];
} santa_message_t;
#endif // SANTA__COMMON__KERNELCOMMON_H

View File

@@ -47,27 +47,27 @@ void SantaDecisionManager::free() {
if (cached_decisions_lock_) {
lck_rw_free(cached_decisions_lock_, sdm_lock_grp_);
cached_decisions_lock_ = NULL;
cached_decisions_lock_ = nullptr;
}
if (dataqueue_lock_) {
lck_mtx_free(dataqueue_lock_, sdm_lock_grp_);
dataqueue_lock_ = NULL;
dataqueue_lock_ = nullptr;
}
if (sdm_lock_attr_) {
lck_attr_free(sdm_lock_attr_);
sdm_lock_attr_ = NULL;
sdm_lock_attr_ = nullptr;
}
if (sdm_lock_grp_) {
lck_grp_free(sdm_lock_grp_);
sdm_lock_grp_ = NULL;
sdm_lock_grp_ = nullptr;
}
if (sdm_lock_grp_attr_) {
lck_grp_attr_free(sdm_lock_grp_attr_);
sdm_lock_grp_attr_ = NULL;
sdm_lock_grp_attr_ = nullptr;
}
super::free();
@@ -101,7 +101,7 @@ void SantaDecisionManager::DisconnectClient(bool itDied) {
message->action = ACTION_REQUEST_SHUTDOWN;
PostToQueue(message);
delete message;
dataqueue_->setNotificationPort(NULL);
dataqueue_->setNotificationPort(nullptr);
} else {
// If the client died, reset the data queue so when it reconnects
// it doesn't get swamped straight away.
@@ -145,10 +145,10 @@ kern_return_t SantaDecisionManager::StartListener() {
kern_return_t SantaDecisionManager::StopListener() {
kauth_unlisten_scope(vnode_listener_);
vnode_listener_ = NULL;
vnode_listener_ = nullptr;
kauth_unlisten_scope(fileop_listener_);
fileop_listener_ = NULL;
fileop_listener_ = nullptr;
// Wait for any active invocations to finish before returning
do {
@@ -197,7 +197,7 @@ void SantaDecisionManager::AddToCache(
void SantaDecisionManager::CacheCheck(const char *identifier) {
lck_rw_lock_shared(cached_decisions_lock_);
bool shouldInvalidate = (cached_decisions_->getObject(identifier) != NULL);
bool shouldInvalidate = (cached_decisions_->getObject(identifier) != nullptr);
if (shouldInvalidate) {
if (!lck_rw_lock_shared_to_exclusive(cached_decisions_lock_)) {
// shared_to_exclusive will return false if a previous reader upgraded
@@ -330,6 +330,7 @@ santa_action_t SantaDecisionManager::FetchDecision(
strlcpy(message->path, path, sizeof(message->path));
message->action = ACTION_REQUEST_CHECKBW;
message->vnode_id = vnode_id;
proc_name(message->ppid, message->pname, sizeof(message->pname));
santa_action_t ret = GetFromDaemon(message, vnode_id_str);
delete message;
return ret;
@@ -427,7 +428,7 @@ void SantaDecisionManager::FileOpCallback(
const kauth_action_t action, const vnode_t vp,
const char *path, const char *new_path) {
if (vp) {
vfs_context_t context = vfs_context_create(NULL);
vfs_context_t context = vfs_context_create(nullptr);
uint64_t vnode_id = GetVnodeIDForVnode(context, vp);
vfs_context_rele(context);
@@ -451,6 +452,7 @@ void SantaDecisionManager::FileOpCallback(
santa_message_t *message = NewMessage();
strlcpy(message->path, path, sizeof(message->path));
if (new_path) strlcpy(message->newpath, new_path, sizeof(message->newpath));
proc_name(message->pid, message->pname, sizeof(message->pname));
switch (action) {
case KAUTH_FILEOP_CLOSE:
@@ -481,9 +483,9 @@ extern "C" int fileop_scope_callback(
SantaDecisionManager *sdm = OSDynamicCast(
SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
vnode_t vp = NULL;
char *path = NULL;
char *new_path = NULL;
vnode_t vp = nullptr;
char *path = nullptr;
char *new_path = nullptr;
switch (action) {
case KAUTH_FILEOP_CLOSE:
@@ -517,7 +519,7 @@ extern "C" int vnode_scope_callback(
uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) {
if (action & KAUTH_VNODE_ACCESS ||
!(action & KAUTH_VNODE_EXECUTE) ||
idata == NULL) {
idata == nullptr) {
return KAUTH_RESULT_DEFER;
}

View File

@@ -222,7 +222,7 @@ class SantaDecisionManager : public OSObject {
/// @param action that was requested
/// @param VFS context
/// @param Vnode being operated on
/// @param Parent Vnode. May be NULL.
/// @param Parent Vnode. May be nullptr.
/// @param Pointer to an errno-style error.
///
extern "C" int vnode_scope_callback(

View File

@@ -27,7 +27,7 @@ bool SantaDriver::start(IOService *provider) {
if (!santaDecisionManager->init() ||
santaDecisionManager->StartListener() != kIOReturnSuccess) {
santaDecisionManager->release();
santaDecisionManager = NULL;
santaDecisionManager = nullptr;
return false;
}
@@ -41,7 +41,7 @@ bool SantaDriver::start(IOService *provider) {
void SantaDriver::stop(IOService *provider) {
santaDecisionManager->StopListener();
santaDecisionManager->release();
santaDecisionManager = NULL;
santaDecisionManager = nullptr;
LOGI("Unloaded.");

View File

@@ -49,8 +49,8 @@ bool SantaDriverClient::start(IOService *provider) {
void SantaDriverClient::stop(IOService *provider) {
super::stop(provider);
myProvider = NULL;
decisionManager = NULL;
myProvider = nullptr;
decisionManager = nullptr;
}
IOReturn SantaDriverClient::clientClose() {
@@ -124,7 +124,7 @@ IOReturn SantaDriverClient::static_allow_binary(
void *reference,
IOExternalMethodArguments *arguments) {
if (!target) return kIOReturnBadArgument;
if (arguments->scalarInput == NULL) return kIOReturnBadArgument;
if (arguments->scalarInput == nullptr) return kIOReturnBadArgument;
return target->allow_binary(
static_cast<const uint64_t>(*arguments->scalarInput));
@@ -143,7 +143,7 @@ IOReturn SantaDriverClient::static_deny_binary(
void *reference,
IOExternalMethodArguments *arguments) {
if (!target) return kIOReturnBadArgument;
if (arguments->scalarInput == NULL) return kIOReturnBadArgument;
if (arguments->scalarInput == nullptr) return kIOReturnBadArgument;
return target->deny_binary(
static_cast<const uint64_t>(*arguments->scalarInput));

View File

@@ -38,7 +38,8 @@ REGISTER_COMMAND_NAME(@"status")
}
+ (NSString *)longHelpText {
return nil;
return (@"Provides details about Santa while it's running.\n"
@" Use --json to output in JSON format");
}
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
@@ -69,7 +70,7 @@ REGISTER_COMMAND_NAME(@"status")
ramEvents = events;
dispatch_group_leave(group);
}];
char *fileLogging = ([[SNTConfigurator configurator] fileChangesRegex] ? "Enabled" : "Disabled");
BOOL fileLogging = ([[SNTConfigurator configurator] fileChangesRegex] != nil);
// Kext status
__block int64_t cacheCount = -1;
@@ -95,31 +96,63 @@ REGISTER_COMMAND_NAME(@"status")
// Sync status
NSString *syncURLStr = [[[SNTConfigurator configurator] syncBaseURL] absoluteString];
NSString *lastSyncSuccess = [[[SNTConfigurator configurator] syncLastSuccess] description];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = @"YYYY/MM/dd HH:mm:ss Z";
NSDate *lastSyncSuccess = [[SNTConfigurator configurator] syncLastSuccess];
NSString *lastSyncSuccessStr = [dateFormatter stringFromDate:lastSyncSuccess] ?: @"Never";
BOOL syncCleanReqd = [[SNTConfigurator configurator] syncCleanRequired];
// Wait a maximum of 5s for stats collected from daemon to arrive.
if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5))) {
printf("Failed to retrieve some stats from daemon\n\n");
fprintf(stderr, "Failed to retrieve some stats from daemon\n\n");
}
printf(">>> Daemon Info\n");
printf(" %-22s | %s\n", "Mode", [clientMode UTF8String]);
printf(" %-22s | %s\n", "File Logging", fileLogging);
printf(" %-22s | %lld\n", "Watchdog CPU Events", cpuEvents);
printf(" %-22s | %lld\n", "Watchdog RAM Events", ramEvents);
printf(">>> Kernel Info\n");
printf(" %-22s | %lld\n", "Kernel cache count", cacheCount);
printf(">>> Database Info\n");
printf(" %-22s | %lld\n", "Binary Rules", binaryRuleCount);
printf(" %-22s | %lld\n", "Certificate Rules", certRuleCount);
printf(" %-22s | %lld\n", "Events Pending Upload", eventCount);
if ([arguments containsObject:@"--json"]) {
NSDictionary *stats = @{
@"daemon": @{
@"mode": clientMode,
@"file_logging": @(fileLogging),
@"watchdog_cpu_events": @(cpuEvents),
@"watchdog_ram_events": @(ramEvents),
},
@"kernel": @{
@"cache_count": @(cacheCount),
},
@"database": @{
@"binary_rules": @(binaryRuleCount),
@"certificate_rules": @(certRuleCount),
@"events_pending_upload": @(eventCount),
},
@"sync": @{
@"server": syncURLStr,
@"clean_required": @(syncCleanReqd),
@"last_successful": lastSyncSuccessStr
},
};
NSData *statsData = [NSJSONSerialization dataWithJSONObject:stats
options:NSJSONWritingPrettyPrinted
error:nil];
NSString *statsStr = [[NSString alloc] initWithData:statsData encoding:NSUTF8StringEncoding];
printf("%s\n", [statsStr UTF8String]);
} else {
printf(">>> Daemon Info\n");
printf(" %-22s | %s\n", "Mode", [clientMode UTF8String]);
printf(" %-22s | %s\n", "File Logging", (fileLogging ? "Yes" : "No"));
printf(" %-22s | %lld\n", "Watchdog CPU Events", cpuEvents);
printf(" %-22s | %lld\n", "Watchdog RAM Events", ramEvents);
printf(">>> Kernel Info\n");
printf(" %-22s | %lld\n", "Kernel cache count", cacheCount);
printf(">>> Database Info\n");
printf(" %-22s | %lld\n", "Binary Rules", binaryRuleCount);
printf(" %-22s | %lld\n", "Certificate Rules", certRuleCount);
printf(" %-22s | %lld\n", "Events Pending Upload", eventCount);
if (syncURLStr) {
printf(">>> Sync Info\n");
printf(" %-22s | %s\n", "Sync Server", [syncURLStr UTF8String]);
printf(" %-22s | %s\n", "Clean Sync Required", (syncCleanReqd ? "Yes" : "No"));
const char *syncDateStr = (lastSyncSuccess ? [lastSyncSuccess UTF8String] : "Never");
printf(" %-22s | %s\n", "Last Successful Sync", syncDateStr);
if (syncURLStr) {
printf(">>> Sync Info\n");
printf(" %-22s | %s\n", "Sync Server", [syncURLStr UTF8String]);
printf(" %-22s | %s\n", "Clean Sync Required", (syncCleanReqd ? "Yes" : "No"));
printf(" %-22s | %s\n", "Last Successful Sync", [lastSyncSuccessStr UTF8String]);
}
}
exit(0);

View File

@@ -44,6 +44,10 @@
} else {
NSDictionary *r = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if (syncState.newClientMode) {
[[daemonConn remoteObjectProxy] setClientMode:syncState.newClientMode reply:^{}];
}
NSString *backoffInterval = r[kBackoffInterval];
if (backoffInterval) {
[[daemonConn remoteObjectProxy] setNextSyncInterval:[backoffInterval intValue] reply:^{}];

View File

@@ -80,9 +80,9 @@
syncState.uploadLogURL = [NSURL URLWithString:r[kUploadLogsURL]];
if ([r[kClientMode] isEqual:kClientModeMonitor]) {
[[daemonConn remoteObjectProxy] setClientMode:CLIENTMODE_MONITOR reply:^{}];
syncState.newClientMode = CLIENTMODE_MONITOR;
} else if ([r[kClientMode] isEqual:kClientModeLockdown]) {
[[daemonConn remoteObjectProxy] setClientMode:CLIENTMODE_LOCKDOWN reply:^{}];
syncState.newClientMode = CLIENTMODE_LOCKDOWN;
}
if ([r[kWhitelistRegex] isKindOfClass:[NSString class]]) {

View File

@@ -12,6 +12,8 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
/// An instance of this class is passed to each stage of the sync process for storing data
/// that might be needed in later stages.
@interface SNTCommandSyncState : NSObject
@@ -27,6 +29,9 @@
/// should be deleted before inserting any new rules.
@property BOOL cleanSync;
/// New client mode sent from server
@property santa_clientmode_t newClientMode;
/// Batch size for uploading events, sent from server
@property int32_t eventBatchSize;

View File

@@ -41,14 +41,30 @@ REGISTER_COMMAND_NAME(@"version")
}
+ (NSString *)longHelpText {
return nil;
return (@"Show versions of all Santa components.\n"
@" Use --json to output in JSON format.");
}
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
printf("%-15s | %s\n", "santa-driver", [[self santaKextVersion] UTF8String]);
printf("%-15s | %s\n", "santad", [[self santadVersion] UTF8String]);
printf("%-15s | %s\n", "santactl", [[self santactlVersion] UTF8String]);
printf("%-15s | %s\n", "SantaGUI", [[self santaAppVersion] UTF8String]);
if ([arguments containsObject:@"--json"]) {
NSDictionary *versions = @{
@"santa-driver": [self santaKextVersion],
@"santad": [self santadVersion],
@"santactl": [self santactlVersion],
@"SantaGUI": [self santaAppVersion],
};
NSData *versionsData = [NSJSONSerialization dataWithJSONObject:versions
options:NSJSONWritingPrettyPrinted
error:nil];
NSString *versionsStr = [[NSString alloc] initWithData:versionsData
encoding:NSUTF8StringEncoding];
printf("%s\n", [versionsStr UTF8String]);
} else {
printf("%-15s | %s\n", "santa-driver", [[self santaKextVersion] UTF8String]);
printf("%-15s | %s\n", "santad", [[self santadVersion] UTF8String]);
printf("%-15s | %s\n", "santactl", [[self santactlVersion] UTF8String]);
printf("%-15s | %s\n", "SantaGUI", [[self santaAppVersion] UTF8String]);
}
exit(0);
}
@@ -58,7 +74,7 @@ REGISTER_COMMAND_NAME(@"version")
(__bridge CFArrayRef)@[ @"CFBundleVersion" ])
);
if (loadedKexts[@(USERCLIENT_ID)] && loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"]) {
if (loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"]) {
return loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"];
}

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
// This is imported in the header rather than implementation to saves
// This is imported in the header rather than implementation to save
// classes that use this one from also having to import FMDB stuff.
#import <FMDB/FMDB.h>

View File

@@ -14,6 +14,7 @@
#import "SNTKernelCommon.h"
@class SNTCachedDecision;
///

View File

@@ -14,7 +14,7 @@
#import "SNTEventLog.h"
#include <sys/stat.h>
#include <libproc.h>
#include <sys/sysctl.h>
#import "SNTCachedDecision.h"
@@ -69,11 +69,8 @@
}
case ACTION_NOTIFY_WRITE: {
action = @"WRITE";
struct stat filestat;
stat(message.path, &filestat);
if (filestat.st_size < 1024 * 1024) {
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:path];
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:path];
if (fileInfo.fileSize < 1024 * 1024) {
sha256 = fileInfo.SHA256;
} else {
sha256 = @"(too large)";
@@ -87,8 +84,13 @@
if (newpath) {
outStr = [outStr stringByAppendingFormat:@"|newpath=%@", [self sanitizeString:newpath]];
}
outStr = [outStr stringByAppendingFormat:@"|pid=%d|ppid=%d|uid=%d|gid=%d",
message.pid, message.ppid, message.uid, message.gid];
char ppath[PATH_MAX];
if (proc_pidpath(message.pid, ppath, PATH_MAX) < 1) {
strncpy(ppath, "(null)", 6);
}
outStr =
[outStr stringByAppendingFormat:@"|pid=%d|ppid=%d|process=%s|processpath=%s|uid=%d|gid=%d",
message.pid, message.ppid, message.pname, ppath, message.uid, message.gid];
if (sha256) {
outStr = [outStr stringByAppendingFormat:@"|sha256=%@", sha256];
}

View File

@@ -137,10 +137,6 @@
santa_action_t action = [self actionForEventState:cd.decision];
if (action == ACTION_RESPOND_CHECKBW_ALLOW) [self.eventLog saveDecisionDetails:cd];
// Get name of parent process. Do this before responding to be sure parent doesn't go away.
char pname[PROC_PIDPATHINFO_MAXSIZE];
proc_name(message.ppid, pname, PROC_PIDPATHINFO_MAXSIZE);
// Send the decision to the kernel.
[self.driverManager postToKernelAction:action forVnodeID:cd.vnodeId];
@@ -158,7 +154,7 @@
se.signingChain = csInfo.certificates;
se.pid = @(message.pid);
se.ppid = @(message.ppid);
se.parentName = @(pname);
se.parentName = @(message.pname);
se.fileBundleID = [binInfo bundleIdentifier];
se.fileBundleName = [binInfo bundleName];

View File

@@ -20,9 +20,19 @@
#import "SNTLogging.h"
#import "SNTRule.h"
@interface SNTRuleTable ()
@property NSString *santadCertSHA;
@property NSString *launchdCertSHA;
@end
@implementation SNTRuleTable
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version {
// Save hashes of the signing certs for launchd and santad
self.santadCertSHA = [[[[SNTCodesignChecker alloc] initWithSelf] leafCertificate] SHA256];
self.launchdCertSHA = [[[[SNTCodesignChecker alloc] initWithPID:1] leafCertificate] SHA256];
uint32_t newVersion = 0;
if (version < 1) {
@@ -41,12 +51,10 @@
// Insert the codesigning certs for the running santad and launchd into the initial database.
// This helps prevent accidentally denying critical system components while the database
// is empty. This 'initial database' will then be cleared on the first successful sync.
NSString *santadSHA = [[[[SNTCodesignChecker alloc] initWithSelf] leafCertificate] SHA256];
NSString *launchdSHA = [[[[SNTCodesignChecker alloc] initWithPID:1] leafCertificate] SHA256];
[db executeUpdate:@"INSERT INTO rules (shasum, state, type) VALUES (?, ?, ?)",
santadSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
self.santadCertSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
[db executeUpdate:@"INSERT INTO rules (shasum, state, type) VALUES (?, ?, ?)",
launchdSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
self.launchdCertSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
newVersion = 1;
@@ -124,14 +132,28 @@
#pragma mark Adding
- (BOOL)addRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate {
__block BOOL failed = NO;
if (!rules || rules.count < 1) {
LOGE(@"Received request to add rules with nil/empty array.");
return NO;
}
__block BOOL failed = NO;
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Protect rules for santad/launchd certificates.
NSPredicate *p = [NSPredicate predicateWithFormat:
@"(SELF.shasum = %@ OR SELF.shasum = %@) AND SELF.type = %d",
self.santadCertSHA, self.launchdCertSHA, RULETYPE_CERT];
NSArray *requiredHashes = [rules filteredArrayUsingPredicate:p];
p = [NSPredicate predicateWithFormat:@"SELF.state == %d", RULESTATE_WHITELIST];
NSArray *requiredHashesWhitelist = [requiredHashes filteredArrayUsingPredicate:p];
if ((cleanSlate && requiredHashesWhitelist.count != 2) ||
(requiredHashes.count != requiredHashesWhitelist.count)) {
LOGE(@"Received request to remove whitelist for launchd/santad ceritifcates.");
*rollback = failed = YES;
return;
}
if (cleanSlate) {
[db executeUpdate:@"DELETE FROM rules"];
}

View File

@@ -17,6 +17,11 @@
#import "SNTRule.h"
#import "SNTRuleTable.h"
@interface SNTRuleTable (Testing)
@property NSString *santadCertSHA;
@property NSString *launchdCertSHA;
@end
/// This test case actually tests SNTRuleTable and SNTRule
@interface SNTRuleTableTest : XCTestCase
@property SNTRuleTable *sut;
@@ -60,22 +65,36 @@
}
- (void)testAddRulesClean {
// If SNTRuleTable doesn't start with some rules, this test doesn't work properly.
XCTAssert(self.sut.ruleCount);
// Assert that insert without 'self' and launchd cert hashes fails
XCTAssertFalse([self.sut addRules:@[ [self _exampleBinaryRule] ] cleanSlate:YES]);
[self.sut addRules:@[ [self _exampleBinaryRule] ] cleanSlate:YES];
// Now add a binary rule without clean slate
XCTAssertTrue([self.sut addRules:@[ [self _exampleBinaryRule] ] cleanSlate:NO]);
XCTAssertEqual(self.sut.ruleCount, 1);
XCTAssertEqual(self.sut.binaryRuleCount, 1);
// Now add a cert rule + the required rules as a clean slate,
// assert that the binary rule was removed
SNTRule *r1 = [[SNTRule alloc] init];
r1.shasum = self.sut.launchdCertSHA;
r1.state = RULESTATE_WHITELIST;
r1.type = RULETYPE_CERT;
SNTRule *r2 = [[SNTRule alloc] init];
r2.shasum = self.sut.santadCertSHA;
r2.state = RULESTATE_WHITELIST;
r2.type = RULETYPE_CERT;
XCTAssertTrue(([self.sut addRules:@[ [self _exampleCertRule], r1, r2 ] cleanSlate:YES]));
XCTAssertEqual([self.sut binaryRuleCount], 0);
}
- (void)testAddMultipleRules {
NSUInteger ruleCount = self.sut.ruleCount;
[self.sut addRules:@[ [self _exampleBinaryRule],
[self _exampleCertRule],
[self _exampleBinaryRule] ]
cleanSlate:YES];
cleanSlate:NO];
XCTAssertEqual(self.sut.ruleCount, 2);
XCTAssertEqual(self.sut.ruleCount, ruleCount + 2);
}
- (void)testAddRulesEmptyArray {
@@ -87,7 +106,7 @@
}
- (void)testFetchBinaryRule {
[self.sut addRules:@[ [self _exampleBinaryRule], [self _exampleCertRule] ] cleanSlate:YES];
[self.sut addRules:@[ [self _exampleBinaryRule], [self _exampleCertRule] ] cleanSlate:NO];
SNTRule *r = [self.sut binaryRuleForSHA256:@"a"];
XCTAssertNotNil(r);
@@ -99,7 +118,7 @@
}
- (void)testFetchCertificateRule {
[self.sut addRules:@[ [self _exampleBinaryRule], [self _exampleCertRule] ] cleanSlate:YES];
[self.sut addRules:@[ [self _exampleBinaryRule], [self _exampleCertRule] ] cleanSlate:NO];
SNTRule *r = [self.sut certificateRuleForSHA256:@"b"];
XCTAssertNotNil(r);