]> git.saurik.com Git - apple/security.git/blob - SecureTransport/sslKeychain.cpp
Security-54.1.9.tar.gz
[apple/security.git] / SecureTransport / sslKeychain.cpp
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
25
26 Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved.
27
28 */
29
30 #include "ssl.h"
31 #include "sslContext.h"
32 #include "sslMemory.h"
33 #include "appleCdsa.h"
34 #include "sslDebug.h"
35 #include "sslKeychain.h"
36 #include "sslUtils.h"
37 #include <string.h>
38 #include <assert.h>
39 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
40 #include <Security/cssm.h>
41 /* these are to be replaced by Security/Security.h */
42 #include <Security/SecCertificate.h>
43 #include <Security/SecKeychainItem.h>
44 #include <Security/SecKeychain.h>
45 #include <Security/SecIdentity.h>
46 #include <Security/SecIdentitySearch.h>
47 #include <Security/SecKey.h>
48
49 #if ST_MANAGES_TRUSTED_ROOTS
50 static OSStatus
51 addCertData(
52 SSLContext *ctx,
53 KCItemRef kcItem,
54 CSSM_DATA_PTR certData,
55 Boolean *goodCert); /* RETURNED */
56 #endif /* ST_MANAGES_TRUSTED_ROOTS */
57
58 /*
59 * Given an array of certs (as SecIdentityRefs, specified by caller
60 * in SSLSetCertificate or SSLSetEncryptionCertificate) and a
61 * destination SSLCertificate:
62 *
63 * -- free destCerts if we have any
64 * -- Get raw cert data, convert to array of SSLCertificates in *destCert
65 * -- validate cert chain
66 * -- get pub, priv keys from certRef[0], store in *pubKey, *privKey
67 */
68
69 /* Convert a SecCertificateRef to an SSLCertificate * */
70 static OSStatus secCertToSslCert(
71 SSLContext *ctx,
72 SecCertificateRef certRef,
73 SSLCertificate **sslCert)
74 {
75 CSSM_DATA certData; // struct is transient, referent owned by
76 // Sec layer
77 OSStatus ortn;
78 SSLCertificate *thisSslCert = NULL;
79
80 ortn = SecCertificateGetData(certRef, &certData);
81 if(ortn) {
82 sslErrorLog("SecCertificateGetData() returned %d\n", (int)ortn);
83 return ortn;
84 }
85
86 thisSslCert = (SSLCertificate *)sslMalloc(sizeof(SSLCertificate));
87 if(thisSslCert == NULL) {
88 return memFullErr;
89 }
90 if(SSLAllocBuffer(thisSslCert->derCert, certData.Length,
91 ctx)) {
92 return memFullErr;
93 }
94 memcpy(thisSslCert->derCert.data, certData.Data, certData.Length);
95 thisSslCert->derCert.length = certData.Length;
96 *sslCert = thisSslCert;
97 return noErr;
98 }
99
100 OSStatus
101 parseIncomingCerts(
102 SSLContext *ctx,
103 CFArrayRef certs,
104 SSLCertificate **destCert, /* &ctx->{localCert,encryptCert} */
105 CSSM_KEY_PTR *pubKey, /* &ctx->signingPubKey, etc. */
106 CSSM_KEY_PTR *privKey, /* &ctx->signingPrivKey, etc. */
107 CSSM_CSP_HANDLE *cspHand /* &ctx->signingKeyCsp, etc. */
108 #if ST_KC_KEYS_NEED_REF
109 ,
110 SecKeychainRef *privKeyRef) /* &ctx->signingKeyRef, etc. */
111 #else
112 )
113 #endif /* ST_KC_KEYS_NEED_REF */
114 {
115 CFIndex numCerts;
116 CFIndex cert;
117 SSLCertificate *certChain = NULL;
118 SSLCertificate *thisSslCert;
119 SecKeychainRef kcRef;
120 OSStatus ortn;
121 SecIdentityRef identity;
122 SecCertificateRef certRef;
123 SecKeyRef keyRef;
124 CSSM_DATA certData;
125 CSSM_CL_HANDLE clHand; // carefully derive from a SecCertificateRef
126 CSSM_RETURN crtn;
127
128 assert(ctx != NULL);
129 assert(destCert != NULL); /* though its referent may be NULL */
130 assert(pubKey != NULL);
131 assert(privKey != NULL);
132 assert(cspHand != NULL);
133
134 sslDeleteCertificateChain(*destCert, ctx);
135 *destCert = NULL;
136 *pubKey = NULL;
137 *privKey = NULL;
138 *cspHand = 0;
139
140 if(certs == NULL) {
141 sslErrorLog("parseIncomingCerts: NULL incoming cert array\n");
142 return errSSLBadCert;
143 }
144 numCerts = CFArrayGetCount(certs);
145 if(numCerts == 0) {
146 sslErrorLog("parseIncomingCerts: empty incoming cert array\n");
147 return errSSLBadCert;
148 }
149
150 /*
151 * Certs[0] is an SecIdentityRef from which we extract subject cert,
152 * privKey, pubKey, and cspHand.
153 *
154 * 1. ensure the first element is a SecIdentityRef.
155 */
156 identity = (SecIdentityRef)CFArrayGetValueAtIndex(certs, 0);
157 if(identity == NULL) {
158 sslErrorLog("parseIncomingCerts: bad cert array (1)\n");
159 return paramErr;
160 }
161 if(CFGetTypeID(identity) != SecIdentityGetTypeID()) {
162 sslErrorLog("parseIncomingCerts: bad cert array (2)\n");
163 return paramErr;
164 }
165
166 /*
167 * 2. Extract cert, keys, CSP handle and convert to local format.
168 */
169 ortn = SecIdentityCopyCertificate(identity, &certRef);
170 if(ortn) {
171 sslErrorLog("parseIncomingCerts: bad cert array (3)\n");
172 return ortn;
173 }
174 ortn = secCertToSslCert(ctx, certRef, &thisSslCert);
175 if(ortn) {
176 sslErrorLog("parseIncomingCerts: bad cert array (4)\n");
177 return ortn;
178 }
179 /* enqueue onto head of cert chain */
180 thisSslCert->next = certChain;
181 certChain = thisSslCert;
182
183 /* fetch private key from identity */
184 ortn = SecIdentityCopyPrivateKey(identity, &keyRef);
185 if(ortn) {
186 sslErrorLog("parseIncomingCerts: SecIdentityCopyPrivateKey err %d\n",
187 (int)ortn);
188 return ortn;
189 }
190 ortn = SecKeyGetCSSMKey(keyRef, (const CSSM_KEY **)privKey);
191 if(ortn) {
192 sslErrorLog("parseIncomingCerts: SecKeyGetCSSMKey err %d\n",
193 (int)ortn);
194 return ortn;
195 }
196 /* FIXME = release keyRef? */
197
198 /* obtain public key from cert */
199 ortn = SecCertificateGetCLHandle(certRef, &clHand);
200 if(ortn) {
201 sslErrorLog("parseIncomingCerts: SecCertificateGetCLHandle err %d\n",
202 (int)ortn);
203 return ortn;
204 }
205 certData.Data = thisSslCert->derCert.data;
206 certData.Length = thisSslCert->derCert.length;
207 crtn = CSSM_CL_CertGetKeyInfo(clHand, &certData, pubKey);
208 if(crtn) {
209 sslErrorLog("parseIncomingCerts: CSSM_CL_CertGetKeyInfo err\n");
210 return (OSStatus)crtn;
211 }
212
213 /* obtain keychain from key, CSP handle from keychain */
214 ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)keyRef, &kcRef);
215 if(ortn) {
216 sslErrorLog("parseIncomingCerts: SecKeychainItemCopyKeychain err %d\n",
217 (int)ortn);
218 return ortn;
219 }
220 ortn = SecKeychainGetCSPHandle(kcRef, cspHand);
221 if(ortn) {
222 sslErrorLog("parseIncomingCerts: SecKeychainGetCSPHandle err %d\n",
223 (int)ortn);
224 return ortn;
225 }
226
227 /* OK, that's the subject cert. Fetch optional remaining certs. */
228 /*
229 * Convert: CFArray of SecCertificateRefs --> chain of SSLCertificates.
230 * Incoming certs have root last; SSLCertificate chain has root
231 * first.
232 */
233 for(cert=1; cert<numCerts; cert++) {
234 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certs, cert);
235 if(certRef == NULL) {
236 sslErrorLog("parseIncomingCerts: bad cert array (5)\n");
237 return paramErr;
238 }
239 if(CFGetTypeID(certRef) != SecCertificateGetTypeID()) {
240 sslErrorLog("parseIncomingCerts: bad cert array (6)\n");
241 return paramErr;
242 }
243
244 /* Extract cert, convert to local format.
245 */
246 ortn = secCertToSslCert(ctx, certRef, &thisSslCert);
247 if(ortn) {
248 sslErrorLog("parseIncomingCerts: bad cert array (7)\n");
249 return ortn;
250 }
251 /* enqueue onto head of cert chain */
252 thisSslCert->next = certChain;
253 certChain = thisSslCert;
254 }
255
256 /* validate the whole mess, skipping host name verify */
257 ortn = sslVerifyCertChain(ctx, *certChain, false);
258 if(ortn) {
259 goto errOut;
260 }
261
262 /* SUCCESS */
263 *destCert = certChain;
264 return noErr;
265
266 errOut:
267 /* free certChain, everything in it, other vars, return ortn */
268 sslDeleteCertificateChain(certChain, ctx);
269 /* FIXME - anything else? */
270 return ortn;
271 }
272
273 /*
274 * Add Apple built-in root certs to ctx->trustedCerts.
275 */
276 OSStatus addBuiltInCerts (SSLContextRef ctx)
277 {
278 #if ST_MANAGES_TRUSTED_ROOTS
279 OSStatus ortn;
280 KCRef kc = nil;
281
282 ortn = KCDispatch(kKCGetRootCertificateKeychain, &kc);
283 if(ortn) {
284 sslErrorLog("KCDispatch(kKCGetRootCertificateKeychain) returned %d\n",
285 ortn);
286 return ortn;
287 }
288 return parseTrustedKeychain(ctx, kc);
289 #else
290 /* nothing for now */
291 return noErr;
292 #endif /* ST_MANAGES_TRUSTED_ROOTS */
293 }
294
295 #if ST_MANAGES_TRUSTED_ROOTS
296
297 /*
298 * Given an open Keychain:
299 * -- Get raw cert data, add to array of CSSM_DATAs in
300 * ctx->trustedCerts
301 * -- verify that each of these is a valid (self-verifying)
302 * root cert
303 * -- add each subject name to acceptableDNList
304 */
305 OSStatus
306 parseTrustedKeychain (SSLContextRef ctx,
307 KCRef keyChainRef)
308 {
309 CFMutableArrayRef kcCerts = NULL; /* all certs in one keychain */
310 uint32 numGoodCerts = 0; /* # of good root certs */
311 CSSM_DATA_PTR certData = NULL; /* array of CSSM_DATAs */
312 CFIndex certDex; /* index into kcCerts */
313 CFIndex certsPerKc; /* # of certs in this KC */
314 OSStatus ortn;
315 KCItemRef kcItem; /* one cert */
316 Boolean goodCert;
317
318 assert(ctx != NULL);
319 if(keyChainRef == NULL) {
320 return paramErr;
321 }
322
323 ortn = KCFindX509Certificates(keyChainRef,
324 NULL, // name, XXX
325 NULL, // emailAddress, XXX
326 kCertSearchAny, // options
327 &kcCerts); // results
328 switch(ortn) {
329 case noErr:
330 break; // proceed
331 case errKCItemNotFound:
332 return noErr; // no certs; done
333 default:
334 sslErrorLog("parseTrustedKeychains: KCFindX509Certificates returned %d\n",
335 ortn);
336 return ortn;
337 }
338 if(kcCerts == NULL) {
339 sslErrorLog("parseTrustedKeychains: no certs in KC\n");
340 return noErr;
341 }
342
343 /* Note kcCerts must be released on any exit, successful or
344 * otherwise. */
345
346 certsPerKc = CFArrayGetCount(kcCerts);
347
348 /*
349 * This array gets allocd locally; we'll add it to
350 * ctx->trustedCerts when we're done.
351 */
352 certData = sslMalloc(certsPerKc * sizeof(CSSM_DATA));
353 if(certData == NULL) {
354 ortn = memFullErr;
355 goto errOut;
356 }
357 memset(certData, 0, certsPerKc * sizeof(CSSM_DATA));
358
359 /*
360 * Build up local certData one root cert at a time.
361 * Some certs might not pass muster, hence the numGoodCerts
362 * which may or may not increment each time thru.
363 */
364 for(certDex=0; certDex<certsPerKc; certDex++) {
365 kcItem = (KCItemRef)CFArrayGetValueAtIndex(kcCerts, certDex);
366 if(kcItem == NULL) {
367 sslErrorLog("parseTrustedKeychains: CF error 1\n");
368 ortn = errSSLInternal;
369 goto errOut;
370 }
371 if(!KCIsRootCertificate(kcItem)) {
372 /* not root, OK, skip to next cert */
373 sslErrorLog("parseTrustedKeychains: cert %d NOT ROOT\n",
374 certDex);
375 continue;
376 }
377 ortn = addCertData(ctx,
378 kcItem,
379 &certData[numGoodCerts],
380 &goodCert);
381 if(ortn) {
382 goto errOut;
383 }
384 if(goodCert) {
385 /* added valid root to certData */
386 numGoodCerts++;
387 }
388 } /* for each cert in kcCerts */
389
390 #if SSL_DEBUG
391 verifyTrustedRoots(ctx, certData, numGoodCerts);
392 #endif
393
394 /* Realloc ctx->trustedCerts, add new root certs */
395 ctx->trustedCerts = sslRealloc(ctx->trustedCerts,
396 ctx->numTrustedCerts * sizeof(CSSM_DATA),
397 (ctx->numTrustedCerts + numGoodCerts) * sizeof(CSSM_DATA));
398 if(ctx->trustedCerts == NULL) {
399 ortn = memFullErr;
400 goto errOut;
401 }
402 for(certDex=0; certDex<numGoodCerts; certDex++) {
403 ctx->trustedCerts[ctx->numTrustedCerts + certDex] = certData[certDex];
404 }
405 ctx->numTrustedCerts += numGoodCerts;
406 ortn = noErr;
407
408 #if SSL_DEBUG
409 verifyTrustedRoots(ctx, ctx->trustedCerts, ctx->numTrustedCerts);
410 #endif
411
412 errOut:
413 sslFree(certData);
414 if(kcCerts != NULL) {
415 CFRelease(kcCerts);
416 }
417 return ortn;
418 }
419
420 /*
421 * Given a (supposedly) root cert as a KCItemRef:
422 * -- verify that the cert self-verifies
423 * -- add its DER-encoded data *certData.
424 * -- Add its subjectName to acceptableDNList.
425 * -- If all is well, return True in *goodCert.
426 *
427 * The actual CSSM_DATA.Data is mallocd via CSSM_Malloc.
428 */
429 static OSStatus
430 addCertData(
431 SSLContext *ctx,
432 KCItemRef kcItem,
433 CSSM_DATA_PTR certData,
434 Boolean *goodCert) /* RETURNED */
435 {
436 UInt32 certSize;
437 OSStatus ortn;
438 CSSM_BOOL subjectExpired;
439
440 assert(ctx != NULL);
441 assert(certData != NULL);
442 assert(kcItem != NULL);
443 assert(goodCert != NULL);
444
445 *goodCert = false;
446
447 /* how big is the cert? */
448 ortn = KCGetData (kcItem, 0, NULL, &certSize);
449 if(ortn != noErr) {
450 sslErrorLog("addCertData: KCGetData(1) returned %d\n", ortn);
451 return ortn;
452 }
453
454 /* Allocate the buffer. */
455 ortn = stSetUpCssmData(certData, certSize);
456 if(ortn) {
457 return ortn;
458 }
459
460 /* Get the data. */
461 ortn = KCGetData (kcItem, certSize, certData->Data, &certSize);
462 if(ortn) {
463 sslErrorLog("addCertData: KCGetData(2) returned %d\n", ortn);
464 stFreeCssmData(certData, CSSM_FALSE);
465 return ortn;
466 }
467
468 /*
469 * Do actual cert verify, which
470 * KCIsRootCertificate does not do. A failure isn't
471 * fatal; we just don't add the cert to the array in
472 * that case.
473 *
474 * FIXME - we assume here that our common cspHand can
475 * do this cert verify; if not, we have some API work to
476 * do (to let the caller specify which CSP to use with
477 * trusted certs).
478 */
479 if(!sslVerifyCert(ctx,
480 certData,
481 certData,
482 ctx->cspHand,
483 &subjectExpired)) {
484 sslErrorLog("addCertData: cert does not self-verify!\n");
485 stFreeCssmData(certData, CSSM_FALSE);
486 return noErr;
487 }
488
489 /* FIXME - needs update for MANAGES_TRUSTED_ROOTS */
490 /* Add this cert's subject name to (poss. existing) acceptableDNList */
491 CSSM_DATA_PTR dnData = sslGetCertSubjectName(ctx, certData);
492 if(dnData) {
493 DNListElem *dn = sslMalloc(sizeof(DNListElem));
494 if(dn == NULL) {
495 return memFullErr;
496 }
497 dn->next = ctx->acceptableDNList;
498 ctx->acceptableDNList = dn;
499
500 /* move actual data to dn; free the CSSM_DATA struct (must be
501 * via CSSM_Free()!) */
502 CSSM_TO_SSLBUF(dnData, &dn->derDN);
503 sslFree(dnData);
504 }
505
506 *goodCert = true;
507 return noErr;
508 }
509
510 /*
511 * Given a newly encountered root cert (obtained from a peer's cert chain),
512 * add it to newRootCertKc if the user so allows, and if so, add it to
513 * trustedCerts.
514 */
515 OSStatus
516 sslAddNewRoot(
517 SSLContext *ctx,
518 const CSSM_DATA_PTR rootCert)
519 {
520 KCRef defaultKc;
521 Boolean bDefaultKcExists;
522 KCItemRef certRef = NULL;
523 OSStatus ortn;
524 CSSM_DATA_PTR newTrustee;
525 OSStatus serr;
526
527 assert(ctx != NULL);
528 assert(rootCert != NULL);
529 assert(ctx->newRootCertKc != NULL); /* caller verifies this */
530
531 /*
532 * Get default KC, temporarily set new default.
533 */
534 ortn = KCGetDefaultKeychain(&defaultKc);
535 if(ortn) {
536 bDefaultKcExists = false;
537 }
538 else {
539 bDefaultKcExists = true;
540 }
541 ortn = KCSetDefaultKeychain(ctx->newRootCertKc);
542 if(ortn) {
543 sslErrorLog("sslAddNewRoot: KCSetDefaultKeychain returned %d\n", ortn);
544 return errSSLUnknownRootCert;
545 }
546
547 /*
548 * Add cert to newRootCertKc. This may well fail due to user
549 * interaction ("Do you want to add this root cert...?").
550 */
551 ortn = KCAddX509Certificate(rootCert->Data, rootCert->Length, &certRef);
552
553 /* restore default KC in any case */
554 if(bDefaultKcExists) {
555 KCSetDefaultKeychain(defaultKc);
556 }
557 if(ortn) {
558 sslErrorLog("sslAddNewRoot: KCAddX509Certificate returned %d\n", ortn);
559 return errSSLUnknownRootCert;
560 }
561
562 /*
563 * OK, user accepted new root. Now add to our private stash of
564 * trusted roots. Realloc the whole pile...
565 */
566 ctx->trustedCerts = (CSSM_DATA_PTR)sslRealloc(ctx->trustedCerts,
567 (ctx->numTrustedCerts * sizeof(CSSM_DATA)),
568 ((ctx->numTrustedCerts + 1) * sizeof(CSSM_DATA)));
569 if(ctx->trustedCerts == NULL) {
570 return memFullErr;
571 }
572
573 /* Now add a copy of the new root. */
574 newTrustee = &ctx->trustedCerts[ctx->numTrustedCerts];
575 newTrustee->Data = NULL;
576 newTrustee->Length = 0;
577 serr = stSetUpCssmData(newTrustee, rootCert->Length);
578 if(serr) {
579 return serr;
580 }
581 BlockMove(rootCert->Data, newTrustee->Data, rootCert->Length);
582 (ctx->numTrustedCerts)++;
583 return noErr;
584 }
585
586 #endif /* ST_MANAGES_TRUSTED_ROOTS */
587