diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 78f06cedfd..5431f208e4 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -208,6 +208,9 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; for (NSUInteger i=0; i < [_crashFiles count]; i++) { [_fileManager removeItemAtPath:[_crashFiles objectAtIndex:i] error:&error]; [_fileManager removeItemAtPath:[[_crashFiles objectAtIndex:i] stringByAppendingString:@".meta"] error:&error]; + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", [_crashFiles objectAtIndex:i], kBITCrashMetaUserName]]; + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", [_crashFiles objectAtIndex:i], kBITCrashMetaUserEmail]]; + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", [_crashFiles objectAtIndex:i], kBITCrashMetaUserID]]; } [_crashFiles removeAllObjects]; [_approvedCrashReports removeAllObjects]; @@ -321,9 +324,9 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; NSString *applicationLog = @""; NSString *errorString = nil; - [metaDict setObject:[self userNameForCrashReport] forKey:kBITCrashMetaUserName]; - [metaDict setObject:[self userEmailForCrashReport] forKey:kBITCrashMetaUserEmail]; - [metaDict setObject:[self userIDForCrashReport] forKey:kBITCrashMetaUserID]; + [self addStringValueToKeychain:[self userNameForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; + [self addStringValueToKeychain:[self userEmailForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]]; + [self addStringValueToKeychain:[self userIDForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]]; if (self.delegate != nil && [self.delegate respondsToSelector:@selector(applicationLogForCrashManager:)]) { applicationLog = [self.delegate applicationLogForCrashManager:self] ?: @""; @@ -548,6 +551,10 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; // we cannot do anything with this report, so delete it [_fileManager removeItemAtPath:filename error:&error]; [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error]; + + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserName]]; + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserEmail]]; + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserID]]; continue; } @@ -583,9 +590,9 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; format:&format errorDescription:&errorString]; - username = [metaDict objectForKey:kBITCrashMetaUserName] ?: @""; - useremail = [metaDict objectForKey:kBITCrashMetaUserEmail] ?: @""; - userid = [metaDict objectForKey:kBITCrashMetaUserID] ?: @""; + username = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserName]] ?: @""; + useremail = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserEmail]] ?: @""; + userid = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserID]] ?: @""; applicationLog = [metaDict objectForKey:kBITCrashMetaApplicationLog] ?: @""; } else { BITHockeyLog(@"ERROR: Reading crash meta data. %@", error); @@ -618,6 +625,10 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; // we cannot do anything with this report, so delete it [_fileManager removeItemAtPath:filename error:&error]; [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error]; + + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserName]]; + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserEmail]]; + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserID]]; } } diff --git a/Classes/BITFeedbackManager.m b/Classes/BITFeedbackManager.m index 24d35d4eae..eec35575e2 100644 --- a/Classes/BITFeedbackManager.m +++ b/Classes/BITFeedbackManager.m @@ -295,25 +295,37 @@ } if (!userIDViaDelegate) { - if ([unarchiver containsValueForKey:kBITFeedbackUserID]) + if ([unarchiver containsValueForKey:kBITFeedbackUserID]) { self.userID = [unarchiver decodeObjectForKey:kBITFeedbackUserID]; + [self addStringValueToKeychain:self.userID forKey:kBITFeedbackUserID]; + } + self.userID = [self stringValueFromKeychainForKey:kBITFeedbackUserID]; } if (!userNameViaDelegate) { - if ([unarchiver containsValueForKey:kBITFeedbackName]) + if ([unarchiver containsValueForKey:kBITFeedbackName]) { self.userName = [unarchiver decodeObjectForKey:kBITFeedbackName]; + [self addStringValueToKeychain:self.userName forKey:kBITFeedbackName]; + } + self.userName = [self stringValueFromKeychainForKey:kBITFeedbackName]; } if (!userEmailViaDelegate) { - if ([unarchiver containsValueForKey:kBITFeedbackEmail]) + if ([unarchiver containsValueForKey:kBITFeedbackEmail]) { self.userEmail = [unarchiver decodeObjectForKey:kBITFeedbackEmail]; + [self addStringValueToKeychain:self.userEmail forKey:kBITFeedbackEmail]; + } + self.userEmail = [self stringValueFromKeychainForKey:kBITFeedbackEmail]; } if ([unarchiver containsValueForKey:kBITFeedbackUserDataAsked]) _didAskUserData = YES; - if ([unarchiver containsValueForKey:kBITFeedbackToken]) + if ([unarchiver containsValueForKey:kBITFeedbackToken]) { self.token = [unarchiver decodeObjectForKey:kBITFeedbackToken]; + [self addStringValueToKeychain:self.token forKey:kBITFeedbackToken]; + } + self.token = [self stringValueFromKeychainForKey:kBITFeedbackToken]; if ([unarchiver containsValueForKey:kBITFeedbackAppID]) { NSString *appID = [unarchiver decodeObjectForKey:kBITFeedbackAppID]; @@ -359,19 +371,19 @@ [archiver encodeObject:[NSNumber numberWithBool:YES] forKey:kBITFeedbackUserDataAsked]; if (self.token) - [archiver encodeObject:self.token forKey:kBITFeedbackToken]; + [self addStringValueToKeychain:self.token forKey:kBITFeedbackToken]; if (self.appIdentifier) [archiver encodeObject:self.appIdentifier forKey:kBITFeedbackAppID]; if (self.userID) - [archiver encodeObject:self.userID forKey:kBITFeedbackUserID]; + [self addStringValueToKeychain:self.userID forKey:kBITFeedbackUserID]; if (self.userName) - [archiver encodeObject:self.userName forKey:kBITFeedbackName]; + [self addStringValueToKeychain:self.userName forKey:kBITFeedbackName]; if (self.userEmail) - [archiver encodeObject:self.userEmail forKey:kBITFeedbackEmail]; + [self addStringValueToKeychain:self.userEmail forKey:kBITFeedbackEmail]; if (self.lastCheck) [archiver encodeObject:self.lastCheck forKey:kBITFeedbackDateOfLastCheck]; diff --git a/Classes/BITHockeyBaseManager.m b/Classes/BITHockeyBaseManager.m index d48e795da6..775f405f68 100644 --- a/Classes/BITHockeyBaseManager.m +++ b/Classes/BITHockeyBaseManager.m @@ -16,6 +16,7 @@ #import "BITHockeyBaseViewController.h" #import "BITHockeyManagerPrivate.h" +#import "BITKeychainUtils.h" #import #if !TARGET_IPHONE_SIMULATOR @@ -192,6 +193,33 @@ } } +- (BOOL)addStringValueToKeychain:(NSString *)stringValue forKey:(NSString *)key { + if (!key || !stringValue) + return NO; + + NSString *serviceName = [NSString stringWithFormat:@"%@.HockeySDK", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]]; + + NSError *error = nil; + return [BITKeychainUtils storeUsername:key andPassword:stringValue forServiceName:serviceName updateExisting:YES error:&error]; +} + +- (NSString *)stringValueFromKeychainForKey:(NSString *)key { + if (!key) + return nil; + + NSString *serviceName = [NSString stringWithFormat:@"%@.HockeySDK", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]]; + + NSError *error = nil; + return [BITKeychainUtils getPasswordForUsername:key andServiceName:serviceName error:&error]; +} + +- (BOOL)removeKeyFromKeychain:(NSString *)key { + NSString *serviceName = [NSString stringWithFormat:@"%@.HockeySDK", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]]; + + NSError *error = nil; + return [BITKeychainUtils deleteItemForUsername:key andServiceName:serviceName error:&error]; +} + #pragma mark - Manager Control diff --git a/Classes/BITHockeyBaseManagerPrivate.h b/Classes/BITHockeyBaseManagerPrivate.h index 552c82b78c..01c12c7d21 100644 --- a/Classes/BITHockeyBaseManagerPrivate.h +++ b/Classes/BITHockeyBaseManagerPrivate.h @@ -34,4 +34,8 @@ - (NSDate *)parseRFC3339Date:(NSString *)dateString; +- (BOOL)addStringValueToKeychain:(NSString *)stringValue forKey:(NSString *)key; +- (NSString *)stringValueFromKeychainForKey:(NSString *)key; +- (BOOL)removeKeyFromKeychain:(NSString *)key; + @end diff --git a/Classes/BITKeychainUtils.h b/Classes/BITKeychainUtils.h new file mode 100644 index 0000000000..3397b51cf2 --- /dev/null +++ b/Classes/BITKeychainUtils.h @@ -0,0 +1,42 @@ +// +// SFHFKeychainUtils.h +// +// Created by Buzz Andersen on 10/20/08. +// Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone. +// Copyright 2008 Sci-Fi Hi-Fi. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import + + +@interface BITKeychainUtils : NSObject { + +} + ++ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error; ++ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error; ++ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error; ++ (BOOL) purgeItemsForServiceName:(NSString *) serviceName error: (NSError **) error; + +@end diff --git a/Classes/BITKeychainUtils.m b/Classes/BITKeychainUtils.m new file mode 100644 index 0000000000..cc0f34c98d --- /dev/null +++ b/Classes/BITKeychainUtils.m @@ -0,0 +1,297 @@ +// +// SFHFKeychainUtils.m +// +// Created by Buzz Andersen on 10/20/08. +// Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone. +// Copyright 2008 Sci-Fi Hi-Fi. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import "BITKeychainUtils.h" +#import + +static NSString *BITKeychainUtilsErrorDomain = @"BITKeychainUtilsErrorDomain"; + +@implementation BITKeychainUtils + ++ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error { + if (!username || !serviceName) { + if (error != nil) { + *error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + } + return nil; + } + + if (error != nil) { + *error = nil; + } + + // Set up a query dictionary with the base query attributes: item type (generic), username, and service + + NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass, kSecAttrAccount, kSecAttrService, nil]; + NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword, username, serviceName, nil]; + + NSMutableDictionary *query = [[NSMutableDictionary alloc] initWithObjects: objects forKeys: keys]; + + // First do a query for attributes, in case we already have a Keychain item with no password data set. + // One likely way such an incorrect item could have come about is due to the previous (incorrect) + // version of this code (which set the password as a generic attribute instead of password data). + + NSMutableDictionary *attributeQuery = [query mutableCopy]; + [attributeQuery setObject: (id) kCFBooleanTrue forKey:(__bridge_transfer id) kSecReturnAttributes]; + CFTypeRef attrResult = NULL; + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) attributeQuery, &attrResult); + //NSDictionary *attributeResult = (__bridge_transfer NSDictionary *)attrResult; + + if (status != noErr) { + // No existing item found--simply return nil for the password + if (error != nil && status != errSecItemNotFound) { + //Only return an error if a real exception happened--not simply for "not found." + *error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: status userInfo: nil]; + } + + return nil; + } + + // We have an existing item, now query for the password data associated with it. + + NSMutableDictionary *passwordQuery = [query mutableCopy]; + [passwordQuery setObject: (id) kCFBooleanTrue forKey: (__bridge_transfer id) kSecReturnData]; + CFTypeRef resData = NULL; + status = SecItemCopyMatching((__bridge CFDictionaryRef) passwordQuery, (CFTypeRef *) &resData); + NSData *resultData = (__bridge_transfer NSData *)resData; + + if (status != noErr) { + if (status == errSecItemNotFound) { + // We found attributes for the item previously, but no password now, so return a special error. + // Users of this API will probably want to detect this error and prompt the user to + // re-enter their credentials. When you attempt to store the re-entered credentials + // using storeUsername:andPassword:forServiceName:updateExisting:error + // the old, incorrect entry will be deleted and a new one with a properly encrypted + // password will be added. + if (error != nil) { + *error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: -1999 userInfo: nil]; + } + } + else { + // Something else went wrong. Simply return the normal Keychain API error code. + if (error != nil) { + *error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: status userInfo: nil]; + } + } + + return nil; + } + + NSString *password = nil; + + if (resultData) { + password = [[NSString alloc] initWithData: resultData encoding: NSUTF8StringEncoding]; + } + else { + // There is an existing item, but we weren't able to get password data for it for some reason, + // Possibly as a result of an item being incorrectly entered by the previous code. + // Set the -1999 error so the code above us can prompt the user again. + if (error != nil) { + *error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: -1999 userInfo: nil]; + } + } + + return password; +} + ++ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error +{ + if (!username || !password || !serviceName) + { + if (error != nil) + { + *error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + } + return NO; + } + + // See if we already have a password entered for these credentials. + NSError *getError = nil; + NSString *existingPassword = [BITKeychainUtils getPasswordForUsername: username andServiceName: serviceName error:&getError]; + + if ([getError code] == -1999) + { + // There is an existing entry without a password properly stored (possibly as a result of the previous incorrect version of this code. + // Delete the existing item before moving on entering a correct one. + + getError = nil; + + [self deleteItemForUsername: username andServiceName: serviceName error: &getError]; + + if ([getError code] != noErr) + { + if (error != nil) + { + *error = getError; + } + return NO; + } + } + else if ([getError code] != noErr) + { + if (error != nil) + { + *error = getError; + } + return NO; + } + + if (error != nil) + { + *error = nil; + } + + OSStatus status = noErr; + + if (existingPassword) + { + // We have an existing, properly entered item with a password. + // Update the existing item. + + if (![existingPassword isEqualToString:password] && updateExisting) + { + //Only update if we're allowed to update existing. If not, simply do nothing. + + NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass, + kSecAttrService, + kSecAttrLabel, + kSecAttrAccount, + nil]; + + NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword, + serviceName, + serviceName, + username, + nil]; + + NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys]; + + status = SecItemUpdate((__bridge_retained CFDictionaryRef) query, (__bridge_retained CFDictionaryRef) [NSDictionary dictionaryWithObject: [password dataUsingEncoding: NSUTF8StringEncoding] forKey: (__bridge_transfer NSString *) kSecValueData]); + } + } + else + { + // No existing entry (or an existing, improperly entered, and therefore now + // deleted, entry). Create a new entry. + + NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass, + kSecAttrService, + kSecAttrLabel, + kSecAttrAccount, + kSecValueData, + nil]; + + NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword, + serviceName, + serviceName, + username, + [password dataUsingEncoding: NSUTF8StringEncoding], + nil]; + + NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys]; + + status = SecItemAdd((__bridge_retained CFDictionaryRef) query, NULL); + } + + if (error != nil && status != noErr) + { + // Something went wrong with adding the new item. Return the Keychain error code. + *error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: status userInfo: nil]; + + return NO; + } + + return YES; +} + ++ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error +{ + if (!username || !serviceName) + { + if (error != nil) + { + *error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + } + return NO; + } + + if (error != nil) + { + *error = nil; + } + + NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass, kSecAttrAccount, kSecAttrService, kSecReturnAttributes, nil]; + NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword, username, serviceName, kCFBooleanTrue, nil]; + + NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys]; + + OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query); + + if (error != nil && status != noErr) + { + *error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: status userInfo: nil]; + + return NO; + } + + return YES; +} + ++ (BOOL) purgeItemsForServiceName:(NSString *) serviceName error: (NSError **) error { + if (!serviceName) + { + if (error != nil) + { + *error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + } + return NO; + } + + if (error != nil) + { + *error = nil; + } + + NSMutableDictionary *searchData = [NSMutableDictionary new]; + [searchData setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; + [searchData setObject:serviceName forKey:(__bridge id)kSecAttrService]; + + OSStatus status = SecItemDelete((__bridge CFDictionaryRef)searchData); + + if (error != nil && status != noErr) + { + *error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: status userInfo: nil]; + + return NO; + } + + return YES; +} + +@end diff --git a/Classes/BITUpdateManager.m b/Classes/BITUpdateManager.m index 461b3d4d3d..c68ed8d1d3 100644 --- a/Classes/BITUpdateManager.m +++ b/Classes/BITUpdateManager.m @@ -225,7 +225,7 @@ - (BITUpdateAuthorizationState)authorizationState { NSString *version = [[NSUserDefaults standardUserDefaults] objectForKey:kBITUpdateAuthorizedVersion]; - NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:kBITUpdateAuthorizedToken]; + NSString *token = [self stringValueFromKeychainForKey:kBITUpdateAuthorizedToken]; if (version != nil && token != nil) { if ([version compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) { @@ -602,7 +602,7 @@ // store the new data [[NSUserDefaults standardUserDefaults] setObject:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] forKey:kBITUpdateAuthorizedVersion]; - [[NSUserDefaults standardUserDefaults] setObject:token forKey:kBITUpdateAuthorizedToken]; + [self addStringValueToKeychain:token forKey:kBITUpdateAuthorizedToken]; [[NSUserDefaults standardUserDefaults] synchronize]; self.requireAuthorization = NO; @@ -618,7 +618,7 @@ // store the new data [[NSUserDefaults standardUserDefaults] setObject:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] forKey:kBITUpdateAuthorizedVersion]; - [[NSUserDefaults standardUserDefaults] setObject:token forKey:kBITUpdateAuthorizedToken]; + [self addStringValueToKeychain:token forKey:kBITUpdateAuthorizedToken]; [[NSUserDefaults standardUserDefaults] synchronize]; [self showBlockingScreen:BITHockeyLocalizedString(@"UpdateAuthorizationDenied") image:@"authorize_denied.png"]; diff --git a/LICENSE b/LICENSE index dfa06530db..4121519cd4 100755 --- a/LICENSE +++ b/LICENSE @@ -95,3 +95,30 @@ TTTAttributedLabel is licensed as follows: WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +SFHFKeychainUtils is licensed as follows: + + Created by Buzz Andersen on 10/20/08. + Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone. + Copyright 2008 Sci-Fi Hi-Fi. All rights reserved. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. diff --git a/Support/HockeySDK.xcconfig b/Support/HockeySDK.xcconfig index 648867405b..eccd8e5782 100644 --- a/Support/HockeySDK.xcconfig +++ b/Support/HockeySDK.xcconfig @@ -1,3 +1,3 @@ -OTHER_LDFLAGS=$(inherited) -framework CoreText -framework CoreGraphics -framework Foundation -framework QuartzCore -framework SystemConfiguration -framework UIKit +OTHER_LDFLAGS=$(inherited) -framework CoreText -framework CoreGraphics -framework Foundation -framework QuartzCore -framework SystemConfiguration -framework UIKit -framework Security HOCKEYSDK_DOCSET_NAME=HockeySDK-iOS GCC_PREPROCESSOR_DEFINITIONS=$(inherited) CONFIGURATION_$(CONFIGURATION) \ No newline at end of file diff --git a/Support/HockeySDK.xcodeproj/project.pbxproj b/Support/HockeySDK.xcodeproj/project.pbxproj index fc38c242a3..baff1a6538 100644 --- a/Support/HockeySDK.xcodeproj/project.pbxproj +++ b/Support/HockeySDK.xcodeproj/project.pbxproj @@ -33,6 +33,8 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 1E0FEE28173BDB260061331F /* BITKeychainUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E0FEE26173BDB260061331F /* BITKeychainUtils.h */; }; + 1E0FEE29173BDB260061331F /* BITKeychainUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E0FEE27173BDB260061331F /* BITKeychainUtils.m */; }; 1E1127C416580C87007067A2 /* buttonRoundedDelete.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E1127BC16580C87007067A2 /* buttonRoundedDelete.png */; }; 1E1127C516580C87007067A2 /* buttonRoundedDelete@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E1127BD16580C87007067A2 /* buttonRoundedDelete@2x.png */; }; 1E1127C616580C87007067A2 /* buttonRoundedDeleteHighlighted.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E1127BE16580C87007067A2 /* buttonRoundedDeleteHighlighted.png */; }; @@ -130,6 +132,9 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 1E0FEE22173BD9BB0061331F /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + 1E0FEE26173BDB260061331F /* BITKeychainUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITKeychainUtils.h; sourceTree = ""; }; + 1E0FEE27173BDB260061331F /* BITKeychainUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITKeychainUtils.m; sourceTree = ""; }; 1E1127BC16580C87007067A2 /* buttonRoundedDelete.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = buttonRoundedDelete.png; sourceTree = ""; }; 1E1127BD16580C87007067A2 /* buttonRoundedDelete@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "buttonRoundedDelete@2x.png"; sourceTree = ""; }; 1E1127BE16580C87007067A2 /* buttonRoundedDeleteHighlighted.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = buttonRoundedDeleteHighlighted.png; sourceTree = ""; }; @@ -295,6 +300,8 @@ 1E49A4A4161222B900463151 /* BITHockeyBaseViewController.m */, 1E49A4A5161222B900463151 /* BITHockeyHelper.h */, 1E49A4A6161222B900463151 /* BITHockeyHelper.m */, + 1E0FEE26173BDB260061331F /* BITKeychainUtils.h */, + 1E0FEE27173BDB260061331F /* BITKeychainUtils.m */, 1EACC979162F041E007578C5 /* BITAttributedLabel.h */, 1EACC97A162F041E007578C5 /* BITAttributedLabel.m */, 1E49A4A7161222B900463151 /* BITAppStoreHeader.h */, @@ -463,6 +470,7 @@ 1E754E5F1621FBB70070AB92 /* BITCrashManagerPrivate.h in Headers */, 1E754E601621FBB70070AB92 /* BITCrashReportTextFormatter.h in Headers */, 1EACC97B162F041E007578C5 /* BITAttributedLabel.h in Headers */, + 1E0FEE28173BDB260061331F /* BITKeychainUtils.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -636,6 +644,7 @@ 1E754E611621FBB70070AB92 /* BITCrashReportTextFormatter.m in Sources */, 1EF95CA7162CB037000AE3AD /* BITFeedbackActivity.m in Sources */, 1EACC97C162F041E007578C5 /* BITAttributedLabel.m in Sources */, + 1E0FEE29173BDB260061331F /* BITKeychainUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/docs/Guide-Installation-Setup-Advanced-template.md b/docs/Guide-Installation-Setup-Advanced-template.md index c4f4b0d534..8464f7096b 100644 --- a/docs/Guide-Installation-Setup-Advanced-template.md +++ b/docs/Guide-Installation-Setup-Advanced-template.md @@ -102,6 +102,7 @@ If you need support for iOS 3.x, please check out [HockeyKit](http://support.hoc - `CoreGraphics` - `Foundation` - `QuartzCore` + - `Security` - `SystemConfiguration` - `UIKit` diff --git a/docs/Guide-Installation-Setup-template.md b/docs/Guide-Installation-Setup-template.md index 1b3eedb31a..c77abe1f23 100644 --- a/docs/Guide-Installation-Setup-template.md +++ b/docs/Guide-Installation-Setup-template.md @@ -72,6 +72,7 @@ If you need support for iOS 3.x, please check out [HockeyKit](http://support.hoc - `CoreGraphics` - `Foundation` - `QuartzCore` + - `Security` - `SystemConfiguration` - `UIKit`