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"
15 #import <TrustedPeers/TPPolicy.h>
17 @implementation OTAccountMetadataClassC (KeychainSupport)
20 - (BOOL)saveToKeychainForContainer:(NSString*)containerName
21 contextID:(NSString*)contextID
22 error:(NSError**)error
24 NSMutableDictionary* query = [@{
25 (id)kSecClass : (id)kSecClassInternetPassword,
26 (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
27 (id)kSecUseDataProtectionKeychain : @YES,
28 (id)kSecAttrAccessGroup: @"com.apple.security.octagon",
29 (id)kSecAttrDescription: [NSString stringWithFormat:@"Octagon Account State (%@,%@)", containerName, contextID],
30 (id)kSecAttrServer: [NSString stringWithFormat:@"octagon-%@", containerName],
31 (id)kSecAttrAccount: [NSString stringWithFormat:@"octagon-%@", containerName], // Really should be alt-DSID, no?
32 (id)kSecAttrPath: [NSString stringWithFormat:@"octagon-%@", contextID],
33 (id)kSecAttrIsInvisible: @YES,
34 (id)kSecValueData : self.data,
35 (id)kSecAttrSynchronizable : @NO,
36 (id)kSecAttrSysBound : @(kSecSecAttrSysBoundPreserveDuringRestore),
39 CFTypeRef result = NULL;
40 OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, &result);
42 NSError* localerror = nil;
44 // Did SecItemAdd fall over due to an existing item?
45 if(status == errSecDuplicateItem) {
46 // Add every primary key attribute to this find dictionary
47 NSMutableDictionary* findQuery = [[NSMutableDictionary alloc] init];
48 findQuery[(id)kSecClass] = query[(id)kSecClass];
49 findQuery[(id)kSecAttrSynchronizable] = query[(id)kSecAttrSynchronizable];
50 findQuery[(id)kSecAttrSyncViewHint] = query[(id)kSecAttrSyncViewHint];
51 findQuery[(id)kSecAttrAccessGroup] = query[(id)kSecAttrAccessGroup];
52 findQuery[(id)kSecAttrAccount] = query[(id)kSecAttrAccount];
53 findQuery[(id)kSecAttrServer] = query[(id)kSecAttrServer];
54 findQuery[(id)kSecAttrPath] = query[(id)kSecAttrPath];
55 findQuery[(id)kSecUseDataProtectionKeychain] = query[(id)kSecUseDataProtectionKeychain];
57 NSMutableDictionary* updateQuery = [query mutableCopy];
58 updateQuery[(id)kSecClass] = nil;
60 status = SecItemUpdate((__bridge CFDictionaryRef)findQuery, (__bridge CFDictionaryRef)updateQuery);
63 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
65 description:[NSString stringWithFormat:@"SecItemUpdate: %d", (int)status]];
67 } else if(status != 0) {
68 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
70 description: [NSString stringWithFormat:@"SecItemAdd: %d", (int)status]];
83 + (BOOL) deleteFromKeychainForContainer:(NSString*)containerName
84 contextID:(NSString*)contextID error:(NSError**)error
86 NSMutableDictionary* query = [@{
87 (id)kSecClass : (id)kSecClassInternetPassword,
88 (id)kSecUseDataProtectionKeychain : @YES,
89 (id)kSecAttrAccessGroup: @"com.apple.security.octagon",
90 (id)kSecAttrServer: [NSString stringWithFormat:@"octagon-%@", containerName],
91 (id)kSecAttrAccount: [NSString stringWithFormat:@"octagon-%@", containerName],
92 (id)kSecAttrPath: [NSString stringWithFormat:@"octagon-%@", contextID],
93 (id)kSecAttrSynchronizable : @NO,
96 OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
99 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
101 userInfo:@{NSLocalizedDescriptionKey:
102 [NSString stringWithFormat:@"SecItemDelete: %d", (int)status]}];
109 + (OTAccountMetadataClassC* _Nullable)loadFromKeychainForContainer:(NSString*)containerName
110 contextID:(NSString*)contextID error:(NSError**)error
112 NSMutableDictionary* query = [@{
113 (id)kSecClass : (id)kSecClassInternetPassword,
114 (id)kSecUseDataProtectionKeychain : @YES,
115 (id)kSecAttrAccessGroup: @"com.apple.security.octagon",
116 (id)kSecAttrServer: [NSString stringWithFormat:@"octagon-%@", containerName],
117 (id)kSecAttrAccount: [NSString stringWithFormat:@"octagon-%@", containerName],
118 (id)kSecAttrPath: [NSString stringWithFormat:@"octagon-%@", contextID],
119 (id)kSecAttrSynchronizable : @NO,
120 (id)kSecReturnAttributes: @YES,
121 (id)kSecReturnData: @YES,
124 CFTypeRef result = NULL;
125 OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
128 CFReleaseNull(result);
131 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
133 userInfo:@{NSLocalizedDescriptionKey:
134 [NSString stringWithFormat:@"SecItemCopyMatching: %d", (int)status]}];
139 NSDictionary* resultDict = CFBridgingRelease(result);
141 OTAccountMetadataClassC* state = [[OTAccountMetadataClassC alloc] initWithData:resultDict[(id)kSecValueData]];
144 *error = [NSError errorWithDomain:OctagonErrorDomain code:OTErrorDeserializationFailure description:@"couldn't deserialize account state"];
146 NSError* deleteError = nil;
147 BOOL deleted = [OTAccountMetadataClassC deleteFromKeychainForContainer:containerName contextID:contextID error:&deleteError];
148 if(deleted == NO || deleteError) {
149 secnotice("octagon", "failed to reset account metadata in keychain, %@", deleteError);
154 //check if an account state has the appropriate attributes
155 if(resultDict[(id)kSecAttrSysBound] == nil || ![resultDict[(id)kSecAttrAccessible] isEqualToString:(id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly]){
156 [state saveToKeychainForContainer:containerName contextID:contextID error:error];
162 #pragma mark - Field Coding support
164 - (void)setTPPolicy:(TPPolicy*)policy
167 NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
168 [policy encodeWithCoder:archiver];
169 self.syncingPolicy = archiver.encodedData;
171 self.syncingPolicy = nil;
175 - (TPPolicy* _Nullable)getTPPolicy
177 NSKeyedUnarchiver *coder = [[NSKeyedUnarchiver alloc] initForReadingFromData:self.syncingPolicy error:nil];
178 TPPolicy* policy = [[TPPolicy alloc] initWithCoder:coder];
179 [coder finishDecoding];