Add app group, share keychain. Take a first pass at file migration to shared data directory.

This commit is contained in:
Matthew Chen 2017-11-28 13:03:38 -05:00
parent 1ccf5132c1
commit cd11ec5698
8 changed files with 221 additions and 39 deletions

View File

@ -2052,6 +2052,9 @@
LastSwiftMigration = 0800; LastSwiftMigration = 0800;
ProvisioningStyle = Automatic; ProvisioningStyle = Automatic;
SystemCapabilities = { SystemCapabilities = {
com.apple.ApplicationGroups.iOS = {
enabled = 1;
};
com.apple.DataProtection = { com.apple.DataProtection = {
enabled = 1; enabled = 1;
}; };
@ -2061,6 +2064,9 @@
com.apple.InterAppAudio = { com.apple.InterAppAudio = {
enabled = 0; enabled = 0;
}; };
com.apple.Keychain = {
enabled = 1;
};
com.apple.Push = { com.apple.Push = {
enabled = 1; enabled = 1;
}; };

View File

@ -4,6 +4,8 @@
<dict> <dict>
<key>aps-environment</key> <key>aps-environment</key>
<string>development</string> <string>development</string>
<key>com.apple.developer.default-data-protection</key>
<string>NSFileProtectionComplete</string>
<key>com.apple.developer.icloud-container-identifiers</key> <key>com.apple.developer.icloud-container-identifiers</key>
<array> <array>
<string>iCloud.$(CFBundleIdentifier)</string> <string>iCloud.$(CFBundleIdentifier)</string>
@ -16,5 +18,13 @@
<array> <array>
<string>iCloud.$(CFBundleIdentifier)</string> <string>iCloud.$(CFBundleIdentifier)</string>
</array> </array>
<key>com.apple.security.application-groups</key>
<array>
<string>group.org.whispersystems.signal.group</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)org.whispersystems.signal</string>
</array>
</dict> </dict>
</plist> </plist>

View File

@ -1389,9 +1389,17 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
static NSString *profileAvatarsDirPath = nil; static NSString *profileAvatarsDirPath = nil;
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
NSString *documentsPath = NSString *documentDirPath = [OWSFileSystem appDocumentDirectoryPath];
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *sharedDataDirPath = [OWSFileSystem appSharedDataDirectoryPath];
profileAvatarsDirPath = [documentsPath stringByAppendingPathComponent:@"ProfileAvatars"];
NSString *oldProfileAvatarsDirPath = [documentDirPath stringByAppendingPathComponent:@"ProfileAvatars"];
NSString *newProfileAvatarsDirPath = [sharedDataDirPath stringByAppendingPathComponent:@"ProfileAvatars"];
[OWSFileSystem moveAppFilePath:oldProfileAvatarsDirPath
sharedDataFilePath:newProfileAvatarsDirPath
exceptionName:@"ProfileManagerCouldNotMigrateProfileDirectory"];
profileAvatarsDirPath = newProfileAvatarsDirPath;
BOOL isDirectory; BOOL isDirectory;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:profileAvatarsDirPath isDirectory:&isDirectory]; BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:profileAvatarsDirPath isDirectory:&isDirectory];

View File

@ -185,9 +185,17 @@ NS_ASSUME_NONNULL_BEGIN
static NSString *attachmentsFolder = nil; static NSString *attachmentsFolder = nil;
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
NSString *documentsPath = NSString *documentDirPath = [OWSFileSystem appDocumentDirectoryPath];
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *sharedDataDirPath = [OWSFileSystem appSharedDataDirectoryPath];
attachmentsFolder = [documentsPath stringByAppendingPathComponent:@"Attachments"];
NSString *oldAttachmentsDirPath = [documentDirPath stringByAppendingPathComponent:@"Attachments"];
NSString *newAttachmentsDirPath = [sharedDataDirPath stringByAppendingPathComponent:@"Attachments"];
[OWSFileSystem moveAppFilePath:oldAttachmentsDirPath
sharedDataFilePath:newAttachmentsDirPath
exceptionName:@"CouldNotMigrateAttachmentsDirectory"];
attachmentsFolder = newAttachmentsDirPath;
BOOL isDirectory; BOOL isDirectory;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:attachmentsFolder isDirectory:&isDirectory]; BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:attachmentsFolder isDirectory:&isDirectory];

View File

@ -63,10 +63,6 @@ NS_ASSUME_NONNULL_BEGIN
@property (nullable, nonatomic, readonly) YapDatabaseConnection *dbReadConnection; @property (nullable, nonatomic, readonly) YapDatabaseConnection *dbReadConnection;
@property (nullable, nonatomic, readonly) YapDatabaseConnection *dbReadWriteConnection; @property (nullable, nonatomic, readonly) YapDatabaseConnection *dbReadWriteConnection;
#pragma mark - Utilities
- (void)protectFolderAtPath:(NSString *)path;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@ -24,13 +24,18 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
NSString *const TSStorageManagerExceptionNameDatabasePasswordInaccessible = @"TSStorageManagerExceptionNameDatabasePasswordInaccessible"; NSString *const TSStorageManagerExceptionName_DatabasePasswordInaccessible
NSString *const TSStorageManagerExceptionNameDatabasePasswordInaccessibleWhileBackgrounded = = @"TSStorageManagerExceptionName_DatabasePasswordInaccessible";
@"TSStorageManagerExceptionNameDatabasePasswordInaccessibleWhileBackgrounded"; NSString *const TSStorageManagerExceptionName_DatabasePasswordInaccessibleWhileBackgrounded
NSString *const TSStorageManagerExceptionNameDatabasePasswordUnwritable = @"TSStorageManagerExceptionNameDatabasePasswordUnwritable"; = @"TSStorageManagerExceptionName_DatabasePasswordInaccessibleWhileBackgrounded";
NSString *const TSStorageManagerExceptionNameNoDatabase = @"TSStorageManagerExceptionNameNoDatabase"; NSString *const TSStorageManagerExceptionName_DatabasePasswordUnwritable
= @"TSStorageManagerExceptionName_DatabasePasswordUnwritable";
NSString *const TSStorageManagerExceptionName_NoDatabase = @"TSStorageManagerExceptionName_NoDatabase";
NSString *const TSStorageManagerExceptionName_CouldNotMoveDatabaseFile
= @"TSStorageManagerExceptionName_CouldNotMoveDatabaseFile";
NSString *const TSStorageManagerExceptionName_CouldNotCreateDatabaseDirectory
= @"TSStorageManagerExceptionName_CouldNotCreateDatabaseDirectory";
static const NSString *const databaseName = @"Signal.sqlite";
static NSString *keychainService = @"TSKeyChainService"; static NSString *keychainService = @"TSKeyChainService";
static NSString *keychainDBPassAccount = @"TSDatabasePass"; static NSString *keychainDBPassAccount = @"TSDatabasePass";
@ -231,7 +236,7 @@ void setDatabaseInitialized()
// Sleep to give analytics events time to be delivered. // Sleep to give analytics events time to be delivered.
[NSThread sleepForTimeInterval:15.0f]; [NSThread sleepForTimeInterval:15.0f];
[NSException raise:TSStorageManagerExceptionNameNoDatabase format:@"Failed to initialize database."]; [NSException raise:TSStorageManagerExceptionName_NoDatabase format:@"Failed to initialize database."];
} }
OWSSingletonAssert(); OWSSingletonAssert();
@ -253,6 +258,7 @@ void setDatabaseInitialized()
options.cipherKeyBlock = ^{ options.cipherKeyBlock = ^{
return databasePassword; return databasePassword;
}; };
options.enableMultiProcessSupport = YES;
#ifdef DEBUG #ifdef DEBUG
_database = [[OWSDatabase alloc] initWithPath:[self dbPath] _database = [[OWSDatabase alloc] initWithPath:[self dbPath]
@ -350,9 +356,14 @@ void setDatabaseInitialized()
} }
- (void)protectSignalFiles { - (void)protectSignalFiles {
[OWSFileSystem protectFolderAtPath:[self dbPath]]; // The old database location was in the Document directory,
[OWSFileSystem protectFolderAtPath:[[self dbPath] stringByAppendingString:@"-shm"]]; // so protect the database files individually.
[OWSFileSystem protectFolderAtPath:[[self dbPath] stringByAppendingString:@"-wal"]]; [OWSFileSystem protectFolderAtPath:[self oldDatabaseFilePath]];
[OWSFileSystem protectFolderAtPath:[self oldDatabaseFilePath_SHM]];
[OWSFileSystem protectFolderAtPath:[self oldDatabaseFilePath_WAL]];
// Protect the entire new database directory.
[OWSFileSystem protectFolderAtPath:[self newDatabaseDirPath]];
} }
- (nullable YapDatabaseConnection *)newDatabaseConnection - (nullable YapDatabaseConnection *)newDatabaseConnection
@ -364,33 +375,118 @@ void setDatabaseInitialized()
return FALSE; return FALSE;
} }
- (BOOL)dbExists { - (NSString *)oldDatabaseDirPath
return [[NSFileManager defaultManager] fileExistsAtPath:[self dbPath]]; {
return [OWSFileSystem appDocumentDirectoryPath];
} }
- (NSString *)dbPath { - (NSString *)newDatabaseDirPath
NSString *databasePath; {
NSString *databaseDirPath = [[OWSFileSystem appSharedDataDirectoryPath] stringByAppendingPathComponent:@"database"];
NSFileManager *fileManager = [NSFileManager defaultManager]; NSFileManager *fileManager = [NSFileManager defaultManager];
#if TARGET_OS_IPHONE if (![fileManager fileExistsAtPath:databaseDirPath]) {
NSURL *fileURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; NSError *_Nullable error;
NSString *path = [fileURL path]; BOOL success = [fileManager createDirectoryAtPath:databaseDirPath
databasePath = [path stringByAppendingPathComponent:databaseName]; withIntermediateDirectories:NO
#elif TARGET_OS_MAC attributes:nil
error:&error];
if (!success || error) {
NSString *errorDescription =
[NSString stringWithFormat:@"%@ Could not create new database directory: %@, error: %@",
self.logTag,
databaseDirPath,
error];
OWSFail(@"%@", errorDescription);
[NSException raise:TSStorageManagerExceptionName_CouldNotCreateDatabaseDirectory
format:@"%@", errorDescription];
}
}
return databaseDirPath;
}
NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier]; - (NSString *)databaseFilename
NSArray *urlPaths = [fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask]; {
return @"Signal.sqlite";
}
NSURL *appDirectory = [[urlPaths objectAtIndex:0] URLByAppendingPathComponent:bundleID isDirectory:YES]; - (NSString *)databaseFilename_SHM
{
return [self.databaseFilename stringByAppendingString:@"-shm"];
}
if (![fileManager fileExistsAtPath:[appDirectory path]]) { - (NSString *)databaseFilename_WAL
[fileManager createDirectoryAtURL:appDirectory withIntermediateDirectories:NO attributes:nil error:nil]; {
return [self.databaseFilename stringByAppendingString:@"-wal"];
}
- (NSString *)oldDatabaseFilePath
{
return [self.oldDatabaseDirPath stringByAppendingPathComponent:self.databaseFilename];
}
- (NSString *)oldDatabaseFilePath_SHM
{
return [self.oldDatabaseDirPath stringByAppendingPathComponent:self.databaseFilename_SHM];
}
- (NSString *)oldDatabaseFilePath_WAL
{
return [self.oldDatabaseDirPath stringByAppendingPathComponent:self.databaseFilename_WAL];
}
- (NSString *)newDatabaseFilePath
{
return [self.newDatabaseDirPath stringByAppendingPathComponent:self.databaseFilename];
}
- (NSString *)newDatabaseFilePath_SHM
{
return [self.newDatabaseDirPath stringByAppendingPathComponent:self.databaseFilename_SHM];
}
- (NSString *)newDatabaseFilePath_WAL
{
return [self.newDatabaseDirPath stringByAppendingPathComponent:self.databaseFilename_WAL];
}
- (NSString *)dbPath
{
[OWSFileSystem moveAppFilePath:self.oldDatabaseFilePath
sharedDataFilePath:self.newDatabaseFilePath
exceptionName:TSStorageManagerExceptionName_CouldNotMoveDatabaseFile];
[OWSFileSystem moveAppFilePath:self.oldDatabaseFilePath_SHM
sharedDataFilePath:self.newDatabaseFilePath_SHM
exceptionName:TSStorageManagerExceptionName_CouldNotMoveDatabaseFile];
[OWSFileSystem moveAppFilePath:self.oldDatabaseFilePath_WAL
sharedDataFilePath:self.newDatabaseFilePath_WAL
exceptionName:TSStorageManagerExceptionName_CouldNotMoveDatabaseFile];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL hasAllNewFiles = ([fileManager fileExistsAtPath:self.newDatabaseFilePath] &&
[fileManager fileExistsAtPath:self.newDatabaseFilePath_SHM] &&
[fileManager fileExistsAtPath:self.newDatabaseFilePath_WAL]);
BOOL hasAnyNewFiles = ([fileManager fileExistsAtPath:self.newDatabaseFilePath] ||
[fileManager fileExistsAtPath:self.newDatabaseFilePath_SHM] ||
[fileManager fileExistsAtPath:self.newDatabaseFilePath_WAL]);
if (!hasAllNewFiles && !hasAnyNewFiles) {
for (NSString *filePath in @[
self.newDatabaseFilePath,
self.newDatabaseFilePath_SHM,
self.newDatabaseFilePath_WAL,
self.newDatabaseFilePath,
self.newDatabaseFilePath_SHM,
self.newDatabaseFilePath_WAL,
]) {
DDLogInfo(@"%@ Database file %@ exists %d", self.logTag, filePath, [fileManager fileExistsAtPath:filePath]);
}
OWSFail(@"%@ Incomplete set of database files.", self.logTag);
} }
databasePath = [appDirectory.filePathURL.absoluteString stringByAppendingPathComponent:databaseName]; DDLogError(@"databasePath: %@", self.newDatabaseFilePath);
#endif [DDLog flushLog];
return databasePath; return self.newDatabaseFilePath;
} }
+ (BOOL)isDatabasePasswordAccessible + (BOOL)isDatabasePasswordAccessible
@ -421,7 +517,7 @@ void setDatabaseInitialized()
// Presumably this happened in response to a push notification. It's possible that the keychain is corrupted // Presumably this happened in response to a push notification. It's possible that the keychain is corrupted
// but it could also just be that the user hasn't yet unlocked their device since our password is // but it could also just be that the user hasn't yet unlocked their device since our password is
// kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly // kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
[NSException raise:TSStorageManagerExceptionNameDatabasePasswordInaccessibleWhileBackgrounded [NSException raise:TSStorageManagerExceptionName_DatabasePasswordInaccessibleWhileBackgrounded
format:@"%@", errorDescription]; format:@"%@", errorDescription];
} }
@ -483,7 +579,7 @@ void setDatabaseInitialized()
// Sleep to give analytics events time to be delivered. // Sleep to give analytics events time to be delivered.
[NSThread sleepForTimeInterval:15.0f]; [NSThread sleepForTimeInterval:15.0f];
[NSException raise:TSStorageManagerExceptionNameDatabasePasswordUnwritable [NSException raise:TSStorageManagerExceptionName_DatabasePasswordUnwritable
format:@"Setting DB password failed with error: %@", keySetError]; format:@"Setting DB password failed with error: %@", keySetError];
} else { } else {
DDLogWarn(@"Succesfully set new DB password."); DDLogWarn(@"Succesfully set new DB password.");

View File

@ -10,6 +10,14 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)protectFolderAtPath:(NSString *)path; + (void)protectFolderAtPath:(NSString *)path;
+ (NSString *)appDocumentDirectoryPath;
+ (NSString *)appSharedDataDirectoryPath;
+ (void)moveAppFilePath:(NSString *)oldFilePath
sharedDataFilePath:(NSString *)newFilePath
exceptionName:(NSString *)exceptionName;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@ -28,6 +28,56 @@ NS_ASSUME_NONNULL_BEGIN
} }
} }
+ (NSString *)appDocumentDirectoryPath
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentDirectoryURL =
[[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
return [documentDirectoryURL path];
}
+ (NSString *)appSharedDataDirectoryPath
{
NSURL *groupContainerDirectoryURL = [[NSFileManager defaultManager]
containerURLForSecurityApplicationGroupIdentifier:@"group.org.whispersystems.signal.group"];
return [groupContainerDirectoryURL path];
}
+ (void)moveAppFilePath:(NSString *)oldFilePath
sharedDataFilePath:(NSString *)newFilePath
exceptionName:(NSString *)exceptionName
{
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:oldFilePath]) {
return;
}
if ([fileManager fileExistsAtPath:newFilePath]) {
DDLogError(@"");
return;
}
NSDate *startDate = [NSDate new];
NSError *_Nullable error;
BOOL success = [fileManager moveItemAtPath:oldFilePath toPath:newFilePath error:&error];
if (!success || error) {
NSString *errorDescription =
[NSString stringWithFormat:@"%@ Could not move database file from %@ to %@, error: %@",
self.logTag,
oldFilePath,
newFilePath,
error];
OWSFail(@"%@", errorDescription);
[NSException raise:exceptionName format:@"%@", errorDescription];
}
DDLogInfo(@"%@ Moving file or directory from %@ to %@ in: %f",
self.logTag,
oldFilePath,
newFilePath,
fabs([startDate timeIntervalSinceNow]));
}
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END