SOSPeerOTRTimer.m
1 // 2 // SOSPeerOTRTimer.m 3 // 4 5 #import <Foundation/Foundation.h> 6 #include "keychain/SecureObjectSync/SOSPeer.h" 7 #include "keychain/SecureObjectSync/SOSPeerCoder.h" 8 #include "keychain/SecureObjectSync/SOSTransportMessage.h" 9 #include "keychain/SecureObjectSync/SOSAccount.h" 10 #include "keychain/SecureObjectSync/SOSCoder.h" 11 #include "keychain/SecureObjectSync/SOSEngine.h" 12 #include "keychain/SecureObjectSync/SOSDataSource.h" 13 #include "keychain/SecureObjectSync/SOSAccountTransaction.h" 14 #include "keychain/SecureObjectSync/SOSKVSKeys.h" 15 #include "keychain/SecureObjectSync/SOSPeerOTRTimer.h" 16 #include "keychain/SecureObjectSync/CKBridge/SOSCloudKeychainClient.h" 17 18 #include <utilities/debugging.h> 19 #include <utilities/SecCFWrappers.h> 20 21 #include <AssertMacros.h> 22 #include "keychain/SecureObjectSync/SOSInternal.h" 23 24 static int maxRetryCount = 7; //max number of times to attempt restarting OTR negotiation 25 26 bool SOSPeerOTRTimerHaveReachedMaxRetryAllowance(SOSAccount* account, NSString* peerid){ 27 bool reachedMax = false; 28 NSMutableDictionary* attemptsPerPeer = (__bridge NSMutableDictionary*)SOSAccountGetValue(account, kSOSAccountRenegotiationRetryCount, NULL); 29 if(!attemptsPerPeer){ 30 attemptsPerPeer = [NSMutableDictionary dictionary]; 31 } 32 NSNumber* attempt = [attemptsPerPeer objectForKey:peerid]; 33 if(attempt && [attempt intValue] >= maxRetryCount) 34 { 35 reachedMax = true; 36 } 37 return reachedMax; 38 } 39 40 //used when evaluating whether or not securityd should start a timer for negotiation 41 bool SOSPeerOTRTimerHaveAnRTTAvailable(SOSAccount* account, NSString* peerid) 42 { 43 CFErrorRef error = NULL; 44 CFNumberRef timeout = NULL; 45 bool doesRTTExist = false; 46 47 CFMutableDictionaryRef timeouts = (CFMutableDictionaryRef)asDictionary(SOSAccountGetValue(account, kSOSAccountPeerNegotiationTimeouts, &error), NULL); 48 require_action_quiet(timeouts, exit, secnotice("otrtimer", "do not have an rtt yet")); 49 50 timeout = CFDictionaryGetValue(timeouts, (__bridge CFStringRef)peerid); 51 require_action_quiet(timeout, exit, secnotice("otrtimer", "do not have an rtt yet")); 52 53 doesRTTExist = true; 54 exit: 55 return doesRTTExist; 56 } 57 58 //call when a timer has fired, remove the current rtt entry as the existing one isn't working 59 void SOSPeerOTRTimerRemoveRTTTimeoutForPeer(SOSAccount* account, NSString* peerid) 60 { 61 CFNumberRef timeout = NULL; 62 CFErrorRef error = NULL; 63 64 CFMutableDictionaryRef timeouts = (CFMutableDictionaryRef)asDictionary(SOSAccountGetValue(account, kSOSAccountPeerNegotiationTimeouts, &error), NULL); 65 require_action_quiet(timeouts && (error == NULL), exit, secnotice("otrtimer","timeout dictionary doesn't exist")); 66 67 timeout = CFDictionaryGetValue(timeouts, (__bridge CFStringRef)peerid); 68 require_action_quiet(timeout, exit, secnotice("otrtimer","timeout for peerid: %@, doesn't exist", (__bridge CFStringRef)peerid)); 69 70 CFDictionaryRemoveValue(timeouts, (__bridge CFStringRef)peerid); 71 SOSAccountSetValue(account, kSOSAccountPeerNegotiationTimeouts, timeouts, &error); 72 if(error){ 73 secnotice("otrtimer","SOSAccountSetValue threw an error for key kSOSAccountPeerNegotiationTimeouts: %@", error); 74 } 75 exit: 76 CFReleaseNull(error); 77 } 78 79 void SOSPeerOTRTimerRemoveLastSentMessageTimestamp(SOSAccount* account, NSString* peerid) 80 { 81 NSMutableDictionary *peerToTimeLastSentDict = (__bridge NSMutableDictionary*)SOSAccountGetValue(account, kSOSAccountPeerLastSentTimestamp, NULL); 82 83 if(peerToTimeLastSentDict){ 84 NSDate* storedDate = [peerToTimeLastSentDict objectForKey:peerid]; 85 if(storedDate){ 86 [peerToTimeLastSentDict removeObjectForKey:peerid]; 87 SOSAccountSetValue(account, kSOSAccountPeerLastSentTimestamp, (__bridge CFMutableDictionaryRef)peerToTimeLastSentDict, NULL); 88 } 89 } 90 } 91 92 void SOSPeerOTRTimerIncreaseOTRNegotiationRetryCount(SOSAccount* account, NSString* peerid) 93 { 94 NSMutableDictionary* attemptsPerPeer = (__bridge NSMutableDictionary*)SOSAccountGetValue(account, kSOSAccountRenegotiationRetryCount, NULL); 95 if(!attemptsPerPeer){ 96 attemptsPerPeer = [NSMutableDictionary dictionary]; 97 } 98 NSNumber* attempt = [attemptsPerPeer objectForKey:peerid]; 99 if(!attempt){ 100 attempt = [[NSNumber alloc] initWithInt:1]; 101 [attemptsPerPeer setObject:attempt forKey:peerid]; 102 } 103 else{ 104 NSNumber* newCount = [[NSNumber alloc]initWithInt:([attempt intValue]+1)]; 105 [attemptsPerPeer setObject:newCount forKey:peerid]; 106 secnotice("otr","OTR negotiation retry count: %d", [newCount intValue]); 107 } 108 SOSAccountSetValue(account, kSOSAccountRenegotiationRetryCount, (__bridge CFMutableDictionaryRef)attemptsPerPeer, NULL); 109 } 110 111 int SOSPeerOTRTimerTimeoutValue(SOSAccount* account, SOSPeerRef peer) 112 { 113 CFErrorRef error = NULL; 114 int timeoutIntValue = 0; 115 116 CFMutableDictionaryRef timeouts = (CFMutableDictionaryRef)asDictionary(SOSAccountGetValue(account, kSOSAccountPeerNegotiationTimeouts, &error), NULL); 117 require_action_quiet(timeouts, xit, secnotice("otrtimer","deadline value not available yet")); 118 119 CFNumberRef timeout = CFDictionaryGetValue(timeouts, SOSPeerGetID(peer)); 120 require_action_quiet(timeout, xit, secnotice("otrtimer","deadline value not available yet")); 121 122 secnotice("otrtimer", "decided to wait %d before restarting negotiation", [(__bridge NSNumber*)timeout intValue]); 123 timeoutIntValue = [(__bridge NSNumber*)timeout intValue]; 124 125 xit: 126 return timeoutIntValue; 127 } 128 129 void SOSPeerOTRTimerSetupAwaitingTimer(SOSAccount* account, SOSPeerRef peer, SOSEngineRef engine, SOSCoderRef coder) 130 { 131 //check which timeout value to use 132 int timeoutValue = SOSPeerOTRTimerTimeoutValue(account, peer); 133 CFStringRef peerid = CFRetainSafe(SOSPeerGetID(peer)); 134 135 secnotice("otrtimer", "setting timer for peer: %@", peer); 136 __block dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); 137 dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, timeoutValue * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0); 138 dispatch_source_set_event_handler(timer, ^{ 139 secnotice("otrtimer","otrTimerFired fired"); 140 SOSCCResetOTRNegotiation_Server(peerid); 141 142 }); 143 144 dispatch_source_set_cancel_handler(timer, ^{ 145 CFReleaseSafe(peerid); 146 }); 147 148 dispatch_resume(timer); 149 150 SOSPeerSetOTRTimer(peer, timer); 151 } 152 153 //clear the max retry counter in the account object 154 void SOSPeerOTRTimerClearMaxRetryCount(SOSAccount* account, NSString* peerid) 155 { 156 secnotice("otrtimer", "negotiation finished! clearing max retry counter"); 157 NSMutableDictionary* attemptsPerPeer = (__bridge NSMutableDictionary*)SOSAccountGetValue(account, kSOSAccountRenegotiationRetryCount, NULL); 158 if(!attemptsPerPeer){ 159 attemptsPerPeer = [NSMutableDictionary dictionary]; 160 } 161 [attemptsPerPeer removeObjectForKey:peerid]; 162 SOSAccountSetValue(account, kSOSAccountRenegotiationRetryCount, (__bridge CFMutableDictionaryRef)attemptsPerPeer, NULL); 163 } 164