123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- /*
- * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
- #include <stdio.h>
- #include "ssl_local.h"
- #include "internal/e_os.h"
- #include "internal/refcount.h"
- size_t ossl_calculate_comp_expansion(int alg, size_t length)
- {
- size_t ret;
- /*
- * Uncompressibility expansion:
- * ZLIB: N + 11 + 5 * (N >> 14)
- * Brotli: per RFC7932: N + 5 + 3 * (N >> 16)
- * ZSTD: N + 4 + 14 + 3 * (N >> 17) + 4
- */
-
- switch (alg) {
- case TLSEXT_comp_cert_zlib:
- ret = length + 11 + 5 * (length >> 14);
- break;
- case TLSEXT_comp_cert_brotli:
- ret = length + 5 + 3 * (length >> 16);
- break;
- case TLSEXT_comp_cert_zstd:
- ret = length + 22 + 3 * (length >> 17);
- break;
- default:
- return 0;
- }
- /* Check for overflow */
- if (ret < length)
- return 0;
- return ret;
- }
- int ossl_comp_has_alg(int a)
- {
- #ifndef OPENSSL_NO_COMP_ALG
- /* 0 means "any" algorithm */
- if ((a == 0 || a == TLSEXT_comp_cert_brotli) && BIO_f_brotli() != NULL)
- return 1;
- if ((a == 0 || a == TLSEXT_comp_cert_zstd) && BIO_f_zstd() != NULL)
- return 1;
- if ((a == 0 || a == TLSEXT_comp_cert_zlib) && BIO_f_zlib() != NULL)
- return 1;
- #endif
- return 0;
- }
- /* New operation Helper routine */
- #ifndef OPENSSL_NO_COMP_ALG
- static OSSL_COMP_CERT *OSSL_COMP_CERT_new(unsigned char *data, size_t len, size_t orig_len, int alg)
- {
- OSSL_COMP_CERT *ret = NULL;
- if (!ossl_comp_has_alg(alg)
- || data == NULL
- || (ret = OPENSSL_zalloc(sizeof(*ret))) == NULL
- || !CRYPTO_NEW_REF(&ret->references, 1))
- goto err;
- ret->data = data;
- ret->len = len;
- ret->orig_len = orig_len;
- ret->alg = alg;
- return ret;
- err:
- ERR_raise(ERR_LIB_SSL, ERR_R_MALLOC_FAILURE);
- OPENSSL_free(data);
- OPENSSL_free(ret);
- return NULL;
- }
- __owur static OSSL_COMP_CERT *OSSL_COMP_CERT_from_compressed_data(unsigned char *data, size_t len,
- size_t orig_len, int alg)
- {
- return OSSL_COMP_CERT_new(OPENSSL_memdup(data, len), len, orig_len, alg);
- }
- __owur static OSSL_COMP_CERT *OSSL_COMP_CERT_from_uncompressed_data(unsigned char *data, size_t len,
- int alg)
- {
- OSSL_COMP_CERT *ret = NULL;
- size_t max_length;
- int comp_length;
- COMP_METHOD *method;
- unsigned char *comp_data = NULL;
- COMP_CTX *comp_ctx = NULL;
- switch (alg) {
- case TLSEXT_comp_cert_brotli:
- method = COMP_brotli_oneshot();
- break;
- case TLSEXT_comp_cert_zlib:
- method = COMP_zlib_oneshot();
- break;
- case TLSEXT_comp_cert_zstd:
- method = COMP_zstd_oneshot();
- break;
- default:
- goto err;
- }
- if ((max_length = ossl_calculate_comp_expansion(alg, len)) == 0
- || method == NULL
- || (comp_ctx = COMP_CTX_new(method)) == NULL
- || (comp_data = OPENSSL_zalloc(max_length)) == NULL)
- goto err;
- comp_length = COMP_compress_block(comp_ctx, comp_data, max_length, data, len);
- if (comp_length <= 0)
- goto err;
- ret = OSSL_COMP_CERT_new(comp_data, comp_length, len, alg);
- comp_data = NULL;
- err:
- OPENSSL_free(comp_data);
- COMP_CTX_free(comp_ctx);
- return ret;
- }
- void OSSL_COMP_CERT_free(OSSL_COMP_CERT *cc)
- {
- int i;
- if (cc == NULL)
- return;
- CRYPTO_DOWN_REF(&cc->references, &i);
- REF_PRINT_COUNT("OSSL_COMP_CERT", cc);
- if (i > 0)
- return;
- REF_ASSERT_ISNT(i < 0);
- OPENSSL_free(cc->data);
- CRYPTO_FREE_REF(&cc->references);
- OPENSSL_free(cc);
- }
- int OSSL_COMP_CERT_up_ref(OSSL_COMP_CERT *cc)
- {
- int i;
- if (CRYPTO_UP_REF(&cc->references, &i) <= 0)
- return 0;
- REF_PRINT_COUNT("OSSL_COMP_CERT", cc);
- REF_ASSERT_ISNT(i < 2);
- return ((i > 1) ? 1 : 0);
- }
- static int ssl_set_cert_comp_pref(int *prefs, int *algs, size_t len)
- {
- size_t j = 0;
- size_t i;
- int found = 0;
- int already_set[TLSEXT_comp_cert_limit];
- int tmp_prefs[TLSEXT_comp_cert_limit];
- /* Note that |len| is the number of |algs| elements */
- /* clear all algorithms */
- if (len == 0 || algs == NULL) {
- memset(prefs, 0, sizeof(tmp_prefs));
- return 1;
- }
- /* This will 0-terminate the array */
- memset(tmp_prefs, 0, sizeof(tmp_prefs));
- memset(already_set, 0, sizeof(already_set));
- /* Include only those algorithms we support, ignoring duplicates and unknowns */
- for (i = 0; i < len; i++) {
- if (algs[i] != 0 && ossl_comp_has_alg(algs[i])) {
- /* Check for duplicate */
- if (already_set[algs[i]])
- return 0;
- tmp_prefs[j++] = algs[i];
- already_set[algs[i]] = 1;
- found = 1;
- }
- }
- if (found)
- memcpy(prefs, tmp_prefs, sizeof(tmp_prefs));
- return found;
- }
- static size_t ssl_get_cert_to_compress(SSL *ssl, CERT_PKEY *cpk, unsigned char **data)
- {
- SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
- WPACKET tmppkt;
- BUF_MEM buf = { 0 };
- size_t ret = 0;
- if (sc == NULL
- || cpk == NULL
- || !sc->server
- || !SSL_in_before(ssl))
- return 0;
- /* Use the |tmppkt| for the to-be-compressed data */
- if (!WPACKET_init(&tmppkt, &buf))
- goto out;
- /* no context present, add 0-length context */
- if (!WPACKET_put_bytes_u8(&tmppkt, 0))
- goto out;
- /*
- * ssl3_output_cert_chain() may generate an SSLfatal() error,
- * for this case, we want to ignore it, argument for_comp = 1
- */
- if (!ssl3_output_cert_chain(sc, &tmppkt, cpk, 1))
- goto out;
- WPACKET_get_total_written(&tmppkt, &ret);
- out:
- WPACKET_cleanup(&tmppkt);
- if (ret != 0 && data != NULL)
- *data = (unsigned char *)buf.data;
- else
- OPENSSL_free(buf.data);
- return ret;
- }
- static int ssl_compress_one_cert(SSL *ssl, CERT_PKEY *cpk, int alg)
- {
- unsigned char *cert_data = NULL;
- OSSL_COMP_CERT *comp_cert = NULL;
- size_t length;
- if (cpk == NULL
- || alg == TLSEXT_comp_cert_none
- || !ossl_comp_has_alg(alg))
- return 0;
- if ((length = ssl_get_cert_to_compress(ssl, cpk, &cert_data)) == 0)
- return 0;
- comp_cert = OSSL_COMP_CERT_from_uncompressed_data(cert_data, length, alg);
- OPENSSL_free(cert_data);
- if (comp_cert == NULL)
- return 0;
- OSSL_COMP_CERT_free(cpk->comp_cert[alg]);
- cpk->comp_cert[alg] = comp_cert;
- return 1;
- }
- /* alg_in can be 0, meaning any/all algorithms */
- static int ssl_compress_certs(SSL *ssl, CERT_PKEY *cpks, int alg_in)
- {
- SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
- int i;
- int j;
- int alg;
- int count = 0;
- if (sc == NULL
- || cpks == NULL
- || !ossl_comp_has_alg(alg_in))
- return 0;
- /* Look through the preferences to see what we have */
- for (i = 0; i < TLSEXT_comp_cert_limit; i++) {
- /*
- * alg = 0 means compress for everything, but only for algorithms enabled
- * alg != 0 means compress for that algorithm if enabled
- */
- alg = sc->cert_comp_prefs[i];
- if ((alg_in == 0 && alg != TLSEXT_comp_cert_none)
- || (alg_in != 0 && alg == alg_in)) {
- for (j = 0; j < SSL_PKEY_NUM; j++) {
- /* No cert, move on */
- if (cpks[j].x509 == NULL)
- continue;
- if (!ssl_compress_one_cert(ssl, &cpks[j], alg))
- return 0;
- /* if the cert expanded, set the value in the CERT_PKEY to NULL */
- if (cpks[j].comp_cert[alg]->len >= cpks[j].comp_cert[alg]->orig_len) {
- OSSL_COMP_CERT_free(cpks[j].comp_cert[alg]);
- cpks[j].comp_cert[alg] = NULL;
- } else {
- count++;
- }
- }
- }
- }
- return (count > 0);
- }
- static size_t ssl_get_compressed_cert(SSL *ssl, CERT_PKEY *cpk, int alg, unsigned char **data,
- size_t *orig_len)
- {
- SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
- size_t cert_len = 0;
- size_t comp_len = 0;
- unsigned char *cert_data = NULL;
- OSSL_COMP_CERT *comp_cert = NULL;
- if (sc == NULL
- || cpk == NULL
- || data == NULL
- || orig_len == NULL
- || !sc->server
- || !SSL_in_before(ssl)
- || !ossl_comp_has_alg(alg))
- return 0;
- if ((cert_len = ssl_get_cert_to_compress(ssl, cpk, &cert_data)) == 0)
- goto err;
- comp_cert = OSSL_COMP_CERT_from_uncompressed_data(cert_data, cert_len, alg);
- OPENSSL_free(cert_data);
- if (comp_cert == NULL)
- goto err;
- comp_len = comp_cert->len;
- *orig_len = comp_cert->orig_len;
- *data = comp_cert->data;
- comp_cert->data = NULL;
- err:
- OSSL_COMP_CERT_free(comp_cert);
- return comp_len;
- }
- static int ossl_set1_compressed_cert(CERT *cert, int algorithm,
- unsigned char *comp_data, size_t comp_length,
- size_t orig_length)
- {
- OSSL_COMP_CERT *comp_cert;
- /* No explicit cert set */
- if (cert == NULL || cert->key == NULL)
- return 0;
- comp_cert = OSSL_COMP_CERT_from_compressed_data(comp_data, comp_length,
- orig_length, algorithm);
- if (comp_cert == NULL)
- return 0;
- OSSL_COMP_CERT_free(cert->key->comp_cert[algorithm]);
- cert->key->comp_cert[algorithm] = comp_cert;
- return 1;
- }
- #endif
- /*-
- * Public API
- */
- int SSL_CTX_set1_cert_comp_preference(SSL_CTX *ctx, int *algs, size_t len)
- {
- #ifndef OPENSSL_NO_COMP_ALG
- return ssl_set_cert_comp_pref(ctx->cert_comp_prefs, algs, len);
- #else
- return 0;
- #endif
- }
- int SSL_set1_cert_comp_preference(SSL *ssl, int *algs, size_t len)
- {
- #ifndef OPENSSL_NO_COMP_ALG
- SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
- if (sc == NULL)
- return 0;
- return ssl_set_cert_comp_pref(sc->cert_comp_prefs, algs, len);
- #else
- return 0;
- #endif
- }
- int SSL_compress_certs(SSL *ssl, int alg)
- {
- #ifndef OPENSSL_NO_COMP_ALG
- SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
- if (sc == NULL || sc->cert == NULL)
- return 0;
- return ssl_compress_certs(ssl, sc->cert->pkeys, alg);
- #endif
- return 0;
- }
- int SSL_CTX_compress_certs(SSL_CTX *ctx, int alg)
- {
- int ret = 0;
- #ifndef OPENSSL_NO_COMP_ALG
- SSL *new = SSL_new(ctx);
- if (new == NULL)
- return 0;
- ret = ssl_compress_certs(new, ctx->cert->pkeys, alg);
- SSL_free(new);
- #endif
- return ret;
- }
- size_t SSL_get1_compressed_cert(SSL *ssl, int alg, unsigned char **data, size_t *orig_len)
- {
- #ifndef OPENSSL_NO_COMP_ALG
- SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
- CERT_PKEY *cpk = NULL;
- if (sc->cert != NULL)
- cpk = sc->cert->key;
- else
- cpk = ssl->ctx->cert->key;
- return ssl_get_compressed_cert(ssl, cpk, alg, data, orig_len);
- #else
- return 0;
- #endif
- }
- size_t SSL_CTX_get1_compressed_cert(SSL_CTX *ctx, int alg, unsigned char **data, size_t *orig_len)
- {
- #ifndef OPENSSL_NO_COMP_ALG
- size_t ret;
- SSL *new = SSL_new(ctx);
- ret = ssl_get_compressed_cert(new, ctx->cert->key, alg, data, orig_len);
- SSL_free(new);
- return ret;
- #else
- return 0;
- #endif
- }
- int SSL_CTX_set1_compressed_cert(SSL_CTX *ctx, int algorithm, unsigned char *comp_data,
- size_t comp_length, size_t orig_length)
- {
- #ifndef OPENSSL_NO_COMP_ALG
- return ossl_set1_compressed_cert(ctx->cert, algorithm, comp_data, comp_length, orig_length);
- #else
- return 0;
- #endif
- }
- int SSL_set1_compressed_cert(SSL *ssl, int algorithm, unsigned char *comp_data,
- size_t comp_length, size_t orig_length)
- {
- #ifndef OPENSSL_NO_COMP_ALG
- SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
- /* Cannot set a pre-compressed certificate on a client */
- if (sc == NULL || !sc->server)
- return 0;
- return ossl_set1_compressed_cert(sc->cert, algorithm, comp_data, comp_length, orig_length);
- #else
- return 0;
- #endif
- }
|