s2n_cert_validation_code s2n_x509_validator_validate_cert_chain()

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, &current_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;
}