]> git.saurik.com Git - apple/security.git/blob - SecurityTests/clxutils/clAppUtils/identPicker.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTests / clxutils / clAppUtils / identPicker.cpp
1 /*
2 * Copyright (c) 2003-2008 Apple Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please
7 * obtain a copy of the License at http://www.apple.com/publicsource and
8 * read it before using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
12 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
13 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
15 * Please see the License for the specific language governing rights and
16 * limitations under the License.
17 */
18
19 /*
20 * identPicker.cp - Given a keychain, select from possible multiple
21 * SecIdentityRefs via stdio UI, and cook up a
22 * CFArray containing that identity and all certs needed
23 * for cert verification by an SSL peer. The resulting
24 * CFArrayRef is suitable for passing to SSLSetCertificate().
25 */
26
27 #include "identPicker.h"
28 #include "sslAppUtils.h"
29 #include <sys/param.h>
30 #include <string.h>
31 #include <stdio.h>
32 #include <ctype.h>
33
34 /*
35 * Safe gets().
36 * -- guaranteed no buffer overflow
37 * -- guaranteed NULL-terminated string
38 * -- handles empty string (i.e., response is just CR) properly
39 */
40 void getString(
41 char *buf,
42 unsigned bufSize)
43 {
44 unsigned dex;
45 char c;
46 char *cp = buf;
47
48 for(dex=0; dex<bufSize-1; dex++) {
49 c = getchar();
50 if(!isprint(c)) {
51 break;
52 }
53 switch(c) {
54 case '\n':
55 case '\r':
56 goto done;
57 default:
58 *cp++ = c;
59 }
60 }
61 done:
62 *cp = '\0';
63 }
64
65 /*
66 * Obtain the printable name of a SecKeychainItemRef as a C string.
67 * Caller must free() the result.
68 */
69 char *kcItemPrintableName(
70 SecKeychainItemRef itemRef)
71 {
72 char *crtn = NULL;
73
74 /* just search for the one attr we want */
75 UInt32 tag = kSecLabelItemAttr;
76 SecKeychainAttributeInfo attrInfo;
77 attrInfo.count = 1;
78 attrInfo.tag = &tag;
79 attrInfo.format = NULL;
80 SecKeychainAttributeList *attrList = NULL;
81 SecKeychainAttribute *attr = NULL;
82
83 OSStatus ortn = SecKeychainItemCopyAttributesAndData(
84 itemRef,
85 &attrInfo,
86 NULL, // itemClass
87 &attrList,
88 NULL, // length - don't need the data
89 NULL); // outData
90 if(ortn) {
91 cssmPerror("SecKeychainItemCopyAttributesAndData", ortn);
92 /* may want to be a bit more robust here, but this should
93 * never happen */
94 return strdup("Unnamed KeychainItem");
95 }
96 /* subsequent errors to errOut: */
97
98 if((attrList == NULL) || (attrList->count != 1)) {
99 printf("***Unexpected result fetching label attr\n");
100 crtn = strdup("Unnamed KeychainItem");
101 goto errOut;
102 }
103 /* We're assuming 8-bit ASCII attribute data here... */
104 attr = attrList->attr;
105 crtn = (char *)malloc(attr->length + 1);
106 memmove(crtn, attr->data, attr->length);
107 crtn[attr->length] = '\0';
108
109 errOut:
110 SecKeychainItemFreeAttributesAndData(attrList, NULL);
111 return crtn;
112 }
113
114 /*
115 * Get the final term of a keychain's path as a C string. Caller must free()
116 * the result.
117 */
118 char *kcFileName(
119 SecKeychainRef kcRef)
120 {
121 char fullPath[MAXPATHLEN + 1];
122 OSStatus ortn;
123 UInt32 pathLen = MAXPATHLEN;
124
125 ortn = SecKeychainGetPath(kcRef, &pathLen, fullPath);
126 if(ortn) {
127 cssmPerror("SecKeychainGetPath", ortn);
128 return strdup("orphan keychain");
129 }
130
131 /* NULL terminate the path string and search for final '/' */
132 fullPath[pathLen] = '\0';
133 char *lastSlash = NULL;
134 char *thisSlash = fullPath;
135 do {
136 thisSlash = strchr(thisSlash, '/');
137 if(thisSlash == NULL) {
138 /* done */
139 break;
140 }
141 thisSlash++;
142 lastSlash = thisSlash;
143 } while(thisSlash != NULL);
144 if(lastSlash == NULL) {
145 /* no slashes, odd, but handle it */
146 return strdup(fullPath);
147 }
148 else {
149 return strdup(lastSlash);
150 }
151 }
152
153 /*
154 * Obtain the final term of a keychain item's keychain path as a C string.
155 * Caller must free() the result.
156 * May well return NULL indicating the item has no keychain (e.g. az floating cert).
157 */
158 char *kcItemKcFileName(SecKeychainItemRef itemRef)
159 {
160 OSStatus ortn;
161 SecKeychainRef kcRef = NULL;
162
163 ortn = SecKeychainItemCopyKeychain(itemRef, &kcRef);
164 if(ortn) {
165 return NULL;
166 }
167 char *rtnStr = kcFileName(kcRef);
168 CFRelease(kcRef);
169 return rtnStr;
170 }
171
172 /*
173 * Given an array of SecIdentityRefs:
174 * -- display a printable name of each identity's cert;
175 * -- prompt user to select which one to use;
176 *
177 * Returns CFIndex of desired identity. A return of <0 indicates
178 * "none - abort".
179 */
180 static CFIndex pickIdent(
181 CFArrayRef idArray)
182 {
183 CFIndex count = CFArrayGetCount(idArray);
184 CFIndex dex;
185 OSStatus ortn;
186
187 if(count == 0) {
188 printf("***sslIdentPicker screwup: no identities found\n");
189 return -1;
190 }
191 for(dex=0; dex<count; dex++) {
192 SecIdentityRef idRef = (SecIdentityRef)CFArrayGetValueAtIndex(idArray, dex);
193 SecCertificateRef certRef;
194 ortn = SecIdentityCopyCertificate(idRef, &certRef);
195 if(ortn) {
196 /* should never happen */
197 cssmPerror("SecIdentityCopyCertificate", ortn);
198 return -1;
199 }
200
201 /* get printable name of cert and the keychain it's in */
202 char *certLabel = kcItemPrintableName((SecKeychainItemRef)certRef);
203 SecKeychainRef kcRef;
204 char *kcLabel;
205 ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)certRef, &kcRef);
206 if(ortn) {
207 cssmPerror("SecKeychainItemCopyKeychain", ortn);
208 kcLabel = (char *)"Unnamed keychain";
209 }
210 else {
211 kcLabel = kcFileName(kcRef);
212 }
213 printf("[%ld] keychain : %s\n", dex, kcLabel);
214 printf(" cert : %s\n", certLabel);
215 free(certLabel);
216 if(ortn == noErr) {
217 free(kcLabel);
218 }
219 CFRelease(certRef);
220 }
221
222 while(1) {
223 fpurge(stdin);
224 printf("\nEnter Certificate number or CR to quit : ");
225 fflush(stdout);
226 char resp[64];
227 getString(resp, sizeof(resp));
228 if(resp[0] == '\0') {
229 return -1;
230 }
231 int ires = atoi(resp);
232 if((ires >= 0) && (ires < count)) {
233 return (CFIndex)ires;
234 }
235 printf("***Invalid entry. Type a number between 0 and %ld\n",
236 count-1);
237 }
238 return -1;
239 }
240
241 OSStatus sslSimpleIdentPicker(
242 SecKeychainRef kcRef, // NULL means use default list
243 SecIdentityRef *ident) // RETURNED
244 {
245 OSStatus ortn;
246 CFMutableArrayRef idArray = NULL; // holds all SecIdentityRefs found
247
248 /* Search for all identities */
249 *ident = NULL;
250 SecIdentitySearchRef srchRef = nil;
251 ortn = SecIdentitySearchCreate(kcRef,
252 0, // keyUsage - any
253 &srchRef);
254 if(ortn) {
255 cssmPerror("SecIdentitySearchCreate", (CSSM_RETURN)ortn);
256 printf("Cannot find signing key in keychain.\n");
257 return ortn;
258 }
259
260 /* get all identities, stuff them into idArray */
261 SecIdentityRef identity = nil;
262 idArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
263 do {
264 ortn = SecIdentitySearchCopyNext(srchRef, &identity);
265 if(ortn != noErr) {
266 break;
267 }
268 CFArrayAppendValue(idArray, identity);
269
270 /* the array has the retain count we need */
271 CFRelease(identity);
272 } while(ortn == noErr);
273 CFRelease(srchRef);
274
275 switch(ortn) {
276 case errSecItemNotFound:
277 if(CFArrayGetCount(idArray) == 0) {
278 printf("No signing keys found in keychain.\n");
279 return errSecItemNotFound;
280 }
281 else {
282 /* found at least one; proceed */
283 break;
284 }
285 default:
286 cssmPerror("SecIdentitySearchCopyNext", (CSSM_RETURN)ortn);
287 printf("Cannot find signing key in keychain.\n");
288 return ortn;
289 }
290
291 /*
292 * If there is just one, use it without asking
293 */
294 CFIndex whichId;
295 if(CFArrayGetCount(idArray) == 1) {
296 whichId = 0;
297 }
298 else {
299 whichId = pickIdent(idArray);
300 if(whichId < 0) {
301 return CSSMERR_CSSM_USER_CANCELED;
302 }
303 }
304
305 /* keep this one, free the rest */
306 identity = (SecIdentityRef)CFArrayGetValueAtIndex(idArray, whichId);
307 CFRetain(identity);
308 CFRelease(idArray);
309 *ident = identity;
310 return noErr;
311 }
312
313 OSStatus sslIdentPicker(
314 SecKeychainRef kcRef, // NULL means use default list
315 SecCertificateRef trustedAnchor, // optional additional trusted anchor
316 bool includeRoot, // true --> root is appended to outArray
317 // false --> root not included
318 const CSSM_OID *vfyPolicy, // optional - if NULL, use SSL
319 CFArrayRef *outArray) // created and RETURNED
320 {
321 OSStatus ortn;
322 SecIdentityRef identity;
323
324 ortn = sslSimpleIdentPicker(kcRef, &identity);
325 if(ortn) {
326 return ortn;
327 }
328 return sslCompleteCertChain(identity, trustedAnchor, includeRoot,
329 vfyPolicy, outArray);
330 }
331