Coverage Report

Created: 2023-03-26 07:33

/src/gnutls/lib/ext/alpn.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2013 Nikos Mavrogiannopoulos
3
 * Copyright (C) 2017 Red Hat, Inc.
4
 * 
5
 * This file is part of GnuTLS.
6
 *
7
 * The GnuTLS is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public License
9
 * as published by the Free Software Foundation; either version 2.1 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
19
 *
20
 */
21
22
#include "gnutls_int.h"
23
#include "auth.h"
24
#include "errors.h"
25
#include "num.h"
26
#include <ext/alpn.h>
27
28
static int _gnutls_alpn_recv_params(gnutls_session_t session,
29
            const uint8_t * data, size_t data_size);
30
static int _gnutls_alpn_send_params(gnutls_session_t session,
31
            gnutls_buffer_st * extdata);
32
33
static void _gnutls_alpn_deinit_data(gnutls_ext_priv_data_t priv);
34
35
const hello_ext_entry_st ext_mod_alpn = {
36
  .name = "ALPN",
37
  .tls_id = 16,
38
  .gid = GNUTLS_EXTENSION_ALPN,
39
  /* this extension must be parsed even on resumption */
40
  .client_parse_point = GNUTLS_EXT_MANDATORY,
41
  .server_parse_point = GNUTLS_EXT_MANDATORY,
42
  .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS |
43
      GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_EE |
44
      GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO,
45
  .recv_func = _gnutls_alpn_recv_params,
46
  .send_func = _gnutls_alpn_send_params,
47
  .deinit_func = _gnutls_alpn_deinit_data,
48
  .cannot_be_overriden = 1
49
};
50
51
static int
52
_gnutls_alpn_recv_params(gnutls_session_t session,
53
       const uint8_t * data, size_t data_size)
54
0
{
55
0
  unsigned int i;
56
0
  int ret;
57
0
  const uint8_t *p = data;
58
0
  unsigned len1, len;
59
0
  alpn_ext_st *priv;
60
0
  gnutls_ext_priv_data_t epriv;
61
0
  int selected_protocol_index;
62
63
0
  ret =
64
0
      _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_ALPN, &epriv);
65
0
  if (ret < 0)
66
0
    return 0;
67
68
0
  priv = epriv;
69
70
0
  DECR_LENGTH_RET(data_size, 2, GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
71
0
  len = _gnutls_read_uint16(p);
72
0
  p += 2;
73
74
0
  if (len == 0 || len > (size_t)data_size)
75
0
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
76
77
0
  if (session->security_parameters.entity == GNUTLS_SERVER) {
78
0
    selected_protocol_index = MAX_ALPN_PROTOCOLS + 1;
79
80
0
    while (data_size > 0) {
81
0
      DECR_LENGTH_RET(data_size, 1,
82
0
          GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
83
0
      len1 = *p;
84
0
      p += 1;
85
0
      DECR_LENGTH_RET(data_size, len1,
86
0
          GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
87
88
0
      if (len1 == 0)
89
0
        return
90
0
            gnutls_assert_val
91
0
            (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
92
93
0
      for (i = 0; i < priv->size; i++) {
94
0
        if (priv->protocol_size[i] == len1
95
0
            && memcmp(p, priv->protocols[i],
96
0
                len1) == 0) {
97
98
0
          if (priv->flags &
99
0
              GNUTLS_ALPN_SERVER_PRECEDENCE) {
100
0
            if (selected_protocol_index >
101
0
                (int)i) {
102
0
              selected_protocol_index
103
0
                  = i;
104
0
              priv->selected_protocol
105
0
                  =
106
0
                  priv->protocols[i];
107
0
              priv->selected_protocol_size
108
0
                  =
109
0
                  priv->protocol_size
110
0
                  [i];
111
0
              break;
112
0
            }
113
0
          } else {
114
0
            priv->selected_protocol =
115
0
                priv->protocols[i];
116
0
            priv->selected_protocol_size =
117
0
                priv->protocol_size[i];
118
0
            return 0;
119
0
          }
120
0
        }
121
0
      }
122
0
      p += len1;
123
0
    }
124
0
  } else {
125
0
    DECR_LENGTH_RET(data_size, 1,
126
0
        GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
127
0
    len1 = *p;
128
0
    p += 1;
129
0
    DECR_LENGTH_RET(data_size, len1,
130
0
        GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
131
132
0
    for (i = 0; i < priv->size; i++) {
133
0
      if (priv->protocol_size[i] == len1
134
0
          && memcmp(p, priv->protocols[i], len1) == 0) {
135
0
        priv->selected_protocol = priv->protocols[i];
136
0
        priv->selected_protocol_size =
137
0
            priv->protocol_size[i];
138
0
        break;
139
0
      }
140
0
    }
141
    /*p += len1; */
142
0
  }
143
144
0
  if (priv->selected_protocol == NULL && (priv->flags & GNUTLS_ALPN_MAND))
145
0
    return gnutls_assert_val(GNUTLS_E_NO_APPLICATION_PROTOCOL);
146
147
0
  return 0;
148
0
}
149
150
static int
151
_gnutls_alpn_send_params(gnutls_session_t session, gnutls_buffer_st * extdata)
152
0
{
153
0
  unsigned i;
154
0
  int total_size = 0, ret;
155
0
  alpn_ext_st *priv;
156
0
  gnutls_ext_priv_data_t epriv;
157
158
0
  ret =
159
0
      _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_ALPN, &epriv);
160
0
  if (ret < 0)
161
0
    return 0;
162
163
0
  priv = epriv;
164
165
0
  if (priv->size == 0)
166
0
    return 0;
167
168
0
  if (session->security_parameters.entity == GNUTLS_SERVER) {
169
0
    if (priv->selected_protocol_size == 0)
170
0
      return 0;
171
172
0
    ret =
173
0
        _gnutls_buffer_append_prefix(extdata, 16,
174
0
             priv->selected_protocol_size +
175
0
             1);
176
0
    if (ret < 0)
177
0
      return gnutls_assert_val(ret);
178
179
0
    total_size += 2;
180
181
0
    ret =
182
0
        _gnutls_buffer_append_data_prefix(extdata, 8,
183
0
                  priv->selected_protocol,
184
0
                  priv->
185
0
                  selected_protocol_size);
186
0
    if (ret < 0)
187
0
      return gnutls_assert_val(ret);
188
189
0
    total_size += 1 + priv->selected_protocol_size;
190
0
  } else {
191
0
    int t = 0;
192
0
    for (i = 0; i < priv->size; i++)
193
0
      t += priv->protocol_size[i] + 1;
194
195
0
    ret = _gnutls_buffer_append_prefix(extdata, 16, t);
196
0
    if (ret < 0)
197
0
      return gnutls_assert_val(ret);
198
199
0
    total_size += 2;
200
201
0
    for (i = 0; i < priv->size; i++) {
202
0
      ret =
203
0
          _gnutls_buffer_append_data_prefix(extdata, 8,
204
0
                    priv->protocols
205
0
                    [i],
206
0
                    priv->
207
0
                    protocol_size[i]);
208
0
      if (ret < 0)
209
0
        return gnutls_assert_val(ret);
210
211
0
      total_size += 1 + priv->protocol_size[i];
212
0
    }
213
0
  }
214
215
0
  return total_size;
216
0
}
217
218
/**
219
 * gnutls_alpn_get_selected_protocol:
220
 * @session: is a #gnutls_session_t type.
221
 * @protocol: will hold the protocol name
222
 *
223
 * This function allows you to get the negotiated protocol name. The
224
 * returned protocol should be treated as opaque, constant value and
225
 * only valid during the session life.
226
 *
227
 * The selected protocol is the first supported by the list sent
228
 * by the client.
229
 *
230
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
231
 *   otherwise a negative error code is returned.
232
 *
233
 * Since 3.2.0
234
 **/
235
int
236
gnutls_alpn_get_selected_protocol(gnutls_session_t session,
237
          gnutls_datum_t * protocol)
238
0
{
239
0
  alpn_ext_st *priv;
240
0
  int ret;
241
0
  gnutls_ext_priv_data_t epriv;
242
243
0
  ret =
244
0
      _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_ALPN, &epriv);
245
0
  if (ret < 0) {
246
0
    gnutls_assert();
247
0
    return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
248
0
  }
249
250
0
  priv = epriv;
251
252
0
  if (priv->selected_protocol_size == 0)
253
0
    return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
254
255
0
  protocol->data = priv->selected_protocol;
256
0
  protocol->size = priv->selected_protocol_size;
257
258
0
  return 0;
259
0
}
260
261
/**
262
 * gnutls_alpn_set_protocols:
263
 * @session: is a #gnutls_session_t type.
264
 * @protocols: is the protocol names to add.
265
 * @protocols_size: the number of protocols to add.
266
 * @flags: zero or a sequence of %gnutls_alpn_flags_t
267
 *
268
 * This function is to be used by both clients and servers, to declare
269
 * the supported ALPN protocols, which are used during negotiation with peer.
270
 *
271
 * See %gnutls_alpn_flags_t description for the documentation of available
272
 * flags.
273
 *
274
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
275
 *   otherwise a negative error code is returned.
276
 *
277
 * Since 3.2.0
278
 **/
279
int
280
gnutls_alpn_set_protocols(gnutls_session_t session,
281
        const gnutls_datum_t * protocols,
282
        unsigned protocols_size, unsigned int flags)
283
0
{
284
0
  int ret;
285
0
  alpn_ext_st *priv;
286
0
  gnutls_ext_priv_data_t epriv;
287
0
  unsigned i;
288
289
0
  ret =
290
0
      _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_ALPN, &epriv);
291
0
  if (ret < 0) {
292
0
    priv = gnutls_calloc(1, sizeof(*priv));
293
0
    if (priv == NULL) {
294
0
      gnutls_assert();
295
0
      return GNUTLS_E_MEMORY_ERROR;
296
0
    }
297
0
    epriv = priv;
298
0
    _gnutls_hello_ext_set_priv(session,
299
0
             GNUTLS_EXTENSION_ALPN, epriv);
300
0
  } else
301
0
    priv = epriv;
302
303
0
  if (protocols_size > MAX_ALPN_PROTOCOLS)
304
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
305
306
0
  for (i = 0; i < protocols_size; i++) {
307
0
    if (protocols[i].size >= MAX_ALPN_PROTOCOL_NAME)
308
0
      return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
309
310
0
    memcpy(priv->protocols[i], protocols[i].data,
311
0
           protocols[i].size);
312
0
    priv->protocol_size[i] = protocols[i].size;
313
0
    priv->size++;
314
0
  }
315
0
  priv->flags = flags;
316
317
0
  return 0;
318
0
}
319
320
static void _gnutls_alpn_deinit_data(gnutls_ext_priv_data_t priv)
321
0
{
322
0
  gnutls_free(priv);
323
0
}