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