/ keychain / SecureObjectSync / SOSPeerOTRTimer.m
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