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