No Description

FIRInstanceIDCheckinStore.m 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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. NSString *const kFIRInstanceIDLegacyCheckinKeychainAccount = @"com.google.iid.checkin-account";
  29. NSString *const kFIRInstanceIDLegacyCheckinKeychainService = @"com.google.iid.checkin-service";
  30. // Checkin plist used to have the deviceID and secret stored in them and that's why they
  31. // had 6 items in it. Since the deviceID and secret have been moved to the keychain
  32. // there would only be 4 items.
  33. static const NSInteger kOldCheckinPlistCount = 6;
  34. @interface FIRInstanceIDCheckinStore ()
  35. @property(nonatomic, readwrite, strong) FIRInstanceIDBackupExcludedPlist *plist;
  36. @property(nonatomic, readwrite, strong) FIRInstanceIDAuthKeychain *keychain;
  37. // Checkin will store items under
  38. // Keychain account: <app bundle id>,
  39. // Keychain service: |kFIRInstanceIDCheckinKeychainService|
  40. @property(nonatomic, readonly) NSString *bundleIdentifierForKeychainAccount;
  41. @end
  42. @implementation FIRInstanceIDCheckinStore
  43. - (instancetype)initWithCheckinPlistFileName:(NSString *)checkinFilename
  44. subDirectoryName:(NSString *)subDirectoryName {
  45. FIRInstanceIDBackupExcludedPlist *plist =
  46. [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:checkinFilename
  47. subDirectory:subDirectoryName];
  48. FIRInstanceIDAuthKeychain *keychain =
  49. [[FIRInstanceIDAuthKeychain alloc] initWithIdentifier:kFIRInstanceIDCheckinKeychainGeneric];
  50. return [self initWithCheckinPlist:plist keychain:keychain];
  51. }
  52. - (instancetype)initWithCheckinPlist:(FIRInstanceIDBackupExcludedPlist *)plist
  53. keychain:(FIRInstanceIDAuthKeychain *)keychain {
  54. self = [super init];
  55. if (self) {
  56. _plist = plist;
  57. _keychain = keychain;
  58. }
  59. return self;
  60. }
  61. - (BOOL)hasCheckinPlist {
  62. return [self.plist doesFileExist];
  63. }
  64. - (NSString *)bundleIdentifierForKeychainAccount {
  65. static NSString *bundleIdentifier;
  66. static dispatch_once_t onceToken;
  67. dispatch_once(&onceToken, ^{
  68. bundleIdentifier = FIRInstanceIDAppIdentifier();
  69. });
  70. return bundleIdentifier;
  71. }
  72. - (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences
  73. handler:(void (^)(NSError *error))handler {
  74. NSDictionary *checkinPlistContents = [preferences checkinPlistContents];
  75. NSString *checkinKeychainContent = [preferences checkinKeychainContent];
  76. if (![checkinKeychainContent length]) {
  77. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore000,
  78. @"Failed to get checkin keychain content from memory.");
  79. if (handler) {
  80. handler([NSError
  81. errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn]);
  82. }
  83. return;
  84. }
  85. if (![checkinPlistContents count]) {
  86. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore001,
  87. @"Failed to get checkin plist contents from memory.");
  88. if (handler) {
  89. handler([NSError
  90. errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn]);
  91. }
  92. return;
  93. }
  94. // Save all other checkin preferences in a plist
  95. NSError *error;
  96. if (![self.plist writeDictionary:checkinPlistContents error:&error]) {
  97. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore003,
  98. @"Failed to save checkin plist contents."
  99. @"Will delete auth credentials");
  100. [self.keychain removeItemsMatchingService:kFIRInstanceIDCheckinKeychainService
  101. account:self.bundleIdentifierForKeychainAccount
  102. handler:nil];
  103. if (handler) {
  104. handler(error);
  105. }
  106. return;
  107. }
  108. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStoreCheckinPlistSaved,
  109. @"Checkin plist file is saved");
  110. // Save the deviceID and secret in the Keychain
  111. if (!preferences.hasPreCachedAuthCredentials) {
  112. NSData *data = [checkinKeychainContent dataUsingEncoding:NSUTF8StringEncoding];
  113. [self.keychain setData:data
  114. forService:kFIRInstanceIDCheckinKeychainService
  115. accessibility:nil
  116. account:self.bundleIdentifierForKeychainAccount
  117. handler:^(NSError *error) {
  118. if (error) {
  119. if (handler) {
  120. handler(error);
  121. }
  122. return;
  123. }
  124. if (handler) {
  125. handler(nil);
  126. }
  127. }];
  128. } else {
  129. handler(nil);
  130. }
  131. }
  132. - (void)removeCheckinPreferencesWithHandler:(void (^)(NSError *error))handler {
  133. // Delete the checkin preferences plist first to avoid delay.
  134. NSError *deletePlistError;
  135. if (![self.plist deleteFile:&deletePlistError]) {
  136. handler(deletePlistError);
  137. return;
  138. }
  139. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStoreCheckinPlistDeleted,
  140. @"Deleted checkin plist file.");
  141. // Remove deviceID and secret from Keychain
  142. [self.keychain
  143. removeItemsMatchingService:kFIRInstanceIDCheckinKeychainService
  144. account:self.bundleIdentifierForKeychainAccount
  145. handler:^(NSError *error) {
  146. // Try to remove from old location as well because migration
  147. // is no longer needed. Consider this is either a fresh install
  148. // or an identity wipe.
  149. [self.keychain
  150. removeItemsMatchingService:kFIRInstanceIDLegacyCheckinKeychainService
  151. account:kFIRInstanceIDLegacyCheckinKeychainAccount
  152. handler:nil];
  153. handler(error);
  154. }];
  155. }
  156. - (FIRInstanceIDCheckinPreferences *)cachedCheckinPreferences {
  157. // Query the keychain for deviceID and secret
  158. NSData *item = [self.keychain dataForService:kFIRInstanceIDCheckinKeychainService
  159. account:self.bundleIdentifierForKeychainAccount];
  160. // Check info found in keychain
  161. NSString *checkinKeychainContent = [[NSString alloc] initWithData:item
  162. encoding:NSUTF8StringEncoding];
  163. FIRInstanceIDCheckinPreferences *checkinPreferences =
  164. [FIRInstanceIDCheckinPreferences preferencesFromKeychainContents:checkinKeychainContent];
  165. NSDictionary *checkinPlistContents = [self.plist contentAsDictionary];
  166. NSString *plistDeviceAuthID = checkinPlistContents[kFIRInstanceIDDeviceAuthIdKey];
  167. NSString *plistSecretToken = checkinPlistContents[kFIRInstanceIDSecretTokenKey];
  168. // If deviceID and secret not found in the keychain verify that we don't have them in the
  169. // checkin preferences plist.
  170. if (![checkinPreferences.deviceID length] && ![checkinPreferences.secretToken length]) {
  171. if ([plistDeviceAuthID length] && [plistSecretToken length]) {
  172. // Couldn't find checkin credentials in keychain but found them in the plist.
  173. checkinPreferences =
  174. [[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:plistDeviceAuthID
  175. secretToken:plistSecretToken];
  176. } else {
  177. // Couldn't find checkin credentials in keychain nor plist
  178. return nil;
  179. }
  180. } else if (kOldCheckinPlistCount == checkinPlistContents.count) {
  181. // same check as above but just to be extra sure that we cover all upgrade cases properly.
  182. // TODO(chliangGoogle): Remove this case, after verifying it's not needed
  183. if ([plistDeviceAuthID length] && [plistSecretToken length]) {
  184. checkinPreferences =
  185. [[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:plistDeviceAuthID
  186. secretToken:plistSecretToken];
  187. }
  188. }
  189. [checkinPreferences updateWithCheckinPlistContents:checkinPlistContents];
  190. return checkinPreferences;
  191. }
  192. - (void)migrateCheckinItemIfNeeded {
  193. // Check for checkin in the old location, using the legacy keys
  194. // Query the keychain for deviceID and secret
  195. NSData *dataInOldLocation =
  196. [self.keychain dataForService:kFIRInstanceIDLegacyCheckinKeychainService
  197. account:kFIRInstanceIDLegacyCheckinKeychainAccount];
  198. if (dataInOldLocation) {
  199. // Save to new location
  200. [self.keychain setData:dataInOldLocation
  201. forService:kFIRInstanceIDCheckinKeychainService
  202. accessibility:NULL
  203. account:self.bundleIdentifierForKeychainAccount
  204. handler:nil];
  205. // Remove from old location
  206. [self.keychain removeItemsMatchingService:kFIRInstanceIDLegacyCheckinKeychainService
  207. account:kFIRInstanceIDLegacyCheckinKeychainAccount
  208. handler:nil];
  209. }
  210. }
  211. @end