1/*
2 * Copyright (c) 2012 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25    SOSCloudTransport.c -  Implementation of the transport layer from CKBridge to SOSAccount/SOSCircle
26    These are the exported functions from CloudKeychainProxy
27*/
28
29/*
30    This XPC service is essentially just a proxy to iCloud KVS, which exists since
31    the main security code cannot link against Foundation.
32
33    See sendTSARequestWithXPC in tsaSupport.c for how to call the service
34
35    The client of an XPC service does not get connection events, nor does it
36    need to deal with transactions.
37*/
38
39#include <AssertMacros.h>
40
41#include <xpc/xpc.h>
42#include <CoreFoundation/CoreFoundation.h>
43#include <CoreFoundation/CFXPCBridge.h>
44#include <sysexits.h>
45#include <syslog.h>
46#include <CoreFoundation/CFUserNotification.h>
47
48#include <utilities/debugging.h>
49#include <utilities/SecCFWrappers.h>
50#include <utilities/SecXPCError.h>
51
52#include "SOSCloudKeychainConstants.h"
53#include "SOSCloudKeychainClient.h"
54
55static CFStringRef sErrorDomain = CFSTR("com.apple.security.sos.transport.error");
56
57#define SOSCKCSCOPE "sync"
58
59// MARK: ---------- SOSCloudTransport ----------
60
61/* SOSCloudTransport, a statically initialized transport singleton. */
62static SOSCloudTransportRef sTransport = NULL;
63
64static SOSCloudTransportRef SOSCloudTransportCreateXPCTransport(void);
65
66void SOSCloudKeychainSetTransport(SOSCloudTransportRef transport) {
67    sTransport = transport;
68}
69
70/* Return the singleton cloud transport instance. */
71static SOSCloudTransportRef SOSCloudTransportDefaultTransport(void)
72{
73    static dispatch_once_t sTransportOnce;
74    dispatch_once(&sTransportOnce, ^{
75        if (!sTransport)
76            SOSCloudKeychainSetTransport(SOSCloudTransportCreateXPCTransport());
77    });
78    return sTransport;
79}
80
81
82// MARK: ----- utilities -----
83
84static CFErrorRef makeError(CFIndex which)
85{
86    CFDictionaryRef userInfo = NULL;
87    return CFErrorCreate(kCFAllocatorDefault, sErrorDomain, which, userInfo);
88}
89
90// MARK: ----- DEBUG Utilities -----
91
92//------------------------------------------------------------------------------------------------
93//          DEBUG only
94//------------------------------------------------------------------------------------------------
95
96static void describeXPCObject(char *prefix, xpc_object_t object)
97{
98//#ifndef NDEBUG
99    // This is useful for debugging.
100    if (object)
101    {
102        char *desc = xpc_copy_description(object);
103        secdebug(SOSCKCSCOPE, "%s%s\n", prefix, desc);
104        free(desc);
105    }
106    else
107        secdebug(SOSCKCSCOPE, "%s<NULL>\n", prefix);
108//#endif
109}
110
111static void describeXPCType(char *prefix, xpc_type_t xtype)
112{
113    // Add others as necessary, e.g. XPC_TYPE_DOUBLE
114#ifndef NDEBUG
115    // This is useful for debugging.
116    char msg[256]={0,};
117    if (XPC_TYPE_CONNECTION == xtype)
118        strcpy(msg, "XPC_TYPE_CONNECTION");
119    else if (XPC_TYPE_ERROR == xtype)
120        strcpy(msg, "XPC_TYPE_ERROR");
121    else if  (XPC_TYPE_DICTIONARY == xtype)
122        strcpy(msg, "XPC_TYPE_DICTIONARY");
123    else
124        strcpy(msg, "<unknown>");
125
126    secdebug(SOSCKCSCOPE, "%s type:%s\n", prefix, msg);
127#endif
128}
129
130// MARK: ---------- SOSXPCCloudTransport ----------
131
132typedef struct SOSXPCCloudTransport *SOSXPCCloudTransportRef;
133struct SOSXPCCloudTransport
134{
135    struct SOSCloudTransport transport;
136    xpc_connection_t serviceConnection;
137    dispatch_queue_t xpc_queue;
138};
139
140static bool xpc_event_filter(const xpc_connection_t peer, xpc_object_t event, CFErrorRef *error)
141{
142    // return true if the type is XPC_TYPE_DICTIONARY (and therefore something more to process)
143    secdebug(SOSCKCSCOPE, "handle_connection_event\n");
144    xpc_type_t xtype = xpc_get_type(event);
145    describeXPCType("handle_xpc_event", xtype);
146    if (XPC_TYPE_CONNECTION == xtype)
147    {
148        secdebug(SOSCKCSCOPE, "handle_xpc_event: XPC_TYPE_CONNECTION (unexpected)");
149        // The client of an XPC service does not get connection events
150        // For now, we log this and keep going
151        describeXPCObject("handle_xpc_event: XPC_TYPE_CONNECTION, obj : ", event);
152#if 0
153        if (error)
154            *error = makeError(kSOSOUnexpectedConnectionEvent); // FIX
155        assert(true);
156#endif
157    }
158    else
159    if (XPC_TYPE_ERROR == xtype)
160    {
161#ifndef NDEBUG
162        const char *estr = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
163#endif
164        secdebug(SOSCKCSCOPE, "default: xpc error: %s\n", estr);
165#if 0   // just log for now
166        CFStringRef errStr = CFStringCreateWithCString(kCFAllocatorDefault, estr, kCFStringEncodingUTF8);
167        CFMutableDictionaryRef userInfo = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
168        if (errStr)
169            CFDictionaryAddValue(userInfo, kCFErrorLocalizedDescriptionKey, errStr);
170        if (error)
171            *error = CFErrorCreate(kCFAllocatorDefault, sErrorDomain, kSOSOXPCErrorEvent, userInfo);
172        CFReleaseSafe(errStr);
173        CFReleaseSafe(userInfo);
174#endif
175    }
176    else
177    if (XPC_TYPE_DICTIONARY == xtype)
178    {
179        secdebug(SOSCKCSCOPE, "received dictionary event %p\n", event);
180        return true;
181    }
182    else
183    {
184        secdebug(SOSCKCSCOPE, "default: unexpected connection event %p\n", event);
185        describeXPCObject("handle_xpc_event: obj : ", event);
186        if (error)
187            *error = makeError(kSOSOUnexpectedXPCEvent);
188    }
189    return false;
190}
191
192static bool handle_xpc_event(SOSXPCCloudTransportRef transport, xpc_object_t event)
193{
194    CFErrorRef localError = NULL;
195    // See <rdar://problem/14566253>
196    secerror(">>>>> handle_connection_event via event_handler <<<<<, WTF?");
197    bool result = false;
198    if ((result = xpc_event_filter(transport->serviceConnection, event, &localError)))
199    {
200        const char *operation = xpc_dictionary_get_string(event, kMessageKeyOperation);
201        if (!operation || strcmp(operation, kMessageOperationItemChanged))  // some op we don't care about
202        {
203            secdebug(SOSCKCSCOPE, "operation: %s", operation);
204            return result;
205        }
206
207        xpc_object_t xrv = xpc_dictionary_get_value(event, kMessageKeyValue);
208        if (!xrv)
209        {
210            secdebug(SOSCKCSCOPE, "xrv null for kMessageKeyValue");
211            return result;
212        }
213        describeXPCObject("xrv", xrv);
214
215        CFDictionaryRef returnedValues = _CFXPCCreateCFObjectFromXPCObject(xrv);
216        secdebug(SOSCKCSCOPE, "returnedValues: %@", returnedValues);
217
218        SOSCloudKeychainHandleUpdate(returnedValues);
219
220        CFReleaseNull(returnedValues);
221    }
222    CFReleaseSafe(localError);
223
224    return result;
225}
226
227static void SOSXPCCloudTransportInit(SOSXPCCloudTransportRef transport)
228{
229    secdebug(SOSCKCSCOPE, "initXPCConnection\n");
230
231    transport->xpc_queue = dispatch_queue_create(xpcServiceName, DISPATCH_QUEUE_SERIAL);
232
233    transport->serviceConnection = xpc_connection_create_mach_service(xpcServiceName, transport->xpc_queue, 0);
234
235    secdebug(SOSCKCSCOPE, "serviceConnection: %p\n", transport->serviceConnection);
236
237    xpc_connection_set_event_handler(transport->serviceConnection, ^(xpc_object_t event)
238    {
239        secdebug(SOSCKCSCOPE, "xpc_connection_set_event_handler\n");
240        handle_xpc_event(transport, event);
241    });
242
243    xpc_connection_resume(transport->serviceConnection);
244    xpc_retain(transport->serviceConnection);
245}
246
247static void talkWithKVS(SOSXPCCloudTransportRef transport, xpc_object_t message, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
248{
249    __block CFErrorRef error = NULL;
250    __block CFTypeRef object = NULL;
251
252    dispatch_block_t callback = ^{
253            if (replyBlock)
254                replyBlock(object, error);
255            if(object)
256                CFReleaseNull(object);
257            if (error)
258            {
259                secerror("callback error: %@", error);
260                CFReleaseNull(error);
261            }
262            dispatch_release(processQueue);
263        };
264
265    require_action(transport->serviceConnection, xit, error = makeError(kSOSConnectionNotOpen));
266    require_action(message, xit, error = makeError(kSOSObjectNotFoundError));
267    dispatch_retain(processQueue);
268
269    xpc_connection_send_message_with_reply(transport->serviceConnection, message, transport->xpc_queue, ^(xpc_object_t reply)
270        {
271            if (xpc_event_filter(transport->serviceConnection, reply, &error) && reply)
272            {
273                describeXPCObject("getValuesFromKVS: reply : ", reply);
274                if (error)
275                    secerror("Error from xpc_event_filter: %@", error);
276                xpc_object_t xrv = xpc_dictionary_get_value(reply, kMessageKeyValue);
277                if (xrv)
278                {
279                    describeXPCObject("talkWithKVS: xrv: ", xrv);
280                    /*
281                        * The given XPC object must be one that was previously returned by
282                        * _CFXPCCreateXPCMessageWithCFObject().
283                    */
284                    object = _CFXPCCreateCFObjectFromXPCObject(xrv);   // CF object is retained; release in callback
285                    secnotice("talkwithkvs", "converted CF object: %@", object);
286                }
287                else
288                    secerror("missing value reply");
289
290                xpc_object_t xerror = xpc_dictionary_get_value(reply, kMessageKeyError);
291                if (xerror)
292                    error = SecCreateCFErrorWithXPCObject(xerror);  // use SecCFCreateErrorWithFormat?
293            }
294            dispatch_async(processQueue, callback);
295        });
296    return;
297
298xit:
299    secerror("talkWithKVS error: %@", error);
300    if (replyBlock)
301        dispatch_async(processQueue, callback);
302    CFReleaseSafe(error);
303}
304
305// MARK: ---------- SOSXPCCloudTransport Client Calls ----------
306
307/* Concrete function backend implementations. */
308static void SOSCloudTransportSetItemsChangedBlock(SOSCloudTransportRef transport,
309                                                  CloudItemsChangedBlock itemsChangedBlock) {
310    if (transport->itemsChangedBlock != itemsChangedBlock)
311    {
312        if (transport->itemsChangedBlock)
313            Block_release(transport->itemsChangedBlock);
314        transport->itemsChangedBlock = Block_copy(itemsChangedBlock);
315    }
316}
317
318/* Virtual function backend implementations. */
319static void SOSCloudTransportPut(SOSCloudTransportRef transport, CFDictionaryRef values, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
320{
321    SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
322    secdebug(SOSCKCSCOPE, "%@", values);
323    CFErrorRef error = NULL;
324    xpc_object_t message = NULL;
325    xpc_object_t xobject = NULL;
326    require_action(values, xit, error = makeError(kSOSObjectNotFoundError));
327
328    message = xpc_dictionary_create(NULL, NULL, 0);
329    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
330    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationPUTDictionary);
331
332    xobject = _CFXPCCreateXPCObjectFromCFObject(values);
333    require_action(xobject, xit, error = makeError(kSOSObjectCantBeConvertedToXPCObject));
334    xpc_dictionary_set_value(message, kMessageKeyValue, xobject);
335    xpc_release(xobject);
336
337    talkWithKVS(xpcTransport, message, processQueue, replyBlock);
338    xpc_release(message);
339    return;
340
341xit:
342    if (replyBlock)
343        replyBlock(NULL, error);
344    CFReleaseSafe(error);
345}
346
347/* Get from KVS */
348static void SOSCloudTransportGet(SOSCloudTransportRef transport, CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
349{
350    SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
351    secdebug(SOSCKCSCOPE, "%@", keysToGet);
352    CFErrorRef error = NULL;
353    xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0);
354    xpc_object_t xkeysToGet = keysToGet ? _CFXPCCreateXPCObjectFromCFObject(keysToGet) : xpc_null_create();
355
356    require_action(xkeysToGet, xit, error = makeError(kSOSObjectNotFoundError));
357
358    if (keysToGet)  // don't add if nulll; will call getall
359        xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToGet);
360
361    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
362    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
363    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationGETv2);
364    xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest);
365
366    talkWithKVS(xpcTransport, message, processQueue, replyBlock);
367    xpc_release(xkeysToGet);
368    xpc_release(xkeysOfInterest);
369    xpc_release(message);
370    return;
371
372xit:
373    if(xkeysOfInterest)
374        xpc_release(xkeysOfInterest);
375    if(xkeysToGet)
376        xpc_release(xkeysToGet);
377    if (replyBlock)
378        replyBlock(NULL, error);
379    CFReleaseSafe(error);
380}
381
382static void SOSCloudTransportRegisterKeys(SOSCloudTransportRef transport, CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock, CloudItemsChangedBlock notificationBlock)
383{
384    SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
385    secdebug(SOSCKCSCOPE, "%@", keysToGet);
386    xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0);
387    xpc_object_t xkeysToRegister = keysToGet ? _CFXPCCreateXPCObjectFromCFObject(keysToGet) : xpc_null_create();
388    xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToRegister);
389
390    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
391    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
392    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationRegisterKeysAndGet);
393    xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest);
394
395    SOSCloudTransportSetItemsChangedBlock(transport, notificationBlock);
396    talkWithKVS(xpcTransport, message, processQueue, replyBlock);
397    xpc_release(xkeysOfInterest);
398    xpc_release(xkeysToRegister);
399    xpc_release(message);
400}
401
402//
403// Handles NULL by seting xpc_null.
404static void SecXPCDictionarySetCFObject(xpc_object_t xdict, const char *key, CFTypeRef object)
405{
406    xpc_object_t xpc_obj = object ? _CFXPCCreateXPCObjectFromCFObject(object) : xpc_null_create();
407    xpc_dictionary_set_value(xdict, key, xpc_obj);
408    xpc_release(xpc_obj);
409}
410
411static bool SOSCloudTransportUpdateKeys(SOSCloudTransportRef transport,
412                                        bool getNewKeysOnly,
413                                        CFArrayRef alwaysKeys,
414                                        CFArrayRef afterFirstUnlockKeys,
415                                        CFArrayRef unlockedKeys,
416                                        CFErrorRef *error)
417{
418    __block bool success = true;
419    dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
420
421    CloudKeychainReplyBlock replyBlock = ^(CFDictionaryRef returnedValues, CFErrorRef returnedError)
422    {
423        if (returnedError) {
424            success = false;
425            if (error) {
426                *error = returnedError;
427                CFRetain(*error);
428            }
429        }
430        CFReleaseSafe(returnedError);
431    };
432
433    SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
434
435    xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0);
436    xpc_dictionary_set_bool(xkeysOfInterest, kMessageKeyGetNewKeysOnly, getNewKeysOnly);
437    SecXPCDictionarySetCFObject(xkeysOfInterest, kMessageKeyKeysToGet, alwaysKeys);
438    SecXPCDictionarySetCFObject(xkeysOfInterest, kMessageKeyKeysRequireFirstUnlock, afterFirstUnlockKeys);
439    SecXPCDictionarySetCFObject(xkeysOfInterest, kMessageKeyKeysRequiresUnlocked, unlockedKeys);
440
441    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
442    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
443    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationRegisterKeysAndGet);
444    xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest);
445
446    talkWithKVS(xpcTransport, message, processQueue, replyBlock);
447    xpc_release(message);
448    xpc_release(xkeysOfInterest);
449
450    return success;
451}
452
453static void SOSCloudTransportUnregisterKeys(SOSCloudTransportRef transport, CFArrayRef keysToUnregister, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
454{
455    SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
456    secdebug(SOSCKCSCOPE, "%@", keysToUnregister);
457    xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0);
458    xpc_object_t xkeysToUnregister = keysToUnregister ? _CFXPCCreateXPCObjectFromCFObject(keysToUnregister) : xpc_null_create();
459    xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToUnregister);
460
461    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
462    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
463    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationUnregisterKeys);
464    xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest);
465
466    talkWithKVS(xpcTransport, message, processQueue, replyBlock);
467    xpc_release(xkeysOfInterest);
468    xpc_release(xkeysToUnregister);
469    xpc_release(message);
470}
471
472static void SOSCloudTransportGetAll(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
473{
474    secdebug(SOSCKCSCOPE, "start");
475    SOSCloudTransportGet(transport, NULL, processQueue, replyBlock);
476}
477
478static void SOSCloudTransportSync(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
479{
480    SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
481    secdebug(SOSCKCSCOPE, "start");
482    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
483    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
484    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationSynchronize);
485    talkWithKVS(xpcTransport, message, processQueue, replyBlock);
486    xpc_release(message);
487}
488
489static void SOSCloudTransportSyncAndWait(SOSCloudTransportRef transport, CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
490{
491    SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
492    secdebug(SOSCKCSCOPE, "%@", keysToGet);
493    xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0);
494
495    xpc_object_t xkeysToRegister = keysToGet ? _CFXPCCreateXPCObjectFromCFObject(keysToGet) : xpc_null_create();
496    xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToRegister);
497    xpc_release(xkeysToRegister);
498    xkeysToRegister = NULL;
499
500    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
501    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
502    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationSynchronizeAndWait);
503    xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest);
504    xpc_release(xkeysOfInterest);
505    xkeysOfInterest = NULL;
506
507    talkWithKVS(xpcTransport, message, processQueue, replyBlock);
508    xpc_release(message);
509}
510
511static void SOSCloudTransportClearAll(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
512{
513    SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
514    secdebug(SOSCKCSCOPE, "start");
515    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
516    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
517    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationClearStore);
518    talkWithKVS(xpcTransport, message, processQueue, replyBlock);
519    xpc_release(message);
520}
521
522static void SOSCloudTransportRemoveObjectForKey(SOSCloudTransportRef transport, CFStringRef keyToRemove, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
523{
524    SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
525    secdebug(SOSCKCSCOPE, "start");
526    CFErrorRef error = NULL;
527    xpc_object_t message = NULL;
528    xpc_object_t xkeytoremove = NULL;
529
530    require_action(keyToRemove, xit, error = makeError(kSOSObjectNotFoundError));
531
532    message = xpc_dictionary_create(NULL, NULL, 0);
533    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
534    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationRemoveObjectForKey);
535
536    xkeytoremove = _CFXPCCreateXPCObjectFromCFObject(keyToRemove);
537    require_action(xkeytoremove, xit, error = makeError(kSOSObjectCantBeConvertedToXPCObject));
538    xpc_dictionary_set_value(message, kMessageKeyKey, xkeytoremove);
539    xpc_release(xkeytoremove);
540
541    talkWithKVS(xpcTransport, message, processQueue, replyBlock);
542    xpc_release(message);
543    return;
544
545xit:
546    if(xkeytoremove)
547        xpc_release(xkeytoremove);
548    if(message)
549        xpc_release(message);
550    if (replyBlock)
551        replyBlock(NULL, error);
552    CFReleaseSafe(error);
553}
554
555static void SOSCloudTransportLocalNotification(SOSCloudTransportRef transport, CFStringRef messageToUser, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
556{
557    SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
558    secdebug(SOSCKCSCOPE, "start");
559    xpc_object_t xLocalNotificationDict = xpc_dictionary_create(NULL, NULL, 0);
560    char *headerKey = CFStringToCString(kCFUserNotificationAlertHeaderKey);
561    char *message = CFStringToCString(messageToUser);
562    xpc_dictionary_set_string(xLocalNotificationDict, headerKey, message);
563
564    xpc_object_t xpcmessage = xpc_dictionary_create(NULL, NULL, 0);
565    xpc_dictionary_set_uint64(xpcmessage, kMessageKeyVersion, kCKDXPCVersion);
566    xpc_dictionary_set_string(xpcmessage, kMessageKeyOperation, kOperationUILocalNotification);
567    xpc_dictionary_set_value (xpcmessage, kMessageKeyValue, xLocalNotificationDict);
568    xpc_release(xLocalNotificationDict);
569
570    talkWithKVS(xpcTransport, xpcmessage, processQueue, replyBlock);
571
572    free(headerKey);
573    free(message);
574    xpc_release(xpcmessage);
575}
576
577static void SOSCloudTransportSetParams(SOSCloudTransportRef transport, CFDictionaryRef paramsDict, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
578{
579    secdebug(SOSCKCSCOPE, "start");
580    SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
581
582    xpc_object_t xParamsDict = paramsDict ? _CFXPCCreateXPCObjectFromCFObject(paramsDict) : xpc_null_create();
583
584    xpc_object_t xpcmessage = xpc_dictionary_create(NULL, NULL, 0);
585    xpc_dictionary_set_uint64(xpcmessage, kMessageKeyVersion, kCKDXPCVersion);
586    xpc_dictionary_set_string(xpcmessage, kMessageKeyOperation, kOperationSetParams);
587    xpc_dictionary_set_value (xpcmessage, kMessageKeyValue, xParamsDict);
588    xpc_release(xParamsDict);
589
590    talkWithKVS(xpcTransport, xpcmessage, processQueue, replyBlock);
591
592    xpc_release(xpcmessage);
593}
594
595static void SOSCloudTransportRequestSyncWithAllPeers(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
596{
597    secdebug(SOSCKCSCOPE, "start");
598    SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
599
600    xpc_object_t xpcmessage = xpc_dictionary_create(NULL, NULL, 0);
601    xpc_dictionary_set_uint64(xpcmessage, kMessageKeyVersion, kCKDXPCVersion);
602    xpc_dictionary_set_string(xpcmessage, kMessageKeyOperation, kOperationRequestSyncWithAllPeers);
603
604    talkWithKVS(xpcTransport, xpcmessage, processQueue, replyBlock);
605
606    xpc_release(xpcmessage);
607}
608
609static SOSCloudTransportRef SOSCloudTransportCreateXPCTransport(void)
610{
611    SOSXPCCloudTransportRef st;
612    st = calloc(1, sizeof(*st));
613    st->transport.put = SOSCloudTransportPut;
614    st->transport.registerKeys = SOSCloudTransportRegisterKeys;
615    st->transport.updateKeys = SOSCloudTransportUpdateKeys;
616    st->transport.unregisterKeys = SOSCloudTransportUnregisterKeys;
617    st->transport.get = SOSCloudTransportGet;
618    st->transport.getAll = SOSCloudTransportGetAll;
619    st->transport.synchronize = SOSCloudTransportSync;
620    st->transport.synchronizeAndWait = SOSCloudTransportSyncAndWait;
621    st->transport.clearAll = SOSCloudTransportClearAll;
622    st->transport.removeObjectForKey = SOSCloudTransportRemoveObjectForKey;
623    st->transport.localNotification = SOSCloudTransportLocalNotification;
624    st->transport.setParams = SOSCloudTransportSetParams;
625    st->transport.requestSyncWithAllPeers = SOSCloudTransportRequestSyncWithAllPeers;
626    SOSXPCCloudTransportInit(st);
627    return &st->transport;
628}
629
630// MARK: ---------- SOSCloudKeychain concrete client APIs ----------
631void SOSCloudKeychainSetItemsChangedBlock(CloudItemsChangedBlock itemsChangedBlock)
632{
633    secdebug(SOSCKCSCOPE, "start");
634    SOSCloudTransportSetItemsChangedBlock(SOSCloudTransportDefaultTransport(),
635                                          itemsChangedBlock);
636}
637
638// MARK: ---------- SOSCloudKeychain virtual client APIs ----------
639
640void SOSCloudKeychainPutObjectsInCloud(CFDictionaryRef objects, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
641{
642    SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
643    if (cTransportRef)
644        cTransportRef->put(cTransportRef, objects, processQueue, replyBlock);
645}
646
647void SOSCloudKeychainRegisterKeysAndGet(CFArrayRef keysToRegister, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock, CloudItemsChangedBlock notificationBlock)
648{
649    SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
650    if (cTransportRef)
651        cTransportRef->registerKeys(cTransportRef, keysToRegister, processQueue, replyBlock, notificationBlock);
652}
653
654bool SOSCloudKeychainUpdateKeys(bool getNewKeysOnly,
655                                CFArrayRef alwaysKeys,
656                                CFArrayRef afterFirstUnlockKeys,
657                                CFArrayRef unlockedKeys,
658                                CFErrorRef *error)
659{
660    SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
661    if (cTransportRef)
662        return cTransportRef->updateKeys(cTransportRef, getNewKeysOnly, alwaysKeys, afterFirstUnlockKeys, unlockedKeys, error);
663
664    return false;
665}
666
667void SOSCloudKeychainUnRegisterKeys(CFArrayRef keysToUnregister, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
668{
669    SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
670    if (cTransportRef)
671        cTransportRef->unregisterKeys(cTransportRef, keysToUnregister, processQueue, replyBlock);
672}
673
674void SOSCloudKeychainHandleUpdate(CFDictionaryRef updates)
675{
676    SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
677    if (cTransportRef->itemsChangedBlock)
678        ((CloudItemsChangedBlock)cTransportRef->itemsChangedBlock)(updates);
679}
680
681void SOSCloudKeychainGetObjectsFromCloud(CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
682{
683    SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
684    if (cTransportRef)
685        cTransportRef->get(cTransportRef, keysToGet, processQueue, replyBlock);
686}
687
688void SOSCloudKeychainGetAllObjectsFromCloud(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
689{
690    SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
691    if (cTransportRef)
692        cTransportRef->getAll(cTransportRef, processQueue, replyBlock);
693}
694
695void SOSCloudKeychainSynchronizeAndWait(CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
696{
697    SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
698    if (cTransportRef)
699        cTransportRef->synchronizeAndWait(cTransportRef, keysToGet, processQueue, replyBlock);
700}
701
702//DEBUG ONLY
703void SOSCloudKeychainSynchronize(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
704{
705    SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
706    if (cTransportRef)
707        cTransportRef->synchronize(cTransportRef, processQueue, replyBlock);
708}
709
710//DEBUG ONLY
711void SOSCloudKeychainClearAll(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
712{
713    SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
714    if (cTransportRef)
715        cTransportRef->clearAll(cTransportRef, processQueue, replyBlock);
716}
717
718void SOSCloudKeychainRemoveObjectForKey(CFStringRef keyToRemove, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
719{
720    SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
721    if (cTransportRef)
722        cTransportRef->removeObjectForKey(cTransportRef, keyToRemove, processQueue, replyBlock);
723}
724
725void SOSCloudKeychainUserNotification(CFStringRef messageToUser, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
726{
727    SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
728    if (cTransportRef)
729        cTransportRef->localNotification(cTransportRef, messageToUser, processQueue, replyBlock);
730}
731
732void SOSCloudKeychainSetParams(CFDictionaryRef paramsDict, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
733{
734    SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
735    if (cTransportRef)
736        cTransportRef->setParams(cTransportRef, paramsDict, processQueue, replyBlock);
737}
738
739void SOSCloudKeychainRequestSyncWithAllPeers(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
740{
741    SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
742    if (cTransportRef)
743        cTransportRef->requestSyncWithAllPeers(cTransportRef, processQueue, replyBlock);
744}
745
746void SOSCloudKeychainSetCallbackMethodXPC(void)
747{
748    // Call this before making any other calls to CloudKeychainProxy
749    CFDictionaryRef paramsDict = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
750        kParamCallbackMethod, kParamCallbackMethodXPC, NULL);
751    dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
752    SOSCloudKeychainSetParams(paramsDict, processQueue, ^(CFDictionaryRef returnedValues, CFErrorRef error)
753        {
754            secerror("set params called back");
755        });
756    CFReleaseSafe(paramsDict);
757}
758