diff --git a/Makefile b/Makefile index 86949932..b67c9cd6 100644 --- a/Makefile +++ b/Makefile @@ -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 @@ -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 # diff --git a/src/bindings/macos_codesign/codesign.sh b/src/bindings/macos_codesign/codesign.sh index b13a64f1..b786a977 100644 --- a/src/bindings/macos_codesign/codesign.sh +++ b/src/bindings/macos_codesign/codesign.sh @@ -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 $@ diff --git a/src/bindings/macos_codesign/rvvm.entitlements b/src/bindings/macos_codesign/rvvm.entitlements new file mode 100644 index 00000000..c0fedb5e --- /dev/null +++ b/src/bindings/macos_codesign/rvvm.entitlements @@ -0,0 +1,26 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.library-validation + + com.apple.security.network.server + + com.apple.security.network.client + + com.apple.security.get-task-allow + + com.apple.security.cs.allow-jit + + com.apple.security.hypervisor + + com.apple.security.files.user-selected.read-write + + com.apple.security.temporary-exception.files.absolute-path.read-only + + /opt/homebrew/Cellar/sdl2/*/lib/libSDL2-2.0.0.dylib + + + diff --git a/src/bindings/macos_codesign/rvvm_debug.entitlements b/src/bindings/macos_codesign/rvvm_debug.entitlements index ea0bb78d..abe99f30 100644 --- a/src/bindings/macos_codesign/rvvm_debug.entitlements +++ b/src/bindings/macos_codesign/rvvm_debug.entitlements @@ -8,6 +8,7 @@ com.apple.security.cs.disable-executable-page-protection - com.apple.security.get-task-allow + com.apple.security.cs.allow-jit + diff --git a/src/bindings/macos_codesign/rvvm_isolement.entitlements b/src/bindings/macos_codesign/rvvm_isolement.entitlements deleted file mode 100644 index c1ce5e6a..00000000 --- a/src/bindings/macos_codesign/rvvm_isolement.entitlements +++ /dev/null @@ -1,14 +0,0 @@ - - - - - com.apple.security.cs.allow-dyld-environment-variables - - com.apple.security.get-task-allow - - com.apple.security.app-sandbox - - com.apple.security.inherit - - - diff --git a/src/bindings/macos_codesign/sandbox.sb b/src/bindings/macos_codesign/sandbox.sb new file mode 100644 index 00000000..4a0831f3 --- /dev/null +++ b/src/bindings/macos_codesign/sandbox.sb @@ -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) diff --git a/src/rvvm_isolation.c b/src/rvvm_isolation.c index 503fb0f4..f594382e 100644 --- a/src/rvvm_isolation.c +++ b/src/rvvm_isolation.c @@ -1,6 +1,6 @@ /* rvvm_isolation.c - Process & thread isolation -Copyright (C) 2024 LekKit +Copyright (C) 2024 LekKit foxxie 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 @@ -60,6 +60,23 @@ along with this program. If not, see . #define ISOLATION_PLEDGE_IMPL #endif +#if defined(__APPLE__) && (USE_EXPERIMENTAL_SHIT) && CHECK_INCLUDE(objc/objc-api.h, 0) +#include +#include +#if USE_ISOLATION == 1 +// Isolate with legacy +#include +#define ISOLATION_GATEKEEPER_IMPL1 +#elif USE_ISOLATION == 2 +// Isolate via MacOS Cocoa sandbox. +#include +#include +#include +#include +#define ISOLATION_GATEKEEPER_IMPL2 +#endif +#endif + #endif // Drop all the capabilities of the calling thread, prevent privilege escalation @@ -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) @@ -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) @@ -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 }