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