|
|
|
@ -1,6 +1,4 @@
|
|
|
|
|
//
|
|
|
|
|
// TSStorageManager.m
|
|
|
|
|
//
|
|
|
|
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
@ -23,6 +21,8 @@
|
|
|
|
|
NSString *const TSUIDatabaseConnectionDidUpdateNotification = @"TSUIDatabaseConnectionDidUpdateNotification";
|
|
|
|
|
|
|
|
|
|
NSString *const TSStorageManagerExceptionNameDatabasePasswordInaccessible = @"TSStorageManagerExceptionNameDatabasePasswordInaccessible";
|
|
|
|
|
NSString *const TSStorageManagerExceptionNameDatabasePasswordInaccessibleWhileBackgrounded =
|
|
|
|
|
@"TSStorageManagerExceptionNameDatabasePasswordInaccessibleWhileBackgrounded";
|
|
|
|
|
NSString *const TSStorageManagerExceptionNameDatabasePasswordUnwritable = @"TSStorageManagerExceptionNameDatabasePasswordUnwritable";
|
|
|
|
|
NSString *const TSStorageManagerExceptionNameNoDatabase = @"TSStorageManagerExceptionNameNoDatabase";
|
|
|
|
|
|
|
|
|
@ -270,7 +270,7 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
|
|
|
|
|
return databasePath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)databasePasswordAccessible
|
|
|
|
|
+ (BOOL)isDatabasePasswordAccessible
|
|
|
|
|
{
|
|
|
|
|
[SAMKeychain setAccessibilityType:kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly];
|
|
|
|
|
NSError *error;
|
|
|
|
@ -287,6 +287,17 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)backgroundedAppDatabasePasswordInaccessibleWithErrorDescription:(NSString *)errorDescription
|
|
|
|
|
{
|
|
|
|
|
OWSAssert([UIApplication sharedApplication].applicationState == UIApplicationStateBackground);
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
// kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
|
|
|
|
|
[NSException raise:TSStorageManagerExceptionNameDatabasePasswordInaccessibleWhileBackgrounded
|
|
|
|
|
format:@"%@", errorDescription];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSData *)databasePassword
|
|
|
|
|
{
|
|
|
|
|
[SAMKeychain setAccessibilityType:kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly];
|
|
|
|
@ -296,7 +307,19 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
|
|
|
|
|
[SAMKeychain passwordForService:keychainService account:keychainDBPassAccount error:&keyFetchError];
|
|
|
|
|
|
|
|
|
|
if (keyFetchError) {
|
|
|
|
|
// Either this is a new install so there's no existing password to retrieve
|
|
|
|
|
UIApplicationState applicationState = [UIApplication sharedApplication].applicationState;
|
|
|
|
|
NSString *errorDescription = [NSString stringWithFormat:@"Database password inaccessible. No unlock since device restart? Error: %@ ApplicationState: %d", keyFetchError, (int)applicationState];
|
|
|
|
|
DDLogError(@"%@ %@", self.tag, errorDescription);
|
|
|
|
|
[DDLog flushLog];
|
|
|
|
|
|
|
|
|
|
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
|
|
|
|
|
// TODO: Rather than crash here, we should detect the situation earlier
|
|
|
|
|
// and exit gracefully - (in the app delegate?). See the `
|
|
|
|
|
// This is a last ditch effort to avoid blowing away the user's database.
|
|
|
|
|
[self backgroundedAppDatabasePasswordInaccessibleWithErrorDescription:errorDescription];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// At this point, either this is a new install so there's no existing password to retrieve
|
|
|
|
|
// or the keychain has become corrupt. Either way, we want to get back to a
|
|
|
|
|
// "known good state" and behave like a new install.
|
|
|
|
|
|
|
|
|
|