2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
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
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.
22 Contains: Apple Keychain routines
24 Written by: Doug Mitchell
26 Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved.
31 #include "sslContext.h"
32 #include "sslMemory.h"
33 #include "appleCdsa.h"
35 #include "sslKeychain.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>
49 #if ST_MANAGES_TRUSTED_ROOTS
54 CSSM_DATA_PTR certData
,
55 Boolean
*goodCert
); /* RETURNED */
56 #endif /* ST_MANAGES_TRUSTED_ROOTS */
59 * Given an array of certs (as SecIdentityRefs, specified by caller
60 * in SSLSetCertificate or SSLSetEncryptionCertificate) and a
61 * destination SSLCertificate:
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
69 /* Convert a SecCertificateRef to an SSLCertificate * */
70 static OSStatus
secCertToSslCert(
72 SecCertificateRef certRef
,
73 SSLCertificate
**sslCert
)
75 CSSM_DATA certData
; // struct is transient, referent owned by
78 SSLCertificate
*thisSslCert
= NULL
;
80 ortn
= SecCertificateGetData(certRef
, &certData
);
82 sslErrorLog("SecCertificateGetData() returned %d\n", (int)ortn
);
86 thisSslCert
= (SSLCertificate
*)sslMalloc(sizeof(SSLCertificate
));
87 if(thisSslCert
== NULL
) {
90 if(SSLAllocBuffer(thisSslCert
->derCert
, certData
.Length
,
94 memcpy(thisSslCert
->derCert
.data
, certData
.Data
, certData
.Length
);
95 thisSslCert
->derCert
.length
= certData
.Length
;
96 *sslCert
= thisSslCert
;
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
110 SecKeychainRef
*privKeyRef
) /* &ctx->signingKeyRef, etc. */
113 #endif /* ST_KC_KEYS_NEED_REF */
117 SSLCertificate
*certChain
= NULL
;
118 SSLCertificate
*thisSslCert
;
119 SecKeychainRef kcRef
;
121 SecIdentityRef identity
;
122 SecCertificateRef certRef
;
125 CSSM_CL_HANDLE clHand
; // carefully derive from a SecCertificateRef
129 assert(destCert
!= NULL
); /* though its referent may be NULL */
130 assert(pubKey
!= NULL
);
131 assert(privKey
!= NULL
);
132 assert(cspHand
!= NULL
);
134 sslDeleteCertificateChain(*destCert
, ctx
);
141 sslErrorLog("parseIncomingCerts: NULL incoming cert array\n");
142 return errSSLBadCert
;
144 numCerts
= CFArrayGetCount(certs
);
146 sslErrorLog("parseIncomingCerts: empty incoming cert array\n");
147 return errSSLBadCert
;
151 * Certs[0] is an SecIdentityRef from which we extract subject cert,
152 * privKey, pubKey, and cspHand.
154 * 1. ensure the first element is a SecIdentityRef.
156 identity
= (SecIdentityRef
)CFArrayGetValueAtIndex(certs
, 0);
157 if(identity
== NULL
) {
158 sslErrorLog("parseIncomingCerts: bad cert array (1)\n");
161 if(CFGetTypeID(identity
) != SecIdentityGetTypeID()) {
162 sslErrorLog("parseIncomingCerts: bad cert array (2)\n");
167 * 2. Extract cert, keys, CSP handle and convert to local format.
169 ortn
= SecIdentityCopyCertificate(identity
, &certRef
);
171 sslErrorLog("parseIncomingCerts: bad cert array (3)\n");
174 ortn
= secCertToSslCert(ctx
, certRef
, &thisSslCert
);
176 sslErrorLog("parseIncomingCerts: bad cert array (4)\n");
179 /* enqueue onto head of cert chain */
180 thisSslCert
->next
= certChain
;
181 certChain
= thisSslCert
;
183 /* fetch private key from identity */
184 ortn
= SecIdentityCopyPrivateKey(identity
, &keyRef
);
186 sslErrorLog("parseIncomingCerts: SecIdentityCopyPrivateKey err %d\n",
190 ortn
= SecKeyGetCSSMKey(keyRef
, (const CSSM_KEY
**)privKey
);
192 sslErrorLog("parseIncomingCerts: SecKeyGetCSSMKey err %d\n",
196 /* FIXME = release keyRef? */
198 /* obtain public key from cert */
199 ortn
= SecCertificateGetCLHandle(certRef
, &clHand
);
201 sslErrorLog("parseIncomingCerts: SecCertificateGetCLHandle err %d\n",
205 certData
.Data
= thisSslCert
->derCert
.data
;
206 certData
.Length
= thisSslCert
->derCert
.length
;
207 crtn
= CSSM_CL_CertGetKeyInfo(clHand
, &certData
, pubKey
);
209 sslErrorLog("parseIncomingCerts: CSSM_CL_CertGetKeyInfo err\n");
210 return (OSStatus
)crtn
;
213 /* obtain keychain from key, CSP handle from keychain */
214 ortn
= SecKeychainItemCopyKeychain((SecKeychainItemRef
)keyRef
, &kcRef
);
216 sslErrorLog("parseIncomingCerts: SecKeychainItemCopyKeychain err %d\n",
220 ortn
= SecKeychainGetCSPHandle(kcRef
, cspHand
);
222 sslErrorLog("parseIncomingCerts: SecKeychainGetCSPHandle err %d\n",
227 /* OK, that's the subject cert. Fetch optional remaining certs. */
229 * Convert: CFArray of SecCertificateRefs --> chain of SSLCertificates.
230 * Incoming certs have root last; SSLCertificate chain has root
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");
239 if(CFGetTypeID(certRef
) != SecCertificateGetTypeID()) {
240 sslErrorLog("parseIncomingCerts: bad cert array (6)\n");
244 /* Extract cert, convert to local format.
246 ortn
= secCertToSslCert(ctx
, certRef
, &thisSslCert
);
248 sslErrorLog("parseIncomingCerts: bad cert array (7)\n");
251 /* enqueue onto head of cert chain */
252 thisSslCert
->next
= certChain
;
253 certChain
= thisSslCert
;
256 /* validate the whole mess, skipping host name verify */
257 ortn
= sslVerifyCertChain(ctx
, *certChain
, false);
263 *destCert
= certChain
;
267 /* free certChain, everything in it, other vars, return ortn */
268 sslDeleteCertificateChain(certChain
, ctx
);
269 /* FIXME - anything else? */
274 * Add Apple built-in root certs to ctx->trustedCerts.
276 OSStatus
addBuiltInCerts (SSLContextRef ctx
)
278 #if ST_MANAGES_TRUSTED_ROOTS
282 ortn
= KCDispatch(kKCGetRootCertificateKeychain
, &kc
);
284 sslErrorLog("KCDispatch(kKCGetRootCertificateKeychain) returned %d\n",
288 return parseTrustedKeychain(ctx
, kc
);
290 /* nothing for now */
292 #endif /* ST_MANAGES_TRUSTED_ROOTS */
295 #if ST_MANAGES_TRUSTED_ROOTS
298 * Given an open Keychain:
299 * -- Get raw cert data, add to array of CSSM_DATAs in
301 * -- verify that each of these is a valid (self-verifying)
303 * -- add each subject name to acceptableDNList
306 parseTrustedKeychain (SSLContextRef ctx
,
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 */
315 KCItemRef kcItem
; /* one cert */
319 if(keyChainRef
== NULL
) {
323 ortn
= KCFindX509Certificates(keyChainRef
,
325 NULL
, // emailAddress, XXX
326 kCertSearchAny
, // options
327 &kcCerts
); // results
331 case errKCItemNotFound
:
332 return noErr
; // no certs; done
334 sslErrorLog("parseTrustedKeychains: KCFindX509Certificates returned %d\n",
338 if(kcCerts
== NULL
) {
339 sslErrorLog("parseTrustedKeychains: no certs in KC\n");
343 /* Note kcCerts must be released on any exit, successful or
346 certsPerKc
= CFArrayGetCount(kcCerts
);
349 * This array gets allocd locally; we'll add it to
350 * ctx->trustedCerts when we're done.
352 certData
= sslMalloc(certsPerKc
* sizeof(CSSM_DATA
));
353 if(certData
== NULL
) {
357 memset(certData
, 0, certsPerKc
* sizeof(CSSM_DATA
));
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.
364 for(certDex
=0; certDex
<certsPerKc
; certDex
++) {
365 kcItem
= (KCItemRef
)CFArrayGetValueAtIndex(kcCerts
, certDex
);
367 sslErrorLog("parseTrustedKeychains: CF error 1\n");
368 ortn
= errSSLInternal
;
371 if(!KCIsRootCertificate(kcItem
)) {
372 /* not root, OK, skip to next cert */
373 sslErrorLog("parseTrustedKeychains: cert %d NOT ROOT\n",
377 ortn
= addCertData(ctx
,
379 &certData
[numGoodCerts
],
385 /* added valid root to certData */
388 } /* for each cert in kcCerts */
391 verifyTrustedRoots(ctx
, certData
, numGoodCerts
);
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
) {
402 for(certDex
=0; certDex
<numGoodCerts
; certDex
++) {
403 ctx
->trustedCerts
[ctx
->numTrustedCerts
+ certDex
] = certData
[certDex
];
405 ctx
->numTrustedCerts
+= numGoodCerts
;
409 verifyTrustedRoots(ctx
, ctx
->trustedCerts
, ctx
->numTrustedCerts
);
414 if(kcCerts
!= NULL
) {
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.
427 * The actual CSSM_DATA.Data is mallocd via CSSM_Malloc.
433 CSSM_DATA_PTR certData
,
434 Boolean
*goodCert
) /* RETURNED */
438 CSSM_BOOL subjectExpired
;
441 assert(certData
!= NULL
);
442 assert(kcItem
!= NULL
);
443 assert(goodCert
!= NULL
);
447 /* how big is the cert? */
448 ortn
= KCGetData (kcItem
, 0, NULL
, &certSize
);
450 sslErrorLog("addCertData: KCGetData(1) returned %d\n", ortn
);
454 /* Allocate the buffer. */
455 ortn
= stSetUpCssmData(certData
, certSize
);
461 ortn
= KCGetData (kcItem
, certSize
, certData
->Data
, &certSize
);
463 sslErrorLog("addCertData: KCGetData(2) returned %d\n", ortn
);
464 stFreeCssmData(certData
, CSSM_FALSE
);
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
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
479 if(!sslVerifyCert(ctx
,
484 sslErrorLog("addCertData: cert does not self-verify!\n");
485 stFreeCssmData(certData
, CSSM_FALSE
);
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
);
493 DNListElem
*dn
= sslMalloc(sizeof(DNListElem
));
497 dn
->next
= ctx
->acceptableDNList
;
498 ctx
->acceptableDNList
= dn
;
500 /* move actual data to dn; free the CSSM_DATA struct (must be
501 * via CSSM_Free()!) */
502 CSSM_TO_SSLBUF(dnData
, &dn
->derDN
);
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
518 const CSSM_DATA_PTR rootCert
)
521 Boolean bDefaultKcExists
;
522 KCItemRef certRef
= NULL
;
524 CSSM_DATA_PTR newTrustee
;
528 assert(rootCert
!= NULL
);
529 assert(ctx
->newRootCertKc
!= NULL
); /* caller verifies this */
532 * Get default KC, temporarily set new default.
534 ortn
= KCGetDefaultKeychain(&defaultKc
);
536 bDefaultKcExists
= false;
539 bDefaultKcExists
= true;
541 ortn
= KCSetDefaultKeychain(ctx
->newRootCertKc
);
543 sslErrorLog("sslAddNewRoot: KCSetDefaultKeychain returned %d\n", ortn
);
544 return errSSLUnknownRootCert
;
548 * Add cert to newRootCertKc. This may well fail due to user
549 * interaction ("Do you want to add this root cert...?").
551 ortn
= KCAddX509Certificate(rootCert
->Data
, rootCert
->Length
, &certRef
);
553 /* restore default KC in any case */
554 if(bDefaultKcExists
) {
555 KCSetDefaultKeychain(defaultKc
);
558 sslErrorLog("sslAddNewRoot: KCAddX509Certificate returned %d\n", ortn
);
559 return errSSLUnknownRootCert
;
563 * OK, user accepted new root. Now add to our private stash of
564 * trusted roots. Realloc the whole pile...
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
) {
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
);
581 BlockMove(rootCert
->Data
, newTrustee
->Data
, rootCert
->Length
);
582 (ctx
->numTrustedCerts
)++;
586 #endif /* ST_MANAGES_TRUSTED_ROOTS */