]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecuritydXPC.c
Security-57740.60.18.tar.gz
[apple/security.git] / OSX / sec / Security / SecuritydXPC.c
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 #include <Security/SecuritydXPC.h>
26 #include <Security/SecCFAllocator.h>
27 #include <ipc/securityd_client.h>
28 #include <utilities/SecCFError.h>
29 #include <utilities/SecDb.h>
30 #include <utilities/SecCFWrappers.h>
31 #include <utilities/der_plist.h>
32
33 // TODO Shorten these string values to save ipc bandwidth.
34 const char *kSecXPCKeyOperation = "operation";
35 const char *kSecXPCKeyResult = "status";
36 const char *kSecXPCKeyError = "error";
37 const char *kSecXPCKeyClientToken = "client";
38 const char *kSecXPCKeyPeerInfoArray = "peer-infos";
39 const char *kSecXPCKeyPeerInfo = "peer-info";
40 const char *kSecXPCKeyUserLabel = "userlabel";
41 const char *kSecXPCKeyBackup = "backup";
42 const char *kSecXPCKeyKeybag = "keybag";
43 const char *kSecXPCKeyUserPassword = "password";
44 const char *kSecXPCKeyDSID = "dsid";
45 const char *kSecXPCKeyQuery = "query";
46 const char *kSecXPCKeyAttributesToUpdate = "attributesToUpdate";
47 const char *kSecXPCKeyDomain = "domain";
48 const char *kSecXPCKeyDigest = "digest";
49 const char *kSecXPCKeyCertificate = "cert";
50 const char *kSecXPCKeySettings = "settings";
51 const char *kSecXPCKeyOTAFileDirectory = "path";
52 const char *kSecXPCLimitInMinutes = "limitMinutes";
53 const char *kSecXPCPublicPeerId = "publicPeerId"; // Public peer id
54 const char *kSecXPCOTRSession = "otrsess"; // OTR session bytes
55 const char *kSecXPCData = "data"; // Data to process
56 const char *kSecXPCOTRReady = "otrrdy"; // OTR ready for messages
57 const char *kSecXPCKeyDeviceID = "deviceID";
58 const char *kSecXPCKeySendIDSMessage = "sendIDSMessageCommand";
59 const char *kSecXPCKeyIDSMessage = "idsMessage";
60 const char *kSecXPCKeyViewName = "viewname";
61 const char *kSecXPCKeyViewActionCode = "viewactioncode";
62 const char *kSecXPCKeyHSA2AutoAcceptInfo = "autoacceptinfo";
63 const char *kSecXPCKeyString = "cfstring";
64 const char *kSecXPCKeyArray = "cfarray";
65 const char *kSecXPCKeySet = "cfset";
66 const char *kSecXPCKeySet2 = "cfset2";
67 const char *kSecXPCKeyNewPublicBackupKey = "newPublicBackupKey";
68 const char *kSecXPCKeyRecoveryPublicKey = "RecoveryPublicKey";
69 const char *kSecXPCKeyIncludeV0 = "includeV0";
70 const char *kSecXPCKeyReason = "reason";
71 const char *kSecXPCKeyEnabledViewsKey = "enabledViews";
72 const char *kSecXPCKeyDisabledViewsKey = "disabledViews";
73 const char *kSecXPCKeyEscrowLabel = "escrow";
74 const char *kSecXPCKeyTriesLabel = "tries";
75 const char *kSecXPCKeyFileDescriptor = "fileDescriptor";
76 const char *kSecXPCKeyAccessGroups = "accessGroups";
77 const char *kSecXPCKeyClasses = "classes";
78
79
80 //
81 // XPC Functions for both client and server.
82 //
83
84
85 CFStringRef SOSCCGetOperationDescription(enum SecXPCOperation op)
86 {
87 switch (op) {
88 case kSecXPCOpAccountSetToNew:
89 return CFSTR("AccountSetToNew");
90 case kSecXPCOpOTAGetEscrowCertificates:
91 return CFSTR("OTAGetEscrowCertificates");
92 case kSecXPCOpOTAPKIGetNewAsset:
93 return CFSTR("OTAPKIGetNewAsset");
94 case kSecXPCOpSetHSA2AutoAcceptInfo:
95 return CFSTR("SetHSA2AutoAcceptInfo");
96 case kSecXPCOpAcceptApplicants:
97 return CFSTR("AcceptApplicants");
98 case kSecXPCOpApplyToARing:
99 return CFSTR("ApplyToARing");
100 case kSecXPCOpBailFromCircle:
101 return CFSTR("BailFromCircle");
102 case kSecXPCOpCanAuthenticate:
103 return CFSTR("CanAuthenticate");
104 case kSecXPCOpCopyApplicantPeerInfo:
105 return CFSTR("CopyApplicantPeerInfo");
106 case kSecXPCOpCopyConcurringPeerPeerInfo:
107 return CFSTR("CopyConcurringPeerPeerInfo");
108 case kSecXPCOpCopyEngineState:
109 return CFSTR("CopyEngineState");
110 case kSecXPCOpCopyGenerationPeerInfo:
111 return CFSTR("CopyGenerationPeerInfo");
112 case kSecXPCOpCopyIncompatibilityInfo:
113 return CFSTR("CopyIncompatibilityInfo");
114 case kSecXPCOpCopyMyPeerInfo:
115 return CFSTR("CopyMyPeerInfo");
116 case kSecXPCOpCopyNotValidPeerPeerInfo:
117 return CFSTR("CopyNotValidPeerPeerInfo");
118 case kSecXPCOpCopyPeerPeerInfo:
119 return CFSTR("CopyPeerPeerInfo");
120 case kSecXPCOpCopyRetirementPeerInfo:
121 return CFSTR("CopyRetirementPeerInfo");
122 case kSecXPCOpCopyValidPeerPeerInfo:
123 return CFSTR("CopyValidPeerPeerInfo");
124 case kSecXPCOpCopyViewUnawarePeerInfo:
125 return CFSTR("CopyViewUnawarePeerInfo");
126 case kSecXPCOpDeviceInCircle:
127 return CFSTR("DeviceInCircle");
128 case kSecXPCOpEnableRing:
129 return CFSTR("EnableRing");
130 case kSecXPCOpGetAllTheRings:
131 return CFSTR("GetAllTheRings");
132 case kSecXPCOpGetLastDepartureReason:
133 return CFSTR("GetLastDepartureReason");
134 case kSecXPCOpHandleIDSMessage:
135 return CFSTR("HandleIDSMessage");
136 case kSecXPCOpSyncWithKVSPeer:
137 return CFSTR("SyncKVSPeer");
138 case kSecXPCOpSyncWithIDSPeer:
139 return CFSTR("SyncIDSPeer");
140 case kSecXPCOpIDSDeviceID:
141 return CFSTR("IDSDeviceID");
142 case kSecXPCOpClearKVSPeerMessage:
143 return CFSTR("kSecXPCOpClearKVSPeerMessage");
144 case kSecXPCOpLoggedOutOfAccount:
145 return CFSTR("LoggedOutOfAccount");
146 case kSecXPCOpPingTest:
147 return CFSTR("PingTest");
148 case kSecXPCOpProcessSyncWithAllPeers:
149 return CFSTR("ProcessSyncWithAllPeers");
150 case kSecXPCOpProcessSyncWithPeers:
151 return CFSTR("ProcessSyncWithPeers");
152 case kSecXPCOpProcessUnlockNotification:
153 return CFSTR("ProcessUnlockNotification");
154 case kSecXPCOpPurgeUserCredentials:
155 return CFSTR("PurgeUserCredentials");
156 case kSecXPCOpRejectApplicants:
157 return CFSTR("RejectApplicants");
158 case kSecXPCOpRemoveThisDeviceFromCircle:
159 return CFSTR("RemoveThisDeviceFromCircle");
160 case kSecXPCOpRemovePeersFromCircle:
161 return CFSTR("RemovePeersFromCircle");
162 case kSecXPCOpRequestDeviceID:
163 return CFSTR("RequestDeviceID");
164 case kSecXPCOpRequestEnsureFreshParameters:
165 return CFSTR("RequestEnsureFreshParameters");
166 case kSecXPCOpRequestToJoin:
167 return CFSTR("RequestToJoin");
168 case kSecXPCOpRequestToJoinAfterRestore:
169 return CFSTR("RequestToJoinAfterRestore");
170 case kSecXPCOpResetToEmpty:
171 return CFSTR("ResetToEmpty");
172 case kSecXPCOpResetToOffering:
173 return CFSTR("ResetToOffering");
174 case kSecXPCOpRingStatus:
175 return CFSTR("RingStatus");
176 case kSecXPCOpRollKeys:
177 return CFSTR("RollKeys");
178 case kSecXPCOpSecurityProperty:
179 return CFSTR("SecurityProperty");
180 case kSecXPCOpSendIDSMessage:
181 return CFSTR("SendIDSMessage");
182 case kSecXPCOpSetBagForAllSlices:
183 return CFSTR("SetBagForAllSlices");
184 case kSecXPCOpSetDeviceID:
185 return CFSTR("SetDeviceID");
186 case kSecXPCOpSetLastDepartureReason:
187 return CFSTR("SetLastDepartureReason");
188 case kSecXPCOpSetNewPublicBackupKey:
189 return CFSTR("SetNewPublicBackupKey");
190 case kSecXPCOpSetUserCredentials:
191 return CFSTR("SetUserCredentials");
192 case kSecXPCOpSetUserCredentialsAndDSID:
193 return CFSTR("SetUserCredentialsAndDSID");
194 case kSecXPCOpTryUserCredentials:
195 return CFSTR("TryUserCredentials");
196 case kSecXPCOpValidateUserPublic:
197 return CFSTR("ValidateUserPublic");
198 case kSecXPCOpView:
199 return CFSTR("View");
200 case kSecXPCOpWithdrawlFromARing:
201 return CFSTR("WithdrawlFromARing");
202 case sec_add_shared_web_credential_id:
203 return CFSTR("add_shared_web_credential");
204 case sec_copy_shared_web_credential_id:
205 return CFSTR("copy_shared_web_credential");
206 case sec_delete_all_id:
207 return CFSTR("delete_all");
208 case sec_get_log_settings_id:
209 return CFSTR("get_log_settings");
210 case sec_item_add_id:
211 return CFSTR("add");
212 case sec_item_backup_copy_names_id:
213 return CFSTR("backup_copy_names");
214 case sec_item_backup_handoff_fd_id:
215 return CFSTR("backup_handoff_fd");
216 case sec_item_backup_restore_id:
217 return CFSTR("backup_restore");
218 case sec_item_backup_set_confirmed_manifest_id:
219 return CFSTR("backup_set_confirmed_manifest");
220 case sec_item_copy_matching_id:
221 return CFSTR("copy_matching");
222 case sec_item_delete_id:
223 return CFSTR("delete");
224 case sec_item_update_id:
225 return CFSTR("update");
226 case sec_keychain_backup_id:
227 return CFSTR("keychain_backup");
228 case sec_keychain_backup_syncable_id:
229 return CFSTR("keychain_backup_syncable");
230 case sec_keychain_restore_id:
231 return CFSTR("keychain_restore");
232 case sec_keychain_restore_syncable_id:
233 return CFSTR("keychain_restore_syncable");
234 case sec_keychain_sync_update_message_id:
235 return CFSTR("keychain_sync_update_message");
236 case sec_ota_pki_asset_version_id:
237 return CFSTR("ota_pki_asset_version");
238 case sec_otr_session_create_remote_id:
239 return CFSTR("otr_session_create_remote");
240 case sec_otr_session_process_packet_remote_id:
241 return CFSTR("otr_session_process_packet_remote");
242 case sec_set_circle_log_settings_id:
243 return CFSTR("set_circle_log_settings");
244 case sec_set_xpc_log_settings_id:
245 return CFSTR("set_xpc_log_settings");
246 case sec_trust_evaluate_id:
247 return CFSTR("trust_evaluate");
248 case sec_trust_store_contains_id:
249 return CFSTR("trust_store_contains");
250 case sec_trust_store_remove_certificate_id:
251 return CFSTR("trust_store_remove_certificate");
252 case sec_trust_store_set_trust_settings_id:
253 return CFSTR("trust_store_set_trust_settings");
254 case sec_trust_store_copy_all_id:
255 return CFSTR("trust_store_copy_all");
256 case sec_trust_store_copy_usage_constraints_id:
257 return CFSTR("trust_store_copy_usage_constraints");
258 case soscc_EnsurePeerRegistration_id:
259 return CFSTR("EnsurePeerRegistration");
260 case kSecXPCOpSetEscrowRecord:
261 return CFSTR("SetEscrowRecord");
262 case kSecXPCOpGetEscrowRecord:
263 return CFSTR("GetEscrowRecord");
264 case kSecXPCOpWhoAmI:
265 return CFSTR("WhoAmI");
266 case kSecXPCOpTransmogrifyToSyncBubble:
267 return CFSTR("TransmogrifyToSyncBubble");
268 case kSecXPCOpWrapToBackupSliceKeyBagForView:
269 return CFSTR("WrapToBackupSliceKeyBagForView");
270 case kSecXPCOpCopyAccountData:
271 return CFSTR("CopyAccountDataFromKeychain");
272 case kSecXPCOpDeleteAccountData:
273 return CFSTR("DeleteAccountDataFromKeychain");
274 case kSecXPCOpCopyEngineData:
275 return CFSTR("CopyEngineDataFromKeychain");
276 case kSecXPCOpDeleteEngineData:
277 return CFSTR("DeleteEngineDataFromKeychain");
278 case sec_item_update_token_items_id:
279 return CFSTR("UpdateTokenItems");
280 case sec_delete_items_with_access_groups_id:
281 return CFSTR("sec_delete_items_with_access_groups_id");
282 case kSecXPCOpPeersHaveViewsEnabled:
283 return CFSTR("kSecXPCOpPeersHaveViewsEnabled");
284 case kSecXPCOpRegisterRecoveryPublicKey:
285 return CFSTR("RegisterRecoveryPublicKey");
286 case kSecXPCOpGetRecoveryPublicKey:
287 return CFSTR("GetRecoveryPublicKey");
288 case kSecXPCOpCopyBackupInformation:
289 return CFSTR("CopyBackupInformation");
290 case sec_device_is_internal_id:
291 return CFSTR("DeviceIsInternal");
292 case kSecXPCOpMessageFromPeerIsPending:
293 return CFSTR("MessageFromPeerIsPending");
294 case kSecXPCOpSendToPeerIsPending:
295 return CFSTR("SendToPeerIsPending");
296 default:
297 return CFSTR("Unknown xpc operation");
298 }
299 }
300
301 bool SecXPCDictionarySetPList(xpc_object_t message, const char *key, CFTypeRef object, CFErrorRef *error)
302 {
303 if (!object)
304 return SecError(errSecParam, error, CFSTR("object for key %s is NULL"), key);
305
306 size_t size = der_sizeof_plist(object, error);
307 if (!size)
308 return false;
309 uint8_t *der = malloc(size);
310 uint8_t *der_end = der + size;
311 uint8_t *der_start = der_encode_plist(object, error, der, der_end);
312 if (!der_start) {
313 free(der);
314 return false;
315 }
316
317 assert(der == der_start);
318 xpc_dictionary_set_data(message, key, der_start, der_end - der_start);
319 free(der);
320 return true;
321 }
322
323 bool SecXPCDictionarySetPListOptional(xpc_object_t message, const char *key, CFTypeRef object, CFErrorRef *error) {
324 return !object || SecXPCDictionarySetPList(message, key, object, error);
325 }
326
327 bool SecXPCDictionarySetData(xpc_object_t message, const char *key, CFDataRef data, CFErrorRef *error)
328 {
329 if (!data)
330 return SecError(errSecParam, error, CFSTR("data for key %s is NULL"), key);
331
332 xpc_dictionary_set_data(message, key, CFDataGetBytePtr(data), CFDataGetLength(data));
333 return true;
334 }
335
336 bool SecXPCDictionarySetBool(xpc_object_t message, const char *key, bool value, CFErrorRef *error)
337 {
338 xpc_dictionary_set_bool(message, key, value);
339 return true;
340 }
341
342 bool SecXPCDictionarySetString(xpc_object_t message, const char *key, CFStringRef string, CFErrorRef *error)
343 {
344 if (!string)
345 return SecError(errSecParam, error, CFSTR("string for key %s is NULL"), key);
346
347 __block bool ok = true;
348 CFStringPerformWithCString(string, ^(const char *utf8Str) {
349 if (utf8Str)
350 xpc_dictionary_set_string(message, key, utf8Str);
351 else
352 ok = SecError(errSecParam, error, CFSTR("failed to convert string for key %s to utf8"), key);
353 });
354 return ok;
355 }
356
357 bool SecXPCDictionarySetStringOptional(xpc_object_t message, const char *key, CFStringRef string, CFErrorRef *error) {
358 return !string || SecXPCDictionarySetString(message, key, string, error);
359 }
360
361 bool SecXPCDictionarySetDataOptional(xpc_object_t message, const char *key, CFDataRef data, CFErrorRef *error) {
362 return !data || SecXPCDictionarySetData(message, key, data, error);
363 }
364
365 bool SecXPCDictionarySetInt64(xpc_object_t message, const char *key, int64_t value, CFErrorRef *error) {
366 xpc_dictionary_set_int64(message, key, value);
367 return true;
368 }
369
370 bool SecXPCDictionarySetFileDescriptor(xpc_object_t message, const char *key, int fd, CFErrorRef *error) {
371 xpc_dictionary_set_fd(message, key, fd);
372 return true;
373 }
374
375 int SecXPCDictionaryDupFileDescriptor(xpc_object_t message, const char *key, CFErrorRef *error) {
376 int fd = xpc_dictionary_dup_fd(message, key);
377 if (fd < 0)
378 SecError(errSecParam, error, CFSTR("missing fd for key %s"), key);
379
380 return fd;
381 }
382
383 CFSetRef SecXPCDictionaryCopySet(xpc_object_t message, const char *key, CFErrorRef *error) {
384 CFTypeRef obj = SecXPCDictionaryCopyPList(message, key, error);
385 CFSetRef set = copyIfSet(obj, error);
386 if (obj && !set) {
387 CFStringRef description = CFCopyTypeIDDescription(CFGetTypeID(obj));
388 SecError(errSecParam, error, CFSTR("object for key %s not set but %@"), key, description);
389 CFReleaseNull(description);
390 }
391 CFReleaseNull(obj);
392 return set;
393 }
394
395 CFArrayRef SecXPCDictionaryCopyArray(xpc_object_t message, const char *key, CFErrorRef *error) {
396 CFTypeRef array = SecXPCDictionaryCopyPList(message, key, error);
397 if (array) {
398 CFTypeID type_id = CFGetTypeID(array);
399 if (type_id != CFArrayGetTypeID()) {
400 CFStringRef description = CFCopyTypeIDDescription(type_id);
401 SecError(errSecParam, error, CFSTR("object for key %s not array but %@"), key, description);
402 CFReleaseNull(description);
403 CFReleaseNull(array);
404 }
405 }
406 return (CFArrayRef)array;
407 }
408
409 bool SecXPCDictionaryCopyArrayOptional(xpc_object_t message, const char *key, CFArrayRef *parray, CFErrorRef *error) {
410 if (!xpc_dictionary_get_value(message, key)) {
411 *parray = NULL;
412 return true;
413 }
414 *parray = SecXPCDictionaryCopyArray(message, key, error);
415 return *parray;
416 }
417
418 CFDataRef SecXPCDictionaryCopyData(xpc_object_t message, const char *key, CFErrorRef *error) {
419 CFDataRef data = NULL;
420 size_t size = 0;
421 const uint8_t *bytes = xpc_dictionary_get_data(message, key, &size);
422 if (!bytes) {
423 SecError(errSecParam, error, CFSTR("no data for key %s"), key);
424 return NULL;
425 }
426
427 data = CFDataCreate(kCFAllocatorDefault, bytes, size);
428 if (!data)
429 SecError(errSecParam, error, CFSTR("failed to create data for key %s"), key);
430
431 return data;
432 }
433
434 bool SecXPCDictionaryGetBool(xpc_object_t message, const char *key, CFErrorRef *__unused error) {
435 return xpc_dictionary_get_bool(message, key);
436 }
437
438 bool SecXPCDictionaryCopyDataOptional(xpc_object_t message, const char *key, CFDataRef *pdata, CFErrorRef *error) {
439 size_t size = 0;
440 if (!xpc_dictionary_get_data(message, key, &size)) {
441 *pdata = NULL;
442 return true;
443 }
444 *pdata = SecXPCDictionaryCopyData(message, key, error);
445 return *pdata;
446 }
447
448 CFDictionaryRef SecXPCDictionaryCopyDictionary(xpc_object_t message, const char *key, CFErrorRef *error) {
449 CFTypeRef dict = SecXPCDictionaryCopyPList(message, key, error);
450 if (dict) {
451 CFTypeID type_id = CFGetTypeID(dict);
452 if (type_id != CFDictionaryGetTypeID()) {
453 CFStringRef description = CFCopyTypeIDDescription(type_id);
454 SecError(errSecParam, error, CFSTR("object for key %s not dictionary but %@"), key, description);
455 CFReleaseNull(description);
456 CFReleaseNull(dict);
457 }
458 }
459 return (CFDictionaryRef)dict;
460 }
461
462 bool SecXPCDictionaryCopyDictionaryOptional(xpc_object_t message, const char *key, CFDictionaryRef *pdictionary, CFErrorRef *error) {
463 if (!xpc_dictionary_get_value(message, key)) {
464 *pdictionary = NULL;
465 return true;
466 }
467 *pdictionary = SecXPCDictionaryCopyDictionary(message, key, error);
468 return *pdictionary;
469 }
470
471 CFTypeRef SecXPCDictionaryCopyPList(xpc_object_t message, const char *key, CFErrorRef *error)
472 {
473 CFTypeRef cfobject = NULL;
474 size_t size = 0;
475 const uint8_t *der = xpc_dictionary_get_data(message, key, &size);
476 if (!der) {
477 SecError(errSecParam, error, CFSTR("no object for key %s"), key);
478 return NULL;
479 }
480
481 const uint8_t *der_end = der + size;
482 /* use the sensitive allocator so that the dictionary is zeroized upon deallocation */
483 const uint8_t *decode_end = der_decode_plist(SecCFAllocatorZeroize(), kCFPropertyListImmutable,
484 &cfobject, error, der, der_end);
485 if (decode_end != der_end) {
486 SecError(errSecParam, error, CFSTR("trailing garbage after der decoded object for key %s"), key);
487 CFReleaseNull(cfobject);
488 }
489
490 /* zeroize xpc value as it may have contained raw key material */
491 cc_clear(size, (void *)der);
492
493 return cfobject;
494 }
495
496 bool SecXPCDictionaryCopyPListOptional(xpc_object_t message, const char *key, CFTypeRef *pobject, CFErrorRef *error) {
497 size_t size = 0;
498 if (!xpc_dictionary_get_data(message, key, &size)) {
499 *pobject = NULL;
500 return true;
501 }
502 *pobject = SecXPCDictionaryCopyPList(message, key, error);
503 return *pobject;
504 }
505
506 CFStringRef SecXPCDictionaryCopyString(xpc_object_t message, const char *key, CFErrorRef *error) {
507 const char *string = xpc_dictionary_get_string(message, key);
508 if (string) {
509 CFStringRef result = CFStringCreateWithCString(kCFAllocatorDefault, string, kCFStringEncodingUTF8);
510 if (!result) {
511 SecError(errSecAllocate, error, CFSTR("object for key %s failed to convert %s to CFString"), key, string);
512 }
513 return result;
514 } else {
515 SecError(errSecParam, error, CFSTR("object for key %s not string"), key);
516 return NULL;
517 }
518 }
519
520 bool SecXPCDictionaryCopyStringOptional(xpc_object_t message, const char *key, CFStringRef *pstring, CFErrorRef *error) {
521 if (!xpc_dictionary_get_value(message, key)) {
522 *pstring = NULL;
523 return true;
524 }
525 *pstring = SecXPCDictionaryCopyString(message, key, error);
526 return *pstring;
527 }