2 * Copyright (c) 2007-2009,2012-2020 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <TargetConditionals.h>
26 // client.c is from the iOS world and must compile with an iOS view of the headers
27 #ifndef SEC_IOS_ON_OSX
28 #define SEC_IOS_ON_OSX 1
29 #endif // SEC_IOS_ON_OSX
32 #ifndef SECITEM_SHIM_OSX
33 #define SECITEM_SHIM_OSX 1
34 #endif // SECITEM_SHIM_OSX
35 #endif // TARGET_OS_OSX
38 #include <sys/queue.h>
40 #include <vproc_priv.h>
42 #include <xpc/private.h>
43 #include <os/feature_private.h>
45 #include <CoreFoundation/CoreFoundation.h>
46 #include <Security/SecItem.h>
47 #include <Security/SecBasePriv.h>
48 #include <Security/SecInternal.h>
49 #include <Security/SecuritydXPC.h>
50 #include <Security/SecTask.h>
51 #include <Security/SecItemPriv.h>
53 #include <utilities/debugging.h>
54 #include <utilities/SecCFError.h>
55 #include <utilities/SecXPCError.h>
56 #include <utilities/SecCFWrappers.h>
57 #include <utilities/SecDispatchRelease.h>
58 #include <utilities/SecDb.h> // TODO Fixme this gets us SecError().
59 #include <utilities/SecAKSWrappers.h>
60 #include <ipc/securityd_client.h>
62 #include "server_security_helpers.h"
64 struct securityd
*gSecurityd
;
65 struct trustd
*gTrustd
;
71 /* Hardcoded Access Groups for the server itself */
72 static CFArrayRef
SecServerCopyAccessGroups(void) {
73 CFArrayRef accessGroups
= CFArrayCreateForCFTypes(kCFAllocatorDefault
,
77 CFSTR("lockdown-identities"),
78 CFSTR("123456.test.group"),
79 CFSTR("123456.test.group2"),
80 CFSTR("com.apple.cfnetwork"),
81 CFSTR("com.apple.bluetooth"),
84 CFSTR("com.apple.security.sos"),
85 CFSTR("com.apple.security.ckks"),
86 CFSTR("com.apple.security.octagon"),
87 CFSTR("com.apple.security.egoIdentities"),
88 CFSTR("com.apple.security.sos-usercredential"),
89 CFSTR("com.apple.sbd"),
90 CFSTR("com.apple.lakitu"),
91 CFSTR("com.apple.security.securityd"),
93 if (os_feature_enabled(CryptoTokenKit
, UseTokens
)) {
94 CFMutableArrayRef mutableGroups
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, accessGroups
);
95 CFArrayAppendValue(mutableGroups
, kSecAttrAccessGroupToken
);
96 CFAssignRetained(accessGroups
, mutableGroups
);
102 static SecurityClient gClient
;
106 SecSecuritySetMusrMode(bool mode
, uid_t uid
, int activeUser
)
108 gClient
.inMultiUser
= mode
;
110 gClient
.activeUser
= activeUser
;
114 SecSecuritySetPersonaMusr(CFStringRef uuid
)
116 if (gClient
.inMultiUser
) {
119 CFReleaseNull(gClient
.musr
);
121 CFUUIDRef u
= CFUUIDCreateFromString(NULL
, uuid
);
125 CFUUIDBytes ubytes
= CFUUIDGetUUIDBytes(u
);
127 gClient
.musr
= CFDataCreate(NULL
, (const void *)&ubytes
, sizeof(ubytes
));
133 SecSecurityClientGet(void)
135 static dispatch_once_t onceToken
;
136 dispatch_once(&onceToken
, ^{
138 gClient
.accessGroups
= SecServerCopyAccessGroups();
139 gClient
.allowSystemKeychain
= true;
140 gClient
.allowSyncBubbleKeychain
= true;
141 gClient
.isNetworkExtension
= false;
143 gClient
.inMultiUser
= false;
144 gClient
.activeUser
= 501;
147 gClient
.applicationIdentifier
= NULL
;
148 gClient
.isAppClip
= false;
153 CFArrayRef
SecAccessGroupsGetCurrent(void) {
154 SecurityClient
*client
= SecSecurityClientGet();
155 assert(client
&& client
->accessGroups
);
156 return client
->accessGroups
;
160 void SecAccessGroupsSetCurrent(CFArrayRef accessGroups
) {
161 // Not thread safe at all, but OK because it is meant to be used only by tests.
162 SecurityClient
* client
= SecSecurityClientGet();
163 CFReleaseNull(client
->accessGroups
);
164 client
->accessGroups
= CFRetainSafe(accessGroups
);
168 void SecSecurityClientRegularToAppClip(void) {
169 SecurityClient
* client
= SecSecurityClientGet();
170 client
->isAppClip
= true;
174 void SecSecurityClientAppClipToRegular(void) {
175 SecurityClient
* client
= SecSecurityClientGet();
176 client
->isAppClip
= false;
180 void SecSecurityClientSetApplicationIdentifier(CFStringRef identifier
) {
181 SecurityClient
* client
= SecSecurityClientGet();
182 CFReleaseNull(client
->applicationIdentifier
);
184 client
->applicationIdentifier
= CFRetain(identifier
);
188 #if !TARGET_OS_IPHONE
189 static bool securityd_in_system_context(void) {
190 static bool runningInSystemContext
;
191 static dispatch_once_t onceToken
;
192 dispatch_once(&onceToken
, ^{
193 runningInSystemContext
= (getuid() == 0);
194 if (!runningInSystemContext
) {
196 if (vproc_swap_string(NULL
, VPROC_GSK_MGR_NAME
, NULL
, &manager
) == NULL
) {
197 runningInSystemContext
= (!strcmp(manager
, VPROCMGR_SESSION_SYSTEM
) ||
198 !strcmp(manager
, VPROCMGR_SESSION_LOGINWINDOW
));
203 return runningInSystemContext
;
207 static const char *securityd_service_name(void) {
208 return kSecuritydXPCServiceName
;
211 #define SECURITY_TARGET_UID_UNSET ((uid_t)-1)
212 static uid_t securityd_target_uid
= SECURITY_TARGET_UID_UNSET
;
215 _SecSetSecuritydTargetUID(uid_t uid
)
217 securityd_target_uid
= uid
;
221 static xpc_connection_t
securityd_create_connection(const char *name
, uid_t target_uid
, uint64_t flags
) {
222 const char *serviceName
= name
;
224 serviceName
= securityd_service_name();
226 xpc_connection_t connection
;
227 connection
= xpc_connection_create_mach_service(serviceName
, NULL
, flags
);
228 xpc_connection_set_event_handler(connection
, ^(xpc_object_t event
) {
229 const char *description
= xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
);
230 secnotice("xpc", "got event: %s", description
);
232 if (target_uid
!= SECURITY_TARGET_UID_UNSET
) {
233 xpc_connection_set_target_uid(connection
, target_uid
);
235 xpc_connection_resume(connection
);
239 static bool is_trust_operation(enum SecXPCOperation op
) {
241 case sec_trust_store_contains_id
:
242 case sec_trust_store_set_trust_settings_id
:
243 case sec_trust_store_remove_certificate_id
:
244 case sec_trust_evaluate_id
:
245 case sec_trust_store_copy_all_id
:
246 case sec_trust_store_copy_usage_constraints_id
:
247 case sec_ocsp_cache_flush_id
:
248 case sec_ota_pki_trust_store_version_id
:
249 case sec_ota_pki_asset_version_id
:
250 case kSecXPCOpOTAGetEscrowCertificates
:
251 case kSecXPCOpOTAPKIGetNewAsset
:
252 case kSecXPCOpOTASecExperimentGetNewAsset
:
253 case kSecXPCOpOTASecExperimentGetAsset
:
254 case kSecXPCOpOTAPKICopyTrustedCTLogs
:
255 case kSecXPCOpOTAPKICopyCTLogForKeyID
:
256 case kSecXPCOpNetworkingAnalyticsReport
:
257 case kSecXPCOpSetCTExceptions
:
258 case kSecXPCOpCopyCTExceptions
:
259 case sec_trust_get_exception_reset_count_id
:
260 case sec_trust_increment_exception_reset_count_id
:
261 case kSecXPCOpSetCARevocationAdditions
:
262 case kSecXPCOpCopyCARevocationAdditions
:
263 case kSecXPCOpValidUpdate
:
271 // sSC* manage a pool of xpc_connection_t objects to securityd
272 static dispatch_queue_t sSecuritydConnectionsQueue
;
273 static CFMutableArrayRef sSecuritydConnectionsPool
;
274 static unsigned sSecuritydConnectionsCount
; // connections in circulation
275 #define MAX_SECURITYD_CONNECTIONS 5
277 static xpc_connection_t
_securityd_connection(void) {
278 static dispatch_once_t onceToken
;
279 dispatch_once(&onceToken
, ^{
280 sSecuritydConnectionsCount
= 0;
281 sSecuritydConnectionsQueue
= dispatch_queue_create("com.apple.security.securityd_connections", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL
);
282 sSecuritydConnectionsPool
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, NULL
);
285 __block xpc_connection_t ret
= NULL
;
286 dispatch_sync(sSecuritydConnectionsQueue
, ^{
287 if (CFArrayGetCount(sSecuritydConnectionsPool
) > 0) {
288 ret
= (xpc_connection_t
)CFArrayGetValueAtIndex(sSecuritydConnectionsPool
, 0);
289 CFArrayRemoveValueAtIndex(sSecuritydConnectionsPool
, 0);
290 } else if (sSecuritydConnectionsCount
< MAX_SECURITYD_CONNECTIONS
) {
291 ret
= securityd_create_connection(kSecuritydXPCServiceName
, securityd_target_uid
, 0);
292 ++sSecuritydConnectionsCount
;
293 secnotice("xpc", "Adding securityd connection to pool, total now %d", sSecuritydConnectionsCount
);
294 } // No connection available and no room in the pool for a new one, touch luck!
299 static xpc_connection_t
securityd_connection(void) {
301 xpc_connection_t ret
= NULL
;
303 ret
= _securityd_connection();
308 if (tries
% 100 == 0) { // 1/4 second is a long time to wait, but maybe you're overdoing it also?
309 secwarning("xpc: have been trying %d times to get a securityd connection", tries
);
315 static void return_securityd_connection_to_pool(enum SecXPCOperation op
, xpc_connection_t conn
) {
316 if (!is_trust_operation(op
)) {
317 dispatch_sync(sSecuritydConnectionsQueue
, ^{
318 if (CFArrayGetCount(sSecuritydConnectionsPool
) >= MAX_SECURITYD_CONNECTIONS
) {
319 xpc_connection_cancel(conn
);
320 secerror("xpc: Unable to re-enqueue securityd connection because already at limit");
321 if (sSecuritydConnectionsCount
< MAX_SECURITYD_CONNECTIONS
) {
322 secerror("xpc: connection pool full but tracker does not agree (%d vs %ld)", sSecuritydConnectionsCount
, CFArrayGetCount(sSecuritydConnectionsPool
));
324 abort(); // We've miscalculated?
326 CFArrayAppendValue(sSecuritydConnectionsPool
, conn
);
331 static xpc_connection_t sTrustdConnection
;
332 static xpc_connection_t
trustd_connection(void) {
334 static dispatch_once_t once
;
335 dispatch_once(&once
, ^{
336 bool sysCtx
= securityd_in_system_context();
337 uint64_t flags
= (sysCtx
) ? XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
: 0;
338 const char *serviceName
= (sysCtx
) ? kTrustdXPCServiceName
: kTrustdAgentXPCServiceName
;
339 sTrustdConnection
= securityd_create_connection(serviceName
, SECURITY_TARGET_UID_UNSET
, flags
);
341 return sTrustdConnection
;
343 static dispatch_once_t once
;
344 dispatch_once(&once
, ^{
345 sTrustdConnection
= securityd_create_connection(kTrustdXPCServiceName
, SECURITY_TARGET_UID_UNSET
, 0);
347 return sTrustdConnection
;
351 static xpc_connection_t
securityd_connection_for_operation(enum SecXPCOperation op
) {
352 bool isTrustOp
= is_trust_operation(op
);
353 #if SECTRUST_VERBOSE_DEBUG
355 bool sysCtx
= securityd_in_system_context();
356 const char *serviceName
= (sysCtx
) ? kTrustdXPCServiceName
: kTrustdAgentXPCServiceName
;
357 syslog(LOG_ERR
, "Will connect to: %s (op=%d)",
358 (isTrustOp
) ? serviceName
: kSecuritydXPCServiceName
, (int)op
);
361 return (isTrustOp
) ? trustd_connection() : securityd_connection();
364 // NOTE: This is not thread safe, but this SPI is for testing only.
365 void SecServerSetTrustdMachServiceName(const char *name
) {
366 // Make sure sSecXPCServer.queue exists.
369 xpc_connection_t oldConection
= sTrustdConnection
;
370 sTrustdConnection
= securityd_create_connection(name
, SECURITY_TARGET_UID_UNSET
, 0);
372 xpc_release(oldConection
);
376 #define SECURITYD_MAX_XPC_TRIES 4
377 // Per <rdar://problem/17829836> N61/12A342: Audio Playback... for why this needs to be at least 3, so we made it 4.
380 _securityd_process_message_reply(xpc_object_t
*reply
,
382 xpc_connection_t connection
,
385 if (xpc_get_type(*reply
) != XPC_TYPE_ERROR
) {
389 if (*reply
== XPC_ERROR_CONNECTION_INTERRUPTED
|| *reply
== XPC_ERROR_CONNECTION_INVALID
) {
390 code
= kSecXPCErrorConnectionFailed
;
391 seccritical("Failed to talk to %s after %d attempts.",
392 (is_trust_operation((enum SecXPCOperation
)operation
)) ? "trustd" :
398 SECURITYD_MAX_XPC_TRIES
);
399 } else if (*reply
== XPC_ERROR_TERMINATION_IMMINENT
) {
400 code
= kSecXPCErrorUnknown
;
402 code
= kSecXPCErrorUnknown
;
405 char *conn_desc
= xpc_copy_description(connection
);
406 const char *description
= xpc_dictionary_get_string(*reply
, XPC_ERROR_KEY_DESCRIPTION
);
407 SecCFCreateErrorWithFormat(code
, sSecXPCErrorDomain
, NULL
, error
, NULL
, CFSTR("%s: %s"), conn_desc
, description
);
417 securityd_message_with_reply_sync(xpc_object_t message
, CFErrorRef
*error
)
419 xpc_object_t reply
= NULL
;
420 uint64_t operation
= xpc_dictionary_get_uint64(message
, kSecXPCKeyOperation
);
421 xpc_connection_t connection
= securityd_connection_for_operation((enum SecXPCOperation
)operation
);
423 const int max_tries
= SECURITYD_MAX_XPC_TRIES
;
425 unsigned int tries_left
= max_tries
;
427 if (reply
) xpc_release(reply
);
428 reply
= xpc_connection_send_message_with_reply_sync(connection
, message
);
429 } while (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
&& --tries_left
> 0);
431 _securityd_process_message_reply(&reply
, error
, connection
, operation
);
433 return_securityd_connection_to_pool((enum SecXPCOperation
)operation
, connection
);
439 _securityd_message_with_reply_async_inner(xpc_object_t message
,
440 dispatch_queue_t replyq
,
441 securityd_handler_t handler
,
444 uint64_t operation
= xpc_dictionary_get_uint64(message
, kSecXPCKeyOperation
);
445 xpc_connection_t connection
= securityd_connection_for_operation((enum SecXPCOperation
)operation
);
448 dispatch_retain(replyq
);
449 securityd_handler_t handlerCopy
= Block_copy(handler
);
450 xpc_connection_send_message_with_reply(connection
, message
, replyq
, ^(xpc_object_t _Nonnull reply
) {
451 if (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
&& tries_left
> 0) {
452 _securityd_message_with_reply_async_inner(message
, replyq
, handlerCopy
, tries_left
- 1);
454 CFErrorRef error
= NULL
;
455 _securityd_process_message_reply(&reply
, &error
, connection
, operation
);
456 handlerCopy(reply
, error
);
457 CFReleaseNull(error
);
459 xpc_release(message
);
460 dispatch_release(replyq
);
461 Block_release(handlerCopy
);
463 return_securityd_connection_to_pool((enum SecXPCOperation
)operation
, connection
);
467 securityd_message_with_reply_async(xpc_object_t message
,
468 dispatch_queue_t replyq
,
469 securityd_handler_t handler
)
471 _securityd_message_with_reply_async_inner(message
, replyq
, handler
, SECURITYD_MAX_XPC_TRIES
);
476 securityd_create_message(enum SecXPCOperation op
, CFErrorRef
* error
)
478 xpc_object_t message
= xpc_dictionary_create(NULL
, NULL
, 0);
480 xpc_dictionary_set_uint64(message
, kSecXPCKeyOperation
, op
);
482 SecCFCreateError(kSecXPCErrorConnectionFailed
, sSecXPCErrorDomain
,
483 CFSTR("xpc_dictionary_create returned NULL"), NULL
, error
);
488 // Return true if there is no error in message, return false and set *error if there is.
489 bool securityd_message_no_error(xpc_object_t message
, CFErrorRef
*error
) {
490 xpc_object_t xpc_error
= NULL
;
494 xpc_error
= xpc_dictionary_get_value(message
, kSecXPCKeyError
);
495 if (xpc_error
== NULL
)
498 CFErrorRef localError
= SecCreateCFErrorWithXPCObject(xpc_error
);
501 secdebug("xpc", "Talking to securityd failed with error: %@", localError
);
504 uint64_t operation
= xpc_dictionary_get_uint64(message
, kSecXPCKeyOperation
);
506 secdebug("xpc", "Talking to %s failed with error: %@",
507 (is_trust_operation((enum SecXPCOperation
)operation
)) ? "trustd" : "secd", localError
);
513 CFReleaseSafe(localError
);
518 bool securityd_send_sync_and_do(enum SecXPCOperation op
, CFErrorRef
*error
,
519 bool (^add_to_message
)(xpc_object_t message
, CFErrorRef
* error
),
520 bool (^handle_response
)(xpc_object_t _Nonnull response
, CFErrorRef
* error
)) {
521 xpc_object_t message
= securityd_create_message(op
, error
);
524 if (!add_to_message
|| add_to_message(message
, error
)) {
525 xpc_object_t response
= securityd_message_with_reply_sync(message
, error
);
527 if (securityd_message_no_error(response
, error
)) {
528 ok
= (!handle_response
|| handle_response(response
, error
));
530 xpc_release(response
);
533 xpc_release(message
);
539 void securityd_send_async_and_do(enum SecXPCOperation op
, dispatch_queue_t replyq
,
540 bool (^add_to_message
)(xpc_object_t message
, CFErrorRef
* error
),
541 securityd_handler_t handler
) {
542 CFErrorRef error
= NULL
;
543 xpc_object_t message
= securityd_create_message(op
, &error
);
544 if (message
== NULL
) {
545 handler(NULL
, error
);
546 CFReleaseNull(error
);
550 if (add_to_message
!= NULL
) {
551 if (!add_to_message(message
, &error
)) {
552 handler(NULL
, error
);
553 xpc_release(message
);
554 CFReleaseNull(error
);
559 securityd_message_with_reply_async(message
, replyq
, ^(xpc_object_t reply
, CFErrorRef error2
) {
560 if (error2
!= NULL
) {
561 handler(NULL
, error2
);
564 CFErrorRef error3
= NULL
;
565 if (!securityd_message_no_error(reply
, &error3
)) {
566 handler(NULL
, error3
);
567 CFReleaseNull(error3
);
570 handler(reply
, NULL
);
572 xpc_release(message
);
577 _SecSecuritydCopyWhoAmI(CFErrorRef
*error
)
579 CFDictionaryRef reply
= NULL
;
580 xpc_object_t message
= securityd_create_message(kSecXPCOpWhoAmI
, error
);
582 xpc_object_t response
= securityd_message_with_reply_sync(message
, error
);
584 reply
= _CFXPCCreateCFObjectFromXPCObject(response
);
585 xpc_release(response
);
587 secerror("Securityd failed getting whoamid with error: %@",
588 error
? *error
: NULL
);
590 xpc_release(message
);
596 _SecSyncBubbleTransfer(CFArrayRef services
, uid_t uid
, CFErrorRef
*error
)
598 xpc_object_t message
;
601 message
= securityd_create_message(kSecXPCOpTransmogrifyToSyncBubble
, error
);
603 xpc_dictionary_set_int64(message
, "uid", uid
);
604 if (SecXPCDictionarySetPList(message
, "services", services
, error
)) {
605 xpc_object_t response
= securityd_message_with_reply_sync(message
, error
);
607 reply
= xpc_dictionary_get_bool(response
, kSecXPCKeyResult
);
609 securityd_message_no_error(response
, error
);
610 xpc_release(response
);
612 xpc_release(message
);
619 _SecSystemKeychainTransfer(CFErrorRef
*error
)
621 xpc_object_t message
;
624 message
= securityd_create_message(kSecXPCOpTransmogrifyToSystemKeychain
, error
);
626 xpc_object_t response
= securityd_message_with_reply_sync(message
, error
);
628 reply
= xpc_dictionary_get_bool(response
, kSecXPCKeyResult
);
630 securityd_message_no_error(response
, error
);
631 xpc_release(response
);
633 xpc_release(message
);
639 _SecSyncDeleteUserViews(uid_t uid
, CFErrorRef
*error
)
641 xpc_object_t message
;
644 message
= securityd_create_message(kSecXPCOpDeleteUserView
, error
);
646 xpc_dictionary_set_int64(message
, "uid", uid
);
648 xpc_object_t response
= securityd_message_with_reply_sync(message
, error
);
650 reply
= xpc_dictionary_get_bool(response
, kSecXPCKeyResult
);
652 securityd_message_no_error(response
, error
);
653 xpc_release(response
);
655 xpc_release(message
);
660 XPC_RETURNS_RETAINED xpc_endpoint_t
661 _SecSecuritydCopyEndpoint(enum SecXPCOperation op
, CFErrorRef
*error
)
663 xpc_endpoint_t endpoint
= NULL
;
664 xpc_object_t message
= securityd_create_message(op
, error
);
666 xpc_object_t response
= securityd_message_with_reply_sync(message
, error
);
668 endpoint
= xpc_dictionary_get_value(response
, kSecXPCKeyEndpoint
);
670 if(xpc_get_type(endpoint
) != XPC_TYPE_ENDPOINT
) {
671 secerror("endpoint was not an endpoint");
674 xpc_retain(endpoint
);
677 secerror("endpoint was null");
679 xpc_release(response
);
681 secerror("Securityd failed getting endpoint with error: %@", error
? *error
: NULL
);
683 xpc_release(message
);
689 XPC_RETURNS_RETAINED xpc_endpoint_t
690 _SecSecuritydCopyCKKSEndpoint(CFErrorRef
*error
)
695 XPC_RETURNS_RETAINED xpc_endpoint_t
696 _SecSecuritydCopyKeychainControlEndpoint(CFErrorRef
* error
)
698 return _SecSecuritydCopyEndpoint(kSecXPCOpKeychainControlEndpoint
, error
);
701 XPC_RETURNS_RETAINED xpc_endpoint_t
702 _SecSecuritydCopySFKeychainEndpoint(CFErrorRef
* error
)
704 return _SecSecuritydCopyEndpoint(kSecXPCOpSFKeychainEndpoint
, error
);