]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSPeerOTRTimer.m
Security-58286.41.2.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSPeerOTRTimer.m
1 //
2 // SOSPeerOTRTimer.m
3 //
4
5 #import <Foundation/Foundation.h>
6 #include <Security/SecureObjectSync/SOSPeer.h>
7 #include <Security/SecureObjectSync/SOSPeerCoder.h>
8 #include <Security/SecureObjectSync/SOSTransportMessage.h>
9 #include <Security/SecureObjectSync/SOSAccount.h>
10 #include <Security/SecureObjectSync/SOSCoder.h>
11 #include <Security/SecureObjectSync/SOSEngine.h>
12 #include <Security/SecureObjectSync/SOSDataSource.h>
13 #include <Security/SecureObjectSync/SOSAccountTransaction.h>
14 #include <Security/SecureObjectSync/SOSKVSKeys.h>
15 #include <Security/SecureObjectSync/SOSPeerOTRTimer.h>
16 #include <Security/CKBridge/SOSCloudKeychainClient.h>
17
18 #include <utilities/SecADWrapper.h>
19 #include <utilities/debugging.h>
20 #include <utilities/SecCFWrappers.h>
21
22 #include <AssertMacros.h>
23 #include "SOSInternal.h"
24
25 //AGGD
26 NSString* const SecSOSAggdMaxRenegotiation = @"com.apple.security.sos.otrrenegotiationmaxretries";
27
28
29 static const CFStringRef OTRTimeoutsPerPeer = CFSTR("OTRTimeoutsPerPeer");
30 static const CFStringRef OTRConfigVersion = CFSTR("OTRConfigVersion");
31
32 __unused static int initialOTRTimeoutValue = 60; //best round trip time in KVS plus extra for good measure
33 static int maxRetryCount = 7; //max number of times to attempt restarting OTR negotiation
34
35 bool SOSPeerOTRTimerHaveReachedMaxRetryAllowance(SOSAccount* account, NSString* peerid){
36 bool reachedMax = false;
37 NSMutableDictionary* attemptsPerPeer = (__bridge NSMutableDictionary*)SOSAccountGetValue(account, kSOSAccountRenegotiationRetryCount, NULL);
38 if(!attemptsPerPeer){
39 attemptsPerPeer = [NSMutableDictionary dictionary];
40 }
41 NSNumber* attempt = [attemptsPerPeer objectForKey:peerid];
42 if(attempt && [attempt intValue] >= maxRetryCount)
43 {
44 reachedMax = true;
45 SecADAddValueForScalarKey((__bridge CFStringRef) SecSOSAggdMaxRenegotiation,1);
46 }
47 return reachedMax;
48 }
49
50 //used when evaluating whether or not securityd should start a timer for negotiation
51 bool SOSPeerOTRTimerHaveAnRTTAvailable(SOSAccount* account, NSString* peerid)
52 {
53 CFErrorRef error = NULL;
54 CFNumberRef timeout = NULL;
55 bool doesRTTExist = false;
56
57 CFMutableDictionaryRef timeouts = (CFMutableDictionaryRef)asDictionary(SOSAccountGetValue(account, kSOSAccountPeerNegotiationTimeouts, &error), NULL);
58 require_action_quiet(timeouts, exit, secnotice("otrtimer", "do not have an rtt yet"));
59
60 timeout = CFDictionaryGetValue(timeouts, (__bridge CFStringRef)peerid);
61 require_action_quiet(timeout, exit, secnotice("otrtimer", "do not have an rtt yet"));
62
63 doesRTTExist = true;
64 exit:
65 return doesRTTExist;
66 }
67
68 //call when a timer has fired, remove the current rtt entry as the existing one isn't working
69 void SOSPeerOTRTimerRemoveRTTTimeoutForPeer(SOSAccount* account, NSString* peerid)
70 {
71 CFNumberRef timeout = NULL;
72 CFErrorRef error = NULL;
73
74 CFMutableDictionaryRef timeouts = (CFMutableDictionaryRef)asDictionary(SOSAccountGetValue(account, kSOSAccountPeerNegotiationTimeouts, &error), NULL);
75 require_action_quiet(timeouts && (error == NULL), exit, secnotice("otrtimer","timeout dictionary doesn't exist"));
76
77 timeout = CFDictionaryGetValue(timeouts, (__bridge CFStringRef)peerid);
78 require_action_quiet(timeout, exit, secnotice("otrtimer","timeout for peerid: %@, doesn't exist", (__bridge CFStringRef)peerid));
79
80 CFDictionaryRemoveValue(timeouts, (__bridge CFStringRef)peerid);
81 SOSAccountSetValue(account, kSOSAccountPeerNegotiationTimeouts, timeouts, &error);
82 if(error){
83 secnotice("otrtimer","SOSAccountSetValue threw an error for key kSOSAccountPeerNegotiationTimeouts: %@", error);
84 }
85 exit:
86 CFReleaseNull(error);
87 }
88
89 void SOSPeerOTRTimerRemoveLastSentMessageTimestamp(SOSAccount* account, NSString* peerid)
90 {
91 NSMutableDictionary *peerToTimeLastSentDict = (__bridge NSMutableDictionary*)SOSAccountGetValue(account, kSOSAccountPeerLastSentTimestamp, NULL);
92
93 if(peerToTimeLastSentDict){
94 NSDate* storedDate = [peerToTimeLastSentDict objectForKey:peerid];
95 if(storedDate){
96 [peerToTimeLastSentDict removeObjectForKey:peerid];
97 SOSAccountSetValue(account, kSOSAccountPeerLastSentTimestamp, (__bridge CFMutableDictionaryRef)peerToTimeLastSentDict, NULL);
98 }
99 }
100 }
101
102 void SOSPeerOTRTimerIncreaseOTRNegotiationRetryCount(SOSAccount* account, NSString* peerid)
103 {
104 NSMutableDictionary* attemptsPerPeer = (__bridge NSMutableDictionary*)SOSAccountGetValue(account, kSOSAccountRenegotiationRetryCount, NULL);
105 if(!attemptsPerPeer){
106 attemptsPerPeer = [NSMutableDictionary dictionary];
107 }
108 NSNumber* attempt = [attemptsPerPeer objectForKey:peerid];
109 if(!attempt){
110 attempt = [[NSNumber alloc] initWithInt:1];
111 [attemptsPerPeer setObject:attempt forKey:peerid];
112 }
113 else{
114 NSNumber* newCount = [[NSNumber alloc]initWithInt:([attempt intValue]+1)];
115 [attemptsPerPeer setObject:newCount forKey:peerid];
116 secnotice("otr","OTR negotiation retry count: %d", [newCount intValue]);
117 }
118 SOSAccountSetValue(account, kSOSAccountRenegotiationRetryCount, (__bridge CFMutableDictionaryRef)attemptsPerPeer, NULL);
119 }
120
121 static CFNumberRef SOSPeerOTRTimerCopyConfigVersion(SOSAccount* account)
122 {
123 uint64_t v = 0;
124 CFNumberRef versionFromAccount = NULL;
125 CFNumberRef version = (CFNumberRef)SOSAccountGetValue(account, OTRConfigVersion, NULL);
126
127 if(!version){
128 versionFromAccount = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &v);
129 SOSAccountSetValue(account, OTRConfigVersion, versionFromAccount, NULL);
130 }
131 else{
132 versionFromAccount = CFRetainSafe(version);
133 }
134 return versionFromAccount;
135 }
136
137 void SOSPeerOTRTimerCreateKVSConfigDict(SOSAccount* account, CFNumberRef timeout, CFStringRef peerid)
138 {
139 CFMutableDictionaryRef peerToTimeout = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
140 CFDictionaryAddValue(peerToTimeout, peerid, timeout);
141
142 CFNumberRef versionFromAccount = SOSPeerOTRTimerCopyConfigVersion(account);
143
144 CFMutableDictionaryRef peerTimeOutsAndVersion = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
145 CFDictionaryAddValue(peerTimeOutsAndVersion, OTRTimeoutsPerPeer, peerToTimeout);
146 CFDictionaryAddValue(peerTimeOutsAndVersion, OTRConfigVersion, versionFromAccount);
147
148 CFMutableDictionaryRef myPeerChanges = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
149 CFStringRef myPeerID = (__bridge CFStringRef) account.peerID;
150 CFDictionaryAddValue(myPeerChanges, myPeerID, peerTimeOutsAndVersion);
151
152 CFMutableDictionaryRef otrConfig = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
153 CFDictionaryAddValue(otrConfig, kSOSKVSOTRConfigVersion, myPeerChanges);
154
155 SOSCloudKeychainPutObjectsInCloud(otrConfig, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues __unused, CFErrorRef block_error) {
156 if (block_error) {
157 secerror("Error putting: %@", block_error);
158 }
159 });
160 secnotice("kvsconfig", "submitting config to KVS: %@", otrConfig);
161 CFReleaseNull(myPeerChanges);
162 CFReleaseNull(peerToTimeout);
163 CFReleaseNull(versionFromAccount);
164 CFReleaseNull(otrConfig);
165 }
166
167 //grab existing key from KVS
168 __unused __unused static CFDictionaryRef SOSPeerOTRTimerCopyConfigFromKVS()
169 {
170 CFErrorRef error = NULL;
171 __block CFTypeRef object = NULL;
172
173 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
174 const uint64_t maxTimeToWaitInSeconds = 5ull * NSEC_PER_SEC;
175
176 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
177
178 SOSCloudKeychainGetAllObjectsFromCloud(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef block_error) {
179 secnotice("otrtimer", "SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues);
180 object = returnedValues;
181 if (object)
182 CFRetain(object);
183 if (error)
184 {
185 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error);
186 }
187 secnotice("otrtimer", "SOSCloudKeychainGetObjectsFromCloud block exit: %@", object);
188 dispatch_semaphore_signal(waitSemaphore);
189 });
190
191 dispatch_semaphore_wait(waitSemaphore, finishTime);
192 if (object && (CFGetTypeID(object) == CFNullGetTypeID())) // return a NULL instead of a CFNull
193 {
194 CFRelease(object);
195 object = NULL;
196 return NULL;
197 }
198 if(isDictionary(object))
199 {
200 return CFRetainSafe((CFDictionaryRef)object);
201 }
202 return NULL;
203 }
204
205 __unused static bool SOSPeerOTRTimerShouldWriteConfig(CFDictionaryRef config, CFStringRef myID, CFStringRef peerid, CFNumberRef currentConfigVersion, CFNumberRef localTimeout)
206 {
207 bool result = true;
208 secnotice("otrtimer", "grabbed config from KVS: %@", config);
209 CFDictionaryRef myPeerConfigSettings = NULL;
210 CFNumberRef versionFromKVS = NULL;
211 CFDictionaryRef peerToTimeouts = NULL;
212 CFNumberRef timeoutInKVS = NULL;
213 CFDictionaryRef otrConfig = NULL;
214
215 require_action_quiet(currentConfigVersion, fail, secnotice("otrtimer","current config version is null"));
216 require_action_quiet(localTimeout, fail, secnotice("otrtimer", "local timeout is null"));
217
218 otrConfig = CFDictionaryGetValue(config, kSOSKVSOTRConfigVersion);
219 require_action_quiet(otrConfig, fail, secnotice("otrtimer","otr config does not exist"));
220
221 myPeerConfigSettings = CFDictionaryGetValue(otrConfig, myID);
222 require_action_quiet(myPeerConfigSettings, fail, secnotice("otrtimer","my peer config settings dictionary is null"));
223
224 versionFromKVS = CFDictionaryGetValue(myPeerConfigSettings, OTRConfigVersion);
225 require_action_quiet(versionFromKVS, fail, secnotice("otrtimer", "version from KVS is null"));
226
227 peerToTimeouts = CFDictionaryGetValue(myPeerConfigSettings, OTRTimeoutsPerPeer);
228 require_action_quiet(peerToTimeouts, fail, secnotice("otrtimer", "dictionary of peerids and timeout values is null"));
229
230 timeoutInKVS = CFDictionaryGetValue(peerToTimeouts, peerid);
231 require_action_quiet(timeoutInKVS, fail, secnotice("otrtimer", "timeout value from kvs is null"));
232
233 if(kCFCompareEqualTo == CFNumberCompare(currentConfigVersion, versionFromKVS, NULL) &&
234 (CFNumberCompare(timeoutInKVS, localTimeout, NULL) == kCFCompareEqualTo)){
235 secnotice("otrtimer", "versions match, can write new config");
236 }else if(CFNumberCompare(versionFromKVS, currentConfigVersion, NULL) == kCFCompareGreaterThan){
237 result = false;
238 secnotice("otrtimer", "versions do not match, cannot write a new config");
239 }else{
240 secnotice("otrtimer", "config versions match, going to write current configuration of peerids to timeouts to KVS");
241 }
242
243 fail:
244 return result;
245
246 }
247
248 __unused static CFNumberRef SOSPeerOTRTimerCopyOTRConfigVersionFromAccount(SOSAccount* account)
249 {
250 CFNumberRef version = SOSAccountGetValue(account, OTRConfigVersion, NULL);
251 if(!version){
252 uint64_t v = 0;
253 version = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &v);
254 SOSAccountSetValue(account, OTRConfigVersion, version, NULL);
255 }else{
256 return CFRetainSafe(version);
257 }
258
259 return version;
260 }
261
262 __unused static bool SOSPeerOTRTimerShouldUseTimeoutValueFromKVS(CFDictionaryRef otrConfigFromKVS, CFStringRef myID, CFNumberRef localConfigVersion){
263 bool shouldUseTimeoutFromKVS = false;
264 CFDictionaryRef otrConfig = NULL;
265 CFDictionaryRef myPeerConfigSettings = NULL;
266 CFNumberRef versionFromKVS = NULL;
267
268 require_action_quiet(otrConfigFromKVS, xit, secnotice("otrtimer", "configuration file from kvs does not exist"));
269
270 otrConfig = CFDictionaryGetValue(otrConfigFromKVS, kSOSKVSOTRConfigVersion);
271 require_action_quiet(otrConfig, xit, secnotice("otrtimer", "configuration file from kvs does not exist"));
272
273 myPeerConfigSettings = CFDictionaryGetValue(otrConfig, myID);
274 require_action_quiet(myPeerConfigSettings, xit, secnotice("otrtimer", "configuration file from kvs does not exist"));
275
276 versionFromKVS = CFDictionaryGetValue(myPeerConfigSettings, OTRConfigVersion);
277 require_action_quiet(versionFromKVS && (CFGetTypeID(versionFromKVS) != CFNullGetTypeID()), xit, secnotice("otrtimer", "configuration file from kvs does not exist"));
278
279 if(CFNumberCompare(versionFromKVS, localConfigVersion, NULL) == kCFCompareGreaterThan){
280 secnotice("otrtimer", "should use timeout from kvs");
281 shouldUseTimeoutFromKVS = true;
282 }
283
284 xit:
285 return shouldUseTimeoutFromKVS;
286 }
287
288 __unused static CFNumberRef SOSPeerOTRTimerTimeoutFromKVS(CFDictionaryRef otrConfigFromKVS, CFStringRef myID, CFStringRef peerID)
289 {
290 CFNumberRef timeout = NULL;
291 CFDictionaryRef otrConfig = NULL;
292 CFDictionaryRef myPeerConfigSettings = NULL;
293 CFDictionaryRef peerToTimeoutDictionary = NULL;
294
295 require_action_quiet(otrConfigFromKVS, xit, secnotice("otrtimer", "configuration file from kvs does not exist"));
296
297 otrConfig = CFDictionaryGetValue(otrConfigFromKVS, kSOSKVSOTRConfigVersion);
298 require_action_quiet(otrConfig, xit, secnotice("otrtimer", "configuration file from kvs does not exist"));
299
300 myPeerConfigSettings = CFDictionaryGetValue(otrConfig, myID);
301 require_action_quiet(myPeerConfigSettings, xit, secnotice("otrtimer", "configuration file from kvs does not exist"));
302
303 peerToTimeoutDictionary = CFDictionaryGetValue(myPeerConfigSettings, OTRTimeoutsPerPeer);
304 require_action_quiet(peerToTimeoutDictionary, xit, secnotice("otrtimer", "configuration file from kvs does not exist"));
305
306 timeout = CFDictionaryGetValue(peerToTimeoutDictionary, peerID);
307 xit:
308 return timeout;
309 }
310
311 int SOSPeerOTRTimerTimeoutValue(SOSAccount* account, SOSPeerRef peer)
312 {
313 CFErrorRef error = NULL;
314 //bool shouldWriteConfig = true;
315 //bool shouldUseTimeoutFromKVS = false;
316 int timeoutIntValue = 0;
317
318 //CFDictionaryRef configFromKVS = SOSPeerOTRTimerCopyConfigFromKVS();
319
320 //CFNumberRef configVersion = SOSPeerOTRTimerCopyOTRConfigVersionFromAccount(account);
321 //shouldUseTimeoutFromKVS = SOSPeerOTRTimerShouldUseTimeoutValueFromKVS(configFromKVS, (__bridge CFStringRef)account.peerID,configVersion);
322 //CFReleaseNull(configVersion);
323
324 //if(shouldUseTimeoutFromKVS){
325 // secnotice("otrtimer", "using timeout from kvs");
326 //CFNumberRef timeoutFromKVS = SOSPeerOTRTimerTimeoutFromKVS(configFromKVS, (__bridge CFStringRef)account.peerID, //SOSPeerGetID(peer));
327 //CFReleaseNull(configFromKVS);
328 //return [(__bridge NSNumber*)timeoutFromKVS intValue];
329 // }
330
331 CFMutableDictionaryRef timeouts = (CFMutableDictionaryRef)asDictionary(SOSAccountGetValue(account, kSOSAccountPeerNegotiationTimeouts, &error), NULL);
332 require_action_quiet(timeouts, xit, secnotice("otrtimer","deadline value not available yet"));
333
334 CFNumberRef timeout = CFDictionaryGetValue(timeouts, SOSPeerGetID(peer));
335 require_action_quiet(timeout, xit, secnotice("otrtimer","deadline value not available yet"));
336
337 secnotice("otrtimer", "decided to wait %d before restarting negotiation", [(__bridge NSNumber*)timeout intValue]);
338 timeoutIntValue = [(__bridge NSNumber*)timeout intValue];
339
340 //CFNumberRef localConfigVersion = SOSPeerOTRTimerCopyOTRConfigVersionFromAccount(account);
341 /*
342 if(localConfigVersion){
343 shouldWriteConfig = SOSPeerOTRTimerShouldWriteConfig(configFromKVS, (__bridge CFStringRef)account.peerID, SOSPeerGetID(peer), localConfigVersion, timeout);
344 }
345
346 if(shouldWriteConfig)
347 SOSPeerOTRTimerCreateKVSConfigDict(account, timeout, SOSPeerGetID(peer));
348 */
349
350 xit:
351 // CFReleaseNull(configVersion);
352 //CFReleaseNull(localConfigVersion);
353 //CFReleaseNull(configFromKVS);
354
355 return timeoutIntValue;
356 }
357
358 void SOSPeerOTRTimerSetupAwaitingTimer(SOSAccount* account, SOSPeerRef peer, SOSEngineRef engine, SOSCoderRef coder)
359 {
360 //check which timeout value to use
361 int timeoutValue = SOSPeerOTRTimerTimeoutValue(account, peer);
362 CFStringRef peerid = CFRetainSafe(SOSPeerGetID(peer));
363
364 secnotice("otrtimer", "setting timer for peer: %@", peer);
365 __block dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
366 dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, timeoutValue * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0);
367 dispatch_source_set_event_handler(timer, ^{
368 secnotice("otrtimer","otrTimerFired fired");
369 SOSCCResetOTRNegotiation_Server(peerid);
370
371 });
372
373 dispatch_source_set_cancel_handler(timer, ^{
374 CFReleaseSafe(peerid);
375 });
376
377 dispatch_resume(timer);
378
379 SOSPeerSetOTRTimer(peer, timer);
380 }
381
382 //clear the max retry counter in the account object
383 void SOSPeerOTRTimerClearMaxRetryCount(SOSAccount* account, NSString* peerid)
384 {
385 secnotice("otrtimer", "negotiation finished! clearing max retry counter");
386 NSMutableDictionary* attemptsPerPeer = (__bridge NSMutableDictionary*)SOSAccountGetValue(account, kSOSAccountRenegotiationRetryCount, NULL);
387 if(!attemptsPerPeer){
388 attemptsPerPeer = [NSMutableDictionary dictionary];
389 }
390 [attemptsPerPeer removeObjectForKey:peerid];
391 SOSAccountSetValue(account, kSOSAccountRenegotiationRetryCount, (__bridge CFMutableDictionaryRef)attemptsPerPeer, NULL);
392 }
393