]> git.sesse.net Git - vlc/blob - modules/misc/gnutls.c
8d5b74f604b928bbc672152e35a333e15f711fc9
[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  * - server-side client cert validation,
28  * - client-side server cert validation (?).
29  */
30
31
32 /*****************************************************************************
33  * Preamble
34  *****************************************************************************/
35 #include <stdlib.h>
36 #include <errno.h>
37 #include <vlc/vlc.h>
38
39 #include "vlc_tls.h"
40
41 #include <gcrypt.h>
42 #include <gnutls/gnutls.h>
43
44 #define DH_BITS 1024
45
46
47 /*****************************************************************************
48  * Module descriptor
49  *****************************************************************************/
50 static int  Open ( vlc_object_t * );
51 static void Close( vlc_object_t * );
52
53 #define DH_BITS_TEXT N_("Diffie-Hellman prime bits")
54 #define DH_BITS_LONGTEXT N_( \
55     "Allows you to modify the Diffie-Hellman prime's number of bits " \
56     "(used for TLS or SSL-based server-side encryption)." )
57
58 vlc_module_begin();
59     set_description( _("GnuTLS TLS encryption layer") );
60     set_capability( "tls", 1 );
61     set_callbacks( Open, Close );
62     set_category( CAT_ADVANCED );
63     set_subcategory( SUBCAT_ADVANCED_MISC );
64
65     add_integer( "dh-bits", DH_BITS, NULL, DH_BITS_TEXT,
66                  DH_BITS_LONGTEXT, VLC_TRUE );
67 vlc_module_end();
68
69
70
71 typedef struct tls_server_sys_t
72 {
73     gnutls_certificate_credentials x509_cred;
74     gnutls_dh_params dh_params;
75 } tls_server_sys_t;
76
77
78 typedef struct tls_session_sys_t
79 {
80     gnutls_session  session;
81 } tls_session_sys_t;
82
83
84 typedef struct tls_client_sys_t
85 {
86     struct tls_session_sys_t       session;
87     gnutls_certificate_credentials x509_cred;
88 } tls_client_sys_t;
89
90
91 /*****************************************************************************
92  * tls_Send:
93  *****************************************************************************
94  * Sends data through a TLS session.
95  *****************************************************************************/
96 static int
97 gnutls_Send( void *p_session, const void *buf, int i_length )
98 {
99     int val;
100     tls_session_sys_t *p_sys;
101
102     p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
103
104     val = gnutls_record_send( p_sys->session, buf, i_length );
105     /* TODO: handle fatal error */
106     return val < 0 ? -1 : val;
107 }
108
109
110 /*****************************************************************************
111  * tls_Recv:
112  *****************************************************************************
113  * Receives data through a TLS session.
114  *****************************************************************************/
115 static int
116 gnutls_Recv( void *p_session, void *buf, int i_length )
117 {
118     int val;
119     tls_session_sys_t *p_sys;
120
121     p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
122
123     val = gnutls_record_recv( p_sys->session, buf, i_length );
124     /* TODO: handle fatal error */
125     return val < 0 ? -1 : val;
126 }
127
128
129 /*****************************************************************************
130  * tls_Session(Continue)?Handshake:
131  *****************************************************************************
132  * Establishes TLS session with a peer through socket <fd>.
133  * Returns -1 on error (you need not and must not call tls_SessionClose)
134  * 0 on succesful handshake completion, 1 if more would-be blocking recv is
135  * needed, 2 if more would-be blocking send is required.
136  *****************************************************************************/
137 static int
138 gnutls_SessionContinueHandshake( tls_session_t *p_session)
139 {
140     tls_session_sys_t *p_sys;
141     int val;
142
143     p_sys = (tls_session_sys_t *)(p_session->p_sys);
144
145      /* TODO: handle fatal error */
146     val = gnutls_handshake( p_sys->session );
147     if( ( val == GNUTLS_E_AGAIN ) || ( val == GNUTLS_E_INTERRUPTED ) )
148         return 1 + gnutls_record_get_direction( p_sys->session );
149
150     if( val < 0 )
151     {
152         gnutls_deinit( p_sys->session );
153         msg_Err( p_session->p_tls, "TLS handshake failed : %s",
154                  gnutls_strerror( val ) );
155         free( p_sys );
156         free( p_session );
157         return -1;
158     }
159
160     return 0;
161 }
162
163 static int
164 gnutls_SessionHandshake( tls_session_t *p_session, int fd )
165 {
166     tls_session_sys_t *p_sys;
167
168     p_sys = (tls_session_sys_t *)(p_session->p_sys);
169
170     gnutls_transport_set_ptr (p_sys->session, (gnutls_transport_ptr)fd);
171
172     return gnutls_SessionContinueHandshake( p_session );
173 }
174
175
176 /*****************************************************************************
177  * tls_SessionClose:
178  *****************************************************************************
179  * Terminates TLS session and releases session data.
180  *****************************************************************************/
181 static void
182 gnutls_SessionClose( tls_session_t *p_session )
183 {
184     tls_session_sys_t *p_sys;
185
186     p_sys = (tls_session_sys_t *)(p_session->p_sys);
187
188     /* On the client-side, credentials are re-allocated per session */
189     if( p_session->p_server == NULL )
190         gnutls_certificate_free_credentials( ((tls_client_sys_t *)p_sys)
191                                                 ->x509_cred );
192
193     gnutls_bye( p_sys->session, GNUTLS_SHUT_WR );
194     gnutls_deinit( p_sys->session );
195     free( p_sys );
196     free( p_session );
197 }
198
199
200 /*****************************************************************************
201  * tls_ClientCreate:
202  *****************************************************************************
203  * Initializes client-side TLS session data.
204  *****************************************************************************/
205 static tls_session_t *
206 gnutls_ClientCreate( tls_t *p_tls, const char *psz_ca_path )
207 {
208     tls_session_t *p_session;
209     tls_client_sys_t *p_sys;
210     int i_val;
211     const int cert_type_priority[3] =
212     {
213         GNUTLS_CRT_X509,
214         0
215     };
216
217     p_sys = (tls_client_sys_t *)malloc( sizeof(struct tls_client_sys_t) );
218     if( p_sys == NULL )
219         return NULL;
220
221     i_val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
222     if( i_val != 0 )
223     {
224         msg_Err( p_tls, "Cannot allocate X509 credentials : %s",
225                  gnutls_strerror( i_val ) );
226         free( p_sys );
227         return NULL;
228     }
229
230     if( psz_ca_path != NULL )
231     {
232         i_val = gnutls_certificate_set_x509_trust_file( p_sys->x509_cred,
233                                                         psz_ca_path,
234                                                         GNUTLS_X509_FMT_PEM );
235         if( i_val != 0 )
236         {
237             msg_Err( p_tls, "Cannot add trusted CA (%s) : %s", psz_ca_path,
238                      gnutls_strerror( i_val ) );
239             gnutls_certificate_free_credentials( p_sys->x509_cred );
240             free( p_sys );
241             return NULL;
242         }
243     }
244
245     i_val = gnutls_init( &p_sys->session.session, GNUTLS_CLIENT );
246     if( i_val != 0 )
247     {
248         msg_Err( p_tls, "Cannot initialize TLS session : %s",
249                  gnutls_strerror( i_val ) );
250         gnutls_certificate_free_credentials( p_sys->x509_cred );
251         free( p_sys );
252         return NULL;
253     }
254
255     i_val = gnutls_set_default_priority( p_sys->session.session );
256     if( i_val < 0 )
257     {
258         msg_Err( p_tls, "Cannot set ciphers priorities : %s",
259                  gnutls_strerror( i_val ) );
260         gnutls_deinit( p_sys->session.session );
261         gnutls_certificate_free_credentials( p_sys->x509_cred );
262         free( p_sys );
263         return NULL;
264     }
265
266     i_val = gnutls_certificate_type_set_priority( p_sys->session.session,
267                                                   cert_type_priority );
268     if( i_val < 0 )
269     {
270         msg_Err( p_tls, "Cannot set certificate type priorities : %s",
271                  gnutls_strerror( i_val ) );
272         gnutls_deinit( p_sys->session.session );
273         gnutls_certificate_free_credentials( p_sys->x509_cred );
274         free( p_sys );
275         return NULL;
276     }
277
278     i_val = gnutls_credentials_set( p_sys->session.session,
279                                     GNUTLS_CRD_CERTIFICATE,
280                                     p_sys->x509_cred );
281     if( i_val < 0 )
282     {
283         msg_Err( p_tls, "Cannot set TLS session credentials : %s",
284                  gnutls_strerror( i_val ) );
285         gnutls_deinit( p_sys->session.session );
286         gnutls_certificate_free_credentials( p_sys->x509_cred );
287         free( p_sys );
288         return NULL;
289     }
290
291     p_session = malloc( sizeof (struct tls_session_t) );
292     if( p_session == NULL )
293     {
294         gnutls_deinit( p_sys->session.session );
295         gnutls_certificate_free_credentials( p_sys->x509_cred );
296         free( p_sys );
297         return NULL;
298     }
299
300     p_session->p_tls = p_tls;
301     p_session->p_server = NULL;
302     p_session->p_sys = p_sys;
303     p_session->sock.p_sys = p_session;
304     p_session->sock.pf_send = gnutls_Send;
305     p_session->sock.pf_recv = gnutls_Recv;
306     p_session->pf_handshake = gnutls_SessionHandshake;
307     p_session->pf_handshake2 = gnutls_SessionContinueHandshake;
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 }