]> git.saurik.com Git - apple/security.git/blob - SecurityTool/macOS/identity_find.m
Security-59306.11.20.tar.gz
[apple/security.git] / SecurityTool / macOS / identity_find.m
1 /*
2 * Copyright (c) 2003-2010,2012,2014-2019 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 * identity_find.c
24 */
25
26 #import <Foundation/Foundation.h>
27
28 #include "identity_find.h"
29 #include "keychain_utilities.h"
30 #include "trusted_cert_utils.h"
31 #include "security_tool.h"
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <Security/cssmtype.h>
38 #include <Security/oidsalg.h>
39 #include <Security/SecCertificate.h>
40 #include <Security/SecIdentity.h>
41 #include <Security/SecIdentitySearch.h>
42 #include <Security/SecPolicySearch.h>
43 #include <Security/SecTrust.h>
44 #include <Security/SecItem.h>
45 #include <CommonCrypto/CommonDigest.h>
46 #include <utilities/SecCFWrappers.h>
47
48 // cssmErrorString
49 #include <Security/SecBasePriv.h>
50 // SecCertificateInferLabel, SecDigestGetData, SecCertificateCopyPublicKeySHA{1,256}Digest
51 #include <Security/SecCertificatePriv.h>
52 // SecIdentitySearchCreateWithPolicy
53 #include <Security/SecIdentitySearchPriv.h>
54 // kSecAttrCanSignRecover, kSecAttrCanVerifyRecover
55 #include <Security/SecItemPriv.h>
56
57 static bool checkKeyUsageOperation(CSSM_KEYUSE keyUse, CSSM_KEYUSE keyOp, NSDictionary *keyAttrs, id secKeyOp) {
58 if (keyUse & keyOp) {
59 return [keyAttrs[secKeyOp] isEqual:@YES];
60 }
61 return true;
62 }
63
64 static bool checkKeyUsage(CSSM_KEYUSE keyUse, NSDictionary *keyAttrs) {
65 CSSM_KEYUSE keyOps[] = { CSSM_KEYUSE_ENCRYPT, CSSM_KEYUSE_DECRYPT, CSSM_KEYUSE_SIGN, CSSM_KEYUSE_VERIFY, CSSM_KEYUSE_SIGN_RECOVER, CSSM_KEYUSE_VERIFY_RECOVER, CSSM_KEYUSE_WRAP, CSSM_KEYUSE_UNWRAP, CSSM_KEYUSE_DERIVE };
66 id secKeyOps[] = { (id)kSecAttrCanEncrypt, (id)kSecAttrCanDecrypt, (id)kSecAttrCanSign, (id)kSecAttrCanVerify, (id)kSecAttrCanSignRecover, (id)kSecAttrCanVerifyRecover, (id)kSecAttrCanWrap, (id)kSecAttrCanUnwrap, (id)kSecAttrCanDerive };
67
68 if (keyUse & CSSM_KEYUSE_ANY) {
69 for (size_t i = 0; i < sizeof(keyOps) / sizeof(CSSM_KEYUSE); ++i) {
70 keyUse |= keyOps[i];
71 }
72 }
73 for (size_t i = 0; i < sizeof(keyOps) / sizeof(CSSM_KEYUSE); ++i) {
74 if (!checkKeyUsageOperation(keyUse, keyOps[i], keyAttrs, secKeyOps[i]))
75 return false;
76 }
77 return true;
78 }
79
80 SecIdentityRef
81 CopyMatchingIdentity(CFTypeRef keychainOrArray,
82 const char *identity,
83 const char *hash,
84 CSSM_KEYUSE keyUsage)
85 {
86 id identityRef;
87 // check input hash string and convert to data
88 NSData *hashData = nil;
89 if (hash) {
90 hashData = (__bridge_transfer NSData *)CFDataCreateFromHexString(kCFAllocatorDefault, (__bridge CFStringRef)[NSString stringWithUTF8String:hash]);
91 }
92
93 // filter candidates against the hash (or the name, if no hash provided)
94 NSString *matchName = (identity) ? [NSString stringWithUTF8String:identity] : nil;
95 Boolean exactMatch = FALSE;
96
97 NSDictionary *query;
98 if (keychainOrArray) {
99 id keychainArray;
100 if (![(__bridge id)keychainOrArray isKindOfClass:[NSArray class]]) {
101 keychainArray = @[(__bridge id)keychainOrArray];
102 } else {
103 keychainArray = (__bridge id)keychainOrArray;
104 }
105 query = @{ (id)kSecClass : (id)kSecClassIdentity,
106 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
107 (id)kSecMatchSearchList : keychainArray,
108 (id)kSecReturnRef : @YES };
109 } else {
110 query = @{ (id)kSecClass : (id)kSecClassIdentity,
111 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
112 (id)kSecReturnRef : @YES };
113 }
114 NSArray *identities;
115 if (SecItemCopyMatching((CFDictionaryRef)query, (void *)&identities) != errSecSuccess) {
116 return NULL;
117 }
118
119 for (id candidate in identities) {
120 id cert;
121 if (SecIdentityCopyCertificate((__bridge SecIdentityRef)candidate, (void *)&cert) != errSecSuccess)
122 continue;
123 if (keyUsage) {
124 NSData *pubKeyHash = (__bridge_transfer NSData*)SecCertificateCopyPublicKeySHA1Digest((__bridge SecCertificateRef)cert);
125 NSDictionary *keyAttrs;
126 if (SecItemCopyMatching((__bridge CFDictionaryRef)@{ (id)kSecClass : (id)kSecClassKey, (id)kSecAttrApplicationLabel : pubKeyHash, (id)kSecReturnAttributes : @YES }, (void *)&keyAttrs) != errSecSuccess)
127 continue;
128 if (!(checkKeyUsage(keyUsage, keyAttrs))) {
129 continue;
130 }
131 }
132 if (hashData) {
133 NSData *certDataHash = nil;
134 NSUInteger hashLength = [hashData length];
135 if (hashLength == CC_SHA256_DIGEST_LENGTH) {
136 certDataHash = (__bridge NSData *)SecCertificateCopySHA256Digest((__bridge SecCertificateRef)cert);
137 } else if (hashLength == CC_SHA1_DIGEST_LENGTH) {
138 certDataHash = (__bridge NSData *)SecCertificateGetSHA1Digest((__bridge SecCertificateRef)cert);
139 }
140 BOOL matched = [hashData isEqual:certDataHash];
141 certDataHash = nil; // ARC releases the hash
142 if (matched) {
143 identityRef = candidate;
144 break;
145 }
146 } else {
147 // copy certificate name
148 NSString *commonName;
149 if ((SecCertificateCopyCommonName((__bridge SecCertificateRef)cert, (void *)&commonName) != errSecSuccess) || commonName == nil) {
150 continue; // no name, so no match is possible
151 }
152
153 if (identity && !strcmp(identity, "*")) { // special case: means "just take the first one"
154 sec_error("Using identity \"%s\"", [commonName UTF8String]);
155 identityRef = candidate;
156 break;
157 }
158 CFRange find = { kCFNotFound, 0 };
159 if (commonName && matchName)
160 find = CFStringFind((__bridge CFStringRef)commonName, (__bridge CFStringRef)matchName, kCFCompareCaseInsensitive | kCFCompareNonliteral);
161 Boolean isExact = (find.location == 0 && find.length == (CFIndex)commonName.length);
162 if (find.location == kCFNotFound) {
163 continue; // no match
164 }
165 if (identityRef) { // got two matches
166 if (exactMatch && !isExact) { // prior is better; ignore this one
167 continue;
168 }
169 if (exactMatch == isExact) { // same class of match
170 if ([identityRef isEqual:candidate]) { // identities have same cert
171 continue;
172 }
173 // ambiguity - must fail
174 sec_error("\"%s\" is ambiguous, matches more than one certificate", identity);
175 identityRef = nil;
176 break;
177 }
178 }
179 identityRef = candidate;
180 exactMatch = isExact;
181 }
182 }
183
184 return identityRef?(__bridge_retained SecIdentityRef)identityRef:NULL;
185 }
186
187 static void printIdentity(SecIdentityRef identity, SecPolicyRef policy, int ordinalValue)
188 {
189 OSStatus status;
190 Boolean printHash = TRUE, printName = TRUE;
191 SecCertificateRef cert = NULL;
192
193 status = SecIdentityCopyCertificate(identity, &cert);
194 if (!status)
195 {
196 CSSM_DATA certData = { 0, nil };
197 (void) SecCertificateGetData(cert, &certData);
198 fprintf(stdout, "%3d) ", ordinalValue);
199 if (printHash) {
200 uint8 sha1_hash[20];
201 CSSM_DATA digest;
202 digest.Length = sizeof(sha1_hash);
203 digest.Data = sha1_hash;
204 if (SecDigestGetData(CSSM_ALGID_SHA1, &digest, &certData) == CSSM_OK) {
205 unsigned int i;
206 size_t len = digest.Length;
207 uint8 *cp = digest.Data;
208 for(i=0; i<len; i++) {
209 fprintf(stdout, "%02X", ((unsigned char *)cp)[i]);
210 }
211 } else {
212 fprintf(stdout, "!----- unable to get SHA-1 digest -----!");
213 }
214 }
215 if (printName) {
216 char *nameBuf = NULL;
217 CFStringRef nameRef = NULL;
218 status = SecCertificateInferLabel(cert, &nameRef);
219 CFIndex nameLen = (nameRef) ? CFStringGetLength(nameRef) : 0;
220 if (nameLen > 0) {
221 CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(nameLen, kCFStringEncodingUTF8);
222 nameBuf = (char *)malloc(bufLen);
223 if (!CFStringGetCString(nameRef, nameBuf, bufLen-1, kCFStringEncodingUTF8))
224 nameBuf[0]=0;
225 }
226 fprintf(stdout, " \"%s\"", (nameBuf && nameBuf[0] != 0) ? nameBuf : "<unknown>");
227 if (nameBuf)
228 free(nameBuf);
229 safe_CFRelease(&nameRef);
230 }
231
232 // Default to X.509 Basic if no policy was specified
233 if (!policy) {
234 SecPolicySearchRef policySearch = NULL;
235 if (SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &policySearch)==noErr) {
236 SecPolicySearchCopyNext(policySearch, &policy);
237 }
238 safe_CFRelease(&policySearch);
239 } else {
240 CFRetain(policy);
241 }
242
243 // Create the trust reference, given policy and certificates
244 SecTrustRef trust = nil;
245 SecTrustResultType trustResult = kSecTrustResultInvalid;
246 OSStatus trustResultCode = noErr;
247 CFMutableArrayRef certificates = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
248 if (certificates) {
249 CFArrayAppendValue(certificates, cert);
250 }
251 status = SecTrustCreateWithCertificates((CFArrayRef)certificates, policy, &trust);
252 if (!status) {
253 status = SecTrustEvaluate(trust, &trustResult);
254 }
255 if (trustResult != kSecTrustResultInvalid) {
256 status = SecTrustGetCssmResultCode(trust, &trustResultCode);
257 }
258 if (trustResultCode != noErr) {
259 fprintf(stdout, " (%s)\n", cssmErrorString(trustResultCode));
260 } else {
261 fprintf(stdout, "\n");
262 }
263 safe_CFRelease(&trust);
264 safe_CFRelease(&policy);
265 safe_CFRelease(&certificates);
266 }
267 safe_CFRelease(&cert);
268 (void) status;
269 }
270
271 static void
272 do_identity_search_with_policy(CFTypeRef keychainOrArray,
273 const char *name,
274 const CSSM_OID* oidPtr,
275 CSSM_KEYUSE keyUsage,
276 Boolean client,
277 Boolean validOnly)
278 {
279 // set up SMIME options with provided data
280 CE_KeyUsage ceKeyUsage = 0;
281 if (keyUsage & CSSM_KEYUSE_SIGN) ceKeyUsage |= CE_KU_DigitalSignature;
282 if (keyUsage & CSSM_KEYUSE_ENCRYPT) ceKeyUsage |= CE_KU_KeyEncipherment;
283 CSSM_APPLE_TP_SMIME_OPTIONS smimeOpts = {
284 CSSM_APPLE_TP_SMIME_OPTS_VERSION, // Version
285 ceKeyUsage, // IntendedUsage
286 name ? (uint32) strlen(name) : 0, // SenderEmailLen
287 name // SenderEmail
288 };
289 CSSM_DATA smimeValue = { sizeof(smimeOpts), (uint8*)&smimeOpts };
290
291 // set up SSL options with provided data
292 CSSM_APPLE_TP_SSL_OPTIONS sslOpts = {
293 CSSM_APPLE_TP_SSL_OPTS_VERSION, // Version
294 (name && !client) ? (uint32) strlen(name) : 0, // ServerNameLen
295 (client) ? NULL : name, // ServerName
296 (client) ? CSSM_APPLE_TP_SSL_CLIENT : 0 // Flags
297 };
298 CSSM_DATA sslValue = { sizeof(sslOpts), (uint8*)&sslOpts };
299
300 // get a policy ref for the specified policy OID
301 OSStatus status = noErr;
302 SecPolicyRef policy = NULL;
303 SecPolicySearchRef policySearch = NULL;
304 status = SecPolicySearchCreate(CSSM_CERT_X_509v3, oidPtr, NULL, &policySearch);
305 if (!status)
306 status = SecPolicySearchCopyNext(policySearch, &policy);
307
308 CSSM_DATA *policyValue = NULL;
309 const char *policyName = "<unknown>";
310
311 if (compareOids(oidPtr, &CSSMOID_APPLE_TP_SMIME)) {
312 policyName = "S/MIME";
313 policyValue = &smimeValue;
314 }
315 else if (compareOids(oidPtr, &CSSMOID_APPLE_TP_SSL)) {
316 if (client)
317 policyName = "SSL (client)";
318 else
319 policyName = "SSL (server)";
320 policyValue = &sslValue;
321 }
322 else if (compareOids(oidPtr, &CSSMOID_APPLE_TP_EAP)) {
323 policyName = "EAP";
324 }
325 else if (compareOids(oidPtr, &CSSMOID_APPLE_TP_IP_SEC)) {
326 policyName = "IPsec";
327 }
328 else if (compareOids(oidPtr, &CSSMOID_APPLE_TP_ICHAT)) {
329 policyName = "iChat";
330 }
331 else if (compareOids(oidPtr, &CSSMOID_APPLE_TP_CODE_SIGNING)) {
332 policyName = "Code Signing";
333 }
334 else if (compareOids(oidPtr, &CSSMOID_APPLE_X509_BASIC)) {
335 policyName = "X.509 Basic";
336 }
337 else if (compareOids(oidPtr, &CSSMOID_APPLE_TP_MACAPPSTORE_RECEIPT)) {
338 policyName = "Mac App Store Receipt";
339 }
340 else if (compareOids(oidPtr, &CSSMOID_APPLE_TP_APPLEID_SHARING)) {
341 policyName = "AppleID Sharing";
342 }
343
344 // set the policy's value, if there is one (this is specific to certain policies)
345 if (policy && policyValue)
346 status = SecPolicySetValue(policy, policyValue);
347
348 CFStringRef idStr = (name) ? CFStringCreateWithCStringNoCopy(NULL, name, kCFStringEncodingUTF8, kCFAllocatorNull) : NULL;
349 SecIdentitySearchRef searchRef = NULL;
350 int identityCount = 0;
351
352 if (!validOnly) {
353 // create an identity search, specifying all identities (i.e. returnOnlyValidIdentities=FALSE)
354 // this should return all identities which match the policy and key usage, regardless of validity
355 fprintf(stdout, "\nPolicy: %s\n", policyName);
356 fprintf(stdout, " Matching identities\n");
357 status = SecIdentitySearchCreateWithPolicy(policy, idStr, keyUsage, keychainOrArray, FALSE, &searchRef);
358 if (!status)
359 {
360 SecIdentityRef identityRef = NULL;
361 while (SecIdentitySearchCopyNext(searchRef, &identityRef) == noErr)
362 {
363 identityCount++;
364 printIdentity(identityRef, policy, identityCount);
365 safe_CFRelease(&identityRef);
366 }
367 safe_CFRelease(&searchRef);
368 }
369 fprintf(stdout, " %d identities found\n\n", identityCount);
370 }
371
372 // create a second identity search, specifying only valid identities (i.e. returnOnlyValidIdentities=TRUE)
373 // this should return only valid identities for the policy.
374 identityCount = 0;
375 if (!validOnly) {
376 fprintf(stdout, " Valid identities only\n");
377 }
378 status = SecIdentitySearchCreateWithPolicy(policy, idStr, keyUsage, keychainOrArray, TRUE, &searchRef);
379 if (!status)
380 {
381 SecIdentityRef identityRef = NULL;
382 while (SecIdentitySearchCopyNext(searchRef, &identityRef) == noErr)
383 {
384 identityCount++;
385 printIdentity(identityRef, policy, identityCount);
386 safe_CFRelease(&identityRef);
387 }
388 safe_CFRelease(&searchRef);
389 }
390 fprintf(stdout, " %d valid identities found\n", identityCount);
391
392 safe_CFRelease(&idStr);
393 safe_CFRelease(&policy);
394 safe_CFRelease(policySearch);
395 }
396
397 static void
398 do_system_identity_search(CFStringRef domain)
399 {
400 SecIdentityRef identity = NULL;
401 OSStatus status = SecIdentityCopySystemIdentity(domain, &identity, NULL);
402 if (CFEqual(domain, kSecIdentityDomainDefault)) {
403 fprintf(stdout, "\n System default identity\n");
404 } else if (CFEqual(domain, kSecIdentityDomainKerberosKDC)) {
405 fprintf(stdout, "\n System Kerberos KDC identity\n");
406 }
407 if (!status && identity) {
408 SecPolicyRef policy = NULL;
409 SecPolicySearchRef policySearch = NULL;
410 if (SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &policySearch) == noErr) {
411 if (SecPolicySearchCopyNext(policySearch, &policy) == noErr) {
412 printIdentity(identity, policy, 1);
413 CFRelease(policy);
414 }
415 safe_CFRelease(&policySearch);
416 }
417 safe_CFRelease(&identity);
418 }
419 }
420
421 static int
422 do_find_identities(CFTypeRef keychainOrArray, const char *name, unsigned int policyFlags, Boolean validOnly)
423 {
424 int result = 0;
425
426 if (name) {
427 fprintf(stdout, "Looking for identities matching \"%s\"\n", name);
428 }
429 if (policyFlags & (1 << 0))
430 do_identity_search_with_policy(keychainOrArray, name, &CSSMOID_APPLE_TP_SSL, CSSM_KEYUSE_SIGN, TRUE, validOnly);
431 if (policyFlags & (1 << 1))
432 do_identity_search_with_policy(keychainOrArray, name, &CSSMOID_APPLE_TP_SSL, CSSM_KEYUSE_SIGN, FALSE, validOnly);
433 if (policyFlags & (1 << 2))
434 do_identity_search_with_policy(keychainOrArray, name, &CSSMOID_APPLE_TP_SMIME, CSSM_KEYUSE_SIGN, TRUE, validOnly);
435 if (policyFlags & (1 << 3))
436 do_identity_search_with_policy(keychainOrArray, name, &CSSMOID_APPLE_TP_EAP, CSSM_KEYUSE_SIGN, TRUE, validOnly);
437 if (policyFlags & (1 << 4))
438 do_identity_search_with_policy(keychainOrArray, name, &CSSMOID_APPLE_TP_IP_SEC, CSSM_KEYUSE_SIGN, TRUE, validOnly);
439 if (policyFlags & (1 << 5))
440 do_identity_search_with_policy(keychainOrArray, name, &CSSMOID_APPLE_TP_ICHAT, CSSM_KEYUSE_SIGN, TRUE, validOnly);
441 if (policyFlags & (1 << 6))
442 do_identity_search_with_policy(keychainOrArray, name, &CSSMOID_APPLE_TP_CODE_SIGNING, CSSM_KEYUSE_SIGN, TRUE, validOnly);
443 if (policyFlags & (1 << 7))
444 do_identity_search_with_policy(keychainOrArray, name, &CSSMOID_APPLE_X509_BASIC, CSSM_KEYUSE_SIGN, TRUE, validOnly);
445
446 if (policyFlags & (1 << 8))
447 do_system_identity_search(kSecIdentityDomainDefault);
448 if (policyFlags & (1 << 9))
449 do_system_identity_search(kSecIdentityDomainKerberosKDC);
450 if (policyFlags & (1 << 10))
451 do_identity_search_with_policy(keychainOrArray, name, &CSSMOID_APPLE_TP_APPLEID_SHARING, CSSM_KEYUSE_SIGN, FALSE, validOnly);
452 if (policyFlags & (1 << 11))
453 do_identity_search_with_policy(keychainOrArray, name, &CSSMOID_APPLE_TP_MACAPPSTORE_RECEIPT, CSSM_KEYUSE_SIGN, TRUE, validOnly);
454
455 return result;
456 }
457
458 int
459 keychain_find_identity(int argc, char * const *argv)
460 {
461 int ch, result = 0;
462 unsigned int policyFlags = 0;
463 const char *name = NULL;
464 Boolean validOnly = FALSE;
465 CFTypeRef keychainOrArray = NULL;
466
467 /*
468 * " -p Specify policy to evaluate (multiple -p options are allowed)\n"
469 * " -s Specify optional policy-specific string (e.g. a DNS hostname for SSL,\n"
470 * " or RFC822 email address for S/MIME)\n"
471 * " -v Show valid identities only (default is to show all identities)\n"
472 */
473
474 while ((ch = getopt(argc, argv, "hp:s:v")) != -1)
475 {
476 switch (ch)
477 {
478 case 'p':
479 if (optarg != NULL) {
480 if (!strcmp(optarg, "ssl-client"))
481 policyFlags |= 1 << 0;
482 else if (!strcmp(optarg, "ssl-server"))
483 policyFlags |= 1 << 1;
484 else if (!strcmp(optarg, "smime"))
485 policyFlags |= 1 << 2;
486 else if (!strcmp(optarg, "eap"))
487 policyFlags |= 1 << 3;
488 else if (!strcmp(optarg, "ipsec"))
489 policyFlags |= 1 << 4;
490 else if (!strcmp(optarg, "ichat"))
491 policyFlags |= 1 << 5;
492 else if (!strcmp(optarg, "codesigning"))
493 policyFlags |= 1 << 6;
494 else if (!strcmp(optarg, "basic"))
495 policyFlags |= 1 << 7;
496 else if (!strcmp(optarg, "sys-default"))
497 policyFlags |= 1 << 8;
498 else if (!strcmp(optarg, "sys-kerberos-kdc"))
499 policyFlags |= 1 << 9;
500 else if (!strcmp(optarg, "appleID"))
501 policyFlags |= 1 << 10;
502 else if (!strcmp(optarg, "macappstore"))
503 policyFlags |= 1 << 11;
504 else {
505 result = SHOW_USAGE_MESSAGE;
506 goto cleanup;
507 }
508 }
509 break;
510 case 's':
511 name = optarg;
512 break;
513 case 'v':
514 validOnly = TRUE;
515 break;
516 case '?':
517 default:
518 result = 2; /* @@@ Return 2 triggers usage message. */
519 goto cleanup;
520 }
521 }
522
523 if (!policyFlags)
524 policyFlags |= 1 << 7; /* default to basic policy if none specified */
525
526 argc -= optind;
527 argv += optind;
528
529 keychainOrArray = keychain_create_array(argc, argv);
530
531 result = do_find_identities(keychainOrArray, name, policyFlags, validOnly);
532
533 cleanup:
534 safe_CFRelease(&keychainOrArray);
535
536 return result;
537 }
538