1/*
2 * Copyright (c) 2003,2011-2012,2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25#include "cert.h"
26#include "cmstpriv.h"
27#include "cmslocal.h"
28#include "secitem.h"
29#include <security_asn1/secerr.h>
30#include <Security/SecKeychain.h>
31#include <Security/SecKeychainItem.h>
32#include <Security/SecKeychainSearch.h>
33#include <Security/SecIdentity.h>
34#include <Security/SecIdentityPriv.h>
35#include <Security/SecIdentitySearch.h>
36#include <Security/SecCertificatePriv.h>
37#include <Security/SecPolicySearch.h>
38#include <Security/oidsalg.h>
39#include <Security/cssmapi.h>
40#include <Security/oidscert.h>
41#include <Security/oidscert.h>
42
43/* for errKCDuplicateItem */
44#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
45
46#define CERT_DEBUG	0
47#if	CERT_DEBUG
48#define dprintf(args...)      printf(args)
49#else
50#define dprintf(args...)
51#endif
52
53/* @@@ Remove this once it's back in the appropriate header. */
54static const uint8 X509V1IssuerNameStd[] = {INTEL_X509V3_CERT_R08, 23};
55static const CSSM_OID OID_X509V1IssuerNameStd = {INTEL_X509V3_CERT_R08_LENGTH+1,  (uint8 *)X509V1IssuerNameStd};
56
57/*
58 * Normalize a Printable String. Per RFC2459 (4.1.2.4), printable strings are case
59 * insensitive and we're supposed to ignore leading and trailing
60 * whitespace, and collapse multiple whitespace characters into one.
61 */
62static void
63CERT_NormalizeString(CSSM_DATA_PTR string)
64{
65    char *pD, *pCh, *pEos;
66
67    if (!string->Length)
68	return;
69
70    pD = pCh = (char *)string->Data;
71    pEos = pCh + string->Length - 1;
72
73    /* Strip trailing NULL terminators */
74    while(*pEos == 0)
75	pEos--;
76
77    /* Remove trailing spaces */
78    while(isspace(*pEos))
79	pEos--;
80
81    /* Point to one past last non-space character */
82    pEos++;
83
84    /* skip all leading whitespace */
85    while(isspace(*pCh) && (pCh < pEos))
86	pCh++;
87
88    /* Eliminate multiple whitespace and convent to upper case.
89     * pCh points to first non-white char.
90     * pD still points to start of string. */
91    while(pCh < pEos)
92    {
93	char ch = *pCh++;
94	*pD++ = toupper(ch);
95	if(isspace(ch))
96	{
97	    /* skip 'til next nonwhite */
98	    while(isspace(*pCh) && (pCh < pEos))
99		pCh++;
100	}
101    }
102
103    string->Length = pD - (char *)string->Data;
104}
105
106/*
107 * Normalize an RDN. Per RFC2459 (4.1.2.4), printable strings are case
108 * insensitive and we're supposed to ignore leading and trailing
109 * whitespace, and collapse multiple whitespace characters into one.
110 *
111 * Incoming NSS_Name is assumed to be entirely within specifed coder's
112 * address space; we'll be munging some of that and possibly replacing
113 * some pointers with others allocated from the same space.
114 */
115void
116CERT_NormalizeX509NameNSS(NSS_Name *nssName)
117{
118    NSS_RDN *rdn;
119
120    for (rdn = *nssName->rdns; rdn; ++rdn)
121    {
122	NSS_ATV *attr;
123	for (attr = *rdn->atvs; attr; ++attr)
124	{
125	    /*
126		* attr->value is an ASN_ANY containing an encoded
127		* string. We only normalize Prinatable String types.
128		* If we find one, decode it, normalize it, encode the
129		* result, and put the encoding back in attr->value.
130		* We temporarily "leak" the original string, which only
131		* has a lifetime of the incoming SecNssCoder.
132		*/
133	    NSS_TaggedItem *attrVal = &attr->value;
134	    if(attrVal->tag != SEC_ASN1_PRINTABLE_STRING)
135		continue;
136
137	    CERT_NormalizeString(&attrVal->item);
138	}
139    }
140}
141
142SecCertificateRef CERT_FindCertByNicknameOrEmailAddr(SecKeychainRef keychainOrArray, char *name)
143{
144   SecCertificateRef certificate;
145    OSStatus status=SecCertificateFindByEmail(keychainOrArray,name,&certificate);
146    return status==noErr?certificate:NULL;
147}
148
149SecPublicKeyRef SECKEY_CopyPublicKey(SecPublicKeyRef pubKey)
150{
151    CFRetain(pubKey);
152    return pubKey;
153}
154
155void SECKEY_DestroyPublicKey(SecPublicKeyRef pubKey)
156{
157    CFRelease(pubKey);
158}
159
160SecPublicKeyRef SECKEY_CopyPrivateKey(SecPublicKeyRef privKey)
161{
162    CFRetain(privKey);
163    return privKey;
164}
165
166void SECKEY_DestroyPrivateKey(SecPublicKeyRef privKey)
167{
168    CFRelease(privKey);
169}
170
171void CERT_DestroyCertificate(SecCertificateRef cert)
172{
173    CFRelease(cert);
174}
175
176SecCertificateRef CERT_DupCertificate(SecCertificateRef cert)
177{
178    CFRetain(cert);
179    return cert;
180}
181
182SecIdentityRef CERT_FindIdentityByUsage(SecKeychainRef keychainOrArray,
183			 char *nickname, SECCertUsage usage, Boolean validOnly, void *proto_win)
184{
185    SecIdentityRef identityRef = NULL;
186    SecCertificateRef cert = CERT_FindCertByNicknameOrEmailAddr(keychainOrArray, nickname);
187    if (!cert)
188	return NULL;
189
190    SecIdentityCreateWithCertificate(keychainOrArray, cert, &identityRef);
191    CFRelease(cert);
192
193    return identityRef;
194}
195
196SecCertificateRef CERT_FindUserCertByUsage(SecKeychainRef keychainOrArray,
197			 char *nickname,SECCertUsage usage,Boolean validOnly,void *proto_win)
198{
199    SecItemClass itemClass = kSecCertificateItemClass;
200    SecKeychainSearchRef searchRef;
201    SecKeychainItemRef itemRef = NULL;
202    OSStatus status;
203    SecKeychainAttribute attrs[1];
204    const char *serialNumber = "12345678";
205 //   const SecKeychainAttributeList attrList;
206#if 0
207    attrs[1].tag = kSecLabelItemAttr;
208    attrs[1].length = strlen(nickname)+1;
209    attrs[1].data = nickname;
210#else
211    attrs[1].tag = kSecSerialNumberItemAttr;
212    attrs[1].length = (UInt32)strlen(serialNumber)+1;
213    attrs[1].data = (uint8 *)serialNumber;
214#endif
215    SecKeychainAttributeList attrList = { 0, attrs };
216 //   12 34 56 78
217	status = SecKeychainSearchCreateFromAttributes(keychainOrArray,itemClass,&attrList,&searchRef);
218    if (status)
219    {
220        printf("CERT_FindUserCertByUsage: SecKeychainSearchCreateFromAttributes:%d",(int)status);
221        return NULL;
222    }
223	status = SecKeychainSearchCopyNext(searchRef,&itemRef);
224    if (status)
225    	printf("CERT_FindUserCertByUsage: SecKeychainSearchCopyNext:%d",(int)status);
226    CFRelease(searchRef);
227    return (SecCertificateRef)itemRef;
228}
229
230/*
231startNewClass(X509Certificate)
232CertType, kSecCertTypeItemAttr, "CertType", 0, NULL, UINT32)
233CertEncoding, kSecCertEncodingItemAttr, "CertEncoding", 0, NULL, UINT32)
234PrintName, kSecLabelItemAttr, "PrintName", 0, NULL, BLOB)
235Alias, kSecAlias, "Alias", 0, NULL, BLOB)
236Subject, kSecSubjectItemAttr, "Subject", 0, NULL, BLOB)
237Issuer, kSecIssuerItemAttr, "Issuer", 0, NULL, BLOB)
238SerialNumber, kSecSerialNumberItemAttr, "SerialNumber", 0, NULL, BLOB)
239SubjectKeyIdentifier, kSecSubjectKeyIdentifierItemAttr, "SubjectKeyIdentifier", 0, NULL, BLOB)
240PublicKeyHash, kSecPublicKeyHashItemAttr, "PublicKeyHash", 0, NULL, BLOB)
241endNewClass()
242*/
243
244CFArrayRef CERT_CertChainFromCert(SecCertificateRef cert, SECCertUsage usage, Boolean includeRoot)
245{
246    SecPolicySearchRef searchRef = NULL;
247    SecPolicyRef policy = NULL;
248    CFArrayRef wrappedCert = NULL;
249    SecTrustRef trust = NULL;
250    CFArrayRef certChain = NULL;
251    CSSM_TP_APPLE_EVIDENCE_INFO *statusChain;
252    CFDataRef actionData = NULL;
253    OSStatus status = 0;
254
255    if (!cert)
256	goto loser;
257
258    status = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &searchRef);
259    if (status)
260	goto loser;
261    status = SecPolicySearchCopyNext(searchRef, &policy);
262    if (status)
263	goto loser;
264
265    wrappedCert = CERT_CertListFromCert(cert);
266    status = SecTrustCreateWithCertificates(wrappedCert, policy, &trust);
267    if (status)
268	goto loser;
269
270    /* Tell SecTrust that we don't care if any certs in the chain have expired,
271       nor do we want to stop when encountering a cert with a trust setting;
272       we always want to build the full chain.
273    */
274    CSSM_APPLE_TP_ACTION_DATA localActionData = {
275        CSSM_APPLE_TP_ACTION_VERSION,
276        CSSM_TP_ACTION_ALLOW_EXPIRED | CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT
277    };
278    actionData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)&localActionData, sizeof(localActionData), kCFAllocatorNull);
279    if (!actionData)
280        goto loser;
281
282    status = SecTrustSetParameters(trust, CSSM_TP_ACTION_DEFAULT, actionData);
283    if (status)
284        goto loser;
285
286    status = SecTrustEvaluate(trust, NULL);
287    if (status)
288	goto loser;
289
290    status = SecTrustGetResult(trust, NULL, &certChain, &statusChain);
291    if (status)
292	goto loser;
293
294    /* We don't drop the root if there is only 1 (self signed) certificate in the chain. */
295    if (!includeRoot && CFArrayGetCount(certChain) > 1)
296    {
297	CFMutableArrayRef subChain =  CFArrayCreateMutableCopy(NULL, 0, certChain);
298	CFRelease(certChain);
299	certChain = subChain;
300	if (subChain)
301	    CFArrayRemoveValueAtIndex(subChain, CFArrayGetCount(subChain) - 1);
302    }
303
304loser:
305    if (searchRef)
306	CFRelease(searchRef);
307    if (policy)
308	CFRelease(policy);
309    if (wrappedCert)
310	CFRelease(wrappedCert);
311    if (trust)
312	CFRelease(trust);
313    if (actionData)
314	CFRelease(actionData);
315    if (certChain && status)
316    {
317	CFRelease(certChain);
318	certChain = NULL;
319    }
320
321    return certChain;
322}
323
324CFArrayRef CERT_CertListFromCert(SecCertificateRef cert)
325{
326    const void *value = cert;
327    return cert ? CFArrayCreate(NULL, &value, 1, &kCFTypeArrayCallBacks) : NULL;
328}
329
330CFArrayRef CERT_DupCertList(CFArrayRef oldList)
331{
332    CFRetain(oldList);
333    return oldList;
334}
335
336// Extract a public key object from a SubjectPublicKeyInfo
337SecPublicKeyRef CERT_ExtractPublicKey(SecCertificateRef cert)
338{
339    SecPublicKeyRef keyRef = NULL;
340    SecCertificateCopyPublicKey(cert,&keyRef);
341    return keyRef;
342}
343
344SECStatus CERT_CheckCertUsage (SecCertificateRef cert,unsigned char usage)
345{
346    // abort();
347    // @@@ It's all good, it's ok.
348    return SECSuccess;
349}
350
351// Find a certificate in the database by a email address
352// "emailAddr" is the email address to look up
353SecCertificateRef CERT_FindCertByEmailAddr(SecKeychainRef keychainOrArray, char *emailAddr)
354{
355    abort();
356    return NULL;
357}
358
359// Find a certificate in the database by a DER encoded certificate
360// "derCert" is the DER encoded certificate
361SecCertificateRef CERT_FindCertByDERCert(SecKeychainRef keychainOrArray, const SECItem *derCert)
362{
363    // @@@ Technically this should look though keychainOrArray for a cert matching this one I guess.
364    SecCertificateRef cert = NULL;
365    OSStatus rv;
366
367    rv = SecCertificateCreateFromData(derCert, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &cert);
368    if (rv && cert)
369    {
370	PORT_SetError(SEC_ERROR_NO_EMAIL_CERT);
371	CFRelease(cert);
372	cert = NULL;
373    }
374
375    return cert;
376}
377
378static int compareCssmData(
379    const CSSM_DATA *d1,
380    const CSSM_DATA *d2)
381{
382    if((d1 == NULL) || (d2 == NULL)) {
383	return 0;
384    }
385    if(d1->Length != d2->Length) {
386	return 0;
387    }
388    if(memcmp(d1->Data, d2->Data, d1->Length)) {
389	return 0;
390    }
391    return 1;
392}
393
394// Generate a certificate key from the issuer and serialnumber, then look it up in the database.
395// Return the cert if found. "issuerAndSN" is the issuer and serial number to look for
396SecCertificateRef CERT_FindCertByIssuerAndSN (CFTypeRef keychainOrArray,
397    CSSM_DATA_PTR *rawCerts, PRArenaPool *pl, const SecCmsIssuerAndSN *issuerAndSN)
398{
399    SecCertificateRef certificate;
400    int numRawCerts = SecCmsArrayCount((void **)rawCerts);
401    int dex;
402    OSStatus ortn;
403
404    /*
405     * First search the rawCerts array.
406     */
407    for(dex=0; dex<numRawCerts; dex++) {
408	ortn = SecCertificateCreateFromData(rawCerts[dex],
409	    CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
410	    &certificate);
411	if(ortn) {
412	    continue;
413	}
414	SecCmsIssuerAndSN *isn = CERT_GetCertIssuerAndSN(pl, certificate);
415	if(isn == NULL) {
416	    CFRelease(certificate);
417	    continue;
418	}
419	if(!compareCssmData(&isn->derIssuer, &issuerAndSN->derIssuer)) {
420	    CFRelease(certificate);
421	    continue;
422	}
423	if(!compareCssmData(&isn->serialNumber, &issuerAndSN->serialNumber)) {
424	    CFRelease(certificate);
425	    continue;
426	}
427	/* got it */
428	dprintf("CERT_FindCertByIssuerAndSN: found cert %p\n", certificate);
429	return certificate;
430    }
431
432    /* now search keychain(s) */
433    OSStatus status = SecCertificateFindByIssuerAndSN(keychainOrArray, &issuerAndSN->derIssuer,
434	&issuerAndSN->serialNumber, &certificate);
435    if (status)
436    {
437	PORT_SetError(SEC_ERROR_NO_EMAIL_CERT);
438	certificate = NULL;
439    }
440
441    return certificate;
442}
443
444SecCertificateRef CERT_FindCertBySubjectKeyID (CFTypeRef keychainOrArray,
445    CSSM_DATA_PTR *rawCerts, const SECItem *subjKeyID)
446{
447    SecCertificateRef certificate;
448    int numRawCerts = SecCmsArrayCount((void **)rawCerts);
449    int dex;
450    OSStatus ortn;
451    SECItem skid;
452
453    /*
454     * First search the rawCerts array.
455     */
456    for(dex=0; dex<numRawCerts; dex++) {
457	int match;
458	ortn = SecCertificateCreateFromData(rawCerts[dex],
459	    CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
460	    &certificate);
461	if(ortn) {
462	    continue;
463	}
464	if(CERT_FindSubjectKeyIDExtension(certificate, &skid)) {
465	    CFRelease(certificate);
466	    /* not present */
467	    continue;
468	}
469	match = compareCssmData(subjKeyID, &skid);
470	SECITEM_FreeItem(&skid, PR_FALSE);
471	if(match) {
472	    /* got it */
473	    return certificate;
474	}
475	CFRelease(certificate);
476    }
477
478    /* now search keychain(s) */
479    OSStatus status = SecCertificateFindBySubjectKeyID(keychainOrArray,subjKeyID,&certificate);
480    if (status)
481    {
482	PORT_SetError(SEC_ERROR_NO_EMAIL_CERT);
483	certificate = NULL;
484    }
485
486    return certificate;
487}
488
489static SecIdentityRef
490CERT_FindIdentityByCertificate (CFTypeRef keychainOrArray, SecCertificateRef certificate)
491{
492    SecIdentityRef  identity = NULL;
493    SecIdentityCreateWithCertificate(keychainOrArray, certificate, &identity);
494    if (!identity)
495	PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT);
496
497    return identity;
498}
499
500SecIdentityRef
501CERT_FindIdentityByIssuerAndSN (CFTypeRef keychainOrArray, const SecCmsIssuerAndSN *issuerAndSN)
502{
503    SecCertificateRef certificate = CERT_FindCertByIssuerAndSN(keychainOrArray, NULL, NULL, issuerAndSN);
504    if (!certificate)
505	return NULL;
506
507    return CERT_FindIdentityByCertificate(keychainOrArray, certificate);
508}
509
510SecIdentityRef
511CERT_FindIdentityBySubjectKeyID (CFTypeRef keychainOrArray, const SECItem *subjKeyID)
512{
513    SecCertificateRef certificate = CERT_FindCertBySubjectKeyID(keychainOrArray, NULL, subjKeyID);
514    if (!certificate)
515	return NULL;
516
517    return CERT_FindIdentityByCertificate(keychainOrArray, certificate);
518}
519
520// find the smime symmetric capabilities profile for a given cert
521SECItem *CERT_FindSMimeProfile(SecCertificateRef cert)
522{
523    return NULL;
524}
525
526// Return the decoded value of the subjectKeyID extension. The caller should
527// free up the storage allocated in retItem->data.
528SECStatus CERT_FindSubjectKeyIDExtension (SecCertificateRef cert, SECItem *retItem)
529{
530    CSSM_DATA_PTR fieldValue = NULL;
531    OSStatus ortn;
532    CSSM_X509_EXTENSION *extp;
533    CE_SubjectKeyID *skid;
534
535    ortn = SecCertificateCopyFirstFieldValue(cert, &CSSMOID_SubjectKeyIdentifier,
536	&fieldValue);
537    if(ortn || (fieldValue == NULL)) {
538	/* this cert doesn't have that extension */
539	return SECFailure;
540    }
541    extp = (CSSM_X509_EXTENSION *)fieldValue->Data;
542    skid = (CE_SubjectKeyID *)extp->value.parsedValue;
543    retItem->Data = (uint8 *)PORT_Alloc(skid->Length);
544    retItem->Length = skid->Length;
545    memmove(retItem->Data, skid->Data, retItem->Length);
546    SecCertificateReleaseFirstFieldValue(cert, &CSSMOID_SubjectKeyIdentifier,
547	fieldValue);
548    return SECSuccess;
549}
550
551// Extract the issuer and serial number from a certificate
552SecCmsIssuerAndSN *CERT_GetCertIssuerAndSN(PRArenaPool *pl, SecCertificateRef cert)
553{
554    OSStatus status;
555    SecCmsIssuerAndSN *certIssuerAndSN;
556    CSSM_CL_HANDLE clHandle;
557    CSSM_DATA_PTR serialNumber = 0;
558    CSSM_DATA_PTR issuer = 0;
559    CSSM_DATA certData = {};
560    CSSM_HANDLE resultsHandle = 0;
561    uint32 numberOfFields = 0;
562    CSSM_RETURN result;
563    void *mark;
564
565    mark = PORT_ArenaMark(pl);
566
567    status = SecCertificateGetCLHandle(cert, &clHandle);
568    if (status)
569	goto loser;
570    status = SecCertificateGetData(cert, &certData);
571    if (status)
572	goto loser;
573
574    /* Get the issuer from the cert. */
575    result = CSSM_CL_CertGetFirstFieldValue(clHandle, &certData,
576	&OID_X509V1IssuerNameStd, &resultsHandle, &numberOfFields, &issuer);
577
578    if (result || numberOfFields < 1)
579	goto loser;
580    result = CSSM_CL_CertAbortQuery(clHandle, resultsHandle);
581    if (result)
582	goto loser;
583
584
585    /* Get the serialNumber from the cert. */
586    result = CSSM_CL_CertGetFirstFieldValue(clHandle, &certData,
587	&CSSMOID_X509V1SerialNumber, &resultsHandle, &numberOfFields, &serialNumber);
588    if (result || numberOfFields < 1)
589	goto loser;
590    result = CSSM_CL_CertAbortQuery(clHandle, resultsHandle);
591    if (result)
592	goto loser;
593
594    /* Allocate the SecCmsIssuerAndSN struct. */
595    certIssuerAndSN = (SecCmsIssuerAndSN *)PORT_ArenaZAlloc (pl, sizeof(SecCmsIssuerAndSN));
596    if (certIssuerAndSN == NULL)
597	goto loser;
598
599    /* Copy the issuer. */
600    certIssuerAndSN->derIssuer.Data = (uint8 *) PORT_ArenaAlloc(pl, issuer->Length);
601    if (!certIssuerAndSN->derIssuer.Data)
602	goto loser;
603    PORT_Memcpy(certIssuerAndSN->derIssuer.Data, issuer->Data, issuer->Length);
604    certIssuerAndSN->derIssuer.Length = issuer->Length;
605
606    /* Copy the serialNumber. */
607    certIssuerAndSN->serialNumber.Data = (uint8 *) PORT_ArenaAlloc(pl, serialNumber->Length);
608    if (!certIssuerAndSN->serialNumber.Data)
609	goto loser;
610    PORT_Memcpy(certIssuerAndSN->serialNumber.Data, serialNumber->Data, serialNumber->Length);
611    certIssuerAndSN->serialNumber.Length = serialNumber->Length;
612
613    PORT_ArenaUnmark(pl, mark);
614
615    CSSM_CL_FreeFieldValue(clHandle, &CSSMOID_X509V1SerialNumber, serialNumber);
616    CSSM_CL_FreeFieldValue(clHandle, &OID_X509V1IssuerNameStd, issuer);
617
618    return certIssuerAndSN;
619
620loser:
621    PORT_ArenaRelease(pl, mark);
622
623    if (serialNumber)
624	CSSM_CL_FreeFieldValue(clHandle, &CSSMOID_X509V1SerialNumber, serialNumber);
625    if (issuer)
626	CSSM_CL_FreeFieldValue(clHandle, &OID_X509V1IssuerNameStd, issuer);
627
628    PORT_SetError(SEC_INTERNAL_ONLY);
629    return NULL;
630}
631
632// import a collection of certs into the temporary or permanent cert database
633SECStatus CERT_ImportCerts(SecKeychainRef keychain, SECCertUsage usage, unsigned int ncerts,
634    SECItem **derCerts, SecCertificateRef **retCerts, Boolean keepCerts, Boolean caOnly, char *nickname)
635{
636    OSStatus rv = SECFailure;
637    SecCertificateRef cert;
638    unsigned int ci;
639
640    // @@@ Do something with caOnly and nickname
641    if (caOnly || nickname)
642	abort();
643
644    for (ci = 0; ci < ncerts; ++ci)
645    {
646	rv = SecCertificateCreateFromData(derCerts[ci], CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &cert);
647	if (rv)
648	    break;
649	if (keepCerts)
650	{
651	    rv = SecCertificateAddToKeychain(cert, keychain);
652	    if (rv)
653	    {
654		if (rv == errKCDuplicateItem)
655		    rv = noErr;
656		else
657		{
658		    CFRelease(cert);
659		    break;
660		}
661	    }
662	}
663
664	if (retCerts)
665	{
666	    // @@@ not yet
667	    abort();
668	}
669	else
670	    CFRelease(cert);
671    }
672
673    return rv;
674}
675
676SECStatus CERT_SaveSMimeProfile(SecCertificateRef cert, SECItem *emailProfile,SECItem *profileTime)
677{
678    fprintf(stderr, "WARNING: CERT_SaveSMimeProfile unimplemented\n");
679    return SECSuccess;
680}
681
682// Check the hostname to make sure that it matches the shexp that
683// is given in the common name of the certificate.
684SECStatus CERT_VerifyCertName(SecCertificateRef cert, const char *hostname)
685{
686    fprintf(stderr, "WARNING: CERT_VerifyCertName unimplemented\n");
687    return SECSuccess;
688}
689
690/*
691** OLD OBSOLETE FUNCTIONS with enum SECCertUsage - DO NOT USE FOR NEW CODE
692** verify a certificate by checking validity times against a certain time,
693** that we trust the issuer, and that the signature on the certificate is
694** valid.
695**	"cert" the certificate to verify
696**	"checkSig" only check signatures if true
697*/
698SECStatus
699CERT_VerifyCert(SecKeychainRef keychainOrArray, SecCertificateRef cert,
700		const CSSM_DATA_PTR *otherCerts,    /* intermediates */
701		CFTypeRef policies, CFAbsoluteTime stime, SecTrustRef *trustRef)
702{
703    CFMutableArrayRef certificates = NULL;
704    SecTrustRef trust = NULL;
705    OSStatus rv;
706    int numOtherCerts = SecCmsArrayCount((void **)otherCerts);
707    int dex;
708
709    /*
710     * Certs to evaluate: first the leaf - our cert - then all the rest we know
711     * about. It's OK for otherCerts to contain a copy of the leaf.
712     */
713    certificates = CFArrayCreateMutable(NULL, numOtherCerts + 1, &kCFTypeArrayCallBacks);
714    CFArrayAppendValue(certificates, cert);
715    for(dex=0; dex<numOtherCerts; dex++) {
716	SecCertificateRef intCert;
717
718	rv = SecCertificateCreateFromData(otherCerts[dex],
719	    CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
720	    &intCert);
721	if(rv) {
722	    goto loser;
723	}
724	CFArrayAppendValue(certificates, intCert);
725	CFRelease(intCert);
726    }
727    rv = SecTrustCreateWithCertificates(certificates, policies, &trust);
728    CFRelease(certificates);
729    certificates = NULL;
730    if (rv)
731	goto loser;
732
733    rv = SecTrustSetKeychains(trust, keychainOrArray);
734    if (rv)
735	goto loser;
736
737    CFDateRef verifyDate = CFDateCreate(NULL, stime);
738    rv = SecTrustSetVerifyDate(trust, verifyDate);
739    CFRelease(verifyDate);
740    if (rv)
741	goto loser;
742
743    if (trustRef)
744    {
745	*trustRef = trust;
746    }
747    else
748    {
749	SecTrustResultType result;
750	/* The caller doesn't want a SecTrust object, so let's evaluate it for them. */
751	rv = SecTrustEvaluate(trust, &result);
752	if (rv)
753	    goto loser;
754
755	switch (result)
756	{
757	case kSecTrustResultProceed:
758	case kSecTrustResultUnspecified:
759	    /* TP Verification succeeded and there was either a UserTurst entry
760	       telling us to procceed, or no user trust setting was specified. */
761	    CFRelease(trust);
762	    break;
763	default:
764	    PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
765	    rv = SECFailure;
766	    goto loser;
767	    break;
768	}
769    }
770
771    return SECSuccess;
772loser:
773    if (trust)
774	CFRelease(trust);
775    if(certificates)
776	CFRelease(certificates);
777    return rv;
778}
779
780CFTypeRef
781CERT_PolicyForCertUsage(SECCertUsage certUsage)
782{
783    SecPolicySearchRef search = NULL;
784    SecPolicyRef policy = NULL;
785    const CSSM_OID *policyOID;
786    OSStatus rv;
787
788    switch (certUsage)
789    {
790    case certUsageSSLServerWithStepUp:
791    case certUsageSSLCA:
792    case certUsageVerifyCA:
793    case certUsageAnyCA:
794	goto loser;
795	break;
796    case certUsageSSLClient:
797    case certUsageSSLServer:
798	policyOID = &CSSMOID_APPLE_TP_SSL;
799	break;
800    case certUsageUserCertImport:
801	policyOID = &CSSMOID_APPLE_TP_CSR_GEN;
802	break;
803    case certUsageStatusResponder:
804	policyOID = &CSSMOID_APPLE_TP_REVOCATION_OCSP;
805	break;
806    case certUsageObjectSigner:
807    case certUsageProtectedObjectSigner:
808	policyOID = &CSSMOID_APPLE_ISIGN;
809	break;
810    case certUsageEmailSigner:
811    case certUsageEmailRecipient:
812	policyOID = &CSSMOID_APPLE_X509_BASIC;
813	break;
814    default:
815	goto loser;
816    }
817    rv = SecPolicySearchCreate(CSSM_CERT_X_509v3, policyOID, NULL, &search);
818    if (rv)
819	goto loser;
820
821    rv = SecPolicySearchCopyNext(search, &policy);
822    if (rv)
823	goto loser;
824
825loser:
826    if(search) CFRelease(search);
827    return policy;
828}
829