]> git.sesse.net Git - vlc/blob - modules/misc/gnutls.c
Fix warnings (refs #258)
[vlc] / modules / misc / gnutls.c
1 /*****************************************************************************
2  * tls.c
3  *****************************************************************************
4  * Copyright (C) 2004-2005 Rémi Denis-Courmont
5  * $Id$
6  *
7  * Authors: Rémi Denis-Courmont <rem # videolan.org>
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  */
28
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <vlc/vlc.h>
36
37 #include <sys/types.h>
38 #include <errno.h>
39 #ifdef HAVE_DIRENT_H
40 # include <dirent.h>
41 #endif
42 #ifdef HAVE_SYS_STAT_H
43 # include <sys/stat.h>
44 # ifdef HAVE_UNISTD_H
45 #  include <unistd.h>
46 # endif
47 #endif
48
49
50 #include "vlc_tls.h"
51
52 #include <gcrypt.h>
53 #include <gnutls/gnutls.h>
54 #include <gnutls/x509.h>
55
56 #define DH_BITS             1024
57 #define CACHE_EXPIRATION    3600
58 #define CACHE_SIZE          64
59
60 /*****************************************************************************
61  * Module descriptor
62  *****************************************************************************/
63 static int  Open ( vlc_object_t * );
64 static void Close( vlc_object_t * );
65
66 #define DH_BITS_TEXT N_("Diffie-Hellman prime bits")
67 #define DH_BITS_LONGTEXT N_( \
68     "Allows you to modify the Diffie-Hellman prime's number of bits " \
69     "(used for TLS or SSL-based server-side encryption)." )
70
71 #define CACHE_EXPIRATION_TEXT N_("Expiration time for resumed TLS sessions")
72 #define CACHE_EXPIRATION_LONGTEXT N_( \
73     "Defines the delay before resumed TLS sessions will be expired " \
74     "(in seconds)." )
75
76 #define CACHE_SIZE_TEXT N_("Number of resumed TLS sessions")
77 #define CACHE_SIZE_LONGTEXT N_( \
78     "Allows you to modify the maximum number of resumed TLS sessions that " \
79     "the cache will hold." )
80
81 #define CHECK_CERT_TEXT N_("Check TLS/SSL server certificate validity")
82 #define CHECK_CERT_LONGTEXT N_( \
83     "Ensures that server certificate is valid " \
84     "(i.e. signed by an approved Certificate Authority)." )
85
86 #define CHECK_HOSTNAME_TEXT N_("Check TLS/SSL server hostname in certificate")
87 #define CHECK_HOSTNAME_LONGTEXT N_( \
88     "Ensures that server hostname in certificate match requested host name." )
89
90 vlc_module_begin();
91     set_shortname( "GnuTLS" );
92     set_description( _("GnuTLS TLS encryption layer") );
93     set_capability( "tls", 1 );
94     set_callbacks( Open, Close );
95     set_category( CAT_ADVANCED );
96     set_subcategory( SUBCAT_ADVANCED_MISC );
97
98     add_bool( "tls-check-cert", VLC_FALSE, NULL, CHECK_CERT_TEXT,
99               CHECK_CERT_LONGTEXT, VLC_FALSE );
100     add_bool( "tls-check-hostname", VLC_FALSE, NULL, CHECK_HOSTNAME_TEXT,
101               CHECK_HOSTNAME_LONGTEXT, VLC_FALSE );
102
103     add_integer( "dh-bits", DH_BITS, NULL, DH_BITS_TEXT,
104                  DH_BITS_LONGTEXT, VLC_TRUE );
105     add_integer( "tls-cache-expiration", CACHE_EXPIRATION, NULL,
106                  CACHE_EXPIRATION_TEXT, CACHE_EXPIRATION_LONGTEXT, VLC_TRUE );
107     add_integer( "tls-cache-size", CACHE_SIZE, NULL, CACHE_SIZE_TEXT,
108                  CACHE_SIZE_LONGTEXT, VLC_TRUE );
109 vlc_module_end();
110
111
112 #define MAX_SESSION_ID    32
113 #define MAX_SESSION_DATA  1024
114
115 typedef struct saved_session_t
116 {
117     char id[MAX_SESSION_ID];
118     char data[MAX_SESSION_DATA];
119
120     unsigned i_idlen;
121     unsigned i_datalen;
122 } saved_session_t;
123
124
125 typedef struct tls_server_sys_t
126 {
127     gnutls_certificate_credentials  x509_cred;
128     gnutls_dh_params                dh_params;
129
130     struct saved_session_t          *p_cache;
131     struct saved_session_t          *p_store;
132     int                             i_cache_size;
133     vlc_mutex_t                     cache_lock;
134
135     int                             (*pf_handshake2)( tls_session_t * );
136 } tls_server_sys_t;
137
138
139 typedef struct tls_session_sys_t
140 {
141     gnutls_session  session;
142     char            *psz_hostname;
143     vlc_bool_t      b_handshaked;
144 } tls_session_sys_t;
145
146
147 typedef struct tls_client_sys_t
148 {
149     struct tls_session_sys_t       session;
150     gnutls_certificate_credentials x509_cred;
151 } tls_client_sys_t;
152
153
154 static int
155 _get_Int( vlc_object_t *p_this, const char *var )
156 {
157     vlc_value_t value;
158
159     if( var_Get( p_this, var, &value ) != VLC_SUCCESS )
160     {
161         var_Create( p_this, var, VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
162         var_Get( p_this, var, &value );
163     }
164
165     return value.i_int;
166 }
167
168 static int
169 _get_Bool( vlc_object_t *p_this, const char *var )
170 {
171     vlc_value_t value;
172
173     if( var_Get( p_this, var, &value ) != VLC_SUCCESS )
174     {
175         var_Create( p_this, var, VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
176         var_Get( p_this, var, &value );
177     }
178
179     return value.b_bool;
180 }
181
182 #define get_Int( a, b ) _get_Int( (vlc_object_t *)(a), (b) )
183 #define get_Bool( a, b ) _get_Bool( (vlc_object_t *)(a), (b) )
184
185
186 /*****************************************************************************
187  * tls_Send:
188  *****************************************************************************
189  * Sends data through a TLS session.
190  *****************************************************************************/
191 static int
192 gnutls_Send( void *p_session, const void *buf, int i_length )
193 {
194     int val;
195     tls_session_sys_t *p_sys;
196
197     p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
198
199     val = gnutls_record_send( p_sys->session, buf, i_length );
200     /* TODO: handle fatal error */
201     return val < 0 ? -1 : val;
202 }
203
204
205 /*****************************************************************************
206  * tls_Recv:
207  *****************************************************************************
208  * Receives data through a TLS session.
209  *****************************************************************************/
210 static int
211 gnutls_Recv( void *p_session, void *buf, int i_length )
212 {
213     int val;
214     tls_session_sys_t *p_sys;
215
216     p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
217
218     val = gnutls_record_recv( p_sys->session, buf, i_length );
219     /* TODO: handle fatal error */
220     return val < 0 ? -1 : val;
221 }
222
223
224 /*****************************************************************************
225  * tls_Session(Continue)?Handshake:
226  *****************************************************************************
227  * Establishes TLS session with a peer through socket <fd>.
228  * Returns -1 on error (you need not and must not call tls_SessionClose)
229  * 0 on succesful handshake completion, 1 if more would-be blocking recv is
230  * needed, 2 if more would-be blocking send is required.
231  *****************************************************************************/
232 static int
233 gnutls_ContinueHandshake( tls_session_t *p_session)
234 {
235     tls_session_sys_t *p_sys;
236     int val;
237
238     p_sys = (tls_session_sys_t *)(p_session->p_sys);
239
240      /* TODO: handle fatal error */
241     val = gnutls_handshake( p_sys->session );
242     if( ( val == GNUTLS_E_AGAIN ) || ( val == GNUTLS_E_INTERRUPTED ) )
243         return 1 + gnutls_record_get_direction( p_sys->session );
244
245     if( val < 0 )
246     {
247         msg_Err( p_session, "TLS handshake failed : %s",
248                  gnutls_strerror( val ) );
249         p_session->pf_close( p_session );
250         return -1;
251     }
252
253     p_sys->b_handshaked = VLC_TRUE;
254     return 0;
255 }
256
257 static int
258 gnutls_HandshakeAndValidate( tls_session_t *p_session )
259 {
260     int val;
261
262     val = gnutls_ContinueHandshake( p_session );
263     if( val == 0 )
264     {
265         unsigned status;
266         gnutls_x509_crt cert;
267         const gnutls_datum *p_data;
268         tls_session_sys_t *p_sys;
269
270         p_sys = (tls_session_sys_t *)(p_session->p_sys);
271         /* certificates chain verification */
272         val = gnutls_certificate_verify_peers2( p_sys->session, &status );
273
274         if( val )
275         {
276             msg_Err( p_session, "TLS certificate verification failed : %s",
277                      gnutls_strerror( val ) );
278             p_session->pf_close( p_session );
279             return -1;
280         }
281
282         if( status )
283         {
284             msg_Warn( p_session, "TLS session : access denied" );
285             if( status & GNUTLS_CERT_INVALID )
286                 msg_Dbg( p_session, "certificate could not be verified" );
287             if( status & GNUTLS_CERT_REVOKED )
288                 msg_Dbg( p_session, "certificate was revoked" );
289             if( status & GNUTLS_CERT_SIGNER_NOT_FOUND )
290                 msg_Dbg( p_session, "certificate's signer was not found" );
291             if( status & GNUTLS_CERT_SIGNER_NOT_CA )
292                 msg_Dbg( p_session, "certificate's signer is not a CA" );
293             p_session->pf_close( p_session );
294             return -1;
295         }
296
297         msg_Dbg( p_session, "TLS certificate verified" );
298         if( p_sys->psz_hostname == NULL )
299             return 0;
300
301         /* certificate (host)name verification */
302         p_data = gnutls_certificate_get_peers( p_sys->session, &status );
303         if( p_data == NULL )
304         {
305             msg_Err( p_session, "TLS peer certificate not available" );
306             p_session->pf_close( p_session );
307             return -1;
308         }
309
310         val = gnutls_x509_crt_init( &cert );
311         if( val )
312         {
313             msg_Err( p_session, "x509 fatal error : %s",
314                      gnutls_strerror( val ) );
315             p_session->pf_close( p_session );
316             return -1;
317         }
318
319         val = gnutls_x509_crt_import( cert, p_data, GNUTLS_X509_FMT_DER );
320         if( val )
321         {
322             msg_Err( p_session, "x509 certificate import error : %s",
323                      gnutls_strerror( val ) );
324             gnutls_x509_crt_deinit( cert );
325             p_session->pf_close( p_session );
326             return -1;
327         }
328
329         if( gnutls_x509_crt_check_hostname( cert, p_sys->psz_hostname ) == 0 )
330         {
331             msg_Err( p_session, "x509 certificate does not match \"%s\"",
332                      p_sys->psz_hostname );
333             gnutls_x509_crt_deinit( cert );
334             p_session->pf_close( p_session );
335             return -1;
336         }
337
338         gnutls_x509_crt_deinit( cert );
339         
340         msg_Dbg( p_session, "x509 hostname verified" );
341         return 0;
342     }
343
344     return val;
345 }
346
347 static int
348 gnutls_BeginHandshake( tls_session_t *p_session, int fd,
349                          const char *psz_hostname )
350 {
351     tls_session_sys_t *p_sys;
352
353     p_sys = (tls_session_sys_t *)(p_session->p_sys);
354
355     gnutls_transport_set_ptr (p_sys->session, (gnutls_transport_ptr)fd);
356
357     if( psz_hostname != NULL )
358     {
359         gnutls_server_name_set( p_sys->session, GNUTLS_NAME_DNS, psz_hostname,
360                                 strlen( psz_hostname ) );
361         if( get_Bool( p_session, "tls-check-hostname" ) )
362         {
363             p_sys->psz_hostname = strdup( psz_hostname );
364             if( p_sys->psz_hostname == NULL )
365             {
366                 p_session->pf_close( p_session );
367                 return -1;
368             }
369         }
370     }
371
372     return p_session->pf_handshake2( p_session );
373 }
374
375 /*****************************************************************************
376  * tls_SessionClose:
377  *****************************************************************************
378  * Terminates TLS session and releases session data.
379  *****************************************************************************/
380 static void
381 gnutls_SessionClose( tls_session_t *p_session )
382 {
383     tls_session_sys_t *p_sys;
384
385     p_sys = (tls_session_sys_t *)(p_session->p_sys);
386
387     if( p_sys->b_handshaked == VLC_TRUE )
388         gnutls_bye( p_sys->session, GNUTLS_SHUT_WR );
389     gnutls_deinit( p_sys->session );
390
391     if( p_sys->psz_hostname != NULL )
392         free( p_sys->psz_hostname );
393
394     vlc_object_detach( p_session );
395     vlc_object_destroy( p_session );
396
397     free( p_sys );
398 }
399
400 static void
401 gnutls_ClientDelete( tls_session_t *p_session )
402 {
403     /* On the client-side, credentials are re-allocated per session */
404     gnutls_certificate_credentials x509_cred =
405                         ((tls_client_sys_t *)(p_session->p_sys))->x509_cred;
406
407     gnutls_SessionClose( p_session );
408
409     /* credentials must be free'd *after* gnutls_deinit() */
410     gnutls_certificate_free_credentials( x509_cred );
411 }
412
413 inline int
414 is_regular( const char *psz_filename )
415 {
416 #ifdef HAVE_SYS_STAT_H
417     struct stat st;
418
419     return ( stat( psz_filename, &st ) == 0 )
420         && S_ISREG( st.st_mode );
421 #else
422     return 1;
423 #endif
424 }
425
426 static int
427 gnutls_Addx509Directory( vlc_object_t *p_this,
428                          gnutls_certificate_credentials cred,
429                          const char *psz_dirname,
430                          vlc_bool_t private )
431 {
432     DIR* dir;
433     struct dirent *p_ent;
434     int i_len;
435
436     if( *psz_dirname == '\0' )
437         psz_dirname = ".";
438
439     dir = opendir( psz_dirname );
440     if( dir == NULL )
441     {
442         msg_Warn( p_this, "Cannot open directory (%s) : %s", psz_dirname,
443                   strerror( errno ) );
444         return VLC_EGENERIC;
445     }
446
447     i_len = strlen( psz_dirname ) + 2;
448
449     while( ( p_ent = readdir( dir ) ) != NULL )
450     {
451         char *psz_filename;
452
453         psz_filename = (char *)malloc( i_len + strlen( p_ent->d_name ) );
454         if( psz_filename == NULL )
455         {
456             closedir( dir );
457             return VLC_ENOMEM;
458         }
459
460         sprintf( psz_filename, "%s/%s", psz_dirname, p_ent->d_name );
461         /* we neglect the race condition here - not security sensitive */
462         if( is_regular( psz_filename ) )
463         {
464             int i;
465
466             i = (private)
467                 ? gnutls_certificate_set_x509_key_file( cred, psz_filename,
468                                                         psz_filename,
469                                                         GNUTLS_X509_FMT_PEM )
470                 : gnutls_certificate_set_x509_trust_file( cred, psz_filename,
471                                                           GNUTLS_X509_FMT_PEM
472                                                           );
473             if( i < 0 )
474             {
475                 msg_Warn( p_this, "Cannot add x509 certificate (%s) : %s",
476                           psz_filename, gnutls_strerror( i ) );
477             }
478         }
479         free( psz_filename );
480     }
481
482     closedir( dir );
483     return VLC_SUCCESS;
484 }
485
486 /*****************************************************************************
487  * tls_ClientCreate:
488  *****************************************************************************
489  * Initializes client-side TLS session data.
490  *****************************************************************************/
491 static tls_session_t *
492 gnutls_ClientCreate( tls_t *p_tls )
493 {
494     tls_session_t *p_session = NULL;
495     tls_client_sys_t *p_sys = NULL;
496     int i_val;
497     const int cert_type_priority[3] =
498     {
499         GNUTLS_CRT_X509,
500         0
501     };
502
503     p_sys = (tls_client_sys_t *)malloc( sizeof(struct tls_client_sys_t) );
504     if( p_sys == NULL )
505         return NULL;
506    
507     p_session = (struct tls_session_t *)vlc_object_create ( p_tls, sizeof(struct tls_session_t) );
508     if( p_session == NULL )
509     {
510         free( p_sys );
511         return NULL;
512     }
513
514     p_session->p_sys = p_sys;
515     p_session->sock.p_sys = p_session;
516     p_session->sock.pf_send = gnutls_Send;
517     p_session->sock.pf_recv = gnutls_Recv;
518     p_session->pf_handshake = gnutls_BeginHandshake;
519     p_session->pf_close = gnutls_ClientDelete;
520
521     p_sys->session.b_handshaked = VLC_FALSE;
522     p_sys->session.psz_hostname = NULL;
523
524     vlc_object_attach( p_session, p_tls );
525
526     i_val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
527     if( i_val != 0 )
528     {
529         msg_Err( p_tls, "Cannot allocate X509 credentials : %s",
530                  gnutls_strerror( i_val ) );
531         goto error;
532     }
533
534     if( get_Bool( p_tls, "tls-check-cert" ) )
535     {
536         /* FIXME: support for changing path/using multiple paths */
537         char *psz_path;
538         const char *psz_homedir;
539
540         psz_homedir = p_tls->p_vlc->psz_homedir;
541         psz_path = (char *)malloc( strlen( psz_homedir )
542                                    + sizeof( CONFIG_DIR ) + 12 );
543         if( psz_path == NULL )
544         {
545             gnutls_certificate_free_credentials( p_sys->x509_cred );
546             goto error;
547         }
548
549         sprintf( psz_path, "%s/"CONFIG_DIR"/ssl/certs", psz_homedir );
550         gnutls_Addx509Directory( (vlc_object_t *)p_session, p_sys->x509_cred,
551                                  psz_path, VLC_FALSE );
552
553         free( psz_path );
554         p_session->pf_handshake2 = gnutls_HandshakeAndValidate;
555     }
556     else
557         p_session->pf_handshake2 = gnutls_ContinueHandshake;
558
559     {
560         /* FIXME: support for changing path/using multiple paths */
561         char *psz_path;
562         const char *psz_homedir;
563
564         psz_homedir = p_tls->p_vlc->psz_homedir;
565         psz_path = (char *)malloc( strlen( psz_homedir )
566                                    + sizeof( CONFIG_DIR ) + 14 );
567         if( psz_path == NULL )
568         {
569             gnutls_certificate_free_credentials( p_sys->x509_cred );
570             goto error;
571         }
572
573         sprintf( psz_path, "%s/"CONFIG_DIR"/ssl/private", psz_homedir );
574         gnutls_Addx509Directory( (vlc_object_t *)p_session, p_sys->x509_cred,
575                                  psz_path, VLC_TRUE );
576
577         free( psz_path );
578     }
579
580     i_val = gnutls_init( &p_sys->session.session, GNUTLS_CLIENT );
581     if( i_val != 0 )
582     {
583         msg_Err( p_tls, "Cannot initialize TLS session : %s",
584                  gnutls_strerror( i_val ) );
585         gnutls_certificate_free_credentials( p_sys->x509_cred );
586         goto error;
587     }
588
589     i_val = gnutls_set_default_priority( p_sys->session.session );
590     if( i_val < 0 )
591     {
592         msg_Err( p_tls, "Cannot set ciphers priorities : %s",
593                  gnutls_strerror( i_val ) );
594         gnutls_deinit( p_sys->session.session );
595         gnutls_certificate_free_credentials( p_sys->x509_cred );
596         goto error;
597     }
598
599     i_val = gnutls_certificate_type_set_priority( p_sys->session.session,
600                                                   cert_type_priority );
601     if( i_val < 0 )
602     {
603         msg_Err( p_tls, "Cannot set certificate type priorities : %s",
604                  gnutls_strerror( i_val ) );
605         gnutls_deinit( p_sys->session.session );
606         gnutls_certificate_free_credentials( p_sys->x509_cred );
607         goto error;
608     }
609
610     i_val = gnutls_credentials_set( p_sys->session.session,
611                                     GNUTLS_CRD_CERTIFICATE,
612                                     p_sys->x509_cred );
613     if( i_val < 0 )
614     {
615         msg_Err( p_tls, "Cannot set TLS session credentials : %s",
616                  gnutls_strerror( i_val ) );
617         gnutls_deinit( p_sys->session.session );
618         gnutls_certificate_free_credentials( p_sys->x509_cred );
619         goto error;
620     }
621
622     return p_session;
623
624 error:
625     vlc_object_detach( p_session );
626     vlc_object_destroy( p_session );
627     free( p_sys );
628
629     return NULL;
630 }
631
632
633 /*****************************************************************************
634  * TLS session resumption callbacks
635  *****************************************************************************/
636 static int cb_store( void *p_server, gnutls_datum key, gnutls_datum data )
637 {
638     tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
639
640     if( ( p_sys->i_cache_size == 0 )
641      || ( key.size > MAX_SESSION_ID )
642      || ( data.size > MAX_SESSION_DATA ) )
643         return -1;
644
645     vlc_mutex_lock( &p_sys->cache_lock );
646
647     memcpy( p_sys->p_store->id, key.data, key.size);
648     memcpy( p_sys->p_store->data, data.data, data.size );
649     p_sys->p_store->i_idlen = key.size;
650     p_sys->p_store->i_datalen = data.size;
651
652     p_sys->p_store++;
653     if( ( p_sys->p_store - p_sys->p_cache ) == p_sys->i_cache_size )
654         p_sys->p_store = p_sys->p_cache;
655
656     vlc_mutex_unlock( &p_sys->cache_lock );
657
658     return 0;
659 }
660
661
662 static const gnutls_datum err_datum = { NULL, 0 };
663
664 static gnutls_datum cb_fetch( void *p_server, gnutls_datum key )
665 {
666     tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
667     saved_session_t *p_session, *p_end;
668
669     p_session = p_sys->p_cache;
670     p_end = p_session + p_sys->i_cache_size;
671
672     vlc_mutex_lock( &p_sys->cache_lock );
673
674     while( p_session < p_end )
675     {
676         if( ( p_session->i_idlen == key.size )
677          && !memcmp( p_session->id, key.data, key.size ) )
678         {
679             gnutls_datum data;
680
681             data.size = p_session->i_datalen;
682
683             data.data = gnutls_malloc( data.size );
684             if( data.data == NULL )
685             {
686                 vlc_mutex_unlock( &p_sys->cache_lock );
687                 return err_datum;
688             }
689
690             memcpy( data.data, p_session->data, data.size );
691             vlc_mutex_unlock( &p_sys->cache_lock );
692             return data;
693         }
694         p_session++;
695     }
696
697     vlc_mutex_unlock( &p_sys->cache_lock );
698
699     return err_datum;
700 }
701
702
703 static int cb_delete( void *p_server, gnutls_datum key )
704 {
705     tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
706     saved_session_t *p_session, *p_end;
707
708     p_session = p_sys->p_cache;
709     p_end = p_session + p_sys->i_cache_size;
710
711     vlc_mutex_lock( &p_sys->cache_lock );
712
713     while( p_session < p_end )
714     {
715         if( ( p_session->i_idlen == key.size )
716          && !memcmp( p_session->id, key.data, key.size ) )
717         {
718             p_session->i_datalen = p_session->i_idlen = 0;
719             vlc_mutex_unlock( &p_sys->cache_lock );
720             return 0;
721         }
722         p_session++;
723     }
724
725     vlc_mutex_unlock( &p_sys->cache_lock );
726
727     return -1;
728 }
729
730
731 /*****************************************************************************
732  * tls_ServerSessionPrepare:
733  *****************************************************************************
734  * Initializes server-side TLS session data.
735  *****************************************************************************/
736 static tls_session_t *
737 gnutls_ServerSessionPrepare( tls_server_t *p_server )
738 {
739     tls_session_t *p_session;
740     tls_server_sys_t *p_server_sys;
741     gnutls_session session;
742     int i_val;
743
744     p_session = vlc_object_create( p_server, sizeof (struct tls_session_t) );
745     if( p_session == NULL )
746         return NULL;
747     
748     p_session->p_sys = malloc( sizeof(struct tls_session_sys_t) );
749     if( p_session->p_sys == NULL )
750     {
751         vlc_object_destroy( p_session );
752         return NULL;
753     }
754
755     vlc_object_attach( p_session, p_server );
756
757     p_server_sys = (tls_server_sys_t *)p_server->p_sys;
758     p_session->sock.p_sys = p_session;
759     p_session->sock.pf_send = gnutls_Send;
760     p_session->sock.pf_recv = gnutls_Recv;
761     p_session->pf_handshake = gnutls_BeginHandshake;
762     p_session->pf_handshake2 = p_server_sys->pf_handshake2;
763     p_session->pf_close = gnutls_SessionClose;
764
765     ((tls_session_sys_t *)p_session->p_sys)->b_handshaked = VLC_FALSE;
766     ((tls_session_sys_t *)p_session->p_sys)->psz_hostname = NULL;
767
768     i_val = gnutls_init( &session, GNUTLS_SERVER );
769     if( i_val != 0 )
770     {
771         msg_Err( p_server, "Cannot initialize TLS session : %s",
772                  gnutls_strerror( i_val ) );
773         goto error;
774     }
775
776     ((tls_session_sys_t *)p_session->p_sys)->session = session;
777
778     i_val = gnutls_set_default_priority( session );
779     if( i_val < 0 )
780     {
781         msg_Err( p_server, "Cannot set ciphers priorities : %s",
782                  gnutls_strerror( i_val ) );
783         gnutls_deinit( session );
784         goto error;
785     }
786
787     i_val = gnutls_credentials_set( session, GNUTLS_CRD_CERTIFICATE,
788                                     p_server_sys->x509_cred );
789     if( i_val < 0 )
790     {
791         msg_Err( p_server, "Cannot set TLS session credentials : %s",
792                  gnutls_strerror( i_val ) );
793         gnutls_deinit( session );
794         goto error;
795     }
796
797     if( p_session->pf_handshake2 == gnutls_HandshakeAndValidate )
798         gnutls_certificate_server_set_request( session, GNUTLS_CERT_REQUIRE );
799
800     gnutls_dh_set_prime_bits( session, get_Int( p_server, "dh-bits" ) );
801
802     /* Session resumption support */
803     gnutls_db_set_cache_expiration( session, get_Int( p_server,
804                                     "tls-cache-expiration" ) );
805     gnutls_db_set_retrieve_function( session, cb_fetch );
806     gnutls_db_set_remove_function( session, cb_delete );
807     gnutls_db_set_store_function( session, cb_store );
808     gnutls_db_set_ptr( session, p_server );
809
810     return p_session;
811
812 error:
813     free( p_session->p_sys );
814     vlc_object_detach( p_session );
815     vlc_object_destroy( p_session );
816     return NULL;
817 }
818
819
820 /*****************************************************************************
821  * tls_ServerDelete:
822  *****************************************************************************
823  * Releases data allocated with tls_ServerCreate.
824  *****************************************************************************/
825 static void
826 gnutls_ServerDelete( tls_server_t *p_server )
827 {
828     tls_server_sys_t *p_sys;
829     p_sys = (tls_server_sys_t *)p_server->p_sys;
830
831     vlc_mutex_destroy( &p_sys->cache_lock );
832     free( p_sys->p_cache );
833
834     vlc_object_detach( p_server );
835     vlc_object_destroy( p_server );
836
837     /* all sessions depending on the server are now deinitialized */
838     gnutls_certificate_free_credentials( p_sys->x509_cred );
839     gnutls_dh_params_deinit( p_sys->dh_params );
840     free( p_sys );
841 }
842
843
844 /*****************************************************************************
845  * tls_ServerAddCA:
846  *****************************************************************************
847  * Adds one or more certificate authorities.
848  * Returns -1 on error, 0 on success.
849  *****************************************************************************/
850 static int
851 gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path )
852 {
853     int val;
854     tls_server_sys_t *p_sys;
855
856     p_sys = (tls_server_sys_t *)(p_server->p_sys);
857
858     val = gnutls_certificate_set_x509_trust_file( p_sys->x509_cred,
859                                                   psz_ca_path,
860                                                   GNUTLS_X509_FMT_PEM );
861     if( val < 0 )
862     {
863         msg_Err( p_server, "Cannot add trusted CA (%s) : %s", psz_ca_path,
864                  gnutls_strerror( val ) );
865         return VLC_EGENERIC;
866     }
867     msg_Dbg( p_server, " %d trusted CA added (%s)", val, psz_ca_path );
868
869     /* enables peer's certificate verification */
870     p_sys->pf_handshake2 = gnutls_HandshakeAndValidate;
871
872     return VLC_SUCCESS;
873 }
874
875
876 /*****************************************************************************
877  * tls_ServerAddCRL:
878  *****************************************************************************
879  * Adds a certificates revocation list to be sent to TLS clients.
880  * Returns -1 on error, 0 on success.
881  *****************************************************************************/
882 static int
883 gnutls_ServerAddCRL( tls_server_t *p_server, const char *psz_crl_path )
884 {
885     int val;
886
887     val = gnutls_certificate_set_x509_crl_file( ((tls_server_sys_t *)
888                                                 (p_server->p_sys))->x509_cred,
889                                                 psz_crl_path,
890                                                 GNUTLS_X509_FMT_PEM );
891     if( val < 0 )
892     {
893         msg_Err( p_server, "Cannot add CRL (%s) : %s", psz_crl_path,
894                  gnutls_strerror( val ) );
895         return VLC_EGENERIC;
896     }
897     msg_Dbg( p_server, "%d CRL added (%s)", val, psz_crl_path );
898     return VLC_SUCCESS;
899 }
900     
901
902 /*****************************************************************************
903  * tls_ServerCreate:
904  *****************************************************************************
905  * Allocates a whole server's TLS credentials.
906  * Returns NULL on error.
907  *****************************************************************************/
908 static tls_server_t *
909 gnutls_ServerCreate( tls_t *p_tls, const char *psz_cert_path,
910                      const char *psz_key_path )
911 {
912     tls_server_t *p_server;
913     tls_server_sys_t *p_sys;
914     int val;
915
916     msg_Dbg( p_tls, "Creating TLS server" );
917
918     p_sys = (tls_server_sys_t *)malloc( sizeof(struct tls_server_sys_t) );
919     if( p_sys == NULL )
920         return NULL;
921
922     p_sys->i_cache_size = get_Int( p_tls, "tls-cache-size" );
923     p_sys->p_cache = (struct saved_session_t *)calloc( p_sys->i_cache_size,
924                                            sizeof( struct saved_session_t ) );
925     if( p_sys->p_cache == NULL )
926     {
927         free( p_sys );
928         return NULL;
929     }
930     p_sys->p_store = p_sys->p_cache;
931
932     p_server = vlc_object_create( p_tls, sizeof(struct tls_server_t) );
933     if( p_server == NULL )
934     {
935         free( p_sys->p_cache );
936         free( p_sys );
937         return NULL;
938     }
939
940     vlc_object_attach( p_server, p_tls );
941
942     p_server->p_sys = p_sys;
943     p_server->pf_delete = gnutls_ServerDelete;
944     p_server->pf_add_CA = gnutls_ServerAddCA;
945     p_server->pf_add_CRL = gnutls_ServerAddCRL;
946     p_server->pf_session_prepare = gnutls_ServerSessionPrepare;
947
948     /* No certificate validation by default */
949     p_sys->pf_handshake2 = gnutls_ContinueHandshake;
950
951     /* FIXME: check for errors */
952     vlc_mutex_init( p_server, &p_sys->cache_lock );
953
954     /* Sets server's credentials */
955     val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
956     if( val != 0 )
957     {
958         msg_Err( p_server, "Cannot allocate X509 credentials : %s",
959                  gnutls_strerror( val ) );
960         goto error;
961     }
962
963     val = gnutls_certificate_set_x509_key_file( p_sys->x509_cred,
964                                                 psz_cert_path, psz_key_path,
965                                                 GNUTLS_X509_FMT_PEM );
966     if( val < 0 )
967     {
968         msg_Err( p_server, "Cannot set certificate chain or private key : %s",
969                  gnutls_strerror( val ) );
970         gnutls_certificate_free_credentials( p_sys->x509_cred );
971         goto error;
972     }
973
974     /* FIXME:
975      * - regenerate these regularly
976      * - support other ciper suites
977      */
978     val = gnutls_dh_params_init( &p_sys->dh_params );
979     if( val >= 0 )
980     {
981         msg_Dbg( p_server, "Computing Diffie Hellman ciphers parameters" );
982         val = gnutls_dh_params_generate2( p_sys->dh_params,
983                                           get_Int( p_tls, "dh-bits" ) );
984     }
985     if( val < 0 )
986     {
987         msg_Err( p_server, "Cannot initialize DH cipher suites : %s",
988                  gnutls_strerror( val ) );
989         gnutls_certificate_free_credentials( p_sys->x509_cred );
990         goto error;
991     }
992     msg_Dbg( p_server, "Ciphers parameters computed" );
993
994     gnutls_certificate_set_dh_params( p_sys->x509_cred, p_sys->dh_params);
995
996     return p_server;
997
998 error:
999     vlc_mutex_destroy( &p_sys->cache_lock );
1000     vlc_object_detach( p_server );
1001     vlc_object_destroy( p_server );
1002     free( p_sys );
1003     return NULL;
1004 }
1005
1006
1007 /*****************************************************************************
1008  * gcrypt thread option VLC implementation:
1009  *****************************************************************************/
1010 vlc_object_t *__p_gcry_data;
1011
1012 static int gcry_vlc_mutex_init( void **p_sys )
1013 {
1014     int i_val;
1015     vlc_mutex_t *p_lock = (vlc_mutex_t *)malloc( sizeof( vlc_mutex_t ) );
1016
1017     if( p_lock == NULL)
1018         return ENOMEM;
1019
1020     i_val = vlc_mutex_init( __p_gcry_data, p_lock );
1021     if( i_val )
1022         free( p_lock );
1023     else
1024         *p_sys = p_lock;
1025     return i_val;
1026 }
1027
1028 static int gcry_vlc_mutex_destroy( void **p_sys )
1029 {
1030     int i_val;
1031     vlc_mutex_t *p_lock = (vlc_mutex_t *)*p_sys;
1032
1033     i_val = vlc_mutex_destroy( p_lock );
1034     free( p_lock );
1035     return i_val;
1036 }
1037
1038 static int gcry_vlc_mutex_lock( void **p_sys )
1039 {
1040     return vlc_mutex_lock( (vlc_mutex_t *)*p_sys );
1041 }
1042
1043 static int gcry_vlc_mutex_unlock( void **lock )
1044 {
1045     return vlc_mutex_unlock( (vlc_mutex_t *)*lock );
1046 }
1047
1048 static struct gcry_thread_cbs gcry_threads_vlc =
1049 {
1050     GCRY_THREAD_OPTION_USER,
1051     NULL,
1052     gcry_vlc_mutex_init,
1053     gcry_vlc_mutex_destroy,
1054     gcry_vlc_mutex_lock,
1055     gcry_vlc_mutex_unlock
1056 };
1057
1058
1059 /*****************************************************************************
1060  * Module initialization
1061  *****************************************************************************/
1062 static int
1063 Open( vlc_object_t *p_this )
1064 {
1065     tls_t *p_tls = (tls_t *)p_this;
1066
1067     vlc_value_t lock, count;
1068
1069     var_Create( p_this->p_libvlc, "gnutls_mutex", VLC_VAR_MUTEX );
1070     var_Get( p_this->p_libvlc, "gnutls_mutex", &lock );
1071     vlc_mutex_lock( lock.p_address );
1072
1073     /* Initialize GnuTLS only once */
1074     var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
1075     var_Get( p_this->p_libvlc, "gnutls_count", &count);
1076
1077     if( count.i_int == 0)
1078     {
1079         const char *psz_version;
1080
1081         __p_gcry_data = VLC_OBJECT( p_this->p_vlc );
1082
1083         gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_vlc);
1084         if( gnutls_global_init( ) )
1085         {
1086             msg_Warn( p_this, "cannot initialize GnuTLS" );
1087             vlc_mutex_unlock( lock.p_address );
1088             return VLC_EGENERIC;
1089         }
1090         /*
1091          * FIXME: in fact, we currently depends on 1.0.17, but it breaks on
1092          * Debian which as a patched 1.0.16 (which we can use).
1093          */
1094         psz_version = gnutls_check_version( "1.0.16" );
1095         if( psz_version == NULL )
1096         {
1097             gnutls_global_deinit( );
1098             vlc_mutex_unlock( lock.p_address );
1099             msg_Err( p_this, "unsupported GnuTLS version" );
1100             return VLC_EGENERIC;
1101         }
1102         msg_Dbg( p_this, "GnuTLS v%s initialized", psz_version );
1103     }
1104
1105     count.i_int++;
1106     var_Set( p_this->p_libvlc, "gnutls_count", count);
1107     vlc_mutex_unlock( lock.p_address );
1108
1109     p_tls->pf_server_create = gnutls_ServerCreate;
1110     p_tls->pf_client_create = gnutls_ClientCreate;
1111     return VLC_SUCCESS;
1112 }
1113
1114
1115 /*****************************************************************************
1116  * Module deinitialization
1117  *****************************************************************************/
1118 static void
1119 Close( vlc_object_t *p_this )
1120 {
1121     /*tls_t *p_tls = (tls_t *)p_this;
1122     tls_sys_t *p_sys = (tls_sys_t *)(p_this->p_sys);*/
1123
1124     vlc_value_t lock, count;
1125
1126     var_Create( p_this->p_libvlc, "gnutls_mutex", VLC_VAR_MUTEX );
1127     var_Get( p_this->p_libvlc, "gnutls_mutex", &lock );
1128     vlc_mutex_lock( lock.p_address );
1129
1130     var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
1131     var_Get( p_this->p_libvlc, "gnutls_count", &count);
1132     count.i_int--;
1133     var_Set( p_this->p_libvlc, "gnutls_count", count);
1134
1135     if( count.i_int == 0 )
1136     {
1137         gnutls_global_deinit( );
1138         msg_Dbg( p_this, "GnuTLS deinitialized" );
1139     }
1140
1141     vlc_mutex_unlock( lock.p_address );
1142 }