2 // SOSTransportCircleKVS.c
5 #include <Security/SecureObjectSync/SOSTransport.h>
6 #include <Security/SecureObjectSync/SOSTransportCircle.h>
7 #include <Security/SecureObjectSync/SOSTransportCircleKVS.h>
8 #include <Security/SecureObjectSync/SOSKVSKeys.h>
9 #include <Security/SecureObjectSync/SOSInternal.h>
10 #include <Security/SecureObjectSync/SOSAccountPriv.h>
11 #include <SOSCloudKeychainClient.h>
12 #include <utilities/SecCFWrappers.h>
14 static bool SOSTransportCircleKVSUpdateRetirementRecords(SOSTransportCircleKVSRef transport
, CFDictionaryRef updates
, CFErrorRef
* error
);
15 static bool SOSTransportCircleKVSUpdateKVS(SOSTransportCircleRef transport
, CFDictionaryRef changes
, CFErrorRef
*error
);
16 static bool expireRetirementRecords(SOSTransportCircleRef transport
, CFDictionaryRef retirements
, CFErrorRef
*error
);
17 static CFArrayRef
handleCircleMessages(SOSTransportCircleRef transport
, CFMutableDictionaryRef circle_circle_messages_table
, CFErrorRef
*error
);
18 static void destroy(SOSTransportCircleRef transport
);
19 static bool postCircle(SOSTransportCircleRef transport
, CFStringRef circleName
, CFDataRef circle_data
, CFErrorRef
*error
);
20 static CFDictionaryRef
handleRetirementMessages(SOSTransportCircleRef transport
, CFMutableDictionaryRef circle_retirement_messages_table
, CFErrorRef
*error
);
21 static inline bool postRetirement(SOSTransportCircleRef transport
, CFStringRef circleName
, CFStringRef peer_id
, CFDataRef retirement_data
, CFErrorRef
*error
);
22 static inline bool flushChanges(SOSTransportCircleRef transport
, CFErrorRef
*error
);
23 static inline CFIndex
getTransportType(SOSTransportCircleRef transport
, CFErrorRef
*error
);
24 static bool sendPeerInfo(SOSTransportCircleRef transport
, CFStringRef peerID
, CFDataRef peerInfoData
, CFErrorRef
*error
);
25 static bool flushRingChanges(SOSTransportCircleRef transport
, CFErrorRef
* error
);
26 static bool postRing(SOSTransportCircleRef transport
, CFStringRef ringName
, CFDataRef ring
, CFErrorRef
*error
);
27 static bool sendAccountChangedWithDSID(SOSTransportCircleRef transport
, CFStringRef dsid
, CFErrorRef
*error
);
28 static bool sendDebugInfo(SOSTransportCircleRef transport
, CFStringRef type
, CFTypeRef debugInfo
, CFErrorRef
*error
);
30 struct __OpaqueSOSTransportCircleKVS
{
31 struct __OpaqueSOSTransportCircle c
;
32 CFMutableDictionaryRef pending_changes
;
33 CFStringRef circleName
;
37 SOSTransportCircleKVSRef
SOSTransportCircleKVSCreate(SOSAccountRef account
, CFStringRef circleName
, CFErrorRef
*error
){
38 SOSTransportCircleKVSRef t
= (SOSTransportCircleKVSRef
) SOSTransportCircleCreateForSubclass(sizeof(struct __OpaqueSOSTransportCircleKVS
) - sizeof(CFRuntimeBase
), account
, error
);
40 t
->circleName
= CFRetainSafe(circleName
);
41 t
->c
.expireRetirementRecords
= expireRetirementRecords
;
42 t
->c
.postCircle
= postCircle
;
43 t
->c
.postRetirement
= postRetirement
;
44 t
->c
.flushChanges
= flushChanges
;
45 t
->pending_changes
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
46 t
->c
.handleRetirementMessages
= handleRetirementMessages
;
47 t
->c
.handleCircleMessages
= handleCircleMessages
;
48 t
->c
.destroy
= destroy
;
49 t
->c
.getTransportType
= getTransportType
;
50 t
->c
.sendDebugInfo
= sendDebugInfo
;
51 t
->c
.postRing
= postRing
;
52 t
->c
.sendPeerInfo
= sendPeerInfo
;
53 t
->c
.flushRingChanges
= flushRingChanges
;
54 t
->c
.sendAccountChangedWithDSID
= sendAccountChangedWithDSID
;
55 SOSRegisterTransportCircle((SOSTransportCircleRef
)t
);
60 static CFStringRef
SOSTransportCircleKVSGetCircleName(SOSTransportCircleKVSRef transport
){
61 return transport
->circleName
;
64 static inline CFIndex
getTransportType(SOSTransportCircleRef transport
, CFErrorRef
*error
){
69 static void destroy(SOSTransportCircleRef transport
){
70 SOSTransportCircleKVSRef tkvs
= (SOSTransportCircleKVSRef
)transport
;
71 CFReleaseNull(tkvs
->pending_changes
);
72 CFReleaseNull(tkvs
->circleName
);
74 SOSUnregisterTransportCircle((SOSTransportCircleRef
)tkvs
);
77 static bool SOSTransportCircleKVSUpdateKVS(SOSTransportCircleRef transport
, CFDictionaryRef changes
, CFErrorRef
*error
){
78 CloudKeychainReplyBlock log_error
= ^(CFDictionaryRef returnedValues __unused
, CFErrorRef error
) {
80 secerror("Error putting: %@", error
);
85 SOSCloudKeychainPutObjectsInCloud(changes
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), log_error
);
89 bool SOSTransportCircleKVSSendPendingChanges(SOSTransportCircleKVSRef transport
, CFErrorRef
*error
) {
90 CFErrorRef changeError
= NULL
;
91 SOSAccountRef account
= SOSTransportCircleGetAccount((SOSTransportCircleRef
)transport
);
93 if (transport
->pending_changes
== NULL
|| CFDictionaryGetCount(transport
->pending_changes
) == 0) {
94 CFReleaseNull(transport
->pending_changes
);
98 CFTypeRef dsid
= SOSAccountGetValue(account
, kSOSDSIDKey
, error
);
102 CFDictionaryAddValue(transport
->pending_changes
, kSOSKVSRequiredKey
, dsid
);
104 bool success
= SOSTransportCircleKVSUpdateKVS((SOSTransportCircleRef
)transport
, transport
->pending_changes
, &changeError
);
106 CFDictionaryRemoveAllValues(transport
->pending_changes
);
108 SOSCreateErrorWithFormat(kSOSErrorSendFailure
, changeError
, error
, NULL
,
109 CFSTR("Send changes block failed [%@]"), transport
->pending_changes
);
115 void SOSTransportCircleKVSAddToPendingChanges(SOSTransportCircleKVSRef transport
, CFStringRef message_key
, CFDataRef message_data
){
117 if (transport
->pending_changes
== NULL
) {
118 transport
->pending_changes
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
120 if (message_data
== NULL
) {
121 CFDictionarySetValue(transport
->pending_changes
, message_key
, kCFNull
);
123 CFDictionarySetValue(transport
->pending_changes
, message_key
, message_data
);
127 static bool expireRetirementRecords(SOSTransportCircleRef transport
, CFDictionaryRef retirements
, CFErrorRef
*error
) {
130 CFMutableDictionaryRef keysToWrite
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
132 CFDictionaryForEach(retirements
, ^(const void *key
, const void *value
) {
133 if (isString(key
) && isArray(value
)) {
134 CFStringRef circle_name
= (CFStringRef
) key
;
135 CFArrayRef retirees
= (CFArrayRef
) value
;
137 CFArrayForEach(retirees
, ^(const void *value
) {
138 if (isString(value
)) {
139 CFStringRef retiree_id
= (CFStringRef
) value
;
141 CFStringRef kvsKey
= SOSRetirementKeyCreateWithCircleNameAndPeer(circle_name
, retiree_id
);
143 CFDictionaryAddValue(keysToWrite
, kvsKey
, kCFNull
);
145 CFReleaseSafe(kvsKey
);
151 if(CFDictionaryGetCount(keysToWrite
)) {
152 success
= SOSTransportCircleKVSUpdateRetirementRecords((SOSTransportCircleKVSRef
)transport
, keysToWrite
, error
);
154 CFReleaseNull(keysToWrite
);
159 static bool SOSTransportCircleKVSUpdateRetirementRecords(SOSTransportCircleKVSRef transport
, CFDictionaryRef updates
, CFErrorRef
* error
){
160 CFErrorRef updateError
= NULL
;
161 bool success
= false;
162 if (SOSTransportCircleKVSUpdateKVS((SOSTransportCircleRef
)transport
, updates
, &updateError
)){
165 SOSCreateErrorWithFormat(kSOSErrorSendFailure
, updateError
, error
, NULL
,
166 CFSTR("update parameters key failed [%@]"), updates
);
171 static inline bool postRetirement(SOSTransportCircleRef transport
, CFStringRef circleName
, CFStringRef peer_id
, CFDataRef retirement_data
, CFErrorRef
*error
)
173 CFStringRef retirement_key
= SOSRetirementKeyCreateWithCircleNameAndPeer(circleName
, peer_id
);
175 SOSTransportCircleKVSAddToPendingChanges((SOSTransportCircleKVSRef
)transport
, retirement_key
, retirement_data
);
177 CFReleaseNull(retirement_key
);
181 static inline bool flushChanges(SOSTransportCircleRef transport
, CFErrorRef
*error
)
183 SOSTransportCircleKVSRef tkvs
= (SOSTransportCircleKVSRef
) transport
;
185 return SOSTransportCircleKVSSendPendingChanges(tkvs
, error
);
188 static bool postCircle(SOSTransportCircleRef transport
, CFStringRef circleName
, CFDataRef circle_data
, CFErrorRef
*error
){
189 SOSTransportCircleKVSRef tkvs
= (SOSTransportCircleKVSRef
)transport
;
190 CFStringRef circle_key
= SOSCircleKeyCreateWithName(circleName
, error
);
192 SOSTransportCircleKVSAddToPendingChanges(tkvs
, circle_key
, circle_data
);
193 CFReleaseNull(circle_key
);
198 static CF_RETURNS_RETAINED CFDictionaryRef
handleRetirementMessages(SOSTransportCircleRef transport
, CFMutableDictionaryRef circle_retirement_messages_table
, CFErrorRef
*error
){
199 SOSAccountRef account
= SOSTransportCircleGetAccount(transport
);
201 return SOSAccountHandleRetirementMessages(account
, circle_retirement_messages_table
, error
);
205 bool SOSTransportCircleRecordLastCirclePushedInKVS(SOSTransportCircleRef transport
, CFStringRef circle_name
, CFDataRef circleData
){
207 SOSAccountRef a
= SOSTransportCircleGetAccount(transport
);
208 SOSTransportCircleKVSRef tkvs
= (SOSTransportCircleKVSRef
)transport
;
209 CFStringRef myPeerID
= SOSAccountGetMyPeerID(a
);
210 CFDataRef timeData
= NULL
;
212 CFMutableStringRef timeDescription
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("["));
213 CFAbsoluteTime currentTimeAndDate
= CFAbsoluteTimeGetCurrent();
215 withStringOfAbsoluteTime(currentTimeAndDate
, ^(CFStringRef decription
) {
216 CFStringAppend(timeDescription
, decription
);
218 CFStringAppend(timeDescription
, CFSTR("]"));
220 timeData
= CFStringCreateExternalRepresentation(NULL
,timeDescription
,
221 kCFStringEncodingUTF8
, '?');
223 CFMutableDataRef timeAndCircleMutable
= CFDataCreateMutable(kCFAllocatorDefault
, CFDataGetLength(timeData
) + CFDataGetLength(circleData
));
224 CFDataAppend(timeAndCircleMutable
, timeData
);
226 CFDataAppend(timeAndCircleMutable
, circleData
);
227 CFDataRef timeAndCircle
= CFDataCreateCopy(kCFAllocatorDefault
, timeAndCircleMutable
);
230 CFStringRef lastPushedCircleKey
= SOSLastCirclePushedKeyCreateWithCircleNameAndPeerID(circle_name
, SOSAccountGetMyPeerID(a
));
231 SOSTransportCircleKVSAddToPendingChanges(tkvs
, lastPushedCircleKey
,timeAndCircle
);
232 CFReleaseSafe(lastPushedCircleKey
);
235 //if we don't have a peerID (we could be retired or its too early in the system, let's use other gestalt information instead
236 CFStringRef lastPushedCircleKeyWithAccount
= SOSLastCirclePushedKeyCreateWithAccountGestalt(SOSTransportCircleGetAccount(transport
));
237 SOSTransportCircleKVSAddToPendingChanges(tkvs
, lastPushedCircleKeyWithAccount
, timeAndCircle
);
238 CFReleaseNull(lastPushedCircleKeyWithAccount
);
240 CFReleaseNull(timeDescription
);
241 CFReleaseNull(timeAndCircleMutable
);
242 CFReleaseNull(timeAndCircle
);
243 CFReleaseNull(timeData
);
247 CFArrayRef
SOSTransportCircleKVSHandlePeerInfoV2Messages(SOSTransportCircleRef transport
, CFMutableDictionaryRef peer_info_message_table
, CFErrorRef
*error
){
248 //SOSTransportCircleKVSRef kvs_transport = (SOSTransportCircleKVSRef)transport;
249 /* TODO Handle peer info messages! */
251 /* assuming array of peer ids or peer infos that were handled as the return value */
255 static CFArrayRef
handleCircleMessages(SOSTransportCircleRef transport
, CFMutableDictionaryRef circle_circle_messages_table
, CFErrorRef
*error
){
256 CFMutableArrayRef handledKeys
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
257 CFDictionaryForEach(circle_circle_messages_table
, ^(const void *key
, const void *value
) {
258 CFErrorRef circleMessageError
= NULL
;
259 if (!SOSAccountHandleCircleMessage(SOSTransportCircleGetAccount(transport
), key
, value
, &circleMessageError
)) {
260 secerror("Error handling circle message %@ (%@): %@", key
, value
, circleMessageError
);
263 CFStringRef circle_id
= (CFStringRef
) key
;
264 CFArrayAppendValue(handledKeys
, circle_id
);
266 CFReleaseNull(circleMessageError
);
272 bool SOSTransportCircleKVSAppendKeyInterest(SOSTransportCircleKVSRef transport
, CFMutableArrayRef alwaysKeys
, CFMutableArrayRef afterFirstUnlockKeys
, CFMutableArrayRef unlockedKeys
, CFErrorRef
*error
){
274 CFStringRef circle_name
= NULL
;
275 CFStringRef circle_key
= NULL
;
277 if(SOSAccountHasPublicKey(SOSTransportCircleGetAccount((SOSTransportCircleRef
)transport
), NULL
)){
278 require_quiet(circle_name
= SOSTransportCircleKVSGetCircleName(transport
), fail
);
279 require_quiet(circle_key
= SOSCircleKeyCreateWithName(circle_name
, error
), fail
);
281 SOSAccountRef account
= SOSTransportCircleGetAccount((SOSTransportCircleRef
)transport
);
282 require_quiet(account
, fail
);
283 SOSCircleRef circle
= account
->trusted_circle
;
284 require_quiet(circle
&& CFEqualSafe(circle_name
, SOSCircleGetName(circle
)), fail
);
286 SOSCircleForEachActivePeer(circle
, ^(SOSPeerInfoRef peer
) {
287 CFStringRef retirement_key
= SOSRetirementKeyCreateWithCircleNameAndPeer(circle_name
, SOSPeerInfoGetPeerID(peer
));
288 CFArrayAppendValue(alwaysKeys
, retirement_key
);
289 CFReleaseNull(retirement_key
);
292 CFArrayAppendValue(alwaysKeys
, circle_key
);
294 CFReleaseNull(circle_key
);
299 CFReleaseNull(circle_key
);
303 //register peer infos key
304 bool SOSTransportCircleKVSAppendPeerInfoKeyInterest(SOSTransportCircleKVSRef transport
, CFMutableArrayRef alwaysKeys
, CFMutableArrayRef afterFirstUnlockKeys
, CFMutableArrayRef unlockedKeys
, CFErrorRef
*error
){
306 if(SOSAccountHasPublicKey(SOSTransportCircleGetAccount((SOSTransportCircleRef
)transport
), NULL
)){
308 SOSAccountRef account
= SOSTransportCircleGetAccount((SOSTransportCircleRef
)transport
);
309 require_quiet(account
, fail
);
310 SOSCircleRef circle
= account
->trusted_circle
;
311 require_quiet(circle
, fail
);
313 SOSCircleForEachActivePeer(circle
, ^(SOSPeerInfoRef peer
) {
314 CFStringRef peer_info_key
= SOSPeerInfoV2KeyCreateWithPeerName(SOSPeerInfoGetPeerID(peer
));
315 CFArrayAppendValue(alwaysKeys
, peer_info_key
);
316 CFReleaseNull(peer_info_key
);
326 bool SOSTransportCircleKVSAppendRingKeyInterest(SOSTransportCircleKVSRef transport
, CFMutableArrayRef alwaysKeys
, CFMutableArrayRef afterFirstUnlockKeys
, CFMutableArrayRef unlockedKeys
, CFErrorRef
*error
){
328 CFMutableSetRef ringKeys
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
330 if(SOSAccountHasPublicKey(SOSTransportCircleGetAccount((SOSTransportCircleRef
)transport
), NULL
)){
331 SOSAccountRef account
= SOSTransportCircleGetAccount((SOSTransportCircleRef
)transport
);
332 require_quiet(account
, fail
);
333 SOSCircleRef circle
= account
->trusted_circle
;
334 require_quiet(circle
, fail
);
336 // Always interested in backup rings:
338 SOSAccountForEachBackupRingName(account
, ^(CFStringRef ringName
) {
339 CFStringRef ring_key
= SOSRingKeyCreateWithRingName(ringName
);
340 CFSetAddValue(ringKeys
, ring_key
);
341 CFReleaseNull(ring_key
);
344 // And any trusted rings!
345 CFMutableDictionaryRef rings
= SOSAccountGetRings(account
, error
);
346 require_quiet(rings
, fail
);
347 require_quiet(CFDictionaryGetCount(rings
) > 0, fail
);
348 CFDictionaryForEach(rings
, ^(const void *key
, const void *value
) {
349 CFStringRef ringName
= asString(key
, NULL
);
350 CFStringRef ring_key
= SOSRingKeyCreateWithRingName(ringName
);
351 CFSetAddValue(ringKeys
, ring_key
);
352 CFReleaseNull(ring_key
);
355 CFSetForEach(ringKeys
, ^(const void *value
) {
356 CFArrayAppendValue(alwaysKeys
, value
);
359 CFReleaseSafe(ringKeys
);
363 CFReleaseNull(ringKeys
);
367 extern CFStringRef kSOSAccountDebugScope
;
368 //register debug scope key
369 bool SOSTransportCircleKVSAppendDebugKeyInterest(SOSTransportCircleKVSRef transport
, CFMutableArrayRef alwaysKeys
, CFMutableArrayRef afterFirstUnlockKeys
, CFMutableArrayRef unlockedKeys
, CFErrorRef
*error
){
371 CFStringRef key
= SOSDebugInfoKeyCreateWithTypeName(kSOSAccountDebugScope
);
372 CFArrayAppendValue(alwaysKeys
, key
);
377 //send debug info over KVS
378 bool sendDebugInfo(SOSTransportCircleRef transport
, CFStringRef type
, CFTypeRef debugInfo
, CFErrorRef
*error
)
380 CFStringRef key
= SOSDebugInfoKeyCreateWithTypeName(type
);
381 CFDictionaryRef changes
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
387 bool success
= SOSTransportCircleKVSUpdateKVS(transport
, changes
, error
);
389 CFReleaseNull(changes
);
394 static bool sendAccountChangedWithDSID(SOSTransportCircleRef transport
, CFStringRef dsid
, CFErrorRef
*error
){
396 CFDictionaryRef changes
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
397 kSOSKVSAccountChangedKey
, dsid
,
400 bool success
= SOSTransportCircleKVSUpdateKVS((SOSTransportCircleRef
)transport
, changes
, error
);
402 CFReleaseNull(changes
);
407 //send the Ring over KVS
408 bool postRing(SOSTransportCircleRef transport
, CFStringRef ringName
, CFDataRef ring
, CFErrorRef
*error
)
410 CFStringRef ringKey
= SOSRingKeyCreateWithName(ringName
, error
);
413 SOSTransportCircleKVSAddToPendingChanges((SOSTransportCircleKVSRef
)transport
, ringKey
, ring
);
415 CFReleaseNull(ringKey
);
420 bool flushRingChanges(SOSTransportCircleRef transport
, CFErrorRef
* error
){
422 SOSTransportCircleKVSRef tkvs
= (SOSTransportCircleKVSRef
) transport
;
424 return SOSTransportCircleKVSSendPendingChanges(tkvs
, error
);
427 //send the PeerInfo Data over KVS
428 bool sendPeerInfo(SOSTransportCircleRef transport
, CFStringRef peerID
, CFDataRef peerInfoData
, CFErrorRef
*error
)
430 CFStringRef peerName
= SOSPeerInfoV2KeyCreateWithPeerName(peerID
);
432 CFDictionaryRef changes
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
433 peerName
, peerInfoData
,
436 CFReleaseNull(peerName
);
437 bool success
= SOSTransportCircleKVSUpdateKVS(transport
, changes
, error
);
439 CFReleaseNull(changes
);
444 bool SOSTransportCircleSendOfficialDSID(SOSTransportCircleRef transport
, CFStringRef dsid
, CFErrorRef
*error
)
446 CFDictionaryRef changes
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
447 kSOSKVSOfficialDSIDKey
, dsid
,
449 bool success
= SOSTransportCircleKVSUpdateKVS(transport
, changes
, error
);
450 CFReleaseNull(changes
);