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