1 /* 2 * Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 #include <stdio.h> 11 #include "ssl_local.h" 12 #include "internal/e_os.h" 13 #include "internal/refcount.h" 14 #include "internal/ssl_unwrap.h" 15 16 size_t ossl_calculate_comp_expansion(int alg, size_t length) 17 { 18 size_t ret; 19 /* 20 * Uncompressibility expansion: 21 * ZLIB: N + 11 + 5 * (N >> 14) 22 * Brotli: per RFC7932: N + 5 + 3 * (N >> 16) 23 * ZSTD: N + 4 + 14 + 3 * (N >> 17) + 4 24 */ 25 26 switch (alg) { 27 case TLSEXT_comp_cert_zlib: 28 ret = length + 11 + 5 * (length >> 14); 29 break; 30 case TLSEXT_comp_cert_brotli: 31 ret = length + 5 + 3 * (length >> 16); 32 break; 33 case TLSEXT_comp_cert_zstd: 34 ret = length + 22 + 3 * (length >> 17); 35 break; 36 default: 37 return 0; 38 } 39 /* Check for overflow */ 40 if (ret < length) 41 return 0; 42 return ret; 43 } 44 45 int ossl_comp_has_alg(int a) 46 { 47 #ifndef OPENSSL_NO_COMP_ALG 48 /* 0 means "any" algorithm */ 49 if ((a == 0 || a == TLSEXT_comp_cert_brotli) && BIO_f_brotli() != NULL) 50 return 1; 51 if ((a == 0 || a == TLSEXT_comp_cert_zstd) && BIO_f_zstd() != NULL) 52 return 1; 53 if ((a == 0 || a == TLSEXT_comp_cert_zlib) && BIO_f_zlib() != NULL) 54 return 1; 55 #endif 56 return 0; 57 } 58 59 /* New operation Helper routine */ 60 #ifndef OPENSSL_NO_COMP_ALG 61 static OSSL_COMP_CERT *OSSL_COMP_CERT_new(unsigned char *data, size_t len, size_t orig_len, int alg) 62 { 63 OSSL_COMP_CERT *ret = NULL; 64 65 if (!ossl_comp_has_alg(alg) 66 || data == NULL 67 || (ret = OPENSSL_zalloc(sizeof(*ret))) == NULL 68 || !CRYPTO_NEW_REF(&ret->references, 1)) 69 goto err; 70 71 ret->data = data; 72 ret->len = len; 73 ret->orig_len = orig_len; 74 ret->alg = alg; 75 return ret; 76 err: 77 ERR_raise(ERR_LIB_SSL, ERR_R_MALLOC_FAILURE); 78 OPENSSL_free(data); 79 OPENSSL_free(ret); 80 return NULL; 81 } 82 83 __owur static OSSL_COMP_CERT *OSSL_COMP_CERT_from_compressed_data(unsigned char *data, size_t len, 84 size_t orig_len, int alg) 85 { 86 return OSSL_COMP_CERT_new(OPENSSL_memdup(data, len), len, orig_len, alg); 87 } 88 89 __owur static OSSL_COMP_CERT *OSSL_COMP_CERT_from_uncompressed_data(unsigned char *data, size_t len, 90 int alg) 91 { 92 OSSL_COMP_CERT *ret = NULL; 93 size_t max_length; 94 int comp_length; 95 COMP_METHOD *method; 96 unsigned char *comp_data = NULL; 97 COMP_CTX *comp_ctx = NULL; 98 99 switch (alg) { 100 case TLSEXT_comp_cert_brotli: 101 method = COMP_brotli_oneshot(); 102 break; 103 case TLSEXT_comp_cert_zlib: 104 method = COMP_zlib_oneshot(); 105 break; 106 case TLSEXT_comp_cert_zstd: 107 method = COMP_zstd_oneshot(); 108 break; 109 default: 110 goto err; 111 } 112 113 if ((max_length = ossl_calculate_comp_expansion(alg, len)) == 0 114 || method == NULL 115 || (comp_ctx = COMP_CTX_new(method)) == NULL 116 || (comp_data = OPENSSL_zalloc(max_length)) == NULL) 117 goto err; 118 119 comp_length = COMP_compress_block(comp_ctx, comp_data, max_length, data, len); 120 if (comp_length <= 0) 121 goto err; 122 123 ret = OSSL_COMP_CERT_new(comp_data, comp_length, len, alg); 124 comp_data = NULL; 125 126 err: 127 OPENSSL_free(comp_data); 128 COMP_CTX_free(comp_ctx); 129 return ret; 130 } 131 132 void OSSL_COMP_CERT_free(OSSL_COMP_CERT *cc) 133 { 134 int i; 135 136 if (cc == NULL) 137 return; 138 139 CRYPTO_DOWN_REF(&cc->references, &i); 140 REF_PRINT_COUNT("OSSL_COMP_CERT", i, cc); 141 if (i > 0) 142 return; 143 REF_ASSERT_ISNT(i < 0); 144 145 OPENSSL_free(cc->data); 146 CRYPTO_FREE_REF(&cc->references); 147 OPENSSL_free(cc); 148 } 149 int OSSL_COMP_CERT_up_ref(OSSL_COMP_CERT *cc) 150 { 151 int i; 152 153 if (CRYPTO_UP_REF(&cc->references, &i) <= 0) 154 return 0; 155 156 REF_PRINT_COUNT("OSSL_COMP_CERT", i, cc); 157 REF_ASSERT_ISNT(i < 2); 158 return ((i > 1) ? 1 : 0); 159 } 160 161 static int ssl_set_cert_comp_pref(int *prefs, int *algs, size_t len) 162 { 163 size_t j = 0; 164 size_t i; 165 int found = 0; 166 int already_set[TLSEXT_comp_cert_limit]; 167 int tmp_prefs[TLSEXT_comp_cert_limit]; 168 169 /* Note that |len| is the number of |algs| elements */ 170 /* clear all algorithms */ 171 if (len == 0 || algs == NULL) { 172 memset(prefs, 0, sizeof(tmp_prefs)); 173 return 1; 174 } 175 176 /* This will 0-terminate the array */ 177 memset(tmp_prefs, 0, sizeof(tmp_prefs)); 178 memset(already_set, 0, sizeof(already_set)); 179 /* Include only those algorithms we support, ignoring duplicates and unknowns */ 180 for (i = 0; i < len; i++) { 181 if (algs[i] != 0 && ossl_comp_has_alg(algs[i])) { 182 /* Check for duplicate */ 183 if (already_set[algs[i]]) 184 return 0; 185 tmp_prefs[j++] = algs[i]; 186 already_set[algs[i]] = 1; 187 found = 1; 188 } 189 } 190 if (found) 191 memcpy(prefs, tmp_prefs, sizeof(tmp_prefs)); 192 return found; 193 } 194 195 static size_t ssl_get_cert_to_compress(SSL *ssl, CERT_PKEY *cpk, unsigned char **data) 196 { 197 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl); 198 WPACKET tmppkt; 199 BUF_MEM buf = { 0 }; 200 size_t ret = 0; 201 202 if (sc == NULL 203 || cpk == NULL 204 || !sc->server 205 || !SSL_in_before(ssl)) 206 return 0; 207 208 /* Use the |tmppkt| for the to-be-compressed data */ 209 if (!WPACKET_init(&tmppkt, &buf)) 210 goto out; 211 212 /* no context present, add 0-length context */ 213 if (!WPACKET_put_bytes_u8(&tmppkt, 0)) 214 goto out; 215 216 /* 217 * ssl3_output_cert_chain() may generate an SSLfatal() error, 218 * for this case, we want to ignore it, argument for_comp = 1 219 */ 220 if (!ssl3_output_cert_chain(sc, &tmppkt, cpk, 1)) 221 goto out; 222 WPACKET_get_total_written(&tmppkt, &ret); 223 224 out: 225 WPACKET_cleanup(&tmppkt); 226 if (ret != 0 && data != NULL) 227 *data = (unsigned char *)buf.data; 228 else 229 OPENSSL_free(buf.data); 230 return ret; 231 } 232 233 static int ssl_compress_one_cert(SSL *ssl, CERT_PKEY *cpk, int alg) 234 { 235 unsigned char *cert_data = NULL; 236 OSSL_COMP_CERT *comp_cert = NULL; 237 size_t length; 238 239 if (cpk == NULL 240 || alg == TLSEXT_comp_cert_none 241 || !ossl_comp_has_alg(alg)) 242 return 0; 243 244 if ((length = ssl_get_cert_to_compress(ssl, cpk, &cert_data)) == 0) 245 return 0; 246 comp_cert = OSSL_COMP_CERT_from_uncompressed_data(cert_data, length, alg); 247 OPENSSL_free(cert_data); 248 if (comp_cert == NULL) 249 return 0; 250 251 OSSL_COMP_CERT_free(cpk->comp_cert[alg]); 252 cpk->comp_cert[alg] = comp_cert; 253 return 1; 254 } 255 256 /* alg_in can be 0, meaning any/all algorithms */ 257 static int ssl_compress_certs(SSL *ssl, CERT_PKEY *cpks, int alg_in) 258 { 259 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl); 260 int i; 261 int j; 262 int alg; 263 int count = 0; 264 265 if (sc == NULL 266 || cpks == NULL 267 || !ossl_comp_has_alg(alg_in)) 268 return 0; 269 270 /* Look through the preferences to see what we have */ 271 for (i = 0; i < TLSEXT_comp_cert_limit; i++) { 272 /* 273 * alg = 0 means compress for everything, but only for algorithms enabled 274 * alg != 0 means compress for that algorithm if enabled 275 */ 276 alg = sc->cert_comp_prefs[i]; 277 if ((alg_in == 0 && alg != TLSEXT_comp_cert_none) 278 || (alg_in != 0 && alg == alg_in)) { 279 280 for (j = 0; j < SSL_PKEY_NUM; j++) { 281 /* No cert, move on */ 282 if (cpks[j].x509 == NULL) 283 continue; 284 285 if (!ssl_compress_one_cert(ssl, &cpks[j], alg)) 286 return 0; 287 288 /* if the cert expanded, set the value in the CERT_PKEY to NULL */ 289 if (cpks[j].comp_cert[alg]->len >= cpks[j].comp_cert[alg]->orig_len) { 290 OSSL_COMP_CERT_free(cpks[j].comp_cert[alg]); 291 cpks[j].comp_cert[alg] = NULL; 292 } else { 293 count++; 294 } 295 } 296 } 297 } 298 return (count > 0); 299 } 300 301 static size_t ssl_get_compressed_cert(SSL *ssl, CERT_PKEY *cpk, int alg, unsigned char **data, 302 size_t *orig_len) 303 { 304 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl); 305 size_t cert_len = 0; 306 size_t comp_len = 0; 307 unsigned char *cert_data = NULL; 308 OSSL_COMP_CERT *comp_cert = NULL; 309 310 if (sc == NULL 311 || cpk == NULL 312 || data == NULL 313 || orig_len == NULL 314 || !sc->server 315 || !SSL_in_before(ssl) 316 || !ossl_comp_has_alg(alg)) 317 return 0; 318 319 if ((cert_len = ssl_get_cert_to_compress(ssl, cpk, &cert_data)) == 0) 320 goto err; 321 322 comp_cert = OSSL_COMP_CERT_from_uncompressed_data(cert_data, cert_len, alg); 323 OPENSSL_free(cert_data); 324 if (comp_cert == NULL) 325 goto err; 326 327 comp_len = comp_cert->len; 328 *orig_len = comp_cert->orig_len; 329 *data = comp_cert->data; 330 comp_cert->data = NULL; 331 err: 332 OSSL_COMP_CERT_free(comp_cert); 333 return comp_len; 334 } 335 336 static int ossl_set1_compressed_cert(CERT *cert, int algorithm, 337 unsigned char *comp_data, size_t comp_length, 338 size_t orig_length) 339 { 340 OSSL_COMP_CERT *comp_cert; 341 342 /* No explicit cert set */ 343 if (cert == NULL || cert->key == NULL) 344 return 0; 345 346 comp_cert = OSSL_COMP_CERT_from_compressed_data(comp_data, comp_length, 347 orig_length, algorithm); 348 if (comp_cert == NULL) 349 return 0; 350 351 OSSL_COMP_CERT_free(cert->key->comp_cert[algorithm]); 352 cert->key->comp_cert[algorithm] = comp_cert; 353 354 return 1; 355 } 356 #endif 357 358 /*- 359 * Public API 360 */ 361 int SSL_CTX_set1_cert_comp_preference(SSL_CTX *ctx, int *algs, size_t len) 362 { 363 #ifndef OPENSSL_NO_COMP_ALG 364 return ssl_set_cert_comp_pref(ctx->cert_comp_prefs, algs, len); 365 #else 366 return 0; 367 #endif 368 } 369 370 int SSL_set1_cert_comp_preference(SSL *ssl, int *algs, size_t len) 371 { 372 #ifndef OPENSSL_NO_COMP_ALG 373 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl); 374 375 if (sc == NULL) 376 return 0; 377 return ssl_set_cert_comp_pref(sc->cert_comp_prefs, algs, len); 378 #else 379 return 0; 380 #endif 381 } 382 383 int SSL_compress_certs(SSL *ssl, int alg) 384 { 385 #ifndef OPENSSL_NO_COMP_ALG 386 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl); 387 388 if (sc == NULL || sc->cert == NULL) 389 return 0; 390 391 return ssl_compress_certs(ssl, sc->cert->pkeys, alg); 392 #endif 393 return 0; 394 } 395 396 int SSL_CTX_compress_certs(SSL_CTX *ctx, int alg) 397 { 398 int ret = 0; 399 #ifndef OPENSSL_NO_COMP_ALG 400 SSL *new = SSL_new(ctx); 401 402 if (new == NULL) 403 return 0; 404 405 ret = ssl_compress_certs(new, ctx->cert->pkeys, alg); 406 SSL_free(new); 407 #endif 408 return ret; 409 } 410 411 size_t SSL_get1_compressed_cert(SSL *ssl, int alg, unsigned char **data, size_t *orig_len) 412 { 413 #ifndef OPENSSL_NO_COMP_ALG 414 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl); 415 CERT_PKEY *cpk = NULL; 416 417 if (sc == NULL) 418 return 0; 419 420 if (sc->cert != NULL) 421 cpk = sc->cert->key; 422 else 423 cpk = ssl->ctx->cert->key; 424 425 return ssl_get_compressed_cert(ssl, cpk, alg, data, orig_len); 426 #else 427 return 0; 428 #endif 429 } 430 431 size_t SSL_CTX_get1_compressed_cert(SSL_CTX *ctx, int alg, unsigned char **data, size_t *orig_len) 432 { 433 #ifndef OPENSSL_NO_COMP_ALG 434 size_t ret; 435 SSL *new = SSL_new(ctx); 436 437 ret = ssl_get_compressed_cert(new, ctx->cert->key, alg, data, orig_len); 438 SSL_free(new); 439 return ret; 440 #else 441 return 0; 442 #endif 443 } 444 445 int SSL_CTX_set1_compressed_cert(SSL_CTX *ctx, int algorithm, unsigned char *comp_data, 446 size_t comp_length, size_t orig_length) 447 { 448 #ifndef OPENSSL_NO_COMP_ALG 449 return ossl_set1_compressed_cert(ctx->cert, algorithm, comp_data, comp_length, orig_length); 450 #else 451 return 0; 452 #endif 453 } 454 455 int SSL_set1_compressed_cert(SSL *ssl, int algorithm, unsigned char *comp_data, 456 size_t comp_length, size_t orig_length) 457 { 458 #ifndef OPENSSL_NO_COMP_ALG 459 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl); 460 461 /* Cannot set a pre-compressed certificate on a client */ 462 if (sc == NULL || !sc->server) 463 return 0; 464 465 return ossl_set1_compressed_cert(sc->cert, algorithm, comp_data, comp_length, orig_length); 466 #else 467 return 0; 468 #endif 469 } 470