Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Macos: add highly experimental sandboxing, add testing entitlements #154

Open
wants to merge 9 commits into
base: staging
Choose a base branch
from
32 changes: 27 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,23 @@ ifneq (,$(findstring linux,$(OS))$(findstring bsd,$(OS))$(findstring sunos,$(OS)
USE_X11 ?= 1
endif

ifneq (,$(findstring darwin,$(OS))$(findstring serenity,$(OS)))
# Enable SDL2 on Darwin, Serenity by default
ifneq (,$(findstring darwin,$(OS)))
# Enable SDL2 on Darwin by default
USE_SDL ?= 2
ifeq ($(USE_ISOLATION),2)
# Foundation/Cocoa runtime
override LDFLAGS += -lobjc -framework Cocoa
endif
ifeq ($(and $(if $(LTO_SUPPORTED),1),$(if $(filter 1,$(USE_DEBUG)$(USE_DEBUG_FULL)),1),$(if $(findstring clang,$(CC_INFO))$(findstring LLVM,$(CC_INFO))),1),1)
# Generate symbols properly with lto on debug on apple clang and darwin>=15.x
override LDFLAGS += -Wl,-object_path_lto,$(BUILDDIR)/obj/lto
# Generate dSYM symbol bundle explicitly on darwin>=15.x, currently messy and is not used but i'll leave it as todo
# override CFLAGS += -Wl, -dsym-dir $(BUILDDIR)/$(BINARY).dSYM
endif
endif

ifneq (,$(findstring serenity,$(OS)))
# Enable SDL2 on Serenity by default
USE_SDL ?= 2
endif

Expand Down Expand Up @@ -556,9 +571,16 @@ override CC_LD := $(CXX)
endif

# Sign binaries on MacOS host
ifneq (,$(if $(findstring darwin,$(OS)),$(shell which codesign $(NULL_STDERR))))
override ENTITLEMENTS := $(SRCDIR)/bindings/macos_codesign/rvvm_debug.entitlements
override CODESIGN = codesign -s - --force --options=runtime --entitlements $(ENTITLEMENTS) $@
ifneq (,$(findstring darwin,$(OS)),$(shell which codesign $(NULL_STDERR)),$(USE_EXPERIMENTAL_SHIT))
ifeq ($(USE_ISOLATION),2)
ENTITLEMENTS = $(SRCDIR)/bindings/macos_codesign/rvvm.entitlements
# todo: fixup sdl2 loading while sandboxing is engaged, can't atm, should opt in as external lib
# $(info $(WHITE)[$(GREEN)RPATH$(WHITE)] $(shell install_name_tool -change /opt/homebrew/Cellar/sdl2/*/lib/libSDL2-2.0.0.dylib @rpath/libSDL2-2.0.0.dylib $@ $(RESET))
# todo: proper entitlements with a certificate authority
override CODESIGN = $(info $(WHITE)[$(GREEN)ENT$(WHITE)] $(shell plutil $(ENTITLEMENTS)) $(RESET)) \
$(info $(WHITE)[$(GREEN)CODESIGN$(WHITE)] $(shell codesign -s - -d -o runtime,library --timestamp --strict --verbose=4 --preserve-metadata=entitlements,requirements,flags,runtime --entitlements $(ENTITLEMENTS) $@ 2>&1) $(RESET)) \
$(info $(WHITE)[$(GREEN)VERIFY$(WHITE)] $(shell spctl --assess --verbose=4 --type execute $@ 2>&1) $(RESET))
endif
endif

#
Expand Down
2 changes: 1 addition & 1 deletion src/bindings/macos_codesign/codesign.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/bin/sh
codesign -s - --force --options=runtime --entitlements rvvm_debug.entitlements $@
codesign -s - -d -o runtime,library --timestamp --strict --verbose=4 --preserve-metadata=entitlements,requirements,flags,runtime --entitlements rvvm.entitlements $@
26 changes: 26 additions & 0 deletions src/bindings/macos_codesign/rvvm.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.library-validation</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.get-task-allow</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.hypervisor</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.temporary-exception.files.absolute-path.read-only</key>
<array>
<string>/opt/homebrew/Cellar/sdl2/*/lib/libSDL2-2.0.0.dylib</string>
</array>
</dict>
</plist>
3 changes: 2 additions & 1 deletion src/bindings/macos_codesign/rvvm_debug.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<true/>
<key>com.apple.security.cs.disable-executable-page-protection</key>
<true/>
<key>com.apple.security.get-task-allow</key><true/></dict></plist>
<key>com.apple.security.cs.allow-jit</key>
<true/>
</dict>
</plist>
14 changes: 0 additions & 14 deletions src/bindings/macos_codesign/rvvm_isolement.entitlements

This file was deleted.

1 change: 1 addition & 0 deletions src/bindings/macos_codesign/sandbox.sb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(version 3)(allow default)(allow file-read-data file-read-metadata (subpath "/opt/homebrew/Cellar/sdl2/"))(allow process-exec)(allow process-fork)
87 changes: 86 additions & 1 deletion src/rvvm_isolation.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
rvvm_isolation.c - Process & thread isolation
Copyright (C) 2024 LekKit <github.com/LekKit>
Copyright (C) 2024 LekKit <github.com/LekKit> foxxie <github.com/n30f0x>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -60,6 +60,23 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#define ISOLATION_PLEDGE_IMPL
#endif

#if defined(__APPLE__) && (USE_EXPERIMENTAL_SHIT) && CHECK_INCLUDE(objc/objc-api.h, 0)
#include <sys/stat.h>
#include <xpc/xpc.h>
#if USE_ISOLATION == 1
// Isolate with legacy
#include <sandbox.h>
#define ISOLATION_GATEKEEPER_IMPL1
#elif USE_ISOLATION == 2
// Isolate via MacOS Cocoa sandbox.
#include <objc/runtime.h>
#include <objc/message.h>
#include <objc/objc-api.h>
#include <objc/NSObject.h>
#define ISOLATION_GATEKEEPER_IMPL2
#endif
#endif

#endif

// Drop all the capabilities of the calling thread, prevent privilege escalation
Expand Down Expand Up @@ -541,8 +558,70 @@ static void seccomp_setup_syscall_filter(bool all_threads) {

#endif

#ifdef ISOLATION_GATEKEEPER_IMPL1
static void engage_legacy_sandboxing(void) {
dispatch_async(dispatch_get_main_queue(), ^{
#if defined(__clang__)// && defined(__llvm__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
char* errorbuf = "";
if (sandbox_init(kSBXProfileNoWriteExceptTemporary, SANDBOX_NAMED, &errorbuf)) {
DO_ONCE(rvvm_warn("Failed to enforce gatekeeper: %s!", errorbuf));
} else {
rvvm_info("Sandbox engaged successfully");
}
sandbox_free_error(errorbuf);
#if defined(__clang__)// && defined(__llvm__)
#pragma clang diagnostic pop
#endif
});
};
#endif

#ifdef ISOLATION_GATEKEEPER_IMPL2
static void engage_gatekeeper_sandboxing(void) {
dispatch_async(dispatch_get_main_queue(), ^{
char* errorbuf = "";
id NSHomeDirectory (void);
id NSFileManager (void);

/*
const char nsfm = ((const char* (*)(id,SEL) &objc_getClass) (NSFileManager), sel_registerName("defaultFileManager"));
const char sharedManager = ((const char* (*)(id,SEL)) &objc_msgSend) nsfm;
id paths = objc_msgSend(NSFileManager, sel_getUid("URLsForDirectory:inDomains:"), 9, 1);
// NSDocumentDirectory = 9
id documentsDirectory = objc_msgSend(paths, sel_getUid("lastObject"));
// Create the file path
id filePath = objc_msgSend(documentsDirectory, sel_getUid("URLByAppendingPathComponent:"), objc_msgSend(objc_getClass("NSString"), sel_getUid("stringWithUTF8String:"), "your_file.txt"));
// Read the file
id fileContents = objc_msgSend(objc_getClass("NSString"), sel_getUid("stringWithContentsOfURL:encoding:error:"), filePath, 4, NULL); // NSUTF8StringEncoding = 4
// id NSString (char *path); // = @"/path/to/your/file.txt";
// id fileHandleForWritingAtPath (char *);

// For more info see: https://opensource.apple.com/source/objc4/objc4-551.1/runtime/message.h.auto.html
// Parameters are passed as an array containing key,value,buff.
// TODO: proper profile for rvvm and rvjit
*/
int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
const char profile[] = "(version 3)(allow default)(allow file-read-data file-read-metadata (subpath \"/opt/homebrew/Cellar/sdl2/\"))(allow process-exec)";
const char* restrict_dir = ((const char* (*)(id,SEL, char *)) &objc_msgSend) (NSHomeDirectory(), sel_registerName("UTF8String"), errorbuf);
const char* parameters[] = { "USER_HOME_DIR", restrict_dir, NULL };

rvvm_debug("Attempting to sandbox...\nProfile is: %s restrict_dir is: %s", profile, restrict_dir);

if (sandbox_init_with_parameters(profile, 0, parameters, &errorbuf)) {
rvvm_warn("Failed to enforce gatekeeper sandbox: %s!", errorbuf);
} else {
rvvm_info("Sandbox engaged successfully");
};
});
};
#endif

void rvvm_restrict_this_thread(void)
{
rvvm_debug("We hit thread sandboxing stage...");
drop_root_user();
drop_thread_caps();
#if defined(ISOLATION_SECCOMP_IMPL) && !defined(SANITIZERS_PRESENT)
Expand All @@ -553,6 +632,7 @@ void rvvm_restrict_this_thread(void)

PUBLIC void rvvm_restrict_process(void)
{
rvvm_debug("We hit process sandboxing stage...");
drop_root_user();
drop_thread_caps();
#if defined(SANITIZERS_PRESENT)
Expand All @@ -563,5 +643,10 @@ PUBLIC void rvvm_restrict_process(void)
if (pledge("stdio inet tty ioctl dns audio drm vmm error", "")) {
DO_ONCE(rvvm_warn("Failed to enforce pledge: %s!", strerror(errno)));
}
#elif defined(ISOLATION_GATEKEEPER_IMPL1)
rvvm_warn("Legacy isolation used, may not work in newer versions!");
engage_legacy_sandboxing();
#elif defined(ISOLATION_GATEKEEPER_IMPL2)
engage_gatekeeper_sandboxing();
#endif
}