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