You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
7.6 KiB

  1. /*
  2. * Copyright 2019 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "FIRInstanceIDCheckinStore.h"
  17. #import "FIRInstanceIDAuthKeyChain.h"
  18. #import "FIRInstanceIDBackupExcludedPlist.h"
  19. #import "FIRInstanceIDCheckinPreferences+Internal.h"
  20. #import "FIRInstanceIDCheckinPreferences_Private.h"
  21. #import "FIRInstanceIDCheckinService.h"
  22. #import "FIRInstanceIDLogger.h"
  23. #import "FIRInstanceIDUtilities.h"
  24. #import "FIRInstanceIDVersionUtilities.h"
  25. #import "NSError+FIRInstanceID.h"
  26. static NSString *const kFIRInstanceIDCheckinKeychainGeneric = @"com.google.iid";
  27. NSString *const kFIRInstanceIDCheckinKeychainService = @"com.google.iid.checkin";
  28. @interface FIRInstanceIDCheckinStore ()
  29. @property(nonatomic, readwrite, strong) FIRInstanceIDBackupExcludedPlist *plist;
  30. @property(nonatomic, readwrite, strong) FIRInstanceIDAuthKeychain *keychain;
  31. // Checkin will store items under
  32. // Keychain account: <app bundle id>,
  33. // Keychain service: |kFIRInstanceIDCheckinKeychainService|
  34. @property(nonatomic, readonly) NSString *bundleIdentifierForKeychainAccount;
  35. @end
  36. @implementation FIRInstanceIDCheckinStore
  37. - (instancetype)initWithCheckinPlistFileName:(NSString *)checkinFilename
  38. subDirectoryName:(NSString *)subDirectoryName {
  39. FIRInstanceIDBackupExcludedPlist *plist =
  40. [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:checkinFilename
  41. subDirectory:subDirectoryName];
  42. FIRInstanceIDAuthKeychain *keychain =
  43. [[FIRInstanceIDAuthKeychain alloc] initWithIdentifier:kFIRInstanceIDCheckinKeychainGeneric];
  44. return [self initWithCheckinPlist:plist keychain:keychain];
  45. }
  46. - (instancetype)initWithCheckinPlist:(FIRInstanceIDBackupExcludedPlist *)plist
  47. keychain:(FIRInstanceIDAuthKeychain *)keychain {
  48. self = [super init];
  49. if (self) {
  50. _plist = plist;
  51. _keychain = keychain;
  52. }
  53. return self;
  54. }
  55. - (BOOL)hasCheckinPlist {
  56. return [self.plist doesFileExist];
  57. }
  58. - (NSString *)bundleIdentifierForKeychainAccount {
  59. static NSString *bundleIdentifier;
  60. static dispatch_once_t onceToken;
  61. dispatch_once(&onceToken, ^{
  62. bundleIdentifier = FIRInstanceIDAppIdentifier();
  63. });
  64. return bundleIdentifier;
  65. }
  66. - (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences
  67. handler:(void (^)(NSError *error))handler {
  68. NSDictionary *checkinPlistContents = [preferences checkinPlistContents];
  69. NSString *checkinKeychainContent = [preferences checkinKeychainContent];
  70. if (![checkinKeychainContent length]) {
  71. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore000,
  72. @"Failed to get checkin keychain content from memory.");
  73. if (handler) {
  74. handler([NSError
  75. errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn]);
  76. }
  77. return;
  78. }
  79. if (![checkinPlistContents count]) {
  80. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore001,
  81. @"Failed to get checkin plist contents from memory.");
  82. if (handler) {
  83. handler([NSError
  84. errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn]);
  85. }
  86. return;
  87. }
  88. // Save all other checkin preferences in a plist
  89. NSError *error;
  90. if (![self.plist writeDictionary:checkinPlistContents error:&error]) {
  91. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore003,
  92. @"Failed to save checkin plist contents."
  93. @"Will delete auth credentials");
  94. [self.keychain removeItemsMatchingService:kFIRInstanceIDCheckinKeychainService
  95. account:self.bundleIdentifierForKeychainAccount
  96. handler:nil];
  97. if (handler) {
  98. handler(error);
  99. }
  100. return;
  101. }
  102. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStoreCheckinPlistSaved,
  103. @"Checkin plist file is saved");
  104. // Save the deviceID and secret in the Keychain
  105. if (!preferences.hasPreCachedAuthCredentials) {
  106. NSData *data = [checkinKeychainContent dataUsingEncoding:NSUTF8StringEncoding];
  107. [self.keychain setData:data
  108. forService:kFIRInstanceIDCheckinKeychainService
  109. account:self.bundleIdentifierForKeychainAccount
  110. handler:^(NSError *error) {
  111. if (error) {
  112. if (handler) {
  113. handler(error);
  114. }
  115. return;
  116. }
  117. if (handler) {
  118. handler(nil);
  119. }
  120. }];
  121. } else {
  122. handler(nil);
  123. }
  124. }
  125. - (void)removeCheckinPreferencesWithHandler:(void (^)(NSError *error))handler {
  126. // Delete the checkin preferences plist first to avoid delay.
  127. NSError *deletePlistError;
  128. if (![self.plist deleteFile:&deletePlistError]) {
  129. handler(deletePlistError);
  130. return;
  131. }
  132. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStoreCheckinPlistDeleted,
  133. @"Deleted checkin plist file.");
  134. // Remove deviceID and secret from Keychain
  135. [self.keychain removeItemsMatchingService:kFIRInstanceIDCheckinKeychainService
  136. account:self.bundleIdentifierForKeychainAccount
  137. handler:^(NSError *error) {
  138. handler(error);
  139. }];
  140. }
  141. - (FIRInstanceIDCheckinPreferences *)cachedCheckinPreferences {
  142. // Query the keychain for deviceID and secret
  143. NSData *item = [self.keychain dataForService:kFIRInstanceIDCheckinKeychainService
  144. account:self.bundleIdentifierForKeychainAccount];
  145. // Check info found in keychain
  146. NSString *checkinKeychainContent = [[NSString alloc] initWithData:item
  147. encoding:NSUTF8StringEncoding];
  148. FIRInstanceIDCheckinPreferences *checkinPreferences =
  149. [FIRInstanceIDCheckinPreferences preferencesFromKeychainContents:checkinKeychainContent];
  150. NSDictionary *checkinPlistContents = [self.plist contentAsDictionary];
  151. NSString *plistDeviceAuthID = checkinPlistContents[kFIRInstanceIDDeviceAuthIdKey];
  152. NSString *plistSecretToken = checkinPlistContents[kFIRInstanceIDSecretTokenKey];
  153. // If deviceID and secret not found in the keychain verify that we don't have them in the
  154. // checkin preferences plist.
  155. if (![checkinPreferences.deviceID length] && ![checkinPreferences.secretToken length]) {
  156. if ([plistDeviceAuthID length] && [plistSecretToken length]) {
  157. // Couldn't find checkin credentials in keychain but found them in the plist.
  158. checkinPreferences =
  159. [[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:plistDeviceAuthID
  160. secretToken:plistSecretToken];
  161. } else {
  162. // Couldn't find checkin credentials in keychain nor plist
  163. return nil;
  164. }
  165. }
  166. [checkinPreferences updateWithCheckinPlistContents:checkinPlistContents];
  167. return checkinPreferences;
  168. }
  169. @end