]> git.saurik.com Git - apple/security.git/blob - AppleX509TP/tpPolicies.cpp
Security-54.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 #include <ctype.h>
39 #include <assert.h>
40
41 /*
42 * Our private per-extension info. One of these per (understood) extension per
43 * cert.
44 */
45 typedef struct {
46 CSSM_BOOL present;
47 CSSM_BOOL critical;
48 CE_Data *extnData; // mallocd by CL
49 CSSM_DATA *valToFree; // the data we pass to freeField()
50 } iSignExtenInfo;
51
52 /*
53 * Struct to keep track of info pertinent to one cert.
54 */
55 typedef struct {
56
57 /* extensions pertinent to iSign */
58 iSignExtenInfo authorityId;
59 iSignExtenInfo subjectId;
60 iSignExtenInfo keyUsage;
61 iSignExtenInfo extendKeyUsage;
62 iSignExtenInfo basicConstraints;
63 iSignExtenInfo netscapeCertType;
64
65 /* flag indicating presence of a critical extension we don't understand */
66 CSSM_BOOL foundUnknownCritical;
67
68 } iSignCertInfo;
69
70
71 /*
72 * Setup a single iSignExtenInfo. Called once per known extension
73 * per cert.
74 */
75 static CSSM_RETURN tpSetupExtension(
76 CssmAllocator &alloc,
77 CSSM_DATA *extnData,
78 iSignExtenInfo *extnInfo) // which component of certInfo
79 {
80 if(extnData->Length != sizeof(CSSM_X509_EXTENSION)) {
81 errorLog0("tpSetupExtension: malformed CSSM_FIELD\n");
82 return CSSMERR_TP_UNKNOWN_FORMAT;
83 }
84 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)extnData->Data;
85 extnInfo->present = CSSM_TRUE;
86 extnInfo->critical = cssmExt->critical;
87 extnInfo->extnData = (CE_Data *)cssmExt->value.parsedValue;
88 extnInfo->valToFree = extnData;
89 return CSSM_OK;
90 }
91
92 /*
93 * Fetch a known extension, set up associated iSignExtenInfo if present.
94 */
95 static CSSM_RETURN iSignFetchExtension(
96 CssmAllocator &alloc,
97 TPCertInfo *tpCert,
98 const CSSM_OID *fieldOid, // which extension to fetch
99 iSignExtenInfo *extnInfo) // where the info goes
100 {
101 CSSM_DATA_PTR fieldValue; // mallocd by CL
102 CSSM_RETURN crtn;
103
104 crtn = tpCert->fetchField(fieldOid, &fieldValue);
105 switch(crtn) {
106 case CSSM_OK:
107 break;
108 case CSSMERR_CL_NO_FIELD_VALUES:
109 /* field not present, OK */
110 return CSSM_OK;
111 default:
112 return crtn;
113 }
114 return tpSetupExtension(alloc,
115 fieldValue,
116 extnInfo);
117 }
118
119 /*
120 * Search for al unknown extensions. If we find one which is flagged critical,
121 * flag certInfo->foundUnknownCritical. Only returns error on gross errors.
122 */
123 static CSSM_RETURN iSignSearchUnknownExtensions(
124 TPCertInfo *tpCert,
125 iSignCertInfo *certInfo)
126 {
127 CSSM_RETURN crtn;
128 CSSM_DATA_PTR fieldValue = NULL;
129 CSSM_HANDLE searchHand = CSSM_INVALID_HANDLE;
130 uint32 numFields = 0;
131
132 crtn = CSSM_CL_CertGetFirstCachedFieldValue(tpCert->clHand(),
133 tpCert->cacheHand(),
134 &CSSMOID_X509V3CertificateExtensionCStruct,
135 &searchHand,
136 &numFields,
137 &fieldValue);
138 switch(crtn) {
139 case CSSM_OK:
140 /* found one, proceed */
141 break;
142 case CSSMERR_CL_NO_FIELD_VALUES:
143 /* no unknown extensions present, OK */
144 return CSSM_OK;
145 default:
146 return crtn;
147 }
148
149 if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
150 errorLog0("iSignSearchUnknownExtensions: malformed CSSM_FIELD\n");
151 return CSSMERR_TP_UNKNOWN_FORMAT;
152 }
153 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
154 if(cssmExt->critical) {
155 /* BRRZAPP! Found an unknown extension marked critical */
156 certInfo->foundUnknownCritical = CSSM_TRUE;
157 goto fini;
158 }
159 CSSM_CL_FreeFieldValue(tpCert->clHand(),
160 &CSSMOID_X509V3CertificateExtensionCStruct,
161 fieldValue);
162 fieldValue = NULL;
163
164 /* process remaining unknown extensions */
165 for(unsigned i=1; i<numFields; i++) {
166 crtn = CSSM_CL_CertGetNextCachedFieldValue(tpCert->clHand(),
167 searchHand,
168 &fieldValue);
169 if(crtn) {
170 /* should never happen */
171 errorLog0("searchUnknownExtensions: GetNextCachedFieldValue error\n");
172 break;
173 }
174 if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
175 errorLog0("iSignSearchUnknownExtensions: malformed CSSM_FIELD\n");
176 crtn = CSSMERR_TP_UNKNOWN_FORMAT;
177 break;
178 }
179 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
180 if(cssmExt->critical) {
181 /* BRRZAPP! Found an unknown extension marked critical */
182 certInfo->foundUnknownCritical = CSSM_TRUE;
183 break;
184 }
185 CSSM_CL_FreeFieldValue(tpCert->clHand(),
186 &CSSMOID_X509V3CertificateExtensionCStruct,
187 fieldValue);
188 fieldValue = NULL;
189 } /* for additional fields */
190
191 fini:
192 if(fieldValue) {
193 CSSM_CL_FreeFieldValue(tpCert->clHand(),
194 &CSSMOID_X509V3CertificateExtensionCStruct,
195 fieldValue);
196 }
197 if(searchHand != CSSM_INVALID_HANDLE) {
198 CSSM_CL_CertAbortQuery(tpCert->clHand(), searchHand);
199 }
200 return crtn;
201 }
202 /*
203 * Given a TPCertInfo, fetch the associated iSignCertInfo fields.
204 * Returns CSSM_FAIL on error.
205 */
206 static CSSM_RETURN iSignGetCertInfo(
207 CssmAllocator &alloc,
208 TPCertInfo *tpCert,
209 iSignCertInfo *certInfo)
210 {
211 CSSM_RETURN crtn;
212
213 /* first grind thru the extensions we're interested in */
214 crtn = iSignFetchExtension(alloc,
215 tpCert,
216 &CSSMOID_AuthorityKeyIdentifier,
217 &certInfo->authorityId);
218 if(crtn) {
219 return crtn;
220 }
221 crtn = iSignFetchExtension(alloc,
222 tpCert,
223 &CSSMOID_SubjectKeyIdentifier,
224 &certInfo->subjectId);
225 if(crtn) {
226 return crtn;
227 }
228 crtn = iSignFetchExtension(alloc,
229 tpCert,
230 &CSSMOID_KeyUsage,
231 &certInfo->keyUsage);
232 if(crtn) {
233 return crtn;
234 }
235 crtn = iSignFetchExtension(alloc,
236 tpCert,
237 &CSSMOID_ExtendedKeyUsage,
238 &certInfo->extendKeyUsage);
239 if(crtn) {
240 return crtn;
241 }
242 crtn = iSignFetchExtension(alloc,
243 tpCert,
244 &CSSMOID_BasicConstraints,
245 &certInfo->basicConstraints);
246 if(crtn) {
247 return crtn;
248 }
249 crtn = iSignFetchExtension(alloc,
250 tpCert,
251 &CSSMOID_NetscapeCertType,
252 &certInfo->netscapeCertType);
253 if(crtn) {
254 return crtn;
255 }
256
257 /* now look for extensions we don't understand - the only thing we're interested
258 * in is the critical flag. */
259 return iSignSearchUnknownExtensions(tpCert, certInfo);
260 }
261
262 /*
263 * Free (via CL) the fields allocated in iSignGetCertInfo().
264 */
265 static void iSignFreeCertInfo(
266 CSSM_CL_HANDLE clHand,
267 iSignCertInfo *certInfo)
268 {
269 if(certInfo->authorityId.present) {
270 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_AuthorityKeyIdentifier,
271 certInfo->authorityId.valToFree);
272 }
273 if(certInfo->subjectId.present) {
274 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_SubjectKeyIdentifier,
275 certInfo->subjectId.valToFree);
276 }
277 if(certInfo->keyUsage.present) {
278 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_KeyUsage,
279 certInfo->keyUsage.valToFree);
280 }
281 if(certInfo->extendKeyUsage.present) {
282 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_ExtendedKeyUsage,
283 certInfo->extendKeyUsage.valToFree);
284 }
285 if(certInfo->basicConstraints.present) {
286 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_BasicConstraints,
287 certInfo->basicConstraints.valToFree);
288 }
289 if(certInfo->netscapeCertType.present) {
290 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_NetscapeCertType,
291 certInfo->netscapeCertType.valToFree);
292 }
293 }
294
295 #if TP_ROOT_CERT_ENABLE
296 /*
297 * Common code for comparing a root to a list of known embedded roots.
298 */
299 static CSSM_BOOL tp_isKnownRootCert(
300 TPCertInfo *rootCert, // raw cert to compare
301 const tpRootCert *knownRoots,
302 unsigned numKnownRoots)
303 {
304 const CSSM_DATA *subjectName = NULL;
305 CSSM_DATA_PTR publicKey = NULL;
306 unsigned dex;
307 CSSM_BOOL brtn = CSSM_FALSE;
308 CSSM_DATA_PTR valToFree = NULL;
309
310 subjectName = rootCert->subjectName();
311 publicKey = tp_CertGetPublicKey(rootCert, &valToFree);
312 if(publicKey == NULL) {
313 errorLog0("tp_isKnownRootCert: error retrieving public key info!\n");
314 goto errOut;
315 }
316
317 /*
318 * Grind thru the list of known certs, demanding perfect match of
319 * both fields
320 */
321 for(dex=0; dex<numKnownRoots; dex++) {
322 if(!tpCompareCssmData(subjectName,
323 &knownRoots[dex].subjectName)) {
324 continue;
325 }
326 if(!tpCompareCssmData(publicKey,
327 &knownRoots[dex].publicKey)) {
328 continue;
329 }
330 #if ENABLE_APPLE_DEBUG_ROOT
331 if( dex == (knownRoots - 1) ){
332 brtn = CSSM_FALSE;
333 //tpSetError(CSSM_TP_DEBUG_CERT);
334 break;
335 }
336 #endif
337 brtn = CSSM_TRUE;
338 break;
339 }
340 errOut:
341 tp_CertFreePublicKey(rootCert->clHand(), valToFree);
342 return brtn;
343 }
344
345 /*
346 * See if specified root cert is a known (embedded) iSign root cert.
347 * Returns CSSM_TRUE if the cert is a known root cert.
348 *
349 * Note as of 6/12/02, we do not distinguish between internally
350 * cached iSign roots and SSL roots. Maybe someday we will do so again,
351 * so let's leave these two functions separate.
352 */
353 static CSSM_BOOL tp_isIsignRootCert(
354 CSSM_CL_HANDLE clHand,
355 TPCertInfo *rootCert) // raw cert from cert group
356 {
357 const tpRootCert *roots;
358 unsigned numRoots;
359 roots = TPRootStore::tpGlobalRoots().rootCerts(clHand, numRoots);
360 return tp_isKnownRootCert(rootCert, roots, numRoots);
361 }
362
363 /*
364 * See if specified root cert is a known (embedded) SSL root cert.
365 * Returns CSSM_TRUE if the cert is a known root cert.
366 */
367 static CSSM_BOOL tp_isSslRootCert(
368 CSSM_CL_HANDLE clHand,
369 TPCertInfo *rootCert) // raw cert from cert group
370 {
371 const tpRootCert *roots;
372 unsigned numRoots;
373 roots = TPRootStore::tpGlobalRoots().rootCerts(clHand, numRoots);
374 return tp_isKnownRootCert(rootCert, roots, numRoots);
375 }
376
377 /*
378 * Attempt to verify specified cert (from the end of a chain) with one of
379 * our known SSL roots.
380 */
381 CSSM_BOOL tp_verifyWithSslRoots(
382 CSSM_CL_HANDLE clHand,
383 CSSM_CSP_HANDLE cspHand,
384 TPCertInfo *certToVfy) // last in chain, not root
385 {
386 CSSM_KEY rootKey; // pub key manufactured from tpRootCert info
387 CSSM_CC_HANDLE ccHand; // signature context
388 CSSM_RETURN crtn;
389 unsigned dex;
390 const tpRootCert *rootInfo;
391 CSSM_BOOL brtn = CSSM_FALSE;
392 CSSM_KEYHEADER *hdr = &rootKey.KeyHeader;
393 CSSM_X509_ALGORITHM_IDENTIFIER_PTR algId;
394 CSSM_DATA_PTR valToFree = NULL;
395 CSSM_ALGORITHMS sigAlg;
396 const tpRootCert *rootCerts = NULL;
397 unsigned numRootCerts = 0;
398
399 memset(&rootKey, 0, sizeof(CSSM_KEY));
400
401 /*
402 * Get signature algorithm from subject key
403 */
404 algId = tp_CertGetAlgId(certToVfy, &valToFree);
405 if(algId == NULL) {
406 /* bad cert */
407 return CSSM_FALSE;
408 }
409 /* subsequest errors to errOut: */
410
411 /* map to key and signature algorithm */
412 sigAlg = tpOidToAldId(&algId->algorithm, &hdr->AlgorithmId);
413 if(sigAlg == CSSM_ALGID_NONE) {
414 errorLog0("tp_verifyWithSslRoots: unknown sig alg\n");
415 goto errOut;
416 }
417
418 /* Set up other constant key fields */
419 hdr->BlobType = CSSM_KEYBLOB_RAW;
420 switch(hdr->AlgorithmId) {
421 case CSSM_ALGID_RSA:
422 hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
423 break;
424 case CSSM_ALGID_DSA:
425 hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_FIPS186;
426 break;
427 case CSSM_ALGID_FEE:
428 hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
429 break;
430 default:
431 /* punt */
432 hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_NONE;
433 }
434 hdr->KeyClass = CSSM_KEYCLASS_PUBLIC_KEY;
435 hdr->KeyAttr = CSSM_KEYATTR_MODIFIABLE | CSSM_KEYATTR_EXTRACTABLE;
436 hdr->KeyUsage = CSSM_KEYUSE_VERIFY;
437
438 rootCerts = TPRootStore::tpGlobalRoots().rootCerts(clHand, numRootCerts);
439 for(dex=0; dex<numRootCerts; dex++) {
440 rootInfo = &rootCerts[dex];
441 if(!tpIsSameName(&rootInfo->subjectName, certToVfy->issuerName())) {
442 /* not this root */
443 continue;
444 }
445
446 /* only variation in key in the loop - raw key bits and size */
447 rootKey.KeyData = rootInfo->publicKey;
448 hdr->LogicalKeySizeInBits = rootInfo->keySize;
449 crtn = CSSM_CSP_CreateSignatureContext(cspHand,
450 sigAlg,
451 NULL, // AcccedCred
452 &rootKey,
453 &ccHand);
454 if(crtn) {
455 errorLog0("tp_verifyWithSslRoots: CSSM_CSP_CreateSignatureContext err\n");
456 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
457 }
458 crtn = CSSM_CL_CertVerify(clHand,
459 ccHand,
460 certToVfy->certData(),
461 NULL, // no signer cert
462 NULL, // VerifyScope
463 0); // ScopeSize
464 CSSM_DeleteContext(ccHand);
465 if(crtn == CSSM_OK) {
466 /* success! */
467 brtn = CSSM_TRUE;
468 break;
469 }
470 }
471 errOut:
472 if(valToFree != NULL) {
473 tp_CertFreeAlgId(clHand, valToFree);
474 }
475 return brtn;
476 }
477 #endif /* TP_ROOT_CERT_ENABLE */
478
479 /*
480 * Convert a C string to lower case in place. NULL terminator not needed.
481 */
482 static void tpToLower(
483 char *str,
484 unsigned strLen)
485 {
486 for(unsigned i=0; i<strLen; i++) {
487 *str++ = tolower(*str);
488 }
489 }
490
491 /*
492 * Verify SSL options. Currently this just consists of matching the
493 * leaf cert's subject common name against the caller's (optional)
494 * server name.
495 */
496 static CSSM_RETURN tp_verifySslOpts(
497 TPCertGroup &certGroup,
498 const CSSM_APPLE_TP_SSL_OPTIONS *sslOpts)
499 {
500 if(sslOpts == NULL) {
501 /* optional */
502 return CSSM_OK;
503 }
504
505 CSSM_DATA_PTR subjNameData = NULL;
506 char *serverName = NULL;
507 unsigned serverNameLen = sslOpts->ServerNameLen;
508 char *commonName = NULL;
509 uint32 commonNameLen = 0;
510
511 if(serverNameLen == 0) {
512 /* optional */
513 return CSSM_OK;
514 }
515 if(sslOpts->ServerName == NULL) {
516 return CSSMERR_TP_INVALID_POINTER;
517 }
518
519 /* Obtain subject name of leaf cert in CSSM_X509_NAME_PTR form */
520 TPCertInfo *leaf = certGroup.certAtIndex(0);
521 assert(leaf != NULL);
522 CSSM_RETURN crtn;
523 crtn = leaf->fetchField(&CSSMOID_X509V1SubjectNameCStruct, &subjNameData);
524 if(crtn) {
525 /* should never happen, we shouldn't be here if there is no subject */
526 errorLog0("tp_verifySslOpts: error retrieving subject name\n");
527 return crtn;
528 }
529 CSSM_X509_NAME_PTR x509name = (CSSM_X509_NAME_PTR)subjNameData->Data;
530 if((x509name == NULL) || (subjNameData->Length != sizeof(CSSM_X509_NAME))) {
531 errorLog0("tp_verifySslOpts: malformed CSSM_X509_NAME\n");
532 crtn = CSSMERR_TP_INVALID_CERTGROUP;
533 goto done;
534 }
535
536 /* Now grunge thru the X509 name looking for a common name */
537 CSSM_X509_TYPE_VALUE_PAIR *ptvp;
538 CSSM_X509_RDN_PTR rdnp;
539 unsigned rdnDex;
540 unsigned pairDex;
541
542 for(rdnDex=0; rdnDex<x509name->numberOfRDNs; rdnDex++) {
543 rdnp = &x509name->RelativeDistinguishedName[rdnDex];
544 for(pairDex=0; pairDex<rdnp->numberOfPairs; pairDex++) {
545 ptvp = &rdnp->AttributeTypeAndValue[pairDex];
546 if(tpCompareOids(&ptvp->type, &CSSMOID_CommonName)) {
547 commonName = (char *)ptvp->value.Data;
548 commonNameLen = ptvp->value.Length;
549 break;
550 }
551 }
552 }
553 if(commonName == NULL) {
554 errorLog0("tp_verifySslOpts: NO COMMON NAME in subject\n");
555 crtn = CSSMERR_TP_VERIFY_ACTION_FAILED;
556 goto done;
557 }
558
559 /* tolerate optional NULL terminators for both */
560 if(commonName[commonNameLen - 1] == '\0') {
561 commonNameLen--;
562 }
563 if(sslOpts->ServerName[serverNameLen - 1] == '\0') {
564 serverNameLen--;
565 }
566
567 /* convert both name strings to lower case. The one in the X509 Name can
568 * be done in place; we have to malloc and copy the caller's string. */
569 tpToLower(commonName, commonNameLen);
570 serverName = (char *)certGroup.alloc().malloc(serverNameLen);
571 memmove(serverName, sslOpts->ServerName, serverNameLen);
572 tpToLower(serverName, serverNameLen);
573
574 /* case 1: exact match */
575 if((serverNameLen == commonNameLen) &&
576 !memcmp(commonName, serverName, commonNameLen)) {
577 crtn = CSSM_OK;
578 goto done;
579 }
580
581 /* case 2: handle optional '*' in cert's common name */
582 if(commonName[0] == '*') {
583 /* last (commonNameLen - 1) chars have to match */
584 unsigned effectLen = commonNameLen - 1; // skip '*'
585 if(serverNameLen < effectLen) {
586 errorLog0("tp_verifySslOpts: subject/server name wildcard mismatch (1)\n");
587 crtn = CSSMERR_TP_VERIFY_ACTION_FAILED;
588 }
589 else if(memcmp(commonName+1, // skip '*'
590 serverName + serverNameLen - effectLen,
591 effectLen)) {
592 errorLog0("tp_verifySslOpts: subject/server name wildcard mismatch (2)\n");
593 crtn = CSSMERR_TP_VERIFY_ACTION_FAILED;
594 }
595 else {
596 /* wildcard match */
597 crtn = CSSM_OK;
598 }
599 }
600 else {
601 /* mismatch */
602 errorLog0("tp_verifySslOpts: subject/server name mismatch\n");
603 crtn = CSSMERR_TP_VERIFY_ACTION_FAILED;
604 }
605 done:
606 if(subjNameData != NULL) {
607 leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
608 }
609 if(serverName != NULL) {
610 certGroup.alloc().free(serverName);
611 }
612 if(crtn == CSSMERR_TP_VERIFY_ACTION_FAILED) {
613 leaf->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH);
614 }
615 return crtn;
616 }
617
618 /*
619 * RFC2459 says basicConstraints must be flagged critical for
620 * CA certs, but Verisign doesn't work that way.
621 */
622 #define BASIC_CONSTRAINTS_MUST_BE_CRITICAL 0
623
624 /*
625 * TP iSign spec says Extended Key Usage required for leaf certs,
626 * but Verisign doesn't work that way.
627 */
628 #define EXTENDED_KEY_USAGE_REQUIRED_FOR_LEAF 0
629
630 /*
631 * TP iSign spec says Subject Alternate Name required for leaf certs,
632 * but Verisign doesn't work that way.
633 */
634 #define SUBJECT_ALT_NAME_REQUIRED_FOR_LEAF 0
635
636 /*
637 * TP iSign spec originally required KeyUsage for all certs, but
638 * Verisign doesn't have that in their roots.
639 */
640 #define KEY_USAGE_REQUIRED_FOR_ROOT 0
641
642 /*
643 * Public routine to perform TP verification on a constructed
644 * cert group.
645 * Returns CSSM_TRUE on success.
646 * Asumes the chain has passed basic subject/issuer verification. First cert of
647 * incoming certGroup is end-entity (leaf).
648 *
649 * Per-policy details:
650 * iSign: Assumes that last cert in incoming certGroup is a root cert.
651 * Also assumes a cert group of more than one cert.
652 * kTPx509Basic: CertGroup of length one allowed.
653 */
654 CSSM_RETURN tp_policyVerify(
655 TPPolicy policy,
656 CssmAllocator &alloc,
657 CSSM_CL_HANDLE clHand,
658 CSSM_CSP_HANDLE cspHand,
659 TPCertGroup *certGroup,
660 CSSM_BOOL verifiedToRoot, // last cert is good root
661 const CSSM_APPLE_TP_ACTION_DATA *actionData,
662 const CSSM_APPLE_TP_SSL_OPTIONS *sslOpts,
663 void *policyOpts) // future options
664 {
665 iSignCertInfo *certInfo = NULL;
666 uint32 numCerts;
667 iSignCertInfo *thisCertInfo;
668 uint16 expUsage;
669 uint16 actUsage;
670 unsigned certDex;
671 CSSM_BOOL cA = CSSM_FALSE; // init for compiler warning
672 CSSM_BOOL isLeaf; // end entity
673 CSSM_BOOL isRoot; // root cert
674 CE_ExtendedKeyUsage *extendUsage;
675 CE_AuthorityKeyID *authorityId;
676 CSSM_RETURN outErr = CSSM_OK; // for gross, non-policy errors
677 CSSM_BOOL policyFail = CSSM_FALSE;
678
679 /* First, kTPDefault is a nop here */
680 if(policy == kTPDefault) {
681 return CSSM_OK;
682 }
683
684 if(certGroup == NULL) {
685 return CSSMERR_TP_INVALID_CERTGROUP;
686 }
687 numCerts = certGroup->numCerts();
688 if(numCerts == 0) {
689 return CSSMERR_TP_INVALID_CERTGROUP;
690 }
691 if(policy == kTPiSign) {
692 if(!verifiedToRoot) {
693 /* no way, this requires a root cert */
694 return CSSMERR_TP_INVALID_CERTGROUP;
695 }
696 if(numCerts <= 1) {
697 /* nope, not for iSign */
698 return CSSMERR_TP_INVALID_CERTGROUP;
699 }
700 }
701
702 /* cook up an iSignCertInfo array */
703 certInfo = (iSignCertInfo *)tpCalloc(alloc, numCerts, sizeof(iSignCertInfo));
704 /* subsequent errors to errOut: */
705
706 /* fill it with interesting info from parsed certs */
707 for(certDex=0; certDex<numCerts; certDex++) {
708 if(iSignGetCertInfo(alloc,
709 certGroup->certAtIndex(certDex),
710 &certInfo[certDex])) {
711 (certGroup->certAtIndex(certDex))->addStatusCode(
712 CSSMERR_TP_INVALID_CERTIFICATE);
713 /* this one is fatal */
714 outErr = CSSMERR_TP_INVALID_CERTIFICATE;
715 goto errOut;
716 }
717 }
718
719 /*
720 * OK, the heart of TP enforcement.
721 * First check for presence of required extensions and
722 * critical extensions we don't understand.
723 */
724 for(certDex=0; certDex<numCerts; certDex++) {
725 thisCertInfo = &certInfo[certDex];
726 TPCertInfo *thisTpCertInfo = certGroup->certAtIndex(certDex);
727
728 if(thisCertInfo->foundUnknownCritical) {
729 /* illegal for all policies */
730 errorLog0("tp_policyVerify: critical flag in unknown extension\n");
731 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN);
732 policyFail = CSSM_TRUE;
733 }
734
735 /*
736 * Note it's possible for both of these to be true, for a
737 * of length one (kTPx509Basic only!)
738 */
739 isLeaf = (certDex == 0) ? CSSM_TRUE : CSSM_FALSE;
740 isRoot = (certDex == (numCerts - 1)) ? CSSM_TRUE : CSSM_FALSE;
741
742 /*
743 * BasicConstraints.cA
744 * iSign: required in all but leaf and root,
745 * for which it is optional (with default values of false
746 * for leaf and true for root).
747 * kTPx509Basic,
748 * kTP_SSL: always optional, default of false for leaf and
749 * true for others
750 * All: cA must be false for leaf, true for others
751 */
752 if(!thisCertInfo->basicConstraints.present) {
753 if(isLeaf) {
754 /* cool, use default; note that kTPx509Basic with
755 * certGroup length of one may take this case */
756 cA = CSSM_FALSE;
757 }
758 else if(isRoot) {
759 /* cool, use default */
760 cA = CSSM_TRUE;
761 }
762 else {
763 switch(policy) {
764 case kTPx509Basic:
765 case kTP_SSL:
766 /*
767 * not present, not leaf, not root, kTPx509Basic
768 * ....OK; infer as true
769 */
770 cA = CSSM_TRUE;
771 break;
772 case kTPiSign:
773 /* required for iSign in this position */
774 errorLog0("tp_policyVerify: no basicConstraints\n");
775 policyFail = CSSM_TRUE;
776 thisTpCertInfo->addStatusCode(
777 CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS);
778 break;
779 default:
780 /* not reached */
781 break;
782 }
783 }
784 }
785 else {
786 /* basicConstraints present */
787 #if BASIC_CONSTRAINTS_MUST_BE_CRITICAL
788 /* disabled for verisign compatibility */
789 if(!thisCertInfo->basicConstraints.critical) {
790 /* per RFC 2459 */
791 errorLog0("tp_policyVerify: basicConstraints marked not critical\n");
792 policyFail = CSSM_TRUE;
793 thisTpCertInfo->addStatusCode(CSSMERR_TP_VERIFY_ACTION_FAILED);
794 }
795 #endif /* BASIC_CONSTRAINTS_MUST_BE_CRITICAL */
796 cA = thisCertInfo->basicConstraints.extnData->basicConstraints.cA;
797 }
798
799 if(isLeaf) {
800 /* special case to allow a chain of length 1, leaf and root
801 * both true (kTPx509Basic, kTP_SSL only) */
802 if(cA && !isRoot) {
803 errorLog0("tp_policyVerify: cA true for leaf\n");
804 policyFail = CSSM_TRUE;
805 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_CA);
806 }
807 } else if(!cA) {
808 errorLog0("tp_policyVerify: cA false for non-leaf\n");
809 policyFail = CSSM_TRUE;
810 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_CA);
811 }
812
813 /*
814 * Authority Key Identifier optional
815 * iSign : only allowed in !root.
816 * If present, must not be critical.
817 * kTPx509Basic :
818 * kTP_SSL : ignored (though used later for chain verification)
819 */
820 if((policy == kTPiSign) && thisCertInfo->authorityId.present) {
821 if(isRoot) {
822 errorLog0("tp_policyVerify: authorityId in root\n");
823 policyFail = CSSM_TRUE;
824 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID);
825 }
826 if(thisCertInfo->authorityId.critical) {
827 /* illegal per RFC 2459 */
828 errorLog0("tp_policyVerify: authorityId marked critical\n");
829 policyFail = CSSM_TRUE;
830 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID);
831 }
832 }
833
834 /*
835 * Subject Key Identifier optional
836 * iSign : can't be critical.
837 * kTPx509Basic,
838 * kTP_SSL : ignored (though used later for chain verification)
839 */
840 if(thisCertInfo->subjectId.present) {
841 if((policy == kTPiSign) && thisCertInfo->subjectId.critical) {
842 errorLog0("tp_policyVerify: subjectId marked critical\n");
843 policyFail = CSSM_TRUE;
844 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_SUBJECT_ID);
845 }
846 }
847
848 /*
849 * Key Usage optional except as noted required
850 * iSign : required for non-root/non-leaf
851 * Leaf cert : if present, usage = digitalSignature
852 * Exception : if leaf, and keyUsage not present,
853 * netscape-cert-type must be present, with
854 * Object Signing bit set
855 * kTPx509Basic : non-leaf : usage = keyCertSign
856 * Leaf: don't care
857 */
858 if(thisCertInfo->keyUsage.present) {
859 /*
860 * Leaf cert: usage = digitalSignature
861 * Others: usage = keyCertSign
862 * We only require that one bit to be set, we ignore others.
863 */
864 if(isLeaf) {
865 if(policy == kTPiSign) {
866 expUsage = CE_KU_DigitalSignature;
867 }
868 else {
869 /* hack to accept whatever's there */
870 expUsage = thisCertInfo->keyUsage.extnData->keyUsage;
871 }
872 }
873 else {
874 /* this is true for all policies */
875 expUsage = CE_KU_KeyCertSign;
876 }
877 actUsage = thisCertInfo->keyUsage.extnData->keyUsage;
878 if(!(actUsage & expUsage)) {
879 errorLog2("tp_policyVerify: bad keyUsage (leaf %s; usage 0x%x)\n",
880 (certDex == 0) ? "TRUE" : "FALSE", actUsage);
881 policyFail = CSSM_TRUE;
882 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE);
883 }
884 }
885 else if(policy == kTPiSign) {
886 /*
887 * iSign requires keyUsage present for non root OR
888 * netscape-cert-type/ObjectSigning for leaf
889 */
890 if(isLeaf && thisCertInfo->netscapeCertType.present) {
891 CE_NetscapeCertType ct =
892 thisCertInfo->netscapeCertType.extnData->netscapeCertType;
893
894 if(!(ct & CE_NCT_ObjSign)) {
895 errorLog0("tp_policyVerify: netscape-cert-type, !ObjectSign\n");
896 policyFail = CSSM_TRUE;
897 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE);
898 }
899 }
900 else if(!isRoot) {
901 errorLog0("tp_policyVerify: !isRoot, no keyUsage, !(leaf and netscapeCertType)\n");
902 policyFail = CSSM_TRUE;
903 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE);
904 }
905 }
906 } /* for certDex, checking presence of extensions */
907
908 /*
909 * Special case checking for leaf (end entity) cert
910 *
911 * iSign only: Extended key usage, optional for leaf,
912 * value CSSMOID_ExtendedUseCodeSigning
913 */
914 if((policy == kTPiSign) && certInfo[0].extendKeyUsage.present) {
915 extendUsage = &certInfo[0].extendKeyUsage.extnData->extendedKeyUsage;
916 if(extendUsage->numPurposes != 1) {
917 errorLog1("tp_policyVerify: bad extendUsage->numPurposes (%d)\n",
918 (int)extendUsage->numPurposes);
919 policyFail = CSSM_TRUE;
920 (certGroup->certAtIndex(0))->addStatusCode(
921 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
922 }
923 if(!tpCompareOids(extendUsage->purposes,
924 &CSSMOID_ExtendedUseCodeSigning)) {
925 errorLog0("tp_policyVerify: bad extendKeyUsage\n");
926 policyFail = CSSM_TRUE;
927 (certGroup->certAtIndex(0))->addStatusCode(
928 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
929 }
930 }
931
932 /*
933 * Verify authorityId-->subjectId linkage.
934 * All optional - skip if needed fields not present.
935 * Also, always skip last (root) cert.
936 */
937 for(certDex=0; certDex<(numCerts-1); certDex++) {
938 if(!certInfo[certDex].authorityId.present ||
939 !certInfo[certDex+1].subjectId.present) {
940 continue;
941 }
942 authorityId = &certInfo[certDex].authorityId.extnData->authorityKeyID;
943 if(!authorityId->keyIdentifierPresent) {
944 /* we only know how to compare keyIdentifier */
945 continue;
946 }
947 if(!tpCompareCssmData(&authorityId->keyIdentifier,
948 &certInfo[certDex+1].subjectId.extnData->subjectKeyID)) {
949 errorLog0("tp_policyVerify: bad key ID linkage\n");
950 policyFail = CSSM_TRUE;
951 (certGroup->certAtIndex(certDex))->addStatusCode(
952 CSSMERR_APPLETP_INVALID_ID_LINKAGE);
953 }
954 }
955
956 /*
957 * SSL: optionally verify common name.
958 * FIXME - should this be before or after the root cert test? How can
959 * we return both errors?
960 */
961 if(policy == kTP_SSL) {
962 CSSM_RETURN cerr = tp_verifySslOpts(*certGroup, sslOpts);
963 if(cerr) {
964 policyFail = CSSM_TRUE;
965 }
966 }
967
968 /* iSign, SSL: compare root against known root certs */
969 /* FIXME - this goes away soon */
970 #if TP_ROOT_CERT_ENABLE
971 if((outErr == CSSM_OK) && // skip if we have a gross error (other than policy failure)
972 (actionData != NULL) &&
973 (actionData->ActionFlags & 0x80000000)) { // The secret "enable root cert check" flag
974 TPCertInfo *lastCert = certGroup->lastCert();
975 if(policy == kTPiSign) {
976 bool brtn = tp_isIsignRootCert(clHand, lastCert);
977 if(!brtn) {
978 policyFail = CSSM_TRUE;
979 }
980 }
981 else if(verifiedToRoot && (policy == kTP_SSL)) {
982 /* note SSL doesn't require root here */
983 bool brtn = tp_isSslRootCert(clHand, lastCert);
984 if(!brtn) {
985 outErr = CSSMERR_TP_INVALID_ANCHOR_CERT;
986 }
987 }
988 }
989 #endif /* TP_ROOT_CERT_ENABLE */
990 if(policyFail && (outErr == CSSM_OK)) {
991 /* only error in this function was policy failure */
992 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
993 }
994 errOut:
995 /* free resources */
996 for(certDex=0; certDex<numCerts; certDex++) {
997 thisCertInfo = &certInfo[certDex];
998 iSignFreeCertInfo(clHand, thisCertInfo);
999 }
1000 tpFree(alloc, certInfo);
1001 return outErr;
1002 }