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