/src/gnutls/lib/ext/session_ticket.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2009-2018 Free Software Foundation, Inc. |
3 | | * |
4 | | * Author: Daiki Ueno, Ander Juaristi |
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 | | /* This file implements the TLS session ticket extension. |
24 | | * |
25 | | * Note that the extension is only used in TLS 1.2. For TLS 1.3, session |
26 | | * tickets are sent as part of pre_shared_key extension (see pre_shared_key.c). |
27 | | */ |
28 | | |
29 | | #include "gnutls_int.h" |
30 | | #include "errors.h" |
31 | | #include "fips.h" |
32 | | #include "datum.h" |
33 | | #include "algorithms.h" |
34 | | #include "handshake.h" |
35 | | #include "num.h" |
36 | | #include "constate.h" |
37 | | #include "session_pack.h" |
38 | | #include "random.h" |
39 | | #include "ext/session_ticket.h" |
40 | | #include "mbuffers.h" |
41 | | #include "hello_ext.h" |
42 | | #include "constate.h" |
43 | | #include "dtls.h" |
44 | | #include "stek.h" |
45 | | #include "db.h" |
46 | | |
47 | | static int session_ticket_recv_params(gnutls_session_t session, |
48 | | const uint8_t *data, size_t data_size); |
49 | | static int session_ticket_send_params(gnutls_session_t session, |
50 | | gnutls_buffer_st *extdata); |
51 | | static int session_ticket_unpack(gnutls_buffer_st *ps, |
52 | | gnutls_ext_priv_data_t *_priv); |
53 | | static int session_ticket_pack(gnutls_ext_priv_data_t _priv, |
54 | | gnutls_buffer_st *ps); |
55 | | static void session_ticket_deinit_data(gnutls_ext_priv_data_t priv); |
56 | | |
57 | | const hello_ext_entry_st ext_mod_session_ticket = { |
58 | | .name = "Session Ticket", |
59 | | .tls_id = 35, |
60 | | .gid = GNUTLS_EXTENSION_SESSION_TICKET, |
61 | | .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | |
62 | | GNUTLS_EXT_FLAG_CLIENT_HELLO | |
63 | | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, |
64 | | /* This extension must be parsed on session resumption as well; see |
65 | | * https://gitlab.com/gnutls/gnutls/issues/841 */ |
66 | | .client_parse_point = GNUTLS_EXT_MANDATORY, |
67 | | /* on server side we want this parsed after normal handshake resumption |
68 | | * actions are complete */ |
69 | | .server_parse_point = GNUTLS_EXT_TLS, |
70 | | .recv_func = session_ticket_recv_params, |
71 | | .send_func = session_ticket_send_params, |
72 | | .pack_func = session_ticket_pack, |
73 | | .unpack_func = session_ticket_unpack, |
74 | | .deinit_func = session_ticket_deinit_data, |
75 | | .cannot_be_overriden = 1 |
76 | | }; |
77 | | |
78 | | typedef struct { |
79 | | uint8_t *session_ticket; |
80 | | int session_ticket_len; |
81 | | } session_ticket_ext_st; |
82 | | |
83 | | static void deinit_ticket(struct ticket_st *ticket) |
84 | 0 | { |
85 | 0 | free(ticket->encrypted_state); |
86 | 0 | } |
87 | | |
88 | | static int unpack_ticket(const gnutls_datum_t *ticket_data, |
89 | | struct ticket_st *ticket) |
90 | | { |
91 | | const uint8_t *data = ticket_data->data; |
92 | | size_t data_size = ticket_data->size; |
93 | | const uint8_t *encrypted_state; |
94 | | |
95 | | /* Format: |
96 | | * Key name |
97 | | * IV |
98 | | * data length |
99 | | * encrypted data |
100 | | * MAC |
101 | | */ |
102 | | DECR_LEN(data_size, TICKET_KEY_NAME_SIZE); |
103 | | memcpy(ticket->key_name, data, TICKET_KEY_NAME_SIZE); |
104 | | data += TICKET_KEY_NAME_SIZE; |
105 | | |
106 | | DECR_LEN(data_size, TICKET_IV_SIZE); |
107 | | memcpy(ticket->IV, data, TICKET_IV_SIZE); |
108 | | data += TICKET_IV_SIZE; |
109 | | |
110 | | DECR_LEN(data_size, 2); |
111 | | ticket->encrypted_state_len = _gnutls_read_uint16(data); |
112 | | data += 2; |
113 | | |
114 | | encrypted_state = data; |
115 | | |
116 | | DECR_LEN(data_size, ticket->encrypted_state_len); |
117 | | data += ticket->encrypted_state_len; |
118 | | |
119 | | DECR_LEN(data_size, TICKET_MAC_SIZE); |
120 | | memcpy(ticket->mac, data, TICKET_MAC_SIZE); |
121 | | |
122 | | ticket->encrypted_state = gnutls_malloc(ticket->encrypted_state_len); |
123 | | if (!ticket->encrypted_state) { |
124 | | gnutls_assert(); |
125 | | return GNUTLS_E_MEMORY_ERROR; |
126 | | } |
127 | | memcpy(ticket->encrypted_state, encrypted_state, |
128 | | ticket->encrypted_state_len); |
129 | | |
130 | | return 0; |
131 | | } |
132 | | |
133 | | static void pack_ticket(const struct ticket_st *ticket, |
134 | | gnutls_datum_t *ticket_data) |
135 | | { |
136 | | uint8_t *p; |
137 | | |
138 | | p = ticket_data->data; |
139 | | |
140 | | memcpy(p, ticket->key_name, TICKET_KEY_NAME_SIZE); |
141 | | p += TICKET_KEY_NAME_SIZE; |
142 | | |
143 | | memcpy(p, ticket->IV, TICKET_IV_SIZE); |
144 | | p += TICKET_IV_SIZE; |
145 | | |
146 | | _gnutls_write_uint16(ticket->encrypted_state_len, p); |
147 | | p += 2; |
148 | | |
149 | | /* We use memmove instead of memcpy here because |
150 | | * ticket->encrypted_state is allocated from |
151 | | * ticket_data->data, and thus both memory areas may overlap. |
152 | | */ |
153 | | memmove(p, ticket->encrypted_state, ticket->encrypted_state_len); |
154 | | p += ticket->encrypted_state_len; |
155 | | |
156 | | memcpy(p, ticket->mac, TICKET_MAC_SIZE); |
157 | | } |
158 | | |
159 | | static int digest_ticket(const gnutls_datum_t *key, struct ticket_st *ticket, |
160 | | uint8_t *digest) |
161 | 0 | { |
162 | 0 | mac_hd_st digest_hd; |
163 | 0 | uint16_t length16; |
164 | 0 | int ret; |
165 | |
|
166 | 0 | ret = _gnutls_mac_init(&digest_hd, mac_to_entry(TICKET_MAC_ALGO), |
167 | 0 | key->data, key->size); |
168 | 0 | if (ret < 0) { |
169 | 0 | gnutls_assert(); |
170 | 0 | return ret; |
171 | 0 | } |
172 | | |
173 | 0 | _gnutls_mac(&digest_hd, ticket->key_name, TICKET_KEY_NAME_SIZE); |
174 | 0 | _gnutls_mac(&digest_hd, ticket->IV, TICKET_IV_SIZE); |
175 | 0 | length16 = _gnutls_conv_uint16(ticket->encrypted_state_len); |
176 | 0 | _gnutls_mac(&digest_hd, &length16, 2); |
177 | 0 | _gnutls_mac(&digest_hd, ticket->encrypted_state, |
178 | 0 | ticket->encrypted_state_len); |
179 | 0 | _gnutls_mac_deinit(&digest_hd, digest); |
180 | |
|
181 | 0 | return 0; |
182 | 0 | } |
183 | | |
184 | | int _gnutls_decrypt_session_ticket(gnutls_session_t session, |
185 | | const gnutls_datum_t *ticket_data, |
186 | | gnutls_datum_t *state) |
187 | 0 | { |
188 | 0 | cipher_hd_st cipher_hd; |
189 | 0 | gnutls_datum_t IV; |
190 | 0 | gnutls_datum_t stek_key_name, stek_cipher_key, stek_mac_key; |
191 | 0 | uint8_t cmac[TICKET_MAC_SIZE]; |
192 | 0 | struct ticket_st ticket; |
193 | 0 | int ret; |
194 | | |
195 | | /* Retrieve ticket decryption keys */ |
196 | 0 | if (_gnutls_get_session_ticket_decryption_key( |
197 | 0 | session, ticket_data, &stek_key_name, &stek_mac_key, |
198 | 0 | &stek_cipher_key) < 0) |
199 | 0 | return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); |
200 | | |
201 | 0 | ret = unpack_ticket(ticket_data, &ticket); |
202 | 0 | if (ret < 0) |
203 | 0 | return ret; |
204 | | |
205 | | /* If the key name of the ticket does not match the one that is currently active, |
206 | | issue a new ticket. */ |
207 | 0 | if (memcmp(ticket.key_name, stek_key_name.data, stek_key_name.size)) { |
208 | 0 | ret = GNUTLS_E_DECRYPTION_FAILED; |
209 | 0 | goto cleanup; |
210 | 0 | } |
211 | | |
212 | | /* Check the integrity of ticket */ |
213 | 0 | ret = digest_ticket(&stek_mac_key, &ticket, cmac); |
214 | 0 | if (ret < 0) { |
215 | 0 | gnutls_assert(); |
216 | 0 | goto cleanup; |
217 | 0 | } |
218 | | |
219 | 0 | if (memcmp(ticket.mac, cmac, TICKET_MAC_SIZE)) { |
220 | 0 | ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); |
221 | 0 | goto cleanup; |
222 | 0 | } |
223 | | |
224 | 0 | if (ticket.encrypted_state_len % TICKET_BLOCK_SIZE != 0) { |
225 | 0 | ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); |
226 | 0 | goto cleanup; |
227 | 0 | } |
228 | | |
229 | | /* Decrypt encrypted_state */ |
230 | 0 | IV.data = ticket.IV; |
231 | 0 | IV.size = TICKET_IV_SIZE; |
232 | 0 | ret = _gnutls_cipher_init(&cipher_hd, cipher_to_entry(TICKET_CIPHER), |
233 | 0 | &stek_cipher_key, &IV, 0); |
234 | 0 | if (ret < 0) { |
235 | 0 | _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); |
236 | 0 | gnutls_assert(); |
237 | 0 | goto cleanup; |
238 | 0 | } |
239 | 0 | _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); |
240 | |
|
241 | 0 | ret = _gnutls_cipher_decrypt(&cipher_hd, ticket.encrypted_state, |
242 | 0 | ticket.encrypted_state_len); |
243 | 0 | if (ret < 0) { |
244 | 0 | gnutls_assert(); |
245 | 0 | goto cleanup2; |
246 | 0 | } |
247 | | |
248 | 0 | state->data = ticket.encrypted_state; |
249 | 0 | state->size = ticket.encrypted_state_len; |
250 | |
|
251 | 0 | ticket.encrypted_state = NULL; |
252 | |
|
253 | 0 | ret = 0; |
254 | |
|
255 | 0 | cleanup2: |
256 | 0 | _gnutls_cipher_deinit(&cipher_hd); |
257 | |
|
258 | 0 | cleanup: |
259 | 0 | deinit_ticket(&ticket); |
260 | |
|
261 | 0 | return ret; |
262 | 0 | } |
263 | | |
264 | | int _gnutls_encrypt_session_ticket(gnutls_session_t session, |
265 | | const gnutls_datum_t *state, |
266 | | gnutls_datum_t *ticket_data) |
267 | 0 | { |
268 | 0 | cipher_hd_st cipher_hd; |
269 | 0 | gnutls_datum_t IV; |
270 | 0 | gnutls_datum_t encrypted_state; |
271 | 0 | gnutls_datum_t result = { NULL, 0 }; |
272 | 0 | uint8_t iv[TICKET_IV_SIZE]; |
273 | 0 | gnutls_datum_t stek_cipher_key, stek_mac_key, stek_key_name; |
274 | 0 | struct ticket_st ticket; |
275 | 0 | int ret; |
276 | |
|
277 | 0 | encrypted_state.size = |
278 | 0 | ((state->size + TICKET_BLOCK_SIZE - 1) / TICKET_BLOCK_SIZE) * |
279 | 0 | TICKET_BLOCK_SIZE; |
280 | 0 | result.size = TICKET_KEY_NAME_SIZE + TICKET_IV_SIZE + 2 + |
281 | 0 | encrypted_state.size + TICKET_MAC_SIZE; |
282 | 0 | result.data = gnutls_calloc(1, result.size); |
283 | 0 | if (!result.data) { |
284 | 0 | return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); |
285 | 0 | } |
286 | 0 | encrypted_state.data = |
287 | 0 | result.data + TICKET_KEY_NAME_SIZE + TICKET_IV_SIZE + 2; |
288 | 0 | memcpy(encrypted_state.data, state->data, state->size); |
289 | | |
290 | | /* Retrieve ticket encryption keys */ |
291 | 0 | if (_gnutls_get_session_ticket_encryption_key(session, &stek_key_name, |
292 | 0 | &stek_mac_key, |
293 | 0 | &stek_cipher_key) < 0) { |
294 | 0 | ret = GNUTLS_E_ENCRYPTION_FAILED; |
295 | 0 | goto cleanup; |
296 | 0 | } |
297 | | |
298 | | /* Encrypt state */ |
299 | 0 | IV.data = iv; |
300 | 0 | IV.size = TICKET_IV_SIZE; |
301 | |
|
302 | 0 | ret = gnutls_rnd(GNUTLS_RND_NONCE, iv, TICKET_IV_SIZE); |
303 | 0 | if (ret < 0) { |
304 | 0 | gnutls_assert(); |
305 | 0 | goto cleanup; |
306 | 0 | } |
307 | | |
308 | 0 | ret = _gnutls_cipher_init(&cipher_hd, cipher_to_entry(TICKET_CIPHER), |
309 | 0 | &stek_cipher_key, &IV, 1); |
310 | 0 | if (ret < 0) { |
311 | 0 | _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); |
312 | 0 | gnutls_assert(); |
313 | 0 | goto cleanup; |
314 | 0 | } |
315 | 0 | _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); |
316 | |
|
317 | 0 | ret = _gnutls_cipher_encrypt(&cipher_hd, encrypted_state.data, |
318 | 0 | encrypted_state.size); |
319 | 0 | if (ret < 0) { |
320 | 0 | gnutls_assert(); |
321 | 0 | goto cleanup2; |
322 | 0 | } |
323 | | |
324 | | /* Fill the ticket structure to compute MAC. */ |
325 | 0 | memcpy(ticket.key_name, stek_key_name.data, stek_key_name.size); |
326 | 0 | memcpy(ticket.IV, IV.data, IV.size); |
327 | 0 | ticket.encrypted_state_len = encrypted_state.size; |
328 | 0 | ticket.encrypted_state = encrypted_state.data; |
329 | |
|
330 | 0 | ret = digest_ticket(&stek_mac_key, &ticket, ticket.mac); |
331 | 0 | if (ret < 0) { |
332 | 0 | gnutls_assert(); |
333 | 0 | goto cleanup2; |
334 | 0 | } |
335 | | |
336 | 0 | pack_ticket(&ticket, &result); |
337 | 0 | ticket_data->data = result.data; |
338 | 0 | ticket_data->size = result.size; |
339 | 0 | result.data = NULL; |
340 | |
|
341 | 0 | cleanup2: |
342 | 0 | _gnutls_cipher_deinit(&cipher_hd); |
343 | |
|
344 | 0 | cleanup: |
345 | 0 | _gnutls_free_datum(&result); |
346 | |
|
347 | 0 | return ret; |
348 | 0 | } |
349 | | |
350 | | static int unpack_session(gnutls_session_t session, const gnutls_datum_t *state) |
351 | 0 | { |
352 | 0 | int ret; |
353 | |
|
354 | 0 | if (unlikely(!state)) |
355 | 0 | return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); |
356 | | |
357 | 0 | ret = _gnutls_session_unpack(session, state); |
358 | 0 | if (ret < 0) |
359 | 0 | return gnutls_assert_val(ret); |
360 | | |
361 | 0 | ret = _gnutls_check_resumed_params(session); |
362 | 0 | if (ret < 0) |
363 | 0 | return gnutls_assert_val(ret); |
364 | | |
365 | 0 | session->internals.resumed = true; |
366 | 0 | return 0; |
367 | 0 | } |
368 | | |
369 | | static int session_ticket_recv_params(gnutls_session_t session, |
370 | | const uint8_t *data, size_t data_size) |
371 | 0 | { |
372 | 0 | gnutls_datum_t ticket_data; |
373 | 0 | gnutls_datum_t state; |
374 | 0 | int ret; |
375 | |
|
376 | 0 | if (session->internals.flags & |
377 | 0 | (GNUTLS_NO_TICKETS | GNUTLS_NO_TICKETS_TLS12)) |
378 | 0 | return 0; |
379 | | |
380 | 0 | if (session->security_parameters.entity == GNUTLS_SERVER) { |
381 | | /* The client requested a new session ticket. */ |
382 | 0 | if (data_size == 0) { |
383 | 0 | session->internals.session_ticket_renew = 1; |
384 | 0 | return 0; |
385 | 0 | } |
386 | | |
387 | 0 | ticket_data.data = (void *)data; |
388 | 0 | ticket_data.size = data_size; |
389 | 0 | if ((ret = _gnutls_decrypt_session_ticket(session, &ticket_data, |
390 | 0 | &state)) == 0) { |
391 | 0 | ret = unpack_session(session, &state); |
392 | |
|
393 | 0 | _gnutls_free_datum(&state); |
394 | 0 | } |
395 | |
|
396 | 0 | if (ret < 0) { |
397 | 0 | session->internals.session_ticket_renew = 1; |
398 | 0 | return 0; |
399 | 0 | } |
400 | 0 | } else { /* Client */ |
401 | |
|
402 | 0 | if (data_size == 0) { |
403 | 0 | session->internals.session_ticket_renew = 1; |
404 | 0 | return 0; |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | 0 | return 0; |
409 | 0 | } |
410 | | |
411 | | /* returns a positive number if we send the extension data, (0) if we |
412 | | do not want to send it, and a negative number on failure. |
413 | | */ |
414 | | static int session_ticket_send_params(gnutls_session_t session, |
415 | | gnutls_buffer_st *extdata) |
416 | 0 | { |
417 | 0 | session_ticket_ext_st *priv = NULL; |
418 | 0 | gnutls_ext_priv_data_t epriv; |
419 | 0 | int ret; |
420 | |
|
421 | 0 | if (session->internals.flags & |
422 | 0 | (GNUTLS_NO_TICKETS | GNUTLS_NO_TICKETS_TLS12)) |
423 | 0 | return 0; |
424 | | |
425 | 0 | if (session->security_parameters.entity == GNUTLS_SERVER) { |
426 | 0 | if (session->internals.session_ticket_renew) { |
427 | 0 | return GNUTLS_E_INT_RET_0; |
428 | 0 | } |
429 | 0 | } else { |
430 | 0 | ret = _gnutls_hello_ext_get_resumed_priv( |
431 | 0 | session, GNUTLS_EXTENSION_SESSION_TICKET, &epriv); |
432 | 0 | if (ret >= 0) |
433 | 0 | priv = epriv; |
434 | | |
435 | | /* no previous data. Just advertise it */ |
436 | 0 | if (ret < 0) |
437 | 0 | return GNUTLS_E_INT_RET_0; |
438 | | |
439 | | /* previous data had session tickets disabled. Don't advertise. Ignore. */ |
440 | 0 | if (session->internals.flags & GNUTLS_NO_TICKETS) |
441 | 0 | return 0; |
442 | | |
443 | 0 | if (priv->session_ticket_len > 0) { |
444 | 0 | ret = _gnutls_buffer_append_data( |
445 | 0 | extdata, priv->session_ticket, |
446 | 0 | priv->session_ticket_len); |
447 | 0 | if (ret < 0) |
448 | 0 | return gnutls_assert_val(ret); |
449 | | |
450 | 0 | return priv->session_ticket_len; |
451 | 0 | } |
452 | 0 | } |
453 | 0 | return 0; |
454 | 0 | } |
455 | | |
456 | | static void session_ticket_deinit_data(gnutls_ext_priv_data_t epriv) |
457 | 0 | { |
458 | 0 | session_ticket_ext_st *priv = epriv; |
459 | |
|
460 | 0 | gnutls_free(priv->session_ticket); |
461 | 0 | gnutls_free(priv); |
462 | 0 | } |
463 | | |
464 | | static int session_ticket_pack(gnutls_ext_priv_data_t epriv, |
465 | | gnutls_buffer_st *ps) |
466 | 0 | { |
467 | 0 | session_ticket_ext_st *priv = epriv; |
468 | 0 | int ret; |
469 | |
|
470 | 0 | BUFFER_APPEND_PFX4(ps, priv->session_ticket, priv->session_ticket_len); |
471 | |
|
472 | 0 | return 0; |
473 | 0 | } |
474 | | |
475 | | static int session_ticket_unpack(gnutls_buffer_st *ps, |
476 | | gnutls_ext_priv_data_t *_priv) |
477 | 0 | { |
478 | 0 | session_ticket_ext_st *priv = NULL; |
479 | 0 | int ret; |
480 | 0 | gnutls_ext_priv_data_t epriv; |
481 | 0 | gnutls_datum_t ticket; |
482 | |
|
483 | 0 | priv = gnutls_calloc(1, sizeof(*priv)); |
484 | 0 | if (priv == NULL) { |
485 | 0 | gnutls_assert(); |
486 | 0 | return GNUTLS_E_MEMORY_ERROR; |
487 | 0 | } |
488 | | |
489 | 0 | BUFFER_POP_DATUM(ps, &ticket); |
490 | 0 | priv->session_ticket = ticket.data; |
491 | 0 | priv->session_ticket_len = ticket.size; |
492 | |
|
493 | 0 | epriv = priv; |
494 | 0 | *_priv = epriv; |
495 | |
|
496 | 0 | return 0; |
497 | | |
498 | 0 | error: |
499 | 0 | gnutls_free(priv); |
500 | 0 | return ret; |
501 | 0 | } |
502 | | |
503 | | /** |
504 | | * gnutls_session_ticket_key_generate: |
505 | | * @key: is a pointer to a #gnutls_datum_t which will contain a newly |
506 | | * created key. |
507 | | * |
508 | | * Generate a random key to encrypt security parameters within |
509 | | * SessionTicket. |
510 | | * |
511 | | * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an |
512 | | * error code. |
513 | | * |
514 | | * Since: 2.10.0 |
515 | | **/ |
516 | | int gnutls_session_ticket_key_generate(gnutls_datum_t *key) |
517 | 0 | { |
518 | 0 | if (_gnutls_fips_mode_enabled()) { |
519 | 0 | int ret; |
520 | | /* in FIPS140-2 mode gnutls_key_generate imposes |
521 | | * some limits on allowed key size, thus it is not |
522 | | * used. These limits do not affect this function as |
523 | | * it does not generate a "key" but rather key material |
524 | | * that includes nonces and other stuff. */ |
525 | 0 | key->data = gnutls_malloc(TICKET_MASTER_KEY_SIZE); |
526 | 0 | if (key->data == NULL) |
527 | 0 | return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); |
528 | | |
529 | 0 | key->size = TICKET_MASTER_KEY_SIZE; |
530 | 0 | ret = gnutls_rnd(GNUTLS_RND_RANDOM, key->data, key->size); |
531 | 0 | if (ret < 0) { |
532 | 0 | gnutls_free(key->data); |
533 | 0 | return ret; |
534 | 0 | } |
535 | 0 | return 0; |
536 | 0 | } else { |
537 | 0 | return gnutls_key_generate(key, TICKET_MASTER_KEY_SIZE); |
538 | 0 | } |
539 | 0 | } |
540 | | |
541 | | /** |
542 | | * gnutls_session_ticket_enable_client: |
543 | | * @session: is a #gnutls_session_t type. |
544 | | * |
545 | | * Request that the client should attempt session resumption using |
546 | | * SessionTicket. This call is typically unnecessary as session |
547 | | * tickets are enabled by default. |
548 | | * |
549 | | * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an |
550 | | * error code. |
551 | | * |
552 | | * Since: 2.10.0 |
553 | | **/ |
554 | | int gnutls_session_ticket_enable_client(gnutls_session_t session) |
555 | 0 | { |
556 | 0 | if (!session) { |
557 | 0 | gnutls_assert(); |
558 | 0 | return GNUTLS_E_INVALID_REQUEST; |
559 | 0 | } |
560 | | |
561 | 0 | session->internals.flags &= ~GNUTLS_NO_TICKETS; |
562 | |
|
563 | 0 | return 0; |
564 | 0 | } |
565 | | |
566 | | /** |
567 | | * gnutls_session_ticket_enable_server: |
568 | | * @session: is a #gnutls_session_t type. |
569 | | * @key: key to encrypt session parameters. |
570 | | * |
571 | | * Request that the server should attempt session resumption using |
572 | | * session tickets, i.e., by delegating storage to the client. |
573 | | * @key must be initialized using gnutls_session_ticket_key_generate(). |
574 | | * To avoid leaking that key, use gnutls_memset() prior to |
575 | | * releasing it. |
576 | | * |
577 | | * The default ticket expiration time can be overridden using |
578 | | * gnutls_db_set_cache_expiration(). |
579 | | * |
580 | | * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an |
581 | | * error code. |
582 | | * |
583 | | * Since: 2.10.0 |
584 | | **/ |
585 | | int gnutls_session_ticket_enable_server(gnutls_session_t session, |
586 | | const gnutls_datum_t *key) |
587 | 0 | { |
588 | 0 | int ret; |
589 | |
|
590 | 0 | if (!session || !key || key->size != TICKET_MASTER_KEY_SIZE || |
591 | 0 | !key->data) { |
592 | 0 | gnutls_assert(); |
593 | 0 | return GNUTLS_E_INVALID_REQUEST; |
594 | 0 | } |
595 | | |
596 | 0 | ret = _gnutls_initialize_session_ticket_key_rotation(session, key); |
597 | 0 | if (ret < 0) |
598 | 0 | return gnutls_assert_val(ret); |
599 | | |
600 | 0 | session->internals.flags &= ~GNUTLS_NO_TICKETS; |
601 | |
|
602 | 0 | return 0; |
603 | 0 | } |
604 | | |
605 | | void _gnutls_session_ticket_disable_server(gnutls_session_t session) |
606 | 0 | { |
607 | 0 | session->internals.flags |= GNUTLS_NO_TICKETS; |
608 | 0 | } |
609 | | |
610 | | /* |
611 | | * Return zero if session tickets haven't been enabled. |
612 | | */ |
613 | | int _gnutls_send_new_session_ticket(gnutls_session_t session, int again) |
614 | 0 | { |
615 | 0 | mbuffer_st *bufel = NULL; |
616 | 0 | uint8_t *data = NULL, *p; |
617 | 0 | int data_size = 0; |
618 | 0 | int ret; |
619 | 0 | gnutls_datum_t state = { NULL, 0 }; |
620 | 0 | uint16_t epoch_saved = session->security_parameters.epoch_write; |
621 | 0 | gnutls_datum_t ticket_data; |
622 | |
|
623 | 0 | if (again == 0) { |
624 | 0 | if (session->internals.flags & |
625 | 0 | (GNUTLS_NO_TICKETS | GNUTLS_NO_TICKETS_TLS12)) { |
626 | 0 | return 0; |
627 | 0 | } |
628 | 0 | if (!session->key.stek_initialized) { |
629 | 0 | return 0; |
630 | 0 | } |
631 | 0 | if (!session->internals.session_ticket_renew) { |
632 | 0 | return 0; |
633 | 0 | } |
634 | | |
635 | 0 | _gnutls_handshake_log("HSK[%p]: sending session ticket\n", |
636 | 0 | session); |
637 | | |
638 | | /* XXX: Temporarily set write algorithms to be used. |
639 | | _gnutls_write_connection_state_init() does this job, but it also |
640 | | triggers encryption, while NewSessionTicket should not be |
641 | | encrypted in the record layer. */ |
642 | 0 | ret = _gnutls_epoch_set_keys( |
643 | 0 | session, session->security_parameters.epoch_next, 0); |
644 | 0 | if (ret < 0) { |
645 | 0 | gnutls_assert(); |
646 | 0 | return ret; |
647 | 0 | } |
648 | | |
649 | | /* Under TLS1.2 with session tickets, the session ID is used for different |
650 | | * purposes than the TLS1.0 session ID. Ensure that there is an internally |
651 | | * set value which the server will see on the original and resumed sessions */ |
652 | 0 | if (!session->internals.resumed) { |
653 | 0 | ret = _gnutls_generate_session_id( |
654 | 0 | session->security_parameters.session_id, |
655 | 0 | &session->security_parameters.session_id_size); |
656 | 0 | if (ret < 0) { |
657 | 0 | gnutls_assert(); |
658 | 0 | return ret; |
659 | 0 | } |
660 | 0 | } |
661 | | |
662 | 0 | session->security_parameters.epoch_write = |
663 | 0 | session->security_parameters.epoch_next; |
664 | | |
665 | | /* Pack security parameters. */ |
666 | 0 | ret = _gnutls_session_pack(session, &state); |
667 | 0 | if (ret < 0) { |
668 | 0 | gnutls_assert(); |
669 | 0 | return ret; |
670 | 0 | } |
671 | | |
672 | | /* Generate an encrypted ticket */ |
673 | 0 | ret = _gnutls_encrypt_session_ticket(session, &state, |
674 | 0 | &ticket_data); |
675 | 0 | session->security_parameters.epoch_write = epoch_saved; |
676 | 0 | _gnutls_free_datum(&state); |
677 | 0 | if (ret < 0) { |
678 | 0 | gnutls_assert(); |
679 | 0 | return ret; |
680 | 0 | } |
681 | | |
682 | 0 | bufel = _gnutls_handshake_alloc(session, |
683 | 0 | 4 + 2 + ticket_data.size); |
684 | 0 | if (!bufel) { |
685 | 0 | gnutls_assert(); |
686 | 0 | _gnutls_free_datum(&ticket_data); |
687 | 0 | return GNUTLS_E_MEMORY_ERROR; |
688 | 0 | } |
689 | | |
690 | 0 | data = _mbuffer_get_udata_ptr(bufel); |
691 | 0 | p = data; |
692 | |
|
693 | 0 | _gnutls_write_uint32(session->internals.expire_time, p); |
694 | 0 | p += 4; |
695 | |
|
696 | 0 | _gnutls_write_uint16(ticket_data.size, p); |
697 | 0 | p += 2; |
698 | |
|
699 | 0 | memcpy(p, ticket_data.data, ticket_data.size); |
700 | 0 | p += ticket_data.size; |
701 | |
|
702 | 0 | _gnutls_free_datum(&ticket_data); |
703 | |
|
704 | 0 | data_size = p - data; |
705 | |
|
706 | 0 | session->internals.hsk_flags |= HSK_TLS12_TICKET_SENT; |
707 | 0 | } |
708 | 0 | return _gnutls_send_handshake(session, data_size ? bufel : NULL, |
709 | 0 | GNUTLS_HANDSHAKE_NEW_SESSION_TICKET); |
710 | 0 | } |
711 | | |
712 | | /* |
713 | | * Return zero if session tickets haven't been enabled. |
714 | | */ |
715 | | int _gnutls_recv_new_session_ticket(gnutls_session_t session) |
716 | 0 | { |
717 | 0 | uint8_t *p; |
718 | 0 | int data_size; |
719 | 0 | gnutls_buffer_st buf; |
720 | 0 | uint16_t ticket_len; |
721 | 0 | int ret; |
722 | 0 | session_ticket_ext_st *priv = NULL; |
723 | 0 | gnutls_ext_priv_data_t epriv; |
724 | |
|
725 | 0 | if (session->internals.flags & |
726 | 0 | (GNUTLS_NO_TICKETS | GNUTLS_NO_TICKETS_TLS12)) |
727 | 0 | return 0; |
728 | 0 | if (!session->internals.session_ticket_renew) |
729 | 0 | return 0; |
730 | | |
731 | | /* This is the last flight and peer cannot be sure |
732 | | * we have received it unless we notify him. So we |
733 | | * wait for a message and retransmit if needed. */ |
734 | 0 | if (IS_DTLS(session) && !_dtls_is_async(session)) { |
735 | 0 | unsigned have; |
736 | 0 | mbuffer_st *bufel = NULL; |
737 | |
|
738 | 0 | have = gnutls_record_check_pending(session) + |
739 | 0 | record_check_unprocessed(session); |
740 | |
|
741 | 0 | if (have != 0) { |
742 | 0 | bufel = _mbuffer_head_get_first( |
743 | 0 | &session->internals.record_buffer, NULL); |
744 | 0 | } |
745 | |
|
746 | 0 | if (have == 0 || (bufel && bufel->type != GNUTLS_HANDSHAKE)) { |
747 | 0 | ret = _dtls_wait_and_retransmit(session); |
748 | 0 | if (ret < 0) |
749 | 0 | return gnutls_assert_val(ret); |
750 | 0 | } |
751 | 0 | } |
752 | | |
753 | 0 | ret = _gnutls_recv_handshake( |
754 | 0 | session, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET, 0, &buf); |
755 | 0 | if (ret < 0) |
756 | 0 | return gnutls_assert_val_fatal(ret); |
757 | | |
758 | 0 | p = buf.data; |
759 | 0 | data_size = buf.length; |
760 | |
|
761 | 0 | DECR_LENGTH_COM(data_size, 4, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; |
762 | 0 | goto error); |
763 | | /* skip over lifetime hint */ |
764 | 0 | p += 4; |
765 | |
|
766 | 0 | DECR_LENGTH_COM(data_size, 2, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; |
767 | 0 | goto error); |
768 | 0 | ticket_len = _gnutls_read_uint16(p); |
769 | 0 | p += 2; |
770 | |
|
771 | 0 | DECR_LENGTH_COM(data_size, ticket_len, |
772 | 0 | ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; |
773 | 0 | goto error); |
774 | | |
775 | 0 | priv = gnutls_calloc(1, sizeof(*priv)); |
776 | 0 | if (!priv) { |
777 | 0 | gnutls_assert(); |
778 | 0 | ret = GNUTLS_E_MEMORY_ERROR; |
779 | 0 | goto error; |
780 | 0 | } |
781 | 0 | if (ticket_len > 0) { |
782 | 0 | priv->session_ticket = |
783 | 0 | gnutls_realloc_fast(priv->session_ticket, ticket_len); |
784 | 0 | if (!priv->session_ticket) { |
785 | 0 | gnutls_free(priv); |
786 | 0 | gnutls_assert(); |
787 | 0 | ret = GNUTLS_E_MEMORY_ERROR; |
788 | 0 | goto error; |
789 | 0 | } |
790 | 0 | memcpy(priv->session_ticket, p, ticket_len); |
791 | 0 | } |
792 | 0 | priv->session_ticket_len = ticket_len; |
793 | 0 | epriv = priv; |
794 | | |
795 | | /* Discard the current session ID. (RFC5077 3.4) */ |
796 | 0 | ret = _gnutls_generate_session_id( |
797 | 0 | session->security_parameters.session_id, |
798 | 0 | &session->security_parameters.session_id_size); |
799 | 0 | if (ret < 0) { |
800 | 0 | gnutls_assert(); |
801 | 0 | session_ticket_deinit_data(epriv); |
802 | 0 | ret = GNUTLS_E_INTERNAL_ERROR; |
803 | 0 | goto error; |
804 | 0 | } |
805 | 0 | ret = 0; |
806 | |
|
807 | 0 | _gnutls_handshake_log("HSK[%p]: received session ticket\n", session); |
808 | 0 | session->internals.hsk_flags |= HSK_TICKET_RECEIVED; |
809 | |
|
810 | 0 | _gnutls_hello_ext_set_priv(session, GNUTLS_EXTENSION_SESSION_TICKET, |
811 | 0 | epriv); |
812 | |
|
813 | 0 | error: |
814 | 0 | _gnutls_buffer_clear(&buf); |
815 | |
|
816 | 0 | return ret; |
817 | 0 | } |