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