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