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