]> git.saurik.com Git - apple/security.git/blob - keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.m
Security-59754.41.1.tar.gz
[apple/security.git] / keychain / ot / categories / OTAccountMetadataClassC+KeychainSupport.m
1
2 #if OCTAGON
3
4 #import <Security/Security.h>
5 #import <Security/SecItemPriv.h>
6 #import "OSX/sec/Security/SecItemShim.h"
7
8 #import "OSX/utilities/SecCFRelease.h"
9
10 #import "OTAccountMetadataClassC+KeychainSupport.h"
11 #import "keychain/categories/NSError+UsefulConstructors.h"
12
13 #import "keychain/ot/OTDefines.h"
14 #import "keychain/ot/OTConstants.h"
15 #import <TrustedPeers/TPSyncingPolicy.h>
16
17 @implementation OTAccountMetadataClassC (KeychainSupport)
18
19
20 - (BOOL)saveToKeychainForContainer:(NSString*)containerName
21 contextID:(NSString*)contextID
22 error:(NSError**)error
23 {
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),
37 } mutableCopy];
38
39 CFTypeRef result = NULL;
40 OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, &result);
41
42 NSError* localerror = nil;
43
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];
56
57 NSMutableDictionary* updateQuery = [query mutableCopy];
58 updateQuery[(id)kSecClass] = nil;
59
60 status = SecItemUpdate((__bridge CFDictionaryRef)findQuery, (__bridge CFDictionaryRef)updateQuery);
61
62 if(status) {
63 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
64 code:status
65 description:[NSString stringWithFormat:@"SecItemUpdate: %d", (int)status]];
66 }
67 } else if(status != 0) {
68 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
69 code:status
70 description: [NSString stringWithFormat:@"SecItemAdd: %d", (int)status]];
71 }
72
73 if(localerror) {
74 if(error) {
75 *error = localerror;
76 }
77 return false;
78 } else {
79 return true;
80 }
81 }
82
83 + (BOOL) deleteFromKeychainForContainer:(NSString*)containerName
84 contextID:(NSString*)contextID error:(NSError**)error
85 {
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,
94 } mutableCopy];
95
96 OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
97 if(status) {
98 if(error) {
99 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
100 code:status
101 userInfo:@{NSLocalizedDescriptionKey:
102 [NSString stringWithFormat:@"SecItemDelete: %d", (int)status]}];
103 }
104 return NO;
105 }
106 return YES;
107 }
108
109 + (OTAccountMetadataClassC* _Nullable)loadFromKeychainForContainer:(NSString*)containerName
110 contextID:(NSString*)contextID error:(NSError**)error
111 {
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,
122 } mutableCopy];
123
124 CFTypeRef result = NULL;
125 OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
126
127 if(status) {
128 CFReleaseNull(result);
129
130 if(error) {
131 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
132 code:status
133 userInfo:@{NSLocalizedDescriptionKey:
134 [NSString stringWithFormat:@"SecItemCopyMatching: %d", (int)status]}];
135 }
136 return nil;
137 }
138
139 NSDictionary* resultDict = CFBridgingRelease(result);
140
141 OTAccountMetadataClassC* state = [[OTAccountMetadataClassC alloc] initWithData:resultDict[(id)kSecValueData]];
142 if(!state) {
143 if(error) {
144 *error = [NSError errorWithDomain:OctagonErrorDomain code:OTErrorDeserializationFailure description:@"couldn't deserialize account state"];
145 }
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);
150 }
151 return nil;
152 }
153
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];
157 }
158
159 return state;
160 }
161
162 #pragma mark - Field Coding support
163
164 - (void)setTPSyncingPolicy:(TPSyncingPolicy*)policy
165 {
166 if(policy) {
167 NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
168 [policy encodeWithCoder:archiver];
169 self.syncingPolicy = archiver.encodedData;
170 } else {
171 self.syncingPolicy = nil;
172 }
173 }
174
175 - (TPSyncingPolicy* _Nullable)getTPSyncingPolicy
176 {
177 NSKeyedUnarchiver *coder = [[NSKeyedUnarchiver alloc] initForReadingFromData:self.syncingPolicy error:nil];
178 TPSyncingPolicy* policy = [[TPSyncingPolicy alloc] initWithCoder:coder];
179 [coder finishDecoding];
180
181 return policy;
182 }
183
184 @end
185
186 #endif // OCTAGON