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