]> git.saurik.com Git - apple/security.git/blob - keychain/escrowrequest/SecEscrowPendingRecord+KeychainSupport.m
Security-59306.61.1.tar.gz
[apple/security.git] / keychain / escrowrequest / SecEscrowPendingRecord+KeychainSupport.m
1
2 #import <Security/Security.h>
3 #import <Security/SecItemPriv.h>
4 #import "OSX/sec/Security/SecItemShim.h"
5
6 #import "OSX/utilities/SecCFRelease.h"
7 #import "utilities/debugging.h"
8
9 #import "SecEscrowPendingRecord+KeychainSupport.h"
10 #import "keychain/categories/NSError+UsefulConstructors.h"
11
12 @implementation SecEscrowPendingRecord (KeychainSupport)
13
14 - (BOOL)saveToKeychain:(NSError**)error
15 {
16 NSMutableDictionary* query = [@{
17 (id)kSecClass : (id)kSecClassInternetPassword,
18 (id)kSecAttrAccessible: (id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
19 (id)kSecAttrAccessGroup: @"com.apple.sbd",
20 (id)kSecAttrServer: @"escrow-prerecord",
21 (id)kSecAttrDescription: [NSString stringWithFormat:@"Escrow Prerecord: %@", self.uuid],
22 (id)kSecAttrAccount: self.uuid,
23 (id)kSecValueData : self.data,
24 (id)kSecAttrIsInvisible: @YES,
25 (id)kSecUseDataProtectionKeychain : @YES,
26 (id)kSecAttrSynchronizable : @NO,
27 } mutableCopy];
28
29 CFTypeRef result = NULL;
30 OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, &result);
31
32 NSError* localerror = nil;
33
34 // Did SecItemAdd fall over due to an existing item?
35 if(status == errSecDuplicateItem) {
36 // Add every primary key attribute to this find dictionary
37 NSMutableDictionary* findQuery = [[NSMutableDictionary alloc] init];
38 findQuery[(id)kSecClass] = query[(id)kSecClass];
39 findQuery[(id)kSecAttrSynchronizable] = query[(id)kSecAttrSynchronizable];
40 findQuery[(id)kSecAttrSyncViewHint] = query[(id)kSecAttrSyncViewHint];
41 findQuery[(id)kSecAttrAccessGroup] = query[(id)kSecAttrAccessGroup];
42 findQuery[(id)kSecAttrAccount] = query[(id)kSecAttrAccount];
43 findQuery[(id)kSecAttrServer] = query[(id)kSecAttrServer];
44 findQuery[(id)kSecAttrPath] = query[(id)kSecAttrPath];
45 findQuery[(id)kSecUseDataProtectionKeychain] = query[(id)kSecUseDataProtectionKeychain];
46
47 NSMutableDictionary* updateQuery = [query mutableCopy];
48 updateQuery[(id)kSecClass] = nil;
49
50 status = SecItemUpdate((__bridge CFDictionaryRef)findQuery, (__bridge CFDictionaryRef)updateQuery);
51
52 if(status) {
53 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
54 code:status
55 description:[NSString stringWithFormat:@"SecItemUpdate: %d", (int)status]];
56 }
57 } else if(status != 0) {
58 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
59 code:status
60 description:[NSString stringWithFormat:@"SecItemAdd: %d", (int)status]];
61 }
62
63 if(localerror) {
64 if(error) {
65 *error = localerror;
66 }
67 return false;
68 } else {
69 return true;
70 }
71 }
72
73 - (BOOL)deleteFromKeychain:(NSError**)error
74 {
75 NSMutableDictionary* query = [@{
76 (id)kSecClass : (id)kSecClassInternetPassword,
77 (id)kSecAttrAccessGroup: @"com.apple.sbd",
78 (id)kSecAttrServer: @"escrow-prerecord",
79 (id)kSecAttrAccount: self.uuid,
80 (id)kSecUseDataProtectionKeychain : @YES,
81 (id)kSecAttrSynchronizable : @NO,
82 } mutableCopy];
83
84 OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
85
86 if(status != errSecSuccess) {
87 if(error) {
88 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
89 code:status
90 description:[NSString stringWithFormat:@"SecItemAdd: %d", (int)status]];;
91 }
92 return false;
93 }
94
95 return true;
96 }
97
98 + (SecEscrowPendingRecord* _Nullable)loadFromKeychain:(NSString*)uuid error:(NSError**)error
99 {
100 NSMutableDictionary* query = [@{
101 (id)kSecClass : (id)kSecClassInternetPassword,
102 (id)kSecAttrAccessGroup: @"com.apple.sbd",
103 (id)kSecAttrServer: @"escrow-prerecord",
104 (id)kSecAttrAccount: uuid,
105 (id)kSecMatchLimit : (id)kSecMatchLimitOne,
106 (id)kSecAttrSynchronizable : @NO,
107 (id)kSecUseDataProtectionKeychain : @YES,
108 (id)kSecReturnAttributes: @YES,
109 (id)kSecReturnData: @YES,
110 } mutableCopy];
111
112 CFTypeRef result = NULL;
113 OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
114
115 if(status) {
116 CFReleaseNull(result);
117
118 if(error) {
119 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
120 code:status
121 userInfo:@{NSLocalizedDescriptionKey:
122 [NSString stringWithFormat:@"SecItemCopyMatching: %d", (int)status]}];
123 }
124 return nil;
125 }
126
127 NSDictionary* resultDict = (NSDictionary*) CFBridgingRelease(result);
128 SecEscrowPendingRecord* record = [[SecEscrowPendingRecord alloc] initWithData:resultDict[(id)kSecValueData]];
129
130 //TODO: if no record, add an error here
131
132 return record;
133 }
134
135 + (NSArray<SecEscrowPendingRecord*>* _Nullable)loadAllFromKeychain:(NSError**)error
136 {
137 NSMutableDictionary* query = [@{
138 (id)kSecClass : (id)kSecClassInternetPassword,
139 (id)kSecAttrAccessGroup: @"com.apple.sbd",
140 (id)kSecAttrServer: @"escrow-prerecord",
141 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
142 (id)kSecAttrSynchronizable : @NO,
143 (id)kSecUseDataProtectionKeychain : @YES,
144 (id)kSecReturnAttributes: @YES,
145 (id)kSecReturnData: @YES,
146 } mutableCopy];
147
148 CFTypeRef result = NULL;
149 OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
150
151 if(status) {
152 CFReleaseNull(result);
153
154 if(error) {
155 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
156 code:status
157 userInfo:@{NSLocalizedDescriptionKey:
158 [NSString stringWithFormat:@"SecItemCopyMatching: %d", (int)status]}];
159 }
160 return nil;
161 }
162
163 NSMutableArray<SecEscrowPendingRecord*>* records = [NSMutableArray array];
164 NSDictionary* resultArray = CFBridgingRelease(result);
165
166 for(NSDictionary* item in resultArray) {
167 SecEscrowPendingRecord* record = [[SecEscrowPendingRecord alloc] initWithData:item[(id)kSecValueData]];
168 if(record) {
169 [records addObject: record];
170 } else {
171 secerror("escrowrequest: Unable to deserialize keychain item");
172 }
173 }
174
175 return records;
176 }
177
178 @end
179
180 @implementation SecEscrowPendingRecord (EscrowAttemptTimeout)
181 - (BOOL)escrowAttemptedWithinLastSeconds:(NSTimeInterval)timeInterval
182 {
183 NSDate* limitDate = [NSDate dateWithTimeIntervalSinceNow:-timeInterval];
184 uint64_t limitMillis = [limitDate timeIntervalSince1970] * 1000;
185
186 if(self.hasLastEscrowAttemptTime && self.lastEscrowAttemptTime >= limitMillis) {
187 return YES;
188 }
189 return NO;
190 }
191 @end