1/*
2 * Copyright (c) 2012-2014 Apple 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//  SOSCloudCircle.m
26//
27
28#include <stdio.h>
29#include <AssertMacros.h>
30#include <SecureObjectSync/SOSCloudCircle.h>
31#include <SecureObjectSync/SOSCloudCircleInternal.h>
32#include <SecureObjectSync/SOSCircle.h>
33#include <SecureObjectSync/SOSAccount.h>
34#include <SecureObjectSync/SOSFullPeerInfo.h>
35#include <SecureObjectSync/SOSPeerInfoCollections.h>
36#include <SecureObjectSync/SOSInternal.h>
37#include <Security/SecKeyPriv.h>
38#include <Security/SecFramework.h>
39#include <CoreFoundation/CFXPCBridge.h>
40
41#include <securityd/SecItemServer.h>
42
43#include <utilities/SecDispatchRelease.h>
44#include <utilities/SecCFRelease.h>
45#include <utilities/SecCFWrappers.h>
46#include <utilities/SecXPCError.h>
47
48#include <utilities/debugging.h>
49
50#include <CoreFoundation/CoreFoundation.h>
51
52#include <xpc/xpc.h>
53#define MINIMIZE_INCLUDES MINIMIZE_INCLUDES
54#include <ipc/securityd_client.h>
55#include <securityd/spi.h>
56
57#include "SOSRegressionUtilities.h"
58
59
60const char * kSOSCCCircleChangedNotification = "com.apple.security.secureobjectsync.circlechanged";
61
62#define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
63
64static bool xpc_dictionary_entry_is_type(xpc_object_t dictionary, const char *key, xpc_type_t type)
65{
66    xpc_object_t value = xpc_dictionary_get_value(dictionary, key);
67
68    return value && (xpc_get_type(value) == type);
69}
70
71SOSCCStatus SOSCCThisDeviceIsInCircle(CFErrorRef *error)
72{
73    static int counter = 0;
74    if(counter++ > 5) secerror("SOSCCThisDeviceIsInCircle!! %d\n", counter);
75    sec_trace_enter_api(NULL);
76    sec_trace_return_api(SOSCCStatus, ^{
77        SOSCCStatus result = kSOSCCError;
78
79        do_if_registered(soscc_ThisDeviceIsInCircle, error);
80
81        xpc_object_t message = securityd_create_message(kSecXPCOpDeviceInCircle, error);
82        if (message) {
83            xpc_object_t response = securityd_message_with_reply_sync(message, error);
84
85            if (response && xpc_dictionary_entry_is_type(response, kSecXPCKeyResult, XPC_TYPE_INT64)) {
86                result = (SOSCCStatus) xpc_dictionary_get_int64(response, kSecXPCKeyResult);
87            } else {
88                result = kSOSCCError;
89            }
90
91            if (result < 0) {
92                if (response && securityd_message_no_error(response, error))
93                {
94                    char *desc = xpc_copy_description(response);
95                    SecCFCreateErrorWithFormat(0, sSecXPCErrorDomain, NULL, error, NULL, CFSTR("Remote error occurred/no info: %s"), desc);
96                    free((void *)desc);
97                }
98            }
99            if(response)
100                xpc_release(response);
101            if(message)
102                xpc_release(message);
103        }
104
105
106        return result;
107    }, CFSTR("SOSCCStatus=%d"))
108}
109
110static CFStringRef simple_cfstring_error_request(enum SecXPCOperation op, CFErrorRef* error)
111{
112    __block CFStringRef result = NULL;
113
114    secdebug("sosops","enter - operation: %d", op);
115    securityd_send_sync_and_do(op, error, NULL, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
116        const char *c_string = xpc_dictionary_get_string(response, kSecXPCKeyResult);
117
118        if (c_string) {
119            result = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)c_string, strlen(c_string), kCFStringEncodingUTF8, false);
120        }
121
122        return c_string != NULL;
123    });
124    return result;
125}
126
127static bool simple_bool_error_request(enum SecXPCOperation op, CFErrorRef* error)
128{
129    __block bool result = false;
130
131    secdebug("sosops","enter - operation: %d", op);
132    securityd_send_sync_and_do(op, error, NULL, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
133        result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
134        return result;
135    });
136    return result;
137}
138
139static CFArrayRef simple_array_error_request(enum SecXPCOperation op, CFErrorRef* error)
140{
141    __block CFArrayRef result = false;
142
143    secdebug("sosops","enter - operation: %d", op);
144    securityd_send_sync_and_do(op, error, NULL, ^bool(xpc_object_t response, CFErrorRef *error) {
145        xpc_object_t temp_result = xpc_dictionary_get_value(response, kSecXPCKeyResult);
146        result = _CFXPCCreateCFObjectFromXPCObject(temp_result);
147        return result != NULL;
148    });
149    return result;
150}
151
152static int simple_int_error_request(enum SecXPCOperation op, CFErrorRef* error)
153{
154    __block int result = 0;
155
156    secdebug("sosops","enter - operation: %d", op);
157    securityd_send_sync_and_do(op, error, NULL, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
158        int64_t temp_result =  xpc_dictionary_get_int64(response, kSecXPCKeyResult);
159        if ((temp_result >= INT32_MIN) && (temp_result <= INT32_MAX)) {
160            result = (int)temp_result;
161        }
162        return result;
163    });
164    return result;
165}
166
167static CFArrayRef array_of_info_error_request(enum SecXPCOperation op, CFErrorRef* error)
168{
169    __block CFArrayRef result = NULL;
170
171    secdebug("sosops","enter - operation: %d", op);
172    securityd_send_sync_and_do(op, error, NULL, ^bool(xpc_object_t response, CFErrorRef *error) {
173        xpc_object_t encoded_array = xpc_dictionary_get_value(response, kSecXPCKeyResult);
174        if (response && (NULL != encoded_array)) {
175            result = CreateArrayOfPeerInfoWithXPCObject(encoded_array,  error);
176        }
177        return result != NULL;
178    });
179
180    return result;
181}
182
183static bool info_array_to_bool_error_request(enum SecXPCOperation op, CFArrayRef peer_infos, CFErrorRef* error)
184{
185    __block bool result = false;
186
187    secdebug("sosops", "enter - operation: %d", op);
188    securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
189        xpc_object_t encoded_peers = CreateXPCObjectWithArrayOfPeerInfo(peer_infos, error);
190        if (encoded_peers)
191            xpc_dictionary_set_value(message, kSecXPCKeyPeerInfos, encoded_peers);
192        return encoded_peers != NULL;
193    }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
194        result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
195        return result;
196    });
197    return result;
198}
199
200static bool uint64_t_to_bool_error_request(enum SecXPCOperation op,
201                                           uint64_t number,
202                                           CFErrorRef* error)
203{
204    __block bool result = false;
205
206    securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
207        xpc_dictionary_set_uint64(message, kSecXPCLimitInMinutes, number);
208        return true;
209    }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
210        result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
211        return result;
212    });
213
214    return result;
215}
216
217bool SOSCCRequestToJoinCircle(CFErrorRef* error)
218{
219    sec_trace_enter_api(NULL);
220    sec_trace_return_bool_api(^{
221        do_if_registered(soscc_RequestToJoinCircle, error);
222
223        return simple_bool_error_request(kSecXPCOpRequestToJoin, error);
224    }, NULL)
225}
226
227bool SOSCCRequestToJoinCircleAfterRestore(CFErrorRef* error)
228{
229    sec_trace_enter_api(NULL);
230    sec_trace_return_bool_api(^{
231        do_if_registered(soscc_RequestToJoinCircleAfterRestore, error);
232
233        return simple_bool_error_request(kSecXPCOpRequestToJoinAfterRestore, error);
234    }, NULL)
235}
236
237bool SOSCCRequestEnsureFreshParameters(CFErrorRef* error)
238{
239        sec_trace_enter_api(NULL);
240        sec_trace_return_bool_api(^{
241                do_if_registered(soscc_RequestEnsureFreshParameters, error);
242
243                return simple_bool_error_request(kSecXPCOpRequestEnsureFreshParameters, error);
244            }, NULL)
245}
246
247bool SOSCCResetToOffering(CFErrorRef* error)
248{
249    secwarning("SOSCCResetToOffering called");
250    sec_trace_enter_api(NULL);
251    sec_trace_return_bool_api(^{
252        do_if_registered(soscc_ResetToOffering, error);
253
254        return simple_bool_error_request(kSecXPCOpResetToOffering, error);
255    }, NULL)
256}
257
258bool SOSCCResetToEmpty(CFErrorRef* error)
259{
260    secwarning("SOSCCResetToEmpty called");
261    sec_trace_enter_api(NULL);
262    sec_trace_return_bool_api(^{
263        do_if_registered(soscc_ResetToEmpty, error);
264
265        return simple_bool_error_request(kSecXPCOpResetToEmpty, error);
266    }, NULL)
267}
268
269bool SOSCCRemoveThisDeviceFromCircle(CFErrorRef* error)
270{
271    sec_trace_enter_api(NULL);
272    sec_trace_return_bool_api(^{
273        do_if_registered(soscc_RemoveThisDeviceFromCircle, error);
274
275        return simple_bool_error_request(kSecXPCOpRemoveThisDeviceFromCircle, error);
276    }, NULL)
277}
278
279bool SOSCCBailFromCircle_BestEffort(uint64_t limit_in_seconds, CFErrorRef* error)
280{
281    sec_trace_enter_api(NULL);
282    sec_trace_return_bool_api(^{
283        do_if_registered(soscc_BailFromCircle, limit_in_seconds, error);
284
285        return uint64_t_to_bool_error_request(kSecXPCOpBailFromCircle, limit_in_seconds, error);
286    }, NULL)
287}
288
289bool SOSCCSignedOut(bool immediate, CFErrorRef* error)
290{
291    uint64_t limit = strtoul(optarg, NULL, 10);
292
293    if(immediate)
294        return SOSCCRemoveThisDeviceFromCircle(error);
295    else
296        return SOSCCBailFromCircle_BestEffort(limit, error);
297
298}
299
300CFArrayRef SOSCCCopyPeerPeerInfo(CFErrorRef* error)
301{
302    sec_trace_enter_api(NULL);
303    sec_trace_return_api(CFArrayRef, ^{
304        do_if_registered(soscc_CopyPeerInfo, error);
305
306        return array_of_info_error_request(kSecXPCOpCopyPeerPeerInfo, error);
307    }, CFSTR("return=%@"));
308}
309
310CFArrayRef SOSCCCopyConcurringPeerPeerInfo(CFErrorRef* error)
311{
312    sec_trace_enter_api(NULL);
313    sec_trace_return_api(CFArrayRef, ^{
314        do_if_registered(soscc_CopyConcurringPeerInfo, error);
315
316        return array_of_info_error_request(kSecXPCOpCopyConcurringPeerPeerInfo, error);
317    }, CFSTR("return=%@"));
318}
319
320CFArrayRef SOSCCCopyGenerationPeerInfo(CFErrorRef* error)
321{
322    sec_trace_enter_api(NULL);
323    sec_trace_return_api(CFArrayRef, ^{
324        do_if_registered(soscc_CopyGenerationPeerInfo, error);
325
326        return simple_array_error_request(kSecXPCOpCopyGenerationPeerInfo, error);
327    }, CFSTR("return=%@"));
328}
329
330CFArrayRef SOSCCCopyApplicantPeerInfo(CFErrorRef* error)
331{
332    sec_trace_enter_api(NULL);
333    sec_trace_return_api(CFArrayRef, ^{
334        do_if_registered(soscc_CopyApplicantPeerInfo, error);
335
336        return array_of_info_error_request(kSecXPCOpCopyApplicantPeerInfo, error);
337    }, CFSTR("return=%@"));
338}
339bool SOSCCValidateUserPublic(CFErrorRef* error){
340    sec_trace_enter_api(NULL);
341    sec_trace_return_api(bool, ^{
342        do_if_registered(soscc_ValidateUserPublic, error);
343
344        return simple_bool_error_request(kSecXPCOpValidateUserPublic, error);
345    }, NULL);
346}
347
348CFArrayRef SOSCCCopyValidPeerPeerInfo(CFErrorRef* error)
349{
350    sec_trace_enter_api(NULL);
351    sec_trace_return_api(CFArrayRef, ^{
352        do_if_registered(soscc_CopyValidPeerPeerInfo, error);
353
354        return array_of_info_error_request(kSecXPCOpCopyValidPeerPeerInfo, error);
355    }, CFSTR("return=%@"));
356}
357
358CFArrayRef SOSCCCopyNotValidPeerPeerInfo(CFErrorRef* error)
359{
360    sec_trace_enter_api(NULL);
361    sec_trace_return_api(CFArrayRef, ^{
362        do_if_registered(soscc_CopyNotValidPeerPeerInfo, error);
363
364        return array_of_info_error_request(kSecXPCOpCopyNotValidPeerPeerInfo, error);
365    }, CFSTR("return=%@"));
366}
367
368CFArrayRef SOSCCCopyRetirementPeerInfo(CFErrorRef* error)
369{
370    sec_trace_enter_api(NULL);
371    sec_trace_return_api(CFArrayRef, ^{
372        do_if_registered(soscc_CopyRetirementPeerInfo, error);
373
374        return array_of_info_error_request(kSecXPCOpCopyRetirementPeerInfo, error);
375    }, CFSTR("return=%@"));
376}
377
378bool SOSCCAcceptApplicants(CFArrayRef applicants, CFErrorRef* error)
379{
380    sec_trace_enter_api(NULL);
381    sec_trace_return_bool_api(^{
382        do_if_registered(soscc_AcceptApplicants, applicants, error);
383
384        return info_array_to_bool_error_request(kSecXPCOpAcceptApplicants, applicants, error);
385    }, NULL)
386}
387
388bool SOSCCRejectApplicants(CFArrayRef applicants, CFErrorRef *error)
389{
390    sec_trace_enter_api(CFSTR("applicants=%@"), applicants);
391    sec_trace_return_bool_api(^{
392        do_if_registered(soscc_RejectApplicants, applicants, error);
393
394        return info_array_to_bool_error_request(kSecXPCOpRejectApplicants, applicants, error);
395    }, NULL)
396}
397
398static bool label_and_password_to_bool_error_request(enum SecXPCOperation op,
399                                                     CFStringRef user_label, CFDataRef user_password,
400                                                     CFErrorRef* error)
401{
402    __block bool result = false;
403
404    securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
405        CFStringPerformWithCString(user_label, ^(const char *utf8Str) {
406            xpc_dictionary_set_string(message, kSecXPCKeyUserLabel, utf8Str);
407        });
408        xpc_dictionary_set_data(message, kSecXPCKeyUserPassword, CFDataGetBytePtr(user_password), CFDataGetLength(user_password));
409        return true;
410    }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
411        result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
412        return result;
413    });
414
415    return result;
416}
417
418static bool deviceid_to_bool_error_request(enum SecXPCOperation op,
419                                                     CFStringRef IDS,
420                                                     CFErrorRef* error)
421{
422    __block bool result = false;
423
424    securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
425        CFStringPerformWithCString(IDS, ^(const char *utf8Str) {
426            xpc_dictionary_set_string(message, kSecXPCKeyDeviceID, utf8Str);
427        });
428        return true;
429    }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
430        result = xpc_dictionary_get_bool(response, kSecXPCKeyDeviceID);
431        return result;
432    });
433
434    return result;
435}
436
437bool SOSCCRegisterUserCredentials(CFStringRef user_label, CFDataRef user_password, CFErrorRef* error)
438{
439    secerror("SOSCCRegisterUserCredentials - calling SOSCCSetUserCredentials!! %@\n", user_label);
440    return SOSCCSetUserCredentials(user_label, user_password, error);
441}
442
443bool SOSCCSetUserCredentials(CFStringRef user_label, CFDataRef user_password, CFErrorRef* error)
444{
445    secerror("SOSCCSetUserCredentials!! %@\n", user_label);
446	sec_trace_enter_api(CFSTR("user_label=%@"), user_label);
447    sec_trace_return_bool_api(^{
448		do_if_registered(soscc_SetUserCredentials, user_label, user_password, error);
449
450    	return label_and_password_to_bool_error_request(kSecXPCOpSetUserCredentials, user_label, user_password, error);
451    }, NULL)
452}
453bool SOSCCSetDeviceID(CFStringRef IDS, CFErrorRef* error)
454{
455    secerror("SOSCCSetDeviceID!! %@\n", IDS);
456    sec_trace_enter_api(NULL);
457    sec_trace_return_bool_api(^{
458        do_if_registered(soscc_SetDeviceID, IDS, error);
459        return deviceid_to_bool_error_request(kSecXPCOpSetDeviceID, IDS, error);
460    }, NULL)
461}
462bool SOSCCTryUserCredentials(CFStringRef user_label, CFDataRef user_password, CFErrorRef* error)
463{
464	sec_trace_enter_api(CFSTR("user_label=%@"), user_label);
465    sec_trace_return_bool_api(^{
466	    do_if_registered(soscc_TryUserCredentials, user_label, user_password, error);
467
468    	return label_and_password_to_bool_error_request(kSecXPCOpTryUserCredentials, user_label, user_password, error);
469    }, NULL)
470}
471
472
473bool SOSCCCanAuthenticate(CFErrorRef* error) {
474    sec_trace_enter_api(NULL);
475    sec_trace_return_bool_api(^{
476	    do_if_registered(soscc_CanAuthenticate, error);
477
478	    return simple_bool_error_request(kSecXPCOpCanAuthenticate, error);
479    }, NULL)
480}
481
482bool SOSCCPurgeUserCredentials(CFErrorRef* error) {
483    sec_trace_enter_api(NULL);
484    sec_trace_return_bool_api(^{
485	    do_if_registered(soscc_PurgeUserCredentials, error);
486
487	    return simple_bool_error_request(kSecXPCOpPurgeUserCredentials, error);
488    }, NULL)
489}
490
491enum DepartureReason SOSCCGetLastDepartureReason(CFErrorRef *error) {
492    sec_trace_enter_api(NULL);
493    sec_trace_return_api(enum DepartureReason, ^{
494	    do_if_registered(soscc_GetLastDepartureReason, error);
495
496	    return (enum DepartureReason) simple_int_error_request(kSecXPCOpGetLastDepartureReason, error);
497    }, NULL)
498}
499
500CFStringRef SOSCCCopyIncompatibilityInfo(CFErrorRef* error) {
501    sec_trace_enter_api(NULL);
502    sec_trace_return_api(CFStringRef, ^{
503	    do_if_registered(soscc_CopyIncompatibilityInfo, error);
504
505	    return simple_cfstring_error_request(kSecXPCOpCopyIncompatibilityInfo, error);
506    }, NULL)
507}
508
509CFStringRef SOSCCRequestDeviceID(CFErrorRef* error)
510{
511    sec_trace_enter_api(NULL);
512    sec_trace_return_api(CFStringRef, ^{
513        do_if_registered(soscc_RequestDeviceID, error);
514        CFStringRef deviceID = simple_cfstring_error_request(kSecXPCOpRequestDeviceID, error);
515        return deviceID;
516    }, NULL)
517}
518
519bool SOSCCProcessEnsurePeerRegistration(CFErrorRef* error){
520    secnotice("updates", "enter SOSCCProcessEnsurePeerRegistration");
521    sec_trace_enter_api(NULL);
522    sec_trace_return_bool_api(^{
523        do_if_registered(soscc_EnsurePeerRegistration, error);
524
525        return simple_bool_error_request(soscc_EnsurePeerRegistration_id, error);
526    }, NULL)
527}
528
529
530SyncWithAllPeersReason SOSCCProcessSyncWithAllPeers(CFErrorRef* error)
531{
532    sec_trace_enter_api(NULL);
533    sec_trace_return_api(SyncWithAllPeersReason, ^{
534	    do_if_registered(soscc_ProcessSyncWithAllPeers, error);
535
536	    return (SyncWithAllPeersReason) simple_int_error_request(kSecXPCOpProcessSyncWithAllPeers, error);
537    }, NULL)
538}
539
540CFStringRef SOSCCGetStatusDescription(SOSCCStatus status)
541{
542    switch (status) {
543        case kSOSCCInCircle:
544            return CFSTR("InCircle");
545        case kSOSCCNotInCircle:
546            return CFSTR("NotInCircle");
547        case kSOSCCRequestPending:
548            return CFSTR("RequestPending");
549        case kSOSCCCircleAbsent:
550            return CFSTR("CircleAbsent");
551        case kSOSCCError:
552            return CFSTR("InternalError");
553        default:
554            return CFSTR("Unknown Status (%d)");
555    };
556}
557