//
// Pre-Signing contexts
//
PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer)
{
	// construct a cert chain
	if (signer.signingIdentity() != SecIdentityRef(kCFNull)) {
		CFRef<SecCertificateRef> signingCert;
		MacOSError::check(SecIdentityCopyCertificate(signer.signingIdentity(), &signingCert.aref()));
		CFRef<SecPolicyRef> policy = SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning);
		CFRef<SecTrustRef> trust;
		MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert.get()), policy, &trust.aref()));
		SecTrustResultType result;
		MacOSError::check(SecTrustEvaluate(trust, &result));
		CSSM_TP_APPLE_EVIDENCE_INFO *info;
		MacOSError::check(SecTrustGetResult(trust, &result, &mCerts.aref(), &info));
		this->certs = mCerts;
	}
	
	// other stuff
	this->identifier = signer.signingIdentifier();
}
CFArrayRef CERT_CertChainFromCert(SecCertificateRef cert, SECCertUsage usage, Boolean includeRoot)
{
    SecPolicySearchRef searchRef = NULL;
    SecPolicyRef policy = NULL;
    CFArrayRef wrappedCert = NULL;
    SecTrustRef trust = NULL;
    CFArrayRef certChain = NULL;
    CSSM_TP_APPLE_EVIDENCE_INFO *statusChain;
    CFDataRef actionData = NULL;
    OSStatus status = 0;

    if (!cert)
	goto loser;

    status = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &searchRef);
    if (status)
	goto loser;
    status = SecPolicySearchCopyNext(searchRef, &policy);
    if (status)
	goto loser;

    wrappedCert = CERT_CertListFromCert(cert);
    status = SecTrustCreateWithCertificates(wrappedCert, policy, &trust);
    if (status)
	goto loser;

    /* Tell SecTrust that we don't care if any certs in the chain have expired,
       nor do we want to stop when encountering a cert with a trust setting;
       we always want to build the full chain.
    */
    CSSM_APPLE_TP_ACTION_DATA localActionData = {
        CSSM_APPLE_TP_ACTION_VERSION,
        CSSM_TP_ACTION_ALLOW_EXPIRED | CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT
    };
    actionData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)&localActionData, sizeof(localActionData), kCFAllocatorNull);
    if (!actionData)
        goto loser;

    status = SecTrustSetParameters(trust, CSSM_TP_ACTION_DEFAULT, actionData);
    if (status)
        goto loser;

    status = SecTrustEvaluate(trust, NULL);
    if (status)
	goto loser;

    status = SecTrustGetResult(trust, NULL, &certChain, &statusChain);
    if (status)
	goto loser;

    /* We don't drop the root if there is only 1 (self signed) certificate in the chain. */
    if (!includeRoot && CFArrayGetCount(certChain) > 1)
    {
	CFMutableArrayRef subChain =  CFArrayCreateMutableCopy(NULL, 0, certChain);
	CFRelease(certChain);
	certChain = subChain;
	if (subChain)
	    CFArrayRemoveValueAtIndex(subChain, CFArrayGetCount(subChain) - 1);
    }

loser:
    if (searchRef)
	CFRelease(searchRef);
    if (policy)
	CFRelease(policy);
    if (wrappedCert)
	CFRelease(wrappedCert);
    if (trust)
	CFRelease(trust);
    if (actionData)
	CFRelease(actionData);
    if (certChain && status)
    {
	CFRelease(certChain);
	certChain = NULL;
    }

    return certChain;
}
/*
 * Given a SecIdentityRef, do our best to construct a complete, ordered, and 
 * verified cert chain, returning the result in a CFArrayRef. The result is 
 * suitable for use when calling SSLSetCertificate().
 */
OSStatus sslCompleteCertChain(
	SecIdentityRef 		identity, 
	SecCertificateRef	trustedAnchor,	// optional additional trusted anchor
	bool 				includeRoot, 	// include the root in outArray
	CFArrayRef			*outArray)		// created and RETURNED
{
	CFMutableArrayRef 			certArray;
	SecTrustRef					secTrust = NULL;
	SecPolicyRef				policy = NULL;
	SecPolicySearchRef			policySearch = NULL;
	SecTrustResultType			secTrustResult;
	CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv;			// not used
	CFArrayRef					certChain = NULL;   // constructed chain
	CFIndex 					numResCerts;
	
	certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
	CFArrayAppendValue(certArray, identity);
	
	/*
	 * Case 1: identity is a root; we're done. Note that this case
	 * overrides the includeRoot argument.
	 */
	SecCertificateRef certRef;
	OSStatus ortn = SecIdentityCopyCertificate(identity, &certRef);
	if(ortn) {
		/* should never happen */
		cssmPerror("SecIdentityCopyCertificate", ortn);
		return ortn;
	}
	bool isRoot = isCertRefRoot(certRef);
	if(isRoot) {
		*outArray = certArray;
		CFRelease(certRef);
		return errSecSuccess;
	}
	
	/* 
	 * Now use SecTrust to get a complete cert chain, using all of the 
	 * user's keychains to look for intermediate certs.
	 * NOTE this does NOT handle root certs which are not in the system
	 * root cert DB. (The above case, where the identity is a root cert, does.)
	 */
	CFMutableArrayRef subjCerts = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks);
	CFArraySetValueAtIndex(subjCerts, 0, certRef);
			
	/* the array owns the subject cert ref now */
	CFRelease(certRef);
	
	/* Get a SecPolicyRef for generic X509 cert chain verification */
	ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
		&CSSMOID_APPLE_X509_BASIC,
		NULL,				// value
		&policySearch);
	if(ortn) {
		cssmPerror("SecPolicySearchCreate", ortn);
		goto errOut;
	}
	ortn = SecPolicySearchCopyNext(policySearch, &policy);
	if(ortn) {
		cssmPerror("SecPolicySearchCopyNext", ortn);
		goto errOut;
	}

	/* build a SecTrustRef for specified policy and certs */
	ortn = SecTrustCreateWithCertificates(subjCerts,
		policy, &secTrust);
	if(ortn) {
		cssmPerror("SecTrustCreateWithCertificates", ortn);
		goto errOut;
	}
	
	if(trustedAnchor) {
		/* 
		 * Tell SecTrust to trust this one in addition to the current
		 * trusted system-wide anchors.
		 */
		CFMutableArrayRef newAnchors;
		CFArrayRef currAnchors;
		
		ortn = SecTrustCopyAnchorCertificates(&currAnchors);
		if(ortn) {
			/* should never happen */
			cssmPerror("SecTrustCopyAnchorCertificates", ortn);
			goto errOut;
		}
		newAnchors = CFArrayCreateMutableCopy(NULL,
			CFArrayGetCount(currAnchors) + 1,
			currAnchors);
		CFRelease(currAnchors);
		CFArrayAppendValue(newAnchors, trustedAnchor);
		ortn = SecTrustSetAnchorCertificates(secTrust, newAnchors);
		CFRelease(newAnchors);
		if(ortn) {
			cssmPerror("SecTrustSetAnchorCertificates", ortn);
			goto errOut;
		}
	}
	/* evaluate: GO */
	ortn = SecTrustEvaluate(secTrust, &secTrustResult);
	if(ortn) {
		cssmPerror("SecTrustEvaluate", ortn);
		goto errOut;
	}
	switch(secTrustResult) {
		case kSecTrustResultUnspecified:
			/* cert chain valid, no special UserTrust assignments */
		case kSecTrustResultProceed:
			/* cert chain valid AND user explicitly trusts this */
			break;
		default:
			/*
			 * Cert chain construction failed. 
			 * Just go with the single subject cert we were given.
			 */
			printf("***Warning: could not construct completed cert chain\n");
			ortn = errSecSuccess;
			goto errOut;
	}

	/* get resulting constructed cert chain */
	ortn = SecTrustGetResult(secTrust, &secTrustResult, &certChain, &dummyEv);
	if(ortn) {
		cssmPerror("SecTrustEvaluate", ortn);
		goto errOut;
	}
	
	/*
	 * Copy certs from constructed chain to our result array, skipping 
	 * the leaf (which is already there, as a SecIdentityRef) and possibly
	 * a root.
	 */
	numResCerts = CFArrayGetCount(certChain);
	if(numResCerts < 2) {
		/*
		 * Can't happen: if subject was a root, we'd already have returned. 
		 * If chain doesn't verify to a root, we'd have bailed after
		 * SecTrustEvaluate().
		 */
		printf("***sslCompleteCertChain screwup: numResCerts %d\n", 
			(int)numResCerts);
		ortn = errSecSuccess;
		goto errOut;
	}
	if(!includeRoot) {
		/* skip the last (root) cert) */
		numResCerts--;
	}
	for(CFIndex dex=1; dex<numResCerts; dex++) {
		certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, dex);
		CFArrayAppendValue(certArray, certRef);
	}
errOut:
	/* clean up */
	if(secTrust) {
		CFRelease(secTrust);
	}
	if(subjCerts) {
		CFRelease(subjCerts);
	}
	if(policy) {
		CFRelease(policy);
	}
	if(policySearch) {
		CFRelease(policySearch);
	}
	*outArray = certArray;
	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);
}