diff options
author | orivej <orivej@yandex-team.ru> | 2022-02-10 16:45:01 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:45:01 +0300 |
commit | 2d37894b1b037cf24231090eda8589bbb44fb6fc (patch) | |
tree | be835aa92c6248212e705f25388ebafcf84bc7a1 /contrib/restricted/aws/s2n/crypto/s2n_certificate.c | |
parent | 718c552901d703c502ccbefdfc3c9028d608b947 (diff) | |
download | ydb-2d37894b1b037cf24231090eda8589bbb44fb6fc.tar.gz |
Restoring authorship annotation for <orivej@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/restricted/aws/s2n/crypto/s2n_certificate.c')
-rw-r--r-- | contrib/restricted/aws/s2n/crypto/s2n_certificate.c | 1068 |
1 files changed, 534 insertions, 534 deletions
diff --git a/contrib/restricted/aws/s2n/crypto/s2n_certificate.c b/contrib/restricted/aws/s2n/crypto/s2n_certificate.c index 988a5f6971..39646c3f28 100644 --- a/contrib/restricted/aws/s2n/crypto/s2n_certificate.c +++ b/contrib/restricted/aws/s2n/crypto/s2n_certificate.c @@ -1,534 +1,534 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#ifndef _GNU_SOURCE -# define _GNU_SOURCE -#endif - -#include <s2n.h> -#include <openssl/x509v3.h> -#include <openssl/pem.h> -#include <string.h> -#include <strings.h> - -#include "crypto/s2n_certificate.h" -#include "utils/s2n_array.h" -#include "utils/s2n_safety.h" -#include "utils/s2n_mem.h" - -#include "tls/extensions/s2n_extension_list.h" -#include "tls/s2n_connection.h" - -int s2n_cert_set_cert_type(struct s2n_cert *cert, s2n_pkey_type pkey_type) -{ - notnull_check(cert); - cert->pkey_type = pkey_type; - GUARD(s2n_pkey_setup_for_type(&cert->public_key, pkey_type)); - return 0; -} - -int s2n_create_cert_chain_from_stuffer(struct s2n_cert_chain *cert_chain_out, struct s2n_stuffer *chain_in_stuffer) -{ - DEFER_CLEANUP(struct s2n_stuffer cert_out_stuffer = {0}, s2n_stuffer_free); - GUARD(s2n_stuffer_growable_alloc(&cert_out_stuffer, 2048)); - - struct s2n_cert **insert = &cert_chain_out->head; - uint32_t chain_size = 0; - do { - struct s2n_cert *new_node = NULL; - - if (s2n_stuffer_certificate_from_pem(chain_in_stuffer, &cert_out_stuffer) < 0) { - if (chain_size == 0) { - S2N_ERROR(S2N_ERR_NO_CERTIFICATE_IN_PEM); - } - break; - } - struct s2n_blob mem = {0}; - GUARD(s2n_alloc(&mem, sizeof(struct s2n_cert))); - new_node = (struct s2n_cert *)(void *)mem.data; - - if (s2n_alloc(&new_node->raw, s2n_stuffer_data_available(&cert_out_stuffer)) != S2N_SUCCESS) { - GUARD(s2n_free(&mem)); - S2N_ERROR_PRESERVE_ERRNO(); - } - if (s2n_stuffer_read(&cert_out_stuffer, &new_node->raw) != S2N_SUCCESS) { - GUARD(s2n_free(&mem)); - S2N_ERROR_PRESERVE_ERRNO(); - } - - /* Additional 3 bytes for the length field in the protocol */ - chain_size += new_node->raw.size + 3; - new_node->next = NULL; - *insert = new_node; - insert = &new_node->next; - } while (s2n_stuffer_data_available(chain_in_stuffer)); - - /* Leftover data at this point means one of two things: - * A bug in s2n's PEM parsing OR a malformed PEM in the user's chain. - * Be conservative and fail instead of using a partial chain. - */ - S2N_ERROR_IF(s2n_stuffer_data_available(chain_in_stuffer) > 0, S2N_ERR_INVALID_PEM); - - cert_chain_out->chain_size = chain_size; - - return 0; -} - -int s2n_cert_chain_and_key_set_cert_chain_from_stuffer(struct s2n_cert_chain_and_key *cert_and_key, struct s2n_stuffer *chain_in_stuffer) -{ - return s2n_create_cert_chain_from_stuffer(cert_and_key->cert_chain, chain_in_stuffer); -} - -int s2n_cert_chain_and_key_set_cert_chain(struct s2n_cert_chain_and_key *cert_and_key, const char *cert_chain_pem) -{ - struct s2n_stuffer chain_in_stuffer = {0}; - - /* Turn the chain into a stuffer */ - GUARD(s2n_stuffer_alloc_ro_from_string(&chain_in_stuffer, cert_chain_pem)); - int rc = s2n_cert_chain_and_key_set_cert_chain_from_stuffer(cert_and_key, &chain_in_stuffer); - - GUARD(s2n_stuffer_free(&chain_in_stuffer)); - - return rc; -} - -int s2n_cert_chain_and_key_set_private_key(struct s2n_cert_chain_and_key *cert_and_key, const char *private_key_pem) -{ - DEFER_CLEANUP(struct s2n_stuffer key_in_stuffer = {0}, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer key_out_stuffer = {0}, s2n_stuffer_free); - struct s2n_blob key_blob = {0}; - - GUARD(s2n_pkey_zero_init(cert_and_key->private_key)); - - /* Put the private key pem in a stuffer */ - GUARD(s2n_stuffer_alloc_ro_from_string(&key_in_stuffer, private_key_pem)); - GUARD(s2n_stuffer_growable_alloc(&key_out_stuffer, strlen(private_key_pem))); - - /* Convert pem to asn1 and asn1 to the private key. Handles both PKCS#1 and PKCS#8 formats */ - GUARD(s2n_stuffer_private_key_from_pem(&key_in_stuffer, &key_out_stuffer)); - key_blob.size = s2n_stuffer_data_available(&key_out_stuffer); - key_blob.data = s2n_stuffer_raw_read(&key_out_stuffer, key_blob.size); - notnull_check(key_blob.data); - - /* Get key type and create appropriate key context */ - GUARD(s2n_asn1der_to_private_key(cert_and_key->private_key, &key_blob)); - - return 0; -} - -int s2n_cert_chain_and_key_set_ocsp_data(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length) -{ - notnull_check(chain_and_key); - GUARD(s2n_free(&chain_and_key->ocsp_status)); - if (data && length) { - GUARD(s2n_alloc(&chain_and_key->ocsp_status, length)); - memcpy_check(chain_and_key->ocsp_status.data, data, length); - } - return 0; -} - -int s2n_cert_chain_and_key_set_sct_list(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length) -{ - notnull_check(chain_and_key); - GUARD(s2n_free(&chain_and_key->sct_list)); - if (data && length) { - GUARD(s2n_alloc(&chain_and_key->sct_list, length)); - memcpy_check(chain_and_key->sct_list.data, data, length); - } - return 0; -} - -struct s2n_cert_chain_and_key *s2n_cert_chain_and_key_new(void) -{ - struct s2n_cert_chain_and_key *chain_and_key; - struct s2n_blob chain_and_key_mem, cert_chain_mem, pkey_mem; - - GUARD_PTR(s2n_alloc(&chain_and_key_mem, sizeof(struct s2n_cert_chain_and_key))); - chain_and_key = (struct s2n_cert_chain_and_key *)(void *)chain_and_key_mem.data; - - /* Allocate the memory for the chain and key */ - if (s2n_alloc(&cert_chain_mem, sizeof(struct s2n_cert_chain)) != S2N_SUCCESS) { - goto cleanup; - } - chain_and_key->cert_chain = (struct s2n_cert_chain *)(void *)cert_chain_mem.data; - - if (s2n_alloc(&pkey_mem, sizeof(s2n_cert_private_key)) != S2N_SUCCESS) { - goto cleanup; - } - chain_and_key->private_key = (s2n_cert_private_key *)(void *)pkey_mem.data; - - chain_and_key->cert_chain->head = NULL; - if (s2n_pkey_zero_init(chain_and_key->private_key) != S2N_SUCCESS) { - goto cleanup; - } - memset(&chain_and_key->ocsp_status, 0, sizeof(chain_and_key->ocsp_status)); - memset(&chain_and_key->sct_list, 0, sizeof(chain_and_key->sct_list)); - chain_and_key->cn_names = s2n_array_new(sizeof(struct s2n_blob)); - if (!chain_and_key->cn_names) { - goto cleanup; - } - - chain_and_key->san_names = s2n_array_new(sizeof(struct s2n_blob)); - if (!chain_and_key->san_names) { - goto cleanup; - } - - chain_and_key->context = NULL; - - return chain_and_key; - cleanup: - s2n_free(&pkey_mem); - s2n_free(&cert_chain_mem); - s2n_free(&chain_and_key_mem); - return NULL; -} - -DEFINE_POINTER_CLEANUP_FUNC(GENERAL_NAMES *, GENERAL_NAMES_free); - -int s2n_cert_chain_and_key_load_sans(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert) -{ - notnull_check(chain_and_key->san_names); - - DEFER_CLEANUP(GENERAL_NAMES *san_names = X509_get_ext_d2i(x509_cert, NID_subject_alt_name, NULL, NULL), GENERAL_NAMES_free_pointer); - if (san_names == NULL) { - /* No SAN extension */ - return 0; - } - - const int num_san_names = sk_GENERAL_NAME_num(san_names); - for (int i = 0; i < num_san_names; i++) { - GENERAL_NAME *san_name = sk_GENERAL_NAME_value(san_names, i); - if (!san_name) { - continue; - } - - if (san_name->type == GEN_DNS) { - /* Decoding isn't necessary here since a DNS SAN name is ASCII(type V_ASN1_IA5STRING) */ - unsigned char *san_str = san_name->d.dNSName->data; - const size_t san_str_len = san_name->d.dNSName->length; - struct s2n_blob *san_blob = NULL; - GUARD_AS_POSIX(s2n_array_pushback(chain_and_key->san_names, (void **)&san_blob)); - if (!san_blob) { - S2N_ERROR(S2N_ERR_NULL_SANS); - } - - if (s2n_alloc(san_blob, san_str_len)) { - S2N_ERROR_PRESERVE_ERRNO(); - } - - memcpy_check(san_blob->data, san_str, san_str_len); - san_blob->size = san_str_len; - /* normalize san_blob to lowercase */ - GUARD(s2n_blob_char_to_lower(san_blob)); - } - } - - return 0; -} - -/* Parse CN names from the Subject of the leaf certificate. Technically there can by multiple CNs - * in the Subject but practically very few certificates in the wild will have more than one CN. - * Since the data for this certificate is coming from the application and not from an untrusted - * source, we will try our best to parse all of the CNs. - * - * A recent CAB thread proposed removing support for multiple CNs: - * https://cabforum.org/pipermail/public/2016-April/007242.html - */ - -DEFINE_POINTER_CLEANUP_FUNC(unsigned char *, OPENSSL_free); - -int s2n_cert_chain_and_key_load_cns(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert) -{ - notnull_check(chain_and_key->cn_names); - - X509_NAME *subject = X509_get_subject_name(x509_cert); - if (!subject) { - return 0; - } - - int lastpos = -1; - while((lastpos = X509_NAME_get_index_by_NID(subject, NID_commonName, lastpos)) >= 0) { - X509_NAME_ENTRY *name_entry = X509_NAME_get_entry(subject, lastpos); - if (!name_entry) { - continue; - } - - ASN1_STRING *asn1_str = X509_NAME_ENTRY_get_data(name_entry); - if (!asn1_str) { - continue; - } - - /* We need to try and decode the CN since it may be encoded as unicode with a - * direct ASCII equivalent. Any non ASCII bytes in the string will fail later when we - * actually compare hostnames. - */ - DEFER_CLEANUP(unsigned char *utf8_str, OPENSSL_free_pointer); - const int utf8_out_len = ASN1_STRING_to_UTF8(&utf8_str, asn1_str); - if (utf8_out_len < 0) { - /* On failure, ASN1_STRING_to_UTF8 does not allocate any memory */ - continue; - } else if (utf8_out_len == 0) { - /* We still need to free memory here see https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7521 */ - OPENSSL_free(utf8_str); - } else { - struct s2n_blob *cn_name = NULL; - GUARD_AS_POSIX(s2n_array_pushback(chain_and_key->cn_names, (void **)&cn_name)); - if (cn_name == NULL) { - S2N_ERROR(S2N_ERR_NULL_CN_NAME); - } - - if (s2n_alloc(cn_name, utf8_out_len) < 0) { - S2N_ERROR_PRESERVE_ERRNO(); - } - memcpy_check(cn_name->data, utf8_str, utf8_out_len); - cn_name->size = utf8_out_len; - /* normalize cn_name to lowercase */ - GUARD(s2n_blob_char_to_lower(cn_name)); - } - } - - return 0; -} - -static int s2n_cert_chain_and_key_set_names(struct s2n_cert_chain_and_key *chain_and_key, struct s2n_blob *leaf_bytes) -{ - const unsigned char *leaf_der = leaf_bytes->data; - X509 *cert = d2i_X509(NULL, &leaf_der, leaf_bytes->size); - if (!cert) { - S2N_ERROR(S2N_ERR_INVALID_PEM); - } - - GUARD(s2n_cert_chain_and_key_load_sans(chain_and_key, cert)); - /* For current use cases, we *could* avoid populating the common names if any sans were loaded in - * s2n_cert_chain_and_key_load_sans. Let's unconditionally populate this field to avoid surprises - * in the future. - */ - GUARD(s2n_cert_chain_and_key_load_cns(chain_and_key, cert)); - - X509_free(cert); - return 0; -} - -int s2n_cert_chain_and_key_load_pem(struct s2n_cert_chain_and_key *chain_and_key, const char *chain_pem, const char *private_key_pem) -{ - notnull_check(chain_and_key); - - GUARD(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, chain_pem)); - GUARD(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); - - /* Parse the leaf cert for the public key and certificate type */ - DEFER_CLEANUP(struct s2n_pkey public_key = {0}, s2n_pkey_free); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - GUARD(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); - S2N_ERROR_IF(pkey_type == S2N_PKEY_TYPE_UNKNOWN, S2N_ERR_CERT_TYPE_UNSUPPORTED); - GUARD(s2n_cert_set_cert_type(chain_and_key->cert_chain->head, pkey_type)); - - /* Validate the leaf cert's public key matches the provided private key */ - GUARD(s2n_pkey_match(&public_key, chain_and_key->private_key)); - - /* Populate name information from the SAN/CN for the leaf certificate */ - GUARD(s2n_cert_chain_and_key_set_names(chain_and_key, &chain_and_key->cert_chain->head->raw)); - - return 0; -} - -int s2n_cert_chain_and_key_free(struct s2n_cert_chain_and_key *cert_and_key) -{ - if (cert_and_key == NULL) { - return 0; - } - - /* Walk the chain and free the certs */ - if (cert_and_key->cert_chain) { - struct s2n_cert *node = cert_and_key->cert_chain->head; - while (node) { - /* Free the cert */ - GUARD(s2n_free(&node->raw)); - /* update head so it won't point to freed memory */ - cert_and_key->cert_chain->head = node->next; - /* Free the node */ - GUARD(s2n_free_object((uint8_t **)&node, sizeof(struct s2n_cert))); - node = cert_and_key->cert_chain->head; - } - - GUARD(s2n_free_object((uint8_t **)&cert_and_key->cert_chain, sizeof(struct s2n_cert_chain))); - } - - if (cert_and_key->private_key) { - GUARD(s2n_pkey_free(cert_and_key->private_key)); - GUARD(s2n_free_object((uint8_t **)&cert_and_key->private_key, sizeof(s2n_cert_private_key))); - } - - uint32_t len = 0; - - if (cert_and_key->san_names) { - GUARD_AS_POSIX(s2n_array_num_elements(cert_and_key->san_names, &len)); - for (uint32_t i = 0; i < len; i++) { - struct s2n_blob *san_name = NULL; - GUARD_AS_POSIX(s2n_array_get(cert_and_key->san_names, i, (void **)&san_name)); - GUARD(s2n_free(san_name)); - } - GUARD_AS_POSIX(s2n_array_free(cert_and_key->san_names)); - cert_and_key->san_names = NULL; - } - - if (cert_and_key->cn_names) { - GUARD_AS_POSIX(s2n_array_num_elements(cert_and_key->cn_names, &len)); - for (uint32_t i = 0; i < len; i++) { - struct s2n_blob *cn_name = NULL; - GUARD_AS_POSIX(s2n_array_get(cert_and_key->cn_names, i, (void **)&cn_name)); - GUARD(s2n_free(cn_name)); - } - GUARD_AS_POSIX(s2n_array_free(cert_and_key->cn_names)); - cert_and_key->cn_names = NULL; - } - - GUARD(s2n_free(&cert_and_key->ocsp_status)); - GUARD(s2n_free(&cert_and_key->sct_list)); - - GUARD(s2n_free_object((uint8_t **)&cert_and_key, sizeof(struct s2n_cert_chain_and_key))); - return 0; -} - -int s2n_send_cert_chain(struct s2n_connection *conn, struct s2n_stuffer *out, struct s2n_cert_chain_and_key *chain_and_key) -{ - notnull_check(conn); - notnull_check(out); - notnull_check(chain_and_key); - struct s2n_cert_chain *chain = chain_and_key->cert_chain; - notnull_check(chain); - struct s2n_cert *cur_cert = chain->head; - notnull_check(cur_cert); - - struct s2n_stuffer_reservation cert_chain_size = {0}; - GUARD(s2n_stuffer_reserve_uint24(out, &cert_chain_size)); - - /* Send certs and extensions (in TLS 1.3) */ - bool first_entry = true; - while (cur_cert) { - notnull_check(cur_cert); - GUARD(s2n_stuffer_write_uint24(out, cur_cert->raw.size)); - GUARD(s2n_stuffer_write_bytes(out, cur_cert->raw.data, cur_cert->raw.size)); - - /* According to https://tools.ietf.org/html/rfc8446#section-4.4.2, - * If an extension applies to the entire chain, it SHOULD be included in - * the first CertificateEntry. - * While the spec allow extensions to be included in other certificate - * entries, only the first matter to use here */ - if (conn->actual_protocol_version >= S2N_TLS13) { - if (first_entry) { - GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_CERTIFICATE, conn, out)); - first_entry = false; - } else { - GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_EMPTY, conn, out)); - } - } - cur_cert = cur_cert->next; - } - - GUARD(s2n_stuffer_write_vector_size(&cert_chain_size)); - - return 0; -} - -int s2n_send_empty_cert_chain(struct s2n_stuffer *out) -{ - notnull_check(out); - GUARD(s2n_stuffer_write_uint24(out, 0)); - return 0; -} - -static int s2n_does_cert_san_match_hostname(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) -{ - notnull_check(chain_and_key); - notnull_check(dns_name); - - struct s2n_array *san_names = chain_and_key->san_names; - uint32_t len = 0; - GUARD_AS_POSIX(s2n_array_num_elements(san_names, &len)); - for (uint32_t i = 0; i < len; i++) { - struct s2n_blob *san_name = NULL; - GUARD_AS_POSIX(s2n_array_get(san_names, i, (void **)&san_name)); - notnull_check(san_name); - if ((dns_name->size == san_name->size) && (strncasecmp((const char *) dns_name->data, (const char *) san_name->data, dns_name->size) == 0)) { - return 1; - } - } - - return 0; -} - -static int s2n_does_cert_cn_match_hostname(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) -{ - notnull_check(chain_and_key); - notnull_check(dns_name); - - struct s2n_array *cn_names = chain_and_key->cn_names; - uint32_t len = 0; - GUARD_AS_POSIX(s2n_array_num_elements(cn_names, &len)); - for (uint32_t i = 0; i < len; i++) { - struct s2n_blob *cn_name = NULL; - GUARD_AS_POSIX(s2n_array_get(cn_names, i, (void **)&cn_name)); - notnull_check(cn_name); - if ((dns_name->size == cn_name->size) && (strncasecmp((const char *) dns_name->data, (const char *) cn_name->data, dns_name->size) == 0)) { - return 1; - } - } - - return 0; -} - -int s2n_cert_chain_and_key_matches_dns_name(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) -{ - uint32_t len = 0; - GUARD_AS_POSIX(s2n_array_num_elements(chain_and_key->san_names, &len)); - if (len > 0) { - if (s2n_does_cert_san_match_hostname(chain_and_key, dns_name)) { - return 1; - } - } else { - /* Per https://tools.ietf.org/html/rfc6125#section-6.4.4 we only will - * consider the CN for matching if no valid DNS entries are provided - * in a SAN. - */ - if (s2n_does_cert_cn_match_hostname(chain_and_key, dns_name)) { - return 1; - } - } - - return 0; -} - -int s2n_cert_chain_and_key_set_ctx(struct s2n_cert_chain_and_key *cert_and_key, void *ctx) -{ - cert_and_key->context = ctx; - return 0; -} - -void *s2n_cert_chain_and_key_get_ctx(struct s2n_cert_chain_and_key *cert_and_key) -{ - return cert_and_key->context; -} - -s2n_pkey_type s2n_cert_chain_and_key_get_pkey_type(struct s2n_cert_chain_and_key *chain_and_key) -{ - return chain_and_key->cert_chain->head->pkey_type; -} - -s2n_cert_private_key *s2n_cert_chain_and_key_get_private_key(struct s2n_cert_chain_and_key *chain_and_key) -{ - ENSURE_REF_PTR(chain_and_key); - return chain_and_key->private_key; -} +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include <s2n.h> +#include <openssl/x509v3.h> +#include <openssl/pem.h> +#include <string.h> +#include <strings.h> + +#include "crypto/s2n_certificate.h" +#include "utils/s2n_array.h" +#include "utils/s2n_safety.h" +#include "utils/s2n_mem.h" + +#include "tls/extensions/s2n_extension_list.h" +#include "tls/s2n_connection.h" + +int s2n_cert_set_cert_type(struct s2n_cert *cert, s2n_pkey_type pkey_type) +{ + notnull_check(cert); + cert->pkey_type = pkey_type; + GUARD(s2n_pkey_setup_for_type(&cert->public_key, pkey_type)); + return 0; +} + +int s2n_create_cert_chain_from_stuffer(struct s2n_cert_chain *cert_chain_out, struct s2n_stuffer *chain_in_stuffer) +{ + DEFER_CLEANUP(struct s2n_stuffer cert_out_stuffer = {0}, s2n_stuffer_free); + GUARD(s2n_stuffer_growable_alloc(&cert_out_stuffer, 2048)); + + struct s2n_cert **insert = &cert_chain_out->head; + uint32_t chain_size = 0; + do { + struct s2n_cert *new_node = NULL; + + if (s2n_stuffer_certificate_from_pem(chain_in_stuffer, &cert_out_stuffer) < 0) { + if (chain_size == 0) { + S2N_ERROR(S2N_ERR_NO_CERTIFICATE_IN_PEM); + } + break; + } + struct s2n_blob mem = {0}; + GUARD(s2n_alloc(&mem, sizeof(struct s2n_cert))); + new_node = (struct s2n_cert *)(void *)mem.data; + + if (s2n_alloc(&new_node->raw, s2n_stuffer_data_available(&cert_out_stuffer)) != S2N_SUCCESS) { + GUARD(s2n_free(&mem)); + S2N_ERROR_PRESERVE_ERRNO(); + } + if (s2n_stuffer_read(&cert_out_stuffer, &new_node->raw) != S2N_SUCCESS) { + GUARD(s2n_free(&mem)); + S2N_ERROR_PRESERVE_ERRNO(); + } + + /* Additional 3 bytes for the length field in the protocol */ + chain_size += new_node->raw.size + 3; + new_node->next = NULL; + *insert = new_node; + insert = &new_node->next; + } while (s2n_stuffer_data_available(chain_in_stuffer)); + + /* Leftover data at this point means one of two things: + * A bug in s2n's PEM parsing OR a malformed PEM in the user's chain. + * Be conservative and fail instead of using a partial chain. + */ + S2N_ERROR_IF(s2n_stuffer_data_available(chain_in_stuffer) > 0, S2N_ERR_INVALID_PEM); + + cert_chain_out->chain_size = chain_size; + + return 0; +} + +int s2n_cert_chain_and_key_set_cert_chain_from_stuffer(struct s2n_cert_chain_and_key *cert_and_key, struct s2n_stuffer *chain_in_stuffer) +{ + return s2n_create_cert_chain_from_stuffer(cert_and_key->cert_chain, chain_in_stuffer); +} + +int s2n_cert_chain_and_key_set_cert_chain(struct s2n_cert_chain_and_key *cert_and_key, const char *cert_chain_pem) +{ + struct s2n_stuffer chain_in_stuffer = {0}; + + /* Turn the chain into a stuffer */ + GUARD(s2n_stuffer_alloc_ro_from_string(&chain_in_stuffer, cert_chain_pem)); + int rc = s2n_cert_chain_and_key_set_cert_chain_from_stuffer(cert_and_key, &chain_in_stuffer); + + GUARD(s2n_stuffer_free(&chain_in_stuffer)); + + return rc; +} + +int s2n_cert_chain_and_key_set_private_key(struct s2n_cert_chain_and_key *cert_and_key, const char *private_key_pem) +{ + DEFER_CLEANUP(struct s2n_stuffer key_in_stuffer = {0}, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer key_out_stuffer = {0}, s2n_stuffer_free); + struct s2n_blob key_blob = {0}; + + GUARD(s2n_pkey_zero_init(cert_and_key->private_key)); + + /* Put the private key pem in a stuffer */ + GUARD(s2n_stuffer_alloc_ro_from_string(&key_in_stuffer, private_key_pem)); + GUARD(s2n_stuffer_growable_alloc(&key_out_stuffer, strlen(private_key_pem))); + + /* Convert pem to asn1 and asn1 to the private key. Handles both PKCS#1 and PKCS#8 formats */ + GUARD(s2n_stuffer_private_key_from_pem(&key_in_stuffer, &key_out_stuffer)); + key_blob.size = s2n_stuffer_data_available(&key_out_stuffer); + key_blob.data = s2n_stuffer_raw_read(&key_out_stuffer, key_blob.size); + notnull_check(key_blob.data); + + /* Get key type and create appropriate key context */ + GUARD(s2n_asn1der_to_private_key(cert_and_key->private_key, &key_blob)); + + return 0; +} + +int s2n_cert_chain_and_key_set_ocsp_data(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length) +{ + notnull_check(chain_and_key); + GUARD(s2n_free(&chain_and_key->ocsp_status)); + if (data && length) { + GUARD(s2n_alloc(&chain_and_key->ocsp_status, length)); + memcpy_check(chain_and_key->ocsp_status.data, data, length); + } + return 0; +} + +int s2n_cert_chain_and_key_set_sct_list(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length) +{ + notnull_check(chain_and_key); + GUARD(s2n_free(&chain_and_key->sct_list)); + if (data && length) { + GUARD(s2n_alloc(&chain_and_key->sct_list, length)); + memcpy_check(chain_and_key->sct_list.data, data, length); + } + return 0; +} + +struct s2n_cert_chain_and_key *s2n_cert_chain_and_key_new(void) +{ + struct s2n_cert_chain_and_key *chain_and_key; + struct s2n_blob chain_and_key_mem, cert_chain_mem, pkey_mem; + + GUARD_PTR(s2n_alloc(&chain_and_key_mem, sizeof(struct s2n_cert_chain_and_key))); + chain_and_key = (struct s2n_cert_chain_and_key *)(void *)chain_and_key_mem.data; + + /* Allocate the memory for the chain and key */ + if (s2n_alloc(&cert_chain_mem, sizeof(struct s2n_cert_chain)) != S2N_SUCCESS) { + goto cleanup; + } + chain_and_key->cert_chain = (struct s2n_cert_chain *)(void *)cert_chain_mem.data; + + if (s2n_alloc(&pkey_mem, sizeof(s2n_cert_private_key)) != S2N_SUCCESS) { + goto cleanup; + } + chain_and_key->private_key = (s2n_cert_private_key *)(void *)pkey_mem.data; + + chain_and_key->cert_chain->head = NULL; + if (s2n_pkey_zero_init(chain_and_key->private_key) != S2N_SUCCESS) { + goto cleanup; + } + memset(&chain_and_key->ocsp_status, 0, sizeof(chain_and_key->ocsp_status)); + memset(&chain_and_key->sct_list, 0, sizeof(chain_and_key->sct_list)); + chain_and_key->cn_names = s2n_array_new(sizeof(struct s2n_blob)); + if (!chain_and_key->cn_names) { + goto cleanup; + } + + chain_and_key->san_names = s2n_array_new(sizeof(struct s2n_blob)); + if (!chain_and_key->san_names) { + goto cleanup; + } + + chain_and_key->context = NULL; + + return chain_and_key; + cleanup: + s2n_free(&pkey_mem); + s2n_free(&cert_chain_mem); + s2n_free(&chain_and_key_mem); + return NULL; +} + +DEFINE_POINTER_CLEANUP_FUNC(GENERAL_NAMES *, GENERAL_NAMES_free); + +int s2n_cert_chain_and_key_load_sans(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert) +{ + notnull_check(chain_and_key->san_names); + + DEFER_CLEANUP(GENERAL_NAMES *san_names = X509_get_ext_d2i(x509_cert, NID_subject_alt_name, NULL, NULL), GENERAL_NAMES_free_pointer); + if (san_names == NULL) { + /* No SAN extension */ + return 0; + } + + const int num_san_names = sk_GENERAL_NAME_num(san_names); + for (int i = 0; i < num_san_names; i++) { + GENERAL_NAME *san_name = sk_GENERAL_NAME_value(san_names, i); + if (!san_name) { + continue; + } + + if (san_name->type == GEN_DNS) { + /* Decoding isn't necessary here since a DNS SAN name is ASCII(type V_ASN1_IA5STRING) */ + unsigned char *san_str = san_name->d.dNSName->data; + const size_t san_str_len = san_name->d.dNSName->length; + struct s2n_blob *san_blob = NULL; + GUARD_AS_POSIX(s2n_array_pushback(chain_and_key->san_names, (void **)&san_blob)); + if (!san_blob) { + S2N_ERROR(S2N_ERR_NULL_SANS); + } + + if (s2n_alloc(san_blob, san_str_len)) { + S2N_ERROR_PRESERVE_ERRNO(); + } + + memcpy_check(san_blob->data, san_str, san_str_len); + san_blob->size = san_str_len; + /* normalize san_blob to lowercase */ + GUARD(s2n_blob_char_to_lower(san_blob)); + } + } + + return 0; +} + +/* Parse CN names from the Subject of the leaf certificate. Technically there can by multiple CNs + * in the Subject but practically very few certificates in the wild will have more than one CN. + * Since the data for this certificate is coming from the application and not from an untrusted + * source, we will try our best to parse all of the CNs. + * + * A recent CAB thread proposed removing support for multiple CNs: + * https://cabforum.org/pipermail/public/2016-April/007242.html + */ + +DEFINE_POINTER_CLEANUP_FUNC(unsigned char *, OPENSSL_free); + +int s2n_cert_chain_and_key_load_cns(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert) +{ + notnull_check(chain_and_key->cn_names); + + X509_NAME *subject = X509_get_subject_name(x509_cert); + if (!subject) { + return 0; + } + + int lastpos = -1; + while((lastpos = X509_NAME_get_index_by_NID(subject, NID_commonName, lastpos)) >= 0) { + X509_NAME_ENTRY *name_entry = X509_NAME_get_entry(subject, lastpos); + if (!name_entry) { + continue; + } + + ASN1_STRING *asn1_str = X509_NAME_ENTRY_get_data(name_entry); + if (!asn1_str) { + continue; + } + + /* We need to try and decode the CN since it may be encoded as unicode with a + * direct ASCII equivalent. Any non ASCII bytes in the string will fail later when we + * actually compare hostnames. + */ + DEFER_CLEANUP(unsigned char *utf8_str, OPENSSL_free_pointer); + const int utf8_out_len = ASN1_STRING_to_UTF8(&utf8_str, asn1_str); + if (utf8_out_len < 0) { + /* On failure, ASN1_STRING_to_UTF8 does not allocate any memory */ + continue; + } else if (utf8_out_len == 0) { + /* We still need to free memory here see https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7521 */ + OPENSSL_free(utf8_str); + } else { + struct s2n_blob *cn_name = NULL; + GUARD_AS_POSIX(s2n_array_pushback(chain_and_key->cn_names, (void **)&cn_name)); + if (cn_name == NULL) { + S2N_ERROR(S2N_ERR_NULL_CN_NAME); + } + + if (s2n_alloc(cn_name, utf8_out_len) < 0) { + S2N_ERROR_PRESERVE_ERRNO(); + } + memcpy_check(cn_name->data, utf8_str, utf8_out_len); + cn_name->size = utf8_out_len; + /* normalize cn_name to lowercase */ + GUARD(s2n_blob_char_to_lower(cn_name)); + } + } + + return 0; +} + +static int s2n_cert_chain_and_key_set_names(struct s2n_cert_chain_and_key *chain_and_key, struct s2n_blob *leaf_bytes) +{ + const unsigned char *leaf_der = leaf_bytes->data; + X509 *cert = d2i_X509(NULL, &leaf_der, leaf_bytes->size); + if (!cert) { + S2N_ERROR(S2N_ERR_INVALID_PEM); + } + + GUARD(s2n_cert_chain_and_key_load_sans(chain_and_key, cert)); + /* For current use cases, we *could* avoid populating the common names if any sans were loaded in + * s2n_cert_chain_and_key_load_sans. Let's unconditionally populate this field to avoid surprises + * in the future. + */ + GUARD(s2n_cert_chain_and_key_load_cns(chain_and_key, cert)); + + X509_free(cert); + return 0; +} + +int s2n_cert_chain_and_key_load_pem(struct s2n_cert_chain_and_key *chain_and_key, const char *chain_pem, const char *private_key_pem) +{ + notnull_check(chain_and_key); + + GUARD(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, chain_pem)); + GUARD(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); + + /* Parse the leaf cert for the public key and certificate type */ + DEFER_CLEANUP(struct s2n_pkey public_key = {0}, s2n_pkey_free); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + GUARD(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); + S2N_ERROR_IF(pkey_type == S2N_PKEY_TYPE_UNKNOWN, S2N_ERR_CERT_TYPE_UNSUPPORTED); + GUARD(s2n_cert_set_cert_type(chain_and_key->cert_chain->head, pkey_type)); + + /* Validate the leaf cert's public key matches the provided private key */ + GUARD(s2n_pkey_match(&public_key, chain_and_key->private_key)); + + /* Populate name information from the SAN/CN for the leaf certificate */ + GUARD(s2n_cert_chain_and_key_set_names(chain_and_key, &chain_and_key->cert_chain->head->raw)); + + return 0; +} + +int s2n_cert_chain_and_key_free(struct s2n_cert_chain_and_key *cert_and_key) +{ + if (cert_and_key == NULL) { + return 0; + } + + /* Walk the chain and free the certs */ + if (cert_and_key->cert_chain) { + struct s2n_cert *node = cert_and_key->cert_chain->head; + while (node) { + /* Free the cert */ + GUARD(s2n_free(&node->raw)); + /* update head so it won't point to freed memory */ + cert_and_key->cert_chain->head = node->next; + /* Free the node */ + GUARD(s2n_free_object((uint8_t **)&node, sizeof(struct s2n_cert))); + node = cert_and_key->cert_chain->head; + } + + GUARD(s2n_free_object((uint8_t **)&cert_and_key->cert_chain, sizeof(struct s2n_cert_chain))); + } + + if (cert_and_key->private_key) { + GUARD(s2n_pkey_free(cert_and_key->private_key)); + GUARD(s2n_free_object((uint8_t **)&cert_and_key->private_key, sizeof(s2n_cert_private_key))); + } + + uint32_t len = 0; + + if (cert_and_key->san_names) { + GUARD_AS_POSIX(s2n_array_num_elements(cert_and_key->san_names, &len)); + for (uint32_t i = 0; i < len; i++) { + struct s2n_blob *san_name = NULL; + GUARD_AS_POSIX(s2n_array_get(cert_and_key->san_names, i, (void **)&san_name)); + GUARD(s2n_free(san_name)); + } + GUARD_AS_POSIX(s2n_array_free(cert_and_key->san_names)); + cert_and_key->san_names = NULL; + } + + if (cert_and_key->cn_names) { + GUARD_AS_POSIX(s2n_array_num_elements(cert_and_key->cn_names, &len)); + for (uint32_t i = 0; i < len; i++) { + struct s2n_blob *cn_name = NULL; + GUARD_AS_POSIX(s2n_array_get(cert_and_key->cn_names, i, (void **)&cn_name)); + GUARD(s2n_free(cn_name)); + } + GUARD_AS_POSIX(s2n_array_free(cert_and_key->cn_names)); + cert_and_key->cn_names = NULL; + } + + GUARD(s2n_free(&cert_and_key->ocsp_status)); + GUARD(s2n_free(&cert_and_key->sct_list)); + + GUARD(s2n_free_object((uint8_t **)&cert_and_key, sizeof(struct s2n_cert_chain_and_key))); + return 0; +} + +int s2n_send_cert_chain(struct s2n_connection *conn, struct s2n_stuffer *out, struct s2n_cert_chain_and_key *chain_and_key) +{ + notnull_check(conn); + notnull_check(out); + notnull_check(chain_and_key); + struct s2n_cert_chain *chain = chain_and_key->cert_chain; + notnull_check(chain); + struct s2n_cert *cur_cert = chain->head; + notnull_check(cur_cert); + + struct s2n_stuffer_reservation cert_chain_size = {0}; + GUARD(s2n_stuffer_reserve_uint24(out, &cert_chain_size)); + + /* Send certs and extensions (in TLS 1.3) */ + bool first_entry = true; + while (cur_cert) { + notnull_check(cur_cert); + GUARD(s2n_stuffer_write_uint24(out, cur_cert->raw.size)); + GUARD(s2n_stuffer_write_bytes(out, cur_cert->raw.data, cur_cert->raw.size)); + + /* According to https://tools.ietf.org/html/rfc8446#section-4.4.2, + * If an extension applies to the entire chain, it SHOULD be included in + * the first CertificateEntry. + * While the spec allow extensions to be included in other certificate + * entries, only the first matter to use here */ + if (conn->actual_protocol_version >= S2N_TLS13) { + if (first_entry) { + GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_CERTIFICATE, conn, out)); + first_entry = false; + } else { + GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_EMPTY, conn, out)); + } + } + cur_cert = cur_cert->next; + } + + GUARD(s2n_stuffer_write_vector_size(&cert_chain_size)); + + return 0; +} + +int s2n_send_empty_cert_chain(struct s2n_stuffer *out) +{ + notnull_check(out); + GUARD(s2n_stuffer_write_uint24(out, 0)); + return 0; +} + +static int s2n_does_cert_san_match_hostname(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) +{ + notnull_check(chain_and_key); + notnull_check(dns_name); + + struct s2n_array *san_names = chain_and_key->san_names; + uint32_t len = 0; + GUARD_AS_POSIX(s2n_array_num_elements(san_names, &len)); + for (uint32_t i = 0; i < len; i++) { + struct s2n_blob *san_name = NULL; + GUARD_AS_POSIX(s2n_array_get(san_names, i, (void **)&san_name)); + notnull_check(san_name); + if ((dns_name->size == san_name->size) && (strncasecmp((const char *) dns_name->data, (const char *) san_name->data, dns_name->size) == 0)) { + return 1; + } + } + + return 0; +} + +static int s2n_does_cert_cn_match_hostname(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) +{ + notnull_check(chain_and_key); + notnull_check(dns_name); + + struct s2n_array *cn_names = chain_and_key->cn_names; + uint32_t len = 0; + GUARD_AS_POSIX(s2n_array_num_elements(cn_names, &len)); + for (uint32_t i = 0; i < len; i++) { + struct s2n_blob *cn_name = NULL; + GUARD_AS_POSIX(s2n_array_get(cn_names, i, (void **)&cn_name)); + notnull_check(cn_name); + if ((dns_name->size == cn_name->size) && (strncasecmp((const char *) dns_name->data, (const char *) cn_name->data, dns_name->size) == 0)) { + return 1; + } + } + + return 0; +} + +int s2n_cert_chain_and_key_matches_dns_name(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) +{ + uint32_t len = 0; + GUARD_AS_POSIX(s2n_array_num_elements(chain_and_key->san_names, &len)); + if (len > 0) { + if (s2n_does_cert_san_match_hostname(chain_and_key, dns_name)) { + return 1; + } + } else { + /* Per https://tools.ietf.org/html/rfc6125#section-6.4.4 we only will + * consider the CN for matching if no valid DNS entries are provided + * in a SAN. + */ + if (s2n_does_cert_cn_match_hostname(chain_and_key, dns_name)) { + return 1; + } + } + + return 0; +} + +int s2n_cert_chain_and_key_set_ctx(struct s2n_cert_chain_and_key *cert_and_key, void *ctx) +{ + cert_and_key->context = ctx; + return 0; +} + +void *s2n_cert_chain_and_key_get_ctx(struct s2n_cert_chain_and_key *cert_and_key) +{ + return cert_and_key->context; +} + +s2n_pkey_type s2n_cert_chain_and_key_get_pkey_type(struct s2n_cert_chain_and_key *chain_and_key) +{ + return chain_and_key->cert_chain->head->pkey_type; +} + +s2n_cert_private_key *s2n_cert_chain_and_key_get_private_key(struct s2n_cert_chain_and_key *chain_and_key) +{ + ENSURE_REF_PTR(chain_and_key); + return chain_and_key->private_key; +} |