]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSPeerOTRTimer.m
Security-58286.51.6.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 __unused static int initialOTRTimeoutValue = 60; //best round trip time in KVS plus extra for good measure
29 static int maxRetryCount = 7; //max number of times to attempt restarting OTR negotiation
30
31 bool SOSPeerOTRTimerHaveReachedMaxRetryAllowance(SOSAccount* account, NSString* peerid){
32 bool reachedMax = false;
33 NSMutableDictionary* attemptsPerPeer = (__bridge NSMutableDictionary*)SOSAccountGetValue(account, kSOSAccountRenegotiationRetryCount, NULL);
34 if(!attemptsPerPeer){
35 attemptsPerPeer = [NSMutableDictionary dictionary];
36 }
37 NSNumber* attempt = [attemptsPerPeer objectForKey:peerid];
38 if(attempt && [attempt intValue] >= maxRetryCount)
39 {
40 reachedMax = true;
41 SecADAddValueForScalarKey((__bridge CFStringRef) SecSOSAggdMaxRenegotiation,1);
42 }
43 return reachedMax;
44 }
45
46 //used when evaluating whether or not securityd should start a timer for negotiation
47 bool SOSPeerOTRTimerHaveAnRTTAvailable(SOSAccount* account, NSString* peerid)
48 {
49 CFErrorRef error = NULL;
50 CFNumberRef timeout = NULL;
51 bool doesRTTExist = false;
52
53 CFMutableDictionaryRef timeouts = (CFMutableDictionaryRef)asDictionary(SOSAccountGetValue(account, kSOSAccountPeerNegotiationTimeouts, &error), NULL);
54 require_action_quiet(timeouts, exit, secnotice("otrtimer", "do not have an rtt yet"));
55
56 timeout = CFDictionaryGetValue(timeouts, (__bridge CFStringRef)peerid);
57 require_action_quiet(timeout, exit, secnotice("otrtimer", "do not have an rtt yet"));
58
59 doesRTTExist = true;
60 exit:
61 return doesRTTExist;
62 }
63
64 //call when a timer has fired, remove the current rtt entry as the existing one isn't working
65 void SOSPeerOTRTimerRemoveRTTTimeoutForPeer(SOSAccount* account, NSString* peerid)
66 {
67 CFNumberRef timeout = NULL;
68 CFErrorRef error = NULL;
69
70 CFMutableDictionaryRef timeouts = (CFMutableDictionaryRef)asDictionary(SOSAccountGetValue(account, kSOSAccountPeerNegotiationTimeouts, &error), NULL);
71 require_action_quiet(timeouts && (error == NULL), exit, secnotice("otrtimer","timeout dictionary doesn't exist"));
72
73 timeout = CFDictionaryGetValue(timeouts, (__bridge CFStringRef)peerid);
74 require_action_quiet(timeout, exit, secnotice("otrtimer","timeout for peerid: %@, doesn't exist", (__bridge CFStringRef)peerid));
75
76 CFDictionaryRemoveValue(timeouts, (__bridge CFStringRef)peerid);
77 SOSAccountSetValue(account, kSOSAccountPeerNegotiationTimeouts, timeouts, &error);
78 if(error){
79 secnotice("otrtimer","SOSAccountSetValue threw an error for key kSOSAccountPeerNegotiationTimeouts: %@", error);
80 }
81 exit:
82 CFReleaseNull(error);
83 }
84
85 void SOSPeerOTRTimerRemoveLastSentMessageTimestamp(SOSAccount* account, NSString* peerid)
86 {
87 NSMutableDictionary *peerToTimeLastSentDict = (__bridge NSMutableDictionary*)SOSAccountGetValue(account, kSOSAccountPeerLastSentTimestamp, NULL);
88
89 if(peerToTimeLastSentDict){
90 NSDate* storedDate = [peerToTimeLastSentDict objectForKey:peerid];
91 if(storedDate){
92 [peerToTimeLastSentDict removeObjectForKey:peerid];
93 SOSAccountSetValue(account, kSOSAccountPeerLastSentTimestamp, (__bridge CFMutableDictionaryRef)peerToTimeLastSentDict, NULL);
94 }
95 }
96 }
97
98 void SOSPeerOTRTimerIncreaseOTRNegotiationRetryCount(SOSAccount* account, NSString* peerid)
99 {
100 NSMutableDictionary* attemptsPerPeer = (__bridge NSMutableDictionary*)SOSAccountGetValue(account, kSOSAccountRenegotiationRetryCount, NULL);
101 if(!attemptsPerPeer){
102 attemptsPerPeer = [NSMutableDictionary dictionary];
103 }
104 NSNumber* attempt = [attemptsPerPeer objectForKey:peerid];
105 if(!attempt){
106 attempt = [[NSNumber alloc] initWithInt:1];
107 [attemptsPerPeer setObject:attempt forKey:peerid];
108 }
109 else{
110 NSNumber* newCount = [[NSNumber alloc]initWithInt:([attempt intValue]+1)];
111 [attemptsPerPeer setObject:newCount forKey:peerid];
112 secnotice("otr","OTR negotiation retry count: %d", [newCount intValue]);
113 }
114 SOSAccountSetValue(account, kSOSAccountRenegotiationRetryCount, (__bridge CFMutableDictionaryRef)attemptsPerPeer, NULL);
115 }
116
117 int SOSPeerOTRTimerTimeoutValue(SOSAccount* account, SOSPeerRef peer)
118 {
119 CFErrorRef error = NULL;
120 int timeoutIntValue = 0;
121
122 CFMutableDictionaryRef timeouts = (CFMutableDictionaryRef)asDictionary(SOSAccountGetValue(account, kSOSAccountPeerNegotiationTimeouts, &error), NULL);
123 require_action_quiet(timeouts, xit, secnotice("otrtimer","deadline value not available yet"));
124
125 CFNumberRef timeout = CFDictionaryGetValue(timeouts, SOSPeerGetID(peer));
126 require_action_quiet(timeout, xit, secnotice("otrtimer","deadline value not available yet"));
127
128 secnotice("otrtimer", "decided to wait %d before restarting negotiation", [(__bridge NSNumber*)timeout intValue]);
129 timeoutIntValue = [(__bridge NSNumber*)timeout intValue];
130
131 xit:
132 return timeoutIntValue;
133 }
134
135 void SOSPeerOTRTimerSetupAwaitingTimer(SOSAccount* account, SOSPeerRef peer, SOSEngineRef engine, SOSCoderRef coder)
136 {
137 //check which timeout value to use
138 int timeoutValue = SOSPeerOTRTimerTimeoutValue(account, peer);
139 CFStringRef peerid = CFRetainSafe(SOSPeerGetID(peer));
140
141 secnotice("otrtimer", "setting timer for peer: %@", peer);
142 __block dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
143 dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, timeoutValue * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0);
144 dispatch_source_set_event_handler(timer, ^{
145 secnotice("otrtimer","otrTimerFired fired");
146 SOSCCResetOTRNegotiation_Server(peerid);
147
148 });
149
150 dispatch_source_set_cancel_handler(timer, ^{
151 CFReleaseSafe(peerid);
152 });
153
154 dispatch_resume(timer);
155
156 SOSPeerSetOTRTimer(peer, timer);
157 }
158
159 //clear the max retry counter in the account object
160 void SOSPeerOTRTimerClearMaxRetryCount(SOSAccount* account, NSString* peerid)
161 {
162 secnotice("otrtimer", "negotiation finished! clearing max retry counter");
163 NSMutableDictionary* attemptsPerPeer = (__bridge NSMutableDictionary*)SOSAccountGetValue(account, kSOSAccountRenegotiationRetryCount, NULL);
164 if(!attemptsPerPeer){
165 attemptsPerPeer = [NSMutableDictionary dictionary];
166 }
167 [attemptsPerPeer removeObjectForKey:peerid];
168 SOSAccountSetValue(account, kSOSAccountRenegotiationRetryCount, (__bridge CFMutableDictionaryRef)attemptsPerPeer, NULL);
169 }
170