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