]> git.saurik.com Git - apple/security.git/blob - SecureTransport/sslKeychain.c
256d78fdf09d79f93dc6239cae89c784cec29f5b
[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 SSLRef 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 #include <string.h>
40 #include <assert.h>
41 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
42 #include <Security/cssm.h>
43 /* these are to be replaced by Security/Security.h */
44 #include <Security/SecCertificate.h>
45 #include <Security/SecKeychainItem.h>
46 #include <Security/SecKeychain.h>
47 #include <Security/SecIdentity.h>
48 #include <Security/SecIdentitySearch.h>
49 #include <Security/SecKey.h>
50
51 #if ST_KEYCHAIN_ENABLE && ST_MANAGES_TRUSTED_ROOTS
52 static OSStatus
53 addCertData(
54 SSLContext *ctx,
55 KCItemRef kcItem,
56 CSSM_DATA_PTR certData,
57 Boolean *goodCert); /* RETURNED */
58 #endif /* ST_KEYCHAIN_ENABLE && ST_MANAGES_TRUSTED_ROOTS */
59
60 #if (ST_SERVER_MODE_ENABLE || ST_CLIENT_AUTHENTICATION)
61
62 #if ST_FAKE_KEYCHAIN
63 /*
64 * Routines which will be replaced by SecKeychainAPI.
65 */
66
67 /*
68 * Given a DLDB, find the first private key in the DB. It's the application's
69 * responsibility to ensure that there is only one private key. The returned
70 * PrintName attribute will be used to search for an associated cert using
71 * TBD.
72 *
73 * Caller must free returned key and PrintName.
74 */
75 static OSStatus
76 findPrivateKeyInDb(
77 SSLContext *ctx,
78 CSSM_DL_DB_HANDLE dlDbHand,
79 CSSM_KEY_PTR *privKey, // mallocd and RETURNED
80 CSSM_DATA *printName) // referent mallocd and RETURNED
81 {
82 CSSM_QUERY query;
83 CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
84 CSSM_RETURN crtn;
85 CSSM_HANDLE resultHand;
86 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
87 CSSM_DB_ATTRIBUTE_DATA theAttr;
88 CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo = &theAttr.Info;
89 CSSM_DATA theData = {0, NULL};
90
91 /* search by record type, no predicates (though we do want the PrintName
92 * attr returned). */
93 query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
94 query.Conjunctive = CSSM_DB_NONE;
95 query.NumSelectionPredicates = 0;
96 query.SelectionPredicate = NULL;
97 query.QueryLimits.TimeLimit = 0; // FIXME - meaningful?
98 query.QueryLimits.SizeLimit = 1; // FIXME - meaningful?
99 query.QueryFlags = CSSM_QUERY_RETURN_DATA; // FIXME - used?
100
101 recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
102 recordAttrs.SemanticInformation = 0;
103 recordAttrs.NumberOfAttributes = 1;
104 recordAttrs.AttributeData = &theAttr;
105
106 attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
107 attrInfo->Label.AttributeName = "PrintName";
108 attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
109
110 theAttr.NumberOfValues = 1;
111 theAttr.Value = NULL;
112
113 crtn = CSSM_DL_DataGetFirst(dlDbHand,
114 &query,
115 &resultHand,
116 &recordAttrs,
117 &theData,
118 &record);
119 /* terminate query only on success */
120 if(crtn == CSSM_OK) {
121 CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
122 *privKey = (CSSM_KEY_PTR)theData.Data;
123 /*
124 * Both the struct and the referent are mallocd by DL. Give our
125 * caller the referent; free the struct.
126 */
127 *printName = *theAttr.Value;
128 stAppFree(theAttr.Value, NULL);
129 return noErr;
130 }
131 else {
132 stPrintCdsaError("CSSM_DL_DataGetFirst", crtn);
133 errorLog0("findCertInDb: cert not found\n");
134 return errSSLBadCert;
135 }
136 }
137
138 static OSStatus
139 findCertInDb(
140 SSLContext *ctx,
141 CSSM_DL_DB_HANDLE dlDbHand,
142 const CSSM_DATA *printName, // obtained from findPrivateKeyInDb
143 CSSM_DATA *certData) // referent mallocd and RETURNED
144 {
145 CSSM_QUERY query;
146 CSSM_SELECTION_PREDICATE predicate;
147 CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
148 CSSM_RETURN crtn;
149 CSSM_HANDLE resultHand;
150
151 predicate.DbOperator = CSSM_DB_EQUAL;
152 predicate.Attribute.Info.AttributeNameFormat =
153 CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
154 predicate.Attribute.Info.Label.AttributeName = "PrintName";
155 predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
156 /* hope this const_cast is OK */
157 predicate.Attribute.Value = (CSSM_DATA_PTR)printName;
158 predicate.Attribute.NumberOfValues = 1;
159
160 query.RecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE;
161 query.Conjunctive = CSSM_DB_NONE;
162 query.NumSelectionPredicates = 1;
163 query.SelectionPredicate = &predicate;
164 query.QueryLimits.TimeLimit = 0; // FIXME - meaningful?
165 query.QueryLimits.SizeLimit = 1; // FIXME - meaningful?
166 query.QueryFlags = 0; // FIXME - used?
167
168 crtn = CSSM_DL_DataGetFirst(dlDbHand,
169 &query,
170 &resultHand,
171 NULL, // no attrs returned
172 certData,
173 &record);
174 /* terminate query only on success */
175 if(crtn == CSSM_OK) {
176 CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
177 return noErr;
178 }
179 else {
180 stPrintCdsaError("CSSM_DL_DataGetFirst", crtn);
181 errorLog0("findCertInDb: cert not found\n");
182 return errSSLBadCert;
183 }
184 }
185
186
187 #endif /* ST_FAKE_KEYCHAIN */
188 /*
189 * Given an array of certs (as SecIdentityRefs, specified by caller
190 * in SSLSetCertificate or SSLSetEncryptionCertificate) and a
191 * destination SSLCertificate:
192 *
193 * -- free destCerts if we have any
194 * -- Get raw cert data, convert to array of SSLCertificates in *destCert
195 * -- validate cert chain
196 * -- get pub, priv keys from certRef[0], store in *pubKey, *privKey
197 */
198
199 #if ST_FAKE_KEYCHAIN
200 /*
201 * In this incarnation, the certs array actually holds one pointer to a
202 * CSSM_DL_DB_HANDLE. In that DL/DB is exactly one private key; that's
203 * our privKey. We use the KeyLabel of that key to look up a cert with
204 * the same label. We get the public key from the cert. Other certs and
205 * public keys in the DL/DB are ignored.
206 */
207 OSStatus
208 parseIncomingCerts(
209 SSLContext *ctx,
210 CFArrayRef certs,
211 SSLCertificate **destCert, /* &ctx->{localCert,encryptCert} */
212 CSSM_KEY_PTR *pubKey, /* &ctx->signingPubKey, etc. */
213 CSSM_KEY_PTR *privKey, /* &ctx->signingPrivKey, etc. */
214 CSSM_CSP_HANDLE *cspHand /* &ctx->signingKeyCsp, etc. */
215 #if ST_KC_KEYS_NEED_REF
216 ,
217 SecKeychainRef *privKeyRef) /* &ctx->signingKeyRef, etc. */
218 #else
219 )
220 #endif /* ST_KC_KEYS_NEED_REF */
221 {
222 CSSM_DL_DB_HANDLE_PTR dlDbHand = NULL;
223 CFIndex numCerts;
224 CSSM_KEY_PTR lookupPriv = NULL;
225 CSSM_DATA lookupLabel = {0, NULL};
226 CSSM_DATA lookupCert = {0, NULL};
227 OSStatus ortn;
228 SSLCertificate *certChain = NULL;
229 SSLCertificate *thisSslCert;
230 SSLErr srtn;
231 CSSM_CSP_HANDLE dummyCsp;
232
233 assert(ctx != NULL);
234 assert(destCert != NULL); /* though its referent may be NULL */
235 assert(pubKey != NULL);
236 assert(privKey != NULL);
237 assert(cspHand != NULL);
238
239 sslDeleteCertificateChain(*destCert, ctx);
240 *destCert = NULL;
241 *pubKey = NULL;
242 *privKey = NULL;
243 *cspHand = 0;
244
245 if(certs == NULL) {
246 dprintf0("parseIncomingCerts: NULL incoming cert (DLDB) array\n");
247 return errSSLBadCert;
248 }
249 numCerts = CFArrayGetCount(certs);
250 if(numCerts != 1) {
251 dprintf0("parseIncomingCerts: empty incoming cert (DLDB) array\n");
252 return errSSLBadCert;
253 }
254 dlDbHand = (CSSM_DL_DB_HANDLE_PTR)CFArrayGetValueAtIndex(certs, 0);
255 if(dlDbHand == NULL) {
256 errorLog0("parseIncomingCerts: bad cert (DLDB) array\n");
257 return paramErr;
258 }
259
260 /* get private key - app has to ensure there is only one (for now) */
261 ortn = findPrivateKeyInDb(ctx, *dlDbHand, &lookupPriv, &lookupLabel);
262 if(ortn) {
263 errorLog0("parseIncomingCerts: no private key\n");
264 return ortn;
265 }
266 assert(lookupPriv->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE);
267 assert(lookupPriv->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY);
268
269 /* get associated cert */
270 ortn = findCertInDb(ctx, *dlDbHand, &lookupLabel, &lookupCert);
271 if(ortn) {
272 errorLog0("parseIncomingCerts: no cert\n");
273 return ortn;
274 }
275 sslFree(lookupLabel.Data);
276 assert(lookupCert.Length > 100); // quickie check
277
278 /*
279 * Cook up an SSLCertificate and its associated SSLBuffer.
280 */
281 thisSslCert = sslMalloc(sizeof(SSLCertificate));
282 if(thisSslCert == NULL) {
283 return memFullErr;
284 }
285 if(SSLAllocBuffer(&thisSslCert->derCert, lookupCert.Length, &ctx->sysCtx)) {
286 return memFullErr;
287 }
288
289 /* copy cert data mallocd by DL */
290 memmove(thisSslCert->derCert.data, lookupCert.Data, lookupCert.Length);
291 sslFree(lookupCert.Data);
292
293 /* enqueue onto head of cert chain */
294 thisSslCert->next = certChain;
295 certChain = thisSslCert;
296
297 /* TBD - we might fetch other certs from CFArrayRef certs here and enqueue
298 * them on certChain */
299
300 /* now the public key of the first cert, from CL */
301 srtn = sslPubKeyFromCert(ctx,
302 &certChain->derCert,
303 pubKey,
304 &dummyCsp);
305 if(srtn) {
306 errorLog1("sslPubKeyFromCert returned %d\n", srtn);
307 ortn = sslErrToOsStatus(srtn);
308 goto errOut;
309 }
310 assert((*pubKey)->KeyHeader.BlobType == CSSM_KEYBLOB_RAW);
311 assert((*pubKey)->KeyHeader.KeyClass == CSSM_KEYCLASS_PUBLIC_KEY);
312
313 /*
314 * NOTE: as of 2/7/02, the size of the extracted public key will NOT
315 * always equal the size of the private key. Non-byte-aligned key sizes
316 * for RSA keys result in the extracted public key's size to be rounded
317 * UP to the next byte boundary.
318 */
319 assert((*pubKey)->KeyHeader.LogicalKeySizeInBits ==
320 ((lookupPriv->KeyHeader.LogicalKeySizeInBits + 7) & ~7));
321
322 /* SUCCESS */
323 *destCert = certChain;
324 *privKey = lookupPriv;
325
326 /* we get this at context create time */
327 assert(ctx->cspDlHand != 0);
328 *cspHand = ctx->cspDlHand;
329 *privKeyRef = NULL; // not used
330 return noErr;
331
332 errOut:
333 /* free certChain, everything in it, other vars, return ortn */
334 sslDeleteCertificateChain(certChain, ctx);
335 if(lookupPriv != NULL) {
336 sslFreeKey(ctx->cspDlHand, &lookupPriv, NULL);
337 }
338 return ortn;
339 }
340
341 #else /* !ST_FAKE_KEYCHAIN */
342
343 /* Convert a SecCertificateRef to an SSLCertificate * */
344 static OSStatus secCertToSslCert(
345 SSLContext *ctx,
346 SecCertificateRef certRef,
347 SSLCertificate **sslCert)
348 {
349 CSSM_DATA certData; // struct is transient, referent owned by
350 // Sec layer
351 OSStatus ortn;
352 SSLCertificate *thisSslCert = NULL;
353
354 ortn = SecCertificateGetData(certRef, &certData);
355 if(ortn) {
356 errorLog1("SecCertificateGetData() returned %d\n", (int)ortn);
357 return ortn;
358 }
359
360 thisSslCert = sslMalloc(sizeof(SSLCertificate));
361 if(thisSslCert == NULL) {
362 return memFullErr;
363 }
364 if(SSLAllocBuffer(&thisSslCert->derCert, certData.Length,
365 &ctx->sysCtx)) {
366 return memFullErr;
367 }
368 memcpy(thisSslCert->derCert.data, certData.Data, certData.Length);
369 thisSslCert->derCert.length = certData.Length;
370 *sslCert = thisSslCert;
371 return noErr;
372 }
373
374 OSStatus
375 parseIncomingCerts(
376 SSLContext *ctx,
377 CFArrayRef certs,
378 SSLCertificate **destCert, /* &ctx->{localCert,encryptCert} */
379 CSSM_KEY_PTR *pubKey, /* &ctx->signingPubKey, etc. */
380 CSSM_KEY_PTR *privKey, /* &ctx->signingPrivKey, etc. */
381 CSSM_CSP_HANDLE *cspHand /* &ctx->signingKeyCsp, etc. */
382 #if ST_KC_KEYS_NEED_REF
383 ,
384 SecKeychainRef *privKeyRef) /* &ctx->signingKeyRef, etc. */
385 #else
386 )
387 #endif /* ST_KC_KEYS_NEED_REF */
388 {
389 CFIndex numCerts;
390 CFIndex cert;
391 SSLCertificate *certChain = NULL;
392 SSLCertificate *thisSslCert;
393 SecKeychainRef kcRef;
394 OSStatus ortn;
395 SSLErr srtn;
396 SecIdentityRef identity;
397 SecCertificateRef certRef;
398 SecKeyRef keyRef;
399 CSSM_DATA certData;
400 CSSM_CL_HANDLE clHand; // carefully derive from a SecCertificateRef
401 CSSM_RETURN crtn;
402
403 CASSERT(ctx != NULL);
404 CASSERT(destCert != NULL); /* though its referent may be NULL */
405 CASSERT(pubKey != NULL);
406 CASSERT(privKey != NULL);
407 CASSERT(cspHand != NULL);
408
409 sslDeleteCertificateChain(*destCert, ctx);
410 *destCert = NULL;
411 *pubKey = NULL;
412 *privKey = NULL;
413 *cspHand = 0;
414
415 if(certs == NULL) {
416 dprintf0("parseIncomingCerts: NULL incoming cert array\n");
417 return errSSLBadCert;
418 }
419 numCerts = CFArrayGetCount(certs);
420 if(numCerts == 0) {
421 dprintf0("parseIncomingCerts: empty incoming cert array\n");
422 return errSSLBadCert;
423 }
424
425 /*
426 * Certs[0] is an SecIdentityRef from which we extract subject cert,
427 * privKey, pubKey, and cspHand.
428 *
429 * 1. ensure the first element is a SecIdentityRef.
430 */
431 identity = (SecIdentityRef)CFArrayGetValueAtIndex(certs, 0);
432 if(identity == NULL) {
433 errorLog0("parseIncomingCerts: bad cert array (1)\n");
434 return paramErr;
435 }
436 if(CFGetTypeID(identity) != SecIdentityGetTypeID()) {
437 errorLog0("parseIncomingCerts: bad cert array (2)\n");
438 return paramErr;
439 }
440
441 /*
442 * 2. Extract cert, keys, CSP handle and convert to local format.
443 */
444 ortn = SecIdentityCopyCertificate(identity, &certRef);
445 if(ortn) {
446 errorLog0("parseIncomingCerts: bad cert array (3)\n");
447 return ortn;
448 }
449 ortn = secCertToSslCert(ctx, certRef, &thisSslCert);
450 if(ortn) {
451 errorLog0("parseIncomingCerts: bad cert array (4)\n");
452 return ortn;
453 }
454 /* enqueue onto head of cert chain */
455 thisSslCert->next = certChain;
456 certChain = thisSslCert;
457
458 /* fetch private key from identity */
459 ortn = SecIdentityCopyPrivateKey(identity, &keyRef);
460 if(ortn) {
461 errorLog1("parseIncomingCerts: SecIdentityCopyPrivateKey err %d\n",
462 (int)ortn);
463 return ortn;
464 }
465 ortn = SecKeyGetCSSMKey(keyRef, (const CSSM_KEY **)privKey);
466 if(ortn) {
467 errorLog1("parseIncomingCerts: SecKeyGetCSSMKey err %d\n",
468 (int)ortn);
469 return ortn;
470 }
471 /* FIXME = release keyRef? */
472
473 /* obtain public key from cert */
474 ortn = SecCertificateGetCLHandle(certRef, &clHand);
475 if(ortn) {
476 errorLog1("parseIncomingCerts: SecCertificateGetCLHandle err %d\n",
477 (int)ortn);
478 return ortn;
479 }
480 certData.Data = thisSslCert->derCert.data;
481 certData.Length = thisSslCert->derCert.length;
482 crtn = CSSM_CL_CertGetKeyInfo(clHand, &certData, pubKey);
483 if(crtn) {
484 errorLog0("parseIncomingCerts: CSSM_CL_CertGetKeyInfo err\n");
485 return (OSStatus)crtn;
486 }
487
488 #if ST_FAKE_GET_CSPDL_HANDLE
489 /* we get this at context create time until SecKeychainGetCSPHandle
490 * is working */
491 assert(ctx->cspDlHand != 0);
492 *cspHand = ctx->cspDlHand;
493 #else /* ST_FAKE_GET_CSPDL_HANDLE */
494 /* obtain keychain from key, CSP handle from keychain */
495 ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)keyRef, &kcRef);
496 if(ortn) {
497 errorLog1("parseIncomingCerts: SecKeychainItemCopyKeychain err %d\n",
498 (int)ortn);
499 return ortn;
500 }
501 ortn = SecKeychainGetCSPHandle(kcRef, cspHand);
502 if(ortn) {
503 errorLog1("parseIncomingCerts: SecKeychainGetCSPHandle err %d\n",
504 (int)ortn);
505 return ortn;
506 }
507 #endif /* ST_FAKE_GET_CSPDL_HANDLE */
508
509 /* OK, that's the subject cert. Fetch optional remaining certs. */
510 /*
511 * Convert: CFArray of SecCertificateRefs --> chain of SSLCertificates.
512 * Incoming certs have root last; SSLCertificate chain has root
513 * first.
514 */
515 for(cert=1; cert<numCerts; cert++) {
516 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certs, cert);
517 if(certRef == NULL) {
518 errorLog0("parseIncomingCerts: bad cert array (5)\n");
519 return paramErr;
520 }
521 if(CFGetTypeID(certRef) != SecCertificateGetTypeID()) {
522 errorLog0("parseIncomingCerts: bad cert array (6)\n");
523 return paramErr;
524 }
525
526 /* Extract cert, convert to local format.
527 */
528 ortn = secCertToSslCert(ctx, certRef, &thisSslCert);
529 if(ortn) {
530 errorLog0("parseIncomingCerts: bad cert array (7)\n");
531 return ortn;
532 }
533 /* enqueue onto head of cert chain */
534 thisSslCert->next = certChain;
535 certChain = thisSslCert;
536 }
537
538 /* validate the whole mess */
539 srtn = sslVerifyCertChain(ctx, certChain);
540 if(srtn) {
541 ortn = sslErrToOsStatus(srtn);
542 goto errOut;
543 }
544
545 /* SUCCESS */
546 *destCert = certChain;
547 return noErr;
548
549 errOut:
550 /* free certChain, everything in it, other vars, return ortn */
551 sslDeleteCertificateChain(certChain, ctx);
552 /* FIXME - anything else? */
553 return ortn;
554 }
555 #endif /* ST_FAKE_KEYCHAIN */
556 #endif /* (ST_SERVER_MODE_ENABLE || ST_CLIENT_AUTHENTICATION) */
557
558 /*
559 * Add Apple built-in root certs to ctx->trustedCerts.
560 */
561 OSStatus addBuiltInCerts (SSLContextRef ctx)
562 {
563 #if ST_KEYCHAIN_ENABLE && ST_MANAGES_TRUSTED_ROOTS
564 OSStatus ortn;
565 KCRef kc = nil;
566
567 ortn = KCDispatch(kKCGetRootCertificateKeychain, &kc);
568 if(ortn) {
569 errorLog1("KCDispatch(kKCGetRootCertificateKeychain) returned %d\n",
570 ortn);
571 return ortn;
572 }
573 return parseTrustedKeychain(ctx, kc);
574 #else
575 /* nothing for now */
576 return noErr;
577 #endif /* ST_KEYCHAIN_ENABLE && ST_MANAGES_TRUSTED_ROOTS */
578 }
579
580 #if ST_KEYCHAIN_ENABLE && ST_MANAGES_TRUSTED_ROOTS
581
582 /*
583 * Given an open Keychain:
584 * -- Get raw cert data, add to array of CSSM_DATAs in
585 * ctx->trustedCerts
586 * -- verify that each of these is a valid (self-verifying)
587 * root cert
588 * -- add each subject name to acceptableDNList
589 */
590 OSStatus
591 parseTrustedKeychain (SSLContextRef ctx,
592 KCRef keyChainRef)
593 {
594 CFMutableArrayRef kcCerts = NULL; /* all certs in one keychain */
595 uint32 numGoodCerts = 0; /* # of good root certs */
596 CSSM_DATA_PTR certData = NULL; /* array of CSSM_DATAs */
597 CFIndex certDex; /* index into kcCerts */
598 CFIndex certsPerKc; /* # of certs in this KC */
599 OSStatus ortn;
600 KCItemRef kcItem; /* one cert */
601 Boolean goodCert;
602
603 CASSERT(ctx != NULL);
604 if(keyChainRef == NULL) {
605 return paramErr;
606 }
607
608 ortn = KCFindX509Certificates(keyChainRef,
609 NULL, // name, XXX
610 NULL, // emailAddress, XXX
611 kCertSearchAny, // options
612 &kcCerts); // results
613 switch(ortn) {
614 case noErr:
615 break; // proceed
616 case errKCItemNotFound:
617 return noErr; // no certs; done
618 default:
619 errorLog1("parseTrustedKeychains: KCFindX509Certificates returned %d\n",
620 ortn);
621 return ortn;
622 }
623 if(kcCerts == NULL) {
624 dprintf0("parseTrustedKeychains: no certs in KC\n");
625 return noErr;
626 }
627
628 /* Note kcCerts must be released on any exit, successful or
629 * otherwise. */
630
631 certsPerKc = CFArrayGetCount(kcCerts);
632
633 /*
634 * This array gets allocd locally; we'll add it to
635 * ctx->trustedCerts when we're done.
636 */
637 certData = sslMalloc(certsPerKc * sizeof(CSSM_DATA));
638 if(certData == NULL) {
639 ortn = memFullErr;
640 goto errOut;
641 }
642 memset(certData, 0, certsPerKc * sizeof(CSSM_DATA));
643
644 /*
645 * Build up local certData one root cert at a time.
646 * Some certs might not pass muster, hence the numGoodCerts
647 * which may or may not increment each time thru.
648 */
649 for(certDex=0; certDex<certsPerKc; certDex++) {
650 kcItem = (KCItemRef)CFArrayGetValueAtIndex(kcCerts, certDex);
651 if(kcItem == NULL) {
652 errorLog0("parseTrustedKeychains: CF error 1\n");
653 ortn = errSSLInternal;
654 goto errOut;
655 }
656 if(!KCIsRootCertificate(kcItem)) {
657 /* not root, OK, skip to next cert */
658 dprintf1("parseTrustedKeychains: cert %d NOT ROOT\n", certDex);
659 continue;
660 }
661 ortn = addCertData(ctx,
662 kcItem,
663 &certData[numGoodCerts],
664 &goodCert);
665 if(ortn) {
666 goto errOut;
667 }
668 if(goodCert) {
669 /* added valid root to certData */
670 numGoodCerts++;
671 }
672 } /* for each cert in kcCerts */
673
674 #if SSL_DEBUG
675 verifyTrustedRoots(ctx, certData, numGoodCerts);
676 #endif
677
678 /* Realloc ctx->trustedCerts, add new root certs */
679 ctx->trustedCerts = sslRealloc(ctx->trustedCerts,
680 ctx->numTrustedCerts * sizeof(CSSM_DATA),
681 (ctx->numTrustedCerts + numGoodCerts) * sizeof(CSSM_DATA));
682 if(ctx->trustedCerts == NULL) {
683 ortn = memFullErr;
684 goto errOut;
685 }
686 for(certDex=0; certDex<numGoodCerts; certDex++) {
687 ctx->trustedCerts[ctx->numTrustedCerts + certDex] = certData[certDex];
688 }
689 ctx->numTrustedCerts += numGoodCerts;
690 ortn = noErr;
691
692 #if SSL_DEBUG
693 verifyTrustedRoots(ctx, ctx->trustedCerts, ctx->numTrustedCerts);
694 #endif
695
696 errOut:
697 sslFree(certData);
698 if(kcCerts != NULL) {
699 CFRelease(kcCerts);
700 }
701 return ortn;
702 }
703
704 /*
705 * Given a (supposedly) root cert as a KCItemRef:
706 * -- verify that the cert self-verifies
707 * -- add its DER-encoded data *certData.
708 * -- Add its subjectName to acceptableDNList.
709 * -- If all is well, return True in *goodCert.
710 *
711 * The actual CSSM_DATA.Data is mallocd via CSSM_Malloc.
712 */
713 static OSStatus
714 addCertData(
715 SSLContext *ctx,
716 KCItemRef kcItem,
717 CSSM_DATA_PTR certData,
718 Boolean *goodCert) /* RETURNED */
719 {
720 UInt32 certSize;
721 OSStatus ortn;
722 SSLErr srtn;
723 CSSM_BOOL subjectExpired;
724 CSSM_DATA_PTR dnData;
725
726 CASSERT(ctx != NULL);
727 CASSERT(certData != NULL);
728 CASSERT(kcItem != NULL);
729 CASSERT(goodCert != NULL);
730
731 *goodCert = false;
732
733 /* how big is the cert? */
734 ortn = KCGetData (kcItem, 0, NULL, &certSize);
735 if(ortn != noErr) {
736 errorLog1("addCertData: KCGetData(1) returned %d\n", ortn);
737 return ortn;
738 }
739
740 /* Allocate the buffer. */
741 srtn = stSetUpCssmData(certData, certSize);
742 if(srtn) {
743 return sslErrToOsStatus(srtn);
744 }
745
746 /* Get the data. */
747 ortn = KCGetData (kcItem, certSize, certData->Data, &certSize);
748 if(ortn) {
749 errorLog1("addCertData: KCGetData(2) returned %d\n", ortn);
750 stFreeCssmData(certData, CSSM_FALSE);
751 return ortn;
752 }
753
754 /*
755 * Do actual cert verify, which
756 * KCIsRootCertificate does not do. A failure isn't
757 * fatal; we just don't add the cert to the array in
758 * that case.
759 *
760 * FIXME - we assume here that our common cspHand can
761 * do this cert verify; if not, we have some API work to
762 * do (to let the caller specify which CSP to use with
763 * trusted certs).
764 */
765 if(!sslVerifyCert(ctx,
766 certData,
767 certData,
768 ctx->cspHand,
769 &subjectExpired)) {
770 dprintf0("addCertData: cert does not self-verify!\n");
771 stFreeCssmData(certData, CSSM_FALSE);
772 return noErr;
773 }
774
775 /* Add this cert's subject name to (poss. existing) acceptableDNList */
776 dnData = sslGetCertSubjectName(ctx, certData);
777 if(dnData) {
778 DNListElem *dn = sslMalloc(sizeof(DNListElem));
779 if(dn == NULL) {
780 return memFullErr;
781 }
782 dn->next = ctx->acceptableDNList;
783 ctx->acceptableDNList = dn;
784
785 /* move actual data to dn; free the CSSM_DATA struct (must be
786 * via CSSM_Free()!) */
787 CSSM_TO_SSLBUF(dnData, &dn->derDN);
788 sslFree(dnData);
789 }
790
791 *goodCert = true;
792 return noErr;
793 }
794
795 /*
796 * Given a newly encountered root cert (obtained from a peer's cert chain),
797 * add it to newRootCertKc if the user so allows, and if so, add it to
798 * trustedCerts.
799 */
800 SSLErr
801 sslAddNewRoot(
802 SSLContext *ctx,
803 const CSSM_DATA_PTR rootCert)
804 {
805 KCRef defaultKc;
806 Boolean bDefaultKcExists;
807 KCItemRef certRef = NULL;
808 OSStatus ortn;
809 CSSM_DATA_PTR newTrustee;
810 SSLErr serr;
811
812 CASSERT(ctx != NULL);
813 CASSERT(rootCert != NULL);
814 CASSERT(ctx->newRootCertKc != NULL); /* caller verifies this */
815
816 /*
817 * Get default KC, temporarily set new default.
818 */
819 ortn = KCGetDefaultKeychain(&defaultKc);
820 if(ortn) {
821 bDefaultKcExists = false;
822 }
823 else {
824 bDefaultKcExists = true;
825 }
826 ortn = KCSetDefaultKeychain(ctx->newRootCertKc);
827 if(ortn) {
828 errorLog1("sslAddNewRoot: KCSetDefaultKeychain returned %d\n", ortn);
829 return SSLUnknownRootCert;
830 }
831
832 /*
833 * Add cert to newRootCertKc. This may well fail due to user
834 * interaction ("Do you want to add this root cert...?").
835 */
836 ortn = KCAddX509Certificate(rootCert->Data, rootCert->Length, &certRef);
837
838 /* restore default KC in any case */
839 if(bDefaultKcExists) {
840 KCSetDefaultKeychain(defaultKc);
841 }
842 if(ortn) {
843 dprintf1("sslAddNewRoot: KCAddX509Certificate returned %d\n", ortn);
844 return SSLUnknownRootCert;
845 }
846
847 /*
848 * OK, user accepted new root. Now add to our private stash of
849 * trusted roots. Realloc the whole pile...
850 */
851 ctx->trustedCerts = (CSSM_DATA_PTR)sslRealloc(ctx->trustedCerts,
852 (ctx->numTrustedCerts * sizeof(CSSM_DATA)),
853 ((ctx->numTrustedCerts + 1) * sizeof(CSSM_DATA)));
854 if(ctx->trustedCerts == NULL) {
855 return SSLMemoryErr;
856 }
857
858 /* Now add a copy of the new root. */
859 newTrustee = &ctx->trustedCerts[ctx->numTrustedCerts];
860 newTrustee->Data = NULL;
861 newTrustee->Length = 0;
862 serr = stSetUpCssmData(newTrustee, rootCert->Length);
863 if(serr) {
864 return serr;
865 }
866 BlockMove(rootCert->Data, newTrustee->Data, rootCert->Length);
867 (ctx->numTrustedCerts)++;
868 return SSLNoErr;
869 }
870
871 #endif /* ST_KEYCHAIN_ENABLE && ST_MANAGES_TRUSTED_ROOTS */
872