1/* 2 * Copyright (c) 2000-2001,2011-2014 Apple Inc. All Rights Reserved. 3 * 4 * The contents of this file constitute Original Code as defined in and are 5 * subject to the Apple Public Source License Version 1.2 (the 'License'). 6 * You may not use this file except in compliance with the License. Please obtain 7 * a copy of the License at http://www.apple.com/publicsource and read it before 8 * using this file. 9 * 10 * This Original Code and all software distributed under the License are 11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS 12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT 13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the 15 * specific language governing rights and limitations under the License. 16 */ 17 18 19/* 20 * tpCertGroup.cpp - Cert group functions (construct, verify) 21 */ 22 23#include "AppleTPSession.h" 24#include "certGroupUtils.h" 25#include "TPCertInfo.h" 26#include "TPCrlInfo.h" 27#include "tpPolicies.h" 28#include "tpdebugging.h" 29#include "tpCrlVerify.h" 30#include <Security/oidsalg.h> 31#include <Security/cssmapple.h> 32 33/* 34 * This is a temporary hack to allow verification of PKINIT server certs 35 * which are self-signed and not in the system anchors list. If the self- 36 * signed cert is in a magic keychain (whose location is not published), 37 * we'll allow it as if it were indeed a full-fledged anchor cert. 38 */ 39#define TP_PKINIT_SERVER_HACK 1 40#if TP_PKINIT_SERVER_HACK 41 42#include <Security/SecKeychain.h> 43#include <Security/SecKeychainSearch.h> 44#include <Security/SecCertificate.h> 45#include <Security/oidscert.h> 46#include <sys/types.h> 47#include <pwd.h> 48 49#define CFRELEASE(cf) if(cf) { CFRelease(cf); } 50 51/* 52 * Returns true if we are to allow/trust the specified 53 * cert as a PKINIT-only anchor. 54 */ 55static bool tpCheckPkinitServerCert( 56 TPCertGroup &certGroup) 57{ 58 /* 59 * Basic requirement: exactly one cert, self-signed. 60 * The numCerts == 1 requirement might change... 61 */ 62 unsigned numCerts = certGroup.numCerts(); 63 if(numCerts != 1) { 64 tpDebug("tpCheckPkinitServerCert: too many certs"); 65 return false; 66 } 67 /* end of chain... */ 68 TPCertInfo *theCert = certGroup.certAtIndex(numCerts - 1); 69 if(!theCert->isSelfSigned()) { 70 tpDebug("tpCheckPkinitServerCert: 1 cert, not self-signed"); 71 return false; 72 } 73 const CSSM_DATA *subjectName = theCert->subjectName(); 74 75 /* 76 * Open the magic keychain. 77 * We're going up and over the Sec layer here, not generally 78 * kosher, but this is a hack. 79 */ 80 OSStatus ortn; 81 SecKeychainRef kcRef = NULL; 82 string fullPathName; 83 const char *homeDir = getenv("HOME"); 84 if (homeDir == NULL) 85 { 86 // If $HOME is unset get the current user's home directory 87 // from the passwd file. 88 uid_t uid = geteuid(); 89 if (!uid) uid = getuid(); 90 struct passwd *pw = getpwuid(uid); 91 if (!pw) { 92 return false; 93 } 94 homeDir = pw->pw_dir; 95 } 96 fullPathName = homeDir; 97 fullPathName += "/Library/Application Support/PKINIT/TrustedServers.keychain"; 98 ortn = SecKeychainOpen(fullPathName.c_str(), &kcRef); 99 if(ortn) { 100 tpDebug("tpCheckPkinitServerCert: keychain not found (1)"); 101 return false; 102 } 103 /* subsequent errors to errOut: */ 104 105 bool ourRtn = false; 106 SecKeychainStatus kcStatus; 107 CSSM_DATA_PTR subjSerial = NULL; 108 CSSM_RETURN crtn; 109 SecKeychainSearchRef srchRef = NULL; 110 SecKeychainAttributeList attrList; 111 SecKeychainAttribute attrs[2]; 112 SecKeychainItemRef foundItem = NULL; 113 114 ortn = SecKeychainGetStatus(kcRef, &kcStatus); 115 if(ortn) { 116 tpDebug("tpCheckPkinitServerCert: keychain not found (2)"); 117 goto errOut; 118 } 119 120 /* 121 * We already have this cert's normalized name; get its 122 * serial number. 123 */ 124 crtn = theCert->fetchField(&CSSMOID_X509V1SerialNumber, &subjSerial); 125 if(crtn) { 126 /* should never happen */ 127 tpDebug("tpCheckPkinitServerCert: error fetching serial number"); 128 goto errOut; 129 } 130 131 attrs[0].tag = kSecSubjectItemAttr; 132 attrs[0].length = (UInt32)subjectName->Length; 133 attrs[0].data = subjectName->Data; 134 attrs[1].tag = kSecSerialNumberItemAttr; 135 attrs[1].length = (UInt32)subjSerial->Length; 136 attrs[1].data = subjSerial->Data; 137 attrList.count = 2; 138 attrList.attr = attrs; 139 140 ortn = SecKeychainSearchCreateFromAttributes(kcRef, 141 kSecCertificateItemClass, 142 &attrList, 143 &srchRef); 144 if(ortn) { 145 tpDebug("tpCheckPkinitServerCert: search failure"); 146 goto errOut; 147 } 148 for(;;) { 149 ortn = SecKeychainSearchCopyNext(srchRef, &foundItem); 150 if(ortn) { 151 tpDebug("tpCheckPkinitServerCert: end search"); 152 break; 153 } 154 155 /* found a matching cert; do byte-for-byte compare */ 156 CSSM_DATA certData; 157 ortn = SecCertificateGetData((SecCertificateRef)foundItem, &certData); 158 if(ortn) { 159 tpDebug("tpCheckPkinitServerCert: SecCertificateGetData failure"); 160 continue; 161 } 162 if(tpCompareCssmData(&certData, theCert->itemData())){ 163 tpDebug("tpCheckPkinitServerCert: FOUND CERT"); 164 ourRtn = true; 165 break; 166 } 167 tpDebug("tpCheckPkinitServerCert: skipping matching cert"); 168 CFRelease(foundItem); 169 foundItem = NULL; 170 } 171errOut: 172 CFRELEASE(kcRef); 173 CFRELEASE(srchRef); 174 CFRELEASE(foundItem); 175 if(subjSerial != NULL) { 176 theCert->freeField(&CSSMOID_X509V1SerialNumber, subjSerial); 177 } 178 return ourRtn; 179} 180#endif /* TP_PKINIT_SERVER_HACK */ 181 182 183/*----------------------------------------------------------------------------- 184 * CertGroupConstruct 185 * 186 * Description: 187 * This function returns a pointer to a mallocd CSSM_CERTGROUP which 188 * refers to a mallocd list of raw ordered X.509 certs which verify back as 189 * far as the TP is able to go. The first cert of the returned list is the 190 * subject cert. The TP will attempt to search thru the DBs passed in 191 * DBList in order to complete the chain. The chain is completed when a 192 * self-signed (root) cert is found in the chain. The root cert may be 193 * present in the input CertGroupFrag, or it may have been obtained from 194 * one of the DBs passed in DBList. It is not an error if no root cert is 195 * found. 196 * 197 * The error conditions are: 198 * -- The first cert of CertGroupFrag is an invalid cert. NULL is returned, 199 * err = CSSM_TP_INVALID_CERTIFICATE. 200 * -- The root cert (if found) fails to verify. Valid certgroup is returned, 201 * err = CSSMERR_TP_VERIFICATION_FAILURE. 202 * -- Any cert in the (possibly partially) constructed chain has expired or 203 * isn't valid yet, err = CSSMERR_TP_CERT_EXPIRED or 204 * CSSMERR_TP_CERT_NOT_VALID_YET. A CertGroup is returned. 205 * -- CSSMERR_TP_CERT_EXPIRED and CSSMERR_TP_CERT_NOT_VALID_YET. If one of these 206 * conditions obtains for the first (leaf) cert, the function throws this 207 * error immediately and the outgoing cert group is empty. For subsequent certs, 208 * the temporal validity of a cert is only tested AFTER a cert successfully 209 * meets the cert chaining criteria (subject/issuer match and signature 210 * verify). A cert in a chain with this error is not added to the outgoing 211 * cert group. 212 * -- the usual errors like bad handle or memory failure. 213 * 214 * Parameters: 215 * Two handles - to an open CL and CSP. The CSP must be capable of 216 * dealing with the signature algorithms used by the certs. The CL must be 217 * an X.509-savvy CL. 218 * 219 * CertGroupFrag, an unordered array of raw X.509 certs in the form of a 220 * CSSM_CERTGROUP_PTR. The first cert of this list is the subject cert 221 * which is eventually to be verified. The other certs can be in any order 222 * and may not even have any relevance to the cert chain being constructed. 223 * They may also be invalid certs. 224 * 225 * DBList, a list of DB/DL handles which may contain certs necessary to 226 * complete the desired cert chain. (Not currently implemented.) 227 * 228 *---------------------------------------------------------------------------*/ 229 230/* public version */ 231void AppleTPSession::CertGroupConstruct(CSSM_CL_HANDLE clHand, 232 CSSM_CSP_HANDLE cspHand, 233 const CSSM_DL_DB_LIST &DBList, 234 const void *ConstructParams, 235 const CSSM_CERTGROUP &CertGroupFrag, 236 CSSM_CERTGROUP_PTR &CertGroup) 237{ 238 TPCertGroup outCertGroup(*this, TGO_Caller); 239 TPCertGroup inCertGroup(CertGroupFrag, 240 clHand, 241 cspHand, 242 *this, 243 NULL, // cssmTimeStr 244 true, // firstCertMustBeValid 245 TGO_Group); 246 247 /* set up for disposal of TPCertInfos created by CertGroupConstructPriv */ 248 TPCertGroup gatheredCerts(*this, TGO_Group); 249 250 CSSM_RETURN constructReturn = CSSM_OK; 251 CSSM_APPLE_TP_ACTION_FLAGS actionFlags = 0; 252 CSSM_BOOL verifiedToRoot; // not used 253 CSSM_BOOL verifiedToAnchor; // not used 254 CSSM_BOOL verifiedViaTrustSetting; // not used 255 256 try { 257 CertGroupConstructPriv(clHand, 258 cspHand, 259 inCertGroup, 260 &DBList, 261 NULL, // cssmTimeStr 262 /* no anchors */ 263 0, NULL, 264 actionFlags, 265 /* no user trust */ 266 NULL, NULL, 0, 0, 267 gatheredCerts, 268 verifiedToRoot, 269 verifiedToAnchor, 270 verifiedViaTrustSetting, 271 outCertGroup); 272 } 273 catch(const CssmError &cerr) { 274 constructReturn = cerr.error; 275 /* abort if no certs found */ 276 if(outCertGroup.numCerts() == 0) { 277 CssmError::throwMe(constructReturn); 278 } 279 } 280 CertGroup = outCertGroup.buildCssmCertGroup(); 281 /* caller of this function never gets evidence... */ 282 outCertGroup.freeDbRecords(); 283 284 if(constructReturn) { 285 CssmError::throwMe(constructReturn); 286 } 287} 288 289 290/* 291 * Private version of CertGroupConstruct, used by CertGroupConstruct and 292 * CertGroupVerify. Populates a TP-style TPCertGroup for further processing. 293 * This only throws CSSM-style exceptions in the following cases: 294 * 295 * -- input parameter errors 296 * -- the first (leaf) cert is bad (doesn't parse, expired, not valid yet). 297 * -- root found but it doesn't self-verify 298 * 299 * All other cert-related errors simply result in the bad cert being ignored. 300 * Other exceptions are gross system errors like malloc failure. 301 */ 302void AppleTPSession::CertGroupConstructPriv(CSSM_CL_HANDLE clHand, 303 CSSM_CSP_HANDLE cspHand, 304 TPCertGroup &inCertGroup, 305 const CSSM_DL_DB_LIST *DBList, // optional here 306 const char *cssmTimeStr, // optional 307 308 /* trusted anchors, optional */ 309 /* FIXME - maybe this should be a TPCertGroup */ 310 uint32 numAnchorCerts, 311 const CSSM_DATA *anchorCerts, 312 313 /* CSSM_TP_ACTION_FETCH_CERT_FROM_NET, CSSM_TP_ACTION_TRUST_SETTINGS */ 314 CSSM_APPLE_TP_ACTION_FLAGS actionFlags, 315 316 /* optional user trust parameters */ 317 const CSSM_OID *policyOid, 318 const char *policyStr, 319 uint32 policyStrLen, 320 SecTrustSettingsKeyUsage keyUse, 321 322 /* 323 * Certs to be freed by caller (i.e., TPCertInfo which we allocate 324 * as a result of using a cert from anchorCerts or dbList) are added 325 * to this group. 326 */ 327 TPCertGroup &certsToBeFreed, 328 329 /* returned */ 330 CSSM_BOOL &verifiedToRoot, // end of chain self-verifies 331 CSSM_BOOL &verifiedToAnchor, // end of chain in anchors 332 CSSM_BOOL &verifiedViaTrustSetting, // chain ends per User Trust setting 333 TPCertGroup &outCertGroup) // RETURNED 334{ 335 TPCertInfo *subjectCert; // the one we're working on 336 CSSM_RETURN outErr = CSSM_OK; 337 338 /* this'll be the first subject cert in the main loop */ 339 subjectCert = inCertGroup.certAtIndex(0); 340 341 /* Append leaf cert to outCertGroup */ 342 outCertGroup.appendCert(subjectCert); 343 subjectCert->isLeaf(true); 344 subjectCert->isFromInputCerts(true); 345 outCertGroup.setAllUnused(); 346 subjectCert->used(true); 347 348 outErr = outCertGroup.buildCertGroup( 349 *subjectCert, 350 &inCertGroup, 351 DBList, 352 clHand, 353 cspHand, 354 cssmTimeStr, 355 numAnchorCerts, 356 anchorCerts, 357 certsToBeFreed, 358 &certsToBeFreed, // gatheredCerts to accumulate net/DB fetches 359 CSSM_TRUE, // subjectIsInGroup - enables root check on 360 // subject cert 361 actionFlags, 362 policyOid, 363 policyStr, 364 policyStrLen, 365 keyUse, 366 367 verifiedToRoot, 368 verifiedToAnchor, 369 verifiedViaTrustSetting); 370 if(outErr) { 371 CssmError::throwMe(outErr); 372 } 373} 374 375/* 376 * Map a policy OID to one of the standard (non-revocation) policies. 377 * Returns true if it's a standard policy. 378 */ 379static bool checkPolicyOid( 380 const CSSM_OID &oid, 381 TPPolicy &tpPolicy) /* RETURNED */ 382{ 383 if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_SSL)) { 384 tpPolicy = kTP_SSL; 385 return true; 386 } 387 else if(tpCompareOids(&oid, &CSSMOID_APPLE_X509_BASIC)) { 388 tpPolicy = kTPx509Basic; 389 return true; 390 } 391 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_SMIME)) { 392 tpPolicy = kTP_SMIME; 393 return true; 394 } 395 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_EAP)) { 396 tpPolicy = kTP_EAP; 397 return true; 398 } 399 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_SW_UPDATE_SIGNING)) { 400 /* note: this was CSSMOID_APPLE_TP_CODE_SIGN until 8/15/06 */ 401 tpPolicy = kTP_SWUpdateSign; 402 return true; 403 } 404 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_RESOURCE_SIGN)) { 405 tpPolicy = kTP_ResourceSign; 406 return true; 407 } 408 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_IP_SEC)) { 409 tpPolicy = kTP_IPSec; 410 return true; 411 } 412 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_ICHAT)) { 413 tpPolicy = kTP_iChat; 414 return true; 415 } 416 else if(tpCompareOids(&oid, &CSSMOID_APPLE_ISIGN)) { 417 tpPolicy = kTPiSign; 418 return true; 419 } 420 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_PKINIT_CLIENT)) { 421 tpPolicy = kTP_PKINIT_Client; 422 return true; 423 } 424 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_PKINIT_SERVER)) { 425 tpPolicy = kTP_PKINIT_Server; 426 return true; 427 } 428 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_CODE_SIGNING)) { 429 tpPolicy = kTP_CodeSigning; 430 return true; 431 } 432 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_PACKAGE_SIGNING)) { 433 tpPolicy = kTP_PackageSigning; 434 return true; 435 } 436 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_MACAPPSTORE_RECEIPT)) { 437 tpPolicy = kTP_MacAppStoreRec; 438 return true; 439 } 440 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_APPLEID_SHARING)) { 441 tpPolicy = kTP_AppleIDSharing; 442 return true; 443 } 444 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_TIMESTAMPING)) { 445 tpPolicy = kTP_TimeStamping; 446 return true; 447 } 448 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_PASSBOOK_SIGNING)) { 449 tpPolicy = kTP_PassbookSigning; 450 return true; 451 } 452 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_MOBILE_STORE)) { 453 tpPolicy = kTP_MobileStore; 454 return true; 455 } 456 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_TEST_MOBILE_STORE)) { 457 tpPolicy = kTP_TestMobileStore; 458 return true; 459 } 460 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_ESCROW_SERVICE)) { 461 tpPolicy = kTP_EscrowService; 462 return true; 463 } 464 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_PROFILE_SIGNING)) { 465 tpPolicy = kTP_ProfileSigning; 466 return true; 467 } 468 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_QA_PROFILE_SIGNING)) { 469 tpPolicy = kTP_QAProfileSigning; 470 return true; 471 } 472 else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_PCS_ESCROW_SERVICE)) { 473 tpPolicy = kTP_PCSEscrowService; 474 return true; 475 } 476 return false; 477} 478 479/*----------------------------------------------------------------------------- 480 * CertGroupVerify 481 * 482 * Description: 483 * -- Construct a cert chain using TP_CertGroupConstruct. 484 * -- Attempt to verify that cert chain against one of the known 485 * good certs passed in AnchorCerts. 486 * -- Optionally enforces additional policies (TBD) when verifying the cert chain. 487 * -- Optionally returns the entire cert chain constructed in 488 * TP_CertGroupConstruct and here, all the way to an anchor cert or as 489 * far as we were able to go, in *Evidence. 490 * 491 * Parameters: 492 * Two handles - to an open CL and CSP. The CSP must be capable of 493 * dealing with the signature algorithms used by the certs. The CL must be 494 * an X.509-savvy CL. 495 * 496 * RawCerts, an unordered array of raw certs in the form of a 497 * CSSM_CERTGROUP_PTR. The first cert of this list is the subject cert 498 * which is eventually to be verified. The other certs can be in any order 499 * and may not even have any relevance to the cert chain being constructed. 500 * They may also be invalid certs. 501 * 502 * DBList, a list of DB/DL handles which may contain certs necessary to 503 * complete the desired cert chain. (Currently not implemented.) 504 * 505 * AnchorCerts, a list of known trusted certs. 506 * NumberOfAnchorCerts, size of AnchorCerts array. 507 * 508 * PolicyIdentifiers, Optional policy OID. NULL indicates default 509 * X.509 trust policy. 510 * 511 * Supported Policies: 512 * CSSMOID_APPLE_ISIGN 513 * CSSMOID_APPLE_X509_BASIC 514 * 515 * For both of these, the associated FieldValue must be {0, NULL}, 516 * 517 * NumberOfPolicyIdentifiers, size of PolicyIdentifiers array, must be 518 * zero or one. 519 * 520 * All other arguments must be zero/NULL. 521 * 522 * Returns: 523 * CSSM_OK : cert chain verified all the way back to an AnchorCert. 524 * CSSMERR_TP_INVALID_ANCHOR_CERT : In this case, the cert chain 525 * was validated back to a self-signed (root) cert found in either 526 * CertToBeVerified or in one of the DBs in DBList, but that root cert 527 * was *NOT* found in the AnchorCert list. 528 * CSSMERR_TP_NOT_TRUSTED: no root cert was found and no AnchorCert 529 * verified the end of the constructed cert chain. 530 * CSSMERR_TP_VERIFICATION_FAILURE: a root cert was found which does 531 * not self-verify. 532 * CSSMERR_TP_VERIFY_ACTION_FAILED: indicates a failure of the requested 533 * policy action. 534 * CSSMERR_TP_INVALID_CERTIFICATE: indicates a bad leaf cert. 535 * CSSMERR_TP_INVALID_REQUEST_INPUTS : no incoming VerifyContext. 536 * CSSMERR_TP_CERT_EXPIRED and CSSMERR_TP_CERT_NOT_VALID_YET: see comments 537 * for CertGroupConstruct. 538 * CSSMERR_TP_CERTIFICATE_CANT_OPERATE : issuer cert was found with a partial 539 * public key, rendering full verification impossible. 540 * CSSMERR_TP_INVALID_CERT_AUTHORITY : issuer cert was found with a partial 541 * public key and which failed to perform subsequent signature 542 * verification. 543 *---------------------------------------------------------------------------*/ 544 545void AppleTPSession::CertGroupVerify(CSSM_CL_HANDLE clHand, 546 CSSM_CSP_HANDLE cspHand, 547 const CSSM_CERTGROUP &CertGroupToBeVerified, 548 const CSSM_TP_VERIFY_CONTEXT *VerifyContext, 549 CSSM_TP_VERIFY_CONTEXT_RESULT_PTR VerifyContextResult) 550{ 551 CSSM_BOOL verifiedToRoot = CSSM_FALSE; 552 CSSM_BOOL verifiedToAnchor = CSSM_FALSE; 553 CSSM_BOOL verifiedViaTrustSetting = CSSM_FALSE; 554 CSSM_RETURN constructReturn = CSSM_OK; 555 CSSM_RETURN policyReturn = CSSM_OK; 556 const CSSM_TP_CALLERAUTH_CONTEXT *cred; 557 /* declare volatile as compiler workaround to avoid caching in CR4 */ 558 const CSSM_APPLE_TP_ACTION_DATA * volatile actionData = NULL; 559 CSSM_TIMESTRING cssmTimeStr; 560 CSSM_APPLE_TP_ACTION_FLAGS actionFlags = 0; 561 CSSM_TP_STOP_ON tpStopOn = 0; 562 563 /* keep track of whether we did policy checking; if not, we do defaults */ 564 bool didCertPolicy = false; 565 bool didRevokePolicy = false; 566 567 /* user trust parameters */ 568 CSSM_OID utNullPolicy = {0, NULL}; 569 const CSSM_OID *utPolicyOid = NULL; 570 const char *utPolicyStr = NULL; 571 uint32 utPolicyStrLen = 0; 572 SecTrustSettingsKeyUsage utKeyUse = 0; 573 bool utTrustSettingEnabled = false; 574 575 if(VerifyContextResult) { 576 memset(VerifyContextResult, 0, sizeof(*VerifyContextResult)); 577 } 578 579 /* verify input args, skipping the ones checked by CertGroupConstruct */ 580 if((VerifyContext == NULL) || (VerifyContext->Cred == NULL)) { 581 /* the spec says that this is optional but we require it */ 582 CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS); 583 } 584 cred = VerifyContext->Cred; 585 586 /* Optional ActionData affecting all policies */ 587 actionData = (CSSM_APPLE_TP_ACTION_DATA * volatile)VerifyContext->ActionData.Data; 588 if(actionData != NULL) { 589 switch(actionData->Version) { 590 case CSSM_APPLE_TP_ACTION_VERSION: 591 if(VerifyContext->ActionData.Length != 592 sizeof(CSSM_APPLE_TP_ACTION_DATA)) { 593 CssmError::throwMe(CSSMERR_TP_INVALID_ACTION_DATA); 594 } 595 break; 596 /* handle backwards versions here if we ever go beyond version 0 */ 597 default: 598 CssmError::throwMe(CSSMERR_TP_INVALID_ACTION_DATA); 599 } 600 actionFlags = actionData->ActionFlags; 601 if(actionFlags & CSSM_TP_ACTION_TRUST_SETTINGS) { 602 utTrustSettingEnabled = true; 603 } 604 } 605 606 /* optional, may be NULL */ 607 cssmTimeStr = cred->VerifyTime; 608 609 tpStopOn = cred->VerificationAbortOn; 610 switch(tpStopOn) { 611 /* the only two we support */ 612 case CSSM_TP_STOP_ON_NONE: 613 case CSSM_TP_STOP_ON_FIRST_FAIL: 614 break; 615 /* default maps to stop on first fail */ 616 case CSSM_TP_STOP_ON_POLICY: 617 tpStopOn = CSSM_TP_STOP_ON_FIRST_FAIL; 618 break; 619 default: 620 CssmError::throwMe(CSSMERR_TP_INVALID_STOP_ON_POLICY); 621 } 622 623 /* now the args we can't deal with */ 624 if(cred->CallerCredentials != NULL) { 625 CssmError::throwMe(CSSMERR_TP_INVALID_CALLERAUTH_CONTEXT_POINTER); 626 } 627 /* ...any others? */ 628 629 /* set up for optional user trust evaluation */ 630 if(utTrustSettingEnabled) { 631 const CSSM_TP_POLICYINFO *pinfo = &cred->Policy; 632 TPPolicy utPolicy = kTPx509Basic; 633 634 /* default policy OID in case caller hasn't specified one */ 635 utPolicyOid = &utNullPolicy; 636 if(pinfo->NumberOfPolicyIds == 0) { 637 tpTrustSettingsDbg("CertGroupVerify: User trust enabled but no policies (1)"); 638 /* keep going, I guess - no policy-specific info - use kTPx509Basic */ 639 } 640 else { 641 CSSM_FIELD_PTR utPolicyField = &pinfo->PolicyIds[0]; 642 utPolicyOid = &utPolicyField->FieldOid; 643 bool foundPolicy = checkPolicyOid(*utPolicyOid, utPolicy); 644 if(!foundPolicy) { 645 tpTrustSettingsDbg("CertGroupVerify: User trust enabled but no policies"); 646 /* keep going, I guess - no policy-specific info - use kTPx509Basic */ 647 } 648 else { 649 /* get policy-specific info */ 650 tp_policyTrustSettingParams(utPolicy, &utPolicyField->FieldValue, 651 &utPolicyStr, &utPolicyStrLen, &utKeyUse); 652 } 653 } 654 } 655 656 /* get verified (possibly partial) outCertGroup - error is fatal */ 657 /* BUT: we still return partial evidence if asked to...from now on. */ 658 TPCertGroup outCertGroup(*this, 659 TGO_Caller); // certs are owned by inCertGroup 660 TPCertGroup inCertGroup(CertGroupToBeVerified, clHand, cspHand, *this, 661 cssmTimeStr, // optional 'this' time 662 true, // firstCertMustBeValid 663 TGO_Group); 664 665 /* set up for disposal of TPCertInfos created by CertGroupConstructPriv */ 666 TPCertGroup gatheredCerts(*this, TGO_Group); 667 668 try { 669 CertGroupConstructPriv( 670 clHand, 671 cspHand, 672 inCertGroup, 673 cred->DBList, 674 cssmTimeStr, 675 cred->NumberOfAnchorCerts, 676 cred->AnchorCerts, 677 actionFlags, 678 utPolicyOid, 679 utPolicyStr, 680 utPolicyStrLen, 681 utKeyUse, 682 gatheredCerts, 683 verifiedToRoot, 684 verifiedToAnchor, 685 verifiedViaTrustSetting, 686 outCertGroup); 687 } 688 catch(const CssmError &cerr) { 689 constructReturn = cerr.error; 690 /* abort if no certs found */ 691 if(outCertGroup.numCerts() == 0) { 692 CssmError::throwMe(constructReturn); 693 } 694 /* else press on, collecting as much info as we can */ 695 } 696 /* others are way fatal */ 697 assert(outCertGroup.numCerts() >= 1); 698 699 /* Infer interim status from return values */ 700 switch(constructReturn) { 701 /* these values do not get overridden */ 702 case CSSMERR_TP_CERTIFICATE_CANT_OPERATE: 703 case CSSMERR_TP_INVALID_CERT_AUTHORITY: 704 case CSSMERR_APPLETP_TRUST_SETTING_DENY: 705 case errSecInvalidTrustSettings: 706 break; 707 default: 708 /* infer status from these values... */ 709 if(verifiedToAnchor || verifiedViaTrustSetting) { 710 /* full success; anchor doesn't have to be root */ 711 constructReturn = CSSM_OK; 712 } 713 else if(verifiedToRoot) { 714 if(actionFlags & CSSM_TP_ACTION_IMPLICIT_ANCHORS) { 715 constructReturn = CSSM_OK; 716 } 717 else { 718 /* verified to root which is not an anchor */ 719 constructReturn = CSSMERR_TP_INVALID_ANCHOR_CERT; 720 } 721 } 722 else { 723 /* partial chain, no root, not verifiable by anchor */ 724 constructReturn = CSSMERR_TP_NOT_TRUSTED; 725 } 726 727 /* 728 * Those errors can be allowed, cert-chain-wide, per individual 729 * certs' allowedErrors 730 */ 731 if((constructReturn != CSSM_OK) && 732 outCertGroup.isAllowedError(constructReturn)) { 733 constructReturn = CSSM_OK; 734 } 735 break; 736 } 737 738 /* 739 * Parameters passed to tp_policyVerify() and which vary per policy 740 * in the loop below 741 */ 742 TPPolicy tpPolicy; 743 const CSSM_APPLE_TP_SSL_OPTIONS *sslOpts; 744 CSSM_RETURN thisPolicyRtn = CSSM_OK; // returned from tp_policyVerify() 745 746 /* common CRL verify parameters */ 747 TPCrlGroup *crlGroup = NULL; 748 try { 749 crlGroup = new TPCrlGroup(&VerifyContext->Crls, 750 clHand, cspHand, 751 *this, // alloc 752 NULL, // cssmTimeStr - we want CRLs that are valid 'now' 753 TGO_Group); 754 } 755 catch(const CssmError &cerr) { 756 CSSM_RETURN cr = cerr.error; 757 /* I don't see a straightforward way to report this error, 758 * other than adding it to the leaf cert's status... */ 759 outCertGroup.certAtIndex(0)->addStatusCode(cr); 760 tpDebug("CertGroupVerify: error constructing CrlGroup; continuing\n"); 761 } 762 /* others are way fatal */ 763 764 TPVerifyContext revokeVfyContext(*this, 765 clHand, 766 cspHand, 767 cssmTimeStr, 768 cred->NumberOfAnchorCerts, 769 cred->AnchorCerts, 770 &inCertGroup, 771 crlGroup, 772 /* 773 * This may consist of certs gathered from the net (which is the purpose 774 * of this argument) and from DLDBs (a side-effect optimization). 775 */ 776 gatheredCerts, 777 cred->DBList, 778 kRevokeNone, // policy 779 actionFlags, 780 NULL, // CRL options 781 NULL, // OCSP options 782 utPolicyOid, 783 utPolicyStr, 784 utPolicyStrLen, 785 utKeyUse); 786 787 /* true if we're to execute tp_policyVerify at end of loop */ 788 bool doPolicyVerify; 789 /* true if we're to execute a revocation policy at end of loop */ 790 bool doRevocationPolicy; 791 792 /* grind thru each policy */ 793 for(uint32 polDex=0; polDex<cred->Policy.NumberOfPolicyIds; polDex++) { 794 if(cred->Policy.PolicyIds == NULL) { 795 policyReturn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS; 796 break; 797 } 798 CSSM_FIELD_PTR policyId = &cred->Policy.PolicyIds[polDex]; 799 const CSSM_DATA *fieldVal = &policyId->FieldValue; 800 const CSSM_OID *oid = &policyId->FieldOid; 801 thisPolicyRtn = CSSM_OK; 802 doPolicyVerify = false; 803 doRevocationPolicy = false; 804 sslOpts = NULL; 805 806 /* first the basic cert policies */ 807 doPolicyVerify = checkPolicyOid(*oid, tpPolicy); 808 if(doPolicyVerify) { 809 /* some basic checks... */ 810 bool policyAbort = false; 811 switch(tpPolicy) { 812 case kTPx509Basic: 813 case kTPiSign: 814 case kTP_PKINIT_Client: 815 case kTP_PKINIT_Server: 816 if(fieldVal->Data != NULL) { 817 policyReturn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS; 818 policyAbort = true; 819 break; 820 } 821 break; 822 default: 823 break; 824 } 825 if(policyAbort) { 826 break; 827 } 828 #if TP_PKINIT_SERVER_HACK 829 if(tpPolicy == kTP_PKINIT_Server) { 830 /* possible override of "root not in anchors" */ 831 if(constructReturn == CSSMERR_TP_INVALID_ANCHOR_CERT) { 832 if(tpCheckPkinitServerCert(outCertGroup)) { 833 constructReturn = CSSM_OK; 834 } 835 } 836 } 837 #endif /* TP_PKINIT_SERVER_HACK */ 838 } 839 840 /* 841 * Now revocation policies. Note some fields in revokeVfyContext can 842 * accumulate across multiple policy calls, e.g., signerCerts. 843 */ 844 else if(tpCompareOids(oid, &CSSMOID_APPLE_TP_REVOCATION_CRL)) { 845 /* CRL-specific options */ 846 const CSSM_APPLE_TP_CRL_OPTIONS *crlOpts; 847 crlOpts = (CSSM_APPLE_TP_CRL_OPTIONS *)fieldVal->Data; 848 thisPolicyRtn = CSSM_OK; 849 if(crlOpts != NULL) { 850 switch(crlOpts->Version) { 851 case CSSM_APPLE_TP_CRL_OPTS_VERSION: 852 if(fieldVal->Length != 853 sizeof(CSSM_APPLE_TP_CRL_OPTIONS)) { 854 thisPolicyRtn = 855 CSSMERR_TP_INVALID_POLICY_IDENTIFIERS; 856 break; 857 } 858 break; 859 /* handle backwards compatibility here if necessary */ 860 default: 861 thisPolicyRtn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS; 862 break; 863 } 864 if(thisPolicyRtn != CSSM_OK) { 865 policyReturn = thisPolicyRtn; 866 break; 867 } 868 } 869 revokeVfyContext.policy = kRevokeCrlBasic; 870 revokeVfyContext.crlOpts = crlOpts; 871 doRevocationPolicy = true; 872 } 873 else if(tpCompareOids(oid, &CSSMOID_APPLE_TP_REVOCATION_OCSP)) { 874 /* OCSP-specific options */ 875 const CSSM_APPLE_TP_OCSP_OPTIONS *ocspOpts; 876 ocspOpts = (CSSM_APPLE_TP_OCSP_OPTIONS *)fieldVal->Data; 877 thisPolicyRtn = CSSM_OK; 878 if(ocspOpts != NULL) { 879 switch(ocspOpts->Version) { 880 case CSSM_APPLE_TP_OCSP_OPTS_VERSION: 881 if(fieldVal->Length != 882 sizeof(CSSM_APPLE_TP_OCSP_OPTIONS)) { 883 thisPolicyRtn = 884 CSSMERR_TP_INVALID_POLICY_IDENTIFIERS; 885 break; 886 } 887 break; 888 /* handle backwards compatibility here if necessary */ 889 default: 890 thisPolicyRtn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS; 891 break; 892 } 893 if(thisPolicyRtn != CSSM_OK) { 894 policyReturn = thisPolicyRtn; 895 break; 896 } 897 } 898 revokeVfyContext.policy = kRevokeOcsp; 899 revokeVfyContext.ocspOpts = ocspOpts; 900 doRevocationPolicy = true; 901 } 902 /* etc. - add more policies here */ 903 else { 904 /* unknown TP policy OID */ 905 policyReturn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS; 906 break; 907 } 908 909 /* common cert policy call */ 910 if(doPolicyVerify) { 911 assert(!doRevocationPolicy); // one at a time 912 thisPolicyRtn = tp_policyVerify(tpPolicy, 913 *this, 914 clHand, 915 cspHand, 916 &outCertGroup, 917 verifiedToRoot, 918 verifiedViaTrustSetting, 919 actionFlags, 920 fieldVal, 921 cred->Policy.PolicyControl); // not currently used 922 didCertPolicy = true; 923 } 924 /* common revocation policy call */ 925 if(doRevocationPolicy) { 926 assert(!doPolicyVerify); // one at a time 927 thisPolicyRtn = tpRevocationPolicyVerify(revokeVfyContext, outCertGroup); 928 didRevokePolicy = true; 929 } 930 /* See if possible error is allowed, cert-chain-wide. */ 931 if((thisPolicyRtn != CSSM_OK) && 932 outCertGroup.isAllowedError(thisPolicyRtn)) { 933 thisPolicyRtn = CSSM_OK; 934 } 935 if(thisPolicyRtn) { 936 /* Now remember the error if it's the first policy 937 * error we've seen. */ 938 if(policyReturn == CSSM_OK) { 939 policyReturn = thisPolicyRtn; 940 } 941 /* Keep going? */ 942 if(tpStopOn == CSSM_TP_STOP_ON_FIRST_FAIL) { 943 /* Nope; we're done with policy evaluation */ 944 break; 945 } 946 } 947 } /* for each policy */ 948 949 /* 950 * Upon completion of the above loop, perform default policy ops if 951 * appropriate. 952 */ 953 if((policyReturn == CSSM_OK) || (tpStopOn == CSSM_TP_STOP_ON_NONE)) { 954 if(!didCertPolicy) { 955 policyReturn = tp_policyVerify(kTPDefault, 956 *this, 957 clHand, 958 cspHand, 959 &outCertGroup, 960 verifiedToRoot, 961 verifiedViaTrustSetting, 962 actionFlags, 963 NULL, // policyFieldData 964 cred->Policy.PolicyControl); // not currently used 965 /* See if error is allowed, cert-chain-wide. */ 966 if((policyReturn != CSSM_OK) && 967 outCertGroup.isAllowedError(policyReturn)) { 968 policyReturn = CSSM_OK; 969 } 970 } 971 if( !didRevokePolicy && // no revoke policy yet 972 ( (policyReturn == CSSM_OK || // default cert policy OK 973 (tpStopOn == CSSM_TP_STOP_ON_NONE)) // keep going anyway 974 ) 975 ) { 976 revokeVfyContext.policy = TP_CRL_POLICY_DEFAULT; 977 CSSM_RETURN thisPolicyRtn = tpRevocationPolicyVerify(revokeVfyContext, 978 outCertGroup); 979 if((thisPolicyRtn != CSSM_OK) && 980 outCertGroup.isAllowedError(thisPolicyRtn)) { 981 thisPolicyRtn = CSSM_OK; 982 } 983 if((thisPolicyRtn != CSSM_OK) && (policyReturn == CSSM_OK)) { 984 policyReturn = thisPolicyRtn; 985 } 986 987 } 988 } /* default policy opts */ 989 990 delete crlGroup; 991 992 /* return evidence - i.e., constructed chain - if asked to */ 993 if(VerifyContextResult != NULL) { 994 /* 995 * VerifyContextResult->Evidence[0] : CSSM_TP_APPLE_EVIDENCE_HEADER 996 * VerifyContextResult->Evidence[1] : CSSM_CERTGROUP 997 * VerifyContextResult->Evidence[2] : CSSM_TP_APPLE_EVIDENCE_INFO 998 */ 999 VerifyContextResult->NumberOfEvidences = 3; 1000 VerifyContextResult->Evidence = 1001 (CSSM_EVIDENCE_PTR)calloc(3, sizeof(CSSM_EVIDENCE)); 1002 1003 CSSM_TP_APPLE_EVIDENCE_HEADER *hdr = 1004 (CSSM_TP_APPLE_EVIDENCE_HEADER *)malloc( 1005 sizeof(CSSM_TP_APPLE_EVIDENCE_HEADER)); 1006 hdr->Version = CSSM_TP_APPLE_EVIDENCE_VERSION; 1007 CSSM_EVIDENCE_PTR ev = &VerifyContextResult->Evidence[0]; 1008 ev->EvidenceForm = CSSM_EVIDENCE_FORM_APPLE_HEADER; 1009 ev->Evidence = hdr; 1010 1011 ev = &VerifyContextResult->Evidence[1]; 1012 ev->EvidenceForm = CSSM_EVIDENCE_FORM_APPLE_CERTGROUP; 1013 ev->Evidence = outCertGroup.buildCssmCertGroup(); 1014 1015 ev = &VerifyContextResult->Evidence[2]; 1016 ev->EvidenceForm = CSSM_EVIDENCE_FORM_APPLE_CERT_INFO; 1017 ev->Evidence = outCertGroup.buildCssmEvidenceInfo(); 1018 } 1019 else { 1020 /* caller responsible for freeing these if they are for evidence.... */ 1021 outCertGroup.freeDbRecords(); 1022 } 1023 CSSM_RETURN outErr = outCertGroup.getReturnCode(constructReturn, policyReturn, 1024 actionFlags); 1025 1026 if(outErr) { 1027 CssmError::throwMe(outErr); 1028 } 1029} 1030 1031 1032