2 * Copyright (C) 2004-2012 Free Software Foundation, Inc.
3 * Copyright (C) 2013 Adam Sampson <ats@offog.org>
5 * Author: Nikos Mavrogiannopoulos
7 * This file is part of GnuTLS.
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.
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.
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
24 /* Parts copied from GnuTLS example programs. */
35 int main(int argc, char **argv)
43 #include <sys/types.h>
44 #include <sys/socket.h>
49 #include <gnutls/gnutls.h>
53 static void wrap_db_init(void);
54 static void wrap_db_deinit(void);
55 static int wrap_db_store(void *dbf, gnutls_datum_t key,
57 static gnutls_datum_t wrap_db_fetch(void *dbf, gnutls_datum_t key);
58 static int wrap_db_delete(void *dbf, gnutls_datum_t key);
60 #define TLS_SESSION_CACHE 50
65 int enable_session_ticket_server;
66 int enable_session_ticket_client;
72 struct params_res resume_tests[] = {
73 {"try to resume from db", 50, 0, 0, 1},
74 {"try to resume from session ticket", 0, 1, 1, 1},
75 {"try to resume from session ticket (server only)", 0, 1, 0, 0},
76 {"try to resume from session ticket (client only)", 0, 0, 1, 0},
80 /* A very basic TLS client, with anonymous authentication.
84 #define MAX_BUF 5*1024
85 #define MSG "Hello TLS"
87 static void tls_log_func(int level, const char *str)
89 fprintf(stderr, "%s |<%d>| %s", child ? "server" : "client", level,
93 static void client(int sds[], struct params_res *params)
96 gnutls_session_t session;
97 char buffer[MAX_BUF + 1];
98 gnutls_anon_client_credentials_t anoncred;
99 /* Need to enable anonymous KX specifically. */
101 /* variables used in session resuming
104 gnutls_datum_t session_data;
107 gnutls_global_set_log_function(tls_log_func);
108 gnutls_global_set_log_level(3);
112 gnutls_anon_allocate_client_credentials(&anoncred);
114 for (t = 0; t < SESSIONS; t++) {
117 /* Initialize TLS session
119 gnutls_init(&session,
120 GNUTLS_CLIENT | GNUTLS_DATAGRAM |
121 GNUTLS_NO_EXTENSIONS);
123 /* Use default priorities */
124 gnutls_priority_set_direct(session,
125 "NONE:+VERS-DTLS1.0:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-DH",
128 /* put the anonymous credentials to the current session
130 gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
132 if (params->enable_session_ticket_client)
133 gnutls_session_ticket_enable_client(session);
136 /* if this is not the first time we connect */
137 gnutls_session_set_data(session, session_data.data,
139 gnutls_free(session_data.data);
142 gnutls_transport_set_int(session, sd);
144 /* Perform the TLS handshake
146 gnutls_handshake_set_timeout(session, 20 * 1000);
148 ret = gnutls_handshake(session);
149 } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
153 fail("client: Handshake failed\n");
158 ("client: Handshake was completed\n");
161 if (t == 0) { /* the first time we connect */
162 /* get the session data size */
164 gnutls_session_get_data2(session,
167 fail("Getting resume data failed\n");
168 } else { /* the second time we connect */
170 /* check if we actually resumed the previous session */
171 if (gnutls_session_is_resumed(session) != 0) {
172 if (params->expect_resume) {
175 ("- Previous session was resumed\n");
177 fail("- Previous session was resumed\n");
179 if (params->expect_resume) {
180 fail("*** Previous session was NOT resumed\n");
184 ("*** Previous session was NOT resumed (expected)\n");
189 gnutls_record_send(session, MSG, strlen(MSG));
191 ret = gnutls_record_recv(session, buffer, MAX_BUF);
195 ("client: Peer has closed the TLS connection\n");
197 } else if (ret < 0) {
198 fail("client: Error: %s\n", gnutls_strerror(ret));
203 printf("- Received %d bytes: ", ret);
204 for (ii = 0; ii < ret; ii++) {
205 fputc(buffer[ii], stdout);
210 gnutls_bye(session, GNUTLS_SHUT_RDWR);
214 gnutls_deinit(session);
218 gnutls_anon_free_client_credentials(anoncred);
221 /* This is a sample TLS 1.0 echo server, for anonymous authentication only.
226 /* These are global */
227 gnutls_anon_server_credentials_t anoncred;
228 static gnutls_datum_t session_ticket_key = { NULL, 0 };
230 static gnutls_session_t initialize_tls_session(struct params_res *params)
232 gnutls_session_t session;
234 gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM);
236 /* avoid calling all the priority functions, since the defaults
239 gnutls_priority_set_direct(session,
240 "NONE:+VERS-DTLS1.0:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-DH",
243 gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
245 gnutls_dh_set_prime_bits(session, DH_BITS);
247 if (params->enable_db) {
248 gnutls_db_set_retrieve_function(session, wrap_db_fetch);
249 gnutls_db_set_remove_function(session, wrap_db_delete);
250 gnutls_db_set_store_function(session, wrap_db_store);
251 gnutls_db_set_ptr(session, NULL);
254 if (params->enable_session_ticket_server)
255 gnutls_session_ticket_enable_server(session,
256 &session_ticket_key);
261 static gnutls_dh_params_t dh_params;
263 static int generate_dh_params(void)
265 const gnutls_datum_t p3 = { (void *) pkcs3, strlen(pkcs3) };
266 /* Generate Diffie-Hellman parameters - for use with DHE
267 * kx algorithms. These should be discarded and regenerated
268 * once a day, once a week or once a month. Depending on the
269 * security requirements.
271 gnutls_dh_params_init(&dh_params);
272 return gnutls_dh_params_import_pkcs3(dh_params, &p3,
273 GNUTLS_X509_FMT_PEM);
278 gnutls_session_t session;
279 char buffer[MAX_BUF + 1];
282 static void global_stop(void)
285 success("global stop\n");
287 gnutls_anon_free_server_credentials(anoncred);
289 gnutls_dh_params_deinit(dh_params);
291 gnutls_global_deinit();
294 static void server(int sds[], struct params_res *params)
298 /* this must be called once in the program, it is mostly for the server.
301 gnutls_global_set_log_function(tls_log_func);
302 gnutls_global_set_log_level(3);
306 gnutls_anon_allocate_server_credentials(&anoncred);
309 success("Launched, generating DH parameters...\n");
311 generate_dh_params();
313 gnutls_anon_set_server_dh_params(anoncred, dh_params);
315 if (params->enable_db) {
319 if (params->enable_session_ticket_server)
320 gnutls_session_ticket_key_generate(&session_ticket_key);
322 for (t = 0; t < SESSIONS; t++) {
325 session = initialize_tls_session(params);
327 gnutls_transport_set_int(session, sd);
328 gnutls_handshake_set_timeout(session, 20 * 1000);
331 ret = gnutls_handshake(session);
332 } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
335 gnutls_deinit(session);
336 kill(child, SIGTERM);
337 fail("server: Handshake has failed (%s)\n\n",
338 gnutls_strerror(ret));
342 success("server: Handshake was completed\n");
344 /* see the Getting peer's information example */
345 /* print_info(session); */
348 memset(buffer, 0, MAX_BUF + 1);
349 ret = gnutls_record_recv(session, buffer, MAX_BUF);
354 ("server: Peer has closed the GnuTLS connection\n");
356 } else if (ret < 0) {
357 kill(child, SIGTERM);
358 fail("server: Received corrupted data(%d). Closing...\n", ret);
360 } else if (ret > 0) {
361 /* echo data back to the client
363 gnutls_record_send(session, buffer,
367 /* do not wait for the peer to close the connection.
369 gnutls_bye(session, GNUTLS_SHUT_WR);
373 gnutls_deinit(session);
376 if (params->enable_db) {
380 gnutls_free(session_ticket_key.data);
381 session_ticket_key.data = NULL;
384 success("server: finished\n");
391 for (i = 0; resume_tests[i].desc; i++) {
392 int client_sds[SESSIONS], server_sds[SESSIONS];
395 printf("%s\n", resume_tests[i].desc);
397 for (j = 0; j < SESSIONS; j++) {
400 err = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
402 perror("socketpair");
403 fail("socketpair failed\n");
407 server_sds[j] = sockets[0];
408 client_sds[j] = sockets[1];
421 server(server_sds, &resume_tests[i]);
423 if (WEXITSTATUS(status) > 0)
431 client(client_sds, &resume_tests[i]);
432 gnutls_global_deinit();
440 /* Functions and other stuff needed for session resuming.
441 * This is done using a very simple list which holds session ids
445 #define MAX_SESSION_ID_SIZE 32
446 #define MAX_SESSION_DATA_SIZE 1024
449 unsigned char session_id[MAX_SESSION_ID_SIZE];
450 unsigned int session_id_size;
452 char session_data[MAX_SESSION_DATA_SIZE];
453 int session_data_size;
456 static CACHE *cache_db;
457 static int cache_db_ptr = 0;
459 static void wrap_db_init(void)
462 /* allocate cache_db */
463 cache_db = calloc(1, TLS_SESSION_CACHE * sizeof(CACHE));
466 static void wrap_db_deinit(void)
474 wrap_db_store(void *dbf, gnutls_datum_t key, gnutls_datum_t data)
477 success("resume db storing... (%d-%d)\n", key.size,
483 for (i = 0; i < key.size; i++) {
484 printf("%02x ", key.data[i] & 0xFF);
485 if ((i + 1) % 16 == 0)
490 for (i = 0; i < data.size; i++) {
491 printf("%02x ", data.data[i] & 0xFF);
492 if ((i + 1) % 16 == 0)
498 if (cache_db == NULL)
501 if (key.size > MAX_SESSION_ID_SIZE)
504 if (data.size > MAX_SESSION_DATA_SIZE)
507 memcpy(cache_db[cache_db_ptr].session_id, key.data, key.size);
508 cache_db[cache_db_ptr].session_id_size = key.size;
510 memcpy(cache_db[cache_db_ptr].session_data, data.data, data.size);
511 cache_db[cache_db_ptr].session_data_size = data.size;
514 cache_db_ptr %= TLS_SESSION_CACHE;
519 static gnutls_datum_t wrap_db_fetch(void *dbf, gnutls_datum_t key)
521 gnutls_datum_t res = { NULL, 0 };
525 success("resume db fetch... (%d)\n", key.size);
529 for (i = 0; i < key.size; i++) {
530 printf("%02x ", key.data[i] & 0xFF);
531 if ((i + 1) % 16 == 0)
537 if (cache_db == NULL)
540 for (i = 0; i < TLS_SESSION_CACHE; i++) {
541 if (key.size == cache_db[i].session_id_size &&
542 memcmp(key.data, cache_db[i].session_id,
546 ("resume db fetch... return info\n");
548 res.size = cache_db[i].session_data_size;
550 res.data = gnutls_malloc(res.size);
551 if (res.data == NULL)
554 memcpy(res.data, cache_db[i].session_data,
560 for (i = 0; i < res.size; i++) {
563 if ((i + 1) % 16 == 0)
574 success("resume db fetch... NOT FOUND\n");
578 static int wrap_db_delete(void *dbf, gnutls_datum_t key)
582 if (cache_db == NULL)
585 for (i = 0; i < TLS_SESSION_CACHE; i++) {
586 if (key.size == cache_db[i].session_id_size &&
587 memcmp(key.data, cache_db[i].session_id,
590 cache_db[i].session_id_size = 0;
591 cache_db[i].session_data_size = 0;