1// 2// SOSTransportCircleKVS.c 3// sec 4// 5#include <SecureObjectSync/SOSTransport.h> 6#include <SecureObjectSync/SOSTransportCircle.h> 7#include <SecureObjectSync/SOSTransportCircleKVS.h> 8#include <SecureObjectSync/SOSKVSKeys.h> 9#include <utilities/SecCFWrappers.h> 10#include <SecureObjectSync/SOSInternal.h> 11#include <SecureObjectSync/SOSAccountPriv.h> 12#include <SOSCloudKeychainClient.h> 13 14static bool SOSTransportCircleKVSUpdateRetirementRecords(SOSTransportCircleKVSRef transport, CFDictionaryRef updates, CFErrorRef* error); 15static bool SOSTransportCircleKVSUpdateKVS(SOSTransportCircleRef transport, CFDictionaryRef changes, CFErrorRef *error); 16static bool expireRetirementRecords(SOSTransportCircleRef transport, CFDictionaryRef retirements, CFErrorRef *error); 17static CFArrayRef handleCircleMessages(SOSTransportCircleRef transport, CFMutableDictionaryRef circle_circle_messages_table, CFErrorRef *error); 18static void destroy(SOSTransportCircleRef transport); 19static bool postCircle(SOSTransportCircleRef transport, CFStringRef circleName, CFDataRef circle_data, CFErrorRef *error); 20static CFDictionaryRef handleRetirementMessages(SOSTransportCircleRef transport, CFMutableDictionaryRef circle_retirement_messages_table, CFErrorRef *error); 21static inline bool postRetirement(SOSTransportCircleRef transport, CFStringRef circleName, CFStringRef peer_id, CFDataRef retirement_data, CFErrorRef *error); 22static inline bool flushChanges(SOSTransportCircleRef transport, CFErrorRef *error); 23 24 25struct __OpaqueSOSTransportCircleKVS{ 26 struct __OpaqueSOSTransportCircle c; 27 CFMutableDictionaryRef pending_changes; 28 CFStringRef circleName; 29}; 30 31 32SOSTransportCircleKVSRef SOSTransportCircleKVSCreate(SOSAccountRef account, CFStringRef circleName, CFErrorRef *error){ 33 SOSTransportCircleKVSRef t = (SOSTransportCircleKVSRef) SOSTransportCircleCreateForSubclass(sizeof(struct __OpaqueSOSTransportCircleKVS) - sizeof(CFRuntimeBase), account, error); 34 if(t){ 35 t->circleName = CFRetainSafe(circleName); 36 t->c.expireRetirementRecords = expireRetirementRecords; 37 t->c.postCircle = postCircle; 38 t->c.postRetirement = postRetirement; 39 t->c.flushChanges = flushChanges; 40 t->pending_changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 41 t->c.handleRetirementMessages = handleRetirementMessages; 42 t->c.handleCircleMessages = handleCircleMessages; 43 t->c.destroy = destroy; 44 SOSRegisterTransportCircle((SOSTransportCircleRef)t); 45 } 46 return t; 47} 48 49static CFStringRef SOSTransportCircleKVSGetCircleName(SOSTransportCircleKVSRef transport){ 50 return transport->circleName; 51} 52 53static void destroy(SOSTransportCircleRef transport){ 54 SOSTransportCircleKVSRef tkvs = (SOSTransportCircleKVSRef)transport; 55 CFReleaseNull(tkvs->pending_changes); 56 CFReleaseNull(tkvs->circleName); 57 58 SOSUnregisterTransportCircle((SOSTransportCircleRef)tkvs); 59} 60 61bool SOSTransportCircleKVSSendPendingChanges(SOSTransportCircleKVSRef transport, CFErrorRef *error) { 62 CFErrorRef changeError = NULL; 63 64 if (transport->pending_changes == NULL || CFDictionaryGetCount(transport->pending_changes) == 0) { 65 CFReleaseNull(transport->pending_changes); 66 return true; 67 } 68 69 bool success = SOSTransportCircleKVSUpdateKVS((SOSTransportCircleRef)transport, transport->pending_changes, &changeError); 70 if (success) { 71 CFDictionaryRemoveAllValues(transport->pending_changes); 72 } else { 73 SOSCreateErrorWithFormat(kSOSErrorSendFailure, changeError, error, NULL, 74 CFSTR("Send changes block failed [%@]"), transport->pending_changes); 75 } 76 77 return success; 78} 79 80void SOSTransportCircleKVSAddToPendingChanges(SOSTransportCircleKVSRef transport, CFStringRef message_key, CFDataRef message_data){ 81 82 if (transport->pending_changes == NULL) { 83 transport->pending_changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 84 } 85 if (message_data == NULL) { 86 CFDictionarySetValue(transport->pending_changes, message_key, kCFNull); 87 } else { 88 CFDictionarySetValue(transport->pending_changes, message_key, message_data); 89 } 90} 91 92static bool expireRetirementRecords(SOSTransportCircleRef transport, CFDictionaryRef retirements, CFErrorRef *error) { 93 94 bool success = true; 95 CFMutableDictionaryRef keysToWrite = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 96 97 CFDictionaryForEach(retirements, ^(const void *key, const void *value) { 98 if (isString(key) && isArray(value)) { 99 CFStringRef circle_name = (CFStringRef) key; 100 CFArrayRef retirees = (CFArrayRef) value; 101 102 CFArrayForEach(retirees, ^(const void *value) { 103 if (isString(value)) { 104 CFStringRef retiree_id = (CFStringRef) value; 105 106 CFStringRef kvsKey = SOSRetirementKeyCreateWithCircleNameAndPeer(circle_name, retiree_id); 107 108 CFDictionaryAddValue(keysToWrite, kvsKey, kCFNull); 109 110 CFReleaseSafe(kvsKey); 111 } 112 }); 113 } 114 }); 115 116 if(CFDictionaryGetCount(keysToWrite)) { 117 success = SOSTransportCircleKVSUpdateRetirementRecords((SOSTransportCircleKVSRef)transport, keysToWrite, error); 118 } 119 CFReleaseNull(keysToWrite); 120 121 return success; 122} 123 124static bool SOSTransportCircleKVSUpdateRetirementRecords(SOSTransportCircleKVSRef transport, CFDictionaryRef updates, CFErrorRef* error){ 125 CFErrorRef updateError = NULL; 126 bool success = false; 127 if (SOSTransportCircleKVSUpdateKVS((SOSTransportCircleRef)transport, updates, &updateError)){ 128 success = true; 129 } else { 130 SOSCreateErrorWithFormat(kSOSErrorSendFailure, updateError, error, NULL, 131 CFSTR("update parameters key failed [%@]"), updates); 132 } 133 return success; 134} 135 136static inline bool postRetirement(SOSTransportCircleRef transport, CFStringRef circleName, CFStringRef peer_id, CFDataRef retirement_data, CFErrorRef *error) 137{ 138 CFStringRef retirement_key = SOSRetirementKeyCreateWithCircleNameAndPeer(circleName, peer_id); 139 if (retirement_key) 140 SOSTransportCircleKVSAddToPendingChanges((SOSTransportCircleKVSRef)transport, retirement_key, retirement_data); 141 142 CFReleaseNull(retirement_key); 143 return true; 144} 145 146static inline bool flushChanges(SOSTransportCircleRef transport, CFErrorRef *error) 147{ 148 SOSTransportCircleKVSRef tkvs = (SOSTransportCircleKVSRef) transport; 149 150 return SOSTransportCircleKVSSendPendingChanges(tkvs, error); 151} 152 153static bool postCircle(SOSTransportCircleRef transport, CFStringRef circleName, CFDataRef circle_data, CFErrorRef *error){ 154 SOSTransportCircleKVSRef tkvs = (SOSTransportCircleKVSRef)transport; 155 CFStringRef circle_key = SOSCircleKeyCreateWithName(circleName, error); 156 if (circle_key) 157 SOSTransportCircleKVSAddToPendingChanges(tkvs, circle_key, circle_data); 158 CFReleaseNull(circle_key); 159 160 return true; 161} 162 163static CFDictionaryRef handleRetirementMessages(SOSTransportCircleRef transport, CFMutableDictionaryRef circle_retirement_messages_table, CFErrorRef *error){ 164 SOSAccountRef account = SOSTransportCircleGetAccount(transport); 165 CFMutableDictionaryRef handledRetirementMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 166 CFDictionaryForEach(circle_retirement_messages_table, ^(const void *key, const void *value) { 167 if (isString(key) && isDictionary(value)) { 168 CFStringRef circle_name = (CFStringRef) key; 169 170 CFDictionaryRef retirment_dictionary = (CFDictionaryRef) value; 171 CFDictionaryForEach(retirment_dictionary, ^(const void *key, const void *value) { 172 if(isData(value)) { 173 SOSPeerInfoRef pi = SOSPeerInfoCreateFromData(NULL, error, (CFDataRef) value); 174 if(pi && CFEqual(key, SOSPeerInfoGetPeerID(pi)) && SOSPeerInfoInspectRetirementTicket(pi, error)) { 175 CFMutableDictionaryRef circle_retirements = CFDictionaryEnsureCFDictionaryAndGetCurrentValue(account->retired_peers, circle_name); 176 CFDictionarySetValue(circle_retirements, key, value); 177 178 SOSAccountRecordRetiredPeerInCircleNamed(account, circle_name, pi); 179 180 CFMutableArrayRef handledRetirementIDs = CFDictionaryEnsureCFArrayAndGetCurrentValue(handledRetirementMessages, circle_name); 181 CFArrayAppendValue(handledRetirementIDs, SOSPeerInfoGetPeerID(pi)); 182 } 183 CFReleaseSafe(pi); 184 } 185 }); 186 } 187 }); 188 return handledRetirementMessages; 189} 190 191static CFArrayRef handleCircleMessages(SOSTransportCircleRef transport, CFMutableDictionaryRef circle_circle_messages_table, CFErrorRef *error){ 192 CFMutableArrayRef handledKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 193 CFDictionaryForEach(circle_circle_messages_table, ^(const void *key, const void *value) { 194 CFErrorRef circleMessageError = NULL; 195 if (!SOSAccountHandleCircleMessage(SOSTransportCircleGetAccount(transport), key, value, &circleMessageError)) { 196 secerror("Error handling circle message %@ (%@): %@", key, value, circleMessageError); 197 } 198 else{ 199 CFStringRef circle_id = (CFStringRef) key; 200 CFArrayAppendValue(handledKeys, circle_id); 201 } 202 CFReleaseNull(circleMessageError); 203 }); 204 205 return handledKeys; 206} 207 208bool SOSTransportCircleKVSAppendKeyInterest(SOSTransportCircleKVSRef transport, CFMutableArrayRef alwaysKeys, CFMutableArrayRef afterFirstUnlockKeys, CFMutableArrayRef unlockedKeys, CFErrorRef *error){ 209 210 CFStringRef circle_name = NULL; 211 CFStringRef circle_key = NULL; 212 213 if(SOSAccountHasPublicKey(SOSTransportCircleGetAccount((SOSTransportCircleRef)transport), NULL)){ 214 require_quiet(circle_name = SOSTransportCircleKVSGetCircleName(transport), fail); 215 require_quiet(circle_key = SOSCircleKeyCreateWithName(circle_name, error), fail); 216 217 SOSAccountRef account = SOSTransportCircleGetAccount((SOSTransportCircleRef)transport); 218 require_quiet(account, fail); 219 SOSCircleRef circle = SOSAccountFindCircle(account, circle_name, error); 220 require_quiet(circle, fail); 221 222 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) { 223 CFStringRef retirement_key = SOSRetirementKeyCreateWithCircleNameAndPeer(circle_name, SOSPeerInfoGetPeerID(peer)); 224 CFArrayAppendValue(alwaysKeys, retirement_key); 225 CFReleaseNull(retirement_key); 226 }); 227 228 CFArrayAppendValue(alwaysKeys, circle_key); 229 230 CFReleaseNull(circle_key); 231 } 232 return true; 233 234fail: 235 CFReleaseNull(circle_key); 236 return false; 237} 238 239static bool SOSTransportCircleKVSUpdateKVS(SOSTransportCircleRef transport, CFDictionaryRef changes, CFErrorRef *error){ 240 CloudKeychainReplyBlock log_error = ^(CFDictionaryRef returnedValues __unused, CFErrorRef error) { 241 if (error) { 242 secerror("Error putting: %@", error); 243 CFReleaseSafe(error); 244 } 245 }; 246 247 SOSCloudKeychainPutObjectsInCloud(changes, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), log_error); 248 return true; 249} 250