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