]> git.saurik.com Git - apple/security.git/blob - AppleX509TP/tpPolicies.cpp
Security-29.tar.gz
[apple/security.git] / AppleX509TP / tpPolicies.cpp
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 policies.cpp - TP module policy implementation
21
22 Created 10/9/2000 by Doug Mitchell.
23 */
24
25 #include <Security/cssmtype.h>
26 #include <Security/cssmapi.h>
27 #include "tpPolicies.h"
28 #include <Security/oidsattr.h>
29 #include <Security/cssmerr.h>
30 #include "tpdebugging.h"
31 #include "rootCerts.h"
32 #include "certGroupUtils.h"
33 #include <Security/x509defs.h>
34 #include <Security/oidscert.h>
35 #include <Security/certextensions.h>
36 #include <Security/cssmapple.h>
37 #include <string.h>
38
39 /*
40 * Our private per-extension info. One of these per (understood) extension per
41 * cert.
42 */
43 typedef struct {
44 CSSM_BOOL present;
45 CSSM_BOOL critical;
46 CE_Data *extnData; // mallocd by CL
47 CSSM_DATA *valToFree; // the data we pass to freeField()
48 } iSignExtenInfo;
49
50 /*
51 * Struct to keep track of info pertinent to one cert.
52 */
53 typedef struct {
54
55 /* extensions pertinent to iSign */
56 iSignExtenInfo authorityId;
57 iSignExtenInfo subjectId;
58 iSignExtenInfo keyUsage;
59 iSignExtenInfo extendKeyUsage;
60 iSignExtenInfo basicConstraints;
61 iSignExtenInfo netscapeCertType;
62
63 /* flag indicating presence of a critical extension we don't understand */
64 CSSM_BOOL foundUnknownCritical;
65
66 } iSignCertInfo;
67
68
69 /*
70 * Setup a single iSignExtenInfo. Called once per known extension
71 * per cert.
72 */
73 static CSSM_RETURN tpSetupExtension(
74 CssmAllocator &alloc,
75 CSSM_DATA *extnData,
76 iSignExtenInfo *extnInfo) // which component of certInfo
77 {
78 if(extnData->Length != sizeof(CSSM_X509_EXTENSION)) {
79 errorLog0("tpSetupExtension: malformed CSSM_FIELD\n");
80 return CSSMERR_TP_UNKNOWN_FORMAT;
81 }
82 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)extnData->Data;
83 extnInfo->present = CSSM_TRUE;
84 extnInfo->critical = cssmExt->critical;
85 extnInfo->extnData = (CE_Data *)cssmExt->value.parsedValue;
86 extnInfo->valToFree = extnData;
87 return CSSM_OK;
88 }
89
90 /*
91 * Fetch a known extension, set up associated iSignExtenInfo if present.
92 */
93 static CSSM_RETURN iSignFetchExtension(
94 CssmAllocator &alloc,
95 TPCertInfo *tpCert,
96 const CSSM_OID *fieldOid, // which extension to fetch
97 iSignExtenInfo *extnInfo) // where the info goes
98 {
99 CSSM_DATA_PTR fieldValue; // mallocd by CL
100 CSSM_RETURN crtn;
101
102 crtn = tpCert->fetchField(fieldOid, &fieldValue);
103 switch(crtn) {
104 case CSSM_OK:
105 break;
106 case CSSMERR_CL_NO_FIELD_VALUES:
107 /* field not present, OK */
108 return CSSM_OK;
109 default:
110 return crtn;
111 }
112 return tpSetupExtension(alloc,
113 fieldValue,
114 extnInfo);
115 }
116
117 /*
118 * Search for al unknown extensions. If we find one which is flagged critical,
119 * flag certInfo->foundUnknownCritical. Only returns error on gross errors.
120 */
121 static CSSM_RETURN iSignSearchUnknownExtensions(
122 TPCertInfo *tpCert,
123 iSignCertInfo *certInfo)
124 {
125 CSSM_RETURN crtn;
126 CSSM_DATA_PTR fieldValue = NULL;
127 CSSM_HANDLE searchHand = CSSM_INVALID_HANDLE;
128 uint32 numFields = 0;
129
130 crtn = CSSM_CL_CertGetFirstCachedFieldValue(tpCert->clHand(),
131 tpCert->cacheHand(),
132 &CSSMOID_X509V3CertificateExtensionCStruct,
133 &searchHand,
134 &numFields,
135 &fieldValue);
136 switch(crtn) {
137 case CSSM_OK:
138 /* found one, proceed */
139 break;
140 case CSSMERR_CL_NO_FIELD_VALUES:
141 /* no unknown extensions present, OK */
142 return CSSM_OK;
143 default:
144 return crtn;
145 }
146
147 if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
148 errorLog0("iSignSearchUnknownExtensions: malformed CSSM_FIELD\n");
149 return CSSMERR_TP_UNKNOWN_FORMAT;
150 }
151 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
152 if(cssmExt->critical) {
153 /* BRRZAPP! Found an unknown extension marked critical */
154 certInfo->foundUnknownCritical = CSSM_TRUE;
155 goto fini;
156 }
157 CSSM_CL_FreeFieldValue(tpCert->clHand(),
158 &CSSMOID_X509V3CertificateExtensionCStruct,
159 fieldValue);
160 fieldValue = NULL;
161
162 /* process remaining unknown extensions */
163 for(unsigned i=1; i<numFields; i++) {
164 crtn = CSSM_CL_CertGetNextCachedFieldValue(tpCert->clHand(),
165 searchHand,
166 &fieldValue);
167 if(crtn) {
168 /* should never happen */
169 errorLog0("searchUnknownExtensions: GetNextCachedFieldValue error\n");
170 break;
171 }
172 if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
173 errorLog0("iSignSearchUnknownExtensions: malformed CSSM_FIELD\n");
174 crtn = CSSMERR_TP_UNKNOWN_FORMAT;
175 break;
176 }
177 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
178 if(cssmExt->critical) {
179 /* BRRZAPP! Found an unknown extension marked critical */
180 certInfo->foundUnknownCritical = CSSM_TRUE;
181 break;
182 }
183 CSSM_CL_FreeFieldValue(tpCert->clHand(),
184 &CSSMOID_X509V3CertificateExtensionCStruct,
185 fieldValue);
186 fieldValue = NULL;
187 } /* for additional fields */
188
189 fini:
190 if(fieldValue) {
191 CSSM_CL_FreeFieldValue(tpCert->clHand(),
192 &CSSMOID_X509V3CertificateExtensionCStruct,
193 fieldValue);
194 }
195 if(searchHand != CSSM_INVALID_HANDLE) {
196 CSSM_CL_CertAbortQuery(tpCert->clHand(), searchHand);
197 }
198 return crtn;
199 }
200 /*
201 * Given a TPCertInfo, fetch the associated iSignCertInfo fields.
202 * Returns CSSM_FAIL on error.
203 */
204 static CSSM_RETURN iSignGetCertInfo(
205 CssmAllocator &alloc,
206 TPCertInfo *tpCert,
207 iSignCertInfo *certInfo)
208 {
209 CSSM_RETURN crtn;
210
211 /* first grind thru the extensions we're interested in */
212 crtn = iSignFetchExtension(alloc,
213 tpCert,
214 &CSSMOID_AuthorityKeyIdentifier,
215 &certInfo->authorityId);
216 if(crtn) {
217 return crtn;
218 }
219 crtn = iSignFetchExtension(alloc,
220 tpCert,
221 &CSSMOID_SubjectKeyIdentifier,
222 &certInfo->subjectId);
223 if(crtn) {
224 return crtn;
225 }
226 crtn = iSignFetchExtension(alloc,
227 tpCert,
228 &CSSMOID_KeyUsage,
229 &certInfo->keyUsage);
230 if(crtn) {
231 return crtn;
232 }
233 crtn = iSignFetchExtension(alloc,
234 tpCert,
235 &CSSMOID_ExtendedKeyUsage,
236 &certInfo->extendKeyUsage);
237 if(crtn) {
238 return crtn;
239 }
240 crtn = iSignFetchExtension(alloc,
241 tpCert,
242 &CSSMOID_BasicConstraints,
243 &certInfo->basicConstraints);
244 if(crtn) {
245 return crtn;
246 }
247 crtn = iSignFetchExtension(alloc,
248 tpCert,
249 &CSSMOID_NetscapeCertType,
250 &certInfo->netscapeCertType);
251 if(crtn) {
252 return crtn;
253 }
254
255 /* now look for extensions we don't understand - the only thing we're interested
256 * in is the critical flag. */
257 return iSignSearchUnknownExtensions(tpCert, certInfo);
258 }
259
260 /*
261 * Free (via CL) the fields allocated in iSignGetCertInfo().
262 */
263 static void iSignFreeCertInfo(
264 CSSM_CL_HANDLE clHand,
265 iSignCertInfo *certInfo)
266 {
267 if(certInfo->authorityId.present) {
268 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_AuthorityKeyIdentifier,
269 certInfo->authorityId.valToFree);
270 }
271 if(certInfo->subjectId.present) {
272 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_SubjectKeyIdentifier,
273 certInfo->subjectId.valToFree);
274 }
275 if(certInfo->keyUsage.present) {
276 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_KeyUsage,
277 certInfo->keyUsage.valToFree);
278 }
279 if(certInfo->extendKeyUsage.present) {
280 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_ExtendedKeyUsage,
281 certInfo->extendKeyUsage.valToFree);
282 }
283 if(certInfo->basicConstraints.present) {
284 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_BasicConstraints,
285 certInfo->basicConstraints.valToFree);
286 }
287 if(certInfo->netscapeCertType.present) {
288 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_NetscapeCertType,
289 certInfo->netscapeCertType.valToFree);
290 }
291 }
292
293 /*
294 * Common code for comparing a root to a list of known embedded roots.
295 */
296 static CSSM_BOOL tp_isKnownRootCert(
297 TPCertInfo *rootCert, // raw cert to compare
298 const tpRootCert *knownRoots,
299 unsigned numKnownRoots)
300 {
301 const CSSM_DATA *subjectName = NULL;
302 CSSM_DATA_PTR publicKey = NULL;
303 unsigned dex;
304 CSSM_BOOL brtn = CSSM_FALSE;
305 CSSM_DATA_PTR valToFree = NULL;
306
307 subjectName = rootCert->subjectName();
308 publicKey = tp_CertGetPublicKey(rootCert, &valToFree);
309 if(publicKey == NULL) {
310 errorLog0("tp_isKnownRootCert: error retrieving public key info!\n");
311 goto errOut;
312 }
313
314 /*
315 * Grind thru the list of known certs, demanding perfect match of
316 * both fields
317 */
318 for(dex=0; dex<numKnownRoots; dex++) {
319 if(!tpCompareCssmData(subjectName,
320 knownRoots[dex].subjectName)) {
321 continue;
322 }
323 if(!tpCompareCssmData(publicKey,
324 knownRoots[dex].publicKey)) {
325 continue;
326 }
327 #if ENABLE_APPLE_DEBUG_ROOT
328 if( dex == (knownRoots - 1) ){
329 brtn = CSSM_FALSE;
330 //tpSetError(CSSM_TP_DEBUG_CERT);
331 break;
332 }
333 #endif
334 brtn = CSSM_TRUE;
335 break;
336 }
337 errOut:
338 tp_CertFreePublicKey(rootCert->clHand(), valToFree);
339 return brtn;
340 }
341
342 /*
343 * See if specified root cert is a known (embedded) iSign root cert.
344 * Returns CSSM_TRUE if the cert is a known root cert.
345 */
346 static CSSM_BOOL tp_isIsignRootCert(
347 CSSM_CL_HANDLE clHand,
348 TPCertInfo *rootCert) // raw cert from cert group
349 {
350 return tp_isKnownRootCert(rootCert, iSignRootCerts, numiSignRootCerts);
351 }
352
353 /*
354 * See if specified root cert is a known (embedded) SSL root cert.
355 * Returns CSSM_TRUE if the cert is a known root cert.
356 */
357 static CSSM_BOOL tp_isSslRootCert(
358 CSSM_CL_HANDLE clHand,
359 TPCertInfo *rootCert) // raw cert from cert group
360 {
361 return tp_isKnownRootCert(rootCert, sslRootCerts, numSslRootCerts);
362 }
363
364 /*
365 * Attempt to verify specified cert (from the end of a chain) with one of
366 * our known SSL roots.
367 */
368 CSSM_BOOL tp_verifyWithSslRoots(
369 CSSM_CL_HANDLE clHand,
370 CSSM_CSP_HANDLE cspHand,
371 TPCertInfo *certToVfy) // last in chain, not root
372 {
373 CSSM_KEY rootKey; // pub key manufactured from tpRootCert info
374 CSSM_CC_HANDLE ccHand; // signature context
375 CSSM_RETURN crtn;
376 unsigned dex;
377 const tpRootCert *rootInfo;
378 CSSM_BOOL brtn = CSSM_FALSE;
379 CSSM_KEYHEADER *hdr = &rootKey.KeyHeader;
380 CSSM_X509_ALGORITHM_IDENTIFIER_PTR algId;
381 CSSM_DATA_PTR valToFree = NULL;
382 CSSM_ALGORITHMS sigAlg;
383
384 memset(&rootKey, 0, sizeof(CSSM_KEY));
385
386 /*
387 * Get signature algorithm from subject key
388 */
389 algId = tp_CertGetAlgId(certToVfy, &valToFree);
390 if(algId == NULL) {
391 /* bad cert */
392 return CSSM_FALSE;
393 }
394 /* subsequest errors to errOut: */
395
396 /* map to key and signature algorithm */
397 sigAlg = tpOidToAldId(&algId->algorithm, &hdr->AlgorithmId);
398 if(sigAlg == CSSM_ALGID_NONE) {
399 errorLog0("tp_verifyWithSslRoots: unknown sig alg\n");
400 goto errOut;
401 }
402
403 /* Set up other constant key fields */
404 hdr->BlobType = CSSM_KEYBLOB_RAW;
405 switch(hdr->AlgorithmId) {
406 case CSSM_ALGID_RSA:
407 hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
408 break;
409 case CSSM_ALGID_DSA:
410 hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_FIPS186;
411 break;
412 case CSSM_ALGID_FEE:
413 hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
414 break;
415 default:
416 /* punt */
417 hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_NONE;
418 }
419 hdr->KeyClass = CSSM_KEYCLASS_PUBLIC_KEY;
420 hdr->KeyAttr = CSSM_KEYATTR_MODIFIABLE | CSSM_KEYATTR_EXTRACTABLE;
421 hdr->KeyUsage = CSSM_KEYUSE_VERIFY;
422
423 for(dex=0; dex<numSslRootCerts; dex++) {
424 /* only variation in key in the loop - raw key bits and size */
425 rootInfo = &sslRootCerts[dex];
426 if(!tpIsSameName(rootInfo->subjectName, certToVfy->issuerName())) {
427 /* not this root */
428 continue;
429 }
430 rootKey.KeyData = *rootInfo->publicKey;
431 hdr->LogicalKeySizeInBits = rootInfo->keySize;
432 crtn = CSSM_CSP_CreateSignatureContext(cspHand,
433 sigAlg,
434 NULL, // AcccedCred
435 &rootKey,
436 &ccHand);
437 if(crtn) {
438 errorLog0("tp_verifyWithSslRoots: CSSM_CSP_CreateSignatureContext err\n");
439 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
440 }
441 crtn = CSSM_CL_CertVerify(clHand,
442 ccHand,
443 certToVfy->certData(),
444 NULL, // no signer cert
445 NULL, // VerifyScope
446 0); // ScopeSize
447 CSSM_DeleteContext(ccHand);
448 if(crtn == CSSM_OK) {
449 /* success! */
450 brtn = CSSM_TRUE;
451 break;
452 }
453 }
454 errOut:
455 if(valToFree != NULL) {
456 tp_CertFreeAlgId(clHand, valToFree);
457 }
458 return brtn;
459 }
460
461 /*
462 * RFC2459 says basicConstraints must be flagged critical for
463 * CA certs, but Verisign doesn't work that way.
464 */
465 #define BASIC_CONSTRAINTS_MUST_BE_CRITICAL 0
466
467 /*
468 * TP iSign spec says Extended Key Usage required for leaf certs,
469 * but Verisign doesn't work that way.
470 */
471 #define EXTENDED_KEY_USAGE_REQUIRED_FOR_LEAF 0
472
473 /*
474 * TP iSign spec says Subject Alternate Name required for leaf certs,
475 * but Verisign doesn't work that way.
476 */
477 #define SUBJECT_ALT_NAME_REQUIRED_FOR_LEAF 0
478
479 /*
480 * TP iSign spec originally required KeyUsage for all certs, but
481 * Verisign doesn't have that in their roots.
482 */
483 #define KEY_USAGE_REQUIRED_FOR_ROOT 0
484
485 /*
486 * Public routine to perform TP verification on a constructed
487 * cert group.
488 * Returns CSSM_TRUE on success.
489 * Asumes the chain has passed basic subject/issuer verification. First cert of
490 * incoming certGroup is end-entity (leaf).
491 *
492 * Per-policy details:
493 * iSign: Assumes that last cert in incoming certGroup is a root cert.
494 * Also assumes a cert group of more than one cert.
495 * kTPx509Basic: CertGroup of length one allowed.
496 */
497 CSSM_RETURN tp_policyVerify(
498 TPPolicy policy,
499 CssmAllocator &alloc,
500 CSSM_CL_HANDLE clHand,
501 CSSM_CSP_HANDLE cspHand,
502 TPCertGroup *certGroup,
503 CSSM_BOOL verifiedToRoot) // last cert is good root
504 {
505 iSignCertInfo *certInfo = NULL;
506 uint32 numCerts;
507 iSignCertInfo *thisCertInfo;
508 uint16 expUsage;
509 uint16 actUsage;
510 unsigned certDex;
511 CSSM_BOOL cA = CSSM_FALSE;// init for compiler warning
512 CSSM_BOOL isLeaf; // end entity
513 CSSM_BOOL isRoot; // root cert
514 CE_ExtendedKeyUsage *extendUsage;
515 CE_AuthorityKeyID *authorityId;
516 CSSM_RETURN outErr = CSSM_OK;
517 TPCertInfo *lastCert;
518
519 /* First, kTPDefault is a nop here */
520 if(policy == kTPDefault) {
521 return CSSM_OK;
522 }
523
524 if(certGroup == NULL) {
525 return CSSMERR_TP_INVALID_CERTGROUP;
526 }
527 numCerts = certGroup->numCerts();
528 if(numCerts == 0) {
529 return CSSMERR_TP_INVALID_CERTGROUP;
530 }
531 if(policy == kTPiSign) {
532 if(!verifiedToRoot) {
533 /* no way, this requires a root cert */
534 return CSSMERR_TP_INVALID_CERTGROUP;
535 }
536 if(numCerts <= 1) {
537 /* nope, not for iSign */
538 return CSSMERR_TP_INVALID_CERTGROUP;
539 }
540 }
541
542 /* cook up an iSignCertInfo array */
543 certInfo = (iSignCertInfo *)tpCalloc(alloc, numCerts, sizeof(iSignCertInfo));
544 /* subsequent errors to errOut: */
545
546 /* fill it with interesting info from parsed certs */
547 for(certDex=0; certDex<numCerts; certDex++) {
548 if(iSignGetCertInfo(alloc,
549 certGroup->certAtIndex(certDex),
550 &certInfo[certDex])) {
551 outErr = CSSMERR_TP_INVALID_CERTIFICATE;
552 goto errOut;
553 }
554 }
555
556 /*
557 * OK, the heart of TP enforcement.
558 * First check for presence of required extensions and
559 * critical extensions we don't understand.
560 */
561 for(certDex=0; certDex<numCerts; certDex++) {
562 thisCertInfo = &certInfo[certDex];
563
564 if(thisCertInfo->foundUnknownCritical) {
565 /* illegal for all policies */
566 errorLog0("tp_policyVerify: critical flag in unknown extension\n");
567 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
568 goto errOut;
569 }
570
571 /*
572 * Note it's possible for both of these to be true, for a
573 * of length one (kTPx509Basic only!)
574 */
575 isLeaf = (certDex == 0) ? CSSM_TRUE : CSSM_FALSE;
576 isRoot = (certDex == (numCerts - 1)) ? CSSM_TRUE : CSSM_FALSE;
577
578 /*
579 * BasicConstraints.cA
580 * iSign: required in all but leaf and root,
581 * for which it is optional (with default values of false
582 * for leaf and true for root).
583 * kTPx509Basic,
584 * kTP_SSL: always optional, default of false for leaf and
585 * true for others
586 * All: cA must be false for leaf, true for others
587 */
588 if(!thisCertInfo->basicConstraints.present) {
589 if(isLeaf) {
590 /* cool, use default; note that kTPx509Basic with
591 * certGroup length of one may take this case */
592 cA = CSSM_FALSE;
593 }
594 else if(isRoot) {
595 /* cool, use default */
596 cA = CSSM_TRUE;
597 }
598 else {
599 switch(policy) {
600 case kTPx509Basic:
601 case kTP_SSL:
602 /*
603 * not present, not leaf, not root, kTPx509Basic
604 * ....OK; infer as true
605 */
606 cA = CSSM_TRUE;
607 break;
608 case kTPiSign:
609 /* required for iSign in this position */
610 errorLog0("tp_policyVerify: no basicConstraints\n");
611 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
612 goto errOut;
613 default:
614 /* not reached */
615 break;
616 }
617 }
618 }
619 else {
620 /* basicConstraints present */
621 #if BASIC_CONSTRAINTS_MUST_BE_CRITICAL
622 /* disabled for verisign compatibility */
623 if(!thisCertInfo->basicConstraints.critical) {
624 /* per RFC 2459 */
625 errorLog0("tp_policyVerify: basicConstraints marked not critical\n");
626 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
627 goto errOut;
628 }
629 #endif /* BASIC_CONSTRAINTS_MUST_BE_CRITICAL */
630 cA = thisCertInfo->basicConstraints.extnData->basicConstraints.cA;
631 }
632
633 if(isLeaf) {
634 /* special case to allow a chain of length 1, leaf and root
635 * both true (kTPx509Basic, kTP_SSL only) */
636 if(cA && !isRoot) {
637 errorLog0("tp_policyVerify: cA true for leaf\n");
638 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
639 goto errOut;
640 }
641 } else if(!cA) {
642 errorLog0("tp_policyVerify: cA false for non-leaf\n");
643 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
644 goto errOut;
645 }
646
647 /*
648 * Authority Key Identifier optional
649 * iSign : only allowed in !root.
650 * If present, must not be critical.
651 * kTPx509Basic :
652 * kTP_SSL : ignored (though used later for chain verification)
653 */
654 if((policy == kTPiSign) && thisCertInfo->authorityId.present) {
655 if(isRoot) {
656 errorLog0("tp_policyVerify: authorityId in root\n");
657 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
658 goto errOut;
659 }
660 if(thisCertInfo->authorityId.critical) {
661 /* illegal per RFC 2459 */
662 errorLog0("tp_policyVerify: authorityId marked critical\n");
663 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
664 goto errOut;
665 }
666 }
667
668 /*
669 * Subject Key Identifier optional
670 * iSign : can't be critical.
671 * kTPx509Basic,
672 * kTP_SSL : ignored (though used later for chain verification)
673 */
674 if(thisCertInfo->subjectId.present) {
675 if((policy == kTPiSign) && thisCertInfo->subjectId.critical) {
676 errorLog0("tp_policyVerify: subjectId marked critical\n");
677 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
678 goto errOut;
679 }
680 }
681
682 /*
683 * Key Usage optional except as noted required
684 * iSign : required for non-root/non-leaf
685 * Leaf cert : if present, usage = digitalSignature
686 * Exception : if leaf, and keyUsage not present,
687 * netscape-cert-type must be present, with
688 * Object Signing bit set
689 * kTPx509Basic : non-leaf : usage = keyCertSign
690 * Leaf: don't care
691 */
692 if(thisCertInfo->keyUsage.present) {
693 /*
694 * Leaf cert: usage = digitalSignature
695 * Others: usage = keyCertSign
696 * We only require that one bit to be set, we ignore others.
697 */
698 if(isLeaf) {
699 if(policy == kTPiSign) {
700 expUsage = CE_KU_DigitalSignature;
701 }
702 else {
703 /* hack to accept whatever's there */
704 expUsage = thisCertInfo->keyUsage.extnData->keyUsage;
705 }
706 }
707 else {
708 /* this is true for all policies */
709 expUsage = CE_KU_KeyCertSign;
710 }
711 actUsage = thisCertInfo->keyUsage.extnData->keyUsage;
712 if(!(actUsage & expUsage)) {
713 errorLog2("tp_policyVerify: bad keyUsage (leaf %s; usage 0x%x)\n",
714 (certDex == 0) ? "TRUE" : "FALSE", actUsage);
715 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
716 goto errOut;
717 }
718 }
719 else if(policy == kTPiSign) {
720 /*
721 * iSign requires keyUsage present for non root OR
722 * netscape-cert-type/ObjectSigning for leaf
723 */
724 if(isLeaf && thisCertInfo->netscapeCertType.present) {
725 CE_NetscapeCertType ct =
726 thisCertInfo->netscapeCertType.extnData->netscapeCertType;
727
728 if(!(ct & CE_NCT_ObjSign)) {
729 errorLog0("tp_policyVerify: netscape-cert-type, !ObjectSign\n");
730 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
731 goto errOut;
732 }
733 }
734 else if(!isRoot) {
735 errorLog0("tp_policyVerify: !isRoot, no keyUsage, !(leaf and netscapeCertType)\n");
736 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
737 goto errOut;
738 }
739 }
740 } /* for certDex, checking presence of extensions */
741
742 /*
743 * Special case checking for leaf (end entity) cert
744 *
745 * iSign only: Extended key usage, optional for leaf,
746 * value CSSMOID_ExtendedUseCodeSigning
747 */
748 if((policy == kTPiSign) && certInfo[0].extendKeyUsage.present) {
749 extendUsage = &certInfo[0].extendKeyUsage.extnData->extendedKeyUsage;
750 if(extendUsage->numPurposes != 1) {
751 errorLog1("tp_policyVerify: bad extendUsage->numPurposes (%d)\n",
752 (int)extendUsage->numPurposes);
753 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
754 goto errOut;
755 }
756 if(!tpCompareOids(extendUsage->purposes,
757 &CSSMOID_ExtendedUseCodeSigning)) {
758 errorLog0("tp_policyVerify: bad extendKeyUsage\n");
759 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
760 goto errOut;
761 }
762 }
763
764 /*
765 * Verify authorityId-->subjectId linkage.
766 * All optional - skip if needed fields not present.
767 * Also, always skip last (root) cert.
768 */
769 for(certDex=0; certDex<(numCerts-1); certDex++) {
770 if(!certInfo[certDex].authorityId.present ||
771 !certInfo[certDex+1].subjectId.present) {
772 continue;
773 }
774 authorityId = &certInfo[certDex].authorityId.extnData->authorityKeyID;
775 if(!authorityId->keyIdentifierPresent) {
776 /* we only know how to compare keyIdentifier */
777 continue;
778 }
779 if(!tpCompareCssmData(&authorityId->keyIdentifier,
780 &certInfo[certDex+1].subjectId.extnData->subjectKeyID)) {
781 errorLog0("tp_policyVerify: bad key ID linkage\n");
782 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
783 goto errOut;
784 }
785 }
786
787 /* iSign, SSL: compare root against known root certs */
788 lastCert = certGroup->lastCert();
789 if(policy == kTPiSign) {
790 bool brtn = tp_isIsignRootCert(clHand, lastCert);
791 if(!brtn) {
792 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
793 }
794 }
795 else if(verifiedToRoot && (policy == kTP_SSL)) {
796 /* note SSL doesn't require root here */
797 bool brtn = tp_isSslRootCert(clHand, lastCert);
798 if(!brtn) {
799 outErr = CSSMERR_TP_INVALID_ANCHOR_CERT;
800 }
801 }
802 else {
803 outErr = CSSM_OK;
804 }
805 errOut:
806 /* free resources */
807 for(certDex=0; certDex<numCerts; certDex++) {
808 thisCertInfo = &certInfo[certDex];
809 iSignFreeCertInfo(clHand, thisCertInfo);
810 }
811 tpFree(alloc, certInfo);
812 return outErr;
813 }