4 #import <Security/Security.h>
5 #import <Security/SecItemPriv.h>
6 #import "OSX/sec/Security/SecItemShim.h"
8 #import "OSX/utilities/SecCFRelease.h"
10 #import "OTAccountMetadataClassC+KeychainSupport.h"
11 #import "keychain/categories/NSError+UsefulConstructors.h"
13 #import "keychain/ot/OTDefines.h"
14 #import "keychain/ot/OTConstants.h"
16 @implementation OTAccountMetadataClassC (KeychainSupport)
19 - (BOOL)saveToKeychainForContainer:(NSString*)containerName
20 contextID:(NSString*)contextID
21 error:(NSError**)error
23 NSMutableDictionary* query = [@{
24 (id)kSecClass : (id)kSecClassInternetPassword,
25 (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
26 (id)kSecUseDataProtectionKeychain : @YES,
27 (id)kSecAttrAccessGroup: @"com.apple.security.octagon",
28 (id)kSecAttrDescription: [NSString stringWithFormat:@"Octagon Account State (%@,%@)", containerName, contextID],
29 (id)kSecAttrServer: [NSString stringWithFormat:@"octagon-%@", containerName],
30 (id)kSecAttrAccount: [NSString stringWithFormat:@"octagon-%@", containerName], // Really should be alt-DSID, no?
31 (id)kSecAttrPath: [NSString stringWithFormat:@"octagon-%@", contextID],
32 (id)kSecAttrIsInvisible: @YES,
33 (id)kSecValueData : self.data,
34 (id)kSecAttrSynchronizable : @NO,
35 (id)kSecAttrSysBound : @(kSecSecAttrSysBoundPreserveDuringRestore),
38 CFTypeRef result = NULL;
39 OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, &result);
41 NSError* localerror = nil;
43 // Did SecItemAdd fall over due to an existing item?
44 if(status == errSecDuplicateItem) {
45 // Add every primary key attribute to this find dictionary
46 NSMutableDictionary* findQuery = [[NSMutableDictionary alloc] init];
47 findQuery[(id)kSecClass] = query[(id)kSecClass];
48 findQuery[(id)kSecAttrSynchronizable] = query[(id)kSecAttrSynchronizable];
49 findQuery[(id)kSecAttrSyncViewHint] = query[(id)kSecAttrSyncViewHint];
50 findQuery[(id)kSecAttrAccessGroup] = query[(id)kSecAttrAccessGroup];
51 findQuery[(id)kSecAttrAccount] = query[(id)kSecAttrAccount];
52 findQuery[(id)kSecAttrServer] = query[(id)kSecAttrServer];
53 findQuery[(id)kSecAttrPath] = query[(id)kSecAttrPath];
54 findQuery[(id)kSecUseDataProtectionKeychain] = query[(id)kSecUseDataProtectionKeychain];
56 NSMutableDictionary* updateQuery = [query mutableCopy];
57 updateQuery[(id)kSecClass] = nil;
59 status = SecItemUpdate((__bridge CFDictionaryRef)findQuery, (__bridge CFDictionaryRef)updateQuery);
62 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
64 description:[NSString stringWithFormat:@"SecItemUpdate: %d", (int)status]];
66 } else if(status != 0) {
67 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
69 description: [NSString stringWithFormat:@"SecItemAdd: %d", (int)status]];
82 + (BOOL) deleteFromKeychainForContainer:(NSString*)containerName
83 contextID:(NSString*)contextID error:(NSError**)error
85 NSMutableDictionary* query = [@{
86 (id)kSecClass : (id)kSecClassInternetPassword,
87 (id)kSecUseDataProtectionKeychain : @YES,
88 (id)kSecAttrAccessGroup: @"com.apple.security.octagon",
89 (id)kSecAttrServer: [NSString stringWithFormat:@"octagon-%@", containerName],
90 (id)kSecAttrAccount: [NSString stringWithFormat:@"octagon-%@", containerName],
91 (id)kSecAttrPath: [NSString stringWithFormat:@"octagon-%@", contextID],
92 (id)kSecAttrSynchronizable : @NO,
95 OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
98 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
100 userInfo:@{NSLocalizedDescriptionKey:
101 [NSString stringWithFormat:@"SecItemDelete: %d", (int)status]}];
108 + (OTAccountMetadataClassC* _Nullable)loadFromKeychainForContainer:(NSString*)containerName
109 contextID:(NSString*)contextID error:(NSError**)error
111 NSMutableDictionary* query = [@{
112 (id)kSecClass : (id)kSecClassInternetPassword,
113 (id)kSecUseDataProtectionKeychain : @YES,
114 (id)kSecAttrAccessGroup: @"com.apple.security.octagon",
115 (id)kSecAttrServer: [NSString stringWithFormat:@"octagon-%@", containerName],
116 (id)kSecAttrAccount: [NSString stringWithFormat:@"octagon-%@", containerName],
117 (id)kSecAttrPath: [NSString stringWithFormat:@"octagon-%@", contextID],
118 (id)kSecAttrSynchronizable : @NO,
119 (id)kSecReturnAttributes: @YES,
120 (id)kSecReturnData: @YES,
123 CFTypeRef result = NULL;
124 OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
127 CFReleaseNull(result);
130 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
132 userInfo:@{NSLocalizedDescriptionKey:
133 [NSString stringWithFormat:@"SecItemCopyMatching: %d", (int)status]}];
138 NSDictionary* resultDict = CFBridgingRelease(result);
140 OTAccountMetadataClassC* state = [[OTAccountMetadataClassC alloc] initWithData:resultDict[(id)kSecValueData]];
143 *error = [NSError errorWithDomain:OctagonErrorDomain code:OTErrorDeserializationFailure description:@"couldn't deserialize account state"];
145 NSError* deleteError = nil;
146 BOOL deleted = [OTAccountMetadataClassC deleteFromKeychainForContainer:containerName contextID:contextID error:&deleteError];
147 if(deleted == NO || deleteError) {
148 secnotice("octagon", "failed to reset account metadata in keychain, %@", deleteError);
153 //check if an account state has the appropriate attributes
154 if(resultDict[(id)kSecAttrSysBound] == nil || ![resultDict[(id)kSecAttrAccessible] isEqualToString:(id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly]){
155 [state saveToKeychainForContainer:containerName contextID:contextID error:error];