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(!isString(key
) || !isData(value
)) {
260 secerror("Error, Key-Value for CircleMessage was not CFString/CFData");
261 } if (!SOSAccountHandleCircleMessage(SOSTransportCircleGetAccount(transport
), key
, value
, &circleMessageError
)) {
262 secerror("Error handling circle message %@ (%@): %@", key
, value
, circleMessageError
);
264 CFStringRef circle_id
= (CFStringRef
) key
;
265 CFArrayAppendValue(handledKeys
, circle_id
);
267 CFReleaseNull(circleMessageError
);
273 bool SOSTransportCircleKVSAppendKeyInterest(SOSTransportCircleKVSRef transport
, CFMutableArrayRef alwaysKeys
, CFMutableArrayRef afterFirstUnlockKeys
, CFMutableArrayRef unlockedKeys
, CFErrorRef
*error
){
275 CFStringRef circle_name
= NULL
;
276 CFStringRef circle_key
= NULL
;
278 if(SOSAccountHasPublicKey(SOSTransportCircleGetAccount((SOSTransportCircleRef
)transport
), NULL
)){
279 require_quiet(circle_name
= SOSTransportCircleKVSGetCircleName(transport
), fail
);
280 require_quiet(circle_key
= SOSCircleKeyCreateWithName(circle_name
, error
), fail
);
282 SOSAccountRef account
= SOSTransportCircleGetAccount((SOSTransportCircleRef
)transport
);
283 require_quiet(account
, fail
);
284 SOSCircleRef circle
= account
->trusted_circle
;
285 require_quiet(circle
&& CFEqualSafe(circle_name
, SOSCircleGetName(circle
)), fail
);
287 SOSCircleForEachActivePeer(circle
, ^(SOSPeerInfoRef peer
) {
288 CFStringRef retirement_key
= SOSRetirementKeyCreateWithCircleNameAndPeer(circle_name
, SOSPeerInfoGetPeerID(peer
));
289 CFArrayAppendValue(alwaysKeys
, retirement_key
);
290 CFReleaseNull(retirement_key
);
293 CFArrayAppendValue(alwaysKeys
, circle_key
);
295 CFReleaseNull(circle_key
);
300 CFReleaseNull(circle_key
);
304 //register peer infos key
305 bool SOSTransportCircleKVSAppendPeerInfoKeyInterest(SOSTransportCircleKVSRef transport
, CFMutableArrayRef alwaysKeys
, CFMutableArrayRef afterFirstUnlockKeys
, CFMutableArrayRef unlockedKeys
, CFErrorRef
*error
){
307 if(SOSAccountHasPublicKey(SOSTransportCircleGetAccount((SOSTransportCircleRef
)transport
), NULL
)){
309 SOSAccountRef account
= SOSTransportCircleGetAccount((SOSTransportCircleRef
)transport
);
310 require_quiet(account
, fail
);
311 SOSCircleRef circle
= account
->trusted_circle
;
312 require_quiet(circle
, fail
);
314 SOSCircleForEachActivePeer(circle
, ^(SOSPeerInfoRef peer
) {
315 CFStringRef peer_info_key
= SOSPeerInfoV2KeyCreateWithPeerName(SOSPeerInfoGetPeerID(peer
));
316 CFArrayAppendValue(alwaysKeys
, peer_info_key
);
317 CFReleaseNull(peer_info_key
);
327 bool SOSTransportCircleKVSAppendRingKeyInterest(SOSTransportCircleKVSRef transport
, CFMutableArrayRef alwaysKeys
, CFMutableArrayRef afterFirstUnlockKeys
, CFMutableArrayRef unlockedKeys
, CFErrorRef
*error
){
329 CFMutableSetRef ringKeys
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
331 if(SOSAccountHasPublicKey(SOSTransportCircleGetAccount((SOSTransportCircleRef
)transport
), NULL
)){
332 SOSAccountRef account
= SOSTransportCircleGetAccount((SOSTransportCircleRef
)transport
);
333 require_quiet(account
, fail
);
334 SOSCircleRef circle
= account
->trusted_circle
;
335 require_quiet(circle
, fail
);
337 // Always interested in backup rings:
339 SOSAccountForEachBackupRingName(account
, ^(CFStringRef ringName
) {
340 CFStringRef ring_key
= SOSRingKeyCreateWithRingName(ringName
);
341 CFSetAddValue(ringKeys
, ring_key
);
342 CFReleaseNull(ring_key
);
345 SOSAccountForEachRing(account
, ^SOSRingRef(CFStringRef name
, SOSRingRef ring
) {
346 CFStringRef ring_key
= SOSRingKeyCreateWithRingName(name
);
347 CFSetAddValue(ringKeys
, ring_key
);
348 CFReleaseNull(ring_key
);
352 CFSetForEach(ringKeys
, ^(const void *value
) {
353 CFArrayAppendValue(alwaysKeys
, value
);
356 CFReleaseSafe(ringKeys
);
360 CFReleaseNull(ringKeys
);
364 extern CFStringRef kSOSAccountDebugScope
;
365 //register debug scope key
366 bool SOSTransportCircleKVSAppendDebugKeyInterest(SOSTransportCircleKVSRef transport
, CFMutableArrayRef alwaysKeys
, CFMutableArrayRef afterFirstUnlockKeys
, CFMutableArrayRef unlockedKeys
, CFErrorRef
*error
){
368 CFStringRef key
= SOSDebugInfoKeyCreateWithTypeName(kSOSAccountDebugScope
);
369 CFArrayAppendValue(alwaysKeys
, key
);
374 //send debug info over KVS
375 bool sendDebugInfo(SOSTransportCircleRef transport
, CFStringRef type
, CFTypeRef debugInfo
, CFErrorRef
*error
)
377 CFStringRef key
= SOSDebugInfoKeyCreateWithTypeName(type
);
378 CFDictionaryRef changes
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
384 bool success
= SOSTransportCircleKVSUpdateKVS(transport
, changes
, error
);
386 CFReleaseNull(changes
);
391 static bool sendAccountChangedWithDSID(SOSTransportCircleRef transport
, CFStringRef dsid
, CFErrorRef
*error
){
393 CFDictionaryRef changes
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
394 kSOSKVSAccountChangedKey
, dsid
,
397 bool success
= SOSTransportCircleKVSUpdateKVS((SOSTransportCircleRef
)transport
, changes
, error
);
399 CFReleaseNull(changes
);
404 //send the Ring over KVS
405 bool postRing(SOSTransportCircleRef transport
, CFStringRef ringName
, CFDataRef ring
, CFErrorRef
*error
)
407 CFStringRef ringKey
= SOSRingKeyCreateWithName(ringName
, error
);
410 SOSTransportCircleKVSAddToPendingChanges((SOSTransportCircleKVSRef
)transport
, ringKey
, ring
);
412 CFReleaseNull(ringKey
);
417 bool flushRingChanges(SOSTransportCircleRef transport
, CFErrorRef
* error
){
419 SOSTransportCircleKVSRef tkvs
= (SOSTransportCircleKVSRef
) transport
;
421 return SOSTransportCircleKVSSendPendingChanges(tkvs
, error
);
424 //send the PeerInfo Data over KVS
425 bool sendPeerInfo(SOSTransportCircleRef transport
, CFStringRef peerID
, CFDataRef peerInfoData
, CFErrorRef
*error
)
427 CFStringRef peerName
= SOSPeerInfoV2KeyCreateWithPeerName(peerID
);
429 CFDictionaryRef changes
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
430 peerName
, peerInfoData
,
433 CFReleaseNull(peerName
);
434 bool success
= SOSTransportCircleKVSUpdateKVS(transport
, changes
, error
);
436 CFReleaseNull(changes
);
441 bool SOSTransportCircleSendOfficialDSID(SOSTransportCircleRef transport
, CFStringRef dsid
, CFErrorRef
*error
)
443 CFDictionaryRef changes
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
444 kSOSKVSOfficialDSIDKey
, dsid
,
446 bool success
= SOSTransportCircleKVSUpdateKVS(transport
, changes
, error
);
447 CFReleaseNull(changes
);