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