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