mirror of
https://github.com/google/santa.git
synced 2026-01-18 10:38:04 -05:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
292c51e8a5 | ||
|
|
c2f04c50a5 | ||
|
|
65cb531ac8 | ||
|
|
373b49cee4 | ||
|
|
654aa7cf2d | ||
|
|
6174f80abd | ||
|
|
7268de352f | ||
|
|
f046711fb0 | ||
|
|
fac4e68adf | ||
|
|
8693fad2c9 | ||
|
|
bca08ad359 | ||
|
|
e32b4e2350 | ||
|
|
97a5755c3d | ||
|
|
17f102662a | ||
|
|
f47ce3d2bc |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,5 +1,8 @@
|
||||
.DS_Store
|
||||
Build
|
||||
Dist
|
||||
Pods
|
||||
Santa.xcodeproj/xcuserdata
|
||||
Santa.xcodeproj/project.xcworkspace
|
||||
Santa.xcworkspace/xcuserdata
|
||||
Santa.xcworkspace/xcshareddata
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
# Copy this file to /etc/asl to log all messages from santa-driver to the log file
|
||||
? [S= Message santa-driver:] file /var/log/santa.log claim only format="[$((Time)(utc.3))] $Message"
|
||||
? [S= Message santa-driver:] claim only
|
||||
? [S= Message santa-driver:] file /var/log/santa.log format="[$((Time)(utc.3))] $Message"
|
||||
> /var/log/santa.log mode=0644 rotate=seq compress file_max=5M all_max=100M
|
||||
|
||||
@@ -8,10 +8,6 @@
|
||||
<array>
|
||||
<string>/Applications/Santa.app/Contents/MacOS/Santa</string>
|
||||
</array>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/var/log/santa.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/log/santa.log</string>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
</dict>
|
||||
|
||||
20
README.md
20
README.md
@@ -112,16 +112,22 @@ be shown when loading the kext for the first time. In 10.10 this is a hard error
|
||||
and the kext will not load at all unless the machine is booted with a debug
|
||||
boot-arg.
|
||||
|
||||
There are two possible solutions for this:
|
||||
There are two possible solutions for this, for distribution purposes:
|
||||
|
||||
1) Use a pre-built, pre-signed version of the kext that we supply. Each time
|
||||
changes are made to the kext code we will update the pre-built version that you
|
||||
can make use of. This doesn't prevent you from making changes to the non-kext
|
||||
parts of Santa and distributing those. If you make changes to the kext and make
|
||||
a pull request, we can merge them in and distribute a new version of the
|
||||
pre-signed kext.
|
||||
1) Use a [pre-built, pre-signed version](https://github.com/google/santa/releases)
|
||||
of the kext that we supply. Each time changes are made to the kext code we will
|
||||
update the pre-built version that you can make use of. This doesn't prevent you
|
||||
from making changes to the non-kext parts of Santa and distributing those.
|
||||
If you make changes to the kext and make a pull request, we can merge them in
|
||||
and distribute a new version of the pre-signed kext.
|
||||
|
||||
2) Apply for your own [kext signing certificate](https://developer.apple.com/contact/kext/).
|
||||
Apple will only grant this for broad distribution within an organization, they
|
||||
won't issue them just for testing purposes.
|
||||
|
||||
If you just want to locally test changes to the kext code, you should enable
|
||||
kext-dev mode, instructions for which can be found on the Apple developer site.
|
||||
|
||||
|
||||
Contributing
|
||||
============
|
||||
|
||||
27
Rakefile
27
Rakefile
@@ -2,7 +2,9 @@ require 'timeout'
|
||||
|
||||
WORKSPACE = 'Santa.xcworkspace'
|
||||
DEFAULT_SCHEME = 'All'
|
||||
OUTPUT_PATH = 'build'
|
||||
OUTPUT_PATH = 'Build'
|
||||
DIST_PATH = 'Dist'
|
||||
BINARIES = ['Santa.app', 'santa-driver.kext', 'santad', 'santactl']
|
||||
PLISTS = ['Source/SantaGUI/Resources/Santa-Info.plist',
|
||||
'Source/santad/Resources/santad-Info.plist',
|
||||
'Source/santa-driver/Resources/santa-driver-Info.plist',
|
||||
@@ -60,6 +62,7 @@ task :clean => :init do
|
||||
puts "Cleaning"
|
||||
run_and_output_on_fail("xcodebuild #{XCODE_DEFAULTS} clean")
|
||||
FileUtils.rm_rf(OUTPUT_PATH)
|
||||
FileUtils.rm_rf(DIST_PATH)
|
||||
end
|
||||
|
||||
# Build
|
||||
@@ -110,6 +113,28 @@ namespace :install do
|
||||
end
|
||||
end
|
||||
|
||||
# Dist
|
||||
task :dist do
|
||||
desc "Create distribution folder"
|
||||
|
||||
Rake::Task['build:build'].invoke("Release")
|
||||
|
||||
FileUtils.rm_rf(DIST_PATH)
|
||||
|
||||
FileUtils.mkdir_p("#{DIST_PATH}/binaries")
|
||||
FileUtils.mkdir_p("#{DIST_PATH}/conf")
|
||||
FileUtils.mkdir_p("#{DIST_PATH}/dsym")
|
||||
|
||||
BINARIES.each do |x|
|
||||
FileUtils.cp_r("#{OUTPUT_PATH}/Products/Release/#{x}", "#{DIST_PATH}/binaries")
|
||||
FileUtils.cp_r("#{OUTPUT_PATH}/Products/Release/#{x}.dSYM", "#{DIST_PATH}/dsym")
|
||||
end
|
||||
|
||||
Dir.glob("Conf/*") {|x| FileUtils.cp(x, "#{DIST_PATH}/conf")}
|
||||
|
||||
puts "Distribution folder created"
|
||||
end
|
||||
|
||||
# Tests
|
||||
namespace :tests do
|
||||
desc "Tests: Logic"
|
||||
|
||||
@@ -1492,7 +1492,6 @@
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
@@ -1508,7 +1507,7 @@
|
||||
INFOPLIST_FILE = "Source/santa-driver/Resources/santa-driver-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited)";
|
||||
MODULE_NAME = "com.google.santa-driver";
|
||||
MODULE_VERSION = 0.7;
|
||||
MODULE_VERSION = 0.7.1;
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
@@ -1527,7 +1526,6 @@
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
@@ -1540,7 +1538,7 @@
|
||||
INFOPLIST_FILE = "Source/santa-driver/Resources/santa-driver-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited)";
|
||||
MODULE_NAME = "com.google.santa-driver";
|
||||
MODULE_VERSION = 0.7;
|
||||
MODULE_VERSION = 0.7.1;
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WARNING_CFLAGS = "-Wno-deprecated-register";
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.7</string>
|
||||
<string>0.7.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.7</string>
|
||||
<string>0.7.1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>LSUIElement</key>
|
||||
|
||||
@@ -61,7 +61,9 @@
|
||||
- (void)attemptReconnection {
|
||||
// TODO(rah): Make this smarter.
|
||||
sleep(10);
|
||||
[self createConnection];
|
||||
[self performSelectorOnMainThread:@selector(createConnection)
|
||||
withObject:nil
|
||||
waitUntilDone:NO];
|
||||
}
|
||||
|
||||
#pragma mark Menu Management
|
||||
|
||||
@@ -58,6 +58,9 @@ typedef enum {
|
||||
ACTION_ERROR = 99,
|
||||
} santa_action_t;
|
||||
|
||||
#define RESPONSE_VALID(x) (x == ACTION_RESPOND_CHECKBW_ALLOW || \
|
||||
x == ACTION_RESPOND_CHECKBW_DENY)
|
||||
|
||||
// Message struct that is sent down the IODataQueue.
|
||||
typedef struct {
|
||||
santa_action_t action;
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>KEXT</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.7</string>
|
||||
<string>0.7.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.7</string>
|
||||
<string>0.7.1</string>
|
||||
<key>IOKitPersonalities</key>
|
||||
<dict>
|
||||
<key>SantaDriver</key>
|
||||
|
||||
@@ -73,7 +73,7 @@ void SantaDecisionManager::free() {
|
||||
|
||||
# pragma mark Cache Management
|
||||
|
||||
bool SantaDecisionManager::AddToCache(
|
||||
void SantaDecisionManager::AddToCache(
|
||||
const char *identifier, santa_action_t decision, uint64_t microsecs) {
|
||||
IORWLockWrite(cached_decisions_lock_);
|
||||
|
||||
@@ -87,25 +87,20 @@ bool SantaDecisionManager::AddToCache(
|
||||
cached_decisions_->flushCollection();
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (decision == ACTION_REQUEST_CHECKBW) {
|
||||
SantaMessage *pending = new SantaMessage();
|
||||
pending->setAction(ACTION_REQUEST_CHECKBW, 0);
|
||||
result = cached_decisions_->setObject(identifier, pending);
|
||||
cached_decisions_->setObject(identifier, pending);
|
||||
pending->release(); // it was retained when added to the dictionary
|
||||
} else {
|
||||
SantaMessage *pending = OSDynamicCast(
|
||||
SantaMessage, cached_decisions_->getObject(identifier));
|
||||
if (pending) {
|
||||
pending->setAction(decision, microsecs);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
IORWLockUnlock(cached_decisions_lock_);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SantaDecisionManager::CacheCheck(const char *identifier) {
|
||||
@@ -142,8 +137,7 @@ santa_action_t SantaDecisionManager::GetFromCache(const char *identifier) {
|
||||
}
|
||||
IORWLockUnlock(cached_decisions_lock_);
|
||||
|
||||
if (result == ACTION_RESPOND_CHECKBW_ALLOW ||
|
||||
result == ACTION_RESPOND_CHECKBW_DENY) {
|
||||
if (RESPONSE_VALID(result)) {
|
||||
uint64_t diff_time = GetCurrentUptime();
|
||||
|
||||
if (result == ACTION_RESPOND_CHECKBW_ALLOW) {
|
||||
@@ -195,14 +189,10 @@ santa_action_t SantaDecisionManager::FetchDecision(
|
||||
return_action = GetFromCache(vnode_id_str);
|
||||
|
||||
// If item wasn't in cache, fetch decision from daemon.
|
||||
if (return_action == ACTION_UNSET) {
|
||||
if (!RESPONSE_VALID(return_action)) {
|
||||
// Add pending request to cache
|
||||
AddToCache(vnode_id_str, ACTION_REQUEST_CHECKBW, 0);
|
||||
|
||||
// Get SHA-1
|
||||
// TODO(rah): Investigate possible race condition where file is modified
|
||||
// in between SHA-1 being calculated and response for said file being
|
||||
// received.
|
||||
char sha[MAX_SHA1_STRING];
|
||||
if (!CalculateSHA1ForVnode(credential, vfs_context, vnode, sha)) {
|
||||
LOGD("Unable to get SHA-1 for file, denying execution");
|
||||
@@ -240,11 +230,12 @@ santa_action_t SantaDecisionManager::FetchDecision(
|
||||
for (int i = 0; i < kMaxRequestLoops; ++i) {
|
||||
IOSleep(kRequestLoopSleepMilliseconds);
|
||||
return_action = GetFromCache(vnode_id_str);
|
||||
if (return_action != ACTION_REQUEST_CHECKBW) break;
|
||||
if (RESPONSE_VALID(return_action)) break;
|
||||
}
|
||||
} while (return_action == ACTION_REQUEST_CHECKBW && proc_exiting(owning_proc_) == 0);
|
||||
} while (!RESPONSE_VALID(return_action) && proc_exiting(owning_proc_) == 0);
|
||||
|
||||
if (return_action == ACTION_UNSET || return_action == ACTION_ERROR) {
|
||||
// If response is still not valid, the daemon exited
|
||||
if (!RESPONSE_VALID(return_action)) {
|
||||
LOGE("Daemon process did not respond correctly. Allowing executions "
|
||||
"until it comes back.");
|
||||
CacheCheck(vnode_id_str);
|
||||
@@ -449,6 +440,7 @@ extern int vnode_scope_callback(kauth_cred_t credential,
|
||||
vtype vt = vnode_vtype(vnode);
|
||||
if (vt != VREG) return returnResult;
|
||||
|
||||
// Don't operate on ACCESS events, as they're advisory
|
||||
if (action & KAUTH_VNODE_ACCESS) return returnResult;
|
||||
|
||||
// Filter for only WRITE_DATA actions
|
||||
@@ -459,6 +451,7 @@ extern int vnode_scope_callback(kauth_cred_t credential,
|
||||
|
||||
// If an execution request is pending, deny write
|
||||
if (sdm->GetFromCache(vnode_id_str) == ACTION_REQUEST_CHECKBW) {
|
||||
LOGD("Denying write due to pending execution: %s", vnode_id_str);
|
||||
*(reinterpret_cast<int *>(arg3)) = EACCES;
|
||||
return KAUTH_RESULT_DENY;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ class SantaDecisionManager : public OSObject {
|
||||
uint64_t GetVnodeIDForVnode(const vfs_context_t context, const vnode_t vp);
|
||||
|
||||
// Cache management
|
||||
bool AddToCache(const char *identifier,
|
||||
void AddToCache(const char *identifier,
|
||||
const santa_action_t decision,
|
||||
const uint64_t microsecs);
|
||||
void CacheCheck(const char *identifier);
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.7</string>
|
||||
<string>0.7.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.7</string>
|
||||
<string>0.7.1</string>
|
||||
<key>CSFlags</key>
|
||||
<string>kill</string>
|
||||
</dict>
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.7</string>
|
||||
<string>0.7.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.7</string>
|
||||
<string>0.7.1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
serviceObject = IOServiceGetMatchingService(kIOMasterPortDefault, classToMatch);
|
||||
|
||||
if (!serviceObject) {
|
||||
LOGD(@"Waiting for Santa driver to become available");
|
||||
sleep(10);
|
||||
}
|
||||
} while (!serviceObject);
|
||||
|
||||
@@ -48,6 +48,11 @@
|
||||
_operatingMode = operatingMode;
|
||||
_notifierConnection = notifier;
|
||||
LOGI(@"Log format: Decision (A|D), Reason (B|C), SHA-1, Path, Cert SHA-1, Cert CN");
|
||||
|
||||
// Workaround for xpcproxy/libsecurity bug on Yosemite
|
||||
// This establishes the XPC connection between libsecurity and syspolicyd.
|
||||
// Not doing this causes a deadlock as establishing this link goes through xpcproxy.
|
||||
(void)[[SNTCodesignChecker alloc] initWithSelf];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -253,6 +258,7 @@
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
fclose(stdout);
|
||||
fclose(stderr);
|
||||
|
||||
// Ensure we have no privileges
|
||||
if (!DropRootPrivileges()) {
|
||||
|
||||
Reference in New Issue
Block a user