]> git.saurik.com Git - apple/security.git/blob - SecureTransport/sslctx.c
Security-29.tar.gz
[apple/security.git] / SecureTransport / sslctx.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: sslctx.c
21
22 Contains: SSLContext accessors
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 File: sslctx.c
31
32 SSLRef 3.0 Final -- 11/19/96
33
34 Copyright (c)1996 by Netscape Communications Corp.
35
36 By retrieving this software you are bound by the licensing terms
37 disclosed in the file "LICENSE.txt". Please read it, and if you don't
38 accept the terms, delete this software.
39
40 SSLRef 3.0 was developed by Netscape Communications Corp. of Mountain
41 View, California <http://home.netscape.com/> and Consensus Development
42 Corporation of Berkeley, California <http://www.consensus.com/>.
43
44 *********************************************************************
45
46 File: sslctx.c SSLContext accessors
47
48 Functions called by the end user which configure an SSLContext
49 structure or access data stored there.
50
51 ****************************************************************** */
52
53
54 #include "ssl.h"
55 #include "sslctx.h"
56 #include "sslalloc.h"
57 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
58 #include "digests.h"
59 #include "sslDebug.h"
60 #include "appleCdsa.h"
61 #include "appleGlue.h"
62 #include "sslKeychain.h"
63 #include "sslutil.h"
64 #include "cipherSpecs.h"
65
66 #include <string.h>
67
68 static void sslFreeDnList(
69 SSLContext *ctx)
70 {
71 DNListElem *dn, *nextDN;
72 SSLBuffer buf;
73
74 dn = ctx->acceptableDNList;
75
76 while (dn)
77 {
78 SSLFreeBuffer(&dn->derDN, &ctx->sysCtx);
79 nextDN = dn->next;
80 buf.data = (uint8*)dn;
81 buf.length = sizeof(DNListElem);
82 SSLFreeBuffer(&buf, &ctx->sysCtx);
83 dn = nextDN;
84 }
85 ctx->acceptableDNList = NULL;
86 }
87
88 static SSLErr sslFreeTrustedRoots(
89 SSLContext *ctx)
90 {
91 int i;
92
93 CASSERT(ctx != NULL);
94 if((ctx->numTrustedCerts == 0) || (ctx->trustedCerts == NULL)) {
95 /* they really should both be zero, right? */
96 CASSERT((ctx->numTrustedCerts == 0) && (ctx->trustedCerts == NULL));
97 }
98 else {
99 for(i=0; i<ctx->numTrustedCerts; i++) {
100 stFreeCssmData(&ctx->trustedCerts[i], CSSM_FALSE);
101 }
102 sslFree(ctx->trustedCerts);
103 }
104 ctx->numTrustedCerts = 0;
105 ctx->trustedCerts = NULL;
106 sslFreeDnList(ctx);
107 return SSLNoErr;
108 }
109
110 OSStatus
111 SSLNewContext (Boolean isServer,
112 SSLContextRef *contextPtr) /* RETURNED */
113 {
114 SSLContext *ctx;
115 OSStatus oerr;
116 SSLErr serr;
117
118 if(contextPtr == NULL) {
119 return paramErr;
120 }
121 *contextPtr = NULL;
122 ctx = (SSLContext *)sslMalloc(sizeof(SSLContext));
123 if(ctx == NULL) {
124 return memFullErr;
125 }
126 /* subsequent errors to errOut: */
127
128 memset(ctx, 0, sizeof(SSLContext));
129 ctx->state = SSLUninitialized;
130
131 /* different defaults for client and server ... */
132 if(isServer) {
133 ctx->protocolSide = SSL_ServerSide;
134 ctx->reqProtocolVersion = SSL_Version_3_0;
135 }
136 else {
137 ctx->protocolSide = SSL_ClientSide;
138 ctx->reqProtocolVersion = SSL_Version_Undetermined;
139 }
140 ctx->negProtocolVersion = SSL_Version_Undetermined;
141
142 /* Initialize the cipher state to NULL_WITH_NULL_NULL */
143 ctx->selectedCipherSpec = &SSL_NULL_WITH_NULL_NULL_CipherSpec;
144 ctx->selectedCipher = ctx->selectedCipherSpec->cipherSpec;
145 ctx->writeCipher.hash = ctx->selectedCipherSpec->macAlgorithm;
146 ctx->readCipher.hash = ctx->selectedCipherSpec->macAlgorithm;
147 ctx->readCipher.symCipher = ctx->selectedCipherSpec->cipher;
148 ctx->writeCipher.symCipher = ctx->selectedCipherSpec->cipher;
149
150 #if _APPLE_CDSA_
151 /* these two are invariant */
152 ctx->writeCipher.encrypting = 1;
153 ctx->writePending.encrypting = 1;
154 #endif /* _APPLE_CDSA_ */
155
156 /* this gets init'd on first call to SSLHandshake() */
157 ctx->validCipherSpecs = NULL;
158 ctx->numValidCipherSpecs = 0;
159
160 SSLInitMACPads();
161 if(cfSetUpAllocators(ctx)) {
162 oerr = memFullErr;
163 goto errOut;
164 }
165
166 /* attach to CSP, CL, TP */
167 serr = attachToAll(ctx);
168 if(serr) {
169 oerr = sslErrToOsStatus(serr);
170 goto errOut;
171 }
172
173 /* snag root certs from Keychain, tolerate error */
174 addBuiltInCerts(ctx);
175
176 *contextPtr = ctx;
177 return noErr;
178
179 errOut:
180 sslFree(ctx);
181 return oerr;
182 }
183
184
185 /*
186 * Dispose of an SSLContext.
187 */
188 OSStatus
189 SSLDisposeContext (SSLContext *ctx)
190 {
191 WaitingRecord *wait, *next;
192 SSLBuffer buf;
193
194 if(ctx == NULL) {
195 return paramErr;
196 }
197 sslDeleteCertificateChain(ctx->localCert, ctx);
198 sslDeleteCertificateChain(ctx->encryptCert, ctx);
199 sslDeleteCertificateChain(ctx->peerCert, ctx);
200 ctx->localCert = ctx->encryptCert = ctx->peerCert = NULL;
201 SSLFreeBuffer(&ctx->partialReadBuffer, &ctx->sysCtx);
202
203 wait = ctx->recordWriteQueue;
204 while (wait)
205 { SSLFreeBuffer(&wait->data, &ctx->sysCtx);
206 next = wait->next;
207 buf.data = (uint8*)wait;
208 buf.length = sizeof(WaitingRecord);
209 SSLFreeBuffer(&buf, &ctx->sysCtx);
210 wait = next;
211 }
212
213 SSLFreeBuffer(&ctx->dhPeerPublic, &ctx->sysCtx);
214 SSLFreeBuffer(&ctx->dhExchangePublic, &ctx->sysCtx);
215 SSLFreeBuffer(&ctx->dhPrivate, &ctx->sysCtx);
216
217 SSLFreeBuffer(&ctx->shaState, &ctx->sysCtx);
218 SSLFreeBuffer(&ctx->md5State, &ctx->sysCtx);
219
220 SSLFreeBuffer(&ctx->sessionID, &ctx->sysCtx);
221 SSLFreeBuffer(&ctx->peerID, &ctx->sysCtx);
222 SSLFreeBuffer(&ctx->resumableSession, &ctx->sysCtx);
223 SSLFreeBuffer(&ctx->preMasterSecret, &ctx->sysCtx);
224 SSLFreeBuffer(&ctx->partialReadBuffer, &ctx->sysCtx);
225 SSLFreeBuffer(&ctx->fragmentedMessageCache, &ctx->sysCtx);
226 SSLFreeBuffer(&ctx->receivedDataBuffer, &ctx->sysCtx);
227
228 SSLDisposeCipherSuite(&ctx->readCipher, ctx);
229 SSLDisposeCipherSuite(&ctx->writeCipher, ctx);
230 SSLDisposeCipherSuite(&ctx->readPending, ctx);
231 SSLDisposeCipherSuite(&ctx->writePending, ctx);
232
233 sslFree(ctx->validCipherSpecs);
234 ctx->validCipherSpecs = NULL;
235 ctx->numValidCipherSpecs = 0;
236
237 /* free APPLE_CDSA stuff */
238 #if ST_KEYCHAIN_ENABLE
239 sslFreeKey(ctx->signingKeyCsp, &ctx->signingPrivKey, &ctx->signingKeyRef);
240 sslFreeKey(ctx->encryptKeyCsp, &ctx->encryptPrivKey, &ctx->encryptKeyRef);
241 #else
242 sslFreeKey(ctx->signingKeyCsp, &ctx->signingPrivKey, NULL);
243 sslFreeKey(ctx->encryptKeyCsp, &ctx->encryptPrivKey, NULL);
244 #endif /* ST_KEYCHAIN_ENABLE */
245 sslFreeKey(ctx->signingKeyCsp, &ctx->signingPubKey, NULL);
246 sslFreeKey(ctx->encryptKeyCsp, &ctx->encryptPubKey, NULL);
247 sslFreeKey(ctx->peerPubKeyCsp, &ctx->peerPubKey, NULL);
248
249 #if SSL_DEBUG
250 if(ctx->rootCertName != NULL) {
251 sslFree(ctx->rootCertName);
252 }
253 #endif /* SSL_DEBUG */
254
255 sslFreeTrustedRoots(ctx);
256
257 detachFromAll(ctx);
258
259 cfTearDownAllocators(ctx);
260 memset(ctx, 0, sizeof(SSLContext));
261 sslFree(ctx);
262 return noErr;
263 }
264
265 /*
266 * Determine the state of an SSL session.
267 */
268 OSStatus
269 SSLGetSessionState (SSLContextRef context,
270 SSLSessionState *state) /* RETURNED */
271 {
272 SSLSessionState rtnState = kSSLIdle;
273
274 if(context == NULL) {
275 return paramErr;
276 }
277 *state = rtnState;
278 switch(context->state) {
279 case SSLUninitialized:
280 case HandshakeServerUninit:
281 case HandshakeClientUninit:
282 rtnState = kSSLIdle;
283 break;
284 case SSLGracefulClose:
285 rtnState = kSSLClosed;
286 break;
287 case SSLErrorClose:
288 case SSLNoNotifyClose:
289 rtnState = kSSLAborted;
290 break;
291 case HandshakeServerReady:
292 case HandshakeClientReady:
293 rtnState = kSSLConnected;
294 break;
295 default:
296 CASSERT((context->state >= HandshakeServerHello) &&
297 (context->state <= HandshakeSSL2ServerFinished));
298 rtnState = kSSLHandshake;
299 break;
300
301 }
302 *state = rtnState;
303 return noErr;
304 }
305
306 OSStatus
307 SSLSetIOFuncs (SSLContextRef ctx,
308 SSLReadFunc read,
309 SSLWriteFunc write)
310 {
311 if(ctx == NULL) {
312 return paramErr;
313 }
314 if(sslIsSessionActive(ctx)) {
315 /* can't do this with an active session */
316 return badReqErr;
317 }
318 ctx->ioCtx.read = read;
319 ctx->ioCtx.write = write;
320 return noErr;
321 }
322
323 OSStatus
324 SSLSetConnection (SSLContextRef ctx,
325 SSLConnectionRef connection)
326 {
327 if(ctx == NULL) {
328 return paramErr;
329 }
330 if(sslIsSessionActive(ctx)) {
331 /* can't do this with an active session */
332 return badReqErr;
333 }
334 ctx->ioCtx.ioRef = connection;
335 return noErr;
336 }
337
338 OSStatus
339 SSLSetProtocolVersion (SSLContextRef ctx,
340 SSLProtocol version)
341 {
342 SSLProtocolVersion versInt;
343
344 if(ctx == NULL) {
345 return paramErr;
346 }
347 if(sslIsSessionActive(ctx)) {
348 /* can't do this with an active session */
349 return badReqErr;
350 }
351
352 /* convert external representation to private */
353 switch(version) {
354 case kSSLProtocolUnknown:
355 versInt = SSL_Version_Undetermined;
356 break;
357 case kSSLProtocol2:
358 versInt = SSL_Version_2_0;
359 break;
360 case kSSLProtocol3:
361 /* this tells us to do our best but allows 2.0 */
362 versInt = SSL_Version_Undetermined;
363 break;
364 case kSSLProtocol3Only:
365 versInt = SSL_Version_3_0_Only;
366 break;
367 default:
368 return paramErr;
369 }
370 ctx->reqProtocolVersion = ctx->negProtocolVersion = versInt;
371 return noErr;
372 }
373
374 static SSLProtocol convertProtToExtern(SSLProtocolVersion prot)
375 {
376 switch(prot) {
377 case SSL_Version_Undetermined:
378 return kSSLProtocolUnknown;
379 case SSL_Version_3_0_Only:
380 return kSSLProtocol3Only;
381 case SSL_Version_2_0:
382 return kSSLProtocol2;
383 case SSL_Version_3_0:
384 return kSSLProtocol3;
385 case SSL_Version_3_0_With_2_0_Hello:
386 sslPanic("How did we get SSL_Version_3_0_With_2_0_Hello?");
387 default:
388 sslPanic("convertProtToExtern: bad prot");
389 }
390 /* not reached but make compiler happy */
391 return kSSLProtocolUnknown;
392 }
393
394 OSStatus
395 SSLGetProtocolVersion (SSLContextRef ctx,
396 SSLProtocol *protocol) /* RETURNED */
397 {
398 if(ctx == NULL) {
399 return paramErr;
400 }
401 *protocol = convertProtToExtern(ctx->reqProtocolVersion);
402 return noErr;
403 }
404
405 OSStatus
406 SSLGetNegotiatedProtocolVersion (SSLContextRef ctx,
407 SSLProtocol *protocol) /* RETURNED */
408 {
409 if(ctx == NULL) {
410 return paramErr;
411 }
412 *protocol = convertProtToExtern(ctx->negProtocolVersion);
413 return noErr;
414 }
415
416 OSStatus
417 SSLSetAllowExpiredCerts (SSLContextRef ctx,
418 Boolean allowExpired)
419 {
420 if(ctx == NULL) {
421 return paramErr;
422 }
423 if(sslIsSessionActive(ctx)) {
424 /* can't do this with an active session */
425 return badReqErr;
426 }
427 ctx->allowExpiredCerts = allowExpired;
428 return noErr;
429 }
430
431 OSStatus
432 SSLGetAllowExpiredCerts (SSLContextRef ctx,
433 Boolean *allowExpired)
434 {
435 if(ctx == NULL) {
436 return paramErr;
437 }
438 *allowExpired = ctx->allowExpiredCerts;
439 return noErr;
440 }
441
442 OSStatus SSLSetAllowAnyRoot(
443 SSLContextRef ctx,
444 Boolean anyRoot)
445 {
446 if(ctx == NULL) {
447 return paramErr;
448 }
449 ctx->allowAnyRoot = anyRoot;
450 return noErr;
451 }
452
453 OSStatus
454 SSLGetAllowAnyRoot(
455 SSLContextRef ctx,
456 Boolean *anyRoot)
457 {
458 if(ctx == NULL) {
459 return paramErr;
460 }
461 *anyRoot = ctx->allowAnyRoot;
462 return noErr;
463 }
464
465 #if ST_SERVER_MODE_ENABLE
466 OSStatus
467 SSLSetClientSideAuthenticate (SSLContext *ctx,
468 SSLAuthenticate auth)
469 {
470 if(ctx == NULL) {
471 return paramErr;
472 }
473 if(sslIsSessionActive(ctx)) {
474 /* can't do this with an active session */
475 return badReqErr;
476 }
477 ctx->clientAuth = auth;
478 switch(auth) {
479 case kNeverAuthenticate:
480 ctx->tryClientAuth = false;
481 break;
482 case kAlwaysAuthenticate:
483 case kTryAuthenticate:
484 /* FIXME - needs work to distinguish these cases at
485 * handshake time */
486 ctx->tryClientAuth = true;
487 break;
488 }
489 return noErr;
490 }
491 #endif /* ST_SERVER_MODE_ENABLE */
492
493 #if (ST_SERVER_MODE_ENABLE || ST_CLIENT_AUTHENTICATION)
494
495 OSStatus
496 SSLSetCertificate (SSLContextRef ctx,
497 CFArrayRef certRefs)
498 {
499 /*
500 * -- free localCerts if we have any
501 * -- Get raw cert data, convert to ctx->localCert
502 * -- get pub, priv keys from certRef[0]
503 * -- validate cert chain
504 */
505 if(ctx == NULL) {
506 return paramErr;
507 }
508 if(sslIsSessionActive(ctx)) {
509 /* can't do this with an active session */
510 return badReqErr;
511 }
512 return parseIncomingCerts(ctx,
513 certRefs,
514 &ctx->localCert,
515 &ctx->signingPubKey,
516 &ctx->signingPrivKey,
517 &ctx->signingKeyCsp,
518 &ctx->signingKeyRef);
519 }
520 #endif /* (ST_SERVER_MODE_ENABLE || ST_CLIENT_AUTHENTICATION) */
521
522 #if ST_SERVER_MODE_ENABLE
523 OSStatus
524 SSLSetEncryptionCertificate (SSLContextRef ctx,
525 CFArrayRef certRefs)
526 {
527 /*
528 * -- free encryptCert if we have any
529 * -- Get raw cert data, convert to ctx->encryptCert
530 * -- get pub, priv keys from certRef[0]
531 * -- validate cert chain
532 */
533 if(ctx == NULL) {
534 return paramErr;
535 }
536 if(sslIsSessionActive(ctx)) {
537 /* can't do this with an active session */
538 return badReqErr;
539 }
540 return parseIncomingCerts(ctx,
541 certRefs,
542 &ctx->encryptCert,
543 &ctx->encryptPubKey,
544 &ctx->encryptPrivKey,
545 &ctx->encryptKeyCsp,
546 &ctx->encryptKeyRef);
547 }
548 #endif /* ST_SERVER_MODE_ENABLE*/
549
550 #if ST_KEYCHAIN_ENABLE
551
552 /*
553 * Add (optional, additional) trusted root certs.
554 */
555 OSStatus
556 SSLSetTrustedRootCertKC (SSLContextRef ctx,
557 KCRef keyChainRef,
558 Boolean deleteExisting)
559 {
560 /*
561 * -- free trustedCerts if deleteExisting
562 * -- Get raw cert data, add to ctx->trustedCerts
563 * -- verify that each of these is a valid (self-verifying)
564 * root cert
565 * -- add each subject name to acceptableDNList
566 */
567 if((ctx == NULL) || (keyChainRef == nil)) {
568 return paramErr;
569 }
570 if(sslIsSessionActive(ctx)) {
571 /* can't do this with an active session */
572 return badReqErr;
573 }
574 if(deleteExisting) {
575 sslFreeTrustedRoots(ctx);
576 }
577 return parseTrustedKeychain(ctx, keyChainRef);
578 }
579
580 OSStatus
581 SSLSetNewRootKC (SSLContextRef ctx,
582 KCRef keyChainRef,
583 void *accessCreds)
584 {
585 if((ctx == NULL) || (keyChainRef == nil)) {
586 return paramErr;
587 }
588 if(sslIsSessionActive(ctx)) {
589 /* can't do this with an active session */
590 return badReqErr;
591 }
592 if(ctx->newRootCertKc != NULL) {
593 /* can't do this multiple times */
594 return badReqErr;
595 }
596 ctx->newRootCertKc = keyChainRef;
597 ctx->accessCreds = accessCreds;
598 return noErr;
599 }
600 #endif /* ST_KEYCHAIN_ENABLE */
601
602 OSStatus
603 SSLSetPeerID (SSLContext *ctx,
604 CFDataRef peerID)
605 {
606 SSLErr serr;
607 uint32 len;
608
609 /* copy peerId to context->peerId */
610 if((ctx == NULL) ||
611 (peerID == NULL) ||
612 ((len = CFDataGetLength(peerID)) == 0)) {
613 return paramErr;
614 }
615 if(sslIsSessionActive(ctx)) {
616 /* can't do this with an active session */
617 return badReqErr;
618 }
619 SSLFreeBuffer(&ctx->peerID, &ctx->sysCtx);
620 serr = SSLAllocBuffer(&ctx->peerID, len, &ctx->sysCtx);
621 if(serr) {
622 return sslErrToOsStatus(serr);
623 }
624 memmove(ctx->peerID.data, CFDataGetBytePtr(peerID), len);
625 ctx->peerID.length = len;
626 return noErr;
627 }
628
629 OSStatus
630 SSLGetNegotiatedCipher (SSLContextRef ctx,
631 SSLCipherSuite *cipherSuite)
632 {
633 if(ctx == NULL) {
634 return paramErr;
635 }
636 if(!sslIsSessionActive(ctx)) {
637 return badReqErr;
638 }
639 *cipherSuite = (SSLCipherSuite)ctx->selectedCipher;
640 return noErr;
641 }
642
643 /*
644 * Add an acceptable distinguished name.
645 * FIXME - this looks like a big hole in the SSLRef code;
646 * acceptableDNList is set here and in SSLProcessCertificateRequest();
647 * it's used and sent to a client in SSLEncodeCertificateRequest();
648 * but the list is never used to decide what certs to send!
649 *
650 * Also FIXME - this allocation of dnBufs is total horseshit. The
651 * SSLBufs can never get freed. Why not just allocate the
652 * raw DNListElems? Sheesh.
653 */
654 #if 0
655 /* not used */
656 static SSLErr
657 SSLAddDistinguishedName(SSLContext *ctx, SSLBuffer derDN)
658 { SSLBuffer dnBuf;
659 DNListElem *dn;
660 SSLErr err;
661
662 if ((err = SSLAllocBuffer(&dnBuf, sizeof(DNListElem), &ctx->sysCtx)) != 0)
663 return err;
664 dn = (DNListElem*)dnBuf.data;
665 if ((err = SSLAllocBuffer(&dn->derDN, derDN.length, &ctx->sysCtx)) != 0)
666 { SSLFreeBuffer(&dnBuf, &ctx->sysCtx);
667 return err;
668 }
669 memcpy(dn->derDN.data, derDN.data, derDN.length);
670 dn->next = ctx->acceptableDNList;
671 ctx->acceptableDNList = dn;
672 return SSLNoErr;
673 }
674 #endif /* not used */
675
676 /*
677 * Request peer certificates. Valid anytime, subsequent to
678 * a handshake attempt.
679 */
680 OSStatus
681 SSLGetPeerCertificates (SSLContextRef ctx,
682 CFArrayRef *certs)
683 {
684 uint32 numCerts;
685 CFMutableArrayRef ca;
686 CFIndex i;
687 CFDataRef cfd;
688 SSLCertificate *scert;
689
690 if(ctx == NULL) {
691 return paramErr;
692 }
693 *certs = NULL;
694
695 /*
696 * Copy peerCert, a chain of SSLCertificates, to a CFArray of
697 * CFDataRefs, each of which is one DER-encoded cert.
698 */
699 numCerts = SSLGetCertificateChainLength(ctx->peerCert);
700 if(numCerts == 0) {
701 return noErr;
702 }
703 ca = CFArrayCreateMutable(ctx->cfAllocatorRef,
704 (CFIndex)numCerts, &kCFTypeArrayCallBacks);
705 if(ca == NULL) {
706 return memFullErr;
707 }
708
709 /*
710 * We'll give the certs in the same order we store them -
711 * caller gets root first. OK?
712 */
713 scert = ctx->peerCert;
714 for(i=0; i<numCerts; i++) {
715 CASSERT(scert != NULL); /* else SSLGetCertificateChainLength
716 * broken */
717 cfd = CFDataCreate(ctx->cfAllocatorRef,
718 scert->derCert.data,
719 scert->derCert.length);
720 if(cfd == NULL) {
721 CFRelease(ca);
722 return memFullErr;
723 }
724 CFArrayAppendValue(ca, cfd);
725 scert = scert->next;
726 }
727 *certs = ca;
728 return noErr;
729 }
730
731
732