#include <stdint.h>
#include <inttypes.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <TrustEvaluationAgent/TrustEvaluationAgent.h>
#include <syslog.h>
#include "cryptlib.h"
#include "x509_vfy_apple.h"
#define TEA_might_correct_error(err) (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY || err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT || err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN)
int
X509_verify_cert(X509_STORE_CTX *ctx)
{
TEAResult ret = kTEAResultCertNotTrusted;
TEACertificateChainRef inputChain = NULL;
TEACertificateChainRef outputChain = NULL;
__block uint64_t certCount = 0;
uint64_t certLastIndex = 0;
uint64_t i = 0;
int error = 0;
TEAParams params = { 0 };
if (ctx == NULL || ctx->cert == NULL)
return kTEAResultErrorOccured;
ret = X509_verify_cert_orig(ctx);
if (0 != X509_TEA_is_enabled() &&
ret != 1 && TEA_might_correct_error(ctx->error)) {
if (ctx->chain == NULL && (ctx->chain = sk_X509_new_null()) == NULL) {
TEALogDebug("Could not create the certificate chain");
ret = kTEAResultCertNotTrusted;
goto bail;
}
certLastIndex = sk_X509_num(ctx->untrusted);
if (certLastIndex > ctx->param->depth) {
TEALogInfo("Pruning certificate chain to %" PRIu64, certLastIndex);
certLastIndex = ctx->param->depth;
}
inputChain = TEACertificateChainCreate();
if (inputChain == NULL) {
TEALogDebug("Certificate chain creation failed");
goto bail;
}
error = TEACertificateChainAddCert(inputChain, ctx->cert, i2d_X509(ctx->cert, NULL));
if (error) {
TEALogDebug("An error occured while inserting the certificate into the chain");
goto bail;
}
for (i = 0; i < certLastIndex; ++i) {
X509 *t = sk_X509_value(ctx->untrusted, i);
error = TEACertificateChainAddCert(inputChain, t, i2d_X509(t, NULL));
if (error) {
TEALogDebug("An error occured while inserting an untrusted certificate into the chain");
goto bail;
}
}
TEACertificateChainSetEncodingHandler(inputChain, ^(uint8_t *dstBuffer, const TEACertificateRef cert) {
if (i2d_X509((void *)TEACertificateGetData(cert), &dstBuffer) == 0)
return 1;
return 0;
});
params.purpose = ctx->param->purpose;
if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME)
params.time = ctx->param->check_time;
outputChain = TEAVerifyCert(inputChain, ¶ms);
TEACertificateChainRelease(inputChain);
inputChain = NULL;
if (outputChain == NULL) {
TEALogDebug("TEAVerifyCert() returned NULL.");
goto bail;
}
for (i = 0; i < sk_X509_num(ctx->chain); ++i)
sk_X509_pop(ctx->chain);
error = TEACertificateChainGetCerts(outputChain, ^(const TEACertificateRef cert) {
const unsigned char *ptr = TEACertificateGetData(cert);
X509 *c = NULL;
if (certCount++ > certLastIndex)
return 0;
c = d2i_X509(NULL, &ptr, TEACertificateGetSize(cert));
if (c == NULL) {
TEALogDebug("Could not parse certificate");
return 1;
}
if (!sk_X509_push(ctx->chain, c)) {
TEALogDebug("Could not insert certificate into the chain");
return 1;
}
return 0;
});
if (error) {
TEALogDebug("An error occured while deserializing the trusted certificate chain");
ret = kTEAResultCertNotTrusted;
goto bail;
}
TEACertificateChainRelease(outputChain);
outputChain = NULL;
ctx->current_cert = sk_X509_value(ctx->chain, 0);
ctx->current_issuer = sk_X509_value(ctx->chain, sk_X509_num(ctx->chain) - 1);
ctx->error_depth = 0;
ctx->error = 0;
X509_get_pubkey_parameters(NULL, ctx->chain);
ret = kTEAResultCertTrusted;
}
bail:
return ret;
}
#pragma mark Trust Evaluation Agent
static tea_enabled = -1;
void
X509_TEA_set_state(int change)
{
tea_enabled = (change) ? 1 : 0;
}
int
X509_TEA_is_enabled()
{
if (tea_enabled < 0)
tea_enabled = (NULL == getenv(X509_TEA_ENV_DISABLE));
return tea_enabled != 0;
}