Coverage Report

Created: 2023-03-26 08:33

/src/gnutls/lib/ext/compress_certificate.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2022 Red Hat, Inc.
3
 *
4
 * Author: Zoltan Fridrich
5
 *
6
 * This file is part of GnuTLS.
7
 *
8
 * The GnuTLS is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public License
10
 * as published by the Free Software Foundation; either version 2.1 of
11
 * the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful, but
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
20
 *
21
 */
22
23
#include "errors.h"
24
#include "gnutls_int.h"
25
#include "hello_ext_lib.h"
26
#include "num.h"
27
#include <ext/compress_certificate.h>
28
29
/* Check whether certificate compression method is valid, ie. supported by gnutls
30
 */
31
static inline int is_valid_method(gnutls_compression_method_t method)
32
0
{
33
0
  switch (method) {
34
0
#ifdef HAVE_LIBZ
35
0
  case GNUTLS_COMP_ZLIB:
36
0
    return 1;
37
0
#endif
38
#ifdef HAVE_LIBBROTLI
39
  case GNUTLS_COMP_BROTLI:
40
    return 1;
41
#endif
42
#ifdef HAVE_LIBZSTD
43
  case GNUTLS_COMP_ZSTD:
44
    return 1;
45
#endif
46
0
  default:
47
0
    return 0;
48
0
  }
49
0
}
50
51
/* Converts compression algorithm number established in RFC8879 to internal compression method type
52
 */
53
gnutls_compression_method_t
54
_gnutls_compress_certificate_num2method(uint16_t num)
55
0
{
56
0
  switch (num) {
57
0
  case 1:
58
0
    return GNUTLS_COMP_ZLIB;
59
0
  case 2:
60
0
    return GNUTLS_COMP_BROTLI;
61
0
  case 3:
62
0
    return GNUTLS_COMP_ZSTD;
63
0
  default:
64
0
    return GNUTLS_COMP_UNKNOWN;
65
0
  }
66
0
}
67
68
/* Converts compression method type to compression algorithm number established in RFC8879
69
 */
70
int _gnutls_compress_certificate_method2num(gnutls_compression_method_t method)
71
0
{
72
0
  switch (method) {
73
0
  case GNUTLS_COMP_ZLIB:
74
0
    return 1;
75
0
  case GNUTLS_COMP_BROTLI:
76
0
    return 2;
77
0
  case GNUTLS_COMP_ZSTD:
78
0
    return 3;
79
0
  default:
80
0
    return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
81
0
  }
82
0
}
83
84
/* Returns 1 if the method is set as supported compression method for the session,
85
 * returns 0 otherwise
86
 */
87
bool
88
_gnutls_compress_certificate_is_method_enabled(gnutls_session_t session,
89
                 gnutls_compression_method_t
90
                 method)
91
0
{
92
0
  int ret;
93
0
  unsigned i;
94
0
  compress_certificate_ext_st *priv;
95
0
  gnutls_ext_priv_data_t epriv;
96
97
0
  ret =
98
0
      _gnutls_hello_ext_get_priv(session,
99
0
               GNUTLS_EXTENSION_COMPRESS_CERTIFICATE,
100
0
               &epriv);
101
0
  if (ret < 0)
102
0
    return false;
103
0
  priv = epriv;
104
105
0
  for (i = 0; i < priv->methods_len; ++i)
106
0
    if (priv->methods[i] == method)
107
0
      return true;
108
109
0
  return false;
110
0
}
111
112
/**
113
 * gnutls_compress_certificate_get_selected_method:
114
 * @session: is a #gnutls_session_t type.
115
 *
116
 * This function returns the certificate compression method that has been
117
 * selected to compress the certificate before sending it to the peer.
118
 * The selection is done based on the local list of supported compression
119
 * methods and the peer's requested compression methods.
120
 *
121
 * Returns: selected certificate compression method.
122
 *
123
 * Since 3.7.4
124
 **/
125
gnutls_compression_method_t
126
gnutls_compress_certificate_get_selected_method(gnutls_session_t session)
127
0
{
128
0
  return session->internals.compress_certificate_method;
129
0
}
130
131
/**
132
 * gnutls_compress_certificate_set_methods:
133
 * @session: is a #gnutls_session_t type.
134
 * @methods: is a list of supported compression methods.
135
 * @methods_len: number of compression methods in @methods
136
 *
137
 * This function sets the supported compression methods for certificate compression
138
 * for the given session. The list of supported compression methods will be used
139
 * for a) requesting the compression of peer's certificate and b) selecting the
140
 * method to compress the local certificate before sending it to the peer.
141
 * The order of compression methods inside the list does matter as the method
142
 * that appears earlier in the list will be preferred before the later ones.
143
 * Note that even if you set the list of supported compression methods, the
144
 * compression might not be used if the peer does not support any of your chosen
145
 * compression methods.
146
 *
147
 * The list of supported compression methods must meet the following criteria:
148
 * Argument @methods must be an array of valid compression methods of type
149
 * #gnutls_compression_method_t. Argument @methods_len must contain the number of
150
 * compression methods stored in the @methods array and must be within range <1, 127>.
151
 * The length constraints are defined by %MIN_COMPRESS_CERTIFICATE_METHODS
152
 * and %MAX_COMPRESS_CERTIFICATE_METHODS macros located in the header file
153
 * compress_certificate.h.
154
 *
155
 * If either @methods or @methods_len is equal to 0, current list of supported
156
 * compression methods will be unset.
157
 *
158
 * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
159
 *
160
 * Since 3.7.4
161
 **/
162
int
163
gnutls_compress_certificate_set_methods(gnutls_session_t session,
164
          const gnutls_compression_method_t *
165
          methods, size_t methods_len)
166
0
{
167
0
  unsigned i;
168
0
  compress_certificate_ext_st *priv;
169
170
0
  if (methods == NULL || methods_len == 0) {
171
0
    _gnutls_hello_ext_unset_priv(session,
172
0
               GNUTLS_EXTENSION_COMPRESS_CERTIFICATE);
173
0
    return 0;
174
0
  }
175
176
0
  if (methods_len > MAX_COMPRESS_CERTIFICATE_METHODS)
177
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
178
179
0
  for (i = 0; i < methods_len; ++i)
180
0
    if (!is_valid_method(methods[i]))
181
0
      return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
182
183
0
  priv = gnutls_malloc(sizeof(*priv));
184
0
  if (priv == NULL)
185
0
    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
186
187
0
  priv->methods_len = methods_len;
188
0
  memcpy(priv->methods, methods, methods_len * sizeof(*methods));
189
0
  _gnutls_hello_ext_set_priv(session,
190
0
           GNUTLS_EXTENSION_COMPRESS_CERTIFICATE, priv);
191
192
0
  return 0;
193
0
}
194
195
int
196
_gnutls_compress_certificate_recv_params(gnutls_session_t session,
197
           const uint8_t * data, size_t data_size)
198
0
{
199
0
  int ret;
200
0
  unsigned i, j;
201
0
  uint16_t num;
202
0
  uint8_t bytes_len;
203
0
  size_t methods_len;
204
0
  gnutls_compression_method_t methods[MAX_COMPRESS_CERTIFICATE_METHODS];
205
0
  gnutls_compression_method_t method;
206
0
  compress_certificate_ext_st *priv;
207
0
  gnutls_ext_priv_data_t epriv;
208
209
0
  ret =
210
0
      _gnutls_hello_ext_get_priv(session,
211
0
               GNUTLS_EXTENSION_COMPRESS_CERTIFICATE,
212
0
               &epriv);
213
0
  if (ret < 0)
214
0
    return 0;
215
0
  priv = epriv;
216
217
0
  DECR_LEN(data_size, 1);
218
0
  bytes_len = *data;
219
220
0
  if (bytes_len < 2 || bytes_len > 254 || bytes_len % 2 == 1)
221
0
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
222
223
0
  DECR_LEN(data_size, bytes_len);
224
225
0
  methods_len = 0;
226
0
  for (i = 0; i < bytes_len / 2; ++i) {
227
0
    num = _gnutls_read_uint16(data + i + i + 1);
228
0
    method = _gnutls_compress_certificate_num2method(num);
229
0
    if (method != GNUTLS_COMP_UNKNOWN)
230
0
      methods[methods_len++] = method;
231
0
  }
232
233
0
  method = GNUTLS_COMP_UNKNOWN;
234
0
  for (i = 0; i < methods_len; ++i)
235
0
    for (j = 0; j < priv->methods_len; ++j)
236
0
      if (methods[i] == priv->methods[j]) {
237
0
        method = methods[i];
238
0
        goto endloop;
239
0
      }
240
0
 endloop:
241
0
  session->internals.compress_certificate_method = method;
242
243
0
  return 0;
244
0
}
245
246
int
247
_gnutls_compress_certificate_send_params(gnutls_session_t session,
248
           gnutls_buffer_st * data)
249
0
{
250
0
  int ret, num;
251
0
  unsigned i;
252
0
  uint8_t bytes_len;
253
0
  uint8_t bytes[2 * MAX_COMPRESS_CERTIFICATE_METHODS];
254
0
  compress_certificate_ext_st *priv;
255
0
  gnutls_ext_priv_data_t epriv;
256
257
0
  ret =
258
0
      _gnutls_hello_ext_get_priv(session,
259
0
               GNUTLS_EXTENSION_COMPRESS_CERTIFICATE,
260
0
               &epriv);
261
0
  if (ret < 0)
262
0
    return 0;
263
0
  priv = epriv;
264
265
0
  bytes_len = 2 * priv->methods_len;
266
0
  for (i = 0; i < priv->methods_len; ++i) {
267
0
    num = _gnutls_compress_certificate_method2num(priv->methods[i]);
268
0
    _gnutls_write_uint16(num, bytes + i + i);
269
0
  }
270
271
0
  ret = _gnutls_buffer_append_data_prefix(data, 8, bytes, bytes_len);
272
0
  if (ret < 0)
273
0
    return gnutls_assert_val(ret);
274
275
0
  session->internals.hsk_flags |= HSK_COMP_CRT_REQ_SENT;
276
277
0
  return bytes_len + 1;
278
0
}
279
280
const hello_ext_entry_st ext_mod_compress_certificate = {
281
  .name = "Compress Certificate",
282
  .tls_id = 27,
283
  .gid = GNUTLS_EXTENSION_COMPRESS_CERTIFICATE,
284
  .client_parse_point = GNUTLS_EXT_TLS,
285
  .server_parse_point = GNUTLS_EXT_TLS,
286
  .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS |
287
      GNUTLS_EXT_FLAG_CLIENT_HELLO,
288
  .recv_func = _gnutls_compress_certificate_recv_params,
289
  .send_func = _gnutls_compress_certificate_send_params,
290
  .deinit_func = _gnutls_hello_ext_default_deinit
291
};