]> git.saurik.com Git - apple/security.git/blob - keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.m
Security-59306.11.20.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
16 @implementation OTAccountMetadataClassC (KeychainSupport)
17
18
19 - (BOOL)saveToKeychainForContainer:(NSString*)containerName
20 contextID:(NSString*)contextID
21 error:(NSError**)error
22 {
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),
36 } mutableCopy];
37
38 CFTypeRef result = NULL;
39 OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, &result);
40
41 NSError* localerror = nil;
42
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];
55
56 NSMutableDictionary* updateQuery = [query mutableCopy];
57 updateQuery[(id)kSecClass] = nil;
58
59 status = SecItemUpdate((__bridge CFDictionaryRef)findQuery, (__bridge CFDictionaryRef)updateQuery);
60
61 if(status) {
62 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
63 code:status
64 description:[NSString stringWithFormat:@"SecItemUpdate: %d", (int)status]];
65 }
66 } else if(status != 0) {
67 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
68 code:status
69 description: [NSString stringWithFormat:@"SecItemAdd: %d", (int)status]];
70 }
71
72 if(localerror) {
73 if(error) {
74 *error = localerror;
75 }
76 return false;
77 } else {
78 return true;
79 }
80 }
81
82 + (BOOL) deleteFromKeychainForContainer:(NSString*)containerName
83 contextID:(NSString*)contextID error:(NSError**)error
84 {
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,
93 } mutableCopy];
94
95 OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
96 if(status) {
97 if(error) {
98 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
99 code:status
100 userInfo:@{NSLocalizedDescriptionKey:
101 [NSString stringWithFormat:@"SecItemDelete: %d", (int)status]}];
102 }
103 return NO;
104 }
105 return YES;
106 }
107
108 + (OTAccountMetadataClassC* _Nullable)loadFromKeychainForContainer:(NSString*)containerName
109 contextID:(NSString*)contextID error:(NSError**)error
110 {
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,
121 } mutableCopy];
122
123 CFTypeRef result = NULL;
124 OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
125
126 if(status) {
127 CFReleaseNull(result);
128
129 if(error) {
130 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
131 code:status
132 userInfo:@{NSLocalizedDescriptionKey:
133 [NSString stringWithFormat:@"SecItemCopyMatching: %d", (int)status]}];
134 }
135 return nil;
136 }
137
138 NSDictionary* resultDict = CFBridgingRelease(result);
139
140 OTAccountMetadataClassC* state = [[OTAccountMetadataClassC alloc] initWithData:resultDict[(id)kSecValueData]];
141 if(!state) {
142 if(error) {
143 *error = [NSError errorWithDomain:OctagonErrorDomain code:OTErrorDeserializationFailure description:@"couldn't deserialize account state"];
144 }
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);
149 }
150 return nil;
151 }
152
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];
156 }
157
158 return state;
159 }
160
161 @end
162
163 #endif // OCTAGON