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, based on Netscape RSARef 3.0
26 Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved.
33 #include "appleCdsa.h"
34 #include "appleGlue.h"
37 #include "sslKeychain.h"
40 #if ST_KEYCHAIN_ENABLE
42 #include <KeychainPriv.h>
43 #endif /* ST_KEYCHAIN_ENABLE */
47 #if ST_KEYCHAIN_ENABLE
52 CSSM_DATA_PTR certData
,
53 Boolean
*goodCert
); /* RETURNED */
56 * Given a KCItemRef: is this item a cert?
59 isItemACert(KCItemRef kcItem
)
62 FourCharCode itemClass
;
66 attr
.tag
= kClassKCItemAttr
;
67 attr
.length
= sizeof(FourCharCode
);
68 attr
.data
= &itemClass
;
70 ortn
= KCGetAttribute (kcItem
, &attr
, &len
);
72 return((itemClass
== kCertificateKCItemClass
) ? true : false);
75 errorLog1("isItemACert: KCGetAttribute returned %d\n", ortn
);
80 #endif /* ST_KEYCHAIN_ENABLE */
82 #if (ST_SERVER_MODE_ENABLE || ST_CLIENT_AUTHENTICATION)
84 * Given an array of certs (as KCItemRefs, specified by caller
85 * in SSLSetCertificate or SSLSetEncryptionCertificate) and a
86 * destination SSLCertificate:
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
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. */
105 SSLCertificate
*certChain
= NULL
;
106 SSLCertificate
*thisSslCert
;
108 SSLBuffer
*derSubjCert
= NULL
;
112 FromItemGetPrivateKeyParams keyParams
= {NULL
, NULL
};
113 FromItemGetKeyInfoParams keyInfo
= {NULL
, NULL
, 0};
114 CSSM_CSP_HANDLE dummyCsp
;
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
);
123 sslDeleteCertificateChain(*destCert
, ctx
);
130 dprintf0("parseIncomingCerts: NULL incoming cert array\n");
131 return errSSLBadCert
;
133 numCerts
= CFArrayGetCount(certs
);
135 dprintf0("parseIncomingCerts: empty incoming cert array\n");
136 return errSSLBadCert
;
140 * Convert: CFArray of KCItemRefs --> chain of SSLCertificates.
141 * Incoming certs have root last; SSLCertificate chain has root
144 for(cert
=0; cert
<numCerts
; cert
++) {
145 kcItem
= (KCItemRef
)CFArrayGetValueAtIndex(certs
, cert
);
147 errorLog0("parseIncomingCerts: bad cert array\n");
150 if(!isItemACert(kcItem
)) {
151 /* client app error, not ours */
156 * OK, cook up an SSLCertificate and its associated SSLBuffer.
157 * First the size of the actual cert data...
159 ortn
= KCGetData(kcItem
, 0, NULL
, &certLen
);
161 errorLog1("parseIncomingCerts: KCGetData(1) returned %d\n", ortn
);
164 thisSslCert
= sslMalloc(sizeof(SSLCertificate
));
165 if(thisSslCert
== NULL
) {
168 if(SSLAllocBuffer(&thisSslCert
->derCert
, certLen
, &ctx
->sysCtx
)) {
172 /* now the data itself */
173 ortn
= KCGetData (kcItem
,
175 thisSslCert
->derCert
.data
,
178 errorLog1("parseIncomingCerts: KCGetData(2) returned %d\n", ortn
);
179 SSLFreeBuffer(&thisSslCert
->derCert
, &ctx
->sysCtx
);
183 /* enqueue onto head of cert chain */
184 thisSslCert
->next
= certChain
;
185 certChain
= thisSslCert
;
187 if(derSubjCert
== NULL
) {
188 /* Save this ptr for obtaining public key */
189 derSubjCert
= &thisSslCert
->derCert
;
193 /* validate the whole mess */
194 srtn
= sslVerifyCertChain(ctx
, certChain
);
196 ortn
= sslErrToOsStatus(srtn
);
201 * Get privKey, pubKey, KCItem of certs[0].
202 * First, the private key, from the Keychain, using crufy private API.
204 keyParams
.item
= (KCItemRef
)CFArrayGetValueAtIndex(certs
, 0);
205 ortn
= KCDispatch(kKCFromItemGetPrivateKey
, &keyParams
);
207 errorLog1("KCDispatch(kKCFromItemGetPrivateKey) returned %d\n", ortn
);
210 keyInfo
.item
= keyParams
.privateKeyItem
;
211 ortn
= KCDispatch(kKCFromItemGetKeyInfo
, &keyInfo
);
213 errorLog1("KCDispatch(kKCFromItemGetKeyInfo) returned %d\n", ortn
);
216 *privKey
= (CSSM_KEY_PTR
)keyInfo
.keyPtr
;
217 *cspHand
= keyInfo
.cspHandle
;
218 *privKeyRef
= keyParams
.privateKeyItem
;
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
,
227 errorLog1("sslPubKeyFromCert returned %d\n", srtn
);
228 ortn
= sslErrToOsStatus(srtn
);
233 *destCert
= certChain
;
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
);
242 if(keyParams
.privateKeyItem
!= NULL
) {
243 KCReleaseItem(&keyParams
.privateKeyItem
);
247 #endif /* (ST_SERVER_MODE_ENABLE || ST_CLIENT_AUTHENTICATION) */
250 * Add Apple built-in root certs to ctx->trustedCerts.
252 OSStatus
addBuiltInCerts (SSLContextRef ctx
)
254 #if ST_KEYCHAIN_ENABLE
258 ortn
= KCDispatch(kKCGetRootCertificateKeychain
, &kc
);
260 errorLog1("KCDispatch(kKCGetRootCertificateKeychain) returned %d\n",
264 return parseTrustedKeychain(ctx
, kc
);
266 /* nothing for now */
268 #endif /* ST_KEYCHAIN_ENABLE */
271 #if ST_KEYCHAIN_ENABLE
274 * Given an open Keychain:
275 * -- Get raw cert data, add to array of CSSM_DATAs in
277 * -- verify that each of these is a valid (self-verifying)
279 * -- add each subject name to acceptableDNList
282 parseTrustedKeychain (SSLContextRef ctx
,
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 */
291 KCItemRef kcItem
; /* one cert */
294 CASSERT(ctx
!= NULL
);
295 if(keyChainRef
== NULL
) {
299 ortn
= KCFindX509Certificates(keyChainRef
,
301 NULL
, // emailAddress, XXX
302 kCertSearchAny
, // options
303 &kcCerts
); // results
307 case errKCItemNotFound
:
308 return noErr
; // no certs; done
310 errorLog1("parseTrustedKeychains: KCFindX509Certificates returned %d\n",
314 if(kcCerts
== NULL
) {
315 dprintf0("parseTrustedKeychains: no certs in KC\n");
319 /* Note kcCerts must be released on any exit, successful or
322 certsPerKc
= CFArrayGetCount(kcCerts
);
325 * This array gets allocd locally; we'll add it to
326 * ctx->trustedCerts when we're done.
328 certData
= sslMalloc(certsPerKc
* sizeof(CSSM_DATA
));
329 if(certData
== NULL
) {
333 memset(certData
, 0, certsPerKc
* sizeof(CSSM_DATA
));
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.
340 for(certDex
=0; certDex
<certsPerKc
; certDex
++) {
341 kcItem
= (KCItemRef
)CFArrayGetValueAtIndex(kcCerts
, certDex
);
343 errorLog0("parseTrustedKeychains: CF error 1\n");
344 ortn
= errSSLInternal
;
347 if(!KCIsRootCertificate(kcItem
)) {
348 /* not root, OK, skip to next cert */
349 dprintf1("parseTrustedKeychains: cert %d NOT ROOT\n", certDex
);
352 ortn
= addCertData(ctx
,
354 &certData
[numGoodCerts
],
360 /* added valid root to certData */
363 } /* for each cert in kcCerts */
366 verifyTrustedRoots(ctx
, certData
, numGoodCerts
);
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
) {
377 for(certDex
=0; certDex
<numGoodCerts
; certDex
++) {
378 ctx
->trustedCerts
[ctx
->numTrustedCerts
+ certDex
] = certData
[certDex
];
380 ctx
->numTrustedCerts
+= numGoodCerts
;
384 verifyTrustedRoots(ctx
, ctx
->trustedCerts
, ctx
->numTrustedCerts
);
389 if(kcCerts
!= NULL
) {
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.
402 * The actual CSSM_DATA.Data is mallocd via CSSM_Malloc.
408 CSSM_DATA_PTR certData
,
409 Boolean
*goodCert
) /* RETURNED */
414 CSSM_BOOL subjectExpired
;
415 CSSM_DATA_PTR dnData
;
417 CASSERT(ctx
!= NULL
);
418 CASSERT(certData
!= NULL
);
419 CASSERT(kcItem
!= NULL
);
420 CASSERT(goodCert
!= NULL
);
424 /* how big is the cert? */
425 ortn
= KCGetData (kcItem
, 0, NULL
, &certSize
);
427 errorLog1("addCertData: KCGetData(1) returned %d\n", ortn
);
431 /* Allocate the buffer. */
432 srtn
= stSetUpCssmData(certData
, certSize
);
434 return sslErrToOsStatus(srtn
);
438 ortn
= KCGetData (kcItem
, certSize
, certData
->Data
, &certSize
);
440 errorLog1("addCertData: KCGetData(2) returned %d\n", ortn
);
441 stFreeCssmData(certData
, CSSM_FALSE
);
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
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
456 if(!sslVerifyCert(ctx
,
461 dprintf0("addCertData: cert does not self-verify!\n");
462 stFreeCssmData(certData
, CSSM_FALSE
);
466 /* Add this cert's subject name to (poss. existing) acceptableDNList */
467 dnData
= sslGetCertSubjectName(ctx
, certData
);
469 DNListElem
*dn
= sslMalloc(sizeof(DNListElem
));
473 dn
->next
= ctx
->acceptableDNList
;
474 ctx
->acceptableDNList
= dn
;
476 /* move actual data to dn; free the CSSM_DATA struct (must be
477 * via CSSM_Free()!) */
478 CSSM_TO_SSLBUF(dnData
, &dn
->derDN
);
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
494 const CSSM_DATA_PTR rootCert
)
497 Boolean bDefaultKcExists
;
498 KCItemRef certRef
= NULL
;
500 CSSM_DATA_PTR newTrustee
;
503 CASSERT(ctx
!= NULL
);
504 CASSERT(rootCert
!= NULL
);
505 CASSERT(ctx
->newRootCertKc
!= NULL
); /* caller verifies this */
508 * Get default KC, temporarily set new default.
510 ortn
= KCGetDefaultKeychain(&defaultKc
);
512 bDefaultKcExists
= false;
515 bDefaultKcExists
= true;
517 ortn
= KCSetDefaultKeychain(ctx
->newRootCertKc
);
519 errorLog1("sslAddNewRoot: KCSetDefaultKeychain returned %d\n", ortn
);
520 return SSLUnknownRootCert
;
524 * Add cert to newRootCertKc. This may well fail due to user
525 * interaction ("Do you want to add this root cert...?").
527 ortn
= KCAddX509Certificate(rootCert
->Data
, rootCert
->Length
, &certRef
);
529 /* restore default KC in any case */
530 if(bDefaultKcExists
) {
531 KCSetDefaultKeychain(defaultKc
);
534 dprintf1("sslAddNewRoot: KCAddX509Certificate returned %d\n", ortn
);
535 return SSLUnknownRootCert
;
539 * OK, user accepted new root. Now add to our private stash of
540 * trusted roots. Realloc the whole pile...
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
) {
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
);
557 BlockMove(rootCert
->Data
, newTrustee
->Data
, rootCert
->Length
);
558 (ctx
->numTrustedCerts
)++;
562 #endif /* ST_KEYCHAIN_ENABLE */