/ OSX / sec / ipc / client.c
client.c
  1  /*
  2   * Copyright (c) 2007-2009,2012-2020 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  #include <TargetConditionals.h>
 25  
 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
 30  
 31  #if TARGET_OS_OSX
 32  #ifndef SECITEM_SHIM_OSX
 33  #define SECITEM_SHIM_OSX 1
 34  #endif // SECITEM_SHIM_OSX
 35  #endif // TARGET_OS_OSX
 36  
 37  #include <stdbool.h>
 38  #include <sys/queue.h>
 39  #include <syslog.h>
 40  #include <vproc_priv.h>
 41  #include <xpc/xpc.h>
 42  #include <xpc/private.h>
 43  #include <os/feature_private.h>
 44  
 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>
 52  
 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>
 61  
 62  #include "server_security_helpers.h"
 63  
 64  struct securityd *gSecurityd;
 65  struct trustd *gTrustd;
 66  
 67  //
 68  // MARK: XPC IPC.
 69  //
 70  
 71  /* Hardcoded Access Groups for the server itself */
 72  static CFArrayRef SecServerCopyAccessGroups(void) {
 73      CFArrayRef accessGroups = CFArrayCreateForCFTypes(kCFAllocatorDefault,
 74  #if NO_SERVER
 75                                                        CFSTR("test"),
 76                                                        CFSTR("apple"),
 77                                                        CFSTR("lockdown-identities"),
 78                                                        CFSTR("123456.test.group"),
 79                                                        CFSTR("123456.test.group2"),
 80                                                        CFSTR("com.apple.cfnetwork"),
 81                                                        CFSTR("com.apple.bluetooth"),
 82  #endif
 83                                                        CFSTR("sync"),
 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"),
 92                                                        NULL);
 93      if (os_feature_enabled(CryptoTokenKit, UseTokens)) {
 94          CFMutableArrayRef mutableGroups = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, accessGroups);
 95          CFArrayAppendValue(mutableGroups, kSecAttrAccessGroupToken);
 96          CFAssignRetained(accessGroups, mutableGroups);
 97      }
 98  
 99      return accessGroups;
100  }
101  
102  static SecurityClient gClient;
103  
104  #if TARGET_OS_IOS
105  void
106  SecSecuritySetMusrMode(bool mode, uid_t uid, int activeUser)
107  {
108      gClient.inMultiUser = mode;
109      gClient.uid = uid;
110      gClient.activeUser = activeUser;
111  }
112  
113  void
114  SecSecuritySetPersonaMusr(CFStringRef uuid)
115  {
116      if (gClient.inMultiUser) {
117          abort();
118      }
119      CFReleaseNull(gClient.musr);
120      if (uuid) {
121          CFUUIDRef u = CFUUIDCreateFromString(NULL, uuid);
122          if (u == NULL) {
123              abort();
124          }
125          CFUUIDBytes ubytes = CFUUIDGetUUIDBytes(u);
126          CFReleaseNull(u);
127          gClient.musr = CFDataCreate(NULL, (const void *)&ubytes, sizeof(ubytes));
128      }
129  }
130  #endif
131  
132  SecurityClient *
133  SecSecurityClientGet(void)
134  {
135      static dispatch_once_t onceToken;
136      dispatch_once(&onceToken, ^{
137          gClient.task = NULL;
138          gClient.accessGroups = SecServerCopyAccessGroups();
139          gClient.allowSystemKeychain = true;
140          gClient.allowSyncBubbleKeychain = true;
141          gClient.isNetworkExtension = false;
142  #if TARGET_OS_IPHONE
143          gClient.inMultiUser = false;
144          gClient.activeUser = 501;
145          gClient.musr = NULL;
146  #endif
147          gClient.applicationIdentifier = NULL;
148          gClient.isAppClip = false;
149      });
150      return &gClient;
151  }
152  
153  CFArrayRef SecAccessGroupsGetCurrent(void) {
154      SecurityClient *client = SecSecurityClientGet();
155      assert(client && client->accessGroups);
156      return client->accessGroups;
157  }
158  
159  // Only for testing.
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);
165  }
166  
167  // Testing
168  void SecSecurityClientRegularToAppClip(void) {
169      SecurityClient* client = SecSecurityClientGet();
170      client->isAppClip = true;
171  }
172  
173  // Testing
174  void SecSecurityClientAppClipToRegular(void) {
175      SecurityClient* client = SecSecurityClientGet();
176      client->isAppClip = false;
177  }
178  
179  // Testing
180  void SecSecurityClientSetApplicationIdentifier(CFStringRef identifier) {
181      SecurityClient* client = SecSecurityClientGet();
182      CFReleaseNull(client->applicationIdentifier);
183      if (identifier) {
184          client->applicationIdentifier = CFRetain(identifier);
185      }
186  }
187  
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) {
195              char *manager;
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));
199                  free(manager);
200              }
201          }
202      });
203      return runningInSystemContext;
204  }
205  #endif
206  
207  static const char *securityd_service_name(void) {
208  	return kSecuritydXPCServiceName;
209  }
210  
211  #define SECURITY_TARGET_UID_UNSET   ((uid_t)-1)
212  static uid_t securityd_target_uid = SECURITY_TARGET_UID_UNSET;
213  
214  void
215  _SecSetSecuritydTargetUID(uid_t uid)
216  {
217      securityd_target_uid = uid;
218  }
219  
220  
221  static xpc_connection_t securityd_create_connection(const char *name, uid_t target_uid, uint64_t flags) {
222      const char *serviceName = name;
223      if (!serviceName) {
224          serviceName = securityd_service_name();
225      }
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);
231      });
232      if (target_uid != SECURITY_TARGET_UID_UNSET) {
233          xpc_connection_set_target_uid(connection, target_uid);
234      }
235      xpc_connection_resume(connection);
236      return connection;
237  }
238  
239  static bool is_trust_operation(enum SecXPCOperation op) {
240      switch (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:
264          case kSecXPCOpSetTransparentConnectionPins:
265          case kSecXPCOpCopyTransparentConnectionPins:
266              return true;
267          default:
268              break;
269      }
270      return false;
271  }
272  
273  // sSC* manage a pool of xpc_connection_t objects to securityd
274  static dispatch_queue_t sSecuritydConnectionsQueue;
275  static CFMutableArrayRef sSecuritydConnectionsPool;
276  static unsigned sSecuritydConnectionsCount;     // connections in circulation
277  #define MAX_SECURITYD_CONNECTIONS 5
278  
279  static xpc_connection_t _securityd_connection(void) {
280      static dispatch_once_t onceToken;
281      dispatch_once(&onceToken, ^{
282          sSecuritydConnectionsCount = 0;
283          sSecuritydConnectionsQueue = dispatch_queue_create("com.apple.security.securityd_connections", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
284          sSecuritydConnectionsPool = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
285      });
286  
287      __block xpc_connection_t ret = NULL;
288      dispatch_sync(sSecuritydConnectionsQueue, ^{
289          if (CFArrayGetCount(sSecuritydConnectionsPool) > 0) {
290              ret = (xpc_connection_t)CFArrayGetValueAtIndex(sSecuritydConnectionsPool, 0);
291              CFArrayRemoveValueAtIndex(sSecuritydConnectionsPool, 0);
292          } else if (sSecuritydConnectionsCount < MAX_SECURITYD_CONNECTIONS) {
293              ret = securityd_create_connection(kSecuritydXPCServiceName, securityd_target_uid, 0);
294              ++sSecuritydConnectionsCount;
295              secnotice("xpc", "Adding securityd connection to pool, total now %d", sSecuritydConnectionsCount);
296          }   // No connection available and no room in the pool for a new one, touch luck!
297      });
298      return ret;
299  }
300  
301  static xpc_connection_t securityd_connection(void) {
302      unsigned tries = 0;
303      xpc_connection_t ret = NULL;
304      do {
305          ret = _securityd_connection();
306          if (!ret) {
307              usleep(2500);
308          }
309          ++tries;
310          if (tries % 100 == 0) {  // 1/4 second is a long time to wait, but maybe you're overdoing it also?
311              secwarning("xpc: have been trying %d times to get a securityd connection", tries);
312          }
313      } while (!ret);
314      return ret;
315  }
316  
317  static void return_securityd_connection_to_pool(enum SecXPCOperation op, xpc_connection_t conn) {
318      if (!is_trust_operation(op)) {
319          dispatch_sync(sSecuritydConnectionsQueue, ^{
320              if (CFArrayGetCount(sSecuritydConnectionsPool) >= MAX_SECURITYD_CONNECTIONS) {
321                  xpc_connection_cancel(conn);
322                  secerror("xpc: Unable to re-enqueue securityd connection because already at limit");
323                  if (sSecuritydConnectionsCount < MAX_SECURITYD_CONNECTIONS) {
324                      secerror("xpc: connection pool full but tracker does not agree (%d vs %ld)", sSecuritydConnectionsCount, CFArrayGetCount(sSecuritydConnectionsPool));
325                  }
326                  abort();    // We've miscalculated?
327              }
328              CFArrayAppendValue(sSecuritydConnectionsPool, conn);
329          });
330      }
331  }
332  
333  static xpc_connection_t sTrustdConnection;
334  static xpc_connection_t trustd_connection(void) {
335  #if TARGET_OS_OSX
336  	static dispatch_once_t once;
337  	dispatch_once(&once, ^{
338          bool sysCtx = securityd_in_system_context();
339          uint64_t flags = (sysCtx) ? XPC_CONNECTION_MACH_SERVICE_PRIVILEGED : 0;
340          const char *serviceName = (sysCtx) ? kTrustdXPCServiceName : kTrustdAgentXPCServiceName;
341  		sTrustdConnection = securityd_create_connection(serviceName, SECURITY_TARGET_UID_UNSET, flags);
342  	});
343  	return sTrustdConnection;
344  #else
345      static dispatch_once_t once;
346      dispatch_once(&once, ^{
347          sTrustdConnection = securityd_create_connection(kTrustdXPCServiceName, SECURITY_TARGET_UID_UNSET, 0);
348      });
349      return sTrustdConnection;
350  #endif
351  }
352  
353  static xpc_connection_t securityd_connection_for_operation(enum SecXPCOperation op) {
354  	bool isTrustOp = is_trust_operation(op);
355  	#if SECTRUST_VERBOSE_DEBUG
356      {
357          bool sysCtx = securityd_in_system_context();
358          const char *serviceName = (sysCtx) ? kTrustdXPCServiceName : kTrustdAgentXPCServiceName;
359          syslog(LOG_ERR, "Will connect to: %s (op=%d)",
360                 (isTrustOp) ? serviceName : kSecuritydXPCServiceName, (int)op);
361      }
362  	#endif
363  	return (isTrustOp) ? trustd_connection() : securityd_connection();
364  }
365  
366  // NOTE: This is not thread safe, but this SPI is for testing only.
367  void SecServerSetTrustdMachServiceName(const char *name) {
368      // Make sure sSecXPCServer.queue exists.
369      trustd_connection();
370  
371      xpc_connection_t oldConection = sTrustdConnection;
372      sTrustdConnection = securityd_create_connection(name, SECURITY_TARGET_UID_UNSET, 0);
373      if (oldConection)
374          xpc_release(oldConection);
375  }
376  
377  
378  #define SECURITYD_MAX_XPC_TRIES 4
379  // Per <rdar://problem/17829836> N61/12A342: Audio Playback... for why this needs to be at least 3, so we made it 4.
380  
381  static bool
382  _securityd_process_message_reply(xpc_object_t *reply,
383  								 CFErrorRef *error,
384  								 xpc_connection_t connection,
385  								 uint64_t operation)
386  {
387  	if (xpc_get_type(*reply) != XPC_TYPE_ERROR) {
388  		return true;
389  	}
390  	CFIndex code =  0;
391  	if (*reply == XPC_ERROR_CONNECTION_INTERRUPTED || *reply == XPC_ERROR_CONNECTION_INVALID) {
392  		code = kSecXPCErrorConnectionFailed;
393  		seccritical("Failed to talk to %s after %d attempts.",
394  					(is_trust_operation((enum SecXPCOperation)operation)) ? "trustd" :
395  #if TARGET_OS_IPHONE
396  					"securityd",
397  #else
398  					"secd",
399  #endif
400  					SECURITYD_MAX_XPC_TRIES);
401  	} else if (*reply == XPC_ERROR_TERMINATION_IMMINENT) {
402  		code = kSecXPCErrorUnknown;
403  	} else {
404  		code = kSecXPCErrorUnknown;
405  	}
406  
407  	char *conn_desc = xpc_copy_description(connection);
408  	const char *description = xpc_dictionary_get_string(*reply, XPC_ERROR_KEY_DESCRIPTION);
409  	SecCFCreateErrorWithFormat(code, sSecXPCErrorDomain, NULL, error, NULL, CFSTR("%s: %s"), conn_desc, description);
410  	free(conn_desc);
411  	xpc_release(*reply);
412  	*reply = NULL;
413  	return false;
414  }
415  
416  
417  XPC_RETURNS_RETAINED
418  xpc_object_t
419  securityd_message_with_reply_sync(xpc_object_t message, CFErrorRef *error)
420  {
421      xpc_object_t reply = NULL;
422      uint64_t operation = xpc_dictionary_get_uint64(message, kSecXPCKeyOperation);
423      xpc_connection_t connection = securityd_connection_for_operation((enum SecXPCOperation)operation);
424  
425      const int max_tries = SECURITYD_MAX_XPC_TRIES;
426  
427      unsigned int tries_left = max_tries;
428      do {
429          if (reply) xpc_release(reply);
430          reply = xpc_connection_send_message_with_reply_sync(connection, message);
431      } while (reply == XPC_ERROR_CONNECTION_INTERRUPTED && --tries_left > 0);
432  
433  	_securityd_process_message_reply(&reply, error, connection, operation);
434  
435      return_securityd_connection_to_pool((enum SecXPCOperation)operation, connection);
436  
437      return reply;
438  }
439  
440  static void
441  _securityd_message_with_reply_async_inner(xpc_object_t message,
442  										  dispatch_queue_t replyq,
443  										  securityd_handler_t handler,
444  										  uint32_t tries_left)
445  {
446  	uint64_t operation = xpc_dictionary_get_uint64(message, kSecXPCKeyOperation);
447  	xpc_connection_t connection = securityd_connection_for_operation((enum SecXPCOperation)operation);
448  
449  	xpc_retain(message);
450  	dispatch_retain(replyq);
451  	securityd_handler_t handlerCopy = Block_copy(handler);
452  	xpc_connection_send_message_with_reply(connection, message, replyq, ^(xpc_object_t _Nonnull reply) {
453  		if (reply == XPC_ERROR_CONNECTION_INTERRUPTED && tries_left > 0) {
454  			_securityd_message_with_reply_async_inner(message, replyq, handlerCopy, tries_left - 1);
455  		} else {
456  			CFErrorRef error = NULL;
457  			_securityd_process_message_reply(&reply, &error, connection, operation);
458  			handlerCopy(reply, error);
459  			CFReleaseNull(error);
460  		}
461  		xpc_release(message);
462  		dispatch_release(replyq);
463  		Block_release(handlerCopy);
464  	});
465      return_securityd_connection_to_pool((enum SecXPCOperation)operation, connection);
466  }
467  
468  void
469  securityd_message_with_reply_async(xpc_object_t message,
470  								   dispatch_queue_t replyq,
471  								   securityd_handler_t handler)
472  {
473  	_securityd_message_with_reply_async_inner(message, replyq, handler, SECURITYD_MAX_XPC_TRIES);
474  }
475  
476  XPC_RETURNS_RETAINED
477  xpc_object_t
478  securityd_create_message(enum SecXPCOperation op, CFErrorRef* error)
479  {
480      xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
481      if (message) {
482          xpc_dictionary_set_uint64(message, kSecXPCKeyOperation, op);
483      } else {
484          SecCFCreateError(kSecXPCErrorConnectionFailed, sSecXPCErrorDomain,
485                           CFSTR("xpc_dictionary_create returned NULL"), NULL, error);
486      }
487      return message;
488  }
489  
490  // Return true if there is no error in message, return false and set *error if there is.
491  bool securityd_message_no_error(xpc_object_t message, CFErrorRef *error) {
492      xpc_object_t xpc_error = NULL;
493      if (message == NULL)
494          return false;
495  
496      xpc_error = xpc_dictionary_get_value(message, kSecXPCKeyError);
497      if (xpc_error == NULL)
498          return true;
499  
500      CFErrorRef localError = SecCreateCFErrorWithXPCObject(xpc_error);
501  
502  #if TARGET_OS_IPHONE
503      secdebug("xpc", "Talking to securityd failed with error: %@", localError);
504  #else
505  #if !defined(NDEBUG)
506      uint64_t operation = xpc_dictionary_get_uint64(message, kSecXPCKeyOperation);
507  #endif
508      secdebug("xpc", "Talking to %s failed with error: %@",
509               (is_trust_operation((enum SecXPCOperation)operation)) ? "trustd" : "secd", localError);
510  #endif
511  
512      if (error) {
513          *error = localError;
514      } else {
515          CFReleaseSafe(localError);
516      }
517      return false;
518  }
519  
520  bool securityd_send_sync_and_do(enum SecXPCOperation op, CFErrorRef *error,
521                                  bool (^add_to_message)(xpc_object_t message, CFErrorRef* error),
522                                  bool (^handle_response)(xpc_object_t _Nonnull response, CFErrorRef* error)) {
523      xpc_object_t message = securityd_create_message(op, error);
524      bool ok = false;
525      if (message) {
526          if (!add_to_message || add_to_message(message, error)) {
527              xpc_object_t response = securityd_message_with_reply_sync(message, error);
528              if (response) {
529                  if (securityd_message_no_error(response, error)) {
530                      ok = (!handle_response || handle_response(response, error));
531                  }
532                  xpc_release(response);
533              }
534          }
535          xpc_release(message);
536      }
537  
538      return ok;
539  }
540  
541  void securityd_send_async_and_do(enum SecXPCOperation op, dispatch_queue_t replyq,
542  								 bool (^add_to_message)(xpc_object_t message, CFErrorRef* error),
543  								 securityd_handler_t handler) {
544  	CFErrorRef error = NULL;
545  	xpc_object_t message = securityd_create_message(op, &error);
546  	if (message == NULL) {
547  		handler(NULL, error);
548  		CFReleaseNull(error);
549  		return;
550  	}
551  
552  	if (add_to_message != NULL) {
553  		if (!add_to_message(message, &error)) {
554  			handler(NULL, error);
555  			xpc_release(message);
556  			CFReleaseNull(error);
557  			return;
558  		}
559  	}
560  
561  	securityd_message_with_reply_async(message, replyq, ^(xpc_object_t reply, CFErrorRef error2) {
562  		if (error2 != NULL) {
563  			handler(NULL, error2);
564  			return;
565  		}
566  		CFErrorRef error3 = NULL;
567  		if (!securityd_message_no_error(reply, &error3)) {
568  			handler(NULL, error3);
569  			CFReleaseNull(error3);
570  			return;
571  		}
572  		handler(reply, NULL);
573  	});
574  	xpc_release(message);
575  }
576  
577  
578  CFDictionaryRef
579  _SecSecuritydCopyWhoAmI(CFErrorRef *error)
580  {
581      CFDictionaryRef reply = NULL;
582      xpc_object_t message = securityd_create_message(kSecXPCOpWhoAmI, error);
583      if (message) {
584          xpc_object_t response = securityd_message_with_reply_sync(message, error);
585          if (response) {
586              reply = _CFXPCCreateCFObjectFromXPCObject(response);
587              xpc_release(response);
588          } else {
589              secerror("Securityd failed getting whoamid with error: %@",
590                       error ? *error : NULL);
591          }
592          xpc_release(message);
593      }
594      return reply;
595  }
596  
597  bool
598  _SecSyncBubbleTransfer(CFArrayRef services, uid_t uid, CFErrorRef *error)
599  {
600      xpc_object_t message;
601      bool reply = false;
602  
603      message = securityd_create_message(kSecXPCOpTransmogrifyToSyncBubble, error);
604      if (message) {
605          xpc_dictionary_set_int64(message, "uid", uid);
606          if (SecXPCDictionarySetPList(message, "services", services, error)) {
607              xpc_object_t response = securityd_message_with_reply_sync(message, error);
608              if (response) {
609                  reply = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
610                  if (!reply)
611                      securityd_message_no_error(response, error);
612                  xpc_release(response);
613              }
614              xpc_release(message);
615          }
616      }
617      return reply;
618  }
619  
620  bool
621  _SecSystemKeychainTransfer(CFErrorRef *error)
622  {
623      xpc_object_t message;
624      bool reply = false;
625  
626      message = securityd_create_message(kSecXPCOpTransmogrifyToSystemKeychain, error);
627      if (message) {
628          xpc_object_t response = securityd_message_with_reply_sync(message, error);
629          if (response) {
630              reply = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
631              if (!reply)
632                  securityd_message_no_error(response, error);
633              xpc_release(response);
634          }
635          xpc_release(message);
636      }
637      return reply;
638  }
639  
640  bool
641  _SecSyncDeleteUserViews(uid_t uid, CFErrorRef *error)
642  {
643      xpc_object_t message;
644      bool reply = false;
645  
646      message = securityd_create_message(kSecXPCOpDeleteUserView, error);
647      if (message) {
648          xpc_dictionary_set_int64(message, "uid", uid);
649  
650          xpc_object_t response = securityd_message_with_reply_sync(message, error);
651          if (response) {
652              reply = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
653              if (!reply)
654                  securityd_message_no_error(response, error);
655              xpc_release(response);
656          }
657          xpc_release(message);
658      }
659      return reply;
660  }
661  
662  XPC_RETURNS_RETAINED xpc_endpoint_t
663  _SecSecuritydCopyEndpoint(enum SecXPCOperation op, CFErrorRef *error)
664  {
665      xpc_endpoint_t endpoint = NULL;
666      xpc_object_t message = securityd_create_message(op, error);
667      if (message) {
668          xpc_object_t response = securityd_message_with_reply_sync(message, error);
669          if (response) {
670              endpoint = xpc_dictionary_get_value(response, kSecXPCKeyEndpoint);
671              if (endpoint) {
672                  if(xpc_get_type(endpoint) != XPC_TYPE_ENDPOINT) {
673                      secerror("endpoint was not an endpoint");
674                      endpoint = NULL;
675                  } else {
676                      xpc_retain(endpoint);
677                  }
678              } else {
679                  secerror("endpoint was null");
680              }
681              xpc_release(response);
682          } else {
683              secerror("Securityd failed getting endpoint with error: %@", error ? *error : NULL);
684          }
685          xpc_release(message);
686      }
687      return endpoint;
688  }
689  
690  
691  XPC_RETURNS_RETAINED xpc_endpoint_t
692  _SecSecuritydCopyCKKSEndpoint(CFErrorRef *error)
693  {
694      return NULL;
695  }
696  
697  XPC_RETURNS_RETAINED xpc_endpoint_t
698  _SecSecuritydCopyKeychainControlEndpoint(CFErrorRef* error)
699  {
700      return _SecSecuritydCopyEndpoint(kSecXPCOpKeychainControlEndpoint, error);
701  }
702  
703  XPC_RETURNS_RETAINED xpc_endpoint_t
704  _SecSecuritydCopySFKeychainEndpoint(CFErrorRef* error)
705  {
706      return _SecSecuritydCopyEndpoint(kSecXPCOpSFKeychainEndpoint, error);
707  }