Fix every installed app getting assigned the same data container

This commit is contained in:
opa334 2023-11-28 11:54:12 +01:00
parent 3fe3e7f241
commit 1699abd9ab
1 changed files with 82 additions and 54 deletions

View File

@ -562,29 +562,89 @@ int signApp(NSString* appPath)
NSLog(@"[signApp] failed to get static code, can't derive entitlements from %@, continuing anways...", mainExecutablePath); NSLog(@"[signApp] failed to get static code, can't derive entitlements from %@, continuing anways...", mainExecutablePath);
}*/ }*/
int (^signFile)(NSString *, NSDictionary *) = ^(NSString *filePath, NSDictionary *entitlements) { NSURL* fileURL;
NSLog(@"Checking %@", filePath); NSDirectoryEnumerator *enumerator;
// Due to how the new CT bug works, in order for data containers to work properly we need to add the
// com.apple.private.security.container-required=<bundle-identifier> entitlement to every binary inside a bundle
// For this we will want to first collect info about all the bundles in the app by seeking for Info.plist files and adding the ent to the main binary
enumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:appPath] includingPropertiesForKeys:nil options:0 errorHandler:nil];
while(fileURL = [enumerator nextObject])
{
NSString *filePath = fileURL.path;
if ([filePath.lastPathComponent isEqualToString:@"Info.plist"]) {
NSDictionary *infoDict = [NSDictionary dictionaryWithContentsOfFile:filePath];
if (!infoDict) continue;
NSString *bundleId = infoDict[@"CFBundleIdentifier"];
NSString *bundleExecutable = infoDict[@"CFBundleExecutable"];
if (!bundleId || !bundleExecutable) continue;
NSString *bundleMainExecutablePath = [[filePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:bundleExecutable];
if (![[NSFileManager defaultManager] fileExistsAtPath:bundleMainExecutablePath]) continue;
NSString *packageType = infoDict[@"CFBundlePackageType"];
// We don't care about frameworks (yet)
if ([packageType isEqualToString:@"FMWK"]) continue;
NSMutableDictionary *entitlementsToUse = dumpEntitlementsFromBinaryAtPath(bundleMainExecutablePath).mutableCopy;
if (isSameFile(bundleMainExecutablePath, mainExecutablePath)) {
// In the case where the main executable of the app currently has no entitlements at all
// We want to ensure it gets signed with fallback entitlements
// These mimic the entitlements that Xcodes gives every app it signs
if (!entitlementsToUse) {
entitlementsToUse = @{
@"application-identifier" : @"TROLLTROLL.*",
@"com.apple.developer.team-identifier" : @"TROLLTROLL",
@"get-task-allow" : (__bridge id)kCFBooleanTrue,
@"keychain-access-groups" : @[
@"TROLLTROLL.*",
@"com.apple.token"
],
}.mutableCopy;
}
}
if (!entitlementsToUse) entitlementsToUse = [NSMutableDictionary new];
NSObject *containerRequiredO = entitlementsToUse[@"com.apple.private.security.container-required"];
BOOL containerRequired = YES;
if (containerRequiredO && [containerRequiredO isKindOfClass:[NSNumber class]]) {
containerRequired = [(NSNumber *)containerRequiredO boolValue];
}
else if (containerRequiredO && [containerRequiredO isKindOfClass:[NSString class]]) {
// Keep whatever is in it if it's a string...
containerRequired = NO;
}
if (containerRequired) {
NSObject *noContainerO = entitlementsToUse[@"com.apple.private.security.no-container"];
BOOL noContainer = NO;
if (noContainerO && [noContainerO isKindOfClass:[NSNumber class]]) {
noContainer = [(NSNumber *)noContainerO boolValue];
}
if (!noContainer) {
entitlementsToUse[@"com.apple.private.security.container-required"] = bundleId;
}
}
signAdhoc(bundleMainExecutablePath, entitlementsToUse);
}
}
// All entitlement related issues should be fixed at this point, so all we need to do is sign the entire bundle
// And then apply the CoreTrust bypass to all executables
// XXX: This only works because we're using ldid at the moment and that recursively signs everything
signAdhoc(appPath, nil);
enumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:appPath] includingPropertiesForKeys:nil options:0 errorHandler:nil];
while(fileURL = [enumerator nextObject])
{
NSString *filePath = fileURL.path;
FAT *fat = fat_init_from_path(filePath.fileSystemRepresentation); FAT *fat = fat_init_from_path(filePath.fileSystemRepresentation);
if (fat) { if (fat) {
NSLog(@"%@ is binary", filePath); NSLog(@"%@ is binary", filePath);
fat_free(fat);
// First attempt ad hoc signing
int r = signAdhoc(filePath, entitlements);
if (r != 0) {
// If it doesn't work it's not a big deal, that usually happens when the binary had the bypass applied already (Don't ask me why)
NSLog(@"[%@] Adhoc signing failed with error code %d, continuing anyways...\n", filePath, r);
}
else {
NSLog(@"[%@] Adhoc signing worked!\n", filePath);
}
fat = fat_init_from_path(filePath.fileSystemRepresentation);
if (!fat) return 175; // This should never happen, if it does then everything is fucked
// Now apply CoreTrust bypass to best slice
MachO *machoForExtraction = fat_find_preferred_slice(fat); MachO *machoForExtraction = fat_find_preferred_slice(fat);
if (machoForExtraction) { if (machoForExtraction) {
// Extract best slice
NSString *tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString]; NSString *tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString];
MemoryStream *sliceStream = macho_get_stream(machoForExtraction); MemoryStream *sliceStream = macho_get_stream(machoForExtraction);
MemoryStream *sliceOutStream = file_stream_init_from_path(tmpPath.fileSystemRepresentation, 0, 0, FILE_STREAM_FLAG_WRITABLE | FILE_STREAM_FLAG_AUTO_EXPAND); MemoryStream *sliceOutStream = file_stream_init_from_path(tmpPath.fileSystemRepresentation, 0, 0, FILE_STREAM_FLAG_WRITABLE | FILE_STREAM_FLAG_AUTO_EXPAND);
@ -592,12 +652,10 @@ int signApp(NSString* appPath)
memory_stream_copy_data(sliceStream, 0, sliceOutStream, 0, memory_stream_get_size(sliceStream)); memory_stream_copy_data(sliceStream, 0, sliceOutStream, 0, memory_stream_get_size(sliceStream));
memory_stream_free(sliceOutStream); memory_stream_free(sliceOutStream);
// Now we have the single slice at tmpPath, which we will sign and apply the bypass, then copy over the original file // Now we have the best slice at tmpPath, which we will apply the bypass to, then copy it over the original file
// We loose all other slices doing that but they aren't a loss as they wouldn't run either way
NSLog(@"[%@] Adhoc signing...", filePath);
NSLog(@"[%@] Applying CoreTrust bypass...", filePath); NSLog(@"[%@] Applying CoreTrust bypass...", filePath);
r = apply_coretrust_bypass(tmpPath.fileSystemRepresentation); int r = apply_coretrust_bypass(tmpPath.fileSystemRepresentation);
if (r == 0) { if (r == 0) {
NSLog(@"[%@] Applied CoreTrust bypass!", filePath); NSLog(@"[%@] Applied CoreTrust bypass!", filePath);
} }
@ -614,39 +672,9 @@ int signApp(NSString* appPath)
} }
fat_free(fat); fat_free(fat);
} }
}
return 0; return 0;
};
NSURL* fileURL;
NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:appPath] includingPropertiesForKeys:nil options:0 errorHandler:nil];
while(fileURL = [enumerator nextObject])
{
NSString *filePath = fileURL.path;
if (isSameFile(filePath, mainExecutablePath)) {
// Skip main executable, we will sign it at the end
continue;
}
int r = signFile(filePath, nil);
if (r != 0) return r;
}
// In the case where the main executable currently has no entitlements at all
// We want to ensure it gets signed with fallback entitlements
// These mimic the entitlements that Xcodes gives every app it signs
NSDictionary *entitlementsToUse = nil;
NSDictionary* mainExecutableEntitlements = dumpEntitlementsFromBinaryAtPath(mainExecutablePath);
if (!mainExecutableEntitlements) {
entitlementsToUse = @{
@"application-identifier" : @"TROLLTROLL.*",
@"com.apple.developer.team-identifier" : @"TROLLTROLL",
@"get-task-allow" : (__bridge id)kCFBooleanTrue,
@"keychain-access-groups" : @[
@"TROLLTROLL.*",
@"com.apple.token"
],
};
}
return signFile(mainExecutablePath, entitlementsToUse);
} }
#endif #endif