]> git.saurik.com Git - apple/security.git/blob - AppleX509TP/tpPolicies.cpp
Security-54.1.3.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 * See if cert's Subject.commonName matches caller-specified hostname.
481 * Returns CSSM_TRUE if match, else returns CSSM_FALSE.
482 */
483 static CSSM_BOOL tpCompareCommonName(
484 TPCertInfo &cert,
485 const char *hostName,
486 uint32 hostNameLen)
487 {
488 char *commonName = NULL; // from cert's subject name
489 uint32 commonNameLen = 0;
490 CSSM_DATA_PTR subjNameData = NULL;
491 CSSM_RETURN crtn;
492 CSSM_BOOL ourRtn = CSSM_FALSE;
493
494 crtn = cert.fetchField(&CSSMOID_X509V1SubjectNameCStruct, &subjNameData);
495 if(crtn) {
496 /* should never happen, we shouldn't be here if there is no subject */
497 errorLog0("tp_verifySslOpts: error retrieving subject name");
498 return CSSM_FALSE;
499 }
500 CSSM_X509_NAME_PTR x509name = (CSSM_X509_NAME_PTR)subjNameData->Data;
501 if((x509name == NULL) || (subjNameData->Length != sizeof(CSSM_X509_NAME))) {
502 errorLog0("tp_verifySslOpts: malformed CSSM_X509_NAME");
503 cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
504 return CSSM_FALSE;
505 }
506
507 /* Now grunge thru the X509 name looking for a common name */
508 CSSM_X509_TYPE_VALUE_PAIR *ptvp;
509 CSSM_X509_RDN_PTR rdnp;
510 unsigned rdnDex;
511 unsigned pairDex;
512
513 for(rdnDex=0; rdnDex<x509name->numberOfRDNs; rdnDex++) {
514 rdnp = &x509name->RelativeDistinguishedName[rdnDex];
515 for(pairDex=0; pairDex<rdnp->numberOfPairs; pairDex++) {
516 ptvp = &rdnp->AttributeTypeAndValue[pairDex];
517 if(tpCompareOids(&ptvp->type, &CSSMOID_CommonName)) {
518 commonName = (char *)ptvp->value.Data;
519 commonNameLen = ptvp->value.Length;
520 ourRtn = tpCompareHostNames(hostName, hostNameLen,
521 commonName, commonNameLen);
522 if(ourRtn) {
523 /* success */
524 break;
525 }
526 /* else keep going, maybe there's another common name */
527 }
528 }
529 if(ourRtn) {
530 break;
531 }
532 }
533 cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
534 return ourRtn;
535 }
536
537 /*
538 * Compare ASCII form of an IP address to a CSSM_DATA containing
539 * the IP address's numeric components. Returns true on match.
540 */
541 static CSSM_BOOL tpCompIpAddrStr(
542 const char *str,
543 unsigned strLen,
544 const CSSM_DATA *numeric)
545 {
546 const char *cp = str;
547 const char *nextDot;
548 char buf[100];
549
550 if((numeric == NULL) || (numeric->Length == 0) || (str == NULL)) {
551 return CSSM_FALSE;
552 }
553 if(cp[strLen - 1] == '\0') {
554 /* ignore NULL terminator */
555 strLen--;
556 }
557 for(unsigned dex=0; dex<numeric->Length; dex++) {
558 /* cp points to start of current string digit */
559 /* find next dot */
560 const char *lastChar = cp + strLen;
561 nextDot = cp + 1;
562 for( ; nextDot<lastChar; nextDot++) {
563 if(*nextDot == '.') {
564 break;
565 }
566 }
567 if(nextDot == lastChar) {
568 /* legal and required on last digit */
569 if(dex != (numeric->Length - 1)) {
570 return CSSM_FALSE;
571 }
572 }
573 else if(dex == (numeric->Length - 1)) {
574 return CSSM_FALSE;
575 }
576 unsigned digLen = nextDot - cp;
577 if(digLen >= sizeof(buf)) {
578 /* preposterous */
579 return CSSM_FALSE;
580 }
581 memmove(buf, cp, digLen);
582 buf[digLen] = '\0';
583 /* incr digLen to include the next dot */
584 digLen++;
585 cp += digLen;
586 strLen -= digLen;
587 int digVal = atoi(buf);
588 if(digVal != numeric->Data[dex]) {
589 return CSSM_FALSE;
590 }
591 }
592 return CSSM_TRUE;
593 }
594
595 /*
596 * See if cert's subjectAltName matches caller-specified hostname, either
597 * as a dnsName or an iPAddress.
598 *
599 * Returns CSSM_TRUE if match, else returns CSSM_FALSE. Also indicates
600 * whether or not a dnsName was found (in which case the subject's
601 * common name should NOT be a candidate for verification).
602 */
603 static CSSM_BOOL tpCompareSubjectAltName(
604 TPCertInfo &cert,
605 const char *hostName,
606 uint32 hostNameLen,
607 bool &dnsNameFound) // RETURNED
608 {
609 CSSM_DATA_PTR subjAltNameData = NULL;
610 CSSM_RETURN crtn;
611 CSSM_BOOL ourRtn = CSSM_FALSE;
612
613 dnsNameFound = false;
614 crtn = cert.fetchField(&CSSMOID_SubjectAltName, &subjAltNameData);
615 if(crtn) {
616 /* common failure, no subjectAltName found */
617 return CSSM_FALSE;
618 }
619 CSSM_X509_EXTENSION_PTR exten =
620 (CSSM_X509_EXTENSION_PTR)subjAltNameData->Data;
621 /* Paranoid check of extension integrity */
622 if((exten == NULL) ||
623 (subjAltNameData->Length != sizeof(CSSM_X509_EXTENSION)) ||
624 (exten->format != CSSM_X509_DATAFORMAT_PARSED) ||
625 (exten->value.parsedValue == NULL)) {
626 errorLog0("tpCompareSubjectAltName: malformed CSSM_X509_EXTENSION");
627 cert.freeField(&CSSMOID_SubjectAltName, subjAltNameData);
628 return CSSM_FALSE;
629 }
630
631 CE_GeneralNames *names = (CE_GeneralNames *)exten->value.parsedValue;
632 char *serverName;
633 unsigned serverNameLen;
634
635 /* Search thru the CE_GeneralNames looking for a DNSName or IP Address */
636 for(unsigned dex=0; dex<names->numNames; dex++) {
637 CE_GeneralName *name = &names->generalName[dex];
638 switch(name->nameType) {
639 case GNT_IPAddress:
640 ourRtn = tpCompIpAddrStr(hostName, hostNameLen, &name->name);
641 break;
642
643 case GNT_DNSName:
644 if(name->berEncoded) {
645 errorLog0("tpCompareSubjectAltName: malformed "
646 "CE_GeneralName (1)\n");
647 break;
648 }
649 serverName = (char *)name->name.Data;
650 if(serverName == NULL) {
651 errorLog0("tpCompareSubjectAltName: malformed "
652 "CE_GeneralName (2)\n");
653 break;
654 }
655 serverNameLen = name->name.Length;
656 ourRtn = tpCompareHostNames(hostName, hostNameLen,
657 serverName, serverNameLen);
658 dnsNameFound = true;
659 break;
660
661 default:
662 /* not interested, proceed to next name */
663 break;
664 }
665 if(ourRtn) {
666 /* success */
667 break;
668 }
669 }
670 cert.freeField(&CSSMOID_SubjectAltName, subjAltNameData);
671 return ourRtn;
672 }
673
674 /* is host name in the form of a.b.c.d, where a,b,c, and d are digits? */
675 static CSSM_BOOL tpIsNumeric(
676 const char *hostName,
677 unsigned hostNameLen)
678 {
679 if(hostName[hostNameLen - 1] == '\0') {
680 /* ignore NULL terminator */
681 hostNameLen--;
682 }
683 for(unsigned i=0; i<hostNameLen; i++) {
684 char c = *hostName++;
685 if(isdigit(c)) {
686 continue;
687 }
688 if(c != '.') {
689 return CSSM_FALSE;
690 }
691 }
692 return CSSM_TRUE;
693 }
694
695 /*
696 * Verify SSL options. Currently this just consists of matching the
697 * leaf cert's subject common name against the caller's (optional)
698 * server name.
699 */
700 static CSSM_RETURN tp_verifySslOpts(
701 TPCertGroup &certGroup,
702 const CSSM_APPLE_TP_SSL_OPTIONS *sslOpts)
703 {
704 if(sslOpts == NULL) {
705 /* optional */
706 return CSSM_OK;
707 }
708
709 unsigned hostNameLen = sslOpts->ServerNameLen;
710
711 if(hostNameLen == 0) {
712 /* optional */
713 return CSSM_OK;
714 }
715 if(sslOpts->ServerName == NULL) {
716 return CSSMERR_TP_INVALID_POINTER;
717 }
718
719 /* convert caller's hostname string to lower case */
720 char *hostName = (char *)certGroup.alloc().malloc(hostNameLen);
721 memmove(hostName, sslOpts->ServerName, hostNameLen);
722 tpToLower(hostName, hostNameLen);
723
724 TPCertInfo *leaf = certGroup.certAtIndex(0);
725 assert(leaf != NULL);
726
727 CSSM_BOOL match = CSSM_FALSE;
728
729 /* First check subjectAltName... */
730 bool dnsNameFound = false;
731 match = tpCompareSubjectAltName(*leaf, hostName, hostNameLen,
732 dnsNameFound);
733 /*
734 * Then common name, if
735 * -- no match from subjectAltName, AND
736 * -- dnsName was NOT found, AND
737 * -- hostName is not strictly numeric form (1.2.3.4)
738 */
739 if(!match && !dnsNameFound && !tpIsNumeric(hostName, hostNameLen)) {
740 match = tpCompareCommonName(*leaf, hostName, hostNameLen);
741 }
742 certGroup.alloc().free(hostName);
743 if(match) {
744 return CSSM_OK;
745 }
746 else {
747 leaf->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH);
748 return CSSMERR_TP_VERIFY_ACTION_FAILED;
749 }
750 }
751
752 /*
753 * RFC2459 says basicConstraints must be flagged critical for
754 * CA certs, but Verisign doesn't work that way.
755 */
756 #define BASIC_CONSTRAINTS_MUST_BE_CRITICAL 0
757
758 /*
759 * TP iSign spec says Extended Key Usage required for leaf certs,
760 * but Verisign doesn't work that way.
761 */
762 #define EXTENDED_KEY_USAGE_REQUIRED_FOR_LEAF 0
763
764 /*
765 * TP iSign spec says Subject Alternate Name required for leaf certs,
766 * but Verisign doesn't work that way.
767 */
768 #define SUBJECT_ALT_NAME_REQUIRED_FOR_LEAF 0
769
770 /*
771 * TP iSign spec originally required KeyUsage for all certs, but
772 * Verisign doesn't have that in their roots.
773 */
774 #define KEY_USAGE_REQUIRED_FOR_ROOT 0
775
776 /*
777 * Public routine to perform TP verification on a constructed
778 * cert group.
779 * Returns CSSM_TRUE on success.
780 * Asumes the chain has passed basic subject/issuer verification. First cert of
781 * incoming certGroup is end-entity (leaf).
782 *
783 * Per-policy details:
784 * iSign: Assumes that last cert in incoming certGroup is a root cert.
785 * Also assumes a cert group of more than one cert.
786 * kTPx509Basic: CertGroup of length one allowed.
787 */
788 CSSM_RETURN tp_policyVerify(
789 TPPolicy policy,
790 CssmAllocator &alloc,
791 CSSM_CL_HANDLE clHand,
792 CSSM_CSP_HANDLE cspHand,
793 TPCertGroup *certGroup,
794 CSSM_BOOL verifiedToRoot, // last cert is good root
795 const CSSM_APPLE_TP_ACTION_DATA *actionData,
796 const CSSM_APPLE_TP_SSL_OPTIONS *sslOpts,
797 void *policyOpts) // future options
798 {
799 iSignCertInfo *certInfo = NULL;
800 uint32 numCerts;
801 iSignCertInfo *thisCertInfo;
802 uint16 expUsage;
803 uint16 actUsage;
804 unsigned certDex;
805 CSSM_BOOL cA = CSSM_FALSE; // init for compiler warning
806 CSSM_BOOL isLeaf; // end entity
807 CSSM_BOOL isRoot; // root cert
808 CE_ExtendedKeyUsage *extendUsage;
809 CE_AuthorityKeyID *authorityId;
810 CSSM_RETURN outErr = CSSM_OK; // for gross, non-policy errors
811 CSSM_BOOL policyFail = CSSM_FALSE;
812
813 /* First, kTPDefault is a nop here */
814 if(policy == kTPDefault) {
815 return CSSM_OK;
816 }
817
818 if(certGroup == NULL) {
819 return CSSMERR_TP_INVALID_CERTGROUP;
820 }
821 numCerts = certGroup->numCerts();
822 if(numCerts == 0) {
823 return CSSMERR_TP_INVALID_CERTGROUP;
824 }
825 if(policy == kTPiSign) {
826 if(!verifiedToRoot) {
827 /* no way, this requires a root cert */
828 return CSSMERR_TP_INVALID_CERTGROUP;
829 }
830 if(numCerts <= 1) {
831 /* nope, not for iSign */
832 return CSSMERR_TP_INVALID_CERTGROUP;
833 }
834 }
835
836 /* cook up an iSignCertInfo array */
837 certInfo = (iSignCertInfo *)tpCalloc(alloc, numCerts, sizeof(iSignCertInfo));
838 /* subsequent errors to errOut: */
839
840 /* fill it with interesting info from parsed certs */
841 for(certDex=0; certDex<numCerts; certDex++) {
842 if(iSignGetCertInfo(alloc,
843 certGroup->certAtIndex(certDex),
844 &certInfo[certDex])) {
845 (certGroup->certAtIndex(certDex))->addStatusCode(
846 CSSMERR_TP_INVALID_CERTIFICATE);
847 /* this one is fatal */
848 outErr = CSSMERR_TP_INVALID_CERTIFICATE;
849 goto errOut;
850 }
851 }
852
853 /*
854 * OK, the heart of TP enforcement.
855 * First check for presence of required extensions and
856 * critical extensions we don't understand.
857 */
858 for(certDex=0; certDex<numCerts; certDex++) {
859 thisCertInfo = &certInfo[certDex];
860 TPCertInfo *thisTpCertInfo = certGroup->certAtIndex(certDex);
861
862 if(thisCertInfo->foundUnknownCritical) {
863 /* illegal for all policies */
864 errorLog0("tp_policyVerify: critical flag in unknown extension\n");
865 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN);
866 policyFail = CSSM_TRUE;
867 }
868
869 /*
870 * Note it's possible for both of these to be true, for a
871 * of length one (kTPx509Basic only!)
872 */
873 isLeaf = (certDex == 0) ? CSSM_TRUE : CSSM_FALSE;
874 isRoot = (certDex == (numCerts - 1)) ? CSSM_TRUE : CSSM_FALSE;
875
876 /*
877 * BasicConstraints.cA
878 * iSign: required in all but leaf and root,
879 * for which it is optional (with default values of false
880 * for leaf and true for root).
881 * kTPx509Basic,
882 * kTP_SSL: always optional, default of false for leaf and
883 * true for others
884 * All: cA must be false for leaf, true for others
885 */
886 if(!thisCertInfo->basicConstraints.present) {
887 if(isLeaf) {
888 /* cool, use default; note that kTPx509Basic with
889 * certGroup length of one may take this case */
890 cA = CSSM_FALSE;
891 }
892 else if(isRoot) {
893 /* cool, use default */
894 cA = CSSM_TRUE;
895 }
896 else {
897 switch(policy) {
898 case kTPx509Basic:
899 case kTP_SSL:
900 /*
901 * not present, not leaf, not root, kTPx509Basic
902 * ....RFC2459 says this can not be a CA
903 */
904 cA = CSSM_FALSE;
905 break;
906 case kTPiSign:
907 /* required for iSign in this position */
908 errorLog0("tp_policyVerify: no basicConstraints\n");
909 policyFail = CSSM_TRUE;
910 thisTpCertInfo->addStatusCode(
911 CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS);
912 break;
913 default:
914 /* not reached */
915 break;
916 }
917 }
918 }
919 else {
920 /* basicConstraints present */
921 #if BASIC_CONSTRAINTS_MUST_BE_CRITICAL
922 /* disabled for verisign compatibility */
923 if(!thisCertInfo->basicConstraints.critical) {
924 /* per RFC 2459 */
925 errorLog0("tp_policyVerify: basicConstraints marked not critical\n");
926 policyFail = CSSM_TRUE;
927 thisTpCertInfo->addStatusCode(CSSMERR_TP_VERIFY_ACTION_FAILED);
928 }
929 #endif /* BASIC_CONSTRAINTS_MUST_BE_CRITICAL */
930
931 const CE_BasicConstraints *bcp =
932 &thisCertInfo->basicConstraints.extnData->basicConstraints;
933
934 cA = bcp->cA;
935
936 /* Verify pathLenConstraint if present */
937 if(!isLeaf && // leaf, certDex=0, don't care
938 cA && // p.l.c. only valid for CAs
939 bcp->pathLenConstraintPresent) { // present?
940 /*
941 * pathLenConstraint=0 legal for certDex 1 only
942 * pathLenConstraint=1 legal for certDex {1,2}
943 * etc.
944 */
945 if(certDex > (bcp->pathLenConstraint + 1)) {
946 errorLog0("tp_policyVerify: pathLenConstraint exceeded\n");
947 policyFail = CSSM_TRUE;
948 thisTpCertInfo->addStatusCode(
949 CSSMERR_APPLETP_PATH_LEN_CONSTRAINT);
950 }
951 }
952 }
953
954 if(isLeaf) {
955 /* special case to allow a chain of length 1, leaf and root
956 * both true (kTPx509Basic, kTP_SSL only) */
957 if(cA && !isRoot) {
958 errorLog0("tp_policyVerify: cA true for leaf\n");
959 policyFail = CSSM_TRUE;
960 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_CA);
961 }
962 } else if(!cA) {
963 errorLog0("tp_policyVerify: cA false for non-leaf\n");
964 policyFail = CSSM_TRUE;
965 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_CA);
966 }
967
968 /*
969 * Authority Key Identifier optional
970 * iSign : only allowed in !root.
971 * If present, must not be critical.
972 * kTPx509Basic :
973 * kTP_SSL : ignored (though used later for chain verification)
974 */
975 if((policy == kTPiSign) && thisCertInfo->authorityId.present) {
976 if(isRoot) {
977 errorLog0("tp_policyVerify: authorityId in root\n");
978 policyFail = CSSM_TRUE;
979 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID);
980 }
981 if(thisCertInfo->authorityId.critical) {
982 /* illegal per RFC 2459 */
983 errorLog0("tp_policyVerify: authorityId marked critical\n");
984 policyFail = CSSM_TRUE;
985 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID);
986 }
987 }
988
989 /*
990 * Subject Key Identifier optional
991 * iSign : can't be critical.
992 * kTPx509Basic,
993 * kTP_SSL : ignored (though used later for chain verification)
994 */
995 if(thisCertInfo->subjectId.present) {
996 if((policy == kTPiSign) && thisCertInfo->subjectId.critical) {
997 errorLog0("tp_policyVerify: subjectId marked critical\n");
998 policyFail = CSSM_TRUE;
999 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_SUBJECT_ID);
1000 }
1001 }
1002
1003 /*
1004 * Key Usage optional except as noted required
1005 * iSign : required for non-root/non-leaf
1006 * Leaf cert : if present, usage = digitalSignature
1007 * Exception : if leaf, and keyUsage not present,
1008 * netscape-cert-type must be present, with
1009 * Object Signing bit set
1010 * kTPx509Basic : non-leaf : usage = keyCertSign
1011 * Leaf: don't care
1012 */
1013 if(thisCertInfo->keyUsage.present) {
1014 /*
1015 * Leaf cert: usage = digitalSignature
1016 * Others: usage = keyCertSign
1017 * We only require that one bit to be set, we ignore others.
1018 */
1019 if(isLeaf) {
1020 if(policy == kTPiSign) {
1021 expUsage = CE_KU_DigitalSignature;
1022 }
1023 else {
1024 /* hack to accept whatever's there */
1025 expUsage = thisCertInfo->keyUsage.extnData->keyUsage;
1026 }
1027 }
1028 else {
1029 /* this is true for all policies */
1030 expUsage = CE_KU_KeyCertSign;
1031 }
1032 actUsage = thisCertInfo->keyUsage.extnData->keyUsage;
1033 if(!(actUsage & expUsage)) {
1034 errorLog2("tp_policyVerify: bad keyUsage (leaf %s; usage 0x%x)\n",
1035 (certDex == 0) ? "TRUE" : "FALSE", actUsage);
1036 policyFail = CSSM_TRUE;
1037 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE);
1038 }
1039 }
1040 else if(policy == kTPiSign) {
1041 /*
1042 * iSign requires keyUsage present for non root OR
1043 * netscape-cert-type/ObjectSigning for leaf
1044 */
1045 if(isLeaf && thisCertInfo->netscapeCertType.present) {
1046 CE_NetscapeCertType ct =
1047 thisCertInfo->netscapeCertType.extnData->netscapeCertType;
1048
1049 if(!(ct & CE_NCT_ObjSign)) {
1050 errorLog0("tp_policyVerify: netscape-cert-type, !ObjectSign\n");
1051 policyFail = CSSM_TRUE;
1052 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE);
1053 }
1054 }
1055 else if(!isRoot) {
1056 errorLog0("tp_policyVerify: !isRoot, no keyUsage, !(leaf and netscapeCertType)\n");
1057 policyFail = CSSM_TRUE;
1058 thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE);
1059 }
1060 }
1061 } /* for certDex, checking presence of extensions */
1062
1063 /*
1064 * Special case checking for leaf (end entity) cert
1065 *
1066 * iSign only: Extended key usage, optional for leaf,
1067 * value CSSMOID_ExtendedUseCodeSigning
1068 */
1069 if((policy == kTPiSign) && certInfo[0].extendKeyUsage.present) {
1070 extendUsage = &certInfo[0].extendKeyUsage.extnData->extendedKeyUsage;
1071 if(extendUsage->numPurposes != 1) {
1072 errorLog1("tp_policyVerify: bad extendUsage->numPurposes (%d)\n",
1073 (int)extendUsage->numPurposes);
1074 policyFail = CSSM_TRUE;
1075 (certGroup->certAtIndex(0))->addStatusCode(
1076 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
1077 }
1078 if(!tpCompareOids(extendUsage->purposes,
1079 &CSSMOID_ExtendedUseCodeSigning)) {
1080 errorLog0("tp_policyVerify: bad extendKeyUsage\n");
1081 policyFail = CSSM_TRUE;
1082 (certGroup->certAtIndex(0))->addStatusCode(
1083 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
1084 }
1085 }
1086
1087 /*
1088 * Verify authorityId-->subjectId linkage.
1089 * All optional - skip if needed fields not present.
1090 * Also, always skip last (root) cert.
1091 */
1092 for(certDex=0; certDex<(numCerts-1); certDex++) {
1093 if(!certInfo[certDex].authorityId.present ||
1094 !certInfo[certDex+1].subjectId.present) {
1095 continue;
1096 }
1097 authorityId = &certInfo[certDex].authorityId.extnData->authorityKeyID;
1098 if(!authorityId->keyIdentifierPresent) {
1099 /* we only know how to compare keyIdentifier */
1100 continue;
1101 }
1102 if(!tpCompareCssmData(&authorityId->keyIdentifier,
1103 &certInfo[certDex+1].subjectId.extnData->subjectKeyID)) {
1104 errorLog0("tp_policyVerify: bad key ID linkage\n");
1105 policyFail = CSSM_TRUE;
1106 (certGroup->certAtIndex(certDex))->addStatusCode(
1107 CSSMERR_APPLETP_INVALID_ID_LINKAGE);
1108 }
1109 }
1110
1111 /*
1112 * SSL: optionally verify common name.
1113 * FIXME - should this be before or after the root cert test? How can
1114 * we return both errors?
1115 */
1116 if(policy == kTP_SSL) {
1117 CSSM_RETURN cerr = tp_verifySslOpts(*certGroup, sslOpts);
1118 if(cerr) {
1119 policyFail = CSSM_TRUE;
1120 }
1121 }
1122
1123 /* iSign, SSL: compare root against known root certs */
1124 /* FIXME - this goes away soon */
1125 #if TP_ROOT_CERT_ENABLE
1126 if((outErr == CSSM_OK) && // skip if we have a gross error (other than policy failure)
1127 (actionData != NULL) &&
1128 (actionData->ActionFlags & 0x80000000)) { // The secret "enable root cert check" flag
1129 TPCertInfo *lastCert = certGroup->lastCert();
1130 if(policy == kTPiSign) {
1131 bool brtn = tp_isIsignRootCert(clHand, lastCert);
1132 if(!brtn) {
1133 policyFail = CSSM_TRUE;
1134 }
1135 }
1136 else if(verifiedToRoot && (policy == kTP_SSL)) {
1137 /* note SSL doesn't require root here */
1138 bool brtn = tp_isSslRootCert(clHand, lastCert);
1139 if(!brtn) {
1140 outErr = CSSMERR_TP_INVALID_ANCHOR_CERT;
1141 }
1142 }
1143 }
1144 #endif /* TP_ROOT_CERT_ENABLE */
1145 if(policyFail && (outErr == CSSM_OK)) {
1146 /* only error in this function was policy failure */
1147 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
1148 }
1149 errOut:
1150 /* free resources */
1151 for(certDex=0; certDex<numCerts; certDex++) {
1152 thisCertInfo = &certInfo[certDex];
1153 iSignFreeCertInfo(clHand, thisCertInfo);
1154 }
1155 tpFree(alloc, certInfo);
1156 return outErr;
1157 }