2 // SOSTransportCircleKVS.c
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>
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
);
25 struct __OpaqueSOSTransportCircleKVS
{
26 struct __OpaqueSOSTransportCircle c
;
27 CFMutableDictionaryRef pending_changes
;
28 CFStringRef circleName
;
32 SOSTransportCircleKVSRef
SOSTransportCircleKVSCreate(SOSAccountRef account
, CFStringRef circleName
, CFErrorRef
*error
){
33 SOSTransportCircleKVSRef t
= (SOSTransportCircleKVSRef
) SOSTransportCircleCreateForSubclass(sizeof(struct __OpaqueSOSTransportCircleKVS
) - sizeof(CFRuntimeBase
), account
, error
);
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
);
49 static CFStringRef
SOSTransportCircleKVSGetCircleName(SOSTransportCircleKVSRef transport
){
50 return transport
->circleName
;
53 static void destroy(SOSTransportCircleRef transport
){
54 SOSTransportCircleKVSRef tkvs
= (SOSTransportCircleKVSRef
)transport
;
55 CFReleaseNull(tkvs
->pending_changes
);
56 CFReleaseNull(tkvs
->circleName
);
58 SOSUnregisterTransportCircle((SOSTransportCircleRef
)tkvs
);
61 bool SOSTransportCircleKVSSendPendingChanges(SOSTransportCircleKVSRef transport
, CFErrorRef
*error
) {
62 CFErrorRef changeError
= NULL
;
64 if (transport
->pending_changes
== NULL
|| CFDictionaryGetCount(transport
->pending_changes
) == 0) {
65 CFReleaseNull(transport
->pending_changes
);
69 bool success
= SOSTransportCircleKVSUpdateKVS((SOSTransportCircleRef
)transport
, transport
->pending_changes
, &changeError
);
71 CFDictionaryRemoveAllValues(transport
->pending_changes
);
73 SOSCreateErrorWithFormat(kSOSErrorSendFailure
, changeError
, error
, NULL
,
74 CFSTR("Send changes block failed [%@]"), transport
->pending_changes
);
80 void SOSTransportCircleKVSAddToPendingChanges(SOSTransportCircleKVSRef transport
, CFStringRef message_key
, CFDataRef message_data
){
82 if (transport
->pending_changes
== NULL
) {
83 transport
->pending_changes
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
85 if (message_data
== NULL
) {
86 CFDictionarySetValue(transport
->pending_changes
, message_key
, kCFNull
);
88 CFDictionarySetValue(transport
->pending_changes
, message_key
, message_data
);
92 static bool expireRetirementRecords(SOSTransportCircleRef transport
, CFDictionaryRef retirements
, CFErrorRef
*error
) {
95 CFMutableDictionaryRef keysToWrite
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
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
;
102 CFArrayForEach(retirees
, ^(const void *value
) {
103 if (isString(value
)) {
104 CFStringRef retiree_id
= (CFStringRef
) value
;
106 CFStringRef kvsKey
= SOSRetirementKeyCreateWithCircleNameAndPeer(circle_name
, retiree_id
);
108 CFDictionaryAddValue(keysToWrite
, kvsKey
, kCFNull
);
110 CFReleaseSafe(kvsKey
);
116 if(CFDictionaryGetCount(keysToWrite
)) {
117 success
= SOSTransportCircleKVSUpdateRetirementRecords((SOSTransportCircleKVSRef
)transport
, keysToWrite
, error
);
119 CFReleaseNull(keysToWrite
);
124 static bool SOSTransportCircleKVSUpdateRetirementRecords(SOSTransportCircleKVSRef transport
, CFDictionaryRef updates
, CFErrorRef
* error
){
125 CFErrorRef updateError
= NULL
;
126 bool success
= false;
127 if (SOSTransportCircleKVSUpdateKVS((SOSTransportCircleRef
)transport
, updates
, &updateError
)){
130 SOSCreateErrorWithFormat(kSOSErrorSendFailure
, updateError
, error
, NULL
,
131 CFSTR("update parameters key failed [%@]"), updates
);
136 static inline bool postRetirement(SOSTransportCircleRef transport
, CFStringRef circleName
, CFStringRef peer_id
, CFDataRef retirement_data
, CFErrorRef
*error
)
138 CFStringRef retirement_key
= SOSRetirementKeyCreateWithCircleNameAndPeer(circleName
, peer_id
);
140 SOSTransportCircleKVSAddToPendingChanges((SOSTransportCircleKVSRef
)transport
, retirement_key
, retirement_data
);
142 CFReleaseNull(retirement_key
);
146 static inline bool flushChanges(SOSTransportCircleRef transport
, CFErrorRef
*error
)
148 SOSTransportCircleKVSRef tkvs
= (SOSTransportCircleKVSRef
) transport
;
150 return SOSTransportCircleKVSSendPendingChanges(tkvs
, error
);
153 static bool postCircle(SOSTransportCircleRef transport
, CFStringRef circleName
, CFDataRef circle_data
, CFErrorRef
*error
){
154 SOSTransportCircleKVSRef tkvs
= (SOSTransportCircleKVSRef
)transport
;
155 CFStringRef circle_key
= SOSCircleKeyCreateWithName(circleName
, error
);
157 SOSTransportCircleKVSAddToPendingChanges(tkvs
, circle_key
, circle_data
);
158 CFReleaseNull(circle_key
);
163 static 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
;
170 CFDictionaryRef retirment_dictionary
= (CFDictionaryRef
) value
;
171 CFDictionaryForEach(retirment_dictionary
, ^(const void *key
, const void *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
);
178 SOSAccountRecordRetiredPeerInCircleNamed(account
, circle_name
, pi
);
180 CFMutableArrayRef handledRetirementIDs
= CFDictionaryEnsureCFArrayAndGetCurrentValue(handledRetirementMessages
, circle_name
);
181 CFArrayAppendValue(handledRetirementIDs
, SOSPeerInfoGetPeerID(pi
));
188 return handledRetirementMessages
;
191 static 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
);
199 CFStringRef circle_id
= (CFStringRef
) key
;
200 CFArrayAppendValue(handledKeys
, circle_id
);
202 CFReleaseNull(circleMessageError
);
208 bool SOSTransportCircleKVSAppendKeyInterest(SOSTransportCircleKVSRef transport
, CFMutableArrayRef alwaysKeys
, CFMutableArrayRef afterFirstUnlockKeys
, CFMutableArrayRef unlockedKeys
, CFErrorRef
*error
){
210 CFStringRef circle_name
= NULL
;
211 CFStringRef circle_key
= NULL
;
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
);
217 SOSAccountRef account
= SOSTransportCircleGetAccount((SOSTransportCircleRef
)transport
);
218 require_quiet(account
, fail
);
219 SOSCircleRef circle
= SOSAccountFindCircle(account
, circle_name
, error
);
220 require_quiet(circle
, fail
);
222 SOSCircleForEachActivePeer(circle
, ^(SOSPeerInfoRef peer
) {
223 CFStringRef retirement_key
= SOSRetirementKeyCreateWithCircleNameAndPeer(circle_name
, SOSPeerInfoGetPeerID(peer
));
224 CFArrayAppendValue(alwaysKeys
, retirement_key
);
225 CFReleaseNull(retirement_key
);
228 CFArrayAppendValue(alwaysKeys
, circle_key
);
230 CFReleaseNull(circle_key
);
235 CFReleaseNull(circle_key
);
239 static bool SOSTransportCircleKVSUpdateKVS(SOSTransportCircleRef transport
, CFDictionaryRef changes
, CFErrorRef
*error
){
240 CloudKeychainReplyBlock log_error
= ^(CFDictionaryRef returnedValues __unused
, CFErrorRef error
) {
242 secerror("Error putting: %@", error
);
243 CFReleaseSafe(error
);
247 SOSCloudKeychainPutObjectsInCloud(changes
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), log_error
);