]> git.sesse.net Git - vlc/blob - modules/misc/gnutls.c
- Non blocking TLS handshaking
[vlc] / modules / misc / gnutls.c
1 /*****************************************************************************
2  * tls.c
3  *****************************************************************************
4  * Copyright (C) 2004 VideoLAN
5  * $Id: httpd.c 8263 2004-07-24 09:06:58Z courmisch $
6  *
7  * Authors: Remi Denis-Courmont <courmisch@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*
25  * TODO:
26  * - fix FIXMEs,
27  * - client side stuff,
28  * - server-side client cert validation,
29  * - client-side server cert validation (?).
30  */
31
32
33 /*****************************************************************************
34  * Preamble
35  *****************************************************************************/
36 #include <stdlib.h>
37 #include <errno.h>
38 #include <vlc/vlc.h>
39
40 #include "vlc_tls.h"
41
42 #include <gcrypt.h>
43 #include <gnutls/gnutls.h>
44
45 #define DH_BITS 1024
46
47
48 /*****************************************************************************
49  * Module descriptor
50  *****************************************************************************/
51 static int  Open ( vlc_object_t * );
52 static void Close( vlc_object_t * );
53
54 #define DH_BITS_TEXT N_("Diffie-Hellman prime bits")
55 #define DH_BITS_LONGTEXT N_( \
56     "Allows you to modify the Diffie-Hellman prime's number of bits " \
57     "(used for TLS or SSL-based server-side encryption)." )
58
59 vlc_module_begin();
60     set_description( _("GnuTLS TLS encryption layer") );
61     set_capability( "tls", 1 );
62     set_callbacks( Open, Close );
63     set_category( CAT_ADVANCED );
64     set_subcategory( SUBCAT_ADVANCED_MISC );
65
66     add_integer( "dh-bits", DH_BITS, NULL, DH_BITS_TEXT,
67                  DH_BITS_LONGTEXT, VLC_TRUE );
68 vlc_module_end();
69
70
71
72 typedef struct tls_server_sys_t
73 {
74     gnutls_certificate_credentials x509_cred;
75     gnutls_dh_params dh_params;
76 } tls_server_sys_t;
77
78
79 typedef struct tls_session_sys_t
80 {
81     gnutls_session  session;
82 } tls_session_sys_t;
83
84
85 typedef struct tls_client_sys_t
86 {
87     struct tls_session_sys_t       session;
88     gnutls_certificate_credentials x509_cred;
89 } tls_client_sys_t;
90
91
92 /*****************************************************************************
93  * tls_Send:
94  *****************************************************************************
95  * Sends data through a TLS session.
96  *****************************************************************************/
97 static int
98 gnutls_Send( void *p_session, const void *buf, int i_length )
99 {
100     int val;
101     tls_session_sys_t *p_sys;
102
103     p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
104
105     val = gnutls_record_send( p_sys->session, buf, i_length );
106     /* TODO: handle fatal error */
107     return val < 0 ? -1 : val;
108 }
109
110
111 /*****************************************************************************
112  * tls_Recv:
113  *****************************************************************************
114  * Receives data through a TLS session.
115  *****************************************************************************/
116 static int
117 gnutls_Recv( void *p_session, void *buf, int i_length )
118 {
119     int val;
120     tls_session_sys_t *p_sys;
121
122     p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
123
124     val = gnutls_record_recv( p_sys->session, buf, i_length );
125     /* TODO: handle fatal error */
126     return val < 0 ? -1 : val;
127 }
128
129
130 /*****************************************************************************
131  * tls_Session(Continue)?Handshake:
132  *****************************************************************************
133  * Establishes TLS session with a peer through socket <fd>.
134  * Returns -1 on error (you need not and must not call tls_SessionClose)
135  * 0 on succesful handshake completion, 1 if more would-be blocking recv is
136  * needed, 2 if more would-be blocking send is required.
137  *****************************************************************************/
138 static int
139 gnutls_SessionContinueHandshake( tls_session_t *p_session)
140 {
141     tls_session_sys_t *p_sys;
142     int val;
143
144     p_sys = (tls_session_sys_t *)(p_session->p_sys);
145
146      /* TODO: handle fatal error */
147     val = gnutls_handshake( p_sys->session );
148     if( ( val == GNUTLS_E_AGAIN ) || ( val == GNUTLS_E_INTERRUPTED ) )
149         return 1 + gnutls_record_get_direction( p_sys->session );
150
151     if( val < 0 )
152     {
153         gnutls_deinit( p_sys->session );
154         msg_Err( p_session->p_tls, "TLS handshake failed : %s",
155                  gnutls_strerror( val ) );
156         free( p_sys );
157         free( p_session );
158         return -1;
159     }
160
161     return 0;
162 }
163
164 static int
165 gnutls_SessionHandshake( tls_session_t *p_session, int fd )
166 {
167     tls_session_sys_t *p_sys;
168
169     p_sys = (tls_session_sys_t *)(p_session->p_sys);
170
171     gnutls_transport_set_ptr (p_sys->session, (gnutls_transport_ptr)fd);
172
173     return gnutls_SessionContinueHandshake( p_session );
174 }
175
176
177 /*****************************************************************************
178  * tls_SessionClose:
179  *****************************************************************************
180  * Terminates TLS session and releases session data.
181  *****************************************************************************/
182 static void
183 gnutls_SessionClose( tls_session_t *p_session )
184 {
185     tls_session_sys_t *p_sys;
186
187     p_sys = (tls_session_sys_t *)(p_session->p_sys);
188
189     /* On the client-side, credentials are re-allocated per session */
190     if( p_session->p_server == NULL )
191         gnutls_certificate_free_credentials( ((tls_client_sys_t *)p_sys)
192                                                 ->x509_cred );
193
194     gnutls_bye( p_sys->session, GNUTLS_SHUT_WR );
195     gnutls_deinit( p_sys->session );
196     free( p_sys );
197     free( p_session );
198 }
199
200
201 /*****************************************************************************
202  * tls_ClientCreate:
203  *****************************************************************************
204  * Initializes client-side TLS session data.
205  *****************************************************************************/
206 static tls_session_t *
207 gnutls_ClientCreate( tls_t *p_tls, const char *psz_ca_path )
208 {
209     tls_session_t *p_session;
210     tls_client_sys_t *p_sys;
211     int i_val;
212     const int cert_type_priority[3] =
213     {
214         GNUTLS_CRT_X509,
215         0
216     };
217
218     p_sys = (tls_client_sys_t *)malloc( sizeof(struct tls_client_sys_t) );
219     if( p_sys == NULL )
220         return NULL;
221
222     i_val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
223     if( i_val != 0 )
224     {
225         msg_Err( p_tls, "Cannot allocate X509 credentials : %s",
226                  gnutls_strerror( i_val ) );
227         free( p_sys );
228         return NULL;
229     }
230
231     if( psz_ca_path != NULL )
232     {
233         i_val = gnutls_certificate_set_x509_trust_file( p_sys->x509_cred,
234                                                         psz_ca_path,
235                                                         GNUTLS_X509_FMT_PEM );
236         if( i_val != 0 )
237         {
238             msg_Err( p_tls, "Cannot add trusted CA (%s) : %s", psz_ca_path,
239                      gnutls_strerror( i_val ) );
240             gnutls_certificate_free_credentials( p_sys->x509_cred );
241             free( p_sys );
242             return NULL;
243         }
244     }
245
246     i_val = gnutls_init( &p_sys->session.session, GNUTLS_CLIENT );
247     if( i_val != 0 )
248     {
249         msg_Err( p_tls, "Cannot initialize TLS session : %s",
250                  gnutls_strerror( i_val ) );
251         gnutls_certificate_free_credentials( p_sys->x509_cred );
252         free( p_sys );
253         return NULL;
254     }
255
256     i_val = gnutls_set_default_priority( p_sys->session.session );
257     if( i_val < 0 )
258     {
259         msg_Err( p_tls, "Cannot set ciphers priorities : %s",
260                  gnutls_strerror( i_val ) );
261         gnutls_deinit( p_sys->session.session );
262         gnutls_certificate_free_credentials( p_sys->x509_cred );
263         free( p_sys );
264         return NULL;
265     }
266
267     i_val = gnutls_certificate_type_set_priority( p_sys->session.session,
268                                                   cert_type_priority );
269     if( i_val < 0 )
270     {
271         msg_Err( p_tls, "Cannot set certificate type priorities : %s",
272                  gnutls_strerror( i_val ) );
273         gnutls_deinit( p_sys->session.session );
274         gnutls_certificate_free_credentials( p_sys->x509_cred );
275         free( p_sys );
276         return NULL;
277     }
278
279     i_val = gnutls_credentials_set( p_sys->session.session,
280                                     GNUTLS_CRD_CERTIFICATE,
281                                     p_sys->x509_cred );
282     if( i_val < 0 )
283     {
284         msg_Err( p_tls, "Cannot set TLS session credentials : %s",
285                  gnutls_strerror( i_val ) );
286         gnutls_deinit( p_sys->session.session );
287         gnutls_certificate_free_credentials( p_sys->x509_cred );
288         free( p_sys );
289         return NULL;
290     }
291
292     p_session = malloc( sizeof (struct tls_session_t) );
293     if( p_session == NULL )
294     {
295         gnutls_deinit( p_sys->session.session );
296         gnutls_certificate_free_credentials( p_sys->x509_cred );
297         free( p_sys );
298         return NULL;
299     }
300
301     p_session->p_tls = p_tls;
302     p_session->p_server = NULL;
303     p_session->p_sys = p_sys;
304     p_session->sock.p_sys = p_session;
305     p_session->sock.pf_send = gnutls_Send;
306     p_session->sock.pf_recv = gnutls_Recv;
307     p_session->pf_handshake = gnutls_SessionHandshake;
308     p_session->pf_close = gnutls_SessionClose;
309
310     return p_session;
311 }
312
313
314 /*****************************************************************************
315  * tls_ServerSessionPrepare:
316  *****************************************************************************
317  * Initializes server-side TLS session data.
318  *****************************************************************************/
319 static tls_session_t *
320 gnutls_ServerSessionPrepare( tls_server_t *p_server )
321 {
322     tls_session_t *p_session;
323     tls_session_sys_t *p_sys;
324     int i_val;
325     vlc_value_t bits;
326
327     p_sys = (tls_session_sys_t *)malloc( sizeof(struct tls_session_sys_t) );
328     if( p_sys == NULL )
329         return NULL;
330
331     i_val = gnutls_init( &p_sys->session, GNUTLS_SERVER );
332     if( i_val != 0 )
333     {
334         msg_Err( p_server->p_tls, "Cannot initialize TLS session : %s",
335                  gnutls_strerror( i_val ) );
336         free( p_sys );
337         return NULL;
338     }
339    
340     i_val = gnutls_set_default_priority( p_sys->session );
341     if( i_val < 0 )
342     {
343         msg_Err( p_server->p_tls, "Cannot set ciphers priorities : %s",
344                  gnutls_strerror( i_val ) );
345         gnutls_deinit( p_sys->session );
346         free( p_sys );
347         return NULL;
348     }
349
350     i_val = gnutls_credentials_set( p_sys->session, GNUTLS_CRD_CERTIFICATE,
351                                     ((tls_server_sys_t *)(p_server->p_sys))
352                                     ->x509_cred );
353     if( i_val < 0 )
354     {
355         msg_Err( p_server->p_tls, "Cannot set TLS session credentials : %s",
356                  gnutls_strerror( i_val ) );
357         gnutls_deinit( p_sys->session );
358         free( p_sys );
359         return NULL;
360     }
361
362     /* TODO: support for client authentication */
363     /*gnutls_certificate_server_set_request( p_session->session,
364                                            GNUTLS_CERT_REQUEST ); */
365
366     if( var_Get( p_server->p_tls, "dh-bits", &bits ) != VLC_SUCCESS )
367     {
368         var_Create( p_server->p_tls, "dh-bits",
369                     VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
370         var_Get( p_server->p_tls, "dh-bits", &bits );
371     }
372
373     gnutls_dh_set_prime_bits( p_sys->session, bits.i_int );
374
375     p_session = malloc( sizeof (struct tls_session_t) );
376     if( p_session == NULL )
377     {
378         gnutls_deinit( p_sys->session );
379         free( p_sys );
380         return NULL;
381     }
382
383     p_session->p_tls = p_server->p_tls;
384     p_session->p_server = p_server;
385     p_session->p_sys = p_sys;
386     p_session->sock.p_sys = p_session;
387     p_session->sock.pf_send = gnutls_Send;
388     p_session->sock.pf_recv = gnutls_Recv;
389     p_session->pf_handshake = gnutls_SessionHandshake;
390     p_session->pf_handshake2 = gnutls_SessionContinueHandshake;
391     p_session->pf_close = gnutls_SessionClose;
392
393     return p_session;
394 }
395
396
397 /*****************************************************************************
398  * tls_ServerDelete:
399  *****************************************************************************
400  * Releases data allocated with tls_ServerCreate.
401  *****************************************************************************/
402 static void
403 gnutls_ServerDelete( tls_server_t *p_server )
404 {
405     gnutls_certificate_free_credentials(
406                                        ((tls_server_sys_t *)(p_server->p_sys))
407                                          ->x509_cred );
408     free( p_server->p_sys );
409     free( p_server );
410 }
411
412
413 /*****************************************************************************
414  * tls_ServerAddCA:
415  *****************************************************************************
416  * Adds one or more certificate authorities.
417  * TODO: we are not able to check the client credentials yet, so this function
418  * is pretty useless.
419  * Returns -1 on error, 0 on success.
420  *****************************************************************************/
421 static int
422 gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path )
423 {
424     int val;
425
426     val = gnutls_certificate_set_x509_trust_file( ((tls_server_sys_t *)
427                                                   (p_server->p_sys))
428                                                     ->x509_cred,
429                                                   psz_ca_path,
430                                                   GNUTLS_X509_FMT_PEM );
431     if( val < 0 )
432     {
433         msg_Err( p_server->p_tls, "Cannot add trusted CA (%s) : %s",
434                  psz_ca_path, gnutls_strerror( val ) );
435         gnutls_ServerDelete( p_server );
436         return VLC_EGENERIC;
437     }
438     msg_Dbg( p_server->p_tls, " %d trusted CA added (%s)", val,
439              psz_ca_path );
440     return VLC_SUCCESS;
441 }
442
443
444 /*****************************************************************************
445  * tls_ServerAddCRL:
446  *****************************************************************************
447  * Adds a certificates revocation list to be sent to TLS clients.
448  * Returns -1 on error, 0 on success.
449  *****************************************************************************/
450 static int
451 gnutls_ServerAddCRL( tls_server_t *p_server, const char *psz_crl_path )
452 {
453     int val;
454
455     val = gnutls_certificate_set_x509_crl_file( ((tls_server_sys_t *)
456                                                 (p_server->p_sys))->x509_cred,
457                                                 psz_crl_path,
458                                                 GNUTLS_X509_FMT_PEM );
459     if( val < 0 )
460     {
461         msg_Err( p_server->p_tls, "Cannot add CRL (%s) : %s",
462                  psz_crl_path, gnutls_strerror( val ) );
463         gnutls_ServerDelete( p_server );
464         return VLC_EGENERIC;
465     }
466     msg_Dbg( p_server->p_tls, "%d CRL added (%s)", val, psz_crl_path );
467     return VLC_SUCCESS;
468 }
469     
470
471 /*****************************************************************************
472  * tls_ServerCreate:
473  *****************************************************************************
474  * Allocates a whole server's TLS credentials.
475  * Returns NULL on error.
476  *****************************************************************************/
477 static tls_server_t *
478 gnutls_ServerCreate( tls_t *p_this, const char *psz_cert_path,
479                   const char *psz_key_path )
480 {
481     tls_server_t *p_server;
482     tls_server_sys_t *p_server_sys;
483     int val;
484
485     msg_Dbg( p_this, "Creating TLS server" );
486
487     p_server_sys = (tls_server_sys_t *)malloc( sizeof(struct tls_server_sys_t) );
488     if( p_server_sys == NULL )
489         return NULL;
490
491     /* Sets server's credentials */
492     val = gnutls_certificate_allocate_credentials( &p_server_sys->x509_cred );
493     if( val != 0 )
494     {
495         msg_Err( p_this, "Cannot allocate X509 credentials : %s",
496                  gnutls_strerror( val ) );
497         free( p_server_sys );
498         return NULL;
499     }
500
501     val = gnutls_certificate_set_x509_key_file( p_server_sys->x509_cred,
502                                                 psz_cert_path, psz_key_path,
503                                                 GNUTLS_X509_FMT_PEM );
504     if( val < 0 )
505     {
506         msg_Err( p_this, "Cannot set certificate chain or private key : %s",
507                  gnutls_strerror( val ) );
508         gnutls_certificate_free_credentials( p_server_sys->x509_cred );
509         free( p_server_sys );
510         return NULL;
511     }
512
513     /* FIXME: regenerate these regularly */
514     val = gnutls_dh_params_init( &p_server_sys->dh_params );
515     if( val >= 0 )
516     {
517         vlc_value_t bits;
518
519         if( var_Get( p_this, "dh-bits", &bits ) != VLC_SUCCESS )
520         {
521             var_Create( p_this, "dh-bits",
522                         VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
523             var_Get( p_this, "dh-bits", &bits );
524         }
525
526         msg_Dbg( p_this, "Computing Diffie Hellman ciphers parameters" );
527         val = gnutls_dh_params_generate2( p_server_sys->dh_params,
528                                           bits.i_int );
529     }
530     if( val < 0 )
531     {
532         msg_Err( p_this, "Cannot initialize DH cipher suites : %s",
533                  gnutls_strerror( val ) );
534         gnutls_certificate_free_credentials( p_server_sys->x509_cred );
535         free( p_server_sys );
536         return NULL;
537     }
538     msg_Dbg( p_this, "Ciphers parameters computed" );
539
540     gnutls_certificate_set_dh_params( p_server_sys->x509_cred,
541                                       p_server_sys->dh_params);
542
543     p_server = (tls_server_t *)malloc( sizeof(struct tls_server_t) );
544     if( p_server == NULL )
545     {
546         free( p_server_sys );
547         return NULL;
548     }
549
550     p_server->p_tls = p_this;
551     p_server->p_sys = p_server_sys;
552     p_server->pf_delete = gnutls_ServerDelete;
553     p_server->pf_add_CA = gnutls_ServerAddCA;
554     p_server->pf_add_CRL = gnutls_ServerAddCRL;
555     p_server->pf_session_prepare = gnutls_ServerSessionPrepare;
556
557     return p_server;
558 }
559
560
561 /*****************************************************************************
562  * gcrypt thread option VLC implementation:
563  *****************************************************************************/
564 vlc_object_t *__p_gcry_data;
565
566 static int gcry_vlc_mutex_init (void **p_sys)
567 {
568     int i_val;
569     vlc_mutex_t *p_lock = (vlc_mutex_t *)malloc (sizeof (vlc_mutex_t));
570
571     if( p_lock == NULL)
572         return ENOMEM;
573
574     i_val = vlc_mutex_init( __p_gcry_data, p_lock);
575     if (i_val)
576         free (p_lock);
577     else
578         *p_sys = p_lock;
579     return i_val;
580 }
581
582 static int gcry_vlc_mutex_destroy (void **p_sys)
583 {
584     int i_val;
585     vlc_mutex_t *p_lock = (vlc_mutex_t *)*p_sys;
586
587     i_val = vlc_mutex_destroy (p_lock);
588     free (p_lock);
589     return i_val;
590 }
591
592 static int gcry_vlc_mutex_lock (void **p_sys)
593 {
594     return vlc_mutex_lock ((vlc_mutex_t *)*p_sys);
595 }
596
597 static int gcry_vlc_mutex_unlock (void **lock)
598 {
599     return vlc_mutex_unlock ((vlc_mutex_t *)*lock);
600 }
601
602 static struct gcry_thread_cbs gcry_threads_vlc =
603 {
604     GCRY_THREAD_OPTION_USER,
605     NULL,
606     gcry_vlc_mutex_init,
607     gcry_vlc_mutex_destroy,
608     gcry_vlc_mutex_lock,
609     gcry_vlc_mutex_unlock
610 };
611
612
613 /*****************************************************************************
614  * Module initialization
615  *****************************************************************************/
616 static int
617 Open( vlc_object_t *p_this )
618 {
619     tls_t *p_tls = (tls_t *)p_this;
620
621     vlc_value_t lock, count;
622
623     var_Create( p_this->p_libvlc, "tls_mutex", VLC_VAR_MUTEX );
624     var_Get( p_this->p_libvlc, "tls_mutex", &lock );
625     vlc_mutex_lock( lock.p_address );
626
627     /* Initialize GnuTLS only once */
628     var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
629     var_Get( p_this->p_libvlc, "gnutls_count", &count);
630
631     if( count.i_int == 0)
632     {
633         __p_gcry_data = VLC_OBJECT( p_this->p_vlc );
634
635         gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_vlc);
636         if( gnutls_global_init( ) )
637         {
638             msg_Warn( p_this, "cannot initialize GNUTLS" );
639             vlc_mutex_unlock( lock.p_address );
640             return VLC_EGENERIC;
641         }
642         if( gnutls_check_version( "1.0.0" ) == NULL )
643         {
644             gnutls_global_deinit( );
645             vlc_mutex_unlock( lock.p_address );
646             msg_Err( p_this, "unsupported GNUTLS version" );
647             return VLC_EGENERIC;
648         }
649         msg_Dbg( p_this, "GNUTLS initialized" );
650     }
651
652     count.i_int++;
653     var_Set( p_this->p_libvlc, "gnutls_count", count);
654     vlc_mutex_unlock( lock.p_address );
655
656     p_tls->pf_server_create = gnutls_ServerCreate;
657     p_tls->pf_client_create = gnutls_ClientCreate;
658     return VLC_SUCCESS;
659 }
660
661
662 /*****************************************************************************
663  * Module deinitialization
664  *****************************************************************************/
665 static void
666 Close( vlc_object_t *p_this )
667 {
668     /*tls_t *p_tls = (tls_t *)p_this;
669     tls_sys_t *p_sys = (tls_sys_t *)(p_this->p_sys);*/
670
671     vlc_value_t lock, count;
672
673     var_Create( p_this->p_libvlc, "gnutls_mutex", VLC_VAR_MUTEX );
674     var_Get( p_this->p_libvlc, "gnutls_mutex", &lock );
675     vlc_mutex_lock( lock.p_address );
676
677     var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
678     var_Get( p_this->p_libvlc, "gnutls_count", &count);
679     count.i_int--;
680     var_Set( p_this->p_libvlc, "gnutls_count", count);
681
682     if( count.i_int == 0 )
683     {
684         gnutls_global_deinit( );
685         msg_Dbg( p_this, "GNUTLS deinitialized" );
686     }
687
688     vlc_mutex_unlock( lock.p_address);
689 }