1/* 2 * Copyright (c) 2009 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 * 23 */ 24 25#include <stdint.h> 26#include <inttypes.h> 27#include <openssl/x509.h> 28#include <openssl/x509v3.h> 29#include <TrustEvaluationAgent/TrustEvaluationAgent.h> 30#include <syslog.h> 31 32#include "cryptlib.h" 33#include "x509_vfy_apple.h" 34 35#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) 36 37/* 38 * Please see comment in x509_vfy_apple.h 39 */ 40int 41X509_verify_cert(X509_STORE_CTX *ctx) 42{ 43 TEAResult ret = kTEAResultCertNotTrusted; 44 TEACertificateChainRef inputChain = NULL; 45 TEACertificateChainRef outputChain = NULL; 46 __block uint64_t certCount = 0; 47 uint64_t certLastIndex = 0; 48 uint64_t i = 0; 49 int error = 0; 50 TEAParams params = { 0 }; 51 52 53 if (ctx == NULL || ctx->cert == NULL) 54 return kTEAResultErrorOccured; 55 56 /* Try OpenSSL, if we get a local certificate issue verify against trusted roots */ 57 ret = X509_verify_cert_orig(ctx); 58 59 /* Verify TEA is enabled and should be used. */ 60 if (0 != X509_TEA_is_enabled() && 61 ret != 1 && TEA_might_correct_error(ctx->error)) { 62 63 /* Verify that the certificate chain exists, otherwise make it. */ 64 if (ctx->chain == NULL && (ctx->chain = sk_X509_new_null()) == NULL) { 65 TEALogDebug("Could not create the certificate chain"); 66 ret = kTEAResultCertNotTrusted; 67 goto bail; 68 } 69 70 /* Verify chain depth */ 71 certLastIndex = sk_X509_num(ctx->untrusted); 72 if (certLastIndex > ctx->param->depth) { 73 TEALogInfo("Pruning certificate chain to %" PRIu64, certLastIndex); 74 certLastIndex = ctx->param->depth; 75 } 76 77 inputChain = TEACertificateChainCreate(); 78 if (inputChain == NULL) { 79 TEALogDebug("Certificate chain creation failed"); 80 goto bail; 81 } 82 83 unsigned char *asn1_cert_data = NULL; 84 int asn1_cert_len = i2d_X509(ctx->cert, &asn1_cert_data); 85 error = TEACertificateChainAddCert(inputChain, asn1_cert_data, asn1_cert_len); 86 // TEACertificateChainAddCert made a copy of the ASN.1 data, so we get to free ours here 87 OPENSSL_free(asn1_cert_data); 88 if (error) { 89 TEALogDebug("An error occured while inserting the certificate into the chain"); 90 goto bail; 91 } 92 93 for (i = 0; i < certLastIndex; ++i) { 94 X509 *t = sk_X509_value(ctx->untrusted, i); 95 96 asn1_cert_data = NULL; 97 asn1_cert_len = i2d_X509(t, &asn1_cert_data); 98 error = TEACertificateChainAddCert(inputChain, asn1_cert_data, asn1_cert_len); 99 // TEACertificateChainAddCert made a copy of the ASN.1 data, so we get to free ours here 100 OPENSSL_free(asn1_cert_data); 101 if (error) { 102 TEALogDebug("An error occured while inserting an untrusted certificate into the chain"); 103 goto bail; 104 } 105 } 106 107 // We put ASN.1 encoded X509 on the CertificateChain, so we don't call TEACertificateChainSetEncodingHandler 108 109 params.purpose = ctx->param->purpose; 110 if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) 111 params.time = ctx->param->check_time; 112 113 outputChain = TEAVerifyCert(inputChain, ¶ms); 114 115 TEACertificateChainRelease(inputChain); 116 inputChain = NULL; 117 118 if (outputChain == NULL) { 119 TEALogDebug("TEAVerifyCert() returned NULL."); 120 goto bail; 121 } 122 123 /* Empty the context chain */ 124 for (i = 0; i < sk_X509_num(ctx->chain); ++i) 125 sk_X509_pop(ctx->chain); 126 127 error = TEACertificateChainGetCerts(outputChain, ^(const TEACertificateRef cert) { 128 const unsigned char *ptr = TEACertificateGetData(cert); 129 X509 *c = NULL; 130 131 if (certCount++ > certLastIndex) 132 return 0; 133 134 c = d2i_X509(NULL, &ptr, TEACertificateGetSize(cert)); 135 if (c == NULL) { 136 TEALogDebug("Could not parse certificate"); 137 return 1; 138 } 139 140 if (!sk_X509_push(ctx->chain, c)) { 141 TEALogDebug("Could not insert certificate into the chain"); 142 return 1; 143 } 144 145 return 0; 146 }); 147 if (error) { 148 TEALogDebug("An error occured while deserializing the trusted certificate chain"); 149 ret = kTEAResultCertNotTrusted; 150 goto bail; 151 } 152 153 TEACertificateChainRelease(outputChain); 154 outputChain = NULL; 155 156 /* Fixup context data */ 157 ctx->current_cert = sk_X509_value(ctx->chain, 0); 158 ctx->current_issuer = sk_X509_value(ctx->chain, sk_X509_num(ctx->chain) - 1); 159 ctx->error_depth = 0; 160 ctx->error = 0; 161 X509_get_pubkey_parameters(NULL, ctx->chain); 162 163 ret = kTEAResultCertTrusted; 164 } 165 166bail: 167 if (inputChain) { 168 TEACertificateChainRelease(inputChain); 169 inputChain = NULL; 170 } 171 if (outputChain) { 172 TEACertificateChainRelease(outputChain); 173 outputChain = NULL; 174 } 175 return ret; 176} 177 178#pragma mark Trust Evaluation Agent 179 180/* -1: not set 181 * 0: set to false 182 * 1: set to true 183 */ 184static int tea_enabled = -1; 185 186void 187X509_TEA_set_state(int change) 188{ 189 tea_enabled = (change) ? 1 : 0; 190} 191 192int 193X509_TEA_is_enabled() 194{ 195 if (tea_enabled < 0) 196 tea_enabled = (NULL == getenv(X509_TEA_ENV_DISABLE)); 197 198 return tea_enabled != 0; 199} 200