]> git.saurik.com Git - apple/security.git/blob - AppleX509TP/tpPolicies.cpp
Security-179.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 /*
43 * Our private per-extension info. One of these per (understood) extension per
44 * cert.
45 */
46 typedef struct {
47 CSSM_BOOL present;
48 CSSM_BOOL critical;
49 CE_Data *extnData; // mallocd by CL
50 CSSM_DATA *valToFree; // the data we pass to freeField()
51 } iSignExtenInfo;
52
53 /*
54 * Struct to keep track of info pertinent to one cert.
55 */
56 typedef struct {
57
58 /* extensions we're interested in */
59 iSignExtenInfo authorityId;
60 iSignExtenInfo subjectId;
61 iSignExtenInfo keyUsage;
62 iSignExtenInfo extendKeyUsage;
63 iSignExtenInfo basicConstraints;
64 iSignExtenInfo netscapeCertType;
65 iSignExtenInfo subjectAltName;
66
67 /* flag indicating presence of a critical extension we don't understand */
68 CSSM_BOOL foundUnknownCritical;
69
70 } iSignCertInfo;
71
72
73 /*
74 * Setup a single iSignExtenInfo. Called once per known extension
75 * per cert.
76 */
77 static CSSM_RETURN tpSetupExtension(
78 CssmAllocator &alloc,
79 CSSM_DATA *extnData,
80 iSignExtenInfo *extnInfo) // which component of certInfo
81 {
82 if(extnData->Length != sizeof(CSSM_X509_EXTENSION)) {
83 tpPolicyError("tpSetupExtension: malformed CSSM_FIELD");
84 return CSSMERR_TP_UNKNOWN_FORMAT;
85 }
86 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)extnData->Data;
87 extnInfo->present = CSSM_TRUE;
88 extnInfo->critical = cssmExt->critical;
89 extnInfo->extnData = (CE_Data *)cssmExt->value.parsedValue;
90 extnInfo->valToFree = extnData;
91 return CSSM_OK;
92 }
93
94 /*
95 * Fetch a known extension, set up associated iSignExtenInfo if present.
96 */
97 static CSSM_RETURN iSignFetchExtension(
98 CssmAllocator &alloc,
99 TPCertInfo *tpCert,
100 const CSSM_OID *fieldOid, // which extension to fetch
101 iSignExtenInfo *extnInfo) // where the info goes
102 {
103 CSSM_DATA_PTR fieldValue; // mallocd by CL
104 CSSM_RETURN crtn;
105
106 crtn = tpCert->fetchField(fieldOid, &fieldValue);
107 switch(crtn) {
108 case CSSM_OK:
109 break;
110 case CSSMERR_CL_NO_FIELD_VALUES:
111 /* field not present, OK */
112 return CSSM_OK;
113 default:
114 return crtn;
115 }
116 return tpSetupExtension(alloc,
117 fieldValue,
118 extnInfo);
119 }
120
121 /*
122 * Search for al unknown extensions. If we find one which is flagged critical,
123 * flag certInfo->foundUnknownCritical. Only returns error on gross errors.
124 */
125 static CSSM_RETURN iSignSearchUnknownExtensions(
126 TPCertInfo *tpCert,
127 iSignCertInfo *certInfo)
128 {
129 CSSM_RETURN crtn;
130 CSSM_DATA_PTR fieldValue = NULL;
131 CSSM_HANDLE searchHand = CSSM_INVALID_HANDLE;
132 uint32 numFields = 0;
133
134 crtn = CSSM_CL_CertGetFirstCachedFieldValue(tpCert->clHand(),
135 tpCert->cacheHand(),
136 &CSSMOID_X509V3CertificateExtensionCStruct,
137 &searchHand,
138 &numFields,
139 &fieldValue);
140 switch(crtn) {
141 case CSSM_OK:
142 /* found one, proceed */
143 break;
144 case CSSMERR_CL_NO_FIELD_VALUES:
145 /* no unknown extensions present, OK */
146 return CSSM_OK;
147 default:
148 return crtn;
149 }
150
151 if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
152 tpPolicyError("iSignSearchUnknownExtensions: malformed CSSM_FIELD");
153 return CSSMERR_TP_UNKNOWN_FORMAT;
154 }
155 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
156 if(cssmExt->critical) {
157 /* BRRZAPP! Found an unknown extension marked critical */
158 certInfo->foundUnknownCritical = CSSM_TRUE;
159 goto fini;
160 }
161 CSSM_CL_FreeFieldValue(tpCert->clHand(),
162 &CSSMOID_X509V3CertificateExtensionCStruct,
163 fieldValue);
164 fieldValue = NULL;
165
166 /* process remaining unknown extensions */
167 for(unsigned i=1; i<numFields; i++) {
168 crtn = CSSM_CL_CertGetNextCachedFieldValue(tpCert->clHand(),
169 searchHand,
170 &fieldValue);
171 if(crtn) {
172 /* should never happen */
173 tpPolicyError("searchUnknownExtensions: GetNextCachedFieldValue"
174 "error");
175 break;
176 }
177 if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
178 tpPolicyError("iSignSearchUnknownExtensions: "
179 "malformed CSSM_FIELD");
180 crtn = CSSMERR_TP_UNKNOWN_FORMAT;
181 break;
182 }
183 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
184 if(cssmExt->critical) {
185 /* BRRZAPP! Found an unknown extension marked critical */
186 certInfo->foundUnknownCritical = CSSM_TRUE;
187 break;
188 }
189 CSSM_CL_FreeFieldValue(tpCert->clHand(),
190 &CSSMOID_X509V3CertificateExtensionCStruct,
191 fieldValue);
192 fieldValue = NULL;
193 } /* for additional fields */
194
195 fini:
196 if(fieldValue) {
197 CSSM_CL_FreeFieldValue(tpCert->clHand(),
198 &CSSMOID_X509V3CertificateExtensionCStruct,
199 fieldValue);
200 }
201 if(searchHand != CSSM_INVALID_HANDLE) {
202 CSSM_CL_CertAbortQuery(tpCert->clHand(), searchHand);
203 }
204 return crtn;
205 }
206 /*
207 * Given a TPCertInfo, fetch the associated iSignCertInfo fields.
208 * Returns CSSM_FAIL on error.
209 */
210 static CSSM_RETURN iSignGetCertInfo(
211 CssmAllocator &alloc,
212 TPCertInfo *tpCert,
213 iSignCertInfo *certInfo)
214 {
215 CSSM_RETURN crtn;
216
217 /* first grind thru the extensions we're interested in */
218 crtn = iSignFetchExtension(alloc,
219 tpCert,
220 &CSSMOID_AuthorityKeyIdentifier,
221 &certInfo->authorityId);
222 if(crtn) {
223 return crtn;
224 }
225 crtn = iSignFetchExtension(alloc,
226 tpCert,
227 &CSSMOID_SubjectKeyIdentifier,
228 &certInfo->subjectId);
229 if(crtn) {
230 return crtn;
231 }
232 crtn = iSignFetchExtension(alloc,
233 tpCert,
234 &CSSMOID_KeyUsage,
235 &certInfo->keyUsage);
236 if(crtn) {
237 return crtn;
238 }
239 crtn = iSignFetchExtension(alloc,
240 tpCert,
241 &CSSMOID_ExtendedKeyUsage,
242 &certInfo->extendKeyUsage);
243 if(crtn) {
244 return crtn;
245 }
246 crtn = iSignFetchExtension(alloc,
247 tpCert,
248 &CSSMOID_BasicConstraints,
249 &certInfo->basicConstraints);
250 if(crtn) {
251 return crtn;
252 }
253 crtn = iSignFetchExtension(alloc,
254 tpCert,
255 &CSSMOID_NetscapeCertType,
256 &certInfo->netscapeCertType);
257 if(crtn) {
258 return crtn;
259 }
260 crtn = iSignFetchExtension(alloc,
261 tpCert,
262 &CSSMOID_SubjectAltName,
263 &certInfo->subjectAltName);
264 if(crtn) {
265 return crtn;
266 }
267
268 /* now look for extensions we don't understand - the only thing we're interested
269 * in is the critical flag. */
270 return iSignSearchUnknownExtensions(tpCert, certInfo);
271 }
272
273 /*
274 * Free (via CL) the fields allocated in iSignGetCertInfo().
275 */
276 static void iSignFreeCertInfo(
277 CSSM_CL_HANDLE clHand,
278 iSignCertInfo *certInfo)
279 {
280 if(certInfo->authorityId.present) {
281 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_AuthorityKeyIdentifier,
282 certInfo->authorityId.valToFree);
283 }
284 if(certInfo->subjectId.present) {
285 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_SubjectKeyIdentifier,
286 certInfo->subjectId.valToFree);
287 }
288 if(certInfo->keyUsage.present) {
289 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_KeyUsage,
290 certInfo->keyUsage.valToFree);
291 }
292 if(certInfo->extendKeyUsage.present) {
293 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_ExtendedKeyUsage,
294 certInfo->extendKeyUsage.valToFree);
295 }
296 if(certInfo->basicConstraints.present) {
297 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_BasicConstraints,
298 certInfo->basicConstraints.valToFree);
299 }
300 if(certInfo->netscapeCertType.present) {
301 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_NetscapeCertType,
302 certInfo->netscapeCertType.valToFree);
303 }
304 if(certInfo->subjectAltName.present) {
305 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_SubjectAltName,
306 certInfo->subjectAltName.valToFree);
307 }
308 }
309
310 /*
311 * See if cert's Subject.{commonName,EmailAddress} matches caller-specified
312 * string. Returns CSSM_TRUE if match, else returns CSSM_FALSE.
313 * Also indicates whether *any* of the specified fields were found, regardless
314 * of match state.
315 */
316 typedef enum {
317 SN_CommonName, // CSSMOID_CommonName, host name format
318 SN_Email // CSSMOID_EmailAddress
319 } SubjSubjNameSearchType;
320
321 static CSSM_BOOL tpCompareSubjectName(
322 TPCertInfo &cert,
323 SubjSubjNameSearchType searchType,
324 const char *callerStr, // already tpToLower'd
325 uint32 callerStrLen,
326 bool &fieldFound)
327 {
328 char *certName = NULL; // from cert's subject name
329 uint32 certNameLen = 0;
330 CSSM_DATA_PTR subjNameData = NULL;
331 CSSM_RETURN crtn;
332 CSSM_BOOL ourRtn = CSSM_FALSE;
333 const CSSM_OID *oidSrch;
334
335 fieldFound = false;
336 switch(searchType) {
337 case SN_CommonName:
338 oidSrch = &CSSMOID_CommonName;
339 break;
340 case SN_Email:
341 oidSrch = &CSSMOID_EmailAddress;
342 break;
343 default:
344 assert(0);
345 return CSSM_FALSE;
346 }
347 crtn = cert.fetchField(&CSSMOID_X509V1SubjectNameCStruct, &subjNameData);
348 if(crtn) {
349 /* should never happen, we shouldn't be here if there is no subject */
350 tpPolicyError("tp_verifySslOpts: error retrieving subject name");
351 return CSSM_FALSE;
352 }
353 CSSM_X509_NAME_PTR x509name = (CSSM_X509_NAME_PTR)subjNameData->Data;
354 if((x509name == NULL) || (subjNameData->Length != sizeof(CSSM_X509_NAME))) {
355 tpPolicyError("tp_verifySslOpts: malformed CSSM_X509_NAME");
356 cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
357 return CSSM_FALSE;
358 }
359
360 /* Now grunge thru the X509 name looking for a common name */
361 CSSM_X509_TYPE_VALUE_PAIR *ptvp;
362 CSSM_X509_RDN_PTR rdnp;
363 unsigned rdnDex;
364 unsigned pairDex;
365
366 for(rdnDex=0; rdnDex<x509name->numberOfRDNs; rdnDex++) {
367 rdnp = &x509name->RelativeDistinguishedName[rdnDex];
368 for(pairDex=0; pairDex<rdnp->numberOfPairs; pairDex++) {
369 ptvp = &rdnp->AttributeTypeAndValue[pairDex];
370 if(tpCompareOids(&ptvp->type, oidSrch)) {
371 fieldFound = true;
372 certName = (char *)ptvp->value.Data;
373 certNameLen = ptvp->value.Length;
374 switch(searchType) {
375 case SN_CommonName:
376 ourRtn = tpCompareHostNames(callerStr, callerStrLen,
377 certName, certNameLen);
378 break;
379 case SN_Email:
380 ourRtn = tpCompareEmailAddr(callerStr, callerStrLen,
381 certName, certNameLen);
382 break;
383 }
384 if(ourRtn) {
385 /* success */
386 break;
387 }
388 /* else keep going, maybe there's another common name */
389 }
390 }
391 if(ourRtn) {
392 break;
393 }
394 }
395 cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
396 return ourRtn;
397 }
398
399 /*
400 * Compare ASCII form of an IP address to a CSSM_DATA containing
401 * the IP address's numeric components. Returns true on match.
402 */
403 static CSSM_BOOL tpCompIpAddrStr(
404 const char *str,
405 unsigned strLen,
406 const CSSM_DATA *numeric)
407 {
408 const char *cp = str;
409 const char *nextDot;
410 char buf[100];
411
412 if((numeric == NULL) || (numeric->Length == 0) || (str == NULL)) {
413 return CSSM_FALSE;
414 }
415 if(cp[strLen - 1] == '\0') {
416 /* ignore NULL terminator */
417 strLen--;
418 }
419 for(unsigned dex=0; dex<numeric->Length; dex++) {
420 /* cp points to start of current string digit */
421 /* find next dot */
422 const char *lastChar = cp + strLen;
423 nextDot = cp + 1;
424 for( ; nextDot<lastChar; nextDot++) {
425 if(*nextDot == '.') {
426 break;
427 }
428 }
429 if(nextDot == lastChar) {
430 /* legal and required on last digit */
431 if(dex != (numeric->Length - 1)) {
432 return CSSM_FALSE;
433 }
434 }
435 else if(dex == (numeric->Length - 1)) {
436 return CSSM_FALSE;
437 }
438 unsigned digLen = nextDot - cp;
439 if(digLen >= sizeof(buf)) {
440 /* preposterous */
441 return CSSM_FALSE;
442 }
443 memmove(buf, cp, digLen);
444 buf[digLen] = '\0';
445 /* incr digLen to include the next dot */
446 digLen++;
447 cp += digLen;
448 strLen -= digLen;
449 int digVal = atoi(buf);
450 if(digVal != numeric->Data[dex]) {
451 return CSSM_FALSE;
452 }
453 }
454 return CSSM_TRUE;
455 }
456
457 /*
458 * See if cert's subjectAltName contains an element matching caller-specified
459 * string, hostname, in the following forms:
460 *
461 * SAN_HostName : dnsName, iPAddress
462 * SAN_Email : RFC822Name
463 *
464 * Returns CSSM_TRUE if match, else returns CSSM_FALSE.
465 *
466 * Also indicates whether or not a dnsName (search type HostName) or
467 * RFC822Name (search type SAM_Email) was found, regardless of result
468 * of comparison.
469 *
470 * The appStr/appStrLen args are optional - if NULL/0, only the
471 * search for dnsName/RFC822Name is done.
472 */
473 typedef enum {
474 SAN_HostName,
475 SAN_Email
476 } SubjAltNameSearchType;
477
478 static CSSM_BOOL tpCompareSubjectAltName(
479 const iSignExtenInfo &subjAltNameInfo,
480 const char *appStr,
481 uint32 appStrLen,
482 SubjAltNameSearchType searchType,
483 bool &dnsNameFound, // RETURNED, SAN_HostName case
484 bool &emailFound) // RETURNED, SAN_Email case
485 {
486 dnsNameFound = false;
487 emailFound = false;
488 if(!subjAltNameInfo.present) {
489 /* common failure, no subjectAltName found */
490 return CSSM_FALSE;
491 }
492
493 CE_GeneralNames *names = &subjAltNameInfo.extnData->subjectAltName;
494 CSSM_BOOL ourRtn = CSSM_FALSE;
495 char *certName;
496 unsigned certNameLen;
497
498 /* Search thru the CE_GeneralNames looking for the appropriate attribute */
499 for(unsigned dex=0; dex<names->numNames; dex++) {
500 CE_GeneralName *name = &names->generalName[dex];
501 switch(searchType) {
502 case SAN_HostName:
503 switch(name->nameType) {
504 case GNT_IPAddress:
505 if(appStr == NULL) {
506 /* nothing to do here */
507 break;
508 }
509 ourRtn = tpCompIpAddrStr(appStr, appStrLen, &name->name);
510 break;
511
512 case GNT_DNSName:
513 if(name->berEncoded) {
514 tpErrorLog("tpCompareSubjectAltName: malformed "
515 "CE_GeneralName (1)\n");
516 break;
517 }
518 certName = (char *)name->name.Data;
519 if(certName == NULL) {
520 tpErrorLog("tpCompareSubjectAltName: malformed "
521 "CE_GeneralName (2)\n");
522 break;
523 }
524 certNameLen = name->name.Length;
525 dnsNameFound = true;
526 if(appStr != NULL) {
527 /* skip if caller passed in NULL */
528 ourRtn = tpCompareHostNames(appStr, appStrLen,
529 certName, certNameLen);
530 }
531 break;
532
533 default:
534 /* not interested, proceed to next name */
535 break;
536 }
537 break; /* from case HostName */
538
539 case SAN_Email:
540 if(name->nameType != GNT_RFC822Name) {
541 /* not interested */
542 break;
543 }
544 certName = (char *)name->name.Data;
545 if(certName == NULL) {
546 tpErrorLog("tpCompareSubjectAltName: malformed "
547 "GNT_RFC822Name\n");
548 break;
549 }
550 certNameLen = name->name.Length;
551 emailFound = true;
552 if(appStr != NULL) {
553 ourRtn = tpCompareEmailAddr(appStr, appStrLen, certName,
554 certNameLen);
555 }
556 break;
557 }
558 if(ourRtn) {
559 /* success */
560 break;
561 }
562 }
563 return ourRtn;
564 }
565
566 /* is host name in the form of a.b.c.d, where a,b,c, and d are digits? */
567 static CSSM_BOOL tpIsNumeric(
568 const char *hostName,
569 unsigned hostNameLen)
570 {
571 if(hostName[hostNameLen - 1] == '\0') {
572 /* ignore NULL terminator */
573 hostNameLen--;
574 }
575 for(unsigned i=0; i<hostNameLen; i++) {
576 char c = *hostName++;
577 if(isdigit(c)) {
578 continue;
579 }
580 if(c != '.') {
581 return CSSM_FALSE;
582 }
583 }
584 return CSSM_TRUE;
585 }
586
587 /*
588 * Verify SSL options. Currently this just consists of matching the
589 * leaf cert's subject common name against the caller's (optional)
590 * server name.
591 */
592 static CSSM_RETURN tp_verifySslOpts(
593 TPCertGroup &certGroup,
594 const CSSM_DATA *sslFieldOpts,
595 const iSignCertInfo &leafCertInfo)
596 {
597 /* first validate optional SSL options */
598 if((sslFieldOpts == NULL) || (sslFieldOpts->Data == NULL)) {
599 /* optional */
600 return CSSM_OK;
601 }
602 CSSM_APPLE_TP_SSL_OPTIONS *sslOpts;
603 sslOpts = (CSSM_APPLE_TP_SSL_OPTIONS *)sslFieldOpts->Data;
604 switch(sslOpts->Version) {
605 case CSSM_APPLE_TP_SSL_OPTS_VERSION:
606 if(sslFieldOpts->Length != sizeof(CSSM_APPLE_TP_SSL_OPTIONS)) {
607 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
608 }
609 break;
610 /* handle backwards compatibility here if necessary */
611 default:
612 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
613 }
614
615 unsigned hostNameLen = sslOpts->ServerNameLen;
616
617 if(hostNameLen == 0) {
618 /* optional */
619 return CSSM_OK;
620 }
621 if(sslOpts->ServerName == NULL) {
622 return CSSMERR_TP_INVALID_POINTER;
623 }
624
625 /* convert caller's hostname string to lower case */
626 char *hostName = (char *)certGroup.alloc().malloc(hostNameLen);
627 memmove(hostName, sslOpts->ServerName, hostNameLen);
628 tpToLower(hostName, hostNameLen);
629
630 TPCertInfo *leaf = certGroup.certAtIndex(0);
631 assert(leaf != NULL);
632
633 CSSM_BOOL match = CSSM_FALSE;
634
635 /* First check subjectAltName... */
636 bool dnsNameFound = false;
637 bool dummy;
638 match = tpCompareSubjectAltName(leafCertInfo.subjectAltName,
639 hostName, hostNameLen,
640 SAN_HostName, dnsNameFound, dummy);
641
642 /*
643 * Then common name, if
644 * -- no match from subjectAltName, AND
645 * -- dnsName was NOT found, AND
646 * -- hostName is not strictly numeric form (1.2.3.4)
647 */
648 if(!match && !dnsNameFound && !tpIsNumeric(hostName, hostNameLen)) {
649 bool fieldFound;
650 match = tpCompareSubjectName(*leaf, SN_CommonName, hostName, hostNameLen,
651 fieldFound);
652 }
653 certGroup.alloc().free(hostName);
654 if(match) {
655 return CSSM_OK;
656 }
657 else {
658 leaf->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH);
659 return CSSMERR_TP_VERIFY_ACTION_FAILED;
660 }
661 }
662
663 /*
664 * Verify SMIME options.
665 */
666 #define CE_CIPHER_MASK (~(CE_KU_EncipherOnly | CE_KU_DecipherOnly))
667
668 static CSSM_RETURN tp_verifySmimeOpts(
669 TPCertGroup &certGroup,
670 const CSSM_DATA *smimeFieldOpts,
671 const iSignCertInfo &leafCertInfo)
672 {
673 /*
674 * First validate optional S/MIME options.
675 */
676 CSSM_APPLE_TP_SMIME_OPTIONS *smimeOpts = NULL;
677 if(smimeFieldOpts != NULL) {
678 smimeOpts = (CSSM_APPLE_TP_SMIME_OPTIONS *)smimeFieldOpts->Data;
679 }
680 if(smimeOpts != NULL) {
681 switch(smimeOpts->Version) {
682 case CSSM_APPLE_TP_SMIME_OPTS_VERSION:
683 if(smimeFieldOpts->Length !=
684 sizeof(CSSM_APPLE_TP_SMIME_OPTIONS)) {
685 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
686 }
687 break;
688 /* handle backwards compatibility here if necessary */
689 default:
690 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
691 }
692 }
693
694 TPCertInfo *leaf = certGroup.certAtIndex(0);
695 assert(leaf != NULL);
696
697 /* Verify optional email address */
698 unsigned emailLen = 0;
699 if(smimeOpts != NULL) {
700 emailLen = smimeOpts->SenderEmailLen;
701 }
702 bool emailFoundInSAN = false;
703 if(emailLen != 0) {
704 if(smimeOpts->SenderEmail == NULL) {
705 return CSSMERR_TP_INVALID_POINTER;
706 }
707
708 /* normalize caller's email string */
709 char *email = (char *)certGroup.alloc().malloc(emailLen);
710 memmove(email, smimeOpts->SenderEmail, emailLen);
711 tpNormalizeAddrSpec(email, emailLen);
712
713 CSSM_BOOL match = false;
714
715 /*
716 * First check subjectAltName. The emailFound bool indicates
717 * that *some* email address was found, regardless of a match
718 * condition.
719 */
720 bool dummy;
721 match = tpCompareSubjectAltName(leafCertInfo.subjectAltName,
722 email, emailLen,
723 SAN_Email, dummy, emailFoundInSAN);
724
725 /*
726 * Then subject DN, CSSMOID_EmailAddress, if no match from
727 * subjectAltName
728 */
729 bool emailFoundInDn = false;
730 if(!match) {
731 match = tpCompareSubjectName(*leaf, SN_Email, email, emailLen,
732 emailFoundInDn);
733 }
734 certGroup.alloc().free(email);
735
736 /*
737 * Error here only if no match found but there was indeed *some*
738 * email address in the cert.
739 */
740 if(!match && (emailFoundInSAN || emailFoundInDn)) {
741 leaf->addStatusCode(CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND);
742 tpPolicyError("SMIME email addrs in cert but no match");
743 return CSSMERR_TP_VERIFY_ACTION_FAILED;
744 }
745 }
746
747 /*
748 * Going by the letter of the law, here's what RFC 2632 has to say
749 * about the legality of an empty Subject Name:
750 *
751 * ...the subject DN in a user's (i.e. end-entity) certificate MAY
752 * be an empty SEQUENCE in which case the subjectAltName extension
753 * will include the subject's identifier and MUST be marked as
754 * critical.
755 *
756 * OK, first examine the leaf cert's subject name.
757 */
758 CSSM_RETURN crtn;
759 CSSM_DATA_PTR subjNameData = NULL;
760 crtn = leaf->fetchField(&CSSMOID_X509V1SubjectNameCStruct, &subjNameData);
761 if(crtn) {
762 /* This should really never happen */
763 tpPolicyError("SMIME policy: error fetching subjectName");
764 leaf->addStatusCode(CSSMERR_TP_INVALID_CERTIFICATE);
765 return CSSMERR_TP_INVALID_CERTIFICATE;
766 }
767 /* must do a leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct on exit */
768
769 const CSSM_X509_NAME *x509Name = (const CSSM_X509_NAME *)subjNameData->Data;
770 if(x509Name->numberOfRDNs == 0) {
771 /*
772 * Empty subject name. If we haven't already seen a valid
773 * email address in the subject alternate name (by looking
774 * for a specific address specified by app), try to find
775 * one now.
776 */
777 if(!emailFoundInSAN && // haven't found one, and
778 (emailLen == 0)) { // didn't even look yet
779 bool dummy;
780 tpCompareSubjectAltName(leafCertInfo.subjectAltName,
781 NULL, 0, // email, emailLen,
782 SAN_Email, dummy,
783 emailFoundInSAN); // the variable we're updating
784 }
785 if(!emailFoundInSAN) {
786 tpPolicyError("SMIME policy fail: empty subject name and "
787 "no Email Addrs in SubjectAltName");
788 leaf->addStatusCode(CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS);
789 leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
790 return CSSMERR_TP_VERIFY_ACTION_FAILED;
791 }
792
793 /*
794 * One more thing: this leaf must indeed have a subjAltName
795 * extension and it must be critical. We would not have gotten this
796 * far if the subjAltName extension was not actually present....
797 */
798 assert(leafCertInfo.subjectAltName.present);
799 if(!leafCertInfo.subjectAltName.critical) {
800 tpPolicyError("SMIME policy fail: empty subject name and "
801 "no Email Addrs in SubjectAltName");
802 leaf->addStatusCode(CSSMERR_APPLETP_SMIME_SUBJ_ALT_NAME_NOT_CRIT);
803 leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
804 return CSSMERR_TP_VERIFY_ACTION_FAILED;
805 }
806 }
807 leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
808
809 /*
810 * Enforce the usage of the key associated with the leaf cert.
811 * Cert's KeyUsage must be a superset of what the app is trying to do.
812 * Note the {en,de}cipherONly flags are handledÊseparately....
813 */
814 const iSignExtenInfo &kuInfo = leafCertInfo.keyUsage;
815 if(kuInfo.present) {
816 CE_KeyUsage certKu = *((CE_KeyUsage *)kuInfo.extnData);
817 CE_KeyUsage appKu = smimeOpts->IntendedUsage;
818 CE_KeyUsage intersection = certKu & appKu;
819 if((intersection & CE_CIPHER_MASK) != (appKu & CE_CIPHER_MASK)) {
820 tpPolicyError("SMIME KeyUsage err: appKu 0x%x certKu 0x%x",
821 appKu, certKu);
822 leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE);
823 return CSSMERR_TP_VERIFY_ACTION_FAILED;
824 }
825
826 /* Now the en/de cipher only bits - for keyAgreement only */
827 if(appKu & CE_KU_KeyAgreement) {
828 /*
829 * 1. App wants to use this for key agreement; it must
830 * say what it wants to do with the derived key.
831 * In this context, the app's XXXonly bit means that
832 * it wants to use the key for that op - not necessarliy
833 * "only".
834 */
835 if((appKu & (CE_KU_EncipherOnly | CE_KU_DecipherOnly)) == 0) {
836 tpPolicyError("SMIME KeyUsage err: KeyAgreement with "
837 "no Encipher or Decipher");
838 leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE);
839 return CSSMERR_TP_VERIFY_ACTION_FAILED;
840 }
841
842 /*
843 * 2. If cert restricts to encipher only make sure the
844 * app isn't trying to decipher.
845 */
846 if((certKu & CE_KU_EncipherOnly) &&
847 (appKu & CE_KU_DecipherOnly)) {
848 tpPolicyError("SMIME KeyUsage err: cert EncipherOnly, "
849 "app wants to decipher");
850 leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE);
851 return CSSMERR_TP_VERIFY_ACTION_FAILED;
852 }
853
854 /*
855 * 3. If cert restricts to decipher only make sure the
856 * app isn't trying to encipher.
857 */
858 if((certKu & CE_KU_DecipherOnly) &&
859 (appKu & CE_KU_EncipherOnly)) {
860 tpPolicyError("SMIME KeyUsage err: cert DecipherOnly, "
861 "app wants to encipher");
862 leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE);
863 return CSSMERR_TP_VERIFY_ACTION_FAILED;
864 }
865 }
866 }
867
868 /*
869 * Ensure that, if an extendedKeyUsage extension is present in the
870 * leaf, that either emailProtection or anyExtendedKeyUsage usages is present
871 */
872 const iSignExtenInfo &ekuInfo = leafCertInfo.extendKeyUsage;
873 if(ekuInfo.present) {
874 bool foundGoodEku = false;
875 CE_ExtendedKeyUsage *eku = (CE_ExtendedKeyUsage *)ekuInfo.extnData;
876 assert(eku != NULL);
877 for(unsigned i=0; i<eku->numPurposes; i++) {
878 if(tpCompareOids(&eku->purposes[i], &CSSMOID_EmailProtection)) {
879 foundGoodEku = true;
880 break;
881 }
882 if(tpCompareOids(&eku->purposes[i], &CSSMOID_ExtendedKeyUsageAny)) {
883 foundGoodEku = true;
884 break;
885 }
886 }
887 if(!foundGoodEku) {
888 leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE);
889 return CSSMERR_TP_VERIFY_ACTION_FAILED;
890 }
891 }
892
893 return CSSM_OK;
894 }
895
896 /*
897 * RFC2459 says basicConstraints must be flagged critical for
898 * CA certs, but Verisign doesn't work that way.
899 */
900 #define BASIC_CONSTRAINTS_MUST_BE_CRITICAL 0
901
902 /*
903 * TP iSign spec says Extended Key Usage required for leaf certs,
904 * but Verisign doesn't work that way.
905 */
906 #define EXTENDED_KEY_USAGE_REQUIRED_FOR_LEAF 0
907
908 /*
909 * TP iSign spec says Subject Alternate Name required for leaf certs,
910 * but Verisign doesn't work that way.
911 */
912 #define SUBJECT_ALT_NAME_REQUIRED_FOR_LEAF 0
913
914 /*
915 * TP iSign spec originally required KeyUsage for all certs, but
916 * Verisign doesn't have that in their roots.
917 */
918 #define KEY_USAGE_REQUIRED_FOR_ROOT 0
919
920 /*
921 * RFC 2632, "S/MIME Version 3 Certificate Handling", section
922 * 4.4.2, says that KeyUsage extensions MUST be flagged critical,
923 * but Thawte's intermediate cert (common namd "Thawte Personal
924 * Freemail Issuing CA" does not meet this requirement.
925 */
926 #define SMIME_KEY_USAGE_MUST_BE_CRITICAL 0
927
928 /*
929 * Public routine to perform TP verification on a constructed
930 * cert group.
931 * Returns CSSM_TRUE on success.
932 * Asumes the chain has passed basic subject/issuer verification. First cert of
933 * incoming certGroup is end-entity (leaf).
934 *
935 * Per-policy details:
936 * iSign: Assumes that last cert in incoming certGroup is a root cert.
937 * Also assumes a cert group of more than one cert.
938 * kTPx509Basic: CertGroup of length one allowed.
939 */
940 CSSM_RETURN tp_policyVerify(
941 TPPolicy policy,
942 CssmAllocator &alloc,
943 CSSM_CL_HANDLE clHand,
944 CSSM_CSP_HANDLE cspHand,
945 TPCertGroup *certGroup,
946 CSSM_BOOL verifiedToRoot, // last cert is good root
947 CSSM_APPLE_TP_ACTION_FLAGS actionFlags,
948 const CSSM_DATA *policyFieldData, // optional
949 void *policyOpts) // future options
950 {
951 iSignCertInfo *certInfo = NULL;
952 uint32 numCerts;
953 iSignCertInfo *thisCertInfo;
954 uint16 expUsage;
955 uint16 actUsage;
956 unsigned certDex;
957 CSSM_BOOL cA = CSSM_FALSE; // init for compiler warning
958 bool isLeaf; // end entity
959 bool isRoot; // root cert
960 CE_ExtendedKeyUsage *extendUsage;
961 CE_AuthorityKeyID *authorityId;
962 CSSM_RETURN outErr = CSSM_OK; // for gross, non-policy errors
963 CSSM_BOOL policyFail = CSSM_FALSE;
964
965 /* First, kTPDefault is a nop here */
966 if(policy == kTPDefault) {
967 return CSSM_OK;
968 }
969
970 if(certGroup == NULL) {
971 return CSSMERR_TP_INVALID_CERTGROUP;
972 }
973 numCerts = certGroup->numCerts();
974 if(numCerts == 0) {
975 return CSSMERR_TP_INVALID_CERTGROUP;
976 }
977 if(policy == kTPiSign) {
978 if(!verifiedToRoot) {
979 /* no way, this requires a root cert */
980 return CSSMERR_TP_VERIFY_ACTION_FAILED;
981 }
982 if(numCerts <= 1) {
983 /* nope, not for iSign */
984 return CSSMERR_TP_VERIFY_ACTION_FAILED;
985 }
986 }
987
988 /* cook up an iSignCertInfo array */
989 certInfo = (iSignCertInfo *)tpCalloc(alloc, numCerts, sizeof(iSignCertInfo));
990 /* subsequent errors to errOut: */
991
992 /* fill it with interesting info from parsed certs */
993 for(certDex=0; certDex<numCerts; certDex++) {
994 if(iSignGetCertInfo(alloc,
995 certGroup->certAtIndex(certDex),
996 &certInfo[certDex])) {
997 (certGroup->certAtIndex(certDex))->addStatusCode(
998 CSSMERR_TP_INVALID_CERTIFICATE);
999 /* this one is fatal */
1000 outErr = CSSMERR_TP_INVALID_CERTIFICATE;
1001 goto errOut;
1002 }
1003 }
1004
1005 /*
1006 * OK, the heart of TP enforcement.
1007 * First check for presence of required extensions and
1008 * critical extensions we don't understand.
1009 */
1010 for(certDex=0; certDex<numCerts; certDex++) {
1011 thisCertInfo = &certInfo[certDex];
1012 TPCertInfo *thisTpCertInfo = certGroup->certAtIndex(certDex);
1013
1014 if(thisCertInfo->foundUnknownCritical) {
1015 /* illegal for all policies */
1016 tpPolicyError("tp_policyVerify: critical flag in unknown "
1017 "extension");
1018 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN);
1019 policyFail = CSSM_TRUE;
1020 }
1021
1022 /*
1023 * Note it's possible for both of these to be true, for a
1024 * of length one (kTPx509Basic, kCrlPolicy only!)
1025 * FIXME: should this code work of the last cert in the chain is
1026 * NOT a root?
1027 */
1028 isLeaf = thisTpCertInfo->isLeaf();
1029 isRoot = thisTpCertInfo->isSelfSigned();
1030
1031 /*
1032 * BasicConstraints.cA
1033 * iSign: required in all but leaf and root,
1034 * for which it is optional (with default values of false
1035 * for leaf and true for root).
1036 * kTPx509Basic,
1037 * kTP_SSL,
1038 * kTP_SMIME always optional, default of false for leaf and
1039 * true for others
1040 * All: cA must be false for leaf, true for others
1041 */
1042 if(!thisCertInfo->basicConstraints.present) {
1043 /*
1044 * No basicConstraints present; infer a cA value if appropriate.
1045 */
1046 if(isLeaf) {
1047 /* cool, use default; note that kTPx509Basic with
1048 * certGroup length of one may take this case */
1049 cA = CSSM_FALSE;
1050 }
1051 else if(isRoot) {
1052 /* cool, use default */
1053 cA = CSSM_TRUE;
1054 }
1055 else {
1056 switch(policy) {
1057 case kTPx509Basic:
1058 case kTP_SSL:
1059 case kCrlPolicy:
1060 case kTP_SMIME:
1061 /*
1062 * not present, not leaf, not root....
1063 * ....RFC2459 says this can not be a CA
1064 */
1065 cA = CSSM_FALSE;
1066 break;
1067 case kTPiSign:
1068 /* required for iSign in this position */
1069 tpPolicyError("tp_policyVerify: no "
1070 "basicConstraints");
1071 policyFail = CSSM_TRUE;
1072 thisTpCertInfo->addStatusCode(
1073 CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS);
1074 break;
1075 default:
1076 /* not reached */
1077 assert(0);
1078 break;
1079 }
1080 }
1081 }
1082 else {
1083 /* basicConstraints present */
1084 #if BASIC_CONSTRAINTS_MUST_BE_CRITICAL
1085 /* disabled for verisign compatibility */
1086 if(!thisCertInfo->basicConstraints.critical) {
1087 /* per RFC 2459 */
1088 tpPolicyError("tp_policyVerify: basicConstraints marked "
1089 "not critical");
1090 policyFail = CSSM_TRUE;
1091 thisTpCertInfo->addStatusCode(CSSMERR_TP_VERIFY_ACTION_FAILED);
1092 }
1093 #endif /* BASIC_CONSTRAINTS_MUST_BE_CRITICAL */
1094
1095 const CE_BasicConstraints *bcp =
1096 &thisCertInfo->basicConstraints.extnData->basicConstraints;
1097
1098 cA = bcp->cA;
1099
1100 /* Verify pathLenConstraint if present */
1101 if(!isLeaf && // leaf, certDex=0, don't care
1102 cA && // p.l.c. only valid for CAs
1103 bcp->pathLenConstraintPresent) { // present?
1104 /*
1105 * pathLenConstraint=0 legal for certDex 1 only
1106 * pathLenConstraint=1 legal for certDex {1,2}
1107 * etc.
1108 */
1109 if(certDex > (bcp->pathLenConstraint + 1)) {
1110 tpPolicyError("tp_policyVerify: pathLenConstraint "
1111 "exceeded");
1112 policyFail = CSSM_TRUE;
1113 thisTpCertInfo->addStatusCode(
1114 CSSMERR_APPLETP_PATH_LEN_CONSTRAINT);
1115 }
1116 }
1117 }
1118
1119 if(isLeaf) {
1120 /*
1121 * Special cases to allow a chain of length 1, leaf and root
1122 * both true, and for caller to override the "leaf can't be a CA"
1123 * requirement when a CA cert is explicitly being evaluated as the
1124 * leaf.
1125 */
1126 if(cA && !isRoot &&
1127 !(actionFlags & CSSM_TP_ACTION_LEAF_IS_CA)) {
1128 tpPolicyError("tp_policyVerify: cA true for leaf");
1129 policyFail = CSSM_TRUE;
1130 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_CA);
1131 }
1132 } else if(!cA) {
1133 tpPolicyError("tp_policyVerify: cA false for non-leaf");
1134 policyFail = CSSM_TRUE;
1135 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_CA);
1136 }
1137
1138 /*
1139 * Authority Key Identifier optional
1140 * iSign : only allowed in !root.
1141 * If present, must not be critical.
1142 * kTPx509Basic,
1143 * kTP_SSL,
1144 * kTP_SMIME : ignored (though used later for chain verification)
1145 */
1146 if((policy == kTPiSign) && thisCertInfo->authorityId.present) {
1147 if(isRoot) {
1148 tpPolicyError("tp_policyVerify: authorityId in root");
1149 policyFail = CSSM_TRUE;
1150 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID);
1151 }
1152 if(thisCertInfo->authorityId.critical) {
1153 /* illegal per RFC 2459 */
1154 tpPolicyError("tp_policyVerify: authorityId marked "
1155 "critical");
1156 policyFail = CSSM_TRUE;
1157 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID);
1158 }
1159 }
1160
1161 /*
1162 * Subject Key Identifier optional
1163 * iSign : can't be critical.
1164 * kTPx509Basic,
1165 * kTP_SSL,
1166 * kTP_SMIME : ignored (though used later for chain verification)
1167 */
1168 if(thisCertInfo->subjectId.present) {
1169 if((policy == kTPiSign) && thisCertInfo->subjectId.critical) {
1170 tpPolicyError("tp_policyVerify: subjectId marked critical");
1171 policyFail = CSSM_TRUE;
1172 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_SUBJECT_ID);
1173 }
1174 }
1175
1176 /*
1177 * Key Usage optional except as noted required
1178 * iSign : required for non-root/non-leaf
1179 * Leaf cert : if present, usage = digitalSignature
1180 * Exception : if leaf, and keyUsage not present,
1181 * netscape-cert-type must be present, with
1182 * Object Signing bit set
1183 * kTPx509Basic,
1184 * kTP_SSL,
1185 * kTP_SMIME, : non-leaf : usage = keyCertSign
1186 * Leaf: don't care
1187 * kCrlPolicy : Leaf: usage = CRLSign
1188 * kTP_SMIME : if present, must be critical
1189 */
1190 if(thisCertInfo->keyUsage.present) {
1191 /*
1192 * Leaf cert: usage = digitalSignature
1193 * Others: usage = keyCertSign
1194 * We only require that one bit to be set, we ignore others.
1195 */
1196 if(isLeaf) {
1197 switch(policy) {
1198 case kTPiSign:
1199 expUsage = CE_KU_DigitalSignature;
1200 break;
1201 case kCrlPolicy:
1202 /* if present, this bit must be set */
1203 expUsage = CE_KU_CRLSign;
1204 break;
1205 default:
1206 /* hack to accept whatever's there */
1207 expUsage = thisCertInfo->keyUsage.extnData->keyUsage;
1208 break;
1209 }
1210 }
1211 else {
1212 /* this is true for all policies */
1213 expUsage = CE_KU_KeyCertSign;
1214 }
1215 actUsage = thisCertInfo->keyUsage.extnData->keyUsage;
1216 if(!(actUsage & expUsage)) {
1217 tpPolicyError("tp_policyVerify: bad keyUsage (leaf %s; "
1218 "usage 0x%x)",
1219 (certDex == 0) ? "TRUE" : "FALSE", actUsage);
1220 policyFail = CSSM_TRUE;
1221 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE);
1222 }
1223 #if 0
1224 if((policy == kTP_SMIME) && !thisCertInfo->keyUsage.critical) {
1225 /*
1226 * Per Radar 3410245, allow this for intermediate certs.
1227 */
1228 if(SMIME_KEY_USAGE_MUST_BE_CRITICAL || isLeaf || isRoot) {
1229 tpPolicyError("tp_policyVerify: key usage, !critical, SMIME");
1230 policyFail = CSSM_TRUE;
1231 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_SMIME_KEYUSAGE_NOT_CRITICAL);
1232 }
1233 }
1234 #endif
1235 }
1236 else if(policy == kTPiSign) {
1237 /*
1238 * iSign requires keyUsage present for non root OR
1239 * netscape-cert-type/ObjectSigning for leaf
1240 */
1241 if(isLeaf && thisCertInfo->netscapeCertType.present) {
1242 CE_NetscapeCertType ct =
1243 thisCertInfo->netscapeCertType.extnData->netscapeCertType;
1244
1245 if(!(ct & CE_NCT_ObjSign)) {
1246 tpPolicyError("tp_policyVerify: netscape-cert-type, "
1247 "!ObjectSign");
1248 policyFail = CSSM_TRUE;
1249 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE);
1250 }
1251 }
1252 else if(!isRoot) {
1253 tpPolicyError("tp_policyVerify: !isRoot, no keyUsage, "
1254 "!(leaf and netscapeCertType)");
1255 policyFail = CSSM_TRUE;
1256 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE);
1257 }
1258 }
1259 } /* for certDex, checking presence of extensions */
1260
1261 /*
1262 * Special case checking for leaf (end entity) cert
1263 *
1264 * iSign only: Extended key usage, optional for leaf,
1265 * value CSSMOID_ExtendedUseCodeSigning
1266 */
1267 if((policy == kTPiSign) && certInfo[0].extendKeyUsage.present) {
1268 extendUsage = &certInfo[0].extendKeyUsage.extnData->extendedKeyUsage;
1269 if(extendUsage->numPurposes != 1) {
1270 tpPolicyError("tp_policyVerify: bad extendUsage->numPurposes "
1271 "(%d)",
1272 (int)extendUsage->numPurposes);
1273 policyFail = CSSM_TRUE;
1274 (certGroup->certAtIndex(0))->addStatusCode(
1275 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
1276 }
1277 if(!tpCompareOids(extendUsage->purposes,
1278 &CSSMOID_ExtendedUseCodeSigning)) {
1279 tpPolicyError("tp_policyVerify: bad extendKeyUsage");
1280 policyFail = CSSM_TRUE;
1281 (certGroup->certAtIndex(0))->addStatusCode(
1282 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
1283 }
1284 }
1285
1286 /*
1287 * Verify authorityId-->subjectId linkage.
1288 * All optional - skip if needed fields not present.
1289 * Also, always skip last (root) cert.
1290 */
1291 for(certDex=0; certDex<(numCerts-1); certDex++) {
1292 if(!certInfo[certDex].authorityId.present ||
1293 !certInfo[certDex+1].subjectId.present) {
1294 continue;
1295 }
1296 authorityId = &certInfo[certDex].authorityId.extnData->authorityKeyID;
1297 if(!authorityId->keyIdentifierPresent) {
1298 /* we only know how to compare keyIdentifier */
1299 continue;
1300 }
1301 if(!tpCompareCssmData(&authorityId->keyIdentifier,
1302 &certInfo[certDex+1].subjectId.extnData->subjectKeyID)) {
1303 tpPolicyError("tp_policyVerify: bad key ID linkage");
1304 policyFail = CSSM_TRUE;
1305 (certGroup->certAtIndex(certDex))->addStatusCode(
1306 CSSMERR_APPLETP_INVALID_ID_LINKAGE);
1307 }
1308 }
1309
1310 /*
1311 * SSL: optionally verify common name.
1312 * FIXME - should this be before or after the root cert test? How can
1313 * we return both errors?
1314 */
1315 if(policy == kTP_SSL) {
1316 CSSM_RETURN cerr = tp_verifySslOpts(*certGroup, policyFieldData,
1317 certInfo[0]);
1318 if(cerr) {
1319 policyFail = CSSM_TRUE;
1320 }
1321 }
1322
1323 /* S/MIME */
1324 if(policy == kTP_SMIME) {
1325 CSSM_RETURN cerr = tp_verifySmimeOpts(*certGroup, policyFieldData,
1326 certInfo[0]);
1327 if(cerr) {
1328 policyFail = CSSM_TRUE;
1329 }
1330 }
1331
1332 if(policyFail && (outErr == CSSM_OK)) {
1333 /* only error in this function was policy failure */
1334 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
1335 }
1336 errOut:
1337 /* free resources */
1338 for(certDex=0; certDex<numCerts; certDex++) {
1339 thisCertInfo = &certInfo[certDex];
1340 iSignFreeCertInfo(clHand, thisCertInfo);
1341 }
1342 tpFree(alloc, certInfo);
1343 return outErr;
1344 }