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