]> git.saurik.com Git - apple/security.git/blob - libsecurity_apple_x509_tp/lib/tpPolicies.cpp
Security-55179.1.tar.gz
[apple/security.git] / libsecurity_apple_x509_tp / lib / tpPolicies.cpp
1 /*
2 * Copyright (c) 2000-2012 Apple 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
23 #include <Security/cssmtype.h>
24 #include <Security/cssmapi.h>
25 #include "tpPolicies.h"
26 #include <Security/cssmerr.h>
27 #include "tpdebugging.h"
28 #include "certGroupUtils.h"
29 #include <Security/x509defs.h>
30 #include <Security/oidsalg.h>
31 #include <Security/oidsattr.h>
32 #include <Security/oidscert.h>
33 #include <Security/certextensions.h>
34 #include <Security/cssmapple.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <assert.h>
38 #include <CoreFoundation/CFString.h>
39
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 we're interested in */
58 iSignExtenInfo authorityId;
59 iSignExtenInfo subjectId;
60 iSignExtenInfo keyUsage;
61 iSignExtenInfo extendKeyUsage;
62 iSignExtenInfo basicConstraints;
63 iSignExtenInfo netscapeCertType;
64 iSignExtenInfo subjectAltName;
65 iSignExtenInfo certPolicies;
66 iSignExtenInfo qualCertStatements;
67 iSignExtenInfo nameConstraints;
68 iSignExtenInfo policyMappings;
69 iSignExtenInfo policyConstraints;
70 iSignExtenInfo inhibitAnyPolicy;
71 iSignExtenInfo certificatePolicies;
72
73 /* flag indicating presence of a critical extension we don't understand */
74 CSSM_BOOL foundUnknownCritical;
75 /* flag indicating that this certificate was signed with a known-broken algorithm */
76 CSSM_BOOL untrustedSigAlg;
77
78 } iSignCertInfo;
79
80 /*
81 * The list of Qualified Cert Statement statementIds we understand, even though
82 * we don't actually do anything with them; if these are found in a Qualified
83 * Cert Statement that's critical, we can truthfully say "yes we understand this".
84 */
85 static const CSSM_OID_PTR knownQualifiedCertStatements[] =
86 {
87 (const CSSM_OID_PTR)&CSSMOID_OID_QCS_SYNTAX_V1,
88 (const CSSM_OID_PTR)&CSSMOID_OID_QCS_SYNTAX_V2,
89 (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_COMPLIANCE,
90 (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE,
91 (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_RETENTION,
92 (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_SSCD
93 };
94 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
95
96 static CSSM_RETURN tp_verifyMacAppStoreReciptOpts(TPCertGroup &certGroup,
97 const CSSM_DATA *fieldOpts, const iSignCertInfo *certInfo);
98 bool certificatePoliciesContainsOID(const CE_CertPolicies *certPolicies, const CSSM_OID *oidToFind);
99
100 /*
101 * Certificate policy OIDs
102 */
103
104 /* 2.5.29.32.0 */
105 #define ANY_POLICY_OID OID_EXTENSION, 0x32, 0x00
106 #define ANY_POLICY_OID_LEN OID_EXTENSION_LENGTH + 2
107
108 /* 2.5.29.54 */
109 #define INHIBIT_ANY_POLICY_OID OID_EXTENSION, 0x54
110 #define INHIBIT_ANY_POLICY_OID_LEN OID_EXTENSION_LENGTH + 1
111
112 /* 2.16.840.1.101.2.1 */
113 #define US_DOD_INFOSEC 0x60, 0x86, 0x48, 0x01, 0x65, 0x02, 0x01
114 #define US_DOD_INFOSEC_LEN 7
115
116 /* 2.16.840.1.101.2.1.11.10 */
117 #define PIV_AUTH_OID US_DOD_INFOSEC, 0x0B, 0x0A
118 #define PIV_AUTH_OID_LEN US_DOD_INFOSEC_LEN + 2
119
120 /* 2.16.840.1.101.2.1.11.20 */
121 #define PIV_AUTH_2048_OID US_DOD_INFOSEC, 0x0B, 0x14
122 #define PIV_AUTH_2048_OID_LEN US_DOD_INFOSEC_LEN + 2
123
124 static const uint8 OID_ANY_POLICY[] = {ANY_POLICY_OID};
125 const CSSM_OID CSSMOID_ANY_POLICY = {ANY_POLICY_OID_LEN, (uint8 *)OID_ANY_POLICY};
126 static const uint8 OID_INHIBIT_ANY_POLICY[] = {INHIBIT_ANY_POLICY_OID};
127 const CSSM_OID CSSMOID_INHIBIT_ANY_POLICY = {INHIBIT_ANY_POLICY_OID_LEN, (uint8 *)OID_INHIBIT_ANY_POLICY};
128 static const uint8 OID_PIV_AUTH[] = {PIV_AUTH_OID};
129 const CSSM_OID CSSMOID_PIV_AUTH = {PIV_AUTH_OID_LEN, (uint8 *)OID_PIV_AUTH};
130 static const uint8 OID_PIV_AUTH_2048[] = {PIV_AUTH_2048_OID};
131 const CSSM_OID CSSMOID_PIV_AUTH_2048 = {PIV_AUTH_2048_OID_LEN, (uint8 *)OID_PIV_AUTH_2048};
132
133 static CSSM_RETURN tp_verifyAppleIDSharingOpts(TPCertGroup &certGroup,
134 const CSSM_DATA *fieldOpts, // optional Common Name
135 const iSignCertInfo *certInfo);
136 /*
137 * Setup a single iSignExtenInfo. Called once per known extension
138 * per cert.
139 */
140 static CSSM_RETURN tpSetupExtension(
141 Allocator &alloc,
142 CSSM_DATA *extnData,
143 iSignExtenInfo *extnInfo) // which component of certInfo
144 {
145 if(extnData->Length != sizeof(CSSM_X509_EXTENSION)) {
146 tpPolicyError("tpSetupExtension: malformed CSSM_FIELD");
147 return CSSMERR_TP_UNKNOWN_FORMAT;
148 }
149 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)extnData->Data;
150 extnInfo->present = CSSM_TRUE;
151 extnInfo->critical = cssmExt->critical;
152 extnInfo->extnData = (CE_Data *)cssmExt->value.parsedValue;
153 extnInfo->valToFree = extnData;
154 return CSSM_OK;
155 }
156
157 /*
158 * Fetch a known extension, set up associated iSignExtenInfo if present.
159 */
160 static CSSM_RETURN iSignFetchExtension(
161 Allocator &alloc,
162 TPCertInfo *tpCert,
163 const CSSM_OID *fieldOid, // which extension to fetch
164 iSignExtenInfo *extnInfo) // where the info goes
165 {
166 CSSM_DATA_PTR fieldValue; // mallocd by CL
167 CSSM_RETURN crtn;
168
169 crtn = tpCert->fetchField(fieldOid, &fieldValue);
170 switch(crtn) {
171 case CSSM_OK:
172 break;
173 case CSSMERR_CL_NO_FIELD_VALUES:
174 /* field not present, OK */
175 return CSSM_OK;
176 default:
177 return crtn;
178 }
179 return tpSetupExtension(alloc,
180 fieldValue,
181 extnInfo);
182 }
183
184 /*
185 * This function performs a check of an extension marked 'critical'
186 * to see if it's one we understand. Returns CSSM_OK if the extension
187 * is acceptable, CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN if unknown.
188 */
189 static CSSM_RETURN iSignVerifyCriticalExtension(
190 CSSM_X509_EXTENSION *cssmExt)
191 {
192 if (!cssmExt || !cssmExt->extnId.Data)
193 return CSSMERR_TP_INVALID_FIELD_POINTER;
194
195 if (!cssmExt->critical)
196 return CSSM_OK;
197
198 /* FIXME: remove when policyConstraints NSS template is fixed */
199 if (!memcmp(cssmExt->extnId.Data, CSSMOID_PolicyConstraints.Data, CSSMOID_PolicyConstraints.Length))
200 return CSSM_OK;
201
202 if (cssmExt->extnId.Length > APPLE_EXTENSION_OID_LENGTH &&
203 !memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION.Data, APPLE_EXTENSION_OID_LENGTH)) {
204 /* This extension's OID is under the appleCertificateExtensions arc */
205 return CSSM_OK;
206
207 }
208 return CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN;
209 }
210
211 /*
212 * Search for all unknown extensions. If we find one which is flagged critical,
213 * flag certInfo->foundUnknownCritical. Only returns error on gross errors.
214 */
215 static CSSM_RETURN iSignSearchUnknownExtensions(
216 TPCertInfo *tpCert,
217 iSignCertInfo *certInfo)
218 {
219 CSSM_RETURN crtn;
220 CSSM_DATA_PTR fieldValue = NULL;
221 CSSM_HANDLE searchHand = CSSM_INVALID_HANDLE;
222 uint32 numFields = 0;
223
224 crtn = CSSM_CL_CertGetFirstCachedFieldValue(tpCert->clHand(),
225 tpCert->cacheHand(),
226 &CSSMOID_X509V3CertificateExtensionCStruct,
227 &searchHand,
228 &numFields,
229 &fieldValue);
230 switch(crtn) {
231 case CSSM_OK:
232 /* found one, proceed */
233 break;
234 case CSSMERR_CL_NO_FIELD_VALUES:
235 /* no unknown extensions present, OK */
236 return CSSM_OK;
237 default:
238 return crtn;
239 }
240
241 if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
242 tpPolicyError("iSignSearchUnknownExtensions: malformed CSSM_FIELD");
243 return CSSMERR_TP_UNKNOWN_FORMAT;
244 }
245 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
246 if(iSignVerifyCriticalExtension(cssmExt) != CSSM_OK) {
247 /* BRRZAPP! Found an unknown extension marked critical */
248 certInfo->foundUnknownCritical = CSSM_TRUE;
249 goto fini;
250 }
251 CSSM_CL_FreeFieldValue(tpCert->clHand(),
252 &CSSMOID_X509V3CertificateExtensionCStruct,
253 fieldValue);
254 fieldValue = NULL;
255
256 /* process remaining unknown extensions */
257 for(unsigned i=1; i<numFields; i++) {
258 crtn = CSSM_CL_CertGetNextCachedFieldValue(tpCert->clHand(),
259 searchHand,
260 &fieldValue);
261 if(crtn) {
262 /* should never happen */
263 tpPolicyError("searchUnknownExtensions: GetNextCachedFieldValue"
264 "error");
265 break;
266 }
267 if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
268 tpPolicyError("iSignSearchUnknownExtensions: "
269 "malformed CSSM_FIELD");
270 crtn = CSSMERR_TP_UNKNOWN_FORMAT;
271 break;
272 }
273 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
274 if(iSignVerifyCriticalExtension(cssmExt) != CSSM_OK) {
275 /* BRRZAPP! Found an unknown extension marked critical */
276 certInfo->foundUnknownCritical = CSSM_TRUE;
277 break;
278 }
279 CSSM_CL_FreeFieldValue(tpCert->clHand(),
280 &CSSMOID_X509V3CertificateExtensionCStruct,
281 fieldValue);
282 fieldValue = NULL;
283 } /* for additional fields */
284
285 fini:
286 if(fieldValue) {
287 CSSM_CL_FreeFieldValue(tpCert->clHand(),
288 &CSSMOID_X509V3CertificateExtensionCStruct,
289 fieldValue);
290 }
291 if(searchHand != CSSM_INVALID_HANDLE) {
292 CSSM_CL_CertAbortQuery(tpCert->clHand(), searchHand);
293 }
294 return crtn;
295 }
296
297 /*
298 * Check the signature algorithm. If it's known to be untrusted,
299 * flag certInfo->untrustedSigAlg.
300 */
301 static void iSignCheckSignatureAlgorithm(
302 TPCertInfo *tpCert,
303 iSignCertInfo *certInfo)
304 {
305 CSSM_X509_ALGORITHM_IDENTIFIER *algId = NULL;
306 CSSM_DATA_PTR valueToFree = NULL;
307
308 algId = tp_CertGetAlgId(tpCert, &valueToFree);
309 if(!algId ||
310 tpCompareCssmData(&algId->algorithm, &CSSMOID_MD2) ||
311 tpCompareCssmData(&algId->algorithm, &CSSMOID_MD2WithRSA)) {
312 certInfo->untrustedSigAlg = CSSM_TRUE;
313 } else {
314 certInfo->untrustedSigAlg = CSSM_FALSE;
315 }
316
317 if (valueToFree) {
318 tp_CertFreeAlgId(tpCert->clHand(), valueToFree);
319 }
320 }
321
322 /*
323 * Given a TPCertInfo, fetch the associated iSignCertInfo fields.
324 * Returns CSSM_FAIL on error.
325 */
326 static CSSM_RETURN iSignGetCertInfo(
327 Allocator &alloc,
328 TPCertInfo *tpCert,
329 iSignCertInfo *certInfo)
330 {
331 CSSM_RETURN crtn;
332
333 /* first grind thru the extensions we're interested in */
334 crtn = iSignFetchExtension(alloc,
335 tpCert,
336 &CSSMOID_AuthorityKeyIdentifier,
337 &certInfo->authorityId);
338 if(crtn) {
339 return crtn;
340 }
341 crtn = iSignFetchExtension(alloc,
342 tpCert,
343 &CSSMOID_SubjectKeyIdentifier,
344 &certInfo->subjectId);
345 if(crtn) {
346 return crtn;
347 }
348 crtn = iSignFetchExtension(alloc,
349 tpCert,
350 &CSSMOID_KeyUsage,
351 &certInfo->keyUsage);
352 if(crtn) {
353 return crtn;
354 }
355 crtn = iSignFetchExtension(alloc,
356 tpCert,
357 &CSSMOID_ExtendedKeyUsage,
358 &certInfo->extendKeyUsage);
359 if(crtn) {
360 return crtn;
361 }
362 crtn = iSignFetchExtension(alloc,
363 tpCert,
364 &CSSMOID_BasicConstraints,
365 &certInfo->basicConstraints);
366 if(crtn) {
367 return crtn;
368 }
369 crtn = iSignFetchExtension(alloc,
370 tpCert,
371 &CSSMOID_NetscapeCertType,
372 &certInfo->netscapeCertType);
373 if(crtn) {
374 return crtn;
375 }
376 crtn = iSignFetchExtension(alloc,
377 tpCert,
378 &CSSMOID_SubjectAltName,
379 &certInfo->subjectAltName);
380 if(crtn) {
381 return crtn;
382 }
383 crtn = iSignFetchExtension(alloc,
384 tpCert,
385 &CSSMOID_CertificatePolicies,
386 &certInfo->certPolicies);
387 if(crtn) {
388 return crtn;
389 }
390 crtn = iSignFetchExtension(alloc,
391 tpCert,
392 &CSSMOID_QC_Statements,
393 &certInfo->qualCertStatements);
394 if(crtn) {
395 return crtn;
396 }
397 crtn = iSignFetchExtension(alloc,
398 tpCert,
399 &CSSMOID_NameConstraints,
400 &certInfo->nameConstraints);
401 if(crtn) {
402 return crtn;
403 }
404 crtn = iSignFetchExtension(alloc,
405 tpCert,
406 &CSSMOID_PolicyMappings,
407 &certInfo->policyMappings);
408 if(crtn) {
409 return crtn;
410 }
411 crtn = iSignFetchExtension(alloc,
412 tpCert,
413 &CSSMOID_PolicyConstraints,
414 &certInfo->policyConstraints);
415 if(crtn) {
416 return crtn;
417 }
418 crtn = iSignFetchExtension(alloc,
419 tpCert,
420 &CSSMOID_InhibitAnyPolicy,
421 &certInfo->inhibitAnyPolicy);
422 if(crtn) {
423 return crtn;
424 }
425 crtn = iSignFetchExtension(alloc,
426 tpCert,
427 &CSSMOID_CertificatePolicies,
428 &certInfo->certificatePolicies);
429 if(crtn) {
430 return crtn;
431 }
432
433 /* check signature algorithm field */
434 iSignCheckSignatureAlgorithm(tpCert, certInfo);
435
436 /* now look for extensions we don't understand - the only thing we're interested
437 * in is the critical flag. */
438 return iSignSearchUnknownExtensions(tpCert, certInfo);
439 }
440
441 /*
442 * Free (via CL) the fields allocated in iSignGetCertInfo().
443 */
444 static void iSignFreeCertInfo(
445 CSSM_CL_HANDLE clHand,
446 iSignCertInfo *certInfo)
447 {
448 if(certInfo->authorityId.present) {
449 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_AuthorityKeyIdentifier,
450 certInfo->authorityId.valToFree);
451 }
452 if(certInfo->subjectId.present) {
453 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_SubjectKeyIdentifier,
454 certInfo->subjectId.valToFree);
455 }
456 if(certInfo->keyUsage.present) {
457 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_KeyUsage,
458 certInfo->keyUsage.valToFree);
459 }
460 if(certInfo->extendKeyUsage.present) {
461 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_ExtendedKeyUsage,
462 certInfo->extendKeyUsage.valToFree);
463 }
464 if(certInfo->basicConstraints.present) {
465 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_BasicConstraints,
466 certInfo->basicConstraints.valToFree);
467 }
468 if(certInfo->netscapeCertType.present) {
469 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_NetscapeCertType,
470 certInfo->netscapeCertType.valToFree);
471 }
472 if(certInfo->subjectAltName.present) {
473 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_SubjectAltName,
474 certInfo->subjectAltName.valToFree);
475 }
476 if(certInfo->certPolicies.present) {
477 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_CertificatePolicies,
478 certInfo->certPolicies.valToFree);
479 }
480 // if(certInfo->policyConstraints.present) {
481 // CSSM_CL_FreeFieldValue(clHand, &CSSMOID_PolicyConstraints,
482 // certInfo->policyConstraints.valToFree);
483 // }
484 if(certInfo->qualCertStatements.present) {
485 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_QC_Statements,
486 certInfo->qualCertStatements.valToFree);
487 }
488 if(certInfo->certificatePolicies.present) {
489 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_CertificatePolicies,
490 certInfo->certificatePolicies.valToFree);
491 }
492 }
493
494 /*
495 * See if cert's Subject.{commonName,EmailAddress} matches caller-specified
496 * string. Returns CSSM_TRUE if match, else returns CSSM_FALSE.
497 * Also indicates whether *any* of the specified fields were found, regardless
498 * of match state.
499 */
500 typedef enum {
501 SN_CommonName, // CSSMOID_CommonName, host name format
502 SN_Email // CSSMOID_EmailAddress
503 } SubjSubjNameSearchType;
504
505 static CSSM_BOOL tpCompareSubjectName(
506 TPCertInfo &cert,
507 SubjSubjNameSearchType searchType,
508 bool normalizeAll, // for SN_Email case: lower-case all of
509 // the cert's value, not just the portion
510 // after the '@'
511 const char *callerStr, // already tpToLower'd
512 uint32 callerStrLen,
513 bool &fieldFound)
514 {
515 char *certName = NULL; // from cert's subject name
516 uint32 certNameLen = 0;
517 CSSM_DATA_PTR subjNameData = NULL;
518 CSSM_RETURN crtn;
519 CSSM_BOOL ourRtn = CSSM_FALSE;
520 const CSSM_OID *oidSrch;
521
522 fieldFound = false;
523 switch(searchType) {
524 case SN_CommonName:
525 oidSrch = &CSSMOID_CommonName;
526 break;
527 case SN_Email:
528 oidSrch = &CSSMOID_EmailAddress;
529 break;
530 default:
531 assert(0);
532 return CSSM_FALSE;
533 }
534 crtn = cert.fetchField(&CSSMOID_X509V1SubjectNameCStruct, &subjNameData);
535 if(crtn) {
536 /* should never happen, we shouldn't be here if there is no subject */
537 tpPolicyError("tpCompareSubjectName: error retrieving subject name");
538 return CSSM_FALSE;
539 }
540 CSSM_X509_NAME_PTR x509name = (CSSM_X509_NAME_PTR)subjNameData->Data;
541 if((x509name == NULL) || (subjNameData->Length != sizeof(CSSM_X509_NAME))) {
542 tpPolicyError("tpCompareSubjectName: malformed CSSM_X509_NAME");
543 cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
544 return CSSM_FALSE;
545 }
546
547 /* Now grunge thru the X509 name looking for a common name */
548 CSSM_X509_TYPE_VALUE_PAIR *ptvp;
549 CSSM_X509_RDN_PTR rdnp;
550 unsigned rdnDex;
551 unsigned pairDex;
552
553 for(rdnDex=0; rdnDex<x509name->numberOfRDNs; rdnDex++) {
554 rdnp = &x509name->RelativeDistinguishedName[rdnDex];
555 for(pairDex=0; pairDex<rdnp->numberOfPairs; pairDex++) {
556 ptvp = &rdnp->AttributeTypeAndValue[pairDex];
557 if(tpCompareOids(&ptvp->type, oidSrch)) {
558 fieldFound = true;
559 certName = (char *)ptvp->value.Data;
560 certNameLen = ptvp->value.Length;
561 switch(searchType) {
562 case SN_CommonName:
563 {
564 /* handle odd encodings that we need to convert to 8-bit */
565 CFStringBuiltInEncodings encoding;
566 CFDataRef cfd = NULL;
567 bool doConvert = false;
568 switch(ptvp->valueType) {
569 case BER_TAG_T61_STRING:
570 /* a.k.a. Teletex */
571 encoding = kCFStringEncodingISOLatin1;
572 doConvert = true;
573 break;
574 case BER_TAG_PKIX_BMP_STRING:
575 encoding = kCFStringEncodingUnicode;
576 doConvert = true;
577 break;
578 /*
579 * All others - either take as is, or let it fail due to
580 * illegal/incomprehensible format
581 */
582 default:
583 break;
584 }
585 if(doConvert) {
586 /* raw data ==> CFString */
587 cfd = CFDataCreate(NULL, (UInt8 *)certName, certNameLen);
588 if(cfd == NULL) {
589 /* try next component */
590 break;
591 }
592 CFStringRef cfStr = CFStringCreateFromExternalRepresentation(
593 NULL, cfd, encoding);
594 CFRelease(cfd);
595 if(cfStr == NULL) {
596 tpPolicyError("tpCompareSubjectName: bad str (1)");
597 break;
598 }
599
600 /* CFString ==> straight ASCII */
601 cfd = CFStringCreateExternalRepresentation(NULL,
602 cfStr, kCFStringEncodingASCII, 0);
603 CFRelease(cfStr);
604 if(cfd == NULL) {
605 tpPolicyError("tpCompareSubjectName: bad str (2)");
606 break;
607 }
608 certNameLen = CFDataGetLength(cfd);
609 certName = (char *)CFDataGetBytePtr(cfd);
610 }
611 ourRtn = tpCompareHostNames(callerStr, callerStrLen,
612 certName, certNameLen);
613 if(doConvert) {
614 assert(cfd != NULL);
615 CFRelease(cfd);
616 }
617 break;
618 }
619 case SN_Email:
620 ourRtn = tpCompareEmailAddr(callerStr, callerStrLen,
621 certName, certNameLen, normalizeAll);
622 break;
623 }
624 if(ourRtn) {
625 /* success */
626 break;
627 }
628 /* else keep going, maybe there's another common name */
629 }
630 }
631 if(ourRtn) {
632 break;
633 }
634 }
635 cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
636 return ourRtn;
637 }
638
639 /*
640 * Compare ASCII form of an IP address to a CSSM_DATA containing
641 * the IP address's numeric components. Returns true on match.
642 */
643 static CSSM_BOOL tpCompIpAddrStr(
644 const char *str,
645 unsigned strLen,
646 const CSSM_DATA *numeric)
647 {
648 const char *cp = str;
649 const char *nextDot;
650 char buf[100];
651
652 if((numeric == NULL) || (numeric->Length == 0) || (str == NULL)) {
653 return CSSM_FALSE;
654 }
655 if(cp[strLen - 1] == '\0') {
656 /* ignore NULL terminator */
657 strLen--;
658 }
659 for(unsigned dex=0; dex<numeric->Length; dex++) {
660 /* cp points to start of current string digit */
661 /* find next dot */
662 const char *lastChar = cp + strLen;
663 nextDot = cp + 1;
664 for( ; nextDot<lastChar; nextDot++) {
665 if(*nextDot == '.') {
666 break;
667 }
668 }
669 if(nextDot == lastChar) {
670 /* legal and required on last digit */
671 if(dex != (numeric->Length - 1)) {
672 return CSSM_FALSE;
673 }
674 }
675 else if(dex == (numeric->Length - 1)) {
676 return CSSM_FALSE;
677 }
678 unsigned digLen = nextDot - cp;
679 if(digLen >= sizeof(buf)) {
680 /* preposterous */
681 return CSSM_FALSE;
682 }
683 memmove(buf, cp, digLen);
684 buf[digLen] = '\0';
685 /* incr digLen to include the next dot */
686 digLen++;
687 cp += digLen;
688 strLen -= digLen;
689 int digVal = atoi(buf);
690 if(digVal != numeric->Data[dex]) {
691 return CSSM_FALSE;
692 }
693 }
694 return CSSM_TRUE;
695 }
696
697 /*
698 * See if cert's subjectAltName contains an element matching caller-specified
699 * string, hostname, in the following forms:
700 *
701 * SAN_HostName : dnsName, iPAddress
702 * SAN_Email : RFC822Name
703 *
704 * Returns CSSM_TRUE if match, else returns CSSM_FALSE.
705 *
706 * Also indicates whether or not a dnsName (search type HostName) or
707 * RFC822Name (search type SAM_Email) was found, regardless of result
708 * of comparison.
709 *
710 * The appStr/appStrLen args are optional - if NULL/0, only the
711 * search for dnsName/RFC822Name is done.
712 */
713 typedef enum {
714 SAN_HostName,
715 SAN_Email
716 } SubjAltNameSearchType;
717
718 static CSSM_BOOL tpCompareSubjectAltName(
719 const iSignExtenInfo &subjAltNameInfo,
720 const char *appStr, // caller has lower-cased as appropriate
721 uint32 appStrLen,
722 SubjAltNameSearchType searchType,
723 bool normalizeAll, // for SAN_Email case: lower-case all of
724 // the cert's value, not just the portion
725 // after the '@'
726 bool &dnsNameFound, // RETURNED, SAN_HostName case
727 bool &emailFound) // RETURNED, SAN_Email case
728 {
729 dnsNameFound = false;
730 emailFound = false;
731 if(!subjAltNameInfo.present) {
732 /* common failure, no subjectAltName found */
733 return CSSM_FALSE;
734 }
735
736 CE_GeneralNames *names = &subjAltNameInfo.extnData->subjectAltName;
737 CSSM_BOOL ourRtn = CSSM_FALSE;
738 char *certName;
739 unsigned certNameLen;
740
741 /* Search thru the CE_GeneralNames looking for the appropriate attribute */
742 for(unsigned dex=0; dex<names->numNames; dex++) {
743 CE_GeneralName *name = &names->generalName[dex];
744 switch(searchType) {
745 case SAN_HostName:
746 switch(name->nameType) {
747 case GNT_IPAddress:
748 if(appStr == NULL) {
749 /* nothing to do here */
750 break;
751 }
752 ourRtn = tpCompIpAddrStr(appStr, appStrLen, &name->name);
753 break;
754
755 case GNT_DNSName:
756 if(name->berEncoded) {
757 tpErrorLog("tpCompareSubjectAltName: malformed "
758 "CE_GeneralName (1)\n");
759 break;
760 }
761 certName = (char *)name->name.Data;
762 if(certName == NULL) {
763 tpErrorLog("tpCompareSubjectAltName: malformed "
764 "CE_GeneralName (2)\n");
765 break;
766 }
767 certNameLen = name->name.Length;
768 dnsNameFound = true;
769 if(appStr != NULL) {
770 /* skip if caller passed in NULL */
771 ourRtn = tpCompareHostNames(appStr, appStrLen,
772 certName, certNameLen);
773 }
774 break;
775
776 default:
777 /* not interested, proceed to next name */
778 break;
779 }
780 break; /* from case HostName */
781
782 case SAN_Email:
783 if(name->nameType != GNT_RFC822Name) {
784 /* not interested */
785 break;
786 }
787 certName = (char *)name->name.Data;
788 if(certName == NULL) {
789 tpErrorLog("tpCompareSubjectAltName: malformed "
790 "GNT_RFC822Name\n");
791 break;
792 }
793 certNameLen = name->name.Length;
794 emailFound = true;
795 if(appStr != NULL) {
796 ourRtn = tpCompareEmailAddr(appStr, appStrLen, certName,
797 certNameLen, normalizeAll);
798 }
799 break;
800 }
801 if(ourRtn) {
802 /* success */
803 break;
804 }
805 }
806 return ourRtn;
807 }
808
809 /* is host name in the form of a.b.c.d, where a,b,c, and d are digits? */
810 static CSSM_BOOL tpIsNumeric(
811 const char *hostName,
812 unsigned hostNameLen)
813 {
814 if(hostName[hostNameLen - 1] == '\0') {
815 /* ignore NULL terminator */
816 hostNameLen--;
817 }
818 for(unsigned i=0; i<hostNameLen; i++) {
819 char c = *hostName++;
820 if(isdigit(c)) {
821 continue;
822 }
823 if(c != '.') {
824 return CSSM_FALSE;
825 }
826 }
827 return CSSM_TRUE;
828 }
829
830 /*
831 * Convert a typed string represented by a CSSM_X509_TYPE_VALUE_PAIR to a
832 * CFStringRef. Caller owns and must release the result. NULL return means
833 * unconvertible input "string".
834 */
835 static CFStringRef tpTvpToCfString(
836 const CSSM_X509_TYPE_VALUE_PAIR *tvp)
837 {
838 CFStringBuiltInEncodings encoding;
839 switch(tvp->valueType) {
840 case BER_TAG_T61_STRING:
841 /* a.k.a. Teletex */
842 encoding = kCFStringEncodingISOLatin1;
843 break;
844 case BER_TAG_PKIX_BMP_STRING:
845 encoding = kCFStringEncodingUnicode;
846 break;
847 case BER_TAG_PRINTABLE_STRING:
848 case BER_TAG_IA5_STRING:
849 case BER_TAG_PKIX_UTF8_STRING:
850 encoding = kCFStringEncodingUTF8;
851 break;
852 default:
853 return NULL;
854 }
855
856 /* raw data ==> CFString */
857 CFDataRef cfd = CFDataCreate(NULL, tvp->value.Data, tvp->value.Length);
858 if(cfd == NULL) {
859 return NULL;
860 }
861 CFStringRef cfStr = CFStringCreateFromExternalRepresentation(NULL, cfd, encoding);
862 CFRelease(cfd);
863 return cfStr;
864 }
865
866 /*
867 * Compare a CFString and a string represented by a CSSM_X509_TYPE_VALUE_PAIR.
868 * Returns CSSM_TRUE if they are equal.
869 */
870 static bool tpCompareTvpToCfString(
871 const CSSM_X509_TYPE_VALUE_PAIR *tvp,
872 CFStringRef refStr,
873 CFOptionFlags flags) // e.g., kCFCompareCaseInsensitive
874 {
875 CFStringRef cfStr = tpTvpToCfString(tvp);
876 if(cfStr == NULL) {
877 return false;
878 }
879 CFComparisonResult res = CFStringCompare(refStr, cfStr, flags);
880 CFRelease(cfStr);
881 if(res == kCFCompareEqualTo) {
882 return true;
883 }
884 else {
885 return false;
886 }
887 }
888
889 /*
890 * Given one iSignCertInfo, determine whether or not the specified
891 * EKU OID, or - optionally - CSSMOID_ExtendedKeyUsageAny - is present.
892 * Returns true if so, else false.
893 */
894 static bool tpVerifyEKU(
895 const iSignCertInfo &certInfo,
896 const CSSM_OID &ekuOid,
897 bool ekuAnyOK) // if true, CSSMOID_ExtendedKeyUsageAny counts as "found"
898 {
899 if(!certInfo.extendKeyUsage.present) {
900 return false;
901 }
902 CE_ExtendedKeyUsage *eku = &certInfo.extendKeyUsage.extnData->extendedKeyUsage;
903 assert(eku != NULL);
904
905 for(unsigned i=0; i<eku->numPurposes; i++) {
906 const CSSM_OID *foundEku = &eku->purposes[i];
907 if(tpCompareOids(foundEku, &ekuOid)) {
908 return true;
909 }
910 if(ekuAnyOK && tpCompareOids(foundEku, &CSSMOID_ExtendedKeyUsageAny)) {
911 return true;
912 }
913 }
914 return false;
915 }
916
917 /*
918 * Given one iSignCertInfo, determine whether or not the specified
919 * Certificate Policy OID, or - optionally - CSSMOID_ANY_POLICY - is present.
920 * Returns true if so, else false.
921 */
922 static bool tpVerifyCPE(
923 const iSignCertInfo &certInfo,
924 const CSSM_OID &cpOid,
925 bool anyPolicyOK) // if true, CSSMOID_ANY_POLICY counts as "found"
926 {
927 if(!certInfo.certPolicies.present) {
928 return false;
929 }
930 CE_CertPolicies *cp = &certInfo.certPolicies.extnData->certPolicies;
931 assert(cp != NULL);
932
933 for(unsigned i=0; i<cp->numPolicies; i++) {
934 const CE_PolicyInformation *foundPolicy = &cp->policies[i];
935 if(tpCompareOids(&foundPolicy->certPolicyId, &cpOid)) {
936 return true;
937 }
938 if(anyPolicyOK && tpCompareOids(&foundPolicy->certPolicyId, &CSSMOID_ANY_POLICY)) {
939 return true;
940 }
941 }
942 return false;
943 }
944
945 /*
946 * Verify iChat handle. We search for a matching (case-insensitive) string
947 * comprised of:
948 *
949 * -- name component ("dmitch") from subject name's CommonName
950 * -- implicit '@'
951 * -- domain name from subject name's organizationalUnit
952 *
953 * Plus we require an Organization component of "Apple Computer, Inc." or "Apple Inc."
954 */
955 static bool tpCompareIChatHandleName(
956 TPCertInfo &cert,
957 const char *iChatHandle, // UTF8
958 uint32 iChatHandleLen)
959 {
960 CSSM_DATA_PTR subjNameData = NULL; // from fetchField
961 CSSM_RETURN crtn;
962 bool ourRtn = false;
963 CSSM_X509_NAME_PTR x509name;
964 CSSM_X509_TYPE_VALUE_PAIR *ptvp;
965 CSSM_X509_RDN_PTR rdnp;
966 unsigned rdnDex;
967 unsigned pairDex;
968
969 /* search until all of these are true */
970 CSSM_BOOL commonNameMatch = CSSM_FALSE; // name before '@'
971 CSSM_BOOL orgUnitMatch = CSSM_FALSE; // domain after '@
972 CSSM_BOOL orgMatch = CSSM_FALSE; // Apple Computer, Inc. (or Apple Inc.)
973
974 /*
975 * incoming UTF8 handle ==> two components.
976 * First convert to CFString.
977 */
978 if(iChatHandle[iChatHandleLen - 1] == '\0') {
979 /* avoid NULL when creating CFStrings */
980 iChatHandleLen--;
981 }
982 CFDataRef cfd = CFDataCreate(NULL, (const UInt8 *)iChatHandle, iChatHandleLen);
983 if(cfd == NULL) {
984 return false;
985 }
986 CFStringRef handleStr = CFStringCreateFromExternalRepresentation(NULL, cfd,
987 kCFStringEncodingUTF8);
988 CFRelease(cfd);
989 if(handleStr == NULL) {
990 tpPolicyError("tpCompareIChatHandleName: bad incoming handle (1)");
991 return false;
992 }
993
994 /*
995 * Find the '@' delimiter
996 */
997 CFRange whereIsAt;
998 whereIsAt = CFStringFind(handleStr, CFSTR("@"), 0);
999 if(whereIsAt.length == 0) {
1000 tpPolicyError("tpCompareIChatHandleName: bad incoming handle: no @");
1001 CFRelease(handleStr);
1002 return false;
1003 }
1004
1005 /*
1006 * Two components, before and after delimiter
1007 */
1008 CFRange r = {0, whereIsAt.location};
1009 CFStringRef iChatName = CFStringCreateWithSubstring(NULL, handleStr, r);
1010 if(iChatName == NULL) {
1011 tpPolicyError("tpCompareIChatHandleName: bad incoming handle (2)");
1012 CFRelease(handleStr);
1013 return false;
1014 }
1015 r.location = whereIsAt.location + 1; // after the '@'
1016 r.length = CFStringGetLength(handleStr) - r.location;
1017 CFStringRef iChatDomain = CFStringCreateWithSubstring(NULL, handleStr, r);
1018 CFRelease(handleStr);
1019 if(iChatDomain == NULL) {
1020 tpPolicyError("tpCompareIChatHandleName: bad incoming handle (3)");
1021 CFRelease(iChatName);
1022 return false;
1023 }
1024 /* subsequent errors to errOut: */
1025
1026 /* get subject name in CSSM form, all subsequent ops work on that */
1027 crtn = cert.fetchField(&CSSMOID_X509V1SubjectNameCStruct, &subjNameData);
1028 if(crtn) {
1029 /* should never happen, we shouldn't be here if there is no subject */
1030 tpPolicyError("tpCompareIChatHandleName: error retrieving subject name");
1031 goto errOut;
1032 }
1033
1034 x509name = (CSSM_X509_NAME_PTR)subjNameData->Data;
1035 if((x509name == NULL) || (subjNameData->Length != sizeof(CSSM_X509_NAME))) {
1036 tpPolicyError("tpCompareIChatHandleName: malformed CSSM_X509_NAME");
1037 goto errOut;
1038 }
1039
1040 /* Now grunge thru the X509 name looking for three fields */
1041
1042 for(rdnDex=0; rdnDex<x509name->numberOfRDNs; rdnDex++) {
1043 rdnp = &x509name->RelativeDistinguishedName[rdnDex];
1044 for(pairDex=0; pairDex<rdnp->numberOfPairs; pairDex++) {
1045 ptvp = &rdnp->AttributeTypeAndValue[pairDex];
1046 if(!commonNameMatch &&
1047 tpCompareOids(&ptvp->type, &CSSMOID_CommonName) &&
1048 tpCompareTvpToCfString(ptvp, iChatName, kCFCompareCaseInsensitive)) {
1049 commonNameMatch = CSSM_TRUE;
1050 }
1051
1052 if(!orgUnitMatch &&
1053 tpCompareOids(&ptvp->type, &CSSMOID_OrganizationalUnitName) &&
1054 tpCompareTvpToCfString(ptvp, iChatDomain, kCFCompareCaseInsensitive)) {
1055 orgUnitMatch = CSSM_TRUE;
1056 }
1057
1058 if(!orgMatch &&
1059 tpCompareOids(&ptvp->type, &CSSMOID_OrganizationName) &&
1060 /* this one is case sensitive */
1061 (tpCompareTvpToCfString(ptvp, CFSTR("Apple Computer, Inc."), 0) ||
1062 tpCompareTvpToCfString(ptvp, CFSTR("Apple Inc."), 0))) {
1063 orgMatch = CSSM_TRUE;
1064 }
1065
1066 if(commonNameMatch && orgUnitMatch && orgMatch) {
1067 /* TA DA */
1068 ourRtn = true;
1069 goto errOut;
1070 }
1071 }
1072 }
1073 errOut:
1074 cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
1075 CFRelease(iChatName);
1076 CFRelease(iChatDomain);
1077 return ourRtn;
1078 }
1079
1080 /*
1081 * Verify SSL options. Currently this just consists of matching the
1082 * leaf cert's subject common name against the caller's (optional)
1083 * server name.
1084 */
1085 static CSSM_RETURN tp_verifySslOpts(
1086 TPPolicy policy,
1087 TPCertGroup &certGroup,
1088 const CSSM_DATA *sslFieldOpts,
1089 const iSignCertInfo &leafCertInfo)
1090 {
1091 CSSM_APPLE_TP_SSL_OPTIONS *sslOpts = NULL;
1092 unsigned hostNameLen = 0;
1093 const char *serverName = NULL;
1094 TPCertInfo *leaf = certGroup.certAtIndex(0);
1095 assert(leaf != NULL);
1096
1097 /* CSSM_APPLE_TP_SSL_OPTIONS is optional */
1098 if((sslFieldOpts != NULL) && (sslFieldOpts->Data != NULL)) {
1099 sslOpts = (CSSM_APPLE_TP_SSL_OPTIONS *)sslFieldOpts->Data;
1100 switch(sslOpts->Version) {
1101 case CSSM_APPLE_TP_SSL_OPTS_VERSION:
1102 if(sslFieldOpts->Length != sizeof(CSSM_APPLE_TP_SSL_OPTIONS)) {
1103 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
1104 }
1105 break;
1106 /* handle backwards compatibility here if necessary */
1107 default:
1108 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
1109 }
1110 hostNameLen = sslOpts->ServerNameLen;
1111 serverName = sslOpts->ServerName;
1112 }
1113
1114 /* host name check is optional */
1115 if(hostNameLen != 0) {
1116 if(serverName == NULL) {
1117 return CSSMERR_TP_INVALID_POINTER;
1118 }
1119
1120 /* convert caller's hostname string to lower case */
1121 char *hostName = (char *)certGroup.alloc().malloc(hostNameLen);
1122 memmove(hostName, serverName, hostNameLen);
1123 tpToLower(hostName, hostNameLen);
1124
1125 CSSM_BOOL match = CSSM_FALSE;
1126
1127 /* First check subjectAltName... */
1128 bool dnsNameFound = false;
1129 bool dummy;
1130 match = tpCompareSubjectAltName(leafCertInfo.subjectAltName,
1131 hostName, hostNameLen,
1132 SAN_HostName, false, dnsNameFound, dummy);
1133
1134 /*
1135 * Then common name, if
1136 * -- no match from subjectAltName, AND
1137 * -- dnsName was NOT found, AND
1138 * -- hostName is not strictly numeric form (1.2.3.4)
1139 */
1140 if(!match && !dnsNameFound && !tpIsNumeric(hostName, hostNameLen)) {
1141 bool fieldFound;
1142 match = tpCompareSubjectName(*leaf, SN_CommonName, false, hostName, hostNameLen,
1143 fieldFound);
1144 }
1145 certGroup.alloc().free(hostName);
1146 if(!match) {
1147 if(leaf->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH)) {
1148 return CSSMERR_APPLETP_HOSTNAME_MISMATCH;
1149 }
1150 }
1151 }
1152
1153 /*
1154 * Ensure that, if an extendedKeyUsage extension is present in the
1155 * leaf, that either anyExtendedKeyUsage or the appropriate
1156 * CSSMOID_{Server,Client}Auth, or a SeverGatedCrypto usage is present.
1157 */
1158 const iSignExtenInfo &ekuInfo = leafCertInfo.extendKeyUsage;
1159 if(ekuInfo.present) {
1160 bool foundGoodEku = false;
1161 bool isServer = true;
1162 CE_ExtendedKeyUsage *eku = (CE_ExtendedKeyUsage *)ekuInfo.extnData;
1163 assert(eku != NULL);
1164
1165 /*
1166 * Determine appropriate extended key usage; default is SSL server
1167 */
1168 const CSSM_OID *extUse = &CSSMOID_ServerAuth;
1169 if((sslOpts != NULL) && /* optional, default server side */
1170 (sslOpts->Version > 0) && /* this was added in struct version 1 */
1171 (sslOpts->Flags & CSSM_APPLE_TP_SSL_CLIENT)) {
1172 extUse = &CSSMOID_ClientAuth;
1173 isServer = false;
1174 }
1175
1176 /* search for that one or for "any" indicator */
1177 for(unsigned i=0; i<eku->numPurposes; i++) {
1178 const CSSM_OID *purpose = &eku->purposes[i];
1179 if(tpCompareOids(purpose, extUse)) {
1180 foundGoodEku = true;
1181 break;
1182 }
1183 if(tpCompareOids(purpose, &CSSMOID_ExtendedKeyUsageAny)) {
1184 foundGoodEku = true;
1185 break;
1186 }
1187 if((policy == kTP_IPSec) && (tpCompareOids(purpose, &CSSMOID_EKU_IPSec))) {
1188 foundGoodEku = true;
1189 break;
1190 }
1191 if(isServer) {
1192 /* server gated crypto: server side only */
1193 if(tpCompareOids(purpose, &CSSMOID_NetscapeSGC)) {
1194 foundGoodEku = true;
1195 break;
1196 }
1197 if(tpCompareOids(purpose, &CSSMOID_MicrosoftSGC)) {
1198 foundGoodEku = true;
1199 break;
1200 }
1201 }
1202 }
1203 if(!foundGoodEku) {
1204 if(leaf->addStatusCode(CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE)) {
1205 return CSSMERR_TP_VERIFY_ACTION_FAILED;
1206 }
1207 }
1208 }
1209 return CSSM_OK;
1210 }
1211
1212 /*
1213 * Verify SMIME and iChat options.
1214 * This deals with both S/MIME and iChat policies; within the iChat domain it
1215 * deals with Apple-specific .mac certs as well as what we call "generic AIM"
1216 * certs, as used in the Windows AIM client.
1217 */
1218 #define CE_CIPHER_MASK (~(CE_KU_EncipherOnly | CE_KU_DecipherOnly))
1219
1220 static CSSM_RETURN tp_verifySmimeOpts(
1221 TPPolicy policy,
1222 TPCertGroup &certGroup,
1223 const CSSM_DATA *smimeFieldOpts,
1224 const iSignCertInfo &leafCertInfo)
1225 {
1226 bool iChat = (policy == kTP_iChat) ? true : false;
1227
1228 /*
1229 * The CSSM_APPLE_TP_SMIME_OPTIONS pointer is optional as is everything in it.
1230 */
1231 CSSM_APPLE_TP_SMIME_OPTIONS *smimeOpts = NULL;
1232 if(smimeFieldOpts != NULL) {
1233 smimeOpts = (CSSM_APPLE_TP_SMIME_OPTIONS *)smimeFieldOpts->Data;
1234 }
1235 if(smimeOpts != NULL) {
1236 switch(smimeOpts->Version) {
1237 case CSSM_APPLE_TP_SMIME_OPTS_VERSION:
1238 if(smimeFieldOpts->Length !=
1239 sizeof(CSSM_APPLE_TP_SMIME_OPTIONS)) {
1240 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
1241 }
1242 break;
1243 /* handle backwards compatibility here if necessary */
1244 default:
1245 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
1246 }
1247 }
1248
1249 TPCertInfo *leaf = certGroup.certAtIndex(0);
1250 assert(leaf != NULL);
1251
1252 /* Verify optional email address, a.k.a. handle for iChat policy */
1253 unsigned emailLen = 0;
1254 if(smimeOpts != NULL) {
1255 emailLen = smimeOpts->SenderEmailLen;
1256 }
1257
1258 bool match = false;
1259 bool emailFoundInSAN = false;
1260 bool iChatHandleFound = false; /* indicates a genuine Apple iChat cert */
1261 bool emailFoundInDN = false;
1262 if(emailLen != 0) {
1263 if(smimeOpts->SenderEmail == NULL) {
1264 return CSSMERR_TP_INVALID_POINTER;
1265 }
1266
1267 /* iChat - first try the Apple custom format */
1268 if(iChat) {
1269 iChatHandleFound = tpCompareIChatHandleName(*leaf, smimeOpts->SenderEmail,
1270 emailLen);
1271 if(iChatHandleFound) {
1272 match = true;
1273 }
1274
1275 }
1276
1277 if(!match) {
1278 /*
1279 * normalize caller's email string
1280 * SMIME - lowercase only the portion after '@'
1281 * iChat - lowercase all of it
1282 */
1283 char *email = (char *)certGroup.alloc().malloc(emailLen);
1284 memmove(email, smimeOpts->SenderEmail, emailLen);
1285 tpNormalizeAddrSpec(email, emailLen, iChat);
1286
1287
1288 /*
1289 * First check subjectAltName. The emailFound bool indicates
1290 * that *some* email address was found, regardless of a match
1291 * condition.
1292 */
1293 bool dummy;
1294 match = tpCompareSubjectAltName(leafCertInfo.subjectAltName,
1295 email, emailLen,
1296 SAN_Email, iChat, dummy, emailFoundInSAN);
1297
1298 /*
1299 * Then subject DN, CSSMOID_EmailAddress, if no match from
1300 * subjectAltName. In this case the whole email address is
1301 * case insensitive (RFC 3280, section 4.1.2.6), so
1302 * renormalize.
1303 */
1304 if(!match) {
1305 tpNormalizeAddrSpec(email, emailLen, true);
1306 match = tpCompareSubjectName(*leaf, SN_Email, true, email, emailLen,
1307 emailFoundInDN);
1308 }
1309 certGroup.alloc().free(email);
1310
1311 /*
1312 * Error here if no match found but there was indeed *some*
1313 * email address in the cert.
1314 */
1315 if(!match && (emailFoundInSAN || emailFoundInDN)) {
1316 if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND)) {
1317 tpPolicyError("SMIME email addrs in cert but no match");
1318 return CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND;
1319 }
1320 }
1321 }
1322
1323 /*
1324 * iChat only: error if app specified email address but there was
1325 * none in the cert.
1326 */
1327 if(iChat && !emailFoundInSAN && !emailFoundInDN && !iChatHandleFound) {
1328 if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS)) {
1329 tpPolicyError("iChat: no email address or handle in cert");
1330 return CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS;
1331 }
1332 }
1333 }
1334
1335 /*
1336 * Going by the letter of the law, here's what RFC 2632 has to say
1337 * about the legality of an empty Subject Name:
1338 *
1339 * ...the subject DN in a user's (i.e. end-entity) certificate MAY
1340 * be an empty SEQUENCE in which case the subjectAltName extension
1341 * will include the subject's identifier and MUST be marked as
1342 * critical.
1343 *
1344 * OK, first examine the leaf cert's subject name.
1345 */
1346 CSSM_RETURN crtn;
1347 CSSM_DATA_PTR subjNameData = NULL;
1348 const iSignExtenInfo &kuInfo = leafCertInfo.keyUsage;
1349 const iSignExtenInfo &ekuInfo = leafCertInfo.extendKeyUsage;
1350 const CSSM_X509_NAME *x509Name = NULL;
1351
1352 if(iChat) {
1353 /* empty subject name processing is S/MIME only */
1354 goto checkEku;
1355 }
1356
1357 crtn = leaf->fetchField(&CSSMOID_X509V1SubjectNameCStruct, &subjNameData);
1358 if(crtn) {
1359 /* This should really never happen */
1360 tpPolicyError("SMIME policy: error fetching subjectName");
1361 leaf->addStatusCode(CSSMERR_TP_INVALID_CERTIFICATE);
1362 return CSSMERR_TP_INVALID_CERTIFICATE;
1363 }
1364 /* must do a leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct on exit */
1365
1366 x509Name = (const CSSM_X509_NAME *)subjNameData->Data;
1367 if(x509Name->numberOfRDNs == 0) {
1368 /*
1369 * Empty subject name. If we haven't already seen a valid
1370 * email address in the subject alternate name (by looking
1371 * for a specific address specified by app), try to find
1372 * one now.
1373 */
1374 if(!emailFoundInSAN && // haven't found one, and
1375 (emailLen == 0)) { // didn't even look yet
1376 bool dummy;
1377 tpCompareSubjectAltName(leafCertInfo.subjectAltName,
1378 NULL, 0, // email, emailLen,
1379 SAN_Email, false, dummy,
1380 emailFoundInSAN); // the variable we're updating
1381 }
1382 if(!emailFoundInSAN) {
1383 tpPolicyError("SMIME policy fail: empty subject name and "
1384 "no Email Addrs in SubjectAltName");
1385 if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS)) {
1386 leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
1387 return CSSMERR_TP_VERIFY_ACTION_FAILED;
1388 }
1389 else {
1390 /* have to skip the next block */
1391 goto postSAN;
1392 }
1393 }
1394
1395 /*
1396 * One more thing: this leaf must indeed have a subjAltName
1397 * extension and it must be critical. We would not have gotten this
1398 * far if the subjAltName extension was not actually present....
1399 */
1400 assert(leafCertInfo.subjectAltName.present);
1401 if(!leafCertInfo.subjectAltName.critical) {
1402 tpPolicyError("SMIME policy fail: empty subject name and "
1403 "no Email Addrs in SubjectAltName");
1404 if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_SUBJ_ALT_NAME_NOT_CRIT)) {
1405 leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
1406 return CSSMERR_TP_VERIFY_ACTION_FAILED;
1407 }
1408 }
1409 }
1410 postSAN:
1411 leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
1412
1413 /*
1414 * Enforce the usage of the key associated with the leaf cert.
1415 * Cert's KeyUsage must be a superset of what the app is trying to do.
1416 * Note the {en,de}cipherOnly flags are handled separately....
1417 */
1418 if(kuInfo.present && (smimeOpts != NULL)) {
1419 CE_KeyUsage certKu = *((CE_KeyUsage *)kuInfo.extnData);
1420 CE_KeyUsage appKu = smimeOpts->IntendedUsage;
1421 CE_KeyUsage intersection = certKu & appKu;
1422 if((intersection & CE_CIPHER_MASK) != (appKu & CE_CIPHER_MASK)) {
1423 tpPolicyError("SMIME KeyUsage err: appKu 0x%x certKu 0x%x",
1424 appKu, certKu);
1425 if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE)) {
1426 return CSSMERR_TP_VERIFY_ACTION_FAILED;
1427 }
1428 }
1429
1430 /* Now the en/de cipher only bits - for keyAgreement only */
1431 if(appKu & CE_KU_KeyAgreement) {
1432 /*
1433 * 1. App wants to use this for key agreement; it must
1434 * say what it wants to do with the derived key.
1435 * In this context, the app's XXXonly bit means that
1436 * it wants to use the key for that op - not necessarliy
1437 * "only".
1438 */
1439 if((appKu & (CE_KU_EncipherOnly | CE_KU_DecipherOnly)) == 0) {
1440 tpPolicyError("SMIME KeyUsage err: KeyAgreement with "
1441 "no Encipher or Decipher");
1442 if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE)) {
1443 return CSSMERR_TP_VERIFY_ACTION_FAILED;
1444 }
1445 }
1446
1447 /*
1448 * 2. If cert restricts to encipher only make sure the
1449 * app isn't trying to decipher.
1450 */
1451 if((certKu & CE_KU_EncipherOnly) &&
1452 (appKu & CE_KU_DecipherOnly)) {
1453 tpPolicyError("SMIME KeyUsage err: cert EncipherOnly, "
1454 "app wants to decipher");
1455 if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE)) {
1456 return CSSMERR_TP_VERIFY_ACTION_FAILED;
1457 }
1458 }
1459
1460 /*
1461 * 3. If cert restricts to decipher only make sure the
1462 * app isn't trying to encipher.
1463 */
1464 if((certKu & CE_KU_DecipherOnly) &&
1465 (appKu & CE_KU_EncipherOnly)) {
1466 tpPolicyError("SMIME KeyUsage err: cert DecipherOnly, "
1467 "app wants to encipher");
1468 if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE)) {
1469 return CSSMERR_TP_VERIFY_ACTION_FAILED;
1470 }
1471 }
1472 }
1473 }
1474
1475 /*
1476 * Extended Key Use verification, which is different for the two policies.
1477 */
1478 checkEku:
1479 if(iChat && !ekuInfo.present) {
1480 /*
1481 * iChat: whether generic AIM cert or Apple .mac/iChat cert, we must have an
1482 * extended key use extension.
1483 */
1484 tpPolicyError("iChat: No extended Key Use");
1485 if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
1486 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
1487 }
1488 }
1489
1490 if(!iChatHandleFound) {
1491 /*
1492 * S/MIME and generic AIM certs when evaluating iChat policy.
1493 * Look for either emailProtection or anyExtendedKeyUsage usages.
1494 *
1495 * S/MIME : the whole extension is optional.
1496 * iChat : extension must be there (which we've already covered, above)
1497 * and we must find one of those extensions.
1498 */
1499 if(ekuInfo.present) {
1500 bool foundGoodEku = false;
1501 CE_ExtendedKeyUsage *eku = (CE_ExtendedKeyUsage *)ekuInfo.extnData;
1502 assert(eku != NULL);
1503 for(unsigned i=0; i<eku->numPurposes; i++) {
1504 if(tpCompareOids(&eku->purposes[i], &CSSMOID_EmailProtection)) {
1505 foundGoodEku = true;
1506 break;
1507 }
1508 if(tpCompareOids(&eku->purposes[i], &CSSMOID_ExtendedKeyUsageAny)) {
1509 foundGoodEku = true;
1510 break;
1511 }
1512 }
1513 if(!foundGoodEku) {
1514 tpPolicyError("iChat/SMIME: No appropriate extended Key Use");
1515 if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
1516 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
1517 }
1518 }
1519 }
1520 }
1521 else {
1522 /*
1523 * Apple iChat cert. Look for anyExtendedKeyUsage, iChatSigning,
1524 * ichatEncrypting - the latter of two which can optionally be
1525 * required by app.
1526 */
1527 assert(iChat); /* or we could not have even looked for an iChat style handle */
1528 assert(ekuInfo.present); /* checked above */
1529 bool foundAnyEku = false;
1530 bool foundIChatSign = false;
1531 bool foundISignEncrypt = false;
1532 CE_ExtendedKeyUsage *eku = (CE_ExtendedKeyUsage *)ekuInfo.extnData;
1533 assert(eku != NULL);
1534
1535 for(unsigned i=0; i<eku->numPurposes; i++) {
1536 if(tpCompareOids(&eku->purposes[i],
1537 &CSSMOID_APPLE_EKU_ICHAT_SIGNING)) {
1538 foundIChatSign = true;
1539 }
1540 else if(tpCompareOids(&eku->purposes[i],
1541 &CSSMOID_APPLE_EKU_ICHAT_ENCRYPTION)) {
1542 foundISignEncrypt = true;
1543 }
1544 else if(tpCompareOids(&eku->purposes[i], &CSSMOID_ExtendedKeyUsageAny)) {
1545 foundAnyEku = true;
1546 }
1547 }
1548
1549 if(!foundAnyEku && !foundISignEncrypt && !foundIChatSign) {
1550 /* No go - no acceptable uses found */
1551 tpPolicyError("iChat: No valid extended Key Uses found");
1552 if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
1553 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
1554 }
1555 }
1556
1557 /* check for specifically required uses */
1558 if((smimeOpts != NULL) && (smimeOpts->IntendedUsage != 0)) {
1559 if(smimeOpts->IntendedUsage & CE_KU_DigitalSignature) {
1560 if(!foundIChatSign) {
1561 tpPolicyError("iChat: ICHAT_SIGNING required, but missing");
1562 if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
1563 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
1564 }
1565 }
1566 }
1567 if(smimeOpts->IntendedUsage & CE_KU_DataEncipherment) {
1568 if(!foundISignEncrypt) {
1569 tpPolicyError("iChat: ICHAT_ENCRYPT required, but missing");
1570 if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
1571 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
1572 }
1573 }
1574 }
1575 } /* checking IntendedUsage */
1576 } /* iChat cert format */
1577
1578 return CSSM_OK;
1579 }
1580
1581 /*
1582 * Verify Apple SW Update signing (was Apple Code Signing, pre-Leopard) options.
1583 *
1584 * -- Must have one intermediate cert
1585 * -- intermediate must have basic constraints with path length 0
1586 * -- intermediate has CSSMOID_APPLE_EKU_CODE_SIGNING EKU
1587 * -- leaf cert has either CODE_SIGNING or CODE_SIGN_DEVELOPMENT EKU (the latter of
1588 * which triggers a CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT error)
1589 */
1590 static CSSM_RETURN tp_verifySWUpdateSigningOpts(
1591 TPCertGroup &certGroup,
1592 const CSSM_DATA *fieldOpts, // currently unused
1593 const iSignCertInfo *certInfo) // all certs, size certGroup.numCerts()
1594 {
1595 unsigned numCerts = certGroup.numCerts();
1596 const iSignCertInfo *isCertInfo;
1597 TPCertInfo *tpCert;
1598 // const CE_BasicConstraints *bc; // currently unused
1599 CE_ExtendedKeyUsage *eku;
1600 CSSM_RETURN crtn = CSSM_OK;
1601
1602 if(numCerts != 3) {
1603 if(!certGroup.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH)) {
1604 tpPolicyError("tp_verifySWUpdateSigningOpts: numCerts %u", numCerts);
1605 return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
1606 }
1607 else if(numCerts < 3) {
1608 /* this error allowed, but no intermediate...check leaf */
1609 goto checkLeaf;
1610 }
1611 }
1612
1613 /* verify intermediate cert */
1614 isCertInfo = &certInfo[1];
1615 tpCert = certGroup.certAtIndex(1);
1616
1617 if(!isCertInfo->basicConstraints.present) {
1618 tpPolicyError("tp_verifySWUpdateSigningOpts: no basicConstraints in intermediate");
1619 if(tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS)) {
1620 return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS;
1621 }
1622 }
1623
1624 /* ExtendedKeyUse required, one legal value */
1625 if(!isCertInfo->extendKeyUsage.present) {
1626 tpPolicyError("tp_verifySWUpdateSigningOpts: no extendedKeyUse in intermediate");
1627 if(tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE)) {
1628 return CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE;
1629 }
1630 else {
1631 goto checkLeaf;
1632 }
1633 }
1634
1635 eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
1636 assert(eku != NULL);
1637 if(eku->numPurposes != 1) {
1638 tpPolicyError("tp_verifySWUpdateSigningOpts: bad eku->numPurposes in intermediate (%lu)",
1639 (unsigned long)eku->numPurposes);
1640 if(tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
1641 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
1642 }
1643 else if(eku->numPurposes == 0) {
1644 /* ignore that error but no EKU - skip EKU check */
1645 goto checkLeaf;
1646 }
1647 /* else ignore error and we have an intermediate EKU; proceed */
1648 }
1649
1650 if(!tpCompareOids(&eku->purposes[0], &CSSMOID_APPLE_EKU_CODE_SIGNING)) {
1651 tpPolicyError("tp_verifySWUpdateSigningOpts: bad EKU");
1652 if(tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
1653 crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
1654 }
1655 }
1656
1657 checkLeaf:
1658
1659 /* verify leaf cert */
1660 isCertInfo = &certInfo[0];
1661 tpCert = certGroup.certAtIndex(0);
1662 if(!isCertInfo->extendKeyUsage.present) {
1663 tpPolicyError("tp_verifySWUpdateSigningOpts: no extendedKeyUse in leaf");
1664 if(tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE)) {
1665 return crtn ? crtn : CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE;
1666 }
1667 else {
1668 /* have to skip remainder */
1669 return CSSM_OK;
1670 }
1671 }
1672
1673 eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
1674 assert(eku != NULL);
1675 if(eku->numPurposes != 1) {
1676 tpPolicyError("tp_verifySWUpdateSigningOpts: bad eku->numPurposes (%lu)",
1677 (unsigned long)eku->numPurposes);
1678 if(tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
1679 if(crtn == CSSM_OK) {
1680 crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
1681 }
1682 }
1683 return crtn;
1684 }
1685 if(!tpCompareOids(&eku->purposes[0], &CSSMOID_APPLE_EKU_CODE_SIGNING)) {
1686 if(tpCompareOids(&eku->purposes[0], &CSSMOID_APPLE_EKU_CODE_SIGNING_DEV)) {
1687 tpPolicyError("tp_verifySWUpdateSigningOpts: DEVELOPMENT cert");
1688 if(tpCert->addStatusCode(CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT)) {
1689 if(crtn == CSSM_OK) {
1690 crtn = CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT;
1691 }
1692 }
1693 }
1694 else {
1695 tpPolicyError("tp_verifySWUpdateSigningOpts: bad EKU in leaf");
1696 if(tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
1697 if(crtn == CSSM_OK) {
1698 crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
1699 }
1700 }
1701 }
1702 }
1703
1704 return crtn;
1705 }
1706
1707 /*
1708 * Verify Apple Resource Signing options.
1709 *
1710 * -- leaf cert must have CSSMOID_APPLE_EKU_RESOURCE_SIGNING EKU
1711 * -- chain length must be >= 2
1712 * -- mainline code already verified that leaf KeyUsage = digitalSignature (only)
1713 */
1714 static CSSM_RETURN tp_verifyResourceSigningOpts(
1715 TPCertGroup &certGroup,
1716 const CSSM_DATA *fieldOpts, // currently unused
1717 const iSignCertInfo *certInfo) // all certs, size certGroup.numCerts()
1718 {
1719 unsigned numCerts = certGroup.numCerts();
1720 if(numCerts < 2) {
1721 if(!certGroup.isAllowedError(CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH)) {
1722 tpPolicyError("tp_verifyResourceSigningOpts: numCerts %u", numCerts);
1723 return CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH;
1724 }
1725 }
1726 const iSignCertInfo &leafCert = certInfo[0];
1727 TPCertInfo *leaf = certGroup.certAtIndex(0);
1728
1729 /* leaf ExtendedKeyUse required, one legal value */
1730 if(!tpVerifyEKU(leafCert, CSSMOID_APPLE_EKU_RESOURCE_SIGNING, false)) {
1731 tpPolicyError("tp_verifyResourceSigningOpts: no RESOURCE_SIGNING EKU");
1732 if(leaf->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
1733 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
1734 }
1735 }
1736
1737 return CSSM_OK;
1738 }
1739
1740 /*
1741 * Common code for Apple Code Signing and Apple Package Signing.
1742 * For now we just require an RFC3280-style CodeSigning EKU in the leaf
1743 * for both policies.
1744 */
1745 static CSSM_RETURN tp_verifyCodePkgSignOpts(
1746 TPPolicy policy,
1747 TPCertGroup &certGroup,
1748 const CSSM_DATA *fieldOpts, // currently unused
1749 const iSignCertInfo *certInfo) // all certs, size certGroup.numCerts()
1750 {
1751 const iSignCertInfo &leafCert = certInfo[0];
1752
1753 /* leaf ExtendedKeyUse required, one legal value */
1754 if(!tpVerifyEKU(leafCert, CSSMOID_ExtendedUseCodeSigning, false)) {
1755 TPCertInfo *leaf = certGroup.certAtIndex(0);
1756 tpPolicyError("tp_verifyCodePkgSignOpts: no CodeSigning EKU");
1757 if(leaf->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
1758 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
1759 }
1760 }
1761
1762 return CSSM_OK;
1763
1764 }
1765
1766 /*
1767 * Verify MacAppStore receipt verification policy options.
1768 *
1769 * -- Must have one intermediate cert
1770 * -- intermediate must be the FairPlay intermediate
1771 * -- leaf cert has the CSSMOID_APPLE_EXTENSION_MACAPPSTORE_RECEIPT marker extension
1772 */
1773 static CSSM_RETURN tp_verifyMacAppStoreReciptOpts(
1774 TPCertGroup &certGroup,
1775 const CSSM_DATA *fieldOpts, // currently unused
1776 const iSignCertInfo *certInfo) // all certs, size certGroup.numCerts()
1777 {
1778 unsigned numCerts = certGroup.numCerts();
1779 if (numCerts < 3)
1780 {
1781 if (!certGroup.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH))
1782 {
1783 tpPolicyError("tp_verifyMacAppStoreReciptOpts: numCerts %u", numCerts);
1784 return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
1785 }
1786 }
1787
1788 const iSignCertInfo *isCertInfo;
1789 TPCertInfo *tpCert;
1790
1791 /* verify intermediate cert */
1792 isCertInfo = &certInfo[1];
1793 tpCert = certGroup.certAtIndex(1);
1794
1795 if (!isCertInfo->basicConstraints.present)
1796 {
1797 tpPolicyError("tp_verifyAppleIDSharingOpts: no basicConstraints in intermediate");
1798 if (tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS))
1799 return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS;
1800 }
1801
1802 // Now check the leaf
1803 isCertInfo = &certInfo[0];
1804 tpCert = certGroup.certAtIndex(0);
1805 if (certInfo->certificatePolicies.present)
1806 {
1807 // syslog(LOG_ERR, "tp_verifyMacAppStoreReciptOpts: found certificatePolicies");
1808 const CE_CertPolicies *certPolicies =
1809 &isCertInfo->certificatePolicies.extnData->certPolicies;
1810 if (!certificatePoliciesContainsOID(certPolicies, &CSSMOID_MACAPPSTORE_RECEIPT_CERT_POLICY))
1811 if (tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION))
1812 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
1813 }
1814 else
1815 {
1816 // syslog(LOG_ERR, "tp_verifyMacAppStoreReciptOpts: no certificatePolicies present"); // DEBUG
1817 tpPolicyError("tp_verifyMacAppStoreReciptOpts: no certificatePolicies present in leaf");
1818 if (tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION))
1819 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
1820 }
1821
1822 return CSSM_OK;
1823 }
1824
1825 bool certificatePoliciesContainsOID(const CE_CertPolicies *certPolicies, const CSSM_OID *oidToFind)
1826 {
1827 // returns true if the given OID is present in the cert policies
1828
1829 if (!certPolicies || !oidToFind)
1830 return false;
1831
1832 const uint32 maxIndex = 100; // sanity check
1833 for (uint32 policyIndex = 0; policyIndex < certPolicies->numPolicies && policyIndex < maxIndex; policyIndex++)
1834 {
1835 CE_PolicyInformation *certPolicyInfo = &certPolicies->policies[policyIndex];
1836 CSSM_OID_PTR oid = &certPolicyInfo->certPolicyId;
1837 if (oid && tpCompareOids(oid, oidToFind)) // found it
1838 return true;
1839 }
1840
1841 return false;
1842 }
1843
1844
1845 /*
1846 * Verify Apple ID Sharing options.
1847 *
1848 * -- Do basic cert validation (OCSP-based certs)
1849 * -- Validate that the cert is an Apple ID sharing cert:
1850 * has a custom extension: OID: Apple ID Sharing Certificate ( 1 2 840 113635 100 4 7 )
1851 * (CSSMOID_APPLE_EXTENSION_APPLEID_SHARING)
1852 * EKU should have both client and server authentication
1853 * chains to the "Apple Application Integration Certification Authority" intermediate
1854 * -- optionally has a client-specified common name, which is the Apple ID account's UUID.
1855
1856 * -- Must have one intermediate cert ("Apple Application Integration Certification Authority")
1857 * -- intermediate must have basic constraints with path length 0
1858 * -- intermediate has CSSMOID_APPLE_EXTENSION_AAI_INTERMEDIATE extension (OID 1 2 840 113635 100 6 2 3)
1859 OR APPLE_EXTENSION_AAI_INTERMEDIATE_2
1860 */
1861
1862 static CSSM_RETURN tp_verifyAppleIDSharingOpts(TPCertGroup &certGroup,
1863 const CSSM_DATA *fieldOpts, // optional Common Name
1864 const iSignCertInfo *certInfo) // all certs, size certGroup.numCerts()
1865 {
1866 unsigned numCerts = certGroup.numCerts();
1867 const iSignCertInfo *isCertInfo;
1868 TPCertInfo *tpCert;
1869 // const CE_BasicConstraints *bc; // currently unused
1870 CE_ExtendedKeyUsage *eku;
1871 CSSM_RETURN crtn = CSSM_OK;
1872 unsigned int serverNameLen = 0;
1873 const char *serverName = NULL;
1874
1875 // The CSSM_APPLE_TP_SMIME_OPTIONS pointer is optional as is everything in it.
1876 if (fieldOpts && fieldOpts->Data)
1877 {
1878 CSSM_APPLE_TP_SSL_OPTIONS *sslOpts = (CSSM_APPLE_TP_SSL_OPTIONS *)fieldOpts->Data;
1879 switch (sslOpts->Version)
1880 {
1881 case CSSM_APPLE_TP_SSL_OPTS_VERSION:
1882 if (fieldOpts->Length != sizeof(CSSM_APPLE_TP_SSL_OPTIONS))
1883 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
1884 break;
1885 /* handle backwards compatibility here if necessary */
1886 default:
1887 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
1888 }
1889 serverNameLen = sslOpts->ServerNameLen;
1890 serverName = sslOpts->ServerName;
1891 }
1892
1893 //------------------------------------------------------------------------
1894
1895 if (numCerts != 3)
1896 {
1897 if (!certGroup.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH))
1898 {
1899 tpPolicyError("tp_verifyAppleIDSharingOpts: numCerts %u", numCerts);
1900 return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
1901 }
1902 else
1903 if (numCerts < 3)
1904 {
1905 /* this error allowed, but no intermediate...check leaf */
1906 goto checkLeaf;
1907 }
1908 }
1909
1910 /* verify intermediate cert */
1911 isCertInfo = &certInfo[1];
1912 tpCert = certGroup.certAtIndex(1);
1913
1914 if (!isCertInfo->basicConstraints.present)
1915 {
1916 tpPolicyError("tp_verifyAppleIDSharingOpts: no basicConstraints in intermediate");
1917 if (tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS))
1918 return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS;
1919 }
1920
1921 checkLeaf:
1922
1923 /* verify leaf cert */
1924 isCertInfo = &certInfo[0];
1925 tpCert = certGroup.certAtIndex(0);
1926
1927 /* host name check is optional */
1928 if (serverNameLen != 0)
1929 {
1930 if (serverName == NULL)
1931 return CSSMERR_TP_INVALID_POINTER;
1932
1933 /* convert caller's hostname string to lower case */
1934 char *hostName = (char *)certGroup.alloc().malloc(serverNameLen);
1935 memmove(hostName, serverName, serverNameLen);
1936 tpToLower(hostName, serverNameLen);
1937
1938 /* Check common name... */
1939
1940 bool fieldFound;
1941 CSSM_BOOL match = tpCompareSubjectName(*tpCert, SN_CommonName, false, hostName,
1942 serverNameLen, fieldFound);
1943
1944 certGroup.alloc().free(hostName);
1945 if (!match && tpCert->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH))
1946 return CSSMERR_APPLETP_HOSTNAME_MISMATCH;
1947 }
1948
1949 if (certInfo->certificatePolicies.present)
1950 {
1951 const CE_CertPolicies *certPolicies =
1952 &isCertInfo->certificatePolicies.extnData->certPolicies;
1953 if (!certificatePoliciesContainsOID(certPolicies, &CSSMOID_APPLEID_SHARING_CERT_POLICY))
1954 if (tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION))
1955 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
1956 }
1957 else
1958 if (tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION))
1959 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
1960
1961 if (!isCertInfo->extendKeyUsage.present)
1962 {
1963 tpPolicyError("tp_verifyAppleIDSharingOpts: no extendedKeyUse in leaf");
1964 if (tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE))
1965 return crtn ? crtn : CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE;
1966
1967 /* have to skip remainder */
1968 return CSSM_OK;
1969 }
1970
1971 // Check that certificate can do Client and Server Authentication (EKU)
1972 eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
1973 assert(eku != NULL);
1974 if(eku->numPurposes != 2)
1975 {
1976 tpPolicyError("tp_verifyAppleIDSharingOpts: bad eku->numPurposes (%lu)",
1977 (unsigned long)eku->numPurposes);
1978 if (tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE))
1979 {
1980 if (crtn == CSSM_OK)
1981 crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
1982 }
1983 return crtn;
1984 }
1985 bool canDoClientAuth = false, canDoServerAuth = false, ekuError = false;
1986 for (int ix=0;ix<2;ix++)
1987 {
1988 if (tpCompareOids(&eku->purposes[ix], &CSSMOID_ClientAuth))
1989 canDoClientAuth = true;
1990 else
1991 if (tpCompareOids(&eku->purposes[ix], &CSSMOID_ServerAuth))
1992 canDoServerAuth = true;
1993 else
1994 {
1995 ekuError = true;
1996 break;
1997 }
1998 }
1999
2000 if (!(canDoClientAuth && canDoServerAuth))
2001 ekuError = true;
2002 if (ekuError)
2003 {
2004 tpPolicyError("tp_verifyAppleIDSharingOpts: bad EKU in leaf");
2005 if (tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE))
2006 {
2007 if (crtn == CSSM_OK)
2008 crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
2009 }
2010 }
2011
2012 return crtn;
2013 }
2014
2015 /*
2016 * Verify Time Stamping (RFC3161) policy options.
2017 *
2018 * -- Leaf must contain Extended Key Usage (EKU), marked critical
2019 * -- The EKU must contain the id-kp-timeStamping purpose and no other
2020 */
2021 static CSSM_RETURN tp_verifyTimeStampingOpts(TPCertGroup &certGroup,
2022 const CSSM_DATA *fieldOpts, // currently unused
2023 const iSignCertInfo *certInfo) // all certs, size certGroup.numCerts()
2024 {
2025 unsigned numCerts = certGroup.numCerts();
2026 const iSignCertInfo *isCertInfo;
2027 TPCertInfo *tpCert;
2028 CE_ExtendedKeyUsage *eku;
2029
2030 isCertInfo = &certInfo[0];
2031 tpCert = certGroup.certAtIndex(0);
2032
2033 if (!isCertInfo->extendKeyUsage.present)
2034 {
2035 tpPolicyError("tp_verifyTimeStampingOpts: no extendedKeyUse in leaf");
2036 tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
2037 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
2038 }
2039
2040 if(!isCertInfo->extendKeyUsage.critical)
2041 {
2042 tpPolicyError("tp_verifyTimeStampingOpts: extended key usage !critical");
2043 tpCert->addStatusCode(CSSMERR_APPLETP_EXT_KEYUSAGE_NOT_CRITICAL);
2044 return CSSMERR_APPLETP_EXT_KEYUSAGE_NOT_CRITICAL;
2045 }
2046
2047 eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
2048 assert(eku != NULL);
2049
2050 if(eku->numPurposes != 1)
2051 {
2052 tpPolicyError("tp_verifyTimeStampingOpts: bad eku->numPurposes (%lu)",
2053 (unsigned long)eku->numPurposes);
2054 tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
2055 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
2056 }
2057
2058 if(!tpCompareOids(&eku->purposes[0], &CSSMOID_TimeStamping))
2059 {
2060 tpPolicyError("tp_verifyTimeStampingOpts: TimeStamping purpose not found");
2061 tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
2062 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
2063 }
2064
2065 return CSSM_OK;
2066 }
2067
2068 /*
2069 * RFC2459 says basicConstraints must be flagged critical for
2070 * CA certs, but Verisign doesn't work that way.
2071 */
2072 #define BASIC_CONSTRAINTS_MUST_BE_CRITICAL 0
2073
2074 /*
2075 * TP iSign spec says Extended Key Usage required for leaf certs,
2076 * but Verisign doesn't work that way.
2077 */
2078 #define EXTENDED_KEY_USAGE_REQUIRED_FOR_LEAF 0
2079
2080 /*
2081 * TP iSign spec says Subject Alternate Name required for leaf certs,
2082 * but Verisign doesn't work that way.
2083 */
2084 #define SUBJECT_ALT_NAME_REQUIRED_FOR_LEAF 0
2085
2086 /*
2087 * TP iSign spec originally required KeyUsage for all certs, but
2088 * Verisign doesn't have that in their roots.
2089 */
2090 #define KEY_USAGE_REQUIRED_FOR_ROOT 0
2091
2092 /*
2093 * RFC 2632, "S/MIME Version 3 Certificate Handling", section
2094 * 4.4.2, says that KeyUsage extensions MUST be flagged critical,
2095 * but Thawte's intermediate cert (common name "Thawte Personal
2096 * Freemail Issuing CA") does not meet this requirement.
2097 */
2098 #define SMIME_KEY_USAGE_MUST_BE_CRITICAL 0
2099
2100 /*
2101 * Public routine to perform TP verification on a constructed
2102 * cert group.
2103 * Returns CSSM_OK on success.
2104 * Assumes the chain has passed basic subject/issuer verification. First cert of
2105 * incoming certGroup is end-entity (leaf).
2106 *
2107 * Per-policy details:
2108 * iSign: Assumes that last cert in incoming certGroup is a root cert.
2109 * Also assumes a cert group of more than one cert.
2110 * kTPx509Basic: CertGroup of length one allowed.
2111 */
2112 CSSM_RETURN tp_policyVerify(
2113 TPPolicy policy,
2114 Allocator &alloc,
2115 CSSM_CL_HANDLE clHand,
2116 CSSM_CSP_HANDLE cspHand,
2117 TPCertGroup *certGroup,
2118 CSSM_BOOL verifiedToRoot, // last cert is good root
2119 CSSM_BOOL verifiedViaTrustSetting, // last cert verified via
2120 // user trust
2121 CSSM_APPLE_TP_ACTION_FLAGS actionFlags,
2122 const CSSM_DATA *policyFieldData, // optional
2123 void *policyOpts) // future options
2124 {
2125 iSignCertInfo *certInfo = NULL;
2126 uint32 numCerts;
2127 iSignCertInfo *thisCertInfo;
2128 uint16 expUsage;
2129 uint16 actUsage;
2130 unsigned certDex;
2131 CSSM_BOOL cA = CSSM_FALSE; // init for compiler warning
2132 bool isLeaf; // end entity
2133 bool isRoot; // root cert
2134 CE_ExtendedKeyUsage *extendUsage;
2135 CE_AuthorityKeyID *authorityId;
2136 CSSM_KEY_PTR pubKey;
2137 CSSM_RETURN outErr = CSSM_OK; // for gross, non-policy errors
2138 CSSM_BOOL policyFail = CSSM_FALSE;// generic CSSMERR_TP_VERIFY_ACTION_FAILED
2139 CSSM_RETURN policyError = CSSM_OK; // policy-specific failure
2140
2141 /* First, kTPDefault is a nop here */
2142 if(policy == kTPDefault) {
2143 return CSSM_OK;
2144 }
2145
2146 if(certGroup == NULL) {
2147 return CSSMERR_TP_INVALID_CERTGROUP;
2148 }
2149 numCerts = certGroup->numCerts();
2150 if(numCerts == 0) {
2151 return CSSMERR_TP_INVALID_CERTGROUP;
2152 }
2153 if(policy == kTPiSign) {
2154 if(!verifiedToRoot) {
2155 /* no way, this requires a root cert */
2156 return CSSMERR_TP_VERIFY_ACTION_FAILED;
2157 }
2158 if(numCerts <= 1) {
2159 /* nope, not for iSign */
2160 return CSSMERR_TP_VERIFY_ACTION_FAILED;
2161 }
2162 }
2163
2164 /* cook up an iSignCertInfo array */
2165 certInfo = (iSignCertInfo *)tpCalloc(alloc, numCerts, sizeof(iSignCertInfo));
2166 /* subsequent errors to errOut: */
2167
2168 /* fill it with interesting info from parsed certs */
2169 for(certDex=0; certDex<numCerts; certDex++) {
2170 if(iSignGetCertInfo(alloc,
2171 certGroup->certAtIndex(certDex),
2172 &certInfo[certDex])) {
2173 (certGroup->certAtIndex(certDex))->addStatusCode(
2174 CSSMERR_TP_INVALID_CERTIFICATE);
2175 /* this one is fatal (and can't ignore) */
2176 outErr = CSSMERR_TP_INVALID_CERTIFICATE;
2177 goto errOut;
2178 }
2179 }
2180
2181 /*
2182 * OK, the heart of TP enforcement.
2183 */
2184 for(certDex=0; certDex<numCerts; certDex++) {
2185 thisCertInfo = &certInfo[certDex];
2186 TPCertInfo *thisTpCertInfo = certGroup->certAtIndex(certDex);
2187
2188 /*
2189 * First check for presence of required extensions and
2190 * critical extensions we don't understand.
2191 */
2192 if(thisCertInfo->foundUnknownCritical) {
2193 /* illegal for all policies */
2194 tpPolicyError("tp_policyVerify: critical flag in unknown extension");
2195 if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN)) {
2196 policyFail = CSSM_TRUE;
2197 }
2198 }
2199
2200 /*
2201 * Check for unsupported key length, per <rdar://6892837>
2202 */
2203 if((pubKey=thisTpCertInfo->pubKey()) != NULL) {
2204 CSSM_KEYHEADER *keyHdr = &pubKey->KeyHeader;
2205 if(keyHdr->AlgorithmId == CSSM_ALGID_RSA && keyHdr->LogicalKeySizeInBits < 1024) {
2206 tpPolicyError("tp_policyVerify: RSA key size too small");
2207 if(thisTpCertInfo->addStatusCode(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE)) {
2208 policyFail = CSSM_TRUE;
2209 }
2210 }
2211 }
2212
2213 /*
2214 * Note it's possible for both of these to be true, for a chain
2215 * of length one (kTPx509Basic, kCrlPolicy only!)
2216 * FIXME: should this code work if the last cert in the chain is NOT a root?
2217 */
2218 isLeaf = thisTpCertInfo->isLeaf();
2219 isRoot = thisTpCertInfo->isSelfSigned(true);
2220
2221 /*
2222 * BasicConstraints.cA
2223 * iSign: required in all but leaf and root,
2224 * for which it is optional (with default values of false
2225 * for leaf and true for root).
2226 * all others: always optional, default of false for leaf and
2227 * true for others
2228 * All: cA must be false for leaf, true for others
2229 */
2230 if(!thisCertInfo->basicConstraints.present) {
2231 /*
2232 * No basicConstraints present; infer a cA value if appropriate.
2233 */
2234 if(isLeaf) {
2235 /* cool, use default; note that kTPx509Basic with
2236 * certGroup length of one may take this case */
2237 cA = CSSM_FALSE;
2238 }
2239 else if(isRoot) {
2240 /* cool, use default */
2241 cA = CSSM_TRUE;
2242 }
2243 else {
2244 switch(policy) {
2245 default:
2246 /*
2247 * not present, not leaf, not root....
2248 * ....RFC2459 says this can not be a CA
2249 */
2250 cA = CSSM_FALSE;
2251 break;
2252 case kTPiSign:
2253 /* required for iSign in this position */
2254 tpPolicyError("tp_policyVerify: no "
2255 "basicConstraints");
2256 if(thisTpCertInfo->addStatusCode(
2257 CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS)) {
2258 policyFail = CSSM_TRUE;
2259 }
2260 break;
2261 }
2262 }
2263 } /* inferred a default value */
2264 else {
2265 /* basicConstraints present */
2266 #if BASIC_CONSTRAINTS_MUST_BE_CRITICAL
2267 /* disabled for verisign compatibility */
2268 if(!thisCertInfo->basicConstraints.critical) {
2269 /* per RFC 2459 */
2270 tpPolicyError("tp_policyVerify: basicConstraints marked "
2271 "not critical");
2272 if(thisTpCertInfo->addStatusCode(CSSMERR_TP_VERIFY_ACTION_FAILED)) {
2273 policyFail = CSSM_TRUE;
2274 }
2275 }
2276 #endif /* BASIC_CONSTRAINTS_MUST_BE_CRITICAL */
2277
2278 const CE_BasicConstraints *bcp =
2279 &thisCertInfo->basicConstraints.extnData->basicConstraints;
2280
2281 cA = bcp->cA;
2282
2283 /* Verify pathLenConstraint if present */
2284 if(!isLeaf && // leaf, certDex=0, don't care
2285 cA && // p.l.c. only valid for CAs
2286 bcp->pathLenConstraintPresent) { // present?
2287 /*
2288 * pathLenConstraint=0 legal for certDex 1 only
2289 * pathLenConstraint=1 legal for certDex {1,2}
2290 * etc.
2291 */
2292 if(certDex > (bcp->pathLenConstraint + 1)) {
2293 tpPolicyError("tp_policyVerify: pathLenConstraint "
2294 "exceeded");
2295 if(thisTpCertInfo->addStatusCode(
2296 CSSMERR_APPLETP_PATH_LEN_CONSTRAINT)) {
2297 policyFail = CSSM_TRUE;
2298 }
2299 }
2300 }
2301 }
2302
2303 if(isLeaf) {
2304 /*
2305 * Special cases to allow a chain of length 1, leaf and root
2306 * both true, and for caller to override the "leaf can't be a CA"
2307 * requirement when a CA cert is explicitly being evaluated as the
2308 * leaf.
2309 */
2310 if(cA && !isRoot &&
2311 !(actionFlags & CSSM_TP_ACTION_LEAF_IS_CA)) {
2312 tpPolicyError("tp_policyVerify: cA true for leaf");
2313 if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_CA)) {
2314 policyFail = CSSM_TRUE;
2315 }
2316 }
2317 } else if(!cA) {
2318 tpPolicyError("tp_policyVerify: cA false for non-leaf");
2319 if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_CA)) {
2320 policyFail = CSSM_TRUE;
2321 }
2322 }
2323
2324 /*
2325 * Authority Key Identifier optional
2326 * iSign : only allowed in !root.
2327 * If present, must not be critical.
2328 * all others : ignored (though used later for chain verification)
2329 */
2330 if((policy == kTPiSign) && thisCertInfo->authorityId.present) {
2331 if(isRoot) {
2332 tpPolicyError("tp_policyVerify: authorityId in root");
2333 if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID)) {
2334 policyFail = CSSM_TRUE;
2335 }
2336 }
2337 if(thisCertInfo->authorityId.critical) {
2338 /* illegal per RFC 2459 */
2339 tpPolicyError("tp_policyVerify: authorityId marked "
2340 "critical");
2341 if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID)) {
2342 policyFail = CSSM_TRUE;
2343 }
2344 }
2345 }
2346
2347 /*
2348 * Subject Key Identifier optional
2349 * iSign : can't be critical.
2350 * all others : ignored (though used later for chain verification)
2351 */
2352 if(thisCertInfo->subjectId.present) {
2353 if((policy == kTPiSign) && thisCertInfo->subjectId.critical) {
2354 tpPolicyError("tp_policyVerify: subjectId marked critical");
2355 if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_SUBJECT_ID)) {
2356 policyFail = CSSM_TRUE;
2357 }
2358 }
2359 }
2360
2361 /*
2362 * Key Usage optional except required as noted
2363 * iSign : required for non-root/non-leaf
2364 * Leaf cert : if present, usage = digitalSignature
2365 * Exception : if leaf, and keyUsage not present,
2366 * netscape-cert-type must be present, with
2367 * Object Signing bit set
2368 * kCrlPolicy : Leaf: usage = CRLSign
2369 * kTP_SMIME : if present, must be critical
2370 * kTP_SWUpdateSign, kTP_ResourceSign, kTP_CodeSigning, kTP_PackageSigning : Leaf :
2371 usage = digitalSignature
2372 * all others : non-leaf : usage = keyCertSign
2373 * Leaf : don't care
2374 */
2375 if(thisCertInfo->keyUsage.present) {
2376 /*
2377 * Leaf cert:
2378 * iSign and *Signing: usage = digitalSignature
2379 * all others : don't care
2380 * Others: usage = keyCertSign
2381 * We only require that one bit to be set, we ignore others.
2382 */
2383 if(isLeaf) {
2384 switch(policy) {
2385 case kTPiSign:
2386 case kTP_SWUpdateSign:
2387 case kTP_ResourceSign:
2388 case kTP_CodeSigning:
2389 case kTP_PackageSigning:
2390 expUsage = CE_KU_DigitalSignature;
2391 break;
2392 case kCrlPolicy:
2393 /* if present, this bit must be set */
2394 expUsage = CE_KU_CRLSign;
2395 break;
2396 default:
2397 /* accept whatever's there */
2398 expUsage = thisCertInfo->keyUsage.extnData->keyUsage;
2399 break;
2400 }
2401 }
2402 else {
2403 /* !leaf: this is true for all policies */
2404 expUsage = CE_KU_KeyCertSign;
2405 }
2406 actUsage = thisCertInfo->keyUsage.extnData->keyUsage;
2407 if(!(actUsage & expUsage)) {
2408 tpPolicyError("tp_policyVerify: bad keyUsage (leaf %s; "
2409 "usage 0x%x)",
2410 (certDex == 0) ? "TRUE" : "FALSE", actUsage);
2411 if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE)) {
2412 policyFail = CSSM_TRUE;
2413 }
2414 }
2415
2416 #if 0
2417 /*
2418 * Radar 3523221 renders this whole check obsolete, but I'm leaving
2419 * the code here to document its conspicuous functional absence.
2420 */
2421 if((policy == kTP_SMIME) && !thisCertInfo->keyUsage.critical) {
2422 /*
2423 * Per Radar 3410245, allow this for intermediate certs.
2424 */
2425 if(SMIME_KEY_USAGE_MUST_BE_CRITICAL || isLeaf || isRoot) {
2426 tpPolicyError("tp_policyVerify: key usage, !critical, SMIME");
2427 if(thisTpCertInfo->addStatusCode(
2428 CSSMERR_APPLETP_SMIME_KEYUSAGE_NOT_CRITICAL)) {
2429 policyFail = CSSM_TRUE;
2430 }
2431 }
2432 }
2433 #endif
2434 }
2435 else if(policy == kTPiSign) {
2436 /*
2437 * iSign requires keyUsage present for non root OR
2438 * netscape-cert-type/ObjectSigning for leaf
2439 */
2440 if(isLeaf && thisCertInfo->netscapeCertType.present) {
2441 CE_NetscapeCertType ct =
2442 thisCertInfo->netscapeCertType.extnData->netscapeCertType;
2443
2444 if(!(ct & CE_NCT_ObjSign)) {
2445 tpPolicyError("tp_policyVerify: netscape-cert-type, "
2446 "!ObjectSign");
2447 if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE)) {
2448 policyFail = CSSM_TRUE;
2449 }
2450 }
2451 }
2452 else if(!isRoot) {
2453 tpPolicyError("tp_policyVerify: !isRoot, no keyUsage, "
2454 "!(leaf and netscapeCertType)");
2455 if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE)) {
2456 policyFail = CSSM_TRUE;
2457 }
2458 }
2459 }
2460
2461 /*
2462 * RFC 3280, 4.1.2.6, says that an empty subject name can only appear in a
2463 * leaf cert, and only if subjectAltName is present and marked critical.
2464 */
2465 if(isLeaf && thisTpCertInfo->hasEmptySubjectName()) {
2466 bool badEmptySubject = false;
2467 if(actionFlags & CSSM_TP_ACTION_LEAF_IS_CA) {
2468 /*
2469 * True when evaluating a CA cert as well as when
2470 * evaluating a CRL's cert chain. Note the odd case of a CRL's
2471 * signer having an empty subject matching an empty issuer
2472 * in the CRL. That'll be caught here.
2473 */
2474 badEmptySubject = true;
2475 }
2476 else if(!thisCertInfo->subjectAltName.present || /* no subjectAltName */
2477 !thisCertInfo->subjectAltName.critical) { /* not critical */
2478 badEmptySubject = true;
2479 }
2480 if(badEmptySubject) {
2481 tpPolicyError("tp_policyVerify: bad empty subject");
2482 if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_EMPTY_SUBJECT)) {
2483 policyFail = CSSM_TRUE;
2484 }
2485 }
2486 }
2487
2488 /*
2489 * RFC 3739: if this cert has a Qualified Cert Statements extension, and
2490 * it's Critical, make sure we understand all of the extension's statementIds.
2491 */
2492 if(thisCertInfo->qualCertStatements.present &&
2493 thisCertInfo->qualCertStatements.critical) {
2494 CE_QC_Statements *qcss =
2495 &thisCertInfo->qualCertStatements.extnData->qualifiedCertStatements;
2496 uint32 numQcs = qcss->numQCStatements;
2497 for(unsigned qdex=0; qdex<numQcs; qdex++) {
2498 CSSM_OID_PTR qid = &qcss->qcStatements[qdex].statementId;
2499 bool ok = false;
2500 for(unsigned kdex=0; kdex<NUM_KNOWN_QUAL_CERT_STATEMENTS; kdex++) {
2501 if(tpCompareCssmData(qid, knownQualifiedCertStatements[kdex])) {
2502 ok = true;
2503 break;
2504 }
2505 }
2506 if(!ok) {
2507 if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_UNKNOWN_QUAL_CERT_STATEMENT)) {
2508 policyFail = CSSM_TRUE;
2509 break;
2510 }
2511 }
2512 }
2513 } /* critical Qualified Cert Statement */
2514
2515 /*
2516 * Certificate Policies extension validation, per section 1.2 of:
2517 * http://iase.disa.mil/pki/dod_cp_v10_final_2_mar_09_signed.pdf
2518 */
2519 if (tpVerifyCPE(*thisCertInfo, CSSMOID_PIV_AUTH, false) ||
2520 tpVerifyCPE(*thisCertInfo, CSSMOID_PIV_AUTH_2048, false)) {
2521 /*
2522 * Certificate asserts one of the PIV-Auth Certificate Policy OIDs;
2523 * check the required Key Usage extension for compliance.
2524 *
2525 * Leaf cert:
2526 * usage = digitalSignature (only; no other bits asserted)
2527 * Others:
2528 * usage = keyCertSign (required; other bits ignored)
2529 */
2530 if(thisCertInfo->keyUsage.present) {
2531 actUsage = thisCertInfo->keyUsage.extnData->keyUsage;
2532 } else {
2533 /* No key usage! Policy fail. */
2534 actUsage = 0;
2535 }
2536 if(!(actionFlags & CSSM_TP_ACTION_LEAF_IS_CA) && (certDex == 0)) {
2537 expUsage = CE_KU_DigitalSignature;
2538 } else {
2539 expUsage = actUsage | CE_KU_KeyCertSign;
2540 }
2541 if(!(actUsage == expUsage)) {
2542 tpPolicyError("tp_policyVerify: bad keyUsage for PIV-Auth policy (leaf %s; "
2543 "usage 0x%x)",
2544 (certDex == 0) ? "TRUE" : "FALSE", actUsage);
2545 if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE)) {
2546 policyFail = CSSM_TRUE;
2547 }
2548 }
2549 } /* Certificate Policies */
2550
2551
2552 } /* for certDex, checking presence of extensions */
2553
2554 /*
2555 * Special case checking for leaf (end entity) cert
2556 *
2557 * iSign only: Extended key usage, optional for leaf,
2558 * value CSSMOID_ExtendedUseCodeSigning
2559 */
2560 if((policy == kTPiSign) && certInfo[0].extendKeyUsage.present) {
2561 extendUsage = &certInfo[0].extendKeyUsage.extnData->extendedKeyUsage;
2562 if(extendUsage->numPurposes != 1) {
2563 tpPolicyError("tp_policyVerify: bad extendUsage->numPurposes "
2564 "(%d)",
2565 (int)extendUsage->numPurposes);
2566 if((certGroup->certAtIndex(0))->addStatusCode(
2567 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
2568 policyFail = CSSM_TRUE;
2569 }
2570 }
2571 if(!tpCompareOids(extendUsage->purposes,
2572 &CSSMOID_ExtendedUseCodeSigning)) {
2573 tpPolicyError("tp_policyVerify: bad extendKeyUsage");
2574 if((certGroup->certAtIndex(0))->addStatusCode(
2575 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
2576 policyFail = CSSM_TRUE;
2577 }
2578 }
2579 }
2580
2581 /*
2582 * Verify authorityId-->subjectId linkage.
2583 * All optional - skip if needed fields not present.
2584 * Also, always skip last (root) cert.
2585 */
2586 for(certDex=0; certDex<(numCerts-1); certDex++) {
2587 if(!certInfo[certDex].authorityId.present ||
2588 !certInfo[certDex+1].subjectId.present) {
2589 continue;
2590 }
2591 authorityId = &certInfo[certDex].authorityId.extnData->authorityKeyID;
2592 if(!authorityId->keyIdentifierPresent) {
2593 /* we only know how to compare keyIdentifier */
2594 continue;
2595 }
2596 if(!tpCompareCssmData(&authorityId->keyIdentifier,
2597 &certInfo[certDex+1].subjectId.extnData->subjectKeyID)) {
2598 tpPolicyError("tp_policyVerify: bad key ID linkage");
2599 if((certGroup->certAtIndex(certDex))->addStatusCode(
2600 CSSMERR_APPLETP_INVALID_ID_LINKAGE)) {
2601 policyFail = CSSM_TRUE;
2602 }
2603 }
2604 }
2605
2606 /*
2607 * Check signature algorithm on all non-root certs,
2608 * reject if known to be untrusted
2609 */
2610 for(certDex=0; certDex<(numCerts-1); certDex++) {
2611 if(certInfo[certDex].untrustedSigAlg) {
2612 tpPolicyError("tp_policyVerify: untrusted signature algorithm");
2613 if((certGroup->certAtIndex(certDex))->addStatusCode(
2614 CSSMERR_TP_INVALID_CERTIFICATE)) {
2615 policyFail = CSSM_TRUE;
2616 }
2617 }
2618 }
2619
2620 /* specific per-policy checking */
2621 switch(policy) {
2622 case kTP_SSL:
2623 case kTP_EAP:
2624 case kTP_IPSec:
2625 /*
2626 * SSL, EAP, IPSec: optionally verify common name; all are identical
2627 * other than their names.
2628 * FIXME - should this be before or after the root cert test? How can
2629 * we return both errors?
2630 */
2631 policyError = tp_verifySslOpts(policy, *certGroup, policyFieldData, certInfo[0]);
2632 break;
2633
2634 case kTP_iChat:
2635 tpDebug("iChat policy");
2636 /* fall thru */
2637 case kTP_SMIME:
2638 policyError = tp_verifySmimeOpts(policy, *certGroup, policyFieldData,
2639 certInfo[0]);
2640 break;
2641 case kTP_SWUpdateSign:
2642 policyError = tp_verifySWUpdateSigningOpts(*certGroup, policyFieldData, certInfo);
2643 break;
2644 case kTP_ResourceSign:
2645 policyError = tp_verifyResourceSigningOpts(*certGroup, policyFieldData, certInfo);
2646 break;
2647 case kTP_CodeSigning:
2648 case kTP_PackageSigning:
2649 policyError = tp_verifyCodePkgSignOpts(policy, *certGroup, policyFieldData, certInfo);
2650 break;
2651 case kTP_MacAppStoreRec:
2652 policyError = tp_verifyMacAppStoreReciptOpts(*certGroup, policyFieldData, certInfo);
2653 break;
2654 case kTP_AppleIDSharing:
2655 policyError = tp_verifyAppleIDSharingOpts(*certGroup, policyFieldData, certInfo);
2656 break;
2657 case kTP_TimeStamping:
2658 policyError = tp_verifyTimeStampingOpts(*certGroup, policyFieldData, certInfo);
2659 break;
2660 case kTPx509Basic:
2661 case kTPiSign:
2662 case kCrlPolicy:
2663 case kTP_PKINIT_Client:
2664 default:
2665 break;
2666
2667 }
2668
2669 if(outErr == CSSM_OK) {
2670 /* policy-specific error takes precedence here */
2671 if(policyError != CSSM_OK) {
2672 outErr = policyError;
2673 }
2674 else if(policyFail) {
2675 /* plain vanilla error return from this module */
2676 outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
2677 }
2678 }
2679 errOut:
2680 /* free resources */
2681 for(certDex=0; certDex<numCerts; certDex++) {
2682 thisCertInfo = &certInfo[certDex];
2683 iSignFreeCertInfo(clHand, thisCertInfo);
2684 }
2685 tpFree(alloc, certInfo);
2686 return outErr;
2687 }
2688
2689 /*
2690 * Obtain policy-specific User Trust parameters
2691 */
2692 void tp_policyTrustSettingParams(
2693 TPPolicy policy,
2694 const CSSM_DATA *policyData, // optional
2695 /* returned values - not mallocd */
2696 const char **policyStr,
2697 uint32 *policyStrLen,
2698 SecTrustSettingsKeyUsage *keyUse)
2699 {
2700 /* default values */
2701 *policyStr = NULL;
2702 *keyUse = kSecTrustSettingsKeyUseAny;
2703
2704 if((policyData == NULL) || (policyData->Data == NULL)) {
2705 /* currently, no further action possible */
2706 return;
2707 }
2708 switch(policy) {
2709 case kTP_SSL:
2710 case kTP_EAP:
2711 case kTP_IPSec:
2712 {
2713 if(policyData->Length != sizeof(CSSM_APPLE_TP_SSL_OPTIONS)) {
2714 /* this error will be caught later */
2715 return;
2716 }
2717 CSSM_APPLE_TP_SSL_OPTIONS *sslOpts =
2718 (CSSM_APPLE_TP_SSL_OPTIONS *)policyData->Data;
2719 *policyStr = sslOpts->ServerName;
2720 *policyStrLen = sslOpts->ServerNameLen;
2721 if(sslOpts->Flags & CSSM_APPLE_TP_SSL_CLIENT) {
2722 /*
2723 * Client signs with its priv key. Server end,
2724 * which (also) verifies the client cert, verifies.
2725 */
2726 *keyUse = kSecTrustSettingsKeyUseSignature;
2727 }
2728 else {
2729 /* server decrypts */
2730 *keyUse = kSecTrustSettingsKeyUseEnDecryptKey;
2731 }
2732 return;
2733 }
2734
2735 case kTP_iChat:
2736 case kTP_SMIME:
2737 {
2738 if(policyData->Length != sizeof(CSSM_APPLE_TP_SMIME_OPTIONS)) {
2739 /* this error will be caught later */
2740 return;
2741 }
2742 CSSM_APPLE_TP_SMIME_OPTIONS *smimeOpts =
2743 (CSSM_APPLE_TP_SMIME_OPTIONS *)policyData->Data;
2744 *policyStr = smimeOpts->SenderEmail;
2745 *policyStrLen = smimeOpts->SenderEmailLen;
2746 SecTrustSettingsKeyUsage ku = 0;
2747 CE_KeyUsage smimeKu = smimeOpts->IntendedUsage;
2748 if(smimeKu & (CE_KU_DigitalSignature | CE_KU_KeyCertSign | CE_KU_CRLSign)) {
2749 ku |= kSecTrustSettingsKeyUseSignature;
2750 }
2751 if(smimeKu & (CE_KU_KeyEncipherment | CE_KU_DataEncipherment)) {
2752 ku |= kSecTrustSettingsKeyUseEnDecryptKey;
2753 }
2754 *keyUse = ku;
2755 return;
2756 }
2757
2758 default:
2759 /* no other options */
2760 return;
2761 }
2762 }