Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -69,10 +69,11 @@ OFSHA224Or256Hash.m \ OFSHA256Hash.m \ OFSHA384Hash.m \ OFSHA384Or512Hash.m \ OFSHA512Hash.m \ + OFSandbox.m \ OFScrypt.m \ OFSecureData.m \ OFSeekableStream.m \ OFSerialization.m \ OFSet.m \ @@ -192,11 +193,10 @@ OFPointValue.m \ OFPointerValue.m \ OFRangeCharacterSet.m \ OFRangeValue.m \ OFRectValue.m \ - OFSandbox.m \ OFSizeValue.m \ OFSubarray.m \ OFUTF8String.m \ ${LIBBASES_M} \ ${RUNTIME_AUTORELEASE_M} \ Index: src/OFApplication.h ================================================================== --- src/OFApplication.h +++ src/OFApplication.h @@ -198,11 +198,18 @@ */ @property OF_NULLABLE_PROPERTY (assign, nonatomic) id delegate; #ifdef OF_HAVE_SANDBOX +/** + * @brief The sandbox currently active for this application. + */ @property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFSandbox *activeSandbox; + +/** + * @brief The sandbox currently active for child processes of this application. + */ @property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFSandbox *activeSandboxForChildProcesses; #endif /** @@ -244,12 +251,41 @@ * @param status The status with which the application will terminate */ + (void)terminateWithStatus: (int)status OF_NO_RETURN; #ifdef OF_HAVE_SANDBOX -+ (void)of_activateSandbox: (OFSandbox *)sandbox; -+ (void)of_activateSandboxForChildProcesses: (OFSandbox *)sandbox; +/** + * @brief Activates the specified sandbox for the application. + * + * This is only available if `OF_HAVE_SANDBOX` is defined. + * + * @warning If you allow `exec()`, but do not call + * @ref activateSandboxForChildProcesses:, an `exec()`'d process does + * not have its permissions restricted! + * + * @note Once a sandbox has been activated, you cannot activate a different + * sandbox. You can however change the active sandbox and reactivate it. + * + * @param sandbox The sandbox to activate + */ ++ (void)activateSandbox: (OFSandbox *)sandbox; + +/** + * @brief Activates the specified sandbox for child processes of the + * application. + * + * This is only available if `OF_HAVE_SANDBOX` is defined. + * + * `unveiledPaths` on the sandbox must *not* be empty, otherwise an + * @ref OFInvalidArgumentException is raised. + * + * @note Once a sandbox has been activated, you cannot activate a different + * sandbox. You can however change the active sandbox and reactivate it. + * + * @param sandbox The sandbox to activate + */ ++ (void)activateSandboxForChildProcesses: (OFSandbox *)sandbox; #endif - (instancetype)init OF_UNAVAILABLE; /** @@ -272,12 +308,41 @@ * @param status The status with which the application will terminate */ - (void)terminateWithStatus: (int)status OF_NO_RETURN; #ifdef OF_HAVE_SANDBOX -- (void)of_activateSandbox: (OFSandbox *)sandbox; -- (void)of_activateSandboxForChildProcesses: (OFSandbox *)sandbox; +/** + * @brief Activates the specified sandbox for the application. + * + * This is only available if `OF_HAVE_SANDBOX` is defined. + * + * @warning If you allow `exec()`, but do not call + * @ref activateSandboxForChildProcesses:, an `exec()`'d process does + * not have its permissions restricted! + * + * @note Once a sandbox has been activated, you cannot activate a different + * sandbox. You can however change the active sandbox and reactivate it. + * + * @param sandbox The sandbox to activate + */ +- (void)activateSandbox: (OFSandbox *)sandbox; + +/** + * @brief Activates the specified sandbox for child processes of the + * application. + * + * This is only available if `OF_HAVE_SANDBOX` is defined. + * + * `unveiledPaths` on the sandbox must *not* be empty, otherwise an + * @ref OFInvalidArgumentException is raised. + * + * @note Once a sandbox has been activated, you cannot activate a different + * sandbox. You can however change the active sandbox and reactivate it. + * + * @param sandbox The sandbox to activate + */ +- (void)activateSandboxForChildProcesses: (OFSandbox *)sandbox; #endif @end #ifdef __cplusplus extern "C" { Index: src/OFApplication.m ================================================================== --- src/OFApplication.m +++ src/OFApplication.m @@ -193,18 +193,18 @@ OF_UNREACHABLE #endif } #ifdef OF_HAVE_SANDBOX -+ (void)of_activateSandbox: (OFSandbox *)sandbox -{ - [app of_activateSandbox: sandbox]; -} - -+ (void)of_activateSandboxForChildProcesses: (OFSandbox *)sandbox -{ - [app of_activateSandboxForChildProcesses: sandbox]; ++ (void)activateSandbox: (OFSandbox *)sandbox +{ + [app activateSandbox: sandbox]; +} + ++ (void)activateSandboxForChildProcesses: (OFSandbox *)sandbox +{ + [app activateSandboxForChildProcesses: sandbox]; } #endif - (instancetype)init { @@ -599,11 +599,11 @@ OF_UNREACHABLE } #ifdef OF_HAVE_SANDBOX -- (void)of_activateSandbox: (OFSandbox *)sandbox +- (void)activateSandbox: (OFSandbox *)sandbox { # ifdef OF_HAVE_PLEDGE void *pool = objc_autoreleasePoolPush(); OFStringEncoding encoding = [OFLocale encoding]; OFArray OF_GENERIC(OFSandboxUnveilPath) *unveiledPaths; @@ -644,11 +644,11 @@ if (_activeSandbox == nil) _activeSandbox = [sandbox retain]; # endif } -- (void)of_activateSandboxForChildProcesses: (OFSandbox *)sandbox +- (void)activateSandboxForChildProcesses: (OFSandbox *)sandbox { # ifdef OF_HAVE_PLEDGE void *pool = objc_autoreleasePoolPush(); const char *promises; Index: src/OFSandbox.h ================================================================== --- src/OFSandbox.h +++ src/OFSandbox.h @@ -14,17 +14,28 @@ */ #import "OFObject.h" OF_ASSUME_NONNULL_BEGIN + +/** @file */ @class OFArray OF_GENERIC(ObjectType); @class OFMutableArray OF_GENERIC(ObjectType); @class OFPair OF_GENERIC(FirstType, SecondType); +/** + * @brief An @ref OFPair for a path to unveil, with the first string being the + * path and the second the permissions. + */ typedef OFPair OF_GENERIC(OFString *, OFString *) *OFSandboxUnveilPath; +/** + * @class OFSandbox OFSandbox.h ObjFW/OFSandbox.h + * + * @brief A class which describes a sandbox for the application. + */ @interface OFSandbox: OFObject { unsigned int _allowsStdIO: 1; unsigned int _allowsReadingFiles: 1; unsigned int _allowsWritingFiles: 1; @@ -59,46 +70,197 @@ @public size_t _unveiledPathsIndex; OF_RESERVE_IVARS(OFSandbox, 4) } +/** + * @brief Allows IO operations on previously allocated file descriptors. + */ @property (nonatomic) bool allowsStdIO; + +/** + * @brief Allows read access to the file system. + */ @property (nonatomic) bool allowsReadingFiles; + +/** + * @brief Allows write access to the file system. + */ @property (nonatomic) bool allowsWritingFiles; + +/** + * @brief Allows creating files in the file system. + */ @property (nonatomic) bool allowsCreatingFiles; + +/** + * @brief Allows creating special files in the file system. + */ @property (nonatomic) bool allowsCreatingSpecialFiles; + +/** + * @brief Allows creating, reading and writing temporary files in `/tmp`. + */ @property (nonatomic) bool allowsTemporaryFiles; + +/** + * @brief Allows using IP sockets. + */ @property (nonatomic) bool allowsIPSockets; + +/** + * @brief Allows multicast sockets. + */ @property (nonatomic) bool allowsMulticastSockets; + +/** + * @brief Allows explicit changes to file attributes. + */ @property (nonatomic) bool allowsChangingFileAttributes; + +/** + * @brief Allows changing ownership of files. + */ @property (nonatomic) bool allowsFileOwnerChanges; + +/** + * @brief Allows file locks. + */ @property (nonatomic) bool allowsFileLocks; + +/** + * @brief Allows UNIX sockets. + */ @property (nonatomic) bool allowsUNIXSockets; + +/** + * @brief Allows syscalls necessary for DNS lookups. + */ @property (nonatomic) bool allowsDNS; + +/** + * @brief Allows to look up users and groups. + */ @property (nonatomic) bool allowsUserDatabaseReading; + +/** + * @brief Allows sending file descriptors via sendmsg(). + */ @property (nonatomic) bool allowsFileDescriptorSending; + +/** + * @brief Allows receiving file descriptors via recvmsg(). + */ @property (nonatomic) bool allowsFileDescriptorReceiving; + +/** + * @brief Allows MTIOCGET and MTIOCTOP operations on tape devices. + */ @property (nonatomic) bool allowsTape; + +/** + * @brief Allows read-write operations and ioctls on the TTY. + */ @property (nonatomic) bool allowsTTY; + +/** + * @brief Allows various process relationshop operations. + */ @property (nonatomic) bool allowsProcessOperations; + +/** + * @brief Allows execve(). + */ @property (nonatomic) bool allowsExec; + +/** + * @brief Allows PROT_EXEC for `mmap()` and `mprotect()`. + */ @property (nonatomic) bool allowsProtExec; + +/** + * @brief Allows `settime()`. + */ @property (nonatomic) bool allowsSetTime; + +/** + * @brief Allows introspection of processes on the system. + */ @property (nonatomic) bool allowsPS; + +/** + * @brief Allows introspection of the system's virtual memory. + */ @property (nonatomic) bool allowsVMInfo; + +/** + * @brief Allows changing the rights of process, for example the UID. + */ @property (nonatomic) bool allowsChangingProcessRights; + +/** + * @brief Allows certain ioctls on the PF device. + */ @property (nonatomic) bool allowsPF; + +/** + * @brief Allows certain ioctls on audio devices. + */ @property (nonatomic) bool allowsAudio; + +/** + * @brief Allows BIOCGSTATS to collect statistics from a BPF device. + */ @property (nonatomic) bool allowsBPF; + +/** + * @brief Allows unveiling more paths. + */ @property (nonatomic) bool allowsUnveil; + +/** + * @brief Returns errors instead of killing the process. + */ @property (nonatomic) bool returnsErrors; + #ifdef OF_HAVE_PLEDGE +/** + * The string for OpenBSD's pledge() call. + * + * @warning Only available on systems with the pledge() call! + */ @property (readonly, nonatomic) OFString *pledgeString; #endif + +/** + * @brief A list of unveiled paths. + */ @property (readonly, nonatomic) OFArray OF_GENERIC(OFSandboxUnveilPath) *unveiledPaths; +/** + * @brief Create a new, autorelease OFSandbox. + */ + (instancetype)sandbox; + +/** + * @brief "Unveils" the specified path, meaning that it becomes visible from + * the sandbox with the specified permissions. + * + * @param path The path to unveil + * @param permissions The permissions for the path. The following permissions + * can be combined: + * Permission | Description + * -----------|-------------------- + * r | Make the path available for reading, like + * | @ref allowsReadingFiles + * w | Make the path available for writing, like + * | @ref allowsWritingFiles + * x | Make the path available for executing, like + * | @ref allowsExec + * c | Make the path available for creation and + * | deletion, like @ref allowsCreatingFiles + */ - (void)unveilPath: (OFString *)path permissions: (OFString *)permissions; @end OF_ASSUME_NONNULL_END Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -126,10 +126,11 @@ #import "OFSystemInfo.h" #import "OFLocale.h" #import "OFOptionsParser.h" #import "OFTimer.h" #import "OFRunLoop.h" +#import "OFSandbox.h" #ifdef OF_WINDOWS # import "OFWindowsRegistryKey.h" #endif @@ -209,10 +210,11 @@ #import "OFRemoveItemFailedException.h" #ifdef OF_HAVE_SOCKETS # import "OFResolveHostFailedException.h" #endif #import "OFRetrieveItemAttributesFailedException.h" +#import "OFSandboxActivationFailedException.h" #import "OFSeekFailedException.h" #import "OFSetItemAttributesFailedException.h" #import "OFSetOptionFailedException.h" #ifdef OF_WINDOWS # import "OFSetWindowsRegistryValueFailedException.h" Index: src/exceptions/Makefile ================================================================== --- src/exceptions/Makefile +++ src/exceptions/Makefile @@ -31,10 +31,11 @@ OFOutOfRangeException.m \ OFReadFailedException.m \ OFReadOrWriteFailedException.m \ OFRemoveItemFailedException.m \ OFRetrieveItemAttributesFailedException.m \ + OFSandboxActivationFailedException.m \ OFSeekFailedException.m \ OFSetItemAttributesFailedException.m \ OFSetOptionFailedException.m \ OFStillLockedException.m \ OFTruncatedDataException.m \ @@ -74,12 +75,10 @@ OFDeleteWindowsRegistryValueFailedException.m \ OFGetWindowsRegistryValueFailedException.m \ OFOpenWindowsRegistryKeyFailedException.m \ OFSetWindowsRegistryValueFailedException.m -INCLUDES := ${SRCS:.m=.h} - -SRCS += OFSandboxActivationFailedException.m +INCLUDES = ${SRCS:.m=.h} include ../../buildsys.mk CPPFLAGS += -I. -I.. -I../.. -I../runtime Index: src/exceptions/OFSandboxActivationFailedException.h ================================================================== --- src/exceptions/OFSandboxActivationFailedException.h +++ src/exceptions/OFSandboxActivationFailedException.h @@ -17,22 +17,53 @@ OF_ASSUME_NONNULL_BEGIN @class OFSandbox; +/** + * @class OFSandboxActivationFailedException \ + * OFSandboxActivationFailedException.h \ + * ObjFW/OFSandboxActivationFailedException.h + * + * @brief An exception indicating that sandboxing the process failed. + */ @interface OFSandboxActivationFailedException: OFException { OFSandbox *_sandbox; int _errNo; } +/** + * @brief The sandbox which could not be activated. + */ @property (readonly, nonatomic) OFSandbox *sandbox; + +/** + * @brief The errno of the error that occurred. + */ @property (readonly, nonatomic) int errNo; + (instancetype)exception OF_UNAVAILABLE; + +/** + * @brief Creates a new, autoreleased sandboxing failed exception. + * + * @param sandbox The sandbox which could not be activated + * @param errNo The errno of the error that occurred + * @return A new, autoreleased sandboxing failed exception + */ + (instancetype)exceptionWithSandbox: (OFSandbox *)sandbox errNo: (int)errNo; + - (instancetype)init OF_UNAVAILABLE; + +/** + * @brief Initializes an already allocated sandboxing failed exception. + * + * @param sandbox The sandbox which could not be activated + * @param errNo The errno of the error that occurred + * @return An initialized sandboxing failed exception + */ - (instancetype)initWithSandbox: (OFSandbox *)sandbox errNo: (int)errNo OF_DESIGNATED_INITIALIZER; @end OF_ASSUME_NONNULL_END Index: utils/ofarc/OFArc.m ================================================================== --- utils/ofarc/OFArc.m +++ utils/ofarc/OFArc.m @@ -180,11 +180,11 @@ sandbox.allowsChangingFileAttributes = true; sandbox.allowsUserDatabaseReading = true; /* Dropped after parsing options */ sandbox.allowsUnveil = true; - [OFApplication of_activateSandbox: sandbox]; + [OFApplication activateSandbox: sandbox]; #endif #ifndef OF_AMIGAOS [OFLocale addLanguageDirectory: @LANGUAGE_DIR]; #else @@ -325,11 +325,11 @@ for (OFString *path in files) [sandbox unveilPath: path permissions: @"r"]; sandbox.allowsUnveil = false; - [OFApplication of_activateSandbox: sandbox]; + [OFApplication activateSandbox: sandbox]; #endif archive = [self openArchiveWithPath: remainingArguments.firstObject type: type @@ -346,11 +346,11 @@ if (![remainingArguments.firstObject isEqual: @"-"]) [sandbox unveilPath: remainingArguments.firstObject permissions: @"r"]; sandbox.allowsUnveil = false; - [OFApplication of_activateSandbox: sandbox]; + [OFApplication activateSandbox: sandbox]; #endif archive = [self openArchiveWithPath: remainingArguments.firstObject type: type @@ -367,11 +367,11 @@ if (![remainingArguments.firstObject isEqual: @"-"]) [sandbox unveilPath: remainingArguments.firstObject permissions: @"r"]; sandbox.allowsUnveil = false; - [OFApplication of_activateSandbox: sandbox]; + [OFApplication activateSandbox: sandbox]; #endif files = [remainingArguments objectsInRange: OFRangeMake(1, remainingArguments.count - 1)]; @@ -408,11 +408,11 @@ /* We need 'r' to change the directory to it. */ [sandbox unveilPath: path permissions: @"rwc"]; } sandbox.allowsUnveil = false; - [OFApplication of_activateSandbox: sandbox]; + [OFApplication activateSandbox: sandbox]; #endif archive = [self openArchiveWithPath: remainingArguments.firstObject type: type Index: utils/ofdns/OFDNS.m ================================================================== --- utils/ofdns/OFDNS.m +++ utils/ofdns/OFDNS.m @@ -109,11 +109,11 @@ OFSandbox *sandbox = [[OFSandbox alloc] init]; @try { sandbox.allowsStdIO = true; sandbox.allowsDNS = true; - [OFApplication of_activateSandbox: sandbox]; + [OFApplication activateSandbox: sandbox]; } @finally { [sandbox release]; } #endif Index: utils/ofhash/OFHash.m ================================================================== --- utils/ofhash/OFHash.m +++ utils/ofhash/OFHash.m @@ -131,11 +131,11 @@ for (OFString *path in optionsParser.remainingArguments) [sandbox unveilPath: path permissions: @"r"]; [sandbox unveilPath: @LANGUAGE_DIR permissions: @"r"]; - [OFApplication of_activateSandbox: sandbox]; + [OFApplication activateSandbox: sandbox]; } @finally { [sandbox release]; } #endif Index: utils/ofhttp/OFHTTP.m ================================================================== --- utils/ofhttp/OFHTTP.m +++ utils/ofhttp/OFHTTP.m @@ -444,11 +444,11 @@ sandbox.allowsUserDatabaseReading = true; sandbox.allowsTTY = true; /* Dropped after parsing options */ sandbox.allowsUnveil = true; - [OFApplication of_activateSandbox: sandbox]; + [OFApplication activateSandbox: sandbox]; #endif #ifndef OF_AMIGAOS [OFLocale addLanguageDirectory: @LANGUAGE_DIR]; #else @@ -538,11 +538,11 @@ /* In case we use ObjOpenSSL for https later */ [sandbox unveilPath: @"/etc/ssl" permissions: @"r"]; sandbox.allowsUnveil = false; - [OFApplication of_activateSandbox: sandbox]; + [OFApplication activateSandbox: sandbox]; #endif _outputPath = [outputPath copy]; _URLs = [optionsParser.remainingArguments copy];