trustdFileHelper.m   [plain text]

//
// trustdFileHelper.m
// trustdFileHelper
//
// Copyright © 2020 Apple Inc. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <Foundation/NSError_Private.h>
#include <sys/stat.h>

#include <utilities/SecCFWrappers.h>
#include <utilities/SecFileLocations.h>

#include "trust/trustd/trustdFileLocations.h"
#include "trust/trustd/trustdFileHelper/trustdFileHelper.h"

@implementation TrustdFileHelper

- (BOOL)changeOwnerOfValidFile:(NSString *)fileName error:(NSError **)error
{
__block BOOL result = YES;
__block NSError *localError = nil;
WithPathInRevocationInfoDirectory((__bridge CFStringRef)fileName, ^(const char *utf8String) {
int ret = chown(utf8String, TRUSTD_ROLE_ACCOUNT, TRUSTD_ROLE_ACCOUNT);
if (!(ret == 0)) {
int localErrno = errno;
localError = [NSError errorWithDomain:NSPOSIXErrorDomain code:localErrno
userInfo:@{NSLocalizedDescriptionKey : [NSString localizedStringWithFormat:@"failed to change owner of %s: %s", utf8String, strerror(localErrno)]}];
if (localErrno != ENOENT) { // missing file is not a failure
secerror("failed to change owner of %s: %s", utf8String, strerror(localErrno));
result = NO;
}
}
});
if (!result && error && !*error) {
*error = localError;
}
return result;
}

- (BOOL)fixValidPermissions:(NSError **)error
{
secdebug("helper", "fixing permissions on valid database");
NSString *baseFilename = @"valid.sqlite3";
BOOL result = [self changeOwnerOfValidFile:baseFilename error:error];
if (result) {
NSString *shmFile = [NSString stringWithFormat:@"%@-shm", baseFilename];
result = [self changeOwnerOfValidFile:shmFile error:error];
}
if (result) {
NSString *walFile = [NSString stringWithFormat:@"%@-wal", baseFilename];
result = [self changeOwnerOfValidFile:walFile error:error];
}
if (result) {
NSString *journalFile = [NSString stringWithFormat:@"%@-journal", baseFilename];
result = [self changeOwnerOfValidFile:journalFile error:error];
}
// Always change the replacement semaphore if it exists
if (![self changeOwnerOfValidFile:@".valid_replace" error:error]) {
result = NO;
}
return result;
}

- (BOOL)changePermissionsOfKeychainDirectoryFile:(NSString *)fileName error:(NSError **)error
{
__block BOOL result = YES;
__block NSError *localError = nil;
WithPathInKeychainDirectory((__bridge CFStringRef)fileName, ^(const char *utf8String) {
int ret = chmod(utf8String, 0644); // allow all users to read so that _trustd user can migrate contents
if (!(ret == 0)) {
int localErrno = errno;
localError = [NSError errorWithDomain:NSPOSIXErrorDomain code:localErrno
userInfo:@{NSLocalizedDescriptionKey : [NSString localizedStringWithFormat:@"failed to change permissions of %s: %s", utf8String, strerror(localErrno)]}];
if (localErrno != ENOENT) { // missing file is not a failure
secerror("failed to change permissions of %s: %s", utf8String, strerror(localErrno));
result = NO;
}
}
});
if (error && !*error) {
*error = localError;
}
return result;
}

- (BOOL)allowTrustdToReadFilesForMigration:(NSError **)error
{
secdebug("helper", "fixing permissions files that need migration");
BOOL result = [self changePermissionsOfKeychainDirectoryFile:@"TrustStore.sqlite3" error:error];
if (![self changePermissionsOfKeychainDirectoryFile:@"com.apple.security.exception_reset_counter.plist" error:error]) {
result = NO;
}
if (![self changePermissionsOfKeychainDirectoryFile:@"CTExceptions.plist" error:error]) {
result = NO;
}
if (![self changePermissionsOfKeychainDirectoryFile:@"CARevocation.plist" error:error]) {
result = NO;
}
if (![self changePermissionsOfKeychainDirectoryFile:@"TransparentConnectionPins.plist" error:error]) {
result = NO;
}
return result;
}

- (void)allowTrustdToWriteAnalyticsFiles
{
WithPathInKeychainDirectory(CFSTR("Analytics"), ^(const char *path) {
/* We need _securityd, _trustd, and root all to be able to write. They share no groups. */
mode_t permissions = 0777;
int ret = mkpath_np(path, permissions);
if (!(ret == 0 || ret == EEXIST)) {
secerror("could not create path: %s (%s)", path, strerror(ret));
}

ret = chmod(path, permissions);
if (!(ret == 0)) {
secerror("failed to change permissions of %s: %s", path, strerror(errno));
}
});
}

- (void)deleteSystemDbFiles:(CFStringRef)baseFilename
{
WithPathInDirectory(SecCopyURLForFileInSystemKeychainDirectory(baseFilename), ^(const char *utf8String) {
(void)remove(utf8String);
});
CFStringRef shmFile = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@-shm"), baseFilename);
WithPathInDirectory(SecCopyURLForFileInSystemKeychainDirectory(shmFile), ^(const char *utf8String) {
(void)remove(utf8String);
});
CFReleaseNull(shmFile);
CFStringRef walFile = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@-wal"), baseFilename);
WithPathInDirectory(SecCopyURLForFileInSystemKeychainDirectory(walFile), ^(const char *utf8String) {
(void)remove(utf8String);
});
CFReleaseNull(walFile);
CFStringRef journalFile = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@-journal"), baseFilename);
WithPathInDirectory(SecCopyURLForFileInSystemKeychainDirectory(journalFile), ^(const char *utf8String) {
(void)remove(utf8String);
});
CFReleaseNull(journalFile);
}

- (void)deleteSupplementalsAssetsDir
{
WithPathInDirectory(SecCopyURLForFileInSystemKeychainDirectory(CFSTR("SupplementalsAssets/OTAPKIContext.plist")), ^(const char *utf8String) {
(void)remove(utf8String);
});
WithPathInDirectory(SecCopyURLForFileInSystemKeychainDirectory(CFSTR("SupplementalsAssets/TrustedCTLogs.plist")), ^(const char *utf8String) {
(void)remove(utf8String);
});
WithPathInDirectory(SecCopyURLForFileInSystemKeychainDirectory(CFSTR("SupplementalsAssets/TrustedCTLogs_nonTLS.plist")), ^(const char *utf8String) {
(void)remove(utf8String);
});
WithPathInDirectory(SecCopyURLForFileInSystemKeychainDirectory(CFSTR("SupplementalsAssets/AnalyticsSamplingRates.plist")), ^(const char *utf8String) {
(void)remove(utf8String);
});
WithPathInDirectory(SecCopyURLForFileInSystemKeychainDirectory(CFSTR("SupplementalsAssets/AppleCertificateAuthorities.plist")), ^(const char *utf8String) {
(void)remove(utf8String);
});
WithPathInDirectory(SecCopyURLForFileInSystemKeychainDirectory(CFSTR("SupplementalsAssets")), ^(const char *utf8String) {
(void)remove(utf8String);
});
}

- (void)deleteOldFiles
{
secdebug("helper", "deleting /Library/Keychains/crls/valid.sqlite3");
[self deleteSystemDbFiles:CFSTR("crls/valid.sqlite3")];
WithPathInDirectory(SecCopyURLForFileInSystemKeychainDirectory(CFSTR("crls")), ^(const char *utf8String) {
secdebug("helper", "deleting crls directory");
(void)remove(utf8String);
});

secdebug("helper", "deleting /Library/Keychains/pinningrules.sqlite3");
[self deleteSystemDbFiles:CFSTR("pinningrules.sqlite3")];

secdebug("helper", "deleting SupplementalsAssets directory");
[self deleteSupplementalsAssetsDir];

secdebug("helper", "deleting CAIssuer cache");
#if TARGET_OS_IPHONE
WithPathInKeychainDirectory(CFSTR("caissuercache.sqlite3"), ^(const char *utf8String) {
(void)remove(utf8String);
});
#else // !TARGET_OS_IPHONE
WithPathInUserCacheDirectory(CFSTR("caissuercache.sqlite3"), ^(const char *utf8String) {
(void)remove(utf8String);
});
#endif // !TARGET_OS_IPHONE

#if TARGET_OS_IPHONE
secdebug("helper", "deleting OCSP cache");
[self deleteSystemDbFiles:CFSTR("ocspcache.sqlite3")];
#endif // TARGET_OS_IPHONE

}

- (void)fixFiles:(void (^)(BOOL, NSError*))reply
{
secdebug("ipc", "received trustd request to fix files");
NSError *error = nil;
[self deleteOldFiles];
#if TARGET_OS_OSX
if (![self fixValidPermissions:&error]) {
secerror("failed to fix Valid permissions for trustd");
reply(NO, error);
return;
}
#else // !TARGET_OS_OSX
secdebug("helper", "update Analytics directory permissions");
[self allowTrustdToWriteAnalyticsFiles];
if (![self allowTrustdToReadFilesForMigration:&error]) {
secerror("failed to change permissions so trustd can read files for migration");
reply(NO, error);
return;
}
#endif // !TARGET_OS_OSX
reply(YES, error);
}

@end