in tls/s2n_x509_validator.c [287:387]
s2n_cert_validation_code s2n_x509_validator_validate_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn,
uint8_t *cert_chain_in, uint32_t cert_chain_len, s2n_pkey_type *pkey_type, struct s2n_pkey *public_key_out) {
S2N_ERROR_IF(!validator->skip_cert_validation && !s2n_x509_trust_store_has_certs(validator->trust_store), S2N_ERR_CERT_UNTRUSTED);
S2N_ERROR_IF(validator->state != INIT, S2N_ERR_INVALID_CERT_STATE);
struct s2n_blob cert_chain_blob = {.data = cert_chain_in, .size = cert_chain_len};
DEFER_CLEANUP(struct s2n_stuffer cert_chain_in_stuffer = {0}, s2n_stuffer_free);
S2N_ERROR_IF(s2n_stuffer_init(&cert_chain_in_stuffer, &cert_chain_blob) < 0, S2N_ERR_CERT_UNTRUSTED);
S2N_ERROR_IF(s2n_stuffer_write(&cert_chain_in_stuffer, &cert_chain_blob) < 0, S2N_ERR_CERT_UNTRUSTED);
s2n_parsed_extensions_list first_certificate_extensions = {0};
X509 *server_cert = NULL;
DEFER_CLEANUP(struct s2n_pkey public_key = {0}, s2n_pkey_free);
s2n_pkey_zero_init(&public_key);
while (s2n_stuffer_data_available(&cert_chain_in_stuffer) && sk_X509_num(validator->cert_chain_from_wire) < validator->max_chain_depth) {
uint32_t certificate_size = 0;
S2N_ERROR_IF(s2n_stuffer_read_uint24(&cert_chain_in_stuffer, &certificate_size) < 0, S2N_ERR_CERT_UNTRUSTED);
S2N_ERROR_IF(certificate_size == 0 || certificate_size > s2n_stuffer_data_available(&cert_chain_in_stuffer), S2N_ERR_CERT_UNTRUSTED);
struct s2n_blob asn1cert = {0};
asn1cert.size = certificate_size;
asn1cert.data = s2n_stuffer_raw_read(&cert_chain_in_stuffer, certificate_size);
POSIX_ENSURE_REF(asn1cert.data);
const uint8_t *data = asn1cert.data;
/* the cert is der encoded, just convert it. */
server_cert = d2i_X509(NULL, &data, asn1cert.size);
S2N_ERROR_IF(!server_cert, S2N_ERR_CERT_UNTRUSTED);
/* add the cert to the chain. */
if (!sk_X509_push(validator->cert_chain_from_wire, server_cert)) {
X509_free(server_cert);
POSIX_BAIL(S2N_ERR_CERT_UNTRUSTED);
}
if (!validator->skip_cert_validation) {
POSIX_GUARD_RESULT(s2n_validate_certificate_signature(conn, server_cert));
}
/* Pull the public key from the first certificate */
if (sk_X509_num(validator->cert_chain_from_wire) == 1) {
S2N_ERROR_IF(s2n_asn1der_to_public_key_and_type(&public_key, pkey_type, &asn1cert) < 0, S2N_ERR_CERT_UNTRUSTED);
}
/* certificate extensions is a field in TLS 1.3 - https://tools.ietf.org/html/rfc8446#section-4.4.2 */
if (conn->actual_protocol_version >= S2N_TLS13) {
s2n_parsed_extensions_list parsed_extensions_list = { 0 };
POSIX_GUARD(s2n_extension_list_parse(&cert_chain_in_stuffer, &parsed_extensions_list));
/* RFC 8446: if an extension applies to the entire chain, it SHOULD be included in the first CertificateEntry */
if (sk_X509_num(validator->cert_chain_from_wire) == 1) {
first_certificate_extensions = parsed_extensions_list;
}
}
}
/* if this occurred we exceeded validator->max_chain_depth */
S2N_ERROR_IF(!validator->skip_cert_validation && s2n_stuffer_data_available(&cert_chain_in_stuffer), S2N_ERR_CERT_UNTRUSTED);
S2N_ERROR_IF(sk_X509_num(validator->cert_chain_from_wire) < 1, S2N_ERR_CERT_UNTRUSTED);
if (!validator->skip_cert_validation) {
X509 *leaf = sk_X509_value(validator->cert_chain_from_wire, 0);
S2N_ERROR_IF(!leaf, S2N_ERR_CERT_UNTRUSTED);
S2N_ERROR_IF(conn->verify_host_fn && !s2n_verify_host_information(validator, conn, leaf), S2N_ERR_CERT_UNTRUSTED);
int op_code = X509_STORE_CTX_init(validator->store_ctx, validator->trust_store->trust_store, leaf, validator->cert_chain_from_wire);
S2N_ERROR_IF(op_code <= 0, S2N_ERR_CERT_UNTRUSTED);
X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(validator->store_ctx);
X509_VERIFY_PARAM_set_depth(param, validator->max_chain_depth);
uint64_t current_sys_time = 0;
conn->config->wall_clock(conn->config->sys_clock_ctx, ¤t_sys_time);
/* this wants seconds not nanoseconds */
time_t current_time = (time_t)(current_sys_time / 1000000000);
X509_STORE_CTX_set_time(validator->store_ctx, 0, current_time);
op_code = X509_verify_cert(validator->store_ctx);
S2N_ERROR_IF(op_code <= 0, S2N_ERR_CERT_UNTRUSTED);
validator->state = VALIDATED;
}
if (conn->actual_protocol_version >= S2N_TLS13) {
POSIX_GUARD(s2n_extension_list_process(S2N_EXTENSION_LIST_CERTIFICATE, conn, &first_certificate_extensions));
}
*public_key_out = public_key;
/* Reset the old struct, so we don't clean up public_key_out */
s2n_pkey_zero_init(&public_key);
return S2N_CERT_OK;
}