]> git.saurik.com Git - apple/security.git/blob - SecureTransport/sslKeychain.c
Security-30.1.tar.gz
[apple/security.git] / SecureTransport / sslKeychain.c
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, 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 obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * 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 EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 /*
20 File: sslKeychain.c
21
22 Contains: Apple Keychain routines
23
24 Written by: Doug Mitchell, based on Netscape RSARef 3.0
25
26 Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved.
27
28 */
29
30 #include "ssl.h"
31 #include "sslctx.h"
32 #include "sslalloc.h"
33 #include "appleCdsa.h"
34 #include "appleGlue.h"
35 #include "sslerrs.h"
36 #include "sslDebug.h"
37 #include "sslKeychain.h"
38 #include "sslutil.h"
39
40 #if ST_KEYCHAIN_ENABLE
41 #include <Keychain.h>
42 #include <KeychainPriv.h>
43 #endif /* ST_KEYCHAIN_ENABLE */
44
45 #include <string.h>
46
47 #if ST_KEYCHAIN_ENABLE
48 static OSStatus
49 addCertData(
50 SSLContext *ctx,
51 KCItemRef kcItem,
52 CSSM_DATA_PTR certData,
53 Boolean *goodCert); /* RETURNED */
54
55 /*
56 * Given a KCItemRef: is this item a cert?
57 */
58 static Boolean
59 isItemACert(KCItemRef kcItem)
60 {
61 KCAttribute attr;
62 FourCharCode itemClass;
63 OSStatus ortn;
64 UInt32 len;
65
66 attr.tag = kClassKCItemAttr;
67 attr.length = sizeof(FourCharCode);
68 attr.data = &itemClass;
69
70 ortn = KCGetAttribute (kcItem, &attr, &len);
71 if (ortn == noErr) {
72 return((itemClass == kCertificateKCItemClass) ? true : false);
73 }
74 else {
75 errorLog1("isItemACert: KCGetAttribute returned %d\n", ortn);
76 return false;
77 }
78 }
79
80 #endif /* ST_KEYCHAIN_ENABLE */
81
82 #if (ST_SERVER_MODE_ENABLE || ST_CLIENT_AUTHENTICATION)
83 /*
84 * Given an array of certs (as KCItemRefs, specified by caller
85 * in SSLSetCertificate or SSLSetEncryptionCertificate) and a
86 * destination SSLCertificate:
87 *
88 * -- free destCerts if we have any
89 * -- Get raw cert data, convert to array of SSLCertificates in *destCert
90 * -- validate cert chain
91 * -- get pub, priv keys from certRef[0], store in *pubKey, *privKey
92 */
93 OSStatus
94 parseIncomingCerts(
95 SSLContext *ctx,
96 CFArrayRef certs,
97 SSLCertificate **destCert, /* &ctx->{localCert,encryptCert} */
98 CSSM_KEY_PTR *pubKey, /* &ctx->signingPubKey, etc. */
99 CSSM_KEY_PTR *privKey, /* &ctx->signingPrivKey, etc. */
100 CSSM_CSP_HANDLE *cspHand, /* &ctx->signingKeyCsp, etc. */
101 KCItemRef *privKeyRef) /* &ctx->signingKeyRef, etc. */
102 {
103 CFIndex numCerts;
104 CFIndex cert;
105 SSLCertificate *certChain = NULL;
106 SSLCertificate *thisSslCert;
107 KCItemRef kcItem;
108 SSLBuffer *derSubjCert = NULL;
109 UInt32 certLen;
110 OSStatus ortn;
111 SSLErr srtn;
112 FromItemGetPrivateKeyParams keyParams = {NULL, NULL};
113 FromItemGetKeyInfoParams keyInfo = {NULL, NULL, 0};
114 CSSM_CSP_HANDLE dummyCsp;
115
116 CASSERT(ctx != NULL);
117 CASSERT(destCert != NULL); /* though its referent may be NULL */
118 CASSERT(pubKey != NULL);
119 CASSERT(privKey != NULL);
120 CASSERT(cspHand != NULL);
121 CASSERT(privKeyRef != NULL);
122
123 sslDeleteCertificateChain(*destCert, ctx);
124 *destCert = NULL;
125 *pubKey = NULL;
126 *privKey = NULL;
127 *cspHand = 0;
128
129 if(certs == NULL) {
130 dprintf0("parseIncomingCerts: NULL incoming cert array\n");
131 return errSSLBadCert;
132 }
133 numCerts = CFArrayGetCount(certs);
134 if(numCerts == 0) {
135 dprintf0("parseIncomingCerts: empty incoming cert array\n");
136 return errSSLBadCert;
137 }
138
139 /*
140 * Convert: CFArray of KCItemRefs --> chain of SSLCertificates.
141 * Incoming certs have root last; SSLCertificate chain has root
142 * first.
143 */
144 for(cert=0; cert<numCerts; cert++) {
145 kcItem = (KCItemRef)CFArrayGetValueAtIndex(certs, cert);
146 if(kcItem == NULL) {
147 errorLog0("parseIncomingCerts: bad cert array\n");
148 return paramErr;
149 }
150 if(!isItemACert(kcItem)) {
151 /* client app error, not ours */
152 return paramErr;
153 }
154
155 /*
156 * OK, cook up an SSLCertificate and its associated SSLBuffer.
157 * First the size of the actual cert data...
158 */
159 ortn = KCGetData(kcItem, 0, NULL, &certLen);
160 if(ortn != noErr) {
161 errorLog1("parseIncomingCerts: KCGetData(1) returned %d\n", ortn);
162 return ortn;
163 }
164 thisSslCert = sslMalloc(sizeof(SSLCertificate));
165 if(thisSslCert == NULL) {
166 return memFullErr;
167 }
168 if(SSLAllocBuffer(&thisSslCert->derCert, certLen, &ctx->sysCtx)) {
169 return memFullErr;
170 }
171
172 /* now the data itself */
173 ortn = KCGetData (kcItem,
174 certLen,
175 thisSslCert->derCert.data,
176 &certLen);
177 if(ortn) {
178 errorLog1("parseIncomingCerts: KCGetData(2) returned %d\n", ortn);
179 SSLFreeBuffer(&thisSslCert->derCert, &ctx->sysCtx);
180 return ortn;
181 }
182
183 /* enqueue onto head of cert chain */
184 thisSslCert->next = certChain;
185 certChain = thisSslCert;
186
187 if(derSubjCert == NULL) {
188 /* Save this ptr for obtaining public key */
189 derSubjCert = &thisSslCert->derCert;
190 }
191 }
192
193 /* validate the whole mess */
194 srtn = sslVerifyCertChain(ctx, certChain);
195 if(srtn) {
196 ortn = sslErrToOsStatus(srtn);
197 goto errOut;
198 }
199
200 /*
201 * Get privKey, pubKey, KCItem of certs[0].
202 * First, the private key, from the Keychain, using crufy private API.
203 */
204 keyParams.item = (KCItemRef)CFArrayGetValueAtIndex(certs, 0);
205 ortn = KCDispatch(kKCFromItemGetPrivateKey, &keyParams);
206 if(ortn) {
207 errorLog1("KCDispatch(kKCFromItemGetPrivateKey) returned %d\n", ortn);
208 goto errOut;
209 }
210 keyInfo.item = keyParams.privateKeyItem;
211 ortn = KCDispatch(kKCFromItemGetKeyInfo, &keyInfo);
212 if(ortn) {
213 errorLog1("KCDispatch(kKCFromItemGetKeyInfo) returned %d\n", ortn);
214 goto errOut;
215 }
216 *privKey = (CSSM_KEY_PTR)keyInfo.keyPtr;
217 *cspHand = keyInfo.cspHandle;
218 *privKeyRef = keyParams.privateKeyItem;
219
220 /* now the public key, from CL */
221 /* FIXME - what if this CSP differs from the one we got from KC??? */
222 srtn = sslPubKeyFromCert(ctx,
223 derSubjCert,
224 pubKey,
225 &dummyCsp);
226 if(srtn) {
227 errorLog1("sslPubKeyFromCert returned %d\n", srtn);
228 ortn = sslErrToOsStatus(srtn);
229 goto errOut;
230 }
231
232 /* SUCCESS */
233 *destCert = certChain;
234 return noErr;
235
236 errOut:
237 /* free certChain, everything in it, other vars, return ortn */
238 sslDeleteCertificateChain(certChain, ctx);
239 if(keyInfo.keyPtr != NULL) {
240 sslFreeKey(keyInfo.cspHandle, &keyInfo.keyPtr, NULL);
241 }
242 if(keyParams.privateKeyItem != NULL) {
243 KCReleaseItem(&keyParams.privateKeyItem);
244 }
245 return ortn;
246 }
247 #endif /* (ST_SERVER_MODE_ENABLE || ST_CLIENT_AUTHENTICATION) */
248
249 /*
250 * Add Apple built-in root certs to ctx->trustedCerts.
251 */
252 OSStatus addBuiltInCerts (SSLContextRef ctx)
253 {
254 #if ST_KEYCHAIN_ENABLE
255 OSStatus ortn;
256 KCRef kc = nil;
257
258 ortn = KCDispatch(kKCGetRootCertificateKeychain, &kc);
259 if(ortn) {
260 errorLog1("KCDispatch(kKCGetRootCertificateKeychain) returned %d\n",
261 ortn);
262 return ortn;
263 }
264 return parseTrustedKeychain(ctx, kc);
265 #else
266 /* nothing for now */
267 return noErr;
268 #endif /* ST_KEYCHAIN_ENABLE */
269 }
270
271 #if ST_KEYCHAIN_ENABLE
272
273 /*
274 * Given an open Keychain:
275 * -- Get raw cert data, add to array of CSSM_DATAs in
276 * ctx->trustedCerts
277 * -- verify that each of these is a valid (self-verifying)
278 * root cert
279 * -- add each subject name to acceptableDNList
280 */
281 OSStatus
282 parseTrustedKeychain (SSLContextRef ctx,
283 KCRef keyChainRef)
284 {
285 CFMutableArrayRef kcCerts = NULL; /* all certs in one keychain */
286 uint32 numGoodCerts = 0; /* # of good root certs */
287 CSSM_DATA_PTR certData = NULL; /* array of CSSM_DATAs */
288 CFIndex certDex; /* index into kcCerts */
289 CFIndex certsPerKc; /* # of certs in this KC */
290 OSStatus ortn;
291 KCItemRef kcItem; /* one cert */
292 Boolean goodCert;
293
294 CASSERT(ctx != NULL);
295 if(keyChainRef == NULL) {
296 return paramErr;
297 }
298
299 ortn = KCFindX509Certificates(keyChainRef,
300 NULL, // name, XXX
301 NULL, // emailAddress, XXX
302 kCertSearchAny, // options
303 &kcCerts); // results
304 switch(ortn) {
305 case noErr:
306 break; // proceed
307 case errKCItemNotFound:
308 return noErr; // no certs; done
309 default:
310 errorLog1("parseTrustedKeychains: KCFindX509Certificates returned %d\n",
311 ortn);
312 return ortn;
313 }
314 if(kcCerts == NULL) {
315 dprintf0("parseTrustedKeychains: no certs in KC\n");
316 return noErr;
317 }
318
319 /* Note kcCerts must be released on any exit, successful or
320 * otherwise. */
321
322 certsPerKc = CFArrayGetCount(kcCerts);
323
324 /*
325 * This array gets allocd locally; we'll add it to
326 * ctx->trustedCerts when we're done.
327 */
328 certData = sslMalloc(certsPerKc * sizeof(CSSM_DATA));
329 if(certData == NULL) {
330 ortn = memFullErr;
331 goto errOut;
332 }
333 memset(certData, 0, certsPerKc * sizeof(CSSM_DATA));
334
335 /*
336 * Build up local certData one root cert at a time.
337 * Some certs might not pass muster, hence the numGoodCerts
338 * which may or may not increment each time thru.
339 */
340 for(certDex=0; certDex<certsPerKc; certDex++) {
341 kcItem = (KCItemRef)CFArrayGetValueAtIndex(kcCerts, certDex);
342 if(kcItem == NULL) {
343 errorLog0("parseTrustedKeychains: CF error 1\n");
344 ortn = errSSLInternal;
345 goto errOut;
346 }
347 if(!KCIsRootCertificate(kcItem)) {
348 /* not root, OK, skip to next cert */
349 dprintf1("parseTrustedKeychains: cert %d NOT ROOT\n", certDex);
350 continue;
351 }
352 ortn = addCertData(ctx,
353 kcItem,
354 &certData[numGoodCerts],
355 &goodCert);
356 if(ortn) {
357 goto errOut;
358 }
359 if(goodCert) {
360 /* added valid root to certData */
361 numGoodCerts++;
362 }
363 } /* for each cert in kcCerts */
364
365 #if SSL_DEBUG
366 verifyTrustedRoots(ctx, certData, numGoodCerts);
367 #endif
368
369 /* Realloc ctx->trustedCerts, add new root certs */
370 ctx->trustedCerts = sslRealloc(ctx->trustedCerts,
371 ctx->numTrustedCerts * sizeof(CSSM_DATA),
372 (ctx->numTrustedCerts + numGoodCerts) * sizeof(CSSM_DATA));
373 if(ctx->trustedCerts == NULL) {
374 ortn = memFullErr;
375 goto errOut;
376 }
377 for(certDex=0; certDex<numGoodCerts; certDex++) {
378 ctx->trustedCerts[ctx->numTrustedCerts + certDex] = certData[certDex];
379 }
380 ctx->numTrustedCerts += numGoodCerts;
381 ortn = noErr;
382
383 #if SSL_DEBUG
384 verifyTrustedRoots(ctx, ctx->trustedCerts, ctx->numTrustedCerts);
385 #endif
386
387 errOut:
388 sslFree(certData);
389 if(kcCerts != NULL) {
390 CFRelease(kcCerts);
391 }
392 return ortn;
393 }
394
395 /*
396 * Given a cert as a KCItemRef:
397 * -- verify that the cert self-verifies
398 * -- add its DER-encoded data *certData.
399 * -- Add its subjectName to acceptableDNList.
400 * -- If all is well, return True in *goodCert.
401 *
402 * The actual CSSM_DATA.Data is mallocd via CSSM_Malloc.
403 */
404 static OSStatus
405 addCertData(
406 SSLContext *ctx,
407 KCItemRef kcItem,
408 CSSM_DATA_PTR certData,
409 Boolean *goodCert) /* RETURNED */
410 {
411 UInt32 certSize;
412 OSStatus ortn;
413 SSLErr srtn;
414 CSSM_BOOL subjectExpired;
415 CSSM_DATA_PTR dnData;
416
417 CASSERT(ctx != NULL);
418 CASSERT(certData != NULL);
419 CASSERT(kcItem != NULL);
420 CASSERT(goodCert != NULL);
421
422 *goodCert = false;
423
424 /* how big is the cert? */
425 ortn = KCGetData (kcItem, 0, NULL, &certSize);
426 if(ortn != noErr) {
427 errorLog1("addCertData: KCGetData(1) returned %d\n", ortn);
428 return ortn;
429 }
430
431 /* Allocate the buffer. */
432 srtn = stSetUpCssmData(certData, certSize);
433 if(srtn) {
434 return sslErrToOsStatus(srtn);
435 }
436
437 /* Get the data. */
438 ortn = KCGetData (kcItem, certSize, certData->Data, &certSize);
439 if(ortn) {
440 errorLog1("addCertData: KCGetData(2) returned %d\n", ortn);
441 stFreeCssmData(certData, CSSM_FALSE);
442 return ortn;
443 }
444
445 /*
446 * Do actual cert verify, which
447 * KCIsRootCertificate does not do. A failure isn't
448 * fatal; we just don't add the cert to the array in
449 * that case.
450 *
451 * FIXME - we assume here that our common cspHand can
452 * do this cert verify; if not, we have some API work to
453 * do (to let the caller specify which CSP to use with
454 * trusted certs).
455 */
456 if(!sslVerifyCert(ctx,
457 certData,
458 certData,
459 ctx->cspHand,
460 &subjectExpired)) {
461 dprintf0("addCertData: cert does not self-verify!\n");
462 stFreeCssmData(certData, CSSM_FALSE);
463 return noErr;
464 }
465
466 /* Add this cert's subject name to (poss. existing) acceptableDNList */
467 dnData = sslGetCertSubjectName(ctx, certData);
468 if(dnData) {
469 DNListElem *dn = sslMalloc(sizeof(DNListElem));
470 if(dn == NULL) {
471 return memFullErr;
472 }
473 dn->next = ctx->acceptableDNList;
474 ctx->acceptableDNList = dn;
475
476 /* move actual data to dn; free the CSSM_DATA struct (must be
477 * via CSSM_Free()!) */
478 CSSM_TO_SSLBUF(dnData, &dn->derDN);
479 sslFree(dnData);
480 }
481
482 *goodCert = true;
483 return noErr;
484 }
485
486 /*
487 * Given a newly encountered root cert (obtained from a peer's cert chain),
488 * add it to newRootCertKc if the user so allows, and if so, add it to
489 * trustedCerts.
490 */
491 SSLErr
492 sslAddNewRoot(
493 SSLContext *ctx,
494 const CSSM_DATA_PTR rootCert)
495 {
496 KCRef defaultKc;
497 Boolean bDefaultKcExists;
498 KCItemRef certRef = NULL;
499 OSStatus ortn;
500 CSSM_DATA_PTR newTrustee;
501 SSLErr serr;
502
503 CASSERT(ctx != NULL);
504 CASSERT(rootCert != NULL);
505 CASSERT(ctx->newRootCertKc != NULL); /* caller verifies this */
506
507 /*
508 * Get default KC, temporarily set new default.
509 */
510 ortn = KCGetDefaultKeychain(&defaultKc);
511 if(ortn) {
512 bDefaultKcExists = false;
513 }
514 else {
515 bDefaultKcExists = true;
516 }
517 ortn = KCSetDefaultKeychain(ctx->newRootCertKc);
518 if(ortn) {
519 errorLog1("sslAddNewRoot: KCSetDefaultKeychain returned %d\n", ortn);
520 return SSLUnknownRootCert;
521 }
522
523 /*
524 * Add cert to newRootCertKc. This may well fail due to user
525 * interaction ("Do you want to add this root cert...?").
526 */
527 ortn = KCAddX509Certificate(rootCert->Data, rootCert->Length, &certRef);
528
529 /* restore default KC in any case */
530 if(bDefaultKcExists) {
531 KCSetDefaultKeychain(defaultKc);
532 }
533 if(ortn) {
534 dprintf1("sslAddNewRoot: KCAddX509Certificate returned %d\n", ortn);
535 return SSLUnknownRootCert;
536 }
537
538 /*
539 * OK, user accepted new root. Now add to our private stash of
540 * trusted roots. Realloc the whole pile...
541 */
542 ctx->trustedCerts = (CSSM_DATA_PTR)sslRealloc(ctx->trustedCerts,
543 (ctx->numTrustedCerts * sizeof(CSSM_DATA)),
544 ((ctx->numTrustedCerts + 1) * sizeof(CSSM_DATA)));
545 if(ctx->trustedCerts == NULL) {
546 return SSLMemoryErr;
547 }
548
549 /* Now add a copy of the new root. */
550 newTrustee = &ctx->trustedCerts[ctx->numTrustedCerts];
551 newTrustee->Data = NULL;
552 newTrustee->Length = 0;
553 serr = stSetUpCssmData(newTrustee, rootCert->Length);
554 if(serr) {
555 return serr;
556 }
557 BlockMove(rootCert->Data, newTrustee->Data, rootCert->Length);
558 (ctx->numTrustedCerts)++;
559 return SSLNoErr;
560 }
561
562 #endif /* ST_KEYCHAIN_ENABLE */
563