]> git.sesse.net Git - vlc/blob - modules/misc/gnutls.c
Merge string fixes
[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
64     add_integer( "dh-bits", DH_BITS, NULL, DH_BITS_TEXT,
65                  DH_BITS_LONGTEXT, VLC_TRUE );
66 vlc_module_end();
67
68
69
70 typedef struct tls_server_sys_t
71 {
72     gnutls_certificate_credentials x509_cred;
73     gnutls_dh_params dh_params;
74 } tls_server_sys_t;
75
76
77 typedef struct tls_session_sys_t
78 {
79     gnutls_session  session;
80 } tls_session_sys_t;
81
82
83 typedef struct tls_client_sys_t
84 {
85     struct tls_session_sys_t       session;
86     gnutls_certificate_credentials x509_cred;
87 } tls_client_sys_t;
88
89
90 /*****************************************************************************
91  * tls_Send:
92  *****************************************************************************
93  * Sends data through a TLS session.
94  *****************************************************************************/
95 static int
96 gnutls_Send( void *p_session, const void *buf, int i_length )
97 {
98     int val;
99     tls_session_sys_t *p_sys;
100
101     p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
102
103     val = gnutls_record_send( p_sys->session, buf, i_length );
104     /* TODO: handle fatal error */
105     return val < 0 ? -1 : val;
106 }
107
108
109 /*****************************************************************************
110  * tls_Recv:
111  *****************************************************************************
112  * Receives data through a TLS session.
113  *****************************************************************************/
114 static int
115 gnutls_Recv( void *p_session, void *buf, int i_length )
116 {
117     int val;
118     tls_session_sys_t *p_sys;
119
120     p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
121
122     val = gnutls_record_recv( p_sys->session, buf, i_length );
123     /* TODO: handle fatal error */
124     return val < 0 ? -1 : val;
125 }
126
127
128 /*****************************************************************************
129  * tls_SessionHandshake:
130  *****************************************************************************
131  * Establishes TLS session with a peer through socket <fd>.
132  * Returns NULL on error (do NOT call tls_SessionClose in case of error or
133  * re-use the session structure).
134  *****************************************************************************/
135 static tls_session_t *
136 gnutls_SessionHandshake( tls_session_t *p_session, int fd )
137 {
138     tls_session_sys_t *p_sys;
139     int val;
140
141     p_sys = (tls_session_sys_t *)(p_session->p_sys);
142
143     gnutls_transport_set_ptr (p_sys->session, (gnutls_transport_ptr)fd);
144
145     do
146         /* TODO: handle fatal error */
147         val = gnutls_handshake( p_sys->session );
148     while( ( val == GNUTLS_E_AGAIN ) || ( val == GNUTLS_E_INTERRUPTED ) );
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 NULL;
158     }
159
160     return p_session;
161 }
162
163
164 /*****************************************************************************
165  * tls_SessionClose:
166  *****************************************************************************
167  * Terminates TLS session and releases session data.
168  *****************************************************************************/
169 static void
170 gnutls_SessionClose( tls_session_t *p_session )
171 {
172     tls_session_sys_t *p_sys;
173
174     p_sys = (tls_session_sys_t *)(p_session->p_sys);
175
176     /* On the client-side, credentials are re-allocated per session */
177     if( p_session->p_server == NULL )
178         gnutls_certificate_free_credentials( ((tls_client_sys_t *)p_sys)
179                                                 ->x509_cred );
180
181     gnutls_bye( p_sys->session, GNUTLS_SHUT_WR );
182     gnutls_deinit( p_sys->session );
183     free( p_sys );
184     free( p_session );
185 }
186
187
188 /*****************************************************************************
189  * tls_ClientCreate:
190  *****************************************************************************
191  * Initializes client-side TLS session data.
192  *****************************************************************************/
193 static tls_session_t *
194 gnutls_ClientCreate( tls_t *p_tls, const char *psz_ca_path )
195 {
196     tls_session_t *p_session;
197     tls_client_sys_t *p_sys;
198     int i_val;
199     const int cert_type_priority[3] =
200     {
201         GNUTLS_CRT_X509,
202         0
203     };
204
205     p_sys = (tls_client_sys_t *)malloc( sizeof(struct tls_client_sys_t) );
206     if( p_sys == NULL )
207         return NULL;
208
209     i_val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
210     if( i_val != 0 )
211     {
212         msg_Err( p_tls, "Cannot allocate X509 credentials : %s",
213                  gnutls_strerror( i_val ) );
214         free( p_sys );
215         return NULL;
216     }
217
218     if( psz_ca_path != NULL )
219     {
220         i_val = gnutls_certificate_set_x509_trust_file( p_sys->x509_cred,
221                                                         psz_ca_path,
222                                                         GNUTLS_X509_FMT_PEM );
223         if( i_val != 0 )
224         {
225             msg_Err( p_tls, "Cannot add trusted CA (%s) : %s", psz_ca_path,
226                      gnutls_strerror( i_val ) );
227             gnutls_certificate_free_credentials( p_sys->x509_cred );
228             free( p_sys );
229             return NULL;
230         }
231     }
232
233     i_val = gnutls_init( &p_sys->session.session, GNUTLS_CLIENT );
234     if( i_val != 0 )
235     {
236         msg_Err( p_tls, "Cannot initialize TLS session : %s",
237                  gnutls_strerror( i_val ) );
238         gnutls_certificate_free_credentials( p_sys->x509_cred );
239         free( p_sys );
240         return NULL;
241     }
242
243     i_val = gnutls_set_default_priority( p_sys->session.session );
244     if( i_val < 0 )
245     {
246         msg_Err( p_tls, "Cannot set ciphers priorities : %s",
247                  gnutls_strerror( i_val ) );
248         gnutls_deinit( p_sys->session.session );
249         gnutls_certificate_free_credentials( p_sys->x509_cred );
250         free( p_sys );
251         return NULL;
252     }
253
254     i_val = gnutls_certificate_type_set_priority( p_sys->session.session,
255                                                   cert_type_priority );
256     if( i_val < 0 )
257     {
258         msg_Err( p_tls, "Cannot set certificate type 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_credentials_set( p_sys->session.session,
267                                     GNUTLS_CRD_CERTIFICATE,
268                                     p_sys->x509_cred );
269     if( i_val < 0 )
270     {
271         msg_Err( p_tls, "Cannot set TLS session credentials : %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     p_session = malloc( sizeof (struct tls_session_t) );
280     if( p_session == NULL )
281     {
282         gnutls_deinit( p_sys->session.session );
283         gnutls_certificate_free_credentials( p_sys->x509_cred );
284         free( p_sys );
285         return NULL;
286     }
287
288     p_session->p_tls = p_tls;
289     p_session->p_server = NULL;
290     p_session->p_sys = p_sys;
291     p_session->sock.p_sys = p_session;
292     p_session->sock.pf_send = gnutls_Send;
293     p_session->sock.pf_recv = gnutls_Recv;
294     p_session->pf_handshake = gnutls_SessionHandshake;
295     p_session->pf_close = gnutls_SessionClose;
296
297     return p_session;
298 }
299
300
301 /*****************************************************************************
302  * tls_ServerSessionPrepare:
303  *****************************************************************************
304  * Initializes server-side TLS session data.
305  *****************************************************************************/
306 static tls_session_t *
307 gnutls_ServerSessionPrepare( tls_server_t *p_server )
308 {
309     tls_session_t *p_session;
310     tls_session_sys_t *p_sys;
311     int i_val;
312     vlc_value_t bits;
313
314     p_sys = (tls_session_sys_t *)malloc( sizeof(struct tls_session_sys_t) );
315     if( p_sys == NULL )
316         return NULL;
317
318     i_val = gnutls_init( &p_sys->session, GNUTLS_SERVER );
319     if( i_val != 0 )
320     {
321         msg_Err( p_server->p_tls, "Cannot initialize TLS session : %s",
322                  gnutls_strerror( i_val ) );
323         free( p_sys );
324         return NULL;
325     }
326    
327     i_val = gnutls_set_default_priority( p_sys->session );
328     if( i_val < 0 )
329     {
330         msg_Err( p_server->p_tls, "Cannot set ciphers priorities : %s",
331                  gnutls_strerror( i_val ) );
332         gnutls_deinit( p_sys->session );
333         free( p_sys );
334         return NULL;
335     }
336
337     i_val = gnutls_credentials_set( p_sys->session, GNUTLS_CRD_CERTIFICATE,
338                                     ((tls_server_sys_t *)(p_server->p_sys))
339                                     ->x509_cred );
340     if( i_val < 0 )
341     {
342         msg_Err( p_server->p_tls, "Cannot set TLS session credentials : %s",
343                  gnutls_strerror( i_val ) );
344         gnutls_deinit( p_sys->session );
345         free( p_sys );
346         return NULL;
347     }
348
349     /* TODO: support for client authentication */
350     /*gnutls_certificate_server_set_request( p_session->session,
351                                            GNUTLS_CERT_REQUEST ); */
352
353     if( var_Get( p_server->p_tls, "dh-bits", &bits ) != VLC_SUCCESS )
354     {
355         var_Create( p_server->p_tls, "dh-bits",
356                     VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
357         var_Get( p_server->p_tls, "dh-bits", &bits );
358     }
359
360     gnutls_dh_set_prime_bits( p_sys->session, bits.i_int );
361
362     p_session = malloc( sizeof (struct tls_session_t) );
363     if( p_session == NULL )
364     {
365         gnutls_deinit( p_sys->session );
366         free( p_sys );
367         return NULL;
368     }
369
370     p_session->p_tls = p_server->p_tls;
371     p_session->p_server = p_server;
372     p_session->p_sys = p_sys;
373     p_session->sock.p_sys = p_session;
374     p_session->sock.pf_send = gnutls_Send;
375     p_session->sock.pf_recv = gnutls_Recv;
376     p_session->pf_handshake = gnutls_SessionHandshake;
377     p_session->pf_close = gnutls_SessionClose;
378
379     return p_session;
380 }
381
382
383 /*****************************************************************************
384  * tls_ServerDelete:
385  *****************************************************************************
386  * Releases data allocated with tls_ServerCreate.
387  *****************************************************************************/
388 static void
389 gnutls_ServerDelete( tls_server_t *p_server )
390 {
391     gnutls_certificate_free_credentials(
392                                        ((tls_server_sys_t *)(p_server->p_sys))
393                                          ->x509_cred );
394     free( p_server->p_sys );
395     free( p_server );
396 }
397
398
399 /*****************************************************************************
400  * tls_ServerAddCA:
401  *****************************************************************************
402  * Adds one or more certificate authorities.
403  * TODO: we are not able to check the client credentials yet, so this function
404  * is pretty useless.
405  * Returns -1 on error, 0 on success.
406  *****************************************************************************/
407 static int
408 gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path )
409 {
410     int val;
411
412     val = gnutls_certificate_set_x509_trust_file( ((tls_server_sys_t *)
413                                                   (p_server->p_sys))
414                                                     ->x509_cred,
415                                                   psz_ca_path,
416                                                   GNUTLS_X509_FMT_PEM );
417     if( val < 0 )
418     {
419         msg_Err( p_server->p_tls, "Cannot add trusted CA (%s) : %s",
420                  psz_ca_path, gnutls_strerror( val ) );
421         gnutls_ServerDelete( p_server );
422         return VLC_EGENERIC;
423     }
424     msg_Dbg( p_server->p_tls, " %d trusted CA added (%s)", val,
425              psz_ca_path );
426     return VLC_SUCCESS;
427 }
428
429
430 /*****************************************************************************
431  * tls_ServerAddCRL:
432  *****************************************************************************
433  * Adds a certificates revocation list to be sent to TLS clients.
434  * Returns -1 on error, 0 on success.
435  *****************************************************************************/
436 static int
437 gnutls_ServerAddCRL( tls_server_t *p_server, const char *psz_crl_path )
438 {
439     int val;
440
441     val = gnutls_certificate_set_x509_crl_file( ((tls_server_sys_t *)
442                                                 (p_server->p_sys))->x509_cred,
443                                                 psz_crl_path,
444                                                 GNUTLS_X509_FMT_PEM );
445     if( val < 0 )
446     {
447         msg_Err( p_server->p_tls, "Cannot add CRL (%s) : %s",
448                  psz_crl_path, gnutls_strerror( val ) );
449         gnutls_ServerDelete( p_server );
450         return VLC_EGENERIC;
451     }
452     msg_Dbg( p_server->p_tls, "%d CRL added (%s)", val, psz_crl_path );
453     return VLC_SUCCESS;
454 }
455     
456
457 /*****************************************************************************
458  * tls_ServerCreate:
459  *****************************************************************************
460  * Allocates a whole server's TLS credentials.
461  * Returns NULL on error.
462  *****************************************************************************/
463 static tls_server_t *
464 gnutls_ServerCreate( tls_t *p_this, const char *psz_cert_path,
465                   const char *psz_key_path )
466 {
467     tls_server_t *p_server;
468     tls_server_sys_t *p_server_sys;
469     int val;
470
471     msg_Dbg( p_this, "Creating TLS server" );
472
473     p_server_sys = (tls_server_sys_t *)malloc( sizeof(struct tls_server_sys_t) );
474     if( p_server_sys == NULL )
475         return NULL;
476
477     /* Sets server's credentials */
478     val = gnutls_certificate_allocate_credentials( &p_server_sys->x509_cred );
479     if( val != 0 )
480     {
481         msg_Err( p_this, "Cannot allocate X509 credentials : %s",
482                  gnutls_strerror( val ) );
483         free( p_server_sys );
484         return NULL;
485     }
486
487     val = gnutls_certificate_set_x509_key_file( p_server_sys->x509_cred,
488                                                 psz_cert_path, psz_key_path,
489                                                 GNUTLS_X509_FMT_PEM );
490     if( val < 0 )
491     {
492         msg_Err( p_this, "Cannot set certificate chain or private key : %s",
493                  gnutls_strerror( val ) );
494         gnutls_certificate_free_credentials( p_server_sys->x509_cred );
495         free( p_server_sys );
496         return NULL;
497     }
498
499     /* FIXME: regenerate these regularly */
500     val = gnutls_dh_params_init( &p_server_sys->dh_params );
501     if( val >= 0 )
502     {
503         vlc_value_t bits;
504
505         if( var_Get( p_this, "dh-bits", &bits ) != VLC_SUCCESS )
506         {
507             var_Create( p_this, "dh-bits",
508                         VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
509             var_Get( p_this, "dh-bits", &bits );
510         }
511
512         msg_Dbg( p_this, "Computing Diffie Hellman ciphers parameters" );
513         val = gnutls_dh_params_generate2( p_server_sys->dh_params,
514                                           bits.i_int );
515     }
516     if( val < 0 )
517     {
518         msg_Err( p_this, "Cannot initialize DH cipher suites : %s",
519                  gnutls_strerror( val ) );
520         gnutls_certificate_free_credentials( p_server_sys->x509_cred );
521         free( p_server_sys );
522         return NULL;
523     }
524     msg_Dbg( p_this, "Ciphers parameters computed" );
525
526     gnutls_certificate_set_dh_params( p_server_sys->x509_cred,
527                                       p_server_sys->dh_params);
528
529     p_server = (tls_server_t *)malloc( sizeof(struct tls_server_t) );
530     if( p_server == NULL )
531     {
532         free( p_server_sys );
533         return NULL;
534     }
535
536     p_server->p_tls = p_this;
537     p_server->p_sys = p_server_sys;
538     p_server->pf_delete = gnutls_ServerDelete;
539     p_server->pf_add_CA = gnutls_ServerAddCA;
540     p_server->pf_add_CRL = gnutls_ServerAddCRL;
541     p_server->pf_session_prepare = gnutls_ServerSessionPrepare;
542
543     return p_server;
544 }
545
546
547 /*****************************************************************************
548  * gcrypt thread option VLC implementation:
549  *****************************************************************************/
550 vlc_object_t *__p_gcry_data;
551
552 static int gcry_vlc_mutex_init (void **p_sys)
553 {
554     int i_val;
555     vlc_mutex_t *p_lock = (vlc_mutex_t *)malloc (sizeof (vlc_mutex_t));
556
557     if( p_lock == NULL)
558         return ENOMEM;
559
560     i_val = vlc_mutex_init( __p_gcry_data, p_lock);
561     if (i_val)
562         free (p_lock);
563     else
564         *p_sys = p_lock;
565     return i_val;
566 }
567
568 static int gcry_vlc_mutex_destroy (void **p_sys)
569 {
570     int i_val;
571     vlc_mutex_t *p_lock = (vlc_mutex_t *)*p_sys;
572
573     i_val = vlc_mutex_destroy (p_lock);
574     free (p_lock);
575     return i_val;
576 }
577
578 static int gcry_vlc_mutex_lock (void **p_sys)
579 {
580     return vlc_mutex_lock ((vlc_mutex_t *)*p_sys);
581 }
582
583 static int gcry_vlc_mutex_unlock (void **lock)
584 {
585     return vlc_mutex_unlock ((vlc_mutex_t *)*lock);
586 }
587
588 static struct gcry_thread_cbs gcry_threads_vlc =
589 {
590     GCRY_THREAD_OPTION_USER,
591     NULL,
592     gcry_vlc_mutex_init,
593     gcry_vlc_mutex_destroy,
594     gcry_vlc_mutex_lock,
595     gcry_vlc_mutex_unlock
596 };
597
598
599 /*****************************************************************************
600  * Module initialization
601  *****************************************************************************/
602 static int
603 Open( vlc_object_t *p_this )
604 {
605     tls_t *p_tls = (tls_t *)p_this;
606
607     vlc_value_t lock, count;
608
609     var_Create( p_this->p_libvlc, "tls_mutex", VLC_VAR_MUTEX );
610     var_Get( p_this->p_libvlc, "tls_mutex", &lock );
611     vlc_mutex_lock( lock.p_address );
612
613     /* Initialize GnuTLS only once */
614     var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
615     var_Get( p_this->p_libvlc, "gnutls_count", &count);
616
617     if( count.i_int == 0)
618     {
619         __p_gcry_data = VLC_OBJECT( p_this->p_vlc );
620
621         gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_vlc);
622         if( gnutls_global_init( ) )
623         {
624             msg_Warn( p_this, "cannot initialize GNUTLS" );
625             vlc_mutex_unlock( lock.p_address );
626             return VLC_EGENERIC;
627         }
628         if( gnutls_check_version( "1.0.0" ) == NULL )
629         {
630             gnutls_global_deinit( );
631             vlc_mutex_unlock( lock.p_address );
632             msg_Err( p_this, "unsupported GNUTLS version" );
633             return VLC_EGENERIC;
634         }
635         msg_Dbg( p_this, "GNUTLS initialized" );
636     }
637
638     count.i_int++;
639     var_Set( p_this->p_libvlc, "gnutls_count", count);
640     vlc_mutex_unlock( lock.p_address );
641
642     p_tls->pf_server_create = gnutls_ServerCreate;
643     p_tls->pf_client_create = gnutls_ClientCreate;
644     return VLC_SUCCESS;
645 }
646
647
648 /*****************************************************************************
649  * Module deinitialization
650  *****************************************************************************/
651 static void
652 Close( vlc_object_t *p_this )
653 {
654     /*tls_t *p_tls = (tls_t *)p_this;
655     tls_sys_t *p_sys = (tls_sys_t *)(p_this->p_sys);*/
656
657     vlc_value_t lock, count;
658
659     var_Create( p_this->p_libvlc, "gnutls_mutex", VLC_VAR_MUTEX );
660     var_Get( p_this->p_libvlc, "gnutls_mutex", &lock );
661     vlc_mutex_lock( lock.p_address );
662
663     var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
664     var_Get( p_this->p_libvlc, "gnutls_count", &count);
665     count.i_int--;
666     var_Set( p_this->p_libvlc, "gnutls_count", count);
667
668     if( count.i_int == 0 )
669     {
670         gnutls_global_deinit( );
671         msg_Dbg( p_this, "GNUTLS deinitialized" );
672     }
673
674     vlc_mutex_unlock( lock.p_address);
675 }