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