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, &params);
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