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
ossl_calculate_comp_expansion(int alg,size_t length)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
ossl_comp_has_alg(int a)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
OSSL_COMP_CERT_new(unsigned char * data,size_t len,size_t orig_len,int 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
OSSL_COMP_CERT_from_compressed_data(unsigned char * data,size_t len,size_t orig_len,int alg)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
OSSL_COMP_CERT_from_uncompressed_data(unsigned char * data,size_t len,int alg)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 || max_length > INT_MAX
115 || method == NULL
116 || (comp_ctx = COMP_CTX_new(method)) == NULL
117 || (comp_data = OPENSSL_zalloc(max_length)) == NULL)
118 goto err;
119
120 comp_length = COMP_compress_block(comp_ctx, comp_data, (int)max_length, data, (int)len);
121 if (comp_length <= 0)
122 goto err;
123
124 ret = OSSL_COMP_CERT_new(comp_data, comp_length, len, alg);
125 comp_data = NULL;
126
127 err:
128 OPENSSL_free(comp_data);
129 COMP_CTX_free(comp_ctx);
130 return ret;
131 }
132
OSSL_COMP_CERT_free(OSSL_COMP_CERT * cc)133 void OSSL_COMP_CERT_free(OSSL_COMP_CERT *cc)
134 {
135 int i;
136
137 if (cc == NULL)
138 return;
139
140 CRYPTO_DOWN_REF(&cc->references, &i);
141 REF_PRINT_COUNT("OSSL_COMP_CERT", i, cc);
142 if (i > 0)
143 return;
144 REF_ASSERT_ISNT(i < 0);
145
146 OPENSSL_free(cc->data);
147 CRYPTO_FREE_REF(&cc->references);
148 OPENSSL_free(cc);
149 }
OSSL_COMP_CERT_up_ref(OSSL_COMP_CERT * cc)150 int OSSL_COMP_CERT_up_ref(OSSL_COMP_CERT *cc)
151 {
152 int i;
153
154 if (CRYPTO_UP_REF(&cc->references, &i) <= 0)
155 return 0;
156
157 REF_PRINT_COUNT("OSSL_COMP_CERT", i, cc);
158 REF_ASSERT_ISNT(i < 2);
159 return ((i > 1) ? 1 : 0);
160 }
161
ssl_set_cert_comp_pref(int * prefs,int * algs,size_t len)162 static int ssl_set_cert_comp_pref(int *prefs, int *algs, size_t len)
163 {
164 size_t j = 0;
165 size_t i;
166 int found = 0;
167 int already_set[TLSEXT_comp_cert_limit];
168 int tmp_prefs[TLSEXT_comp_cert_limit];
169
170 /* Note that |len| is the number of |algs| elements */
171 /* clear all algorithms */
172 if (len == 0 || algs == NULL) {
173 memset(prefs, 0, sizeof(tmp_prefs));
174 return 1;
175 }
176
177 /* This will 0-terminate the array */
178 memset(tmp_prefs, 0, sizeof(tmp_prefs));
179 memset(already_set, 0, sizeof(already_set));
180 /* Include only those algorithms we support, ignoring duplicates and unknowns */
181 for (i = 0; i < len; i++) {
182 if (algs[i] != 0 && ossl_comp_has_alg(algs[i])) {
183 /* Check for duplicate */
184 if (already_set[algs[i]])
185 return 0;
186 tmp_prefs[j++] = algs[i];
187 already_set[algs[i]] = 1;
188 found = 1;
189 }
190 }
191 if (found)
192 memcpy(prefs, tmp_prefs, sizeof(tmp_prefs));
193 return found;
194 }
195
ssl_get_cert_to_compress(SSL * ssl,CERT_PKEY * cpk,unsigned char ** data)196 static size_t ssl_get_cert_to_compress(SSL *ssl, CERT_PKEY *cpk, unsigned char **data)
197 {
198 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
199 WPACKET tmppkt;
200 BUF_MEM buf = { 0 };
201 size_t ret = 0;
202
203 if (sc == NULL
204 || cpk == NULL
205 || !sc->server
206 || !SSL_in_before(ssl))
207 return 0;
208
209 /* Use the |tmppkt| for the to-be-compressed data */
210 if (!WPACKET_init(&tmppkt, &buf))
211 goto out;
212
213 /* no context present, add 0-length context */
214 if (!WPACKET_put_bytes_u8(&tmppkt, 0))
215 goto out;
216
217 /*
218 * ssl3_output_cert_chain() may generate an SSLfatal() error,
219 * for this case, we want to ignore it, argument for_comp = 1
220 */
221 if (!ssl3_output_cert_chain(sc, &tmppkt, cpk, 1))
222 goto out;
223 WPACKET_get_total_written(&tmppkt, &ret);
224
225 out:
226 WPACKET_cleanup(&tmppkt);
227 if (ret != 0 && data != NULL)
228 *data = (unsigned char *)buf.data;
229 else
230 OPENSSL_free(buf.data);
231 return ret;
232 }
233
ssl_compress_one_cert(SSL * ssl,CERT_PKEY * cpk,int alg)234 static int ssl_compress_one_cert(SSL *ssl, CERT_PKEY *cpk, int alg)
235 {
236 unsigned char *cert_data = NULL;
237 OSSL_COMP_CERT *comp_cert = NULL;
238 size_t length;
239
240 if (cpk == NULL
241 || alg == TLSEXT_comp_cert_none
242 || !ossl_comp_has_alg(alg))
243 return 0;
244
245 if ((length = ssl_get_cert_to_compress(ssl, cpk, &cert_data)) == 0)
246 return 0;
247 comp_cert = OSSL_COMP_CERT_from_uncompressed_data(cert_data, length, alg);
248 OPENSSL_free(cert_data);
249 if (comp_cert == NULL)
250 return 0;
251
252 OSSL_COMP_CERT_free(cpk->comp_cert[alg]);
253 cpk->comp_cert[alg] = comp_cert;
254 return 1;
255 }
256
257 /* alg_in can be 0, meaning any/all algorithms */
ssl_compress_certs(SSL * ssl,CERT_PKEY * cpks,int alg_in)258 static int ssl_compress_certs(SSL *ssl, CERT_PKEY *cpks, int alg_in)
259 {
260 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
261 int i;
262 int j;
263 int alg;
264 int count = 0;
265
266 if (sc == NULL
267 || cpks == NULL
268 || !ossl_comp_has_alg(alg_in))
269 return 0;
270
271 /* Look through the preferences to see what we have */
272 for (i = 0; i < TLSEXT_comp_cert_limit; i++) {
273 /*
274 * alg = 0 means compress for everything, but only for algorithms enabled
275 * alg != 0 means compress for that algorithm if enabled
276 */
277 alg = sc->cert_comp_prefs[i];
278 if ((alg_in == 0 && alg != TLSEXT_comp_cert_none)
279 || (alg_in != 0 && alg == alg_in)) {
280
281 for (j = 0; j < SSL_PKEY_NUM; j++) {
282 /* No cert, move on */
283 if (cpks[j].x509 == NULL)
284 continue;
285
286 if (!ssl_compress_one_cert(ssl, &cpks[j], alg))
287 return 0;
288
289 /* if the cert expanded, set the value in the CERT_PKEY to NULL */
290 if (cpks[j].comp_cert[alg]->len >= cpks[j].comp_cert[alg]->orig_len) {
291 OSSL_COMP_CERT_free(cpks[j].comp_cert[alg]);
292 cpks[j].comp_cert[alg] = NULL;
293 } else {
294 count++;
295 }
296 }
297 }
298 }
299 return (count > 0);
300 }
301
ssl_get_compressed_cert(SSL * ssl,CERT_PKEY * cpk,int alg,unsigned char ** data,size_t * orig_len)302 static size_t ssl_get_compressed_cert(SSL *ssl, CERT_PKEY *cpk, int alg, unsigned char **data,
303 size_t *orig_len)
304 {
305 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
306 size_t cert_len = 0;
307 size_t comp_len = 0;
308 unsigned char *cert_data = NULL;
309 OSSL_COMP_CERT *comp_cert = NULL;
310
311 if (sc == NULL
312 || cpk == NULL
313 || data == NULL
314 || orig_len == NULL
315 || !sc->server
316 || !SSL_in_before(ssl)
317 || !ossl_comp_has_alg(alg))
318 return 0;
319
320 if ((cert_len = ssl_get_cert_to_compress(ssl, cpk, &cert_data)) == 0)
321 goto err;
322
323 comp_cert = OSSL_COMP_CERT_from_uncompressed_data(cert_data, cert_len, alg);
324 OPENSSL_free(cert_data);
325 if (comp_cert == NULL)
326 goto err;
327
328 comp_len = comp_cert->len;
329 *orig_len = comp_cert->orig_len;
330 *data = comp_cert->data;
331 comp_cert->data = NULL;
332 err:
333 OSSL_COMP_CERT_free(comp_cert);
334 return comp_len;
335 }
336
ossl_set1_compressed_cert(CERT * cert,int algorithm,unsigned char * comp_data,size_t comp_length,size_t orig_length)337 static int ossl_set1_compressed_cert(CERT *cert, int algorithm,
338 unsigned char *comp_data, size_t comp_length,
339 size_t orig_length)
340 {
341 OSSL_COMP_CERT *comp_cert;
342
343 /* No explicit cert set */
344 if (cert == NULL || cert->key == NULL)
345 return 0;
346
347 comp_cert = OSSL_COMP_CERT_from_compressed_data(comp_data, comp_length,
348 orig_length, algorithm);
349 if (comp_cert == NULL)
350 return 0;
351
352 OSSL_COMP_CERT_free(cert->key->comp_cert[algorithm]);
353 cert->key->comp_cert[algorithm] = comp_cert;
354
355 return 1;
356 }
357 #endif
358
359 /*-
360 * Public API
361 */
SSL_CTX_set1_cert_comp_preference(SSL_CTX * ctx,int * algs,size_t len)362 int SSL_CTX_set1_cert_comp_preference(SSL_CTX *ctx, int *algs, size_t len)
363 {
364 #ifndef OPENSSL_NO_COMP_ALG
365 return ssl_set_cert_comp_pref(ctx->cert_comp_prefs, algs, len);
366 #else
367 return 0;
368 #endif
369 }
370
SSL_set1_cert_comp_preference(SSL * ssl,int * algs,size_t len)371 int SSL_set1_cert_comp_preference(SSL *ssl, int *algs, size_t len)
372 {
373 #ifndef OPENSSL_NO_COMP_ALG
374 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
375
376 if (sc == NULL)
377 return 0;
378 return ssl_set_cert_comp_pref(sc->cert_comp_prefs, algs, len);
379 #else
380 return 0;
381 #endif
382 }
383
SSL_compress_certs(SSL * ssl,int alg)384 int SSL_compress_certs(SSL *ssl, int alg)
385 {
386 #ifndef OPENSSL_NO_COMP_ALG
387 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
388
389 if (sc == NULL || sc->cert == NULL)
390 return 0;
391
392 return ssl_compress_certs(ssl, sc->cert->pkeys, alg);
393 #endif
394 return 0;
395 }
396
SSL_CTX_compress_certs(SSL_CTX * ctx,int alg)397 int SSL_CTX_compress_certs(SSL_CTX *ctx, int alg)
398 {
399 int ret = 0;
400 #ifndef OPENSSL_NO_COMP_ALG
401 SSL *new = SSL_new(ctx);
402
403 if (new == NULL)
404 return 0;
405
406 ret = ssl_compress_certs(new, ctx->cert->pkeys, alg);
407 SSL_free(new);
408 #endif
409 return ret;
410 }
411
SSL_get1_compressed_cert(SSL * ssl,int alg,unsigned char ** data,size_t * orig_len)412 size_t SSL_get1_compressed_cert(SSL *ssl, int alg, unsigned char **data, size_t *orig_len)
413 {
414 #ifndef OPENSSL_NO_COMP_ALG
415 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
416 CERT_PKEY *cpk = NULL;
417
418 if (sc == NULL)
419 return 0;
420
421 if (sc->cert != NULL)
422 cpk = sc->cert->key;
423 else
424 cpk = ssl->ctx->cert->key;
425
426 return ssl_get_compressed_cert(ssl, cpk, alg, data, orig_len);
427 #else
428 return 0;
429 #endif
430 }
431
SSL_CTX_get1_compressed_cert(SSL_CTX * ctx,int alg,unsigned char ** data,size_t * orig_len)432 size_t SSL_CTX_get1_compressed_cert(SSL_CTX *ctx, int alg, unsigned char **data, size_t *orig_len)
433 {
434 #ifndef OPENSSL_NO_COMP_ALG
435 size_t ret;
436 SSL *new = SSL_new(ctx);
437
438 ret = ssl_get_compressed_cert(new, ctx->cert->key, alg, data, orig_len);
439 SSL_free(new);
440 return ret;
441 #else
442 return 0;
443 #endif
444 }
445
SSL_CTX_set1_compressed_cert(SSL_CTX * ctx,int algorithm,unsigned char * comp_data,size_t comp_length,size_t orig_length)446 int SSL_CTX_set1_compressed_cert(SSL_CTX *ctx, int algorithm, unsigned char *comp_data,
447 size_t comp_length, size_t orig_length)
448 {
449 #ifndef OPENSSL_NO_COMP_ALG
450 return ossl_set1_compressed_cert(ctx->cert, algorithm, comp_data, comp_length, orig_length);
451 #else
452 return 0;
453 #endif
454 }
455
SSL_set1_compressed_cert(SSL * ssl,int algorithm,unsigned char * comp_data,size_t comp_length,size_t orig_length)456 int SSL_set1_compressed_cert(SSL *ssl, int algorithm, unsigned char *comp_data,
457 size_t comp_length, size_t orig_length)
458 {
459 #ifndef OPENSSL_NO_COMP_ALG
460 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
461
462 /* Cannot set a pre-compressed certificate on a client */
463 if (sc == NULL || !sc->server)
464 return 0;
465
466 return ossl_set1_compressed_cert(sc->cert, algorithm, comp_data, comp_length, orig_length);
467 #else
468 return 0;
469 #endif
470 }
471