1 /*
2 * Copyright (C) 2016-2018 Red Hat, Inc.
3 *
4 * Author: Nikos Mavrogiannopoulos
5 *
6 * This file is part of GnuTLS.
7 *
8 * GnuTLS is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * GnuTLS 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 * 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 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <gnutls/gnutls.h>
31 #include <assert.h>
32 #include "utils.h"
33 #include "virt-time.h"
34 #include "eagain-common.h"
35 #include "cert-common.h"
36
37 /* This test checks whether the lifetime of a resumed session can
38 * be extended past the designated one */
39
40 const char *side;
41
tls_log_func(int level,const char * str)42 static void tls_log_func(int level, const char *str)
43 {
44 fprintf(stderr, "%s|<%d>| %s", side, level, str);
45 }
46
47 struct hsk_st {
48 unsigned sent_nst; /* whether the new session ticket was sent */
49 unsigned sent_psk; /* whether the PSK extension was sent */
50 unsigned sleep_at_finished; /* how long to wait at finished message reception */
51
52 };
53
ext_hook_func(void * ctx,unsigned tls_id,const unsigned char * data,unsigned size)54 static int ext_hook_func(void *ctx, unsigned tls_id,
55 const unsigned char *data, unsigned size)
56 {
57 if (tls_id == 41) {
58 struct hsk_st *h = ctx;
59 h->sent_psk = 1;
60 }
61 return 0;
62 }
63
handshake_callback(gnutls_session_t session,unsigned int htype,unsigned post,unsigned int incoming,const gnutls_datum_t * msg)64 static int handshake_callback(gnutls_session_t session, unsigned int htype,
65 unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
66 {
67 struct hsk_st *h = gnutls_session_get_ptr(session);
68
69 if (htype == GNUTLS_HANDSHAKE_FINISHED && !incoming) {
70 if (h->sleep_at_finished)
71 virt_sec_sleep(h->sleep_at_finished);
72 return 0;
73 } else if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO) {
74 gnutls_ext_raw_parse(h, ext_hook_func, msg,
75 GNUTLS_EXT_RAW_FLAG_TLS_CLIENT_HELLO);
76 }
77
78 if (htype != GNUTLS_HANDSHAKE_NEW_SESSION_TICKET)
79 return 0;
80
81 if (h)
82 h->sent_nst = 1;
83 return 0;
84 }
85
86 /* Returns true if resumed */
handshake(const char * prio,unsigned t,const gnutls_datum_t * sdata,gnutls_datum_t * ndata,gnutls_datum_t * skey,struct hsk_st * h)87 static unsigned handshake(const char *prio, unsigned t, const gnutls_datum_t *sdata,
88 gnutls_datum_t *ndata,
89 gnutls_datum_t *skey,
90 struct hsk_st *h)
91 {
92 int ret;
93 /* Server stuff. */
94 gnutls_certificate_credentials_t serverx509cred;
95 gnutls_session_t server;
96 int sret = GNUTLS_E_AGAIN;
97 /* Client stuff. */
98 gnutls_certificate_credentials_t clientx509cred;
99 gnutls_session_t client;
100 int cret = GNUTLS_E_AGAIN;
101 char buf[128];
102
103 gnutls_global_set_log_function(tls_log_func);
104 if (debug)
105 gnutls_global_set_log_level(6);
106
107 assert(gnutls_certificate_allocate_credentials(&serverx509cred)>=0);
108 assert(gnutls_certificate_set_x509_key_mem(serverx509cred,
109 &server_cert, &server_key,
110 GNUTLS_X509_FMT_PEM)>=0);
111
112 assert(gnutls_init(&server, GNUTLS_SERVER)>=0);
113 gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE,
114 serverx509cred);
115 assert(gnutls_priority_set_direct(server, prio, NULL)>=0);
116 gnutls_transport_set_push_function(server, server_push);
117 gnutls_transport_set_pull_function(server, server_pull);
118 gnutls_transport_set_ptr(server, server);
119 gnutls_session_set_ptr(server, h);
120
121 gnutls_db_set_cache_expiration(server, t);
122 assert(gnutls_session_ticket_enable_server(server, skey) >= 0);
123
124 gnutls_handshake_set_hook_function(server, -1,
125 GNUTLS_HOOK_POST,
126 handshake_callback);
127
128 assert(gnutls_certificate_allocate_credentials(&clientx509cred)>=0);
129 assert(gnutls_certificate_set_x509_trust_mem(clientx509cred, &ca_cert, GNUTLS_X509_FMT_PEM)>=0);
130 assert(gnutls_init(&client, GNUTLS_CLIENT)>=0);
131
132 assert(gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE,
133 clientx509cred)>=0);
134
135 assert(gnutls_priority_set_direct(client, prio, NULL)>=0);
136 gnutls_transport_set_push_function(client, client_push);
137 gnutls_transport_set_pull_function(client, client_pull);
138 gnutls_transport_set_ptr(client, client);
139
140 if (sdata) {
141 assert(gnutls_session_set_data(client, sdata->data, sdata->size)>=0);
142 }
143
144 memset(buf, 0, sizeof(buf));
145 ret = gnutls_session_set_data(client, buf, sizeof(buf));
146 if (ret != GNUTLS_E_DB_ERROR) {
147 fail("unexpected error: %s\n", gnutls_strerror(ret));
148 }
149
150 HANDSHAKE(client, server);
151
152 gnutls_record_recv(client, buf, sizeof(buf));
153
154 if (ndata) {
155 ret = gnutls_session_get_data2(client, ndata);
156 if (ret < 0) {
157 fail("unexpected error: %s\n", gnutls_strerror(ret));
158 }
159 }
160
161 ret = gnutls_session_is_resumed(client);
162
163 gnutls_deinit(server);
164 gnutls_deinit(client);
165
166 gnutls_certificate_free_credentials(serverx509cred);
167 gnutls_certificate_free_credentials(clientx509cred);
168
169 reset_buffers();
170 return ret;
171 }
172
173 /* @t is the lifetime of the first ticket, @s is the
174 * time to wait before asking for a ticket the last try */
start(const char * name,const char * prio,unsigned t,unsigned s)175 static void start(const char *name, const char *prio, unsigned t, unsigned s)
176 {
177 gnutls_datum_t sdata, ndata, skey;
178 unsigned ret;
179 struct hsk_st h;
180 memset(&h, 0, sizeof(h));
181
182 success("trying %s\n", name);
183
184 assert(gnutls_session_ticket_key_generate(&skey)>=0);
185
186 /* step1: get a fresh ticket */
187 ret = handshake(prio, t, NULL, &sdata, &skey, &h);
188 assert(ret == 0);
189 assert(h.sent_nst != 0);
190 memset(&h, 0, sizeof(h));
191
192 if (debug)
193 success("completed first handshake\n");
194
195 if (s)
196 virt_sec_sleep(s);
197
198 /* step2: get a ticket from the resumed session of the first */
199 ret = handshake(prio, t, &sdata, &ndata, &skey, &h);
200 assert(ret != 0);
201 assert(h.sent_nst != 0);
202 memset(&h, 0, sizeof(h));
203
204 /* wait until the ticket we got in step1 is invalid, although
205 * the ticket we got in step2 is valid */
206
207 if (debug)
208 success("completed second handshake\n");
209
210 if (s)
211 virt_sec_sleep(s);
212
213 ret = handshake(prio, t, &ndata, NULL, &skey, &h);
214
215 if (s) {
216 if (ret != 0)
217 fail("server resumed session even if ticket expired!\n");
218
219 /* we shouldn't have sent the PSK extension as the ticket was expired */
220 assert(h.sent_psk == 0);
221 }
222
223 gnutls_free(ndata.data);
224 gnutls_free(sdata.data);
225 gnutls_free(skey.data);
226 }
227
228 /* @t is the lifetime of the first ticket, @s is the
229 * time to wait before asking for a ticket the last try
230 *
231 * This makes the ticket to expire during handshake (after resumtion),
232 * but before the client receives the new session ticket. In that
233 * case the server shouldn't send a session ticket.
234 */
start2(const char * name,const char * prio,unsigned t,unsigned s)235 static void start2(const char *name, const char *prio, unsigned t, unsigned s)
236 {
237 gnutls_datum_t sdata, ndata, skey;
238 unsigned ret;
239 struct hsk_st h;
240 memset(&h, 0, sizeof(h));
241
242 success("trying %s\n", name);
243
244 assert(gnutls_session_ticket_key_generate(&skey)>=0);
245
246 /* step1: get a fresh ticket */
247 ret = handshake(prio, t, NULL, &sdata, &skey, &h);
248 assert(ret == 0);
249 assert(h.sent_nst != 0);
250 memset(&h, 0, sizeof(h));
251
252 /* step2: get a ticket from the resumed session of the first */
253 ret = handshake(prio, t, &sdata, &ndata, &skey, &h);
254 assert(ret != 0);
255 assert(h.sent_nst != 0);
256 memset(&h, 0, sizeof(h));
257
258 /* wait until the ticket we got in step1 is invalid, although
259 * the ticket we got in step2 is valid */
260
261 if (s)
262 h.sleep_at_finished = s;
263
264 ret = handshake(prio, t, &ndata, NULL, &skey, &h);
265
266 assert(ret != 0);
267 if (h.sent_nst != 0)
268 fail("server sent session ticket even if ticket expired!\n");
269
270 gnutls_free(ndata.data);
271 gnutls_free(sdata.data);
272 gnutls_free(skey.data);
273 }
274
doit(void)275 void doit(void)
276 {
277 virt_time_init();
278
279 start("TLS1.3 sanity", "NORMAL:-VERS-ALL:+VERS-TLS1.3", 64, 0);
280 start("TLS1.3 ticket extension", "NORMAL:-VERS-ALL:+VERS-TLS1.3", 5, 3);
281 start2("TLS1.3 ticket extension - expires at handshake", "NORMAL:-VERS-ALL:+VERS-TLS1.3", 2, 3);
282 }
283