1 /*
2  * Copyright (C) 2004-2012 Free Software Foundation, Inc.
3  * Copyright (C) 2013 Adam Sampson <ats@offog.org>
4  *
5  * Author: Nikos Mavrogiannopoulos
6  *
7  * This file is part of GnuTLS.
8  *
9  * GnuTLS is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * GnuTLS is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with GnuTLS; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 /* Parts copied from GnuTLS example programs. */
25 
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 
33 #if defined(_WIN32)
34 
main(int argc,char ** argv)35 int main(int argc, char **argv)
36 {
37 	exit(77);
38 }
39 
40 #else
41 
42 #include <string.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #if !defined(_WIN32)
46 #include <sys/wait.h>
47 #endif
48 #include <unistd.h>
49 #include <gnutls/gnutls.h>
50 #include <gnutls/dtls.h>
51 #include <sys/wait.h>
52 #include <signal.h>
53 
54 #include "utils.h"
55 
56 static void wrap_db_init(void);
57 static void wrap_db_deinit(void);
58 static int wrap_db_store(void *dbf, gnutls_datum_t key,
59 			 gnutls_datum_t data);
60 static gnutls_datum_t wrap_db_fetch(void *dbf, gnutls_datum_t key);
61 static int wrap_db_delete(void *dbf, gnutls_datum_t key);
62 
63 #define TLS_SESSION_CACHE 50
64 
65 struct params_res {
66 	const char *desc;
67 	int enable_db;
68 	int enable_session_ticket_server;
69 	int enable_session_ticket_client;
70 	int expect_resume;
71 };
72 
73 pid_t child;
74 
75 struct params_res resume_tests[] = {
76 	{"try to resume from db", 50, 0, 0, 1},
77 	{"try to resume from session ticket", 0, 1, 1, 1},
78 	{"try to resume from session ticket (server only)", 0, 1, 0, 0},
79 	{"try to resume from session ticket (client only)", 0, 0, 1, 0},
80 	{NULL, -1}
81 };
82 
83 /* A very basic TLS client, with anonymous authentication.
84  */
85 
86 #define SESSIONS 2
87 #define MAX_BUF 5*1024
88 #define MSG "Hello TLS"
89 
tls_log_func(int level,const char * str)90 static void tls_log_func(int level, const char *str)
91 {
92 	fprintf(stderr, "%s |<%d>| %s", child ? "server" : "client", level,
93 		str);
94 }
95 
client(int sds[],struct params_res * params)96 static void client(int sds[], struct params_res *params)
97 {
98 	int ret, ii;
99 	gnutls_session_t session;
100 	char buffer[MAX_BUF + 1];
101 	gnutls_anon_client_credentials_t anoncred;
102 	/* Need to enable anonymous KX specifically. */
103 
104 	/* variables used in session resuming
105 	 */
106 	int t;
107 	gnutls_datum_t session_data;
108 
109 	if (debug) {
110 		gnutls_global_set_log_function(tls_log_func);
111 		gnutls_global_set_log_level(3);
112 	}
113 	global_init();
114 
115 	gnutls_anon_allocate_client_credentials(&anoncred);
116 
117 	for (t = 0; t < SESSIONS; t++) {
118 		int sd = sds[t];
119 
120 		/* Initialize TLS session
121 		 */
122 		gnutls_init(&session,
123 			    GNUTLS_CLIENT | GNUTLS_DATAGRAM);
124 
125 		/* Use default priorities */
126 		if (params->enable_session_ticket_client)
127 			gnutls_priority_set_direct(session,
128 					   "NONE:+VERS-DTLS1.0:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-DH",
129 					   NULL);
130 		else
131 			gnutls_priority_set_direct(session,
132 					   "NONE:+VERS-DTLS1.0:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-DH:%NO_TICKETS",
133 					   NULL);
134 
135 		/* put the anonymous credentials to the current session
136 		 */
137 		gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
138 
139 		if (t > 0) {
140 			/* if this is not the first time we connect */
141 			gnutls_session_set_data(session, session_data.data,
142 						session_data.size);
143 			gnutls_free(session_data.data);
144 		}
145 
146 		gnutls_transport_set_int(session, sd);
147 
148 		/* Perform the TLS handshake
149 		 */
150 		gnutls_dtls_set_timeouts(session, 3*1000, 240 * 1000);
151 		do {
152 			ret = gnutls_handshake(session);
153 		} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
154 
155 		if (ret < 0) {
156 			gnutls_perror(ret);
157 			fail("client: Handshake failed\n");
158 			goto end;
159 		} else {
160 			if (debug)
161 				success
162 				    ("client: Handshake was completed\n");
163 		}
164 
165 		if (t == 0) {	/* the first time we connect */
166 			/* get the session data size */
167 			ret =
168 			    gnutls_session_get_data2(session,
169 						     &session_data);
170 			if (ret < 0)
171 				fail("Getting resume data failed\n");
172 		} else {	/* the second time we connect */
173 
174 			/* check if we actually resumed the previous session */
175 			if (gnutls_session_is_resumed(session) != 0) {
176 				if (params->expect_resume) {
177 					if (debug)
178 						success
179 						    ("- Previous session was resumed\n");
180 				} else
181 					fail("- Previous session was resumed\n");
182 			} else {
183 				if (params->expect_resume) {
184 					fail("*** Previous session was NOT resumed\n");
185 				} else {
186 					if (debug)
187 						success
188 						    ("*** Previous session was NOT resumed (expected)\n");
189 				}
190 			}
191 		}
192 
193 		gnutls_record_send(session, MSG, strlen(MSG));
194 
195 		ret = gnutls_record_recv(session, buffer, MAX_BUF);
196 		if (ret == 0) {
197 			if (debug)
198 				success
199 				    ("client: Peer has closed the TLS connection\n");
200 			goto end;
201 		} else if (ret < 0) {
202 			fail("client: Error: %s\n", gnutls_strerror(ret));
203 			goto end;
204 		}
205 
206 		if (debug) {
207 			printf("- Received %d bytes: ", ret);
208 			for (ii = 0; ii < ret; ii++) {
209 				fputc(buffer[ii], stdout);
210 			}
211 			fputs("\n", stdout);
212 		}
213 
214 		gnutls_bye(session, GNUTLS_SHUT_RDWR);
215 
216 		close(sd);
217 
218 		gnutls_deinit(session);
219 	}
220 
221       end:
222 	gnutls_anon_free_client_credentials(anoncred);
223 }
224 
225 /* This is a sample TLS 1.0 echo server, for anonymous authentication only.
226  */
227 
228 #define DH_BITS 1024
229 
230 /* These are global */
231 
232 static gnutls_dh_params_t dh_params;
233 
generate_dh_params(void)234 static int generate_dh_params(void)
235 {
236 	const gnutls_datum_t p3 = { (void *) pkcs3, strlen(pkcs3) };
237 	/* Generate Diffie-Hellman parameters - for use with DHE
238 	 * kx algorithms. These should be discarded and regenerated
239 	 * once a day, once a week or once a month. Depending on the
240 	 * security requirements.
241 	 */
242 	gnutls_dh_params_init(&dh_params);
243 	return gnutls_dh_params_import_pkcs3(dh_params, &p3,
244 					     GNUTLS_X509_FMT_PEM);
245 }
246 
247 static char buffer[MAX_BUF + 1];
248 
global_stop(void)249 static void global_stop(void)
250 {
251 	if (debug)
252 		success("global stop\n");
253 
254 	gnutls_dh_params_deinit(dh_params);
255 
256 	gnutls_global_deinit();
257 }
258 
server(int sds[],struct params_res * params)259 static void server(int sds[], struct params_res *params)
260 {
261 	gnutls_anon_server_credentials_t anoncred;
262 	static gnutls_datum_t session_ticket_key = { NULL, 0 };
263 	int ret;
264 	size_t t;
265 	gnutls_session_t session;
266 
267 	/* this must be called once in the program, it is mostly for the server.
268 	 */
269 	if (debug) {
270 		gnutls_global_set_log_function(tls_log_func);
271 		gnutls_global_set_log_level(3);
272 	}
273 
274 	global_init();
275 	gnutls_anon_allocate_server_credentials(&anoncred);
276 
277 	if (debug)
278 		success("Launched, generating DH parameters...\n");
279 
280 	gnutls_anon_set_server_dh_params(anoncred, dh_params);
281 
282 	if (params->enable_db) {
283 		wrap_db_init();
284 	}
285 
286 	if (params->enable_session_ticket_server)
287 		gnutls_session_ticket_key_generate(&session_ticket_key);
288 
289 	for (t = 0; t < SESSIONS; t++) {
290 		int sd = sds[t];
291 
292 		gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM);
293 
294 		gnutls_priority_set_direct(session,
295 				   "NONE:+VERS-DTLS1.0:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-DH",
296 				   NULL);
297 
298 		gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
299 		gnutls_dh_set_prime_bits(session, DH_BITS);
300 
301 		if (params->enable_db) {
302 			gnutls_db_set_retrieve_function(session, wrap_db_fetch);
303 			gnutls_db_set_remove_function(session, wrap_db_delete);
304 			gnutls_db_set_store_function(session, wrap_db_store);
305 			gnutls_db_set_ptr(session, NULL);
306 		}
307 
308 		if (params->enable_session_ticket_server)
309 			gnutls_session_ticket_enable_server(session,
310 						    &session_ticket_key);
311 
312 		gnutls_transport_set_int(session, sd);
313 		gnutls_dtls_set_timeouts(session, 3*1000, 240 * 1000);
314 
315 		do {
316 			ret = gnutls_handshake(session);
317 		} while (ret < 0 && (ret == GNUTLS_E_INTERRUPTED||ret == GNUTLS_E_AGAIN));
318 		if (ret < 0) {
319 			close(sd);
320 			gnutls_deinit(session);
321 			kill(child, SIGTERM);
322 			fail("server: Handshake has failed (%s)\n\n",
323 			     gnutls_strerror(ret));
324 			return;
325 		}
326 		if (debug)
327 			success("server: Handshake was completed\n");
328 
329 		/* see the Getting peer's information example */
330 		/* print_info(session); */
331 
332 		for (;;) {
333 			memset(buffer, 0, MAX_BUF + 1);
334 			ret = gnutls_record_recv(session, buffer, MAX_BUF);
335 
336 			if (ret == 0) {
337 				if (debug)
338 					success
339 					    ("server: Peer has closed the GnuTLS connection\n");
340 				break;
341 			} else if (ret < 0) {
342 				kill(child, SIGTERM);
343 				fail("server: Received corrupted data(%d). Closing...\n", ret);
344 				break;
345 			} else if (ret > 0) {
346 				/* echo data back to the client
347 				 */
348 				gnutls_record_send(session, buffer,
349 						   strlen(buffer));
350 			}
351 		}
352 		/* do not wait for the peer to close the connection.
353 		 */
354 		gnutls_bye(session, GNUTLS_SHUT_WR);
355 
356 		close(sd);
357 
358 		gnutls_deinit(session);
359 	}
360 
361 	if (params->enable_db) {
362 		wrap_db_deinit();
363 	}
364 
365 	gnutls_free(session_ticket_key.data);
366 	gnutls_anon_free_server_credentials(anoncred);
367 
368 	if (debug)
369 		success("server: finished\n");
370 }
371 
doit(void)372 void doit(void)
373 {
374 	int i, err;
375 
376 	signal(SIGCHLD, SIG_IGN);
377 	signal(SIGPIPE, SIG_IGN);
378 
379 	generate_dh_params();
380 
381 	for (i = 0; resume_tests[i].desc; i++) {
382 		int client_sds[SESSIONS], server_sds[SESSIONS];
383 		int j;
384 
385 		printf("%s\n", resume_tests[i].desc);
386 
387 		for (j = 0; j < SESSIONS; j++) {
388 			int sockets[2];
389 
390 			err = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
391 			if (err == -1) {
392 				perror("socketpair");
393 				fail("socketpair failed\n");
394 				return;
395 			}
396 
397 			server_sds[j] = sockets[0];
398 			client_sds[j] = sockets[1];
399 		}
400 
401 		child = fork();
402 		if (child < 0) {
403 			perror("fork");
404 			fail("fork");
405 			return;
406 		}
407 
408 		if (child) {
409 			int status = 0;
410 			/* parent */
411 			for (j = 0; j < SESSIONS; j++)
412 				close(client_sds[j]);
413 			server(server_sds, &resume_tests[i]);
414 			wait(&status);
415 			check_wait_status(status);
416 		} else {
417 			for (j = 0; j < SESSIONS; j++)
418 				close(server_sds[j]);
419 			client(client_sds, &resume_tests[i]);
420 			gnutls_global_deinit();
421 			exit(0);
422 		}
423 	}
424 	global_stop();
425 }
426 
427 /* Functions and other stuff needed for session resuming.
428  * This is done using a very simple list which holds session ids
429  * and session data.
430  */
431 
432 #define MAX_SESSION_ID_SIZE 32
433 #define MAX_SESSION_DATA_SIZE 1024
434 
435 typedef struct {
436 	unsigned char session_id[MAX_SESSION_ID_SIZE];
437 	unsigned int session_id_size;
438 
439 	char session_data[MAX_SESSION_DATA_SIZE];
440 	int session_data_size;
441 } CACHE;
442 
443 static CACHE *cache_db;
444 static int cache_db_ptr = 0;
445 
wrap_db_init(void)446 static void wrap_db_init(void)
447 {
448 
449 	/* allocate cache_db */
450 	cache_db = calloc(1, TLS_SESSION_CACHE * sizeof(CACHE));
451 }
452 
wrap_db_deinit(void)453 static void wrap_db_deinit(void)
454 {
455 	free(cache_db);
456 	cache_db = NULL;
457 	return;
458 }
459 
460 static int
wrap_db_store(void * dbf,gnutls_datum_t key,gnutls_datum_t data)461 wrap_db_store(void *dbf, gnutls_datum_t key, gnutls_datum_t data)
462 {
463 	if (debug)
464 		success("resume db storing... (%d-%d)\n", key.size,
465 			data.size);
466 
467 	if (debug) {
468 		unsigned int i;
469 		printf("key:\n");
470 		for (i = 0; i < key.size; i++) {
471 			printf("%02x ", key.data[i] & 0xFF);
472 			if ((i + 1) % 16 == 0)
473 				printf("\n");
474 		}
475 		printf("\n");
476 		printf("data:\n");
477 		for (i = 0; i < data.size; i++) {
478 			printf("%02x ", data.data[i] & 0xFF);
479 			if ((i + 1) % 16 == 0)
480 				printf("\n");
481 		}
482 		printf("\n");
483 	}
484 
485 	if (cache_db == NULL)
486 		return -1;
487 
488 	if (key.size > MAX_SESSION_ID_SIZE)
489 		return -1;
490 
491 	if (data.size > MAX_SESSION_DATA_SIZE)
492 		return -1;
493 
494 	memcpy(cache_db[cache_db_ptr].session_id, key.data, key.size);
495 	cache_db[cache_db_ptr].session_id_size = key.size;
496 
497 	memcpy(cache_db[cache_db_ptr].session_data, data.data, data.size);
498 	cache_db[cache_db_ptr].session_data_size = data.size;
499 
500 	cache_db_ptr++;
501 	cache_db_ptr %= TLS_SESSION_CACHE;
502 
503 	return 0;
504 }
505 
wrap_db_fetch(void * dbf,gnutls_datum_t key)506 static gnutls_datum_t wrap_db_fetch(void *dbf, gnutls_datum_t key)
507 {
508 	gnutls_datum_t res = { NULL, 0 };
509 	unsigned i;
510 
511 	if (debug)
512 		success("resume db fetch... (%d)\n", key.size);
513 	if (debug) {
514 		printf("key:\n");
515 		for (i = 0; i < key.size; i++) {
516 			printf("%02x ", key.data[i] & 0xFF);
517 			if ((i + 1) % 16 == 0)
518 				printf("\n");
519 		}
520 		printf("\n");
521 	}
522 
523 	if (cache_db == NULL)
524 		return res;
525 
526 	for (i = 0; i < TLS_SESSION_CACHE; i++) {
527 		if (key.size == cache_db[i].session_id_size &&
528 		    memcmp(key.data, cache_db[i].session_id,
529 			   key.size) == 0) {
530 			if (debug)
531 				success
532 				    ("resume db fetch... return info\n");
533 
534 			res.size = cache_db[i].session_data_size;
535 
536 			res.data = gnutls_malloc(res.size);
537 			if (res.data == NULL)
538 				return res;
539 
540 			memcpy(res.data, cache_db[i].session_data,
541 				res.size);
542 
543 			if (debug) {
544 				unsigned j;
545 				printf("data:\n");
546 				for (j = 0; j < res.size; j++) {
547 					printf("%02x ",
548 						res.data[j] & 0xFF);
549 					if ((j + 1) % 16 == 0)
550 						printf("\n");
551 				}
552 				printf("\n");
553 			}
554 
555 			return res;
556 		}
557 	}
558 
559 	if (debug)
560 		success("resume db fetch... NOT FOUND\n");
561 	return res;
562 }
563 
wrap_db_delete(void * dbf,gnutls_datum_t key)564 static int wrap_db_delete(void *dbf, gnutls_datum_t key)
565 {
566 	int i;
567 
568 	if (cache_db == NULL)
569 		return -1;
570 
571 	for (i = 0; i < TLS_SESSION_CACHE; i++) {
572 		if (key.size == cache_db[i].session_id_size &&
573 		    memcmp(key.data, cache_db[i].session_id,
574 			   key.size) == 0) {
575 
576 			cache_db[i].session_id_size = 0;
577 			cache_db[i].session_data_size = 0;
578 
579 			return 0;
580 		}
581 	}
582 
583 	return -1;
584 
585 }
586 
587 #endif				/* _WIN32 */
588