int s2n_cert_chain_and_key_load_cns()

in crypto/s2n_certificate.c [263:323]


int s2n_cert_chain_and_key_load_cns(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert)
{
    POSIX_ENSURE_REF(chain_and_key->cn_names);
    POSIX_ENSURE_REF(x509_cert);

    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.
         *
         * `ASN1_STRING_to_UTF8` allocates in both the success case and in the zero return case, but
         * not in the failure case (negative return value). Therefore, we use `ZERO_TO_DISABLE_DEFER_CLEANUP`
         * in the failure case to prevent double-freeing `utf8_str`. For the zero and success cases, `utf8_str`
         * will be freed by the `DEFER_CLEANUP`.
         */
        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 */
            ZERO_TO_DISABLE_DEFER_CLEANUP(utf8_str);
            continue;
        } else if (utf8_out_len == 0) {
            /* We still need to free memory for this case, so let the DEFER_CLEANUP free it
             * see https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7521 and
             * https://security.archlinux.org/CVE-2017-7521
            */
        } else {
            struct s2n_blob *cn_name = NULL;
            POSIX_GUARD_RESULT(s2n_array_pushback(chain_and_key->cn_names, (void **) &cn_name));
            if (cn_name == NULL) {
                POSIX_BAIL(S2N_ERR_NULL_CN_NAME);
            }

            if (s2n_alloc(cn_name, utf8_out_len) < 0) {
                S2N_ERROR_PRESERVE_ERRNO();
            }
            POSIX_CHECKED_MEMCPY(cn_name->data, utf8_str, utf8_out_len);
            cn_name->size = utf8_out_len;
            /* normalize cn_name to lowercase */
            POSIX_GUARD(s2n_blob_char_to_lower(cn_name));
        }
    }

    return 0;
}