mirror of
https://github.com/google/santa.git
synced 2026-01-15 01:08:12 -05:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56d4a6b9fb | ||
|
|
28a94cd56a | ||
|
|
4344fc3d7d | ||
|
|
40431d835e | ||
|
|
942804c478 | ||
|
|
d109aae6ef | ||
|
|
b89040c37a | ||
|
|
63aefb4654 | ||
|
|
1c92e968e3 | ||
|
|
c1b7f9ae63 | ||
|
|
0507bc83d2 | ||
|
|
7028c24425 | ||
|
|
6ede057521 | ||
|
|
6f2ccca60a | ||
|
|
a59d2aa8a9 | ||
|
|
d88fa4ecfe | ||
|
|
4df93bfe6c | ||
|
|
08ca3c9d95 |
@@ -51,16 +51,9 @@ Known Issues
|
||||
|
||||
Santa is not yet a 1.0 and we have some known issues to be aware of:
|
||||
|
||||
* Potential race-condition: we currently have a single TODO in the kext code to
|
||||
investigate a potential race condition where a binary is executed and then very
|
||||
quickly modified between the kext getting the SHA-1 and the decision being made.
|
||||
|
||||
* Kext communication security: the kext will only accept a connection from a
|
||||
single client at a time and said client must be running as root. We haven't yet
|
||||
found a good way to ensure the kext only accepts connections from a valid client,
|
||||
short of hardcoding the SHA-1 in the kext. This shouldn't present a huge problem
|
||||
as the daemon is loaded on boot-up by launchd, so any later attempts to connect
|
||||
will be blocked.
|
||||
found a good way to ensure the kext only accepts connections from a valid client.
|
||||
|
||||
* Database protection: the SQLite database is installed with permissions so that
|
||||
only the root user can read/write it. We're considering approaches to secure
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
0D1B477119A53419008CADD3 /* AboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0D1B476F19A53419008CADD3 /* AboutWindow.xib */; };
|
||||
0D20710E1A7C4A86008B0A9A /* SNTStoredEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD604A19105433006B445C /* SNTStoredEvent.m */; };
|
||||
0D28D53819D9F5910015C5EB /* SNTConfigurator.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D42D2B719D2042900955F08 /* SNTConfigurator.m */; };
|
||||
0D2CD4611A81C7B100C9C910 /* dn.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0D2CD4601A81C7B100C9C910 /* dn.plist */; };
|
||||
0D31DF4718D254B3002B300D /* SNTCodesignChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D59C0E317710E6000748EBF /* SNTCodesignChecker.m */; };
|
||||
0D35BD9F18FD71CE00921A21 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D9A7F3E1759330500035EB5 /* Foundation.framework */; };
|
||||
0D35BDA218FD71CE00921A21 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D35BDA118FD71CE00921A21 /* main.m */; };
|
||||
@@ -213,6 +214,7 @@
|
||||
0D28E5E119269B3600280F87 /* SNTLogging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTLogging.h; sourceTree = "<group>"; };
|
||||
0D28E5E31926AFE400280F87 /* SNTKernelCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTKernelCommon.h; sourceTree = "<group>"; };
|
||||
0D28E5E41926B55600280F87 /* santactl-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "santactl-Info.plist"; sourceTree = "<group>"; };
|
||||
0D2CD4601A81C7B100C9C910 /* dn.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = dn.plist; sourceTree = "<group>"; };
|
||||
0D35BD9E18FD71CE00921A21 /* santactl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = santactl; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0D35BDA118FD71CE00921A21 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
0D35BDA418FD71CE00921A21 /* santactl-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "santactl-Prefix.pch"; sourceTree = "<group>"; };
|
||||
@@ -403,6 +405,7 @@
|
||||
0D260DB018B68E12002A0B55 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D2CD4601A81C7B100C9C910 /* dn.plist */,
|
||||
0D6FDC8618C6913D0044685C /* apple.pem */,
|
||||
0D6FDC8218C68D7E0044685C /* GIAG2.crt */,
|
||||
0D6FDC8418C68E500044685C /* GIAG2.pem */,
|
||||
@@ -568,12 +571,12 @@
|
||||
0D91BCB9174E8A7E00131A7D /* santa-driver */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D4644C4182AF81700098690 /* SantaDecisionManager.h */,
|
||||
0D4644C3182AF81700098690 /* SantaDecisionManager.cc */,
|
||||
0D9A7F321759144800035EB5 /* SantaDriver.h */,
|
||||
0D9A7F311759144800035EB5 /* SantaDriver.cc */,
|
||||
0D9A7F361759148E00035EB5 /* SantaDriverClient.h */,
|
||||
0D9A7F351759148E00035EB5 /* SantaDriverClient.cc */,
|
||||
0D4644C4182AF81700098690 /* SantaDecisionManager.h */,
|
||||
0D4644C3182AF81700098690 /* SantaDecisionManager.cc */,
|
||||
0D7A7AF2174FCF4C00B77646 /* SantaMessage.h */,
|
||||
0D7A7AF1174FCF4C00B77646 /* SantaMessage.cc */,
|
||||
0DA36C1F199EA46600A129D6 /* Resources */,
|
||||
@@ -863,6 +866,7 @@
|
||||
0D6FDC8518C68E500044685C /* GIAG2.pem in Resources */,
|
||||
0D6FDC8318C68D7E0044685C /* GIAG2.crt in Resources */,
|
||||
0D6F12DA19EDE51E006B218E /* tubitak.crt in Resources */,
|
||||
0D2CD4611A81C7B100C9C910 /* dn.plist in Resources */,
|
||||
0D6FDC8718C6913D0044685C /* apple.pem in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -894,7 +898,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "GIT_TAG=$(git describe --abbrev=0 --tags)\nGIT_COMMIT=$(git rev-parse --short HEAD)\n\ncp ${SCRIPT_INPUT_FILE_0} ${SCRIPT_OUTPUT_FILE_0}\n\nif [ $CONFIGURATION = \"Debug\" ]; then\n newVersion=\"${GIT_TAG}d${GIT_COMMIT}\"\nelse\n newVersion=\"${GIT_TAG}\"\nfi\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion ${GIT_TAG}\" ${SCRIPT_OUTPUT_FILE_0}\n/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString ${newVersion}\" ${SCRIPT_OUTPUT_FILE_0}";
|
||||
shellScript = "GIT_TAG=$(git describe --abbrev=0 --tags)\nGIT_COMMIT=$(git rev-parse --short HEAD)\n\ncp ${SCRIPT_INPUT_FILE_0} ${SCRIPT_OUTPUT_FILE_0}\n\nif [ $CONFIGURATION = \"Debug\" ]; then\n newVersion=\"${GIT_TAG}d ${GIT_COMMIT}\"\nelse\n newVersion=\"${GIT_TAG}\"\nfi\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion ${GIT_TAG}\" ${SCRIPT_OUTPUT_FILE_0}\n/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString ${newVersion}\" ${SCRIPT_OUTPUT_FILE_0}";
|
||||
};
|
||||
0D673DAD18FC9017009C5B06 /* Delete existing coverage files */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
@@ -925,7 +929,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "GIT_TAG=$(git describe --abbrev=0 --tags)\nGIT_COMMIT=$(git rev-parse --short HEAD)\n\ncp ${SCRIPT_INPUT_FILE_0} ${SCRIPT_OUTPUT_FILE_0}\n\nif [ $CONFIGURATION = \"Debug\" ]; then\nnewVersion=\"${GIT_TAG}d${GIT_COMMIT}\"\nelse\nnewVersion=\"${GIT_TAG}\"\nfi\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion ${GIT_TAG}\" ${SCRIPT_OUTPUT_FILE_0}\n/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString ${newVersion}\" ${SCRIPT_OUTPUT_FILE_0}";
|
||||
shellScript = "GIT_TAG=$(git describe --abbrev=0 --tags)\nGIT_COMMIT=$(git rev-parse --short HEAD)\n\ncp ${SCRIPT_INPUT_FILE_0} ${SCRIPT_OUTPUT_FILE_0}\n\nif [ $CONFIGURATION = \"Debug\" ]; then\nnewVersion=\"${GIT_TAG}d ${GIT_COMMIT}\"\nelse\nnewVersion=\"${GIT_TAG}\"\nfi\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion ${GIT_TAG}\" ${SCRIPT_OUTPUT_FILE_0}\n/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString ${newVersion}\" ${SCRIPT_OUTPUT_FILE_0}";
|
||||
};
|
||||
0DD98E671A5DD02000A754C6 /* Update Version Info */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
@@ -941,7 +945,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "GIT_TAG=$(git describe --abbrev=0 --tags)\nGIT_COMMIT=$(git rev-parse --short HEAD)\n\ncp ${SCRIPT_INPUT_FILE_0} ${SCRIPT_OUTPUT_FILE_0}\n\nif [ $CONFIGURATION = \"Debug\" ]; then\nnewVersion=\"${GIT_TAG}d${GIT_COMMIT}\"\nelse\nnewVersion=\"${GIT_TAG}\"\nfi\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion ${GIT_TAG}\" ${SCRIPT_OUTPUT_FILE_0}\n/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString ${newVersion}\" ${SCRIPT_OUTPUT_FILE_0}";
|
||||
shellScript = "GIT_TAG=$(git describe --abbrev=0 --tags)\nGIT_COMMIT=$(git rev-parse --short HEAD)\n\ncp ${SCRIPT_INPUT_FILE_0} ${SCRIPT_OUTPUT_FILE_0}\n\nif [ $CONFIGURATION = \"Debug\" ]; then\nnewVersion=\"${GIT_TAG}d ${GIT_COMMIT}\"\nelse\nnewVersion=\"${GIT_TAG}\"\nfi\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion ${GIT_TAG}\" ${SCRIPT_OUTPUT_FILE_0}\n/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString ${newVersion}\" ${SCRIPT_OUTPUT_FILE_0}";
|
||||
};
|
||||
0DD98E681A5DD03E00A754C6 /* Update Version Info */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
@@ -957,7 +961,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "GIT_TAG=$(git describe --abbrev=0 --tags)\nGIT_COMMIT=$(git rev-parse --short HEAD)\n\ncp ${SCRIPT_INPUT_FILE_0} ${SCRIPT_OUTPUT_FILE_0}\n\nif [ $CONFIGURATION = \"Debug\" ]; then\nnewVersion=\"${GIT_TAG}d${GIT_COMMIT}\"\nelse\nnewVersion=\"${GIT_TAG}\"\nfi\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion ${GIT_TAG}\" ${SCRIPT_OUTPUT_FILE_0}\n/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString ${newVersion}\" ${SCRIPT_OUTPUT_FILE_0}";
|
||||
shellScript = "GIT_TAG=$(git describe --abbrev=0 --tags)\nGIT_COMMIT=$(git rev-parse --short HEAD)\n\ncp ${SCRIPT_INPUT_FILE_0} ${SCRIPT_OUTPUT_FILE_0}\n\nif [ $CONFIGURATION = \"Debug\" ]; then\nnewVersion=\"${GIT_TAG}d ${GIT_COMMIT}\"\nelse\nnewVersion=\"${GIT_TAG}\"\nfi\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion ${GIT_TAG}\" ${SCRIPT_OUTPUT_FILE_0}\n/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString ${newVersion}\" ${SCRIPT_OUTPUT_FILE_0}";
|
||||
};
|
||||
0DD98E691A5DD5C900A754C6 /* Update Module Version */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
|
||||
@@ -18,9 +18,7 @@
|
||||
#import <Security/Security.h>
|
||||
|
||||
@interface SNTCertificate ()
|
||||
///
|
||||
/// A container for cached property values
|
||||
///
|
||||
@property NSMutableDictionary *memoizedData;
|
||||
@end
|
||||
|
||||
@@ -46,7 +44,8 @@ static NSString *const kCertDataKey = @"certData";
|
||||
// Despite the header file claiming that SecCertificateCreateWithData will return NULL if
|
||||
// @c certData doesn't contain a valid DER-encoded X509 cert, this isn't always true.
|
||||
// radar://problem/16124651
|
||||
// To workaround, check that the certificate serial number can be retrieved.
|
||||
// To workaround, check that the certificate serial number can be retrieved. According to
|
||||
// RFC5280, the serial number field is required.
|
||||
NSData *ser = CFBridgingRelease(SecCertificateCopySerialNumber(cert, NULL));
|
||||
if (ser) {
|
||||
self = [self initWithSecCertificateRef:cert];
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
// List of methods supported by the driver.
|
||||
enum SantaDriverMethods {
|
||||
kSantaUserClientOpen,
|
||||
kSantaUserClientClose,
|
||||
kSantaUserClientAllowBinary,
|
||||
kSantaUserClientDenyBinary,
|
||||
kSantaUserClientClearCache,
|
||||
|
||||
@@ -90,4 +90,9 @@
|
||||
///
|
||||
@property NSNumber *pid;
|
||||
|
||||
///
|
||||
/// The parent process ID of the binary being executed.
|
||||
///
|
||||
@property NSNumber *ppid;
|
||||
|
||||
@end
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
ENCODE(self.executingUser, @"executingUser");
|
||||
ENCODE(self.occurrenceDate, @"occurrenceDate");
|
||||
ENCODE(@(self.decision), @"decision");
|
||||
ENCODE(self.pid, @"pid");
|
||||
ENCODE(self.ppid, @"ppid");
|
||||
|
||||
ENCODE(self.loggedInUsers, @"loggedInUsers");
|
||||
ENCODE(self.currentSessions, @"currentSessions");
|
||||
@@ -63,6 +65,8 @@
|
||||
_executingUser = DECODE(NSString, @"executingUser");
|
||||
_occurrenceDate = DECODE(NSDate, @"occurrenceDate");
|
||||
_decision = [DECODE(NSNumber, @"decision") intValue];
|
||||
_pid = DECODE(NSNumber, @"pid");
|
||||
_ppid = DECODE(NSNumber, @"ppid");
|
||||
|
||||
_loggedInUsers = DECODEARRAY(NSString, @"loggedInUsers");
|
||||
_currentSessions = DECODEARRAY(NSString, @"currentSessions");
|
||||
|
||||
@@ -19,40 +19,15 @@ OSDefineMetaClassAndStructors(SantaDecisionManager, OSObject);
|
||||
|
||||
#pragma mark Object Lifecycle
|
||||
|
||||
SantaDecisionManager *SantaDecisionManager::WithQueueAndPID(
|
||||
IOSharedDataQueue *queue, pid_t pid) {
|
||||
SantaDecisionManager *me = new SantaDecisionManager;
|
||||
bool SantaDecisionManager::init() {
|
||||
dataqueue_lock_ = IORWLockAlloc();
|
||||
cached_decisions_lock_ = IORWLockAlloc();
|
||||
cached_decisions_ = OSDictionary::withCapacity(1000);
|
||||
|
||||
if (me && !me->InitWithQueueAndPID(queue, pid)) {
|
||||
me->free();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return me;
|
||||
}
|
||||
|
||||
bool SantaDecisionManager::InitWithQueueAndPID(
|
||||
IOSharedDataQueue *queue, pid_t pid) {
|
||||
if (!super::init()) return false;
|
||||
|
||||
if (!pid) return false;
|
||||
if (!queue) return false;
|
||||
|
||||
listener_invocations_ = 0;
|
||||
dataqueue_ = queue;
|
||||
owning_pid_ = pid;
|
||||
owning_proc_ = proc_find(pid);
|
||||
|
||||
if (!(dataqueue_lock_ = IORWLockAlloc())) return FALSE;
|
||||
if (!(cached_decisions_lock_ = IORWLockAlloc())) return FALSE;
|
||||
if (!(cached_decisions_ = OSDictionary::withCapacity(1000))) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
void SantaDecisionManager::free() {
|
||||
proc_rele(owning_proc_);
|
||||
|
||||
if (cached_decisions_) {
|
||||
cached_decisions_->release();
|
||||
cached_decisions_ = NULL;
|
||||
@@ -63,7 +38,7 @@ void SantaDecisionManager::free() {
|
||||
cached_decisions_lock_ = NULL;
|
||||
}
|
||||
|
||||
if (dataqueue_lock_) {
|
||||
if (dataqueue_lock_ ) {
|
||||
IORWLockFree(dataqueue_lock_);
|
||||
dataqueue_lock_ = NULL;
|
||||
}
|
||||
@@ -71,7 +46,77 @@ void SantaDecisionManager::free() {
|
||||
super::free();
|
||||
}
|
||||
|
||||
# pragma mark Cache Management
|
||||
#pragma mark Client Management
|
||||
|
||||
void SantaDecisionManager::ConnectClient(IOSharedDataQueue *queue, pid_t pid) {
|
||||
if (!pid) return;
|
||||
if (!queue) return;
|
||||
|
||||
// Any decisions made while the daemon wasn't
|
||||
// connected should be cleared
|
||||
cached_decisions_->flushCollection();
|
||||
|
||||
dataqueue_ = queue;
|
||||
dataqueue_->retain();
|
||||
|
||||
owning_pid_ = pid;
|
||||
owning_proc_ = proc_find(pid);
|
||||
}
|
||||
|
||||
void SantaDecisionManager::DisconnectClient() {
|
||||
owning_pid_ = -1;
|
||||
|
||||
// Ask santad to shutdown, in case it's running.
|
||||
santa_message_t message;
|
||||
message.action = ACTION_REQUEST_SHUTDOWN;
|
||||
message.userId = 0;
|
||||
message.pid = 0;
|
||||
message.ppid = 0;
|
||||
message.vnode_id = 0;
|
||||
PostToQueue(message);
|
||||
|
||||
dataqueue_->release();
|
||||
dataqueue_ = NULL;
|
||||
|
||||
proc_rele(owning_proc_);
|
||||
owning_proc_ = NULL;
|
||||
}
|
||||
|
||||
bool SantaDecisionManager::ClientConnected() {
|
||||
return owning_pid_ > 0;
|
||||
}
|
||||
|
||||
# pragma mark Listener Control
|
||||
|
||||
kern_return_t SantaDecisionManager::StartListener() {
|
||||
vnode_listener_ = kauth_listen_scope(KAUTH_SCOPE_VNODE,
|
||||
vnode_scope_callback,
|
||||
reinterpret_cast<void *>(this));
|
||||
if (!vnode_listener_) return kIOReturnInternalError;
|
||||
|
||||
LOGD("Vnode listener started.");
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
kern_return_t SantaDecisionManager::StopListener() {
|
||||
kauth_unlisten_scope(vnode_listener_);
|
||||
vnode_listener_ = NULL;
|
||||
|
||||
// Wait for any active invocations to finish before returning
|
||||
do {
|
||||
IOSleep(5);
|
||||
} while (listener_invocations_);
|
||||
|
||||
// Delete any cached decisions
|
||||
ClearCache();
|
||||
|
||||
LOGD("Vnode listener stopped.");
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
#pragma mark Cache Management
|
||||
|
||||
void SantaDecisionManager::AddToCache(
|
||||
const char *identifier, santa_action_t decision, uint64_t microsecs) {
|
||||
@@ -169,7 +214,10 @@ santa_action_t SantaDecisionManager::GetFromCache(const char *identifier) {
|
||||
|
||||
bool SantaDecisionManager::PostToQueue(santa_message_t message) {
|
||||
IORWLockWrite(dataqueue_lock_);
|
||||
bool kr = dataqueue_->enqueue(&message, sizeof(message));
|
||||
bool kr = false;
|
||||
if (dataqueue_) {
|
||||
kr = dataqueue_->enqueue(&message, sizeof(message));
|
||||
}
|
||||
IORWLockUnlock(dataqueue_lock_);
|
||||
return kr;
|
||||
}
|
||||
@@ -200,6 +248,14 @@ santa_action_t SantaDecisionManager::FetchDecision(
|
||||
path[0] = '\0';
|
||||
}
|
||||
|
||||
if (!ClientConnected()) {
|
||||
LOGI("Execution request without daemon running: %s", path);
|
||||
AddToCache(vnode_id_str,
|
||||
ACTION_RESPOND_CHECKBW_ALLOW,
|
||||
GetCurrentUptime());
|
||||
return ACTION_RESPOND_CHECKBW_ALLOW;
|
||||
}
|
||||
|
||||
// Prepare to send message to daemon
|
||||
santa_message_t message;
|
||||
strncpy(message.path, path, MAX_PATH_LEN);
|
||||
@@ -260,10 +316,6 @@ uint64_t SantaDecisionManager::GetCurrentUptime() {
|
||||
|
||||
# pragma mark Invocation Tracking & PID comparison
|
||||
|
||||
SInt32 SantaDecisionManager::GetListenerInvocations() {
|
||||
return listener_invocations_;
|
||||
}
|
||||
|
||||
void SantaDecisionManager::IncrementListenerInvocations() {
|
||||
OSIncrementAtomic(&listener_invocations_);
|
||||
}
|
||||
@@ -272,78 +324,9 @@ void SantaDecisionManager::DecrementListenerInvocations() {
|
||||
OSDecrementAtomic(&listener_invocations_);
|
||||
}
|
||||
|
||||
bool SantaDecisionManager::MatchesOwningPID(const pid_t other_pid) {
|
||||
return (owning_pid_ == other_pid);
|
||||
}
|
||||
|
||||
# pragma mark Listener Control
|
||||
|
||||
kern_return_t SantaDecisionManager::StartListener() {
|
||||
process_listener_ = kauth_listen_scope(KAUTH_SCOPE_PROCESS,
|
||||
process_scope_callback,
|
||||
reinterpret_cast<void *>(this));
|
||||
if (!process_listener_) return kIOReturnInternalError;
|
||||
LOGD("Process listener started.");
|
||||
|
||||
vnode_listener_ = kauth_listen_scope(KAUTH_SCOPE_VNODE,
|
||||
vnode_scope_callback,
|
||||
reinterpret_cast<void *>(this));
|
||||
if (!vnode_listener_) return kIOReturnInternalError;
|
||||
|
||||
LOGD("Vnode listener started.");
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
kern_return_t SantaDecisionManager::StopListener() {
|
||||
kauth_unlisten_scope(vnode_listener_);
|
||||
vnode_listener_ = NULL;
|
||||
|
||||
kauth_unlisten_scope(process_listener_);
|
||||
process_listener_ = NULL;
|
||||
|
||||
// Wait for any active invocations to finish before returning
|
||||
do {
|
||||
IOSleep(5);
|
||||
} while (GetListenerInvocations());
|
||||
|
||||
// Delete any cached decisions
|
||||
ClearCache();
|
||||
|
||||
LOGD("Vnode listener stopped.");
|
||||
LOGD("Process listener stopped.");
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
#undef super
|
||||
|
||||
#pragma mark Kauth Callbacks
|
||||
|
||||
extern "C" int process_scope_callback(
|
||||
kauth_cred_t credential, void *idata, kauth_action_t action,
|
||||
uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) {
|
||||
if (idata == NULL) {
|
||||
LOGE("Process callback established without valid decision manager.");
|
||||
return KAUTH_RESULT_ALLOW;
|
||||
}
|
||||
SantaDecisionManager *sdm = OSDynamicCast(
|
||||
SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
|
||||
|
||||
// NOTE: this prevents a debugger from attaching to an existing santad
|
||||
// process but doesn't prevent starting santad under a debugger. This check
|
||||
// is only here to try and prevent the user from deadlocking their machine
|
||||
// by attaching a debugger, so if they work around it and end up deadlocking,
|
||||
// that's their problem.
|
||||
if (action == KAUTH_PROCESS_CANTRACE &&
|
||||
sdm->MatchesOwningPID(proc_pid((proc_t)arg0))) {
|
||||
*(reinterpret_cast<int *>(arg1)) = EPERM;
|
||||
LOGD("Denied debugger access");
|
||||
return KAUTH_RESULT_DENY;
|
||||
}
|
||||
|
||||
return KAUTH_RESULT_ALLOW;
|
||||
}
|
||||
#pragma mark Kauth Callback
|
||||
|
||||
extern "C" int vnode_scope_callback(
|
||||
kauth_cred_t credential, void *idata, kauth_action_t action,
|
||||
@@ -368,7 +351,7 @@ extern "C" int vnode_scope_callback(
|
||||
// Don't operate on ACCESS events, as they're advisory
|
||||
if (action & KAUTH_VNODE_ACCESS) return returnResult;
|
||||
|
||||
// Filter for only WRITE_DATA actions
|
||||
// Filter for only writes
|
||||
if (action & KAUTH_VNODE_WRITE_DATA ||
|
||||
action & KAUTH_VNODE_APPEND_DATA ||
|
||||
action & KAUTH_VNODE_DELETE) {
|
||||
@@ -408,17 +391,7 @@ extern "C" int vnode_scope_callback(
|
||||
default:
|
||||
// NOTE: Any unknown response or error condition causes us to fail open.
|
||||
// Whilst from a security perspective this is bad, it's important that
|
||||
// we don't break user's machines. Every fallen open response will come
|
||||
// through this code path and cause this log entry to be created, so we
|
||||
// can investigate each case and try to fix the root cause.
|
||||
char path[MAX_PATH_LEN];
|
||||
int name_len = MAX_PATH_LEN;
|
||||
if (vn_getpath(vnode, path, &name_len) != 0) {
|
||||
path[0] = '\0';
|
||||
}
|
||||
LOGW("Didn't receive a valid response for %s. Received: %d.",
|
||||
path,
|
||||
returnedAction);
|
||||
// we don't break user's machines.
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,47 +66,73 @@ class SantaDecisionManager : public OSObject {
|
||||
OSDeclareDefaultStructors(SantaDecisionManager);
|
||||
|
||||
public:
|
||||
// Convenience constructor
|
||||
// Queue remains owned by caller but must exist for lifetime of
|
||||
// SantaDecisionManager instance.
|
||||
static SantaDecisionManager *WithQueueAndPID(
|
||||
IOSharedDataQueue *queue, pid_t pid);
|
||||
/// Used for initialization after instantiation. Required because
|
||||
/// constructors cannot throw inside kernel-space.
|
||||
bool init();
|
||||
|
||||
bool InitWithQueueAndPID(IOSharedDataQueue *queue, pid_t pid);
|
||||
/// Called automatically when retain count drops to 0.
|
||||
void free();
|
||||
|
||||
// Decision Fetching / Daemon Communication
|
||||
/// Called by SantaDriverClient when a client connects, providing the data
|
||||
/// queue used to pass messages and the pid of the client process.
|
||||
void ConnectClient(IOSharedDataQueue *queue, pid_t pid);
|
||||
|
||||
/// Called by SantaDriverClient when a client disconnects
|
||||
void DisconnectClient();
|
||||
|
||||
/// Returns whether a client is currently connected or not.
|
||||
bool ClientConnected();
|
||||
|
||||
/// Starts the kauth listener.
|
||||
kern_return_t StartListener();
|
||||
|
||||
/// Stops the kauth listener. After stopping new callback requests,
|
||||
/// waits until all current invocations have finished before clearing the
|
||||
/// cache and returning.
|
||||
kern_return_t StopListener();
|
||||
|
||||
/// Adds a decision to the cache, with a timestamp.
|
||||
void AddToCache(const char *identifier,
|
||||
const santa_action_t decision,
|
||||
const uint64_t microsecs);
|
||||
|
||||
/// Checks to see if a given identifier is in the cache and removes it.
|
||||
void CacheCheck(const char *identifier);
|
||||
|
||||
/// Returns the number of entries in the cache.
|
||||
uint64_t CacheCount();
|
||||
|
||||
/// Clears the cache.
|
||||
void ClearCache();
|
||||
|
||||
/// Fetches a response from the cache, first checking to see if the
|
||||
/// entry has expired.
|
||||
santa_action_t GetFromCache(const char *identifier);
|
||||
|
||||
/// Posts the requested message to the client data queue, if there is one.
|
||||
/// Uses dataqueue_lock_ to ensure two threads don't try to write to the
|
||||
/// queue at the same time.
|
||||
bool PostToQueue(santa_message_t);
|
||||
|
||||
/// Fetches an execution decision for a file, first using the cache and then
|
||||
/// by sending a message to the daemon and waiting until a response arrives.
|
||||
/// If a daemon isn't connected, will allow execution and cache, logging
|
||||
/// the path to the executed file.
|
||||
santa_action_t FetchDecision(const kauth_cred_t credential,
|
||||
const vfs_context_t vfs_context,
|
||||
const vnode_t vnode);
|
||||
|
||||
// Vnode ID string
|
||||
/// Fetches the vnode_id for a given vnode.
|
||||
uint64_t GetVnodeIDForVnode(const vfs_context_t context, const vnode_t vp);
|
||||
|
||||
// Cache management
|
||||
void AddToCache(const char *identifier,
|
||||
const santa_action_t decision,
|
||||
const uint64_t microsecs);
|
||||
void CacheCheck(const char *identifier);
|
||||
uint64_t CacheCount();
|
||||
void ClearCache();
|
||||
santa_action_t GetFromCache(const char *identifier);
|
||||
|
||||
// Listener invocation management
|
||||
SInt32 GetListenerInvocations();
|
||||
void IncrementListenerInvocations();
|
||||
void DecrementListenerInvocations();
|
||||
|
||||
// Owning PID comparison
|
||||
bool MatchesOwningPID(const pid_t other_pid);
|
||||
|
||||
// Returns the current system uptime in microseconds
|
||||
/// Returns the current system uptime in microseconds
|
||||
uint64_t GetCurrentUptime();
|
||||
|
||||
// Starting and stopping the listener
|
||||
kern_return_t StartListener();
|
||||
kern_return_t StopListener();
|
||||
/// Increments the count of active vnode callback's pending.
|
||||
void IncrementListenerInvocations();
|
||||
|
||||
/// Decrements the count of active vnode callback's pending.
|
||||
void DecrementListenerInvocations();
|
||||
|
||||
private:
|
||||
OSDictionary *cached_decisions_;
|
||||
@@ -121,7 +147,6 @@ class SantaDecisionManager : public OSObject {
|
||||
proc_t owning_proc_;
|
||||
|
||||
kauth_listener_t vnode_listener_;
|
||||
kauth_listener_t process_listener_;
|
||||
};
|
||||
|
||||
///
|
||||
@@ -138,18 +163,4 @@ extern "C" int vnode_scope_callback(
|
||||
kauth_cred_t credential, void *idata, kauth_action_t action,
|
||||
uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
|
||||
|
||||
///
|
||||
/// The kauth callback function for the Process scope
|
||||
/// @param actor's credentials
|
||||
/// @param data that was passed when the listener was registered
|
||||
/// @param action that was requested (KAUTH_PROCESS_{CANTRACE,CANSIGNAL})
|
||||
/// @param target process
|
||||
/// @param Pointer to an errno-style error.
|
||||
/// @param unused
|
||||
/// @param unused
|
||||
///
|
||||
extern "C" int process_scope_callback(
|
||||
kauth_cred_t credential, void *idata, kauth_action_t action,
|
||||
uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
|
||||
|
||||
#endif // SANTA__SANTA_DRIVER__SANTADECISIONMANAGER_H
|
||||
|
||||
@@ -23,6 +23,10 @@ OSDefineMetaClassAndStructors(com_google_SantaDriver, IOService);
|
||||
bool SantaDriver::start(IOService *provider) {
|
||||
if (!super::start(provider)) return false;
|
||||
|
||||
santaDecisionManager = new SantaDecisionManager;
|
||||
santaDecisionManager->init();
|
||||
santaDecisionManager->StartListener();
|
||||
|
||||
registerService();
|
||||
|
||||
LOGI("Loaded, version %s.", OSKextGetCurrentVersionString());
|
||||
@@ -31,7 +35,17 @@ bool SantaDriver::start(IOService *provider) {
|
||||
}
|
||||
|
||||
void SantaDriver::stop(IOService *provider) {
|
||||
santaDecisionManager->StopListener();
|
||||
santaDecisionManager->release();
|
||||
santaDecisionManager = NULL;
|
||||
|
||||
LOGI("Unloaded.");
|
||||
|
||||
super::stop(provider);
|
||||
}
|
||||
|
||||
SantaDecisionManager* SantaDriver::GetDecisionManager() {
|
||||
return santaDecisionManager;
|
||||
}
|
||||
|
||||
#undef super
|
||||
|
||||
@@ -22,14 +22,26 @@
|
||||
#include "SNTLogging.h"
|
||||
|
||||
///
|
||||
/// The driver class, which provides just the start/stop functions.
|
||||
/// The driver class, which provides the start/stop functions and holds
|
||||
/// the SantaDecisionManager instance which the connected client
|
||||
/// communicates with.
|
||||
///
|
||||
class com_google_SantaDriver : public IOService {
|
||||
OSDeclareDefaultStructors(com_google_SantaDriver);
|
||||
|
||||
public:
|
||||
/// Called by the kernel when the kext is loaded
|
||||
bool start(IOService *provider);
|
||||
|
||||
/// Called by the kernel when the kext is unloaded
|
||||
void stop(IOService *provider);
|
||||
|
||||
/// Returns a pointer to the SantaDecisionManager created in start().
|
||||
SantaDecisionManager* GetDecisionManager();
|
||||
|
||||
private:
|
||||
SantaDecisionManager *santaDecisionManager;
|
||||
|
||||
};
|
||||
|
||||
#endif // SANTA__SANTA_DRIVER__SANTADRIVER_H
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// The defines above can'be used in this function, must use the full names.
|
||||
OSDefineMetaClassAndStructors(com_google_SantaDriverClient, IOUserClient);
|
||||
|
||||
# pragma mark Driver Management
|
||||
#pragma mark Driver Management
|
||||
|
||||
bool SantaDriverClient::initWithTask(
|
||||
task_t owningTask, void *securityID, UInt32 type) {
|
||||
@@ -41,52 +41,38 @@ bool SantaDriverClient::start(IOService *provider) {
|
||||
if (!fProvider) return false;
|
||||
if (!super::start(provider)) return false;
|
||||
|
||||
fSDMLock = IOLockAlloc();
|
||||
fSDM = fProvider->GetDecisionManager();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SantaDriverClient::stop(IOService *provider) {
|
||||
super::stop(provider);
|
||||
fProvider = NULL;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::clientClose() {
|
||||
close();
|
||||
terminate(kIOServiceSynchronous);
|
||||
|
||||
fProvider = NULL;
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
bool SantaDriverClient::terminate(IOOptionBits options) {
|
||||
// We have to lock before this check in case the client exits and the kext
|
||||
// is unloaded very shortly afterwards.
|
||||
IOLockLock(fSDMLock);
|
||||
if (fSDM) {
|
||||
fSDM->StopListener();
|
||||
fSDM->DisconnectClient();
|
||||
LOGI("Client disconnected.");
|
||||
|
||||
// Ask santad to shutdown
|
||||
santa_message_t message;
|
||||
message.action = ACTION_REQUEST_SHUTDOWN;
|
||||
message.userId = 0;
|
||||
message.pid = 0;
|
||||
message.ppid = 0;
|
||||
message.vnode_id = 0;
|
||||
fSDM->PostToQueue(message);
|
||||
fSharedMemory->release();
|
||||
fDataQueue->release();
|
||||
|
||||
LOGI("Client disconnected.");
|
||||
|
||||
fSDM->release();
|
||||
fSDM = NULL;
|
||||
}
|
||||
IOLockUnlock(fSDMLock);
|
||||
fSharedMemory = NULL;
|
||||
fDataQueue = NULL;
|
||||
|
||||
if (fProvider && fProvider->isOpen(this)) fProvider->close(this);
|
||||
|
||||
return super::terminate(options);
|
||||
}
|
||||
|
||||
#pragma mark Fetching memory and data queue notifications
|
||||
|
||||
IOReturn SantaDriverClient::registerNotificationPort(mach_port_t port,
|
||||
UInt32 type,
|
||||
UInt32 ref) {
|
||||
@@ -108,7 +94,10 @@ IOReturn SantaDriverClient::clientMemoryForType(UInt32 type,
|
||||
fSharedMemory->retain(); // client will decrement this ref
|
||||
*memory = fSharedMemory;
|
||||
|
||||
return fSDM->StartListener();
|
||||
fSDM->ConnectClient(fDataQueue, proc_selfpid());
|
||||
LOGI("Client connected, PID: %d.", proc_selfpid());
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
return kIOReturnNoMemory;
|
||||
@@ -136,12 +125,6 @@ IOReturn SantaDriverClient::open() {
|
||||
return kIOReturnVMError;
|
||||
}
|
||||
|
||||
IOLockLock(fSDMLock);
|
||||
fSDM = SantaDecisionManager::WithQueueAndPID(fDataQueue, proc_selfpid());
|
||||
IOLockUnlock(fSDMLock);
|
||||
|
||||
LOGI("Client connected, PID: %d.", proc_selfpid());
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
@@ -153,21 +136,6 @@ IOReturn SantaDriverClient::static_open(
|
||||
return target->open();
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::close() {
|
||||
if (!fProvider) return kIOReturnNotAttached;
|
||||
if (fProvider->isOpen(this)) fProvider->close(this);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::static_close(
|
||||
SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments) {
|
||||
if (!target) return kIOReturnBadArgument;
|
||||
return target->close();
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::allow_binary(const uint64_t vnode_id) {
|
||||
char vnode_id_str[21];
|
||||
snprintf(vnode_id_str, sizeof(vnode_id_str), "%llu", vnode_id);
|
||||
@@ -240,8 +208,8 @@ IOReturn SantaDriverClient::externalMethod(
|
||||
IOExternalMethodDispatch *dispatch,
|
||||
OSObject *target,
|
||||
void *reference) {
|
||||
// Array of methods callable by clients. The order of these must match the
|
||||
// order of the items in |SantaDriverMethods| in SNTKernelCommon.h
|
||||
/// Array of methods callable by clients. The order of these must match the
|
||||
/// order of the items in SantaDriverMethods in SNTKernelCommon.h
|
||||
IOExternalMethodDispatch sMethods[kSantaUserClientNMethods] = {
|
||||
{
|
||||
reinterpret_cast<IOExternalMethodAction>(&SantaDriverClient::static_open),
|
||||
@@ -250,14 +218,6 @@ IOReturn SantaDriverClient::externalMethod(
|
||||
0, // output scalar
|
||||
0 // output struct
|
||||
},
|
||||
{
|
||||
reinterpret_cast<IOExternalMethodAction>(
|
||||
&SantaDriverClient::static_close),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
{
|
||||
reinterpret_cast<IOExternalMethodAction>(
|
||||
&SantaDriverClient::static_allow_binary),
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "SNTKernelCommon.h"
|
||||
|
||||
// The maximum number of messages can be kept in the IODataQueue at any time.
|
||||
const int kMaxQueueEvents = 64;
|
||||
const int kMaxQueueEvents = 256;
|
||||
|
||||
///
|
||||
/// This class is instantiated by IOKit when a new client process attempts to
|
||||
@@ -38,7 +38,7 @@ const int kMaxQueueEvents = 64;
|
||||
///
|
||||
/// Documentation on how the IOUserClient parts of this code work can be found
|
||||
/// here:
|
||||
/// @link https://developer.apple.com/library/mac/samplecode/SimpleUserClient/Listings/User_Client_Info_txt.html
|
||||
/// https://developer.apple.com/library/mac/samplecode/SimpleUserClient/Listings/User_Client_Info_txt.html
|
||||
///
|
||||
class com_google_SantaDriverClient : public IOUserClient {
|
||||
OSDeclareDefaultStructors(com_google_SantaDriverClient);
|
||||
@@ -48,61 +48,75 @@ class com_google_SantaDriverClient : public IOUserClient {
|
||||
IOMemoryDescriptor *fSharedMemory;
|
||||
com_google_SantaDriver *fProvider;
|
||||
SantaDecisionManager *fSDM;
|
||||
IOLock *fSDMLock;
|
||||
|
||||
public:
|
||||
bool start(IOService *provider);
|
||||
void stop(IOService *provider);
|
||||
IOReturn clientClose();
|
||||
bool terminate(IOOptionBits options);
|
||||
/// Called as part of IOServiceOpen in clients
|
||||
bool initWithTask(task_t owningTask, void *securityID, UInt32 type);
|
||||
|
||||
/// Called after initWithTask as part of IOServiceOpen
|
||||
bool start(IOService *provider);
|
||||
|
||||
/// Called when this class is stopping
|
||||
void stop(IOService *provider);
|
||||
|
||||
/// Called when a client disconnects
|
||||
IOReturn clientClose();
|
||||
|
||||
/// Called when the driver is shutting down
|
||||
bool terminate(IOOptionBits options);
|
||||
|
||||
/// Called in clients with IOConnectSetNotificationPort
|
||||
IOReturn registerNotificationPort(
|
||||
mach_port_t port, UInt32 type, UInt32 refCon);
|
||||
|
||||
/// Called in clients with IOConnectMapMemory
|
||||
IOReturn clientMemoryForType(
|
||||
UInt32 type, IOOptionBits *options, IOMemoryDescriptor **memory);
|
||||
|
||||
/// Called in clients with IOConnectCallScalarMethod etc. Dispatches
|
||||
/// to the requested selector using the SantaDriverMethods enum in
|
||||
/// SNTKernelCommon.
|
||||
IOReturn externalMethod(
|
||||
UInt32 selector,
|
||||
IOExternalMethodArguments *arguments,
|
||||
IOExternalMethodDispatch *dispatch,
|
||||
OSObject *target, void *reference);
|
||||
|
||||
///
|
||||
/// The userpsace callable methods are below. Each method corresponds
|
||||
/// to an entry in SantaDriverMethods. Each method has a static version
|
||||
/// which just calls the method on the provided target.
|
||||
///
|
||||
|
||||
/// Called during client connection
|
||||
IOReturn open();
|
||||
static IOReturn static_open(
|
||||
com_google_SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments);
|
||||
|
||||
IOReturn close();
|
||||
static IOReturn static_close(
|
||||
com_google_SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to allow a binary.
|
||||
/// The daemon calls this to allow a binary.
|
||||
IOReturn allow_binary(uint64_t vnode_id);
|
||||
static IOReturn static_allow_binary(
|
||||
com_google_SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to deny a binary.
|
||||
/// The daemon calls this to deny a binary.
|
||||
IOReturn deny_binary(uint64_t vnode_id);
|
||||
static IOReturn static_deny_binary(
|
||||
com_google_SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to empty the cache.
|
||||
/// The daemon calls this to empty the cache.
|
||||
IOReturn clear_cache();
|
||||
static IOReturn static_clear_cache(
|
||||
com_google_SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to find out how many items are in the cache
|
||||
/// The daemon calls this to find out how many items are in the cache
|
||||
IOReturn cache_count(uint64_t *output);
|
||||
static IOReturn static_cache_count(
|
||||
com_google_SantaDriverClient *target,
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
///
|
||||
@property(nonatomic) NSString *userAgent;
|
||||
|
||||
///
|
||||
/// If set to YES, this session refuses redirect requests. Defaults to NO.
|
||||
///
|
||||
@property(nonatomic) BOOL refusesRedirects;
|
||||
|
||||
///
|
||||
/// If set, the server that we connect to _must_ match this string. Redirects to other
|
||||
/// hosts will not be allowed.
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#import "SNTAuthenticatingURLSession.h"
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTDERDecoder.h"
|
||||
#import "SNTLogging.h"
|
||||
|
||||
@@ -107,6 +106,18 @@
|
||||
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
task:(NSURLSessionTask *)task
|
||||
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
|
||||
newRequest:(NSURLRequest *)request
|
||||
completionHandler:(void (^)(NSURLRequest *))completionHandler {
|
||||
if (self.refusesRedirects) {
|
||||
completionHandler(NULL);
|
||||
} else {
|
||||
completionHandler(request);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Private Helpers for URLSession:didReceiveChallenge:completionHandler:
|
||||
|
||||
///
|
||||
@@ -152,6 +163,7 @@
|
||||
}
|
||||
|
||||
foundIdentity = (__bridge SecIdentityRef)identities[0][(__bridge id)kSecImportItemIdentity];
|
||||
CFRetain(foundIdentity);
|
||||
} else {
|
||||
CFArrayRef cfIdentities;
|
||||
err = SecItemCopyMatching((__bridge CFDictionaryRef)@{
|
||||
@@ -186,6 +198,7 @@
|
||||
if ([clientCert.commonName compare:self.clientCertCommonName
|
||||
options:NSCaseInsensitiveSearch] == NSOrderedSame) {
|
||||
foundIdentity = identityRef;
|
||||
CFRetain(foundIdentity);
|
||||
*stop = YES;
|
||||
return; // return from enumeration block
|
||||
}
|
||||
@@ -193,6 +206,7 @@
|
||||
if ([clientCert.issuerCommonName compare:self.clientCertIssuerCn
|
||||
options:NSCaseInsensitiveSearch] == NSOrderedSame) {
|
||||
foundIdentity = identityRef;
|
||||
CFRetain(foundIdentity);
|
||||
*stop = YES;
|
||||
return; // return from enumeration block
|
||||
}
|
||||
@@ -206,6 +220,7 @@
|
||||
[clientCert.issuerOrgUnit isEqual:decoder.organizationalUnit]) {
|
||||
|
||||
foundIdentity = identityRef;
|
||||
CFRetain(foundIdentity);
|
||||
*stop = YES;
|
||||
return; // return from enumeration block
|
||||
}
|
||||
@@ -216,9 +231,12 @@
|
||||
|
||||
if (foundIdentity) {
|
||||
LOGD(@"Client Trust: Valid client identity %@.", foundIdentity);
|
||||
return [NSURLCredential credentialWithIdentity:foundIdentity
|
||||
certificates:nil
|
||||
persistence:NSURLCredentialPersistenceForSession];
|
||||
NSURLCredential *cred =
|
||||
[NSURLCredential credentialWithIdentity:foundIdentity
|
||||
certificates:nil
|
||||
persistence:NSURLCredentialPersistenceForSession];
|
||||
CFRelease(foundIdentity);
|
||||
return cred;
|
||||
} else {
|
||||
LOGD(@"Client Trust: No valid identity found.");
|
||||
return nil;
|
||||
|
||||
@@ -78,6 +78,7 @@ REGISTER_COMMAND_NAME(@"sync");
|
||||
if (santactlVersion) {
|
||||
authURLSession.userAgent = [authURLSession.userAgent stringByAppendingString:santactlVersion];
|
||||
}
|
||||
authURLSession.refusesRedirects = YES;
|
||||
|
||||
// Configure server auth
|
||||
if ([config syncServerAuthRootsFile]) {
|
||||
|
||||
@@ -143,6 +143,9 @@
|
||||
ADDKEY(newEvent, @"file_bundle_version", event.fileBundleVersion);
|
||||
ADDKEY(newEvent, @"file_bundle_version_string", event.fileBundleVersionString);
|
||||
|
||||
ADDKEY(newEvent, @"pid", event.pid);
|
||||
ADDKEY(newEvent, @"ppid", event.ppid);
|
||||
|
||||
NSMutableArray *signingChain = [NSMutableArray arrayWithCapacity:event.signingChain.count];
|
||||
for (int i = 0; i < event.signingChain.count; i++) {
|
||||
SNTCertificate *cert = [event.signingChain objectAtIndex:i];
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/// 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.
|
||||
|
||||
///
|
||||
/// This is a simple ASN.1 decoder that utilizes Apple's SecAsn1Decode
|
||||
/// to parse the @c distinguishedNames property of NSURLProtectionSpace.
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/// 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 "SNTDERDecoder.h"
|
||||
|
||||
#import <Security/SecAsn1Coder.h>
|
||||
|
||||
@@ -138,6 +138,7 @@
|
||||
[self.execController validateBinaryWithPath:@(message.path)
|
||||
userName:userName
|
||||
pid:@(message.pid)
|
||||
ppid:@(message.ppid)
|
||||
vnodeId:message.vnode_id];
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
if (!serviceObject) {
|
||||
LOGD(@"Waiting for Santa driver to become available");
|
||||
sleep(10);
|
||||
sleep(5);
|
||||
}
|
||||
} while (!serviceObject);
|
||||
CFRelease(classToMatch);
|
||||
|
||||
@@ -53,11 +53,13 @@
|
||||
/// @param path the binary that's being executed
|
||||
/// @param userName the user who's executing the binary
|
||||
/// @param pid the process id being executed
|
||||
/// @param ppid the parent process id
|
||||
/// @param vnoteId the id of the vnode being executed
|
||||
///
|
||||
- (void)validateBinaryWithPath:(NSString *)path
|
||||
userName:(NSString *)userName
|
||||
pid:(NSNumber *)pid
|
||||
ppid:(NSNumber *)ppid
|
||||
vnodeId:(uint64_t)vnodeId;
|
||||
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
- (void)validateBinaryWithPath:(NSString *)path
|
||||
userName:(NSString *)userName
|
||||
pid:(NSNumber *)pid
|
||||
ppid:(NSNumber *)ppid
|
||||
vnodeId:(uint64_t)vnodeId {
|
||||
SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:path];
|
||||
NSString *sha256 = [binInfo SHA256];
|
||||
@@ -121,6 +122,7 @@
|
||||
se.occurrenceDate = [[NSDate alloc] init];
|
||||
se.decision = [self eventStateForDecision:respondedAction type:rule.type];
|
||||
se.pid = pid;
|
||||
se.ppid = ppid;
|
||||
|
||||
NSArray *loggedInUsers, *currentSessions;
|
||||
[self loggedInUsers:&loggedInUsers sessions:¤tSessions];
|
||||
|
||||
12
Tests/LogicTests/Resources/dn.plist
Normal file
12
Tests/LogicTests/Resources/dn.plist
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<array>
|
||||
<data>
|
||||
MIGcMQswCQYDVQQGEwJVUzERMA8GA1UECAwIVGhlIE1vb24xEjAQBgNVBAcMCU1vb24g
|
||||
QmFzZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMSkwJwYDVQQLDCBB
|
||||
d2Vzb21lIEF1dGhlbnRpY2F0aW9uIEF1dGhvcml0eTEYMBYGA1UEAwwPYXV0aC5zZXJ2
|
||||
ZXIuY29t
|
||||
</data>
|
||||
</array>
|
||||
</plist>
|
||||
@@ -80,9 +80,11 @@
|
||||
XCTAssertEqualObjects(sut.commonName, @"Google Internet Authority G2");
|
||||
XCTAssertEqualObjects(sut.orgUnit, nil);
|
||||
XCTAssertEqualObjects(sut.orgName, @"Google Inc");
|
||||
XCTAssertEqualObjects(sut.countryName, @"US");
|
||||
XCTAssertEqualObjects(sut.issuerCommonName, @"GeoTrust Global CA");
|
||||
XCTAssertEqualObjects(sut.issuerOrgName, @"GeoTrust Inc.");
|
||||
XCTAssertEqualObjects(sut.issuerOrgUnit, nil);
|
||||
XCTAssertEqualObjects(sut.issuerCountryName, @"US");
|
||||
XCTAssertEqualObjects(sut.SHA1, @"d83c1a7f4d0446bb2081b81a1670f8183451ca24");
|
||||
XCTAssertEqualObjects(sut.SHA256,
|
||||
@"a047a37fa2d2e118a4f5095fe074d6cfe0e352425a7632bf8659c03919a6c81d");
|
||||
@@ -97,6 +99,7 @@
|
||||
@"Ulusal Elektronik ve Kriptoloji Araştırma Enstitüsü - UEKAE");
|
||||
XCTAssertEqualObjects(sut.orgName,
|
||||
@"Türkiye Bilimsel ve Teknolojik Araştırma Kurumu - TÜBİTAK");
|
||||
XCTAssertEqualObjects(sut.countryName, @"TR");
|
||||
}
|
||||
|
||||
- (void)testInitWithValidPEM {
|
||||
@@ -219,6 +222,8 @@
|
||||
(void)sut.orgName;
|
||||
(void)sut.issuerCommonName;
|
||||
(void)sut.validFrom;
|
||||
(void)sut.countryName;
|
||||
(void)sut.issuerCountryName;
|
||||
|
||||
// Now break some of the properties
|
||||
OCMStub([sutMock x509ValueForLabel:OCMOCK_ANY fromDictionary:OCMOCK_ANY]);
|
||||
@@ -227,6 +232,8 @@
|
||||
XCTAssertEqualObjects(sut.orgName, @"Google Inc");
|
||||
XCTAssertEqualObjects(sut.issuerCommonName, @"GeoTrust Global CA");
|
||||
XCTAssertEqualObjects(sut.validFrom, [NSDate dateWithString:@"2013-04-05 15:15:55 +0000"]);
|
||||
XCTAssertEqualObjects(sut.countryName, @"US");
|
||||
XCTAssertEqualObjects(sut.issuerCountryName, @"US");
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -33,6 +33,17 @@
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testAllFields {
|
||||
NSString *file = [[NSBundle bundleForClass:[self class]] pathForResource:@"dn" ofType:@"plist"];
|
||||
NSArray *distinguishedNames = [NSArray arrayWithContentsOfFile:file];
|
||||
|
||||
SNTDERDecoder *sut = [[SNTDERDecoder alloc] initWithData:[distinguishedNames firstObject]];
|
||||
XCTAssertEqualObjects(sut.commonName, @"auth.server.com");
|
||||
XCTAssertEqualObjects(sut.organizationName, @"Internet Widgits Pty Ltd");
|
||||
XCTAssertEqualObjects(sut.organizationalUnit, @"Awesome Authentication Authority");
|
||||
XCTAssertEqualObjects(sut.countryName, @"US");
|
||||
}
|
||||
|
||||
- (void)testOIDDecoding {
|
||||
unsigned char oidBytes1[] = {0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x14};
|
||||
NSString *oidStr = [SNTDERDecoder decodeOIDWithBytes:oidBytes1 length:sizeof(oidBytes1)];
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
[self.sut validateBinaryWithPath:@"/a/file"
|
||||
userName:@"nobody"
|
||||
pid:@(12)
|
||||
ppid:@(1)
|
||||
vnodeId:1234];
|
||||
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
|
||||
@@ -101,6 +102,7 @@
|
||||
[self.sut validateBinaryWithPath:@"/a/file"
|
||||
userName:@"nobody"
|
||||
pid:@(12)
|
||||
ppid:@(1)
|
||||
vnodeId:1234];
|
||||
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_DENY
|
||||
@@ -125,6 +127,7 @@
|
||||
[self.sut validateBinaryWithPath:@"/a/file"
|
||||
userName:@"nobody"
|
||||
pid:@(12)
|
||||
ppid:@(1)
|
||||
vnodeId:1234];
|
||||
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
|
||||
@@ -149,6 +152,7 @@
|
||||
[self.sut validateBinaryWithPath:@"/a/file"
|
||||
userName:@"nobody"
|
||||
pid:@(12)
|
||||
ppid:@(1)
|
||||
vnodeId:1234];
|
||||
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_DENY
|
||||
@@ -160,7 +164,11 @@
|
||||
OCMStub([mockSut fileIsInScope:OCMOCK_ANY]).andReturn(YES);
|
||||
|
||||
[self.sut setOperatingMode:CLIENTMODE_MONITOR];
|
||||
[self.sut validateBinaryWithPath:@"/a/file" userName:@"nobody" pid:@(12) vnodeId:1234];
|
||||
[self.sut validateBinaryWithPath:@"/a/file"
|
||||
userName:@"nobody"
|
||||
pid:@(12)
|
||||
ppid:@(1)
|
||||
vnodeId:1234];
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
|
||||
forVnodeID:1234]);
|
||||
|
||||
@@ -168,6 +176,7 @@
|
||||
[self.sut validateBinaryWithPath:@"/a/file"
|
||||
userName:@"nobody"
|
||||
pid:@(12)
|
||||
ppid:@(1)
|
||||
vnodeId:1234];
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_DENY
|
||||
forVnodeID:1234]);
|
||||
@@ -178,7 +187,11 @@
|
||||
OCMStub([mockSut fileIsInScope:OCMOCK_ANY]).andReturn(NO);
|
||||
|
||||
[self.sut setOperatingMode:CLIENTMODE_LOCKDOWN];
|
||||
[self.sut validateBinaryWithPath:@"/a/file" userName:@"nobody" pid:@(24) vnodeId:1234];
|
||||
[self.sut validateBinaryWithPath:@"/a/file"
|
||||
userName:@"nobody"
|
||||
pid:@(24)
|
||||
ppid:@(1)
|
||||
vnodeId:1234];
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
|
||||
forVnodeID:1234]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user