CFArrayRef SecTrustCopyProperties(SecTrustRef trust) { /* OS X creates a completely different structure with one dictionary for each certificate */ CFIndex ix, count = SecTrustGetCertificateCount(trust); CFMutableArrayRef properties = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks); for (ix = 0; ix < count; ix++) { CFMutableDictionaryRef certDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); /* Populate the certificate title */ SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, ix); if (cert) { CFStringRef subjectSummary = SecCertificateCopySubjectSummary(cert); if (subjectSummary) { CFDictionaryAddValue(certDict, kSecPropertyTypeTitle, subjectSummary); CFRelease(subjectSummary); } } /* Populate a revocation reason if the cert was revoked */ unsigned int numStatusCodes; CSSM_RETURN *statusCodes = NULL; statusCodes = copyCssmStatusCodes(trust, (uint32_t)ix, &numStatusCodes); if (statusCodes) { int32_t reason = statusCodes[numStatusCodes]; // stored at end of status codes array if (reason > 0) { CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason); if (cfreason) { CFDictionarySetValue(certDict, kSecTrustRevocationReason, cfreason); CFRelease(cfreason); } } free(statusCodes); } /* Populate the error in the leaf dictionary */ if (ix == 0) { OSStatus error = errSecSuccess; (void)SecTrustGetCssmResultCode(trust, &error); CFStringRef errorStr = SecCopyErrorMessageString(error, NULL); if (errorStr) { CFDictionarySetValue(certDict, kSecPropertyTypeError, errorStr); CFRelease(errorStr); } } CFArrayAppendValue(properties, certDict); CFRelease(certDict); } return properties; }
/* * Obtain the status of a CMS message's signature. A CMS message can * be signed my multiple signers; this function returns the status * associated with signer 'n' as indicated by the signerIndex parameter. */ OSStatus CMSDecoderCopySignerStatus( CMSDecoderRef cmsDecoder, size_t signerIndex, CFTypeRef policyOrArray, Boolean evaluateSecTrust, CMSSignerStatus *signerStatus, /* optional; RETURNED */ SecTrustRef *secTrust, /* optional; RETURNED */ OSStatus *certVerifyResultCode) /* optional; RETURNED */ { if((cmsDecoder == NULL) || (cmsDecoder->decState != DS_Final)) { return errSecParam; } /* initialize return values */ if(signerStatus) { *signerStatus = kCMSSignerUnsigned; } if(secTrust) { *secTrust = NULL; } if(certVerifyResultCode) { *certVerifyResultCode = 0; } if(cmsDecoder->signedData == NULL) { *signerStatus = kCMSSignerUnsigned; /* redundant, I know, but explicit */ return errSecSuccess; } ASSERT(cmsDecoder->numSigners > 0); if(signerIndex >= cmsDecoder->numSigners) { *signerStatus = kCMSSignerInvalidIndex; return errSecSuccess; } if(!SecCmsSignedDataHasDigests(cmsDecoder->signedData)) { *signerStatus = kCMSSignerNeedsDetachedContent; return errSecSuccess; } /* * OK, we should be able to verify this signerInfo. * I think we have to do the SecCmsSignedDataVerifySignerInfo first * in order get all the cert pieces into place before returning them * to the caller. */ SecTrustRef theTrust = NULL; OSStatus vfyRtn = SecCmsSignedDataVerifySignerInfo(cmsDecoder->signedData, (int)signerIndex, /* * FIXME this cast should not be necessary, but libsecurity_smime * declares this argument as a SecKeychainRef */ (SecKeychainRef)cmsDecoder->keychainOrArray, policyOrArray, &theTrust); /* Subsequent errors to errOut: */ /* * NOTE the smime lib did NOT evaluate that SecTrust - it only does * SecTrustEvaluate() if we don't ask for a copy. * * FIXME deal with multitudes of status returns here...for now, proceed with * obtaining components the caller wants and assume that a nonzero vfyRtn * means "bad signature". */ OSStatus ortn = errSecSuccess; SecTrustResultType secTrustResult; CSSM_RETURN tpVfyStatus = CSSM_OK; OSStatus evalRtn; if(secTrust != NULL) { *secTrust = theTrust; /* we'll release our reference at the end */ if (theTrust) CFRetain(theTrust); } SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(cmsDecoder->signedData, (int)signerIndex); if(signerInfo == NULL) { /* should never happen */ ASSERT(0); dprintf("CMSDecoderCopySignerStatus: no signerInfo\n"); ortn = errSecInternalComponent; goto errOut; } /* now do the actual cert verify */ if(evaluateSecTrust) { evalRtn = SecTrustEvaluate(theTrust, &secTrustResult); if(evalRtn) { /* should never happen */ CSSM_PERROR("SecTrustEvaluate", evalRtn); dprintf("CMSDecoderCopySignerStatus: SecTrustEvaluate error\n"); ortn = errSecInternalComponent; goto errOut; } switch(secTrustResult) { case kSecTrustResultUnspecified: /* cert chain valid, no special UserTrust assignments */ case kSecTrustResultProceed: /* cert chain valid AND user explicitly trusts this */ break; case kSecTrustResultDeny: tpVfyStatus = CSSMERR_APPLETP_TRUST_SETTING_DENY; break; case kSecTrustResultConfirm: dprintf("SecTrustEvaluate reported confirm\n"); tpVfyStatus = CSSMERR_TP_NOT_TRUSTED; break; default: { /* get low-level TP error */ OSStatus tpStatus; ortn = SecTrustGetCssmResultCode(theTrust, &tpStatus); if(ortn) { CSSM_PERROR("SecTrustGetCssmResultCode", ortn); } else { tpVfyStatus = tpStatus; } CSSM_PERROR("TP status after SecTrustEvaluate", tpVfyStatus); break; } } /* switch(secTrustResult) */ } /* evaluateSecTrust true */ if(certVerifyResultCode != NULL) { *certVerifyResultCode = tpVfyStatus; } /* cook up global status based on vfyRtn and tpVfyStatus */ if(signerStatus != NULL) { if((vfyRtn == errSecSuccess) && (tpVfyStatus == CSSM_OK)) { *signerStatus = kCMSSignerValid; } else if(vfyRtn != errSecSuccess) { /* this could mean other things, but for now... */ *signerStatus = kCMSSignerInvalidSignature; } else { *signerStatus = kCMSSignerInvalidCert; } } errOut: CFRELEASE(theTrust); return ortn; }
// // Installer archive. // Hybrid policy: If we detect an installer signature, use and validate that. // If we don't, check for a code signature instead. // void PolicyEngine::evaluateInstall(CFURLRef path, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result) { const AuthorityType type = kAuthorityInstall; Xar xar(cfString(path).c_str()); if (!xar) { // follow the code signing path evaluateCode(path, type, flags, context, result); return; } SQLite3::int64 latentID = 0; // first (highest priority) disabled matching ID std::string latentLabel; // ... and associated label, if any if (!xar.isSigned()) { // unsigned xar if (SYSPOLICY_ASSESS_OUTCOME_UNSIGNED_ENABLED()) SYSPOLICY_ASSESS_OUTCOME_UNSIGNED(cfString(path).c_str(), type); cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, false); addAuthority(result, "no usable signature"); return; } if (CFRef<CFArrayRef> certs = xar.copyCertChain()) { CFRef<CFTypeRef> policy = installerPolicy(); CFRef<SecTrustRef> trust; MacOSError::check(SecTrustCreateWithCertificates(certs, policy, &trust.aref())); // MacOSError::check(SecTrustSetAnchorCertificates(trust, cfEmptyArray())); // no anchors MacOSError::check(SecTrustSetOptions(trust, kSecTrustOptionAllowExpired | kSecTrustOptionImplicitAnchors)); SecTrustResultType trustResult; MacOSError::check(SecTrustEvaluate(trust, &trustResult)); CFRef<CFArrayRef> chain; CSSM_TP_APPLE_EVIDENCE_INFO *info; MacOSError::check(SecTrustGetResult(trust, &trustResult, &chain.aref(), &info)); if (flags & kSecAssessmentFlagRequestOrigin) setOrigin(chain, result); switch (trustResult) { case kSecTrustResultProceed: case kSecTrustResultUnspecified: break; default: { OSStatus rc; MacOSError::check(SecTrustGetCssmResultCode(trust, &rc)); MacOSError::throwMe(rc); } } SQLite::Statement query(*this, "SELECT allow, requirement, id, label, flags, disabled FROM scan_authority" " WHERE type = :type" " ORDER BY priority DESC;"); query.bind(":type").integer(type); while (query.nextRow()) { bool allow = int(query[0]); const char *reqString = query[1]; SQLite3::int64 id = query[2]; const char *label = query[3]; //sqlite_uint64 ruleFlags = query[4]; SQLite3::int64 disabled = query[5]; CFRef<SecRequirementRef> requirement; MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref())); switch (OSStatus rc = SecRequirementEvaluate(requirement, chain, NULL, kSecCSDefaultFlags)) { case noErr: // success break; case errSecCSReqFailed: // requirement missed, but otherwise okay continue; default: // broken in some way; all tests will fail like this so bail out MacOSError::throwMe(rc); } if (disabled) { if (latentID == 0) { latentID = id; if (label) latentLabel = label; } continue; // the loop } if (SYSPOLICY_ASSESS_OUTCOME_ACCEPT_ENABLED() || SYSPOLICY_ASSESS_OUTCOME_DENY_ENABLED()) { if (allow) SYSPOLICY_ASSESS_OUTCOME_ACCEPT(cfString(path).c_str(), type, label, NULL); else SYSPOLICY_ASSESS_OUTCOME_DENY(cfString(path).c_str(), type, label, NULL); } // not adding to the object cache - we could, but it's not likely to be worth it cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, allow); addAuthority(result, label, id); return; } } if (SYSPOLICY_ASSESS_OUTCOME_DEFAULT_ENABLED()) SYSPOLICY_ASSESS_OUTCOME_DEFAULT(cfString(path).c_str(), type, latentLabel.c_str(), NULL); // no applicable authority. Deny by default cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict); addAuthority(result, latentLabel.c_str(), latentID); }