Filza 27.2 worse_query: public and developer builds
This commit is contained in:
commit
91a6adf723
267 files changed
+42015
No files matched your search
+254
@@ -0,0 +1,254 @@
|
||||
#import "MCMBridge.h"
|
||||
|
||||
#import <dlfcn.h>
|
||||
#import <stdlib.h>
|
||||
#import <xpc/xpc.h>
|
||||
|
||||
typedef void *(*MCMQueryCreate)(void);
|
||||
typedef void (*MCMQuerySetU64)(void *, uint64_t);
|
||||
typedef void (*MCMQuerySetXPC)(void *, xpc_object_t);
|
||||
typedef void (*MCMQuerySetCString)(void *, const char *);
|
||||
typedef void *(*MCMQueryGetPointer)(void *);
|
||||
typedef bool (*MCMQueryIterate)(void *, bool (^)(void *));
|
||||
typedef void (*MCMQueryFree)(void *);
|
||||
typedef const char *(*MCMObjectGetPath)(void *);
|
||||
typedef const char *(*MCMObjectGetIdentifier)(void *);
|
||||
typedef void *(*MCMObjectCopy)(void *);
|
||||
typedef char *(*MCMObjectCopyToken)(void *);
|
||||
typedef bool (*MCMObjectActivate)(void *, bool);
|
||||
typedef void (*MCMObjectFree)(void *);
|
||||
typedef int (*MCMErrorGetInt)(void *);
|
||||
typedef const char *(*MCMErrorGetString)(void *);
|
||||
|
||||
typedef struct {
|
||||
void *handle;
|
||||
MCMQueryCreate queryCreate;
|
||||
MCMQuerySetU64 querySetClass;
|
||||
MCMQuerySetXPC querySetIdentifiers;
|
||||
MCMQuerySetXPC querySetGroupIdentifiers;
|
||||
MCMQuerySetU64 querySetFlags;
|
||||
MCMQuerySetU64 querySetPart;
|
||||
MCMQuerySetCString querySetPartDomain;
|
||||
MCMQueryGetPointer queryGetSingle;
|
||||
MCMQueryGetPointer queryGetLastError;
|
||||
MCMQueryIterate queryIterate;
|
||||
MCMQueryFree queryFree;
|
||||
MCMObjectGetPath objectGetPath;
|
||||
MCMObjectGetIdentifier objectGetIdentifier;
|
||||
MCMObjectCopy objectCopy;
|
||||
MCMObjectCopyToken objectCopyToken;
|
||||
MCMObjectActivate objectActivate;
|
||||
MCMObjectFree objectFree;
|
||||
MCMErrorGetInt errorGetPOSIX;
|
||||
MCMErrorGetString errorGetMessage;
|
||||
} MCMAPI;
|
||||
|
||||
static MCMAPI *MCMSharedAPI(void)
|
||||
{
|
||||
static MCMAPI api;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
api.handle = dlopen("/usr/lib/system/libsystem_containermanager.dylib",
|
||||
RTLD_NOW | RTLD_LOCAL);
|
||||
void *handle = api.handle != NULL ? api.handle : RTLD_DEFAULT;
|
||||
#define LOAD(field, symbol) api.field = (__typeof(api.field))dlsym(handle, symbol)
|
||||
LOAD(queryCreate, "container_query_create");
|
||||
LOAD(querySetClass, "container_query_set_class");
|
||||
LOAD(querySetIdentifiers, "container_query_set_identifiers");
|
||||
LOAD(querySetGroupIdentifiers, "container_query_set_group_identifiers");
|
||||
LOAD(querySetFlags, "container_query_operation_set_flags");
|
||||
LOAD(querySetPart, "container_query_operation_set_part");
|
||||
LOAD(querySetPartDomain, "container_query_operation_set_part_domain");
|
||||
LOAD(queryGetSingle, "container_query_get_single_result");
|
||||
LOAD(queryGetLastError, "container_query_get_last_error");
|
||||
LOAD(queryIterate, "container_query_iterate_results_sync");
|
||||
LOAD(queryFree, "container_query_free");
|
||||
LOAD(objectGetPath, "container_object_get_path");
|
||||
LOAD(objectGetIdentifier, "container_object_get_identifier");
|
||||
LOAD(objectCopy, "container_object_copy");
|
||||
LOAD(objectCopyToken, "container_copy_sandbox_token");
|
||||
LOAD(objectActivate, "container_object_sandbox_extension_activate");
|
||||
LOAD(objectFree, "container_object_free");
|
||||
LOAD(errorGetPOSIX, "container_error_get_posix_errno");
|
||||
LOAD(errorGetMessage, "container_error_get_message");
|
||||
#undef LOAD
|
||||
});
|
||||
return &api;
|
||||
}
|
||||
|
||||
NSArray<NSString *> *MCMEnumerateIdentifiersForClass(
|
||||
uint64_t containerClass, NSUInteger limit, NSString **error)
|
||||
{
|
||||
MCMAPI *api = MCMSharedAPI();
|
||||
if (!MCMBridgeAvailable() || !api->queryIterate || !api->objectGetIdentifier || limit == 0) {
|
||||
if (error) *error = @"ContainerManager enumeration API unavailable";
|
||||
return @[];
|
||||
}
|
||||
void *query = api->queryCreate();
|
||||
if (!query) {
|
||||
if (error) *error = @"container_query_create returned NULL";
|
||||
return @[];
|
||||
}
|
||||
api->querySetClass(query, containerClass);
|
||||
// Metadata-only, no-create lookup. Extensions are requested individually
|
||||
// only after an identifier passes validation.
|
||||
api->querySetFlags(query, 0x100000000ULL);
|
||||
// iOS 18 does not export the part API. A new query already defaults to
|
||||
// part 0, so only set it when the newer API is present.
|
||||
if (api->querySetPart) api->querySetPart(query, 0);
|
||||
NSMutableOrderedSet<NSString *> *identifiers = [NSMutableOrderedSet orderedSet];
|
||||
BOOL iterated = api->queryIterate(query, ^bool(void *object) {
|
||||
const char *raw = object ? api->objectGetIdentifier(object) : NULL;
|
||||
NSString *identifier = raw ? [NSString stringWithUTF8String:raw] : nil;
|
||||
if (identifier.length) [identifiers addObject:identifier];
|
||||
return identifiers.count < limit;
|
||||
});
|
||||
if (!iterated && identifiers.count < limit) {
|
||||
void *queryError = api->queryGetLastError(query);
|
||||
int posix = queryError && api->errorGetPOSIX ? api->errorGetPOSIX(queryError) : 0;
|
||||
const char *message = queryError && api->errorGetMessage
|
||||
? api->errorGetMessage(queryError) : NULL;
|
||||
if (error) *error = [NSString stringWithFormat:@"enumeration denied posix=%d message=%s",
|
||||
posix, message ?: "unknown"];
|
||||
}
|
||||
api->queryFree(query);
|
||||
return identifiers.array;
|
||||
}
|
||||
|
||||
BOOL MCMBridgeAvailable(void)
|
||||
{
|
||||
MCMAPI *api = MCMSharedAPI();
|
||||
return api->queryCreate != NULL && api->querySetClass != NULL &&
|
||||
api->querySetIdentifiers != NULL && api->querySetGroupIdentifiers != NULL &&
|
||||
api->querySetFlags != NULL &&
|
||||
api->queryGetSingle != NULL && api->queryGetLastError != NULL &&
|
||||
api->queryFree != NULL && api->objectGetPath != NULL &&
|
||||
api->objectCopy != NULL && api->objectCopyToken != NULL &&
|
||||
api->objectActivate != NULL && api->objectFree != NULL;
|
||||
}
|
||||
|
||||
@interface MCMLease () {
|
||||
void *_query;
|
||||
void *_activation;
|
||||
}
|
||||
@property(nonatomic, readwrite) uint64_t containerClass;
|
||||
@property(nonatomic, copy, readwrite) NSString *identifier;
|
||||
@property(nonatomic, copy, readwrite) NSString *rootPath;
|
||||
@property(nonatomic, readwrite) BOOL groupIdentifier;
|
||||
@property(nonatomic, readwrite) BOOL tokenPresent;
|
||||
@property(nonatomic, readwrite) BOOL activated;
|
||||
@end
|
||||
|
||||
@implementation MCMLease
|
||||
|
||||
+ (instancetype)leaseForClass:(uint64_t)containerClass
|
||||
identifier:(NSString *)identifier
|
||||
group:(BOOL)group
|
||||
part:(uint64_t)part
|
||||
flags:(uint64_t)flags
|
||||
error:(NSString **)error
|
||||
{
|
||||
return [self leaseForClass:containerClass identifier:identifier group:group
|
||||
part:part partDomain:nil flags:flags error:error];
|
||||
}
|
||||
|
||||
+ (instancetype)leaseForClass:(uint64_t)containerClass
|
||||
identifier:(NSString *)identifier
|
||||
group:(BOOL)group
|
||||
part:(uint64_t)part
|
||||
partDomain:(NSString *)partDomain
|
||||
flags:(uint64_t)flags
|
||||
error:(NSString **)error
|
||||
{
|
||||
if (!MCMBridgeAvailable() || identifier.length == 0) {
|
||||
if (error) *error = @"ContainerManager bridge unavailable or identifier empty";
|
||||
return nil;
|
||||
}
|
||||
MCMAPI *api = MCMSharedAPI();
|
||||
void *query = api->queryCreate();
|
||||
if (query == NULL) {
|
||||
if (error) *error = @"container_query_create returned NULL";
|
||||
return nil;
|
||||
}
|
||||
api->querySetClass(query, containerClass);
|
||||
xpc_object_t value = xpc_string_create(identifier.UTF8String);
|
||||
if (group) api->querySetGroupIdentifiers(query, value);
|
||||
else api->querySetIdentifiers(query, value);
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
xpc_release(value);
|
||||
#endif
|
||||
api->querySetFlags(query, flags);
|
||||
if (part != 0 && !api->querySetPart) {
|
||||
if (error) *error = @"part API unavailable on this OS";
|
||||
api->queryFree(query);
|
||||
return nil;
|
||||
}
|
||||
if (api->querySetPart) api->querySetPart(query, part);
|
||||
if (partDomain.length != 0) {
|
||||
if (!api->querySetPartDomain) {
|
||||
if (error) *error = @"part-domain API unavailable";
|
||||
api->queryFree(query);
|
||||
return nil;
|
||||
}
|
||||
api->querySetPartDomain(query, partDomain.fileSystemRepresentation);
|
||||
}
|
||||
void *object = api->queryGetSingle(query);
|
||||
if (object == NULL) {
|
||||
void *queryError = api->queryGetLastError(query);
|
||||
int posix = queryError && api->errorGetPOSIX ? api->errorGetPOSIX(queryError) : 0;
|
||||
const char *message = queryError && api->errorGetMessage
|
||||
? api->errorGetMessage(queryError) : NULL;
|
||||
if (error) *error = [NSString stringWithFormat:@"lookup denied posix=%d message=%s",
|
||||
posix, message ?: "unknown"];
|
||||
api->queryFree(query);
|
||||
return nil;
|
||||
}
|
||||
const char *rawPath = api->objectGetPath(object);
|
||||
NSString *root = rawPath ? [NSString stringWithUTF8String:rawPath] : nil;
|
||||
if (root.length == 0 || !root.isAbsolutePath) {
|
||||
if (error) *error = @"MCM returned no absolute container path";
|
||||
api->queryFree(query);
|
||||
return nil;
|
||||
}
|
||||
if ([root isEqualToString:@"/var"] || [root hasPrefix:@"/var/"])
|
||||
root = [@"/private" stringByAppendingString:root];
|
||||
MCMLease *lease = [MCMLease new];
|
||||
lease->_query = query;
|
||||
lease.containerClass = containerClass;
|
||||
lease.identifier = identifier;
|
||||
lease.groupIdentifier = group;
|
||||
lease.rootPath = root;
|
||||
return lease;
|
||||
}
|
||||
|
||||
- (BOOL)activate:(NSString **)error
|
||||
{
|
||||
if (self.activated) return YES;
|
||||
if (!_query) {
|
||||
if (error) *error = @"lease invalidated";
|
||||
return NO;
|
||||
}
|
||||
MCMAPI *api = MCMSharedAPI();
|
||||
void *object = api->queryGetSingle(_query);
|
||||
_activation = object ? api->objectCopy(object) : NULL;
|
||||
char *token = _activation ? api->objectCopyToken(_activation) : NULL;
|
||||
self.tokenPresent = token && token[0] != '\0';
|
||||
free(token);
|
||||
self.activated = self.tokenPresent && api->objectActivate(_activation, false);
|
||||
if (!self.activated && error)
|
||||
*error = self.tokenPresent ? @"sandbox extension activation failed"
|
||||
: @"MCM object contained no sandbox token";
|
||||
return self.activated;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
MCMAPI *api = MCMSharedAPI();
|
||||
if (_activation) { api->objectFree(_activation); _activation = NULL; }
|
||||
if (_query) { api->queryFree(_query); _query = NULL; }
|
||||
self.activated = NO;
|
||||
}
|
||||
|
||||
- (void)dealloc { [self invalidate]; }
|
||||
|
||||
@end
|
||||
Reference in new issue
Block a user