]> git.saurik.com Git - apple/security.git/blob - SecurityTests/clxutils/krbtool/identPicker.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / SecurityTests / clxutils / krbtool / identPicker.cpp
1 /*
2 * Copyright (c) 2004 Apple Computer, 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 * identPicker.cpp - Given a keychain, select from possible multiple
25 * SecIdentityRefs via stdio UI, and cook up a
26 * CFArray containing that identity and all certs needed
27 * for cert verification by an SSL peer. The resulting
28 * CFArrayRef is suitable for passing to SSLSetCertificate().
29 */
30
31 #include "identPicker.h"
32 #include <sys/param.h>
33 #include <string.h>
34 #include <stdio.h>
35 #include <ctype.h>
36
37 /*
38 * Safe gets().
39 * -- guaranteed no buffer overflow
40 * -- guaranteed NULL-terminated string
41 * -- handles empty string (i.e., response is just CR) properly
42 */
43 static void getString(
44 char *buf,
45 unsigned bufSize)
46 {
47 unsigned dex;
48 char c;
49 char *cp = buf;
50
51 for(dex=0; dex<bufSize-1; dex++) {
52 c = getchar();
53 if(!isprint(c)) {
54 break;
55 }
56 switch(c) {
57 case '\n':
58 case '\r':
59 goto done;
60 default:
61 *cp++ = c;
62 }
63 }
64 done:
65 *cp = '\0';
66 }
67
68 /*
69 * Obtain the printable name of a SecKeychainItemRef as a C string.
70 * Caller must free() the result.
71 */
72 static char *kcItemPrintableName(
73 SecKeychainItemRef certRef)
74 {
75 char *crtn = NULL;
76
77 /* just search for the one attr we want */
78 UInt32 tag = kSecLabelItemAttr;
79 SecKeychainAttributeInfo attrInfo;
80 attrInfo.count = 1;
81 attrInfo.tag = &tag;
82 attrInfo.format = NULL;
83 SecKeychainAttributeList *attrList = NULL;
84 SecKeychainAttribute *attr = NULL;
85
86 OSStatus ortn = SecKeychainItemCopyAttributesAndData(
87 (SecKeychainItemRef)certRef,
88 &attrInfo,
89 NULL, // itemClass
90 &attrList,
91 NULL, // length - don't need the data
92 NULL); // outData
93 if(ortn) {
94 cssmPerror("SecKeychainItemCopyAttributesAndData", ortn);
95 /* may want to be a bit more robust here, but this should
96 * never happen */
97 return strdup("Unnamed KeychainItem");
98 }
99 /* subsequent errors to errOut: */
100
101 if((attrList == NULL) || (attrList->count != 1)) {
102 printf("***Unexpected result fetching label attr\n");
103 crtn = strdup("Unnamed KeychainItem");
104 goto errOut;
105 }
106 /* We're assuming 8-bit ASCII attribute data here... */
107 attr = attrList->attr;
108 crtn = (char *)malloc(attr->length + 1);
109 memmove(crtn, attr->data, attr->length);
110 crtn[attr->length] = '\0';
111
112 errOut:
113 SecKeychainItemFreeAttributesAndData(attrList, NULL);
114 return crtn;
115 }
116
117 /*
118 * Get the final term of a keychain's path as a C string. Caller must free()
119 * the result.
120 */
121 static char *kcFileName(
122 SecKeychainRef kcRef)
123 {
124 char fullPath[MAXPATHLEN + 1];
125 OSStatus ortn;
126 UInt32 pathLen = MAXPATHLEN;
127
128 ortn = SecKeychainGetPath(kcRef, &pathLen, fullPath);
129 if(ortn) {
130 cssmPerror("SecKeychainGetPath", ortn);
131 return strdup("orphan keychain");
132 }
133
134 /* NULL terminate the path string and search for final '/' */
135 fullPath[pathLen] = '\0';
136 char *lastSlash = NULL;
137 char *thisSlash = fullPath;
138 do {
139 thisSlash = strchr(thisSlash, '/');
140 if(thisSlash == NULL) {
141 /* done */
142 break;
143 }
144 thisSlash++;
145 lastSlash = thisSlash;
146 } while(thisSlash != NULL);
147 if(lastSlash == NULL) {
148 /* no slashes, odd, but handle it */
149 return strdup(fullPath);
150 }
151 else {
152 return strdup(lastSlash);
153 }
154 }
155
156 /*
157 * Determine if specified SecCertificateRef is a self-signed cert.
158 * We do this by comparing the subject and issuerr names; no cryptographic
159 * verification is performed.
160 *
161 * Returns true if the cert appears to be a root.
162 */
163 static bool isCertRefRoot(
164 SecCertificateRef certRef)
165 {
166 /* just search for the two attrs we want */
167 UInt32 tags[2] = {kSecSubjectItemAttr, kSecIssuerItemAttr};
168 SecKeychainAttributeInfo attrInfo;
169 attrInfo.count = 2;
170 attrInfo.tag = tags;
171 attrInfo.format = NULL;
172 SecKeychainAttributeList *attrList = NULL;
173 SecKeychainAttribute *attr1 = NULL;
174 SecKeychainAttribute *attr2 = NULL;
175 bool brtn = false;
176
177 OSStatus ortn = SecKeychainItemCopyAttributesAndData(
178 (SecKeychainItemRef)certRef,
179 &attrInfo,
180 NULL, // itemClass
181 &attrList,
182 NULL, // length - don't need the data
183 NULL); // outData
184 if(ortn) {
185 cssmPerror("SecKeychainItemCopyAttributesAndData", ortn);
186 /* may want to be a bit more robust here, but this should
187 * never happen */
188 return false;
189 }
190 /* subsequent errors to errOut: */
191
192 if((attrList == NULL) || (attrList->count != 2)) {
193 printf("***Unexpected result fetching label attr\n");
194 goto errOut;
195 }
196
197 /* rootness is just byte-for-byte compare of the two names */
198 attr1 = &attrList->attr[0];
199 attr2 = &attrList->attr[1];
200 if(attr1->length == attr2->length) {
201 if(memcmp(attr1->data, attr2->data, attr1->length) == 0) {
202 brtn = true;
203 }
204 }
205 errOut:
206 SecKeychainItemFreeAttributesAndData(attrList, NULL);
207 return brtn;
208 }
209
210
211 /*
212 * Given a SecIdentityRef, do our best to construct a complete, ordered, and
213 * verified cert chain, returning the result in a CFArrayRef. The result is
214 * suitable for use when calling SSLSetCertificate().
215 */
216 static OSStatus completeCertChain(
217 SecIdentityRef identity,
218 SecCertificateRef trustedAnchor, // optional additional trusted anchor
219 bool includeRoot, // include the root in outArray
220 CFArrayRef *outArray) // created and RETURNED
221 {
222 CFMutableArrayRef certArray;
223 SecTrustRef secTrust = NULL;
224 SecPolicyRef policy = NULL;
225 SecPolicySearchRef policySearch = NULL;
226 SecTrustResultType secTrustResult;
227 CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv; // not used
228 CFArrayRef certChain = NULL; // constructed chain
229 CFIndex numResCerts;
230
231 certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
232 CFArrayAppendValue(certArray, identity);
233
234 /*
235 * Case 1: identity is a root; we're done. Note that this case
236 * overrides the includeRoot argument.
237 */
238 SecCertificateRef certRef;
239 OSStatus ortn = SecIdentityCopyCertificate(identity, &certRef);
240 if(ortn) {
241 /* should never happen */
242 cssmPerror("SecIdentityCopyCertificate", ortn);
243 return ortn;
244 }
245 bool isRoot = isCertRefRoot(certRef);
246 if(isRoot) {
247 *outArray = certArray;
248 CFRelease(certRef);
249 return noErr;
250 }
251
252 /*
253 * Now use SecTrust to get a complete cert chain, using all of the
254 * user's keychains to look for intermediate certs.
255 * NOTE this does NOT handle root certs which are not in the system
256 * root cert DB. (The above case, where the identity is a root cert, does.)
257 */
258 CFMutableArrayRef subjCerts = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks);
259 CFArraySetValueAtIndex(subjCerts, 0, certRef);
260
261 /* the array owns the subject cert ref now */
262 CFRelease(certRef);
263
264 /* Get a SecPolicyRef for generic X509 cert chain verification */
265 ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
266 &CSSMOID_APPLE_X509_BASIC,
267 NULL, // value
268 &policySearch);
269 if(ortn) {
270 cssmPerror("SecPolicySearchCreate", ortn);
271 goto errOut;
272 }
273 ortn = SecPolicySearchCopyNext(policySearch, &policy);
274 if(ortn) {
275 cssmPerror("SecPolicySearchCopyNext", ortn);
276 goto errOut;
277 }
278
279 /* build a SecTrustRef for specified policy and certs */
280 ortn = SecTrustCreateWithCertificates(subjCerts,
281 policy, &secTrust);
282 if(ortn) {
283 cssmPerror("SecTrustCreateWithCertificates", ortn);
284 goto errOut;
285 }
286
287 if(trustedAnchor) {
288 /*
289 * Tell SecTrust to trust this one in addition to the current
290 * trusted system-wide anchors.
291 */
292 CFMutableArrayRef newAnchors;
293 CFArrayRef currAnchors;
294
295 ortn = SecTrustCopyAnchorCertificates(&currAnchors);
296 if(ortn) {
297 /* should never happen */
298 cssmPerror("SecTrustCopyAnchorCertificates", ortn);
299 goto errOut;
300 }
301 newAnchors = CFArrayCreateMutableCopy(NULL,
302 CFArrayGetCount(currAnchors) + 1,
303 currAnchors);
304 CFRelease(currAnchors);
305 CFArrayAppendValue(newAnchors, trustedAnchor);
306 ortn = SecTrustSetAnchorCertificates(secTrust, newAnchors);
307 CFRelease(newAnchors);
308 if(ortn) {
309 cssmPerror("SecTrustSetAnchorCertificates", ortn);
310 goto errOut;
311 }
312 }
313 /* evaluate: GO */
314 ortn = SecTrustEvaluate(secTrust, &secTrustResult);
315 if(ortn) {
316 cssmPerror("SecTrustEvaluate", ortn);
317 goto errOut;
318 }
319 switch(secTrustResult) {
320 case kSecTrustResultUnspecified:
321 /* cert chain valid, no special UserTrust assignments */
322 case kSecTrustResultProceed:
323 /* cert chain valid AND user explicitly trusts this */
324 break;
325 default:
326 /*
327 * Cert chain construction failed.
328 * Just go with the single subject cert we were given.
329 */
330 printf("***Warning: could not construct completed cert chain\n");
331 ortn = noErr;
332 goto errOut;
333 }
334
335 /* get resulting constructed cert chain */
336 ortn = SecTrustGetResult(secTrust, &secTrustResult, &certChain, &dummyEv);
337 if(ortn) {
338 cssmPerror("SecTrustEvaluate", ortn);
339 goto errOut;
340 }
341
342 /*
343 * Copy certs from constructed chain to our result array, skipping
344 * the leaf (which is already there, as a SecIdentityRef) and possibly
345 * a root.
346 */
347 numResCerts = CFArrayGetCount(certChain);
348 if(numResCerts < 2) {
349 /*
350 * Can't happen: if subject was a root, we'd already have returned.
351 * If chain doesn't verify to a root, we'd have bailed after
352 * SecTrustEvaluate().
353 */
354 printf("***sslCompleteCertChain screwup: numResCerts %d\n",
355 (int)numResCerts);
356 ortn = noErr;
357 goto errOut;
358 }
359 if(!includeRoot) {
360 /* skip the last (root) cert) */
361 numResCerts--;
362 }
363 for(CFIndex dex=1; dex<numResCerts; dex++) {
364 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, dex);
365 CFArrayAppendValue(certArray, certRef);
366 }
367 errOut:
368 /* clean up */
369 if(secTrust) {
370 CFRelease(secTrust);
371 }
372 if(subjCerts) {
373 CFRelease(subjCerts);
374 }
375 if(policy) {
376 CFRelease(policy);
377 }
378 if(policySearch) {
379 CFRelease(policySearch);
380 }
381 *outArray = certArray;
382 return ortn;
383 }
384
385
386 /*
387 * Given an array of SecIdentityRefs:
388 * -- display a printable name of each identity's cert;
389 * -- prompt user to select which one to use;
390 *
391 * Returns CFIndex of desired identity. A return of <0 indicates
392 * "none - abort".
393 */
394 static CFIndex pickIdent(
395 CFArrayRef idArray)
396 {
397 CFIndex count = CFArrayGetCount(idArray);
398 CFIndex dex;
399 OSStatus ortn;
400
401 if(count == 0) {
402 printf("***sslIdentPicker screwup: no identities found\n");
403 return -1;
404 }
405 for(dex=0; dex<count; dex++) {
406 SecIdentityRef idRef = (SecIdentityRef)CFArrayGetValueAtIndex(idArray, dex);
407 SecCertificateRef certRef;
408 ortn = SecIdentityCopyCertificate(idRef, &certRef);
409 if(ortn) {
410 /* should never happen */
411 cssmPerror("SecIdentityCopyCertificate", ortn);
412 return -1;
413 }
414
415 /* get printable name of cert and the keychain it's in */
416 char *certLabel = kcItemPrintableName((SecKeychainItemRef)certRef);
417 SecKeychainRef kcRef;
418 char *kcLabel;
419 ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)certRef, &kcRef);
420 if(ortn) {
421 cssmPerror("SecKeychainItemCopyKeychain", ortn);
422 kcLabel = "Unnamed keychain";
423 }
424 else {
425 kcLabel = kcFileName(kcRef);
426 }
427 printf("[%d] keychain : %s\n", (int)dex, kcLabel);
428 printf(" cert : %s\n", certLabel);
429 free(certLabel);
430 if(ortn == noErr) {
431 free(kcLabel);
432 }
433 CFRelease(certRef);
434 }
435
436 while(1) {
437 fpurge(stdin);
438 printf("\nEnter Certificate number or CR to quit : ");
439 fflush(stdout);
440 char resp[64];
441 getString(resp, sizeof(resp));
442 if(resp[0] == '\0') {
443 return -1;
444 }
445 int ires = atoi(resp);
446 if((ires >= 0) && (ires < count)) {
447 return (CFIndex)ires;
448 }
449 printf("***Invalid entry. Type a number between 0 and %d\n",
450 (int)(count-1));
451 }
452 return -1;
453 }
454
455 OSStatus simpleIdentPicker(
456 SecKeychainRef kcRef, // NULL means use default list
457 SecIdentityRef *ident) // RETURNED
458 {
459 OSStatus ortn;
460 CFMutableArrayRef idArray = NULL; // holds all SecIdentityRefs found
461
462 /* Search for all identities */
463 *ident = NULL;
464 SecIdentitySearchRef srchRef = nil;
465 ortn = SecIdentitySearchCreate(kcRef,
466 0, // keyUsage - any
467 &srchRef);
468 if(ortn) {
469 cssmPerror("SecIdentitySearchCreate", (CSSM_RETURN)ortn);
470 printf("Cannot find signing key in keychain.\n");
471 return ortn;
472 }
473
474 /* get all identities, stuff them into idArray */
475 SecIdentityRef identity = nil;
476 idArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
477 do {
478 ortn = SecIdentitySearchCopyNext(srchRef, &identity);
479 if(ortn != noErr) {
480 break;
481 }
482 CFArrayAppendValue(idArray, identity);
483
484 /* the array has the retain count we need */
485 CFRelease(identity);
486 } while(ortn == noErr);
487
488 switch(ortn) {
489 case errSecItemNotFound:
490 if(CFArrayGetCount(idArray) == 0) {
491 printf("No signing keys found in keychain.\n");
492 return errSecItemNotFound;
493 }
494 else {
495 /* found at least one; proceed */
496 break;
497 }
498 default:
499 cssmPerror("SecIdentitySearchCopyNext", (CSSM_RETURN)ortn);
500 printf("Cannot find signing key in keychain.\n");
501 return ortn;
502 }
503
504 /*
505 * If there is just one, use it without asking
506 */
507 CFIndex whichId;
508 if(CFArrayGetCount(idArray) == 1) {
509 whichId = 0;
510 }
511 else {
512 whichId = pickIdent(idArray);
513 if(whichId < 0) {
514 return CSSMERR_CSSM_USER_CANCELED;
515 }
516 }
517
518 /* keep this one, free the rest */
519 identity = (SecIdentityRef)CFArrayGetValueAtIndex(idArray, whichId);
520 CFRetain(identity);
521 CFRelease(idArray);
522 *ident = identity;
523 return noErr;
524 }
525
526 OSStatus identPicker(
527 SecKeychainRef kcRef, // NULL means use default list
528 SecCertificateRef trustedAnchor, // optional additional trusted anchor
529 bool includeRoot, // true --> root is appended to outArray
530 // false --> root not included
531 CFArrayRef *outArray) // created and RETURNED
532 {
533 OSStatus ortn;
534 SecIdentityRef identity;
535
536 ortn = simpleIdentPicker(kcRef, &identity);
537 if(ortn) {
538 return ortn;
539 }
540 return completeCertChain(identity, trustedAnchor, includeRoot, outArray);
541 }
542