diff --git a/src/Storage/TSStorageManager.m b/src/Storage/TSStorageManager.m index 82bc6729a..534b27b63 100644 --- a/src/Storage/TSStorageManager.m +++ b/src/Storage/TSStorageManager.m @@ -23,6 +23,7 @@ NSString *const TSUIDatabaseConnectionDidUpdateNotification = @"TSUIDatabaseConnectionDidUpdateNotification"; NSString *const TSStorageManagerExceptionNameDatabasePasswordInaccessible = @"TSStorageManagerExceptionNameDatabasePasswordInaccessible"; +NSString *const TSStorageManagerExceptionNameDatabasePasswordInaccessibleWhileBackgrounded = @"TSStorageManagerExceptionNameDatabasePasswordInaccessibleWhileBackgrounded"; NSString *const TSStorageManagerExceptionNameDatabasePasswordUnwritable = @"TSStorageManagerExceptionNameDatabasePasswordUnwritable"; NSString *const TSStorageManagerExceptionNameNoDatabase = @"TSStorageManagerExceptionNameNoDatabase"; @@ -287,6 +288,17 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass"; return NO; } +- (void)backgroundedAppDatabasePasswordInaccessibleWithError:(NSError *)error +{ + 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:@"Unable to access database password. No unlock since device restart? Error: %@", error]; +} + - (NSData *)databasePassword { [SAMKeychain setAccessibilityType:kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly]; @@ -296,7 +308,14 @@ 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 + 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 backgroundedAppDatabasePasswordInaccessibleWithError:keyFetchError]; + } + + // 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.