Add a growl (http://growl.info/) notification plugin (most of code from msn.c)
[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)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 inline int
415 is_regular( const char *psz_filename )
416 {
417 #ifdef HAVE_SYS_STAT_H
418     struct stat st;
419
420     return ( utf8_stat( psz_filename, &st ) == 0 )
421         && S_ISREG( st.st_mode );
422 #else
423     return 1;
424 #endif
425 }
426
427 static int
428 gnutls_Addx509Directory( vlc_object_t *p_this,
429                          gnutls_certificate_credentials cred,
430                          const char *psz_dirname,
431                          vlc_bool_t private )
432 {
433     DIR* dir;
434     const char *psz_dirent;
435
436     if( *psz_dirname == '\0' )
437         psz_dirname = ".";
438
439     dir = utf8_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     while( ( psz_dirent = utf8_readdir( dir ) ) != NULL )
448     {
449         char *psz_filename;
450         int check = asprintf( &psz_filename, "%s/%s", psz_dirname,
451                               psz_dirent );
452         LocaleFree( psz_dirent );
453         if( check == -1 )
454         {
455             closedir( dir );
456             return VLC_ENOMEM;
457         }
458
459         /* we neglect the race condition here - not security sensitive */
460         if( is_regular( psz_filename ) )
461         {
462             int i;
463             char *psz_localname = ToLocale( psz_filename );
464
465             i = (private)
466                 ? gnutls_certificate_set_x509_key_file( cred, psz_localname,
467                                                         psz_filename,
468                                                         GNUTLS_X509_FMT_PEM )
469                 : gnutls_certificate_set_x509_trust_file( cred, psz_localname,
470                                                           GNUTLS_X509_FMT_PEM
471                                                           );
472             LocaleFree( psz_localname );
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
539         if( asprintf( &psz_path, "%s/"CONFIG_DIR"/ssl/certs",
540                       p_tls->p_vlc->psz_homedir ) == -1 )
541         {
542             gnutls_certificate_free_credentials( p_sys->x509_cred );
543             goto error;
544         }
545
546         gnutls_Addx509Directory( (vlc_object_t *)p_session, p_sys->x509_cred,
547                                  psz_path, VLC_FALSE );
548
549         free( psz_path );
550         p_session->pf_handshake2 = gnutls_HandshakeAndValidate;
551     }
552     else
553         p_session->pf_handshake2 = gnutls_ContinueHandshake;
554
555     {
556         /* FIXME: support for changing path/using multiple paths */
557         char *psz_path;
558
559         if( asprintf( &psz_path, "%s/"CONFIG_DIR"/ssl/private",
560                       p_tls->p_vlc->psz_homedir ) == -1 )
561         {
562             gnutls_certificate_free_credentials( p_sys->x509_cred );
563             goto error;
564         }
565
566         gnutls_Addx509Directory( (vlc_object_t *)p_session, p_sys->x509_cred,
567                                  psz_path, VLC_TRUE );
568
569         free( psz_path );
570     }
571
572     i_val = gnutls_init( &p_sys->session.session, GNUTLS_CLIENT );
573     if( i_val != 0 )
574     {
575         msg_Err( p_tls, "Cannot initialize TLS session : %s",
576                  gnutls_strerror( i_val ) );
577         gnutls_certificate_free_credentials( p_sys->x509_cred );
578         goto error;
579     }
580
581     i_val = gnutls_set_default_priority( p_sys->session.session );
582     if( i_val < 0 )
583     {
584         msg_Err( p_tls, "Cannot set ciphers priorities : %s",
585                  gnutls_strerror( i_val ) );
586         gnutls_deinit( p_sys->session.session );
587         gnutls_certificate_free_credentials( p_sys->x509_cred );
588         goto error;
589     }
590
591     i_val = gnutls_certificate_type_set_priority( p_sys->session.session,
592                                                   cert_type_priority );
593     if( i_val < 0 )
594     {
595         msg_Err( p_tls, "Cannot set certificate type priorities : %s",
596                  gnutls_strerror( i_val ) );
597         gnutls_deinit( p_sys->session.session );
598         gnutls_certificate_free_credentials( p_sys->x509_cred );
599         goto error;
600     }
601
602     i_val = gnutls_credentials_set( p_sys->session.session,
603                                     GNUTLS_CRD_CERTIFICATE,
604                                     p_sys->x509_cred );
605     if( i_val < 0 )
606     {
607         msg_Err( p_tls, "Cannot set TLS session credentials : %s",
608                  gnutls_strerror( i_val ) );
609         gnutls_deinit( p_sys->session.session );
610         gnutls_certificate_free_credentials( p_sys->x509_cred );
611         goto error;
612     }
613
614     return p_session;
615
616 error:
617     vlc_object_detach( p_session );
618     vlc_object_destroy( p_session );
619     free( p_sys );
620
621     return NULL;
622 }
623
624
625 /*****************************************************************************
626  * TLS session resumption callbacks
627  *****************************************************************************/
628 static int cb_store( void *p_server, gnutls_datum key, gnutls_datum data )
629 {
630     tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
631
632     if( ( p_sys->i_cache_size == 0 )
633      || ( key.size > MAX_SESSION_ID )
634      || ( data.size > MAX_SESSION_DATA ) )
635         return -1;
636
637     vlc_mutex_lock( &p_sys->cache_lock );
638
639     memcpy( p_sys->p_store->id, key.data, key.size);
640     memcpy( p_sys->p_store->data, data.data, data.size );
641     p_sys->p_store->i_idlen = key.size;
642     p_sys->p_store->i_datalen = data.size;
643
644     p_sys->p_store++;
645     if( ( p_sys->p_store - p_sys->p_cache ) == p_sys->i_cache_size )
646         p_sys->p_store = p_sys->p_cache;
647
648     vlc_mutex_unlock( &p_sys->cache_lock );
649
650     return 0;
651 }
652
653
654 static const gnutls_datum err_datum = { NULL, 0 };
655
656 static gnutls_datum cb_fetch( void *p_server, gnutls_datum key )
657 {
658     tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
659     saved_session_t *p_session, *p_end;
660
661     p_session = p_sys->p_cache;
662     p_end = p_session + p_sys->i_cache_size;
663
664     vlc_mutex_lock( &p_sys->cache_lock );
665
666     while( p_session < p_end )
667     {
668         if( ( p_session->i_idlen == key.size )
669          && !memcmp( p_session->id, key.data, key.size ) )
670         {
671             gnutls_datum data;
672
673             data.size = p_session->i_datalen;
674
675             data.data = gnutls_malloc( data.size );
676             if( data.data == NULL )
677             {
678                 vlc_mutex_unlock( &p_sys->cache_lock );
679                 return err_datum;
680             }
681
682             memcpy( data.data, p_session->data, data.size );
683             vlc_mutex_unlock( &p_sys->cache_lock );
684             return data;
685         }
686         p_session++;
687     }
688
689     vlc_mutex_unlock( &p_sys->cache_lock );
690
691     return err_datum;
692 }
693
694
695 static int cb_delete( void *p_server, gnutls_datum key )
696 {
697     tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
698     saved_session_t *p_session, *p_end;
699
700     p_session = p_sys->p_cache;
701     p_end = p_session + p_sys->i_cache_size;
702
703     vlc_mutex_lock( &p_sys->cache_lock );
704
705     while( p_session < p_end )
706     {
707         if( ( p_session->i_idlen == key.size )
708          && !memcmp( p_session->id, key.data, key.size ) )
709         {
710             p_session->i_datalen = p_session->i_idlen = 0;
711             vlc_mutex_unlock( &p_sys->cache_lock );
712             return 0;
713         }
714         p_session++;
715     }
716
717     vlc_mutex_unlock( &p_sys->cache_lock );
718
719     return -1;
720 }
721
722
723 /*****************************************************************************
724  * tls_ServerSessionPrepare:
725  *****************************************************************************
726  * Initializes server-side TLS session data.
727  *****************************************************************************/
728 static tls_session_t *
729 gnutls_ServerSessionPrepare( tls_server_t *p_server )
730 {
731     tls_session_t *p_session;
732     tls_server_sys_t *p_server_sys;
733     gnutls_session session;
734     int i_val;
735
736     p_session = vlc_object_create( p_server, sizeof (struct tls_session_t) );
737     if( p_session == NULL )
738         return NULL;
739
740     p_session->p_sys = malloc( sizeof(struct tls_session_sys_t) );
741     if( p_session->p_sys == NULL )
742     {
743         vlc_object_destroy( p_session );
744         return NULL;
745     }
746
747     vlc_object_attach( p_session, p_server );
748
749     p_server_sys = (tls_server_sys_t *)p_server->p_sys;
750     p_session->sock.p_sys = p_session;
751     p_session->sock.pf_send = gnutls_Send;
752     p_session->sock.pf_recv = gnutls_Recv;
753     p_session->pf_handshake = gnutls_BeginHandshake;
754     p_session->pf_handshake2 = p_server_sys->pf_handshake2;
755     p_session->pf_close = gnutls_SessionClose;
756
757     ((tls_session_sys_t *)p_session->p_sys)->b_handshaked = VLC_FALSE;
758     ((tls_session_sys_t *)p_session->p_sys)->psz_hostname = NULL;
759
760     i_val = gnutls_init( &session, GNUTLS_SERVER );
761     if( i_val != 0 )
762     {
763         msg_Err( p_server, "Cannot initialize TLS session : %s",
764                  gnutls_strerror( i_val ) );
765         goto error;
766     }
767
768     ((tls_session_sys_t *)p_session->p_sys)->session = session;
769
770     i_val = gnutls_set_default_priority( session );
771     if( i_val < 0 )
772     {
773         msg_Err( p_server, "Cannot set ciphers priorities : %s",
774                  gnutls_strerror( i_val ) );
775         gnutls_deinit( session );
776         goto error;
777     }
778
779     i_val = gnutls_credentials_set( session, GNUTLS_CRD_CERTIFICATE,
780                                     p_server_sys->x509_cred );
781     if( i_val < 0 )
782     {
783         msg_Err( p_server, "Cannot set TLS session credentials : %s",
784                  gnutls_strerror( i_val ) );
785         gnutls_deinit( session );
786         goto error;
787     }
788
789     if( p_session->pf_handshake2 == gnutls_HandshakeAndValidate )
790         gnutls_certificate_server_set_request( session, GNUTLS_CERT_REQUIRE );
791
792     gnutls_dh_set_prime_bits( session, get_Int( p_server, "dh-bits" ) );
793
794     /* Session resumption support */
795     gnutls_db_set_cache_expiration( session, get_Int( p_server,
796                                     "tls-cache-expiration" ) );
797     gnutls_db_set_retrieve_function( session, cb_fetch );
798     gnutls_db_set_remove_function( session, cb_delete );
799     gnutls_db_set_store_function( session, cb_store );
800     gnutls_db_set_ptr( session, p_server );
801
802     return p_session;
803
804 error:
805     free( p_session->p_sys );
806     vlc_object_detach( p_session );
807     vlc_object_destroy( p_session );
808     return NULL;
809 }
810
811
812 /*****************************************************************************
813  * tls_ServerDelete:
814  *****************************************************************************
815  * Releases data allocated with tls_ServerCreate.
816  *****************************************************************************/
817 static void
818 gnutls_ServerDelete( tls_server_t *p_server )
819 {
820     tls_server_sys_t *p_sys;
821     p_sys = (tls_server_sys_t *)p_server->p_sys;
822
823     vlc_mutex_destroy( &p_sys->cache_lock );
824     free( p_sys->p_cache );
825
826     vlc_object_detach( p_server );
827     vlc_object_destroy( p_server );
828
829     /* all sessions depending on the server are now deinitialized */
830     gnutls_certificate_free_credentials( p_sys->x509_cred );
831     gnutls_dh_params_deinit( p_sys->dh_params );
832     free( p_sys );
833 }
834
835
836 /*****************************************************************************
837  * tls_ServerAddCA:
838  *****************************************************************************
839  * Adds one or more certificate authorities.
840  * Returns -1 on error, 0 on success.
841  *****************************************************************************/
842 static int
843 gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path )
844 {
845     tls_server_sys_t *p_sys;
846     char *psz_local_path;
847     int val;
848
849     p_sys = (tls_server_sys_t *)(p_server->p_sys);
850
851     psz_local_path = ToLocale( psz_ca_path );
852     val = gnutls_certificate_set_x509_trust_file( p_sys->x509_cred,
853                                                   psz_local_path,
854                                                   GNUTLS_X509_FMT_PEM );
855     LocaleFree( psz_local_path );
856     if( val < 0 )
857     {
858         msg_Err( p_server, "Cannot add trusted CA (%s) : %s", psz_ca_path,
859                  gnutls_strerror( val ) );
860         return VLC_EGENERIC;
861     }
862     msg_Dbg( p_server, " %d trusted CA added (%s)", val, psz_ca_path );
863
864     /* enables peer's certificate verification */
865     p_sys->pf_handshake2 = gnutls_HandshakeAndValidate;
866
867     return VLC_SUCCESS;
868 }
869
870
871 /*****************************************************************************
872  * tls_ServerAddCRL:
873  *****************************************************************************
874  * Adds a certificates revocation list to be sent to TLS clients.
875  * Returns -1 on error, 0 on success.
876  *****************************************************************************/
877 static int
878 gnutls_ServerAddCRL( tls_server_t *p_server, const char *psz_crl_path )
879 {
880     int val;
881     char *psz_local_path = ToLocale( psz_crl_path );
882
883     val = gnutls_certificate_set_x509_crl_file( ((tls_server_sys_t *)
884                                                 (p_server->p_sys))->x509_cred,
885                                                 psz_local_path,
886                                                 GNUTLS_X509_FMT_PEM );
887     LocaleFree( psz_crl_path );
888     if( val < 0 )
889     {
890         msg_Err( p_server, "Cannot add CRL (%s) : %s", psz_crl_path,
891                  gnutls_strerror( val ) );
892         return VLC_EGENERIC;
893     }
894     msg_Dbg( p_server, "%d CRL added (%s)", val, psz_crl_path );
895     return VLC_SUCCESS;
896 }
897
898
899 /*****************************************************************************
900  * tls_ServerCreate:
901  *****************************************************************************
902  * Allocates a whole server's TLS credentials.
903  * Returns NULL on error.
904  *****************************************************************************/
905 static tls_server_t *
906 gnutls_ServerCreate( tls_t *p_tls, const char *psz_cert_path,
907                      const char *psz_key_path )
908 {
909     tls_server_t *p_server;
910     tls_server_sys_t *p_sys;
911     char *psz_local_key, *psz_local_cert;
912     int val;
913
914     msg_Dbg( p_tls, "Creating TLS server" );
915
916     p_sys = (tls_server_sys_t *)malloc( sizeof(struct tls_server_sys_t) );
917     if( p_sys == NULL )
918         return NULL;
919
920     p_sys->i_cache_size = get_Int( p_tls, "tls-cache-size" );
921     p_sys->p_cache = (struct saved_session_t *)calloc( p_sys->i_cache_size,
922                                            sizeof( struct saved_session_t ) );
923     if( p_sys->p_cache == NULL )
924     {
925         free( p_sys );
926         return NULL;
927     }
928     p_sys->p_store = p_sys->p_cache;
929
930     p_server = vlc_object_create( p_tls, sizeof(struct tls_server_t) );
931     if( p_server == NULL )
932     {
933         free( p_sys->p_cache );
934         free( p_sys );
935         return NULL;
936     }
937
938     vlc_object_attach( p_server, p_tls );
939
940     p_server->p_sys = p_sys;
941     p_server->pf_delete = gnutls_ServerDelete;
942     p_server->pf_add_CA = gnutls_ServerAddCA;
943     p_server->pf_add_CRL = gnutls_ServerAddCRL;
944     p_server->pf_session_prepare = gnutls_ServerSessionPrepare;
945
946     /* No certificate validation by default */
947     p_sys->pf_handshake2 = gnutls_ContinueHandshake;
948
949     /* FIXME: check for errors */
950     vlc_mutex_init( p_server, &p_sys->cache_lock );
951
952     /* Sets server's credentials */
953     val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
954     if( val != 0 )
955     {
956         msg_Err( p_server, "Cannot allocate X509 credentials : %s",
957                  gnutls_strerror( val ) );
958         goto error;
959     }
960
961     psz_local_cert = ToLocale( psz_cert_path );
962     psz_local_key = ToLocale( psz_key_path );
963     val = gnutls_certificate_set_x509_key_file( p_sys->x509_cred,
964                                                 psz_local_cert, psz_local_key,
965                                                 GNUTLS_X509_FMT_PEM );
966     LocaleFree( psz_cert_path );
967     LocaleFree( psz_key_path );
968     if( val < 0 )
969     {
970         msg_Err( p_server, "Cannot set certificate chain or private key : %s",
971                  gnutls_strerror( val ) );
972         gnutls_certificate_free_credentials( p_sys->x509_cred );
973         goto error;
974     }
975
976     /* FIXME:
977      * - regenerate these regularly
978      * - support other ciper suites
979      */
980     val = gnutls_dh_params_init( &p_sys->dh_params );
981     if( val >= 0 )
982     {
983         msg_Dbg( p_server, "Computing Diffie Hellman ciphers parameters" );
984         val = gnutls_dh_params_generate2( p_sys->dh_params,
985                                           get_Int( p_tls, "dh-bits" ) );
986     }
987     if( val < 0 )
988     {
989         msg_Err( p_server, "Cannot initialize DH cipher suites : %s",
990                  gnutls_strerror( val ) );
991         gnutls_certificate_free_credentials( p_sys->x509_cred );
992         goto error;
993     }
994     msg_Dbg( p_server, "Ciphers parameters computed" );
995
996     gnutls_certificate_set_dh_params( p_sys->x509_cred, p_sys->dh_params);
997
998     return p_server;
999
1000 error:
1001     vlc_mutex_destroy( &p_sys->cache_lock );
1002     vlc_object_detach( p_server );
1003     vlc_object_destroy( p_server );
1004     free( p_sys );
1005     return NULL;
1006 }
1007
1008
1009 /*****************************************************************************
1010  * gcrypt thread option VLC implementation:
1011  *****************************************************************************/
1012 vlc_object_t *__p_gcry_data;
1013
1014 static int gcry_vlc_mutex_init( void **p_sys )
1015 {
1016     int i_val;
1017     vlc_mutex_t *p_lock = (vlc_mutex_t *)malloc( sizeof( vlc_mutex_t ) );
1018
1019     if( p_lock == NULL)
1020         return ENOMEM;
1021
1022     i_val = vlc_mutex_init( __p_gcry_data, p_lock );
1023     if( i_val )
1024         free( p_lock );
1025     else
1026         *p_sys = p_lock;
1027     return i_val;
1028 }
1029
1030 static int gcry_vlc_mutex_destroy( void **p_sys )
1031 {
1032     int i_val;
1033     vlc_mutex_t *p_lock = (vlc_mutex_t *)*p_sys;
1034
1035     i_val = vlc_mutex_destroy( p_lock );
1036     free( p_lock );
1037     return i_val;
1038 }
1039
1040 static int gcry_vlc_mutex_lock( void **p_sys )
1041 {
1042     return vlc_mutex_lock( (vlc_mutex_t *)*p_sys );
1043 }
1044
1045 static int gcry_vlc_mutex_unlock( void **lock )
1046 {
1047     return vlc_mutex_unlock( (vlc_mutex_t *)*lock );
1048 }
1049
1050 static struct gcry_thread_cbs gcry_threads_vlc =
1051 {
1052     GCRY_THREAD_OPTION_USER,
1053     NULL,
1054     gcry_vlc_mutex_init,
1055     gcry_vlc_mutex_destroy,
1056     gcry_vlc_mutex_lock,
1057     gcry_vlc_mutex_unlock
1058 };
1059
1060
1061 /*****************************************************************************
1062  * Module initialization
1063  *****************************************************************************/
1064 static int
1065 Open( vlc_object_t *p_this )
1066 {
1067     tls_t *p_tls = (tls_t *)p_this;
1068
1069     vlc_value_t lock, count;
1070
1071     var_Create( p_this->p_libvlc, "gnutls_mutex", VLC_VAR_MUTEX );
1072     var_Get( p_this->p_libvlc, "gnutls_mutex", &lock );
1073     vlc_mutex_lock( lock.p_address );
1074
1075     /* Initialize GnuTLS only once */
1076     var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
1077     var_Get( p_this->p_libvlc, "gnutls_count", &count);
1078
1079     if( count.i_int == 0)
1080     {
1081         const char *psz_version;
1082
1083         __p_gcry_data = VLC_OBJECT( p_this->p_vlc );
1084
1085         gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_vlc);
1086         if( gnutls_global_init( ) )
1087         {
1088             msg_Warn( p_this, "cannot initialize GnuTLS" );
1089             vlc_mutex_unlock( lock.p_address );
1090             return VLC_EGENERIC;
1091         }
1092         /*
1093          * FIXME: in fact, we currently depends on 1.0.17, but it breaks on
1094          * Debian which as a patched 1.0.16 (which we can use).
1095          */
1096         psz_version = gnutls_check_version( "1.0.16" );
1097         if( psz_version == NULL )
1098         {
1099             gnutls_global_deinit( );
1100             vlc_mutex_unlock( lock.p_address );
1101             msg_Err( p_this, "unsupported GnuTLS version" );
1102             return VLC_EGENERIC;
1103         }
1104         msg_Dbg( p_this, "GnuTLS v%s initialized", psz_version );
1105     }
1106
1107     count.i_int++;
1108     var_Set( p_this->p_libvlc, "gnutls_count", count);
1109     vlc_mutex_unlock( lock.p_address );
1110
1111     p_tls->pf_server_create = gnutls_ServerCreate;
1112     p_tls->pf_client_create = gnutls_ClientCreate;
1113     return VLC_SUCCESS;
1114 }
1115
1116
1117 /*****************************************************************************
1118  * Module deinitialization
1119  *****************************************************************************/
1120 static void
1121 Close( vlc_object_t *p_this )
1122 {
1123     /*tls_t *p_tls = (tls_t *)p_this;
1124     tls_sys_t *p_sys = (tls_sys_t *)(p_this->p_sys);*/
1125
1126     vlc_value_t lock, count;
1127
1128     var_Create( p_this->p_libvlc, "gnutls_mutex", VLC_VAR_MUTEX );
1129     var_Get( p_this->p_libvlc, "gnutls_mutex", &lock );
1130     vlc_mutex_lock( lock.p_address );
1131
1132     var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
1133     var_Get( p_this->p_libvlc, "gnutls_count", &count);
1134     count.i_int--;
1135     var_Set( p_this->p_libvlc, "gnutls_count", count);
1136
1137     if( count.i_int == 0 )
1138     {
1139         gnutls_global_deinit( );
1140         msg_Dbg( p_this, "GnuTLS deinitialized" );
1141     }
1142
1143     vlc_mutex_unlock( lock.p_address );
1144 }