]> git.sesse.net Git - vlc/blob - modules/misc/gnutls.c
TLS session resumption support
[vlc] / modules / misc / gnutls.c
1 /*****************************************************************************
2  * tls.c
3  *****************************************************************************
4  * Copyright (C) 2004-2005 VideoLAN
5  * $Id: httpd.c 8263 2004-07-24 09:06:58Z courmisch $
6  *
7  * Authors: Remi Denis-Courmont <courmisch@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*
25  * TODO:
26  * - fix FIXMEs,
27  * - server-side client cert validation,
28  * - client-side server cert validation (?).
29  */
30
31
32 /*****************************************************************************
33  * Preamble
34  *****************************************************************************/
35 #include <stdlib.h>
36 #include <errno.h>
37 #include <vlc/vlc.h>
38
39 #include "vlc_tls.h"
40
41 #include <gcrypt.h>
42 #include <gnutls/gnutls.h>
43
44 #define DH_BITS             1024
45 #define CACHE_EXPIRATION    3600
46 #define CACHE_SIZE          64
47
48 /*****************************************************************************
49  * Module descriptor
50  *****************************************************************************/
51 static int  Open ( vlc_object_t * );
52 static void Close( vlc_object_t * );
53
54 #define DH_BITS_TEXT N_("Diffie-Hellman prime bits")
55 #define DH_BITS_LONGTEXT N_( \
56     "Allows you to modify the Diffie-Hellman prime's number of bits " \
57     "(used for TLS or SSL-based server-side encryption)." )
58
59 #define CACHE_EXPIRATION_TEXT N_("Expiration time for resumed TLS sessions")
60 #define CACHE_EXPIRATION_LONGTEXT N_( \
61     "Defines the delay before resumed TLS sessions will be expired " \
62     "(in seconds)." )
63
64 #define CACHE_SIZE_TEXT N_("Number of resumed TLS sessions")
65 #define CACHE_SIZE_LONGTEXT N_( \
66     "Allows you to modify the maximum number of resumed TLS sessions that " \
67     "the cache will hold." )
68
69
70 vlc_module_begin();
71     set_description( _("GnuTLS TLS encryption layer") );
72     set_capability( "tls", 1 );
73     set_callbacks( Open, Close );
74     set_category( CAT_ADVANCED );
75     set_subcategory( SUBCAT_ADVANCED_MISC );
76
77     add_integer( "dh-bits", DH_BITS, NULL, DH_BITS_TEXT,
78                  DH_BITS_LONGTEXT, VLC_TRUE );
79     add_integer( "tls-cache-expiration", CACHE_EXPIRATION, NULL,
80                  CACHE_EXPIRATION_TEXT, CACHE_EXPIRATION_LONGTEXT, VLC_TRUE );
81     add_integer( "tls-cache-size", CACHE_SIZE, NULL, CACHE_SIZE_TEXT,
82                  CACHE_SIZE_LONGTEXT, VLC_TRUE );
83 vlc_module_end();
84
85
86 #define MAX_SESSION_ID    32
87 #define MAX_SESSION_DATA  1024
88
89 typedef struct saved_session_t
90 {
91     char id[MAX_SESSION_ID];
92     char data[MAX_SESSION_DATA];
93
94     unsigned i_idlen;
95     unsigned i_datalen;
96 } saved_session_t;
97
98
99 typedef struct tls_server_sys_t
100 {
101     gnutls_certificate_credentials  x509_cred;
102     gnutls_dh_params                dh_params;
103
104     struct saved_session_t          *p_cache;
105     struct saved_session_t          *p_store;
106     int                             i_cache_size;
107     vlc_mutex_t                     cache_lock;
108 } tls_server_sys_t;
109
110
111 typedef struct tls_session_sys_t
112 {
113     gnutls_session  session;
114 } tls_session_sys_t;
115
116
117 typedef struct tls_client_sys_t
118 {
119     struct tls_session_sys_t       session;
120     gnutls_certificate_credentials x509_cred;
121 } tls_client_sys_t;
122
123
124 static int
125 _get_Int (vlc_object_t *p_this, const char *var)
126 {
127     vlc_value_t value;
128
129     if( var_Get( p_this, var, &value ) != VLC_SUCCESS )
130     {
131         var_Create( p_this, var, VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
132         var_Get( p_this, var, &value );
133     }
134
135     return value.i_int;
136 }
137
138 #define get_Int( a, b ) _get_Int( (vlc_object_t *)(a), (b) )
139
140
141 /*****************************************************************************
142  * tls_Send:
143  *****************************************************************************
144  * Sends data through a TLS session.
145  *****************************************************************************/
146 static int
147 gnutls_Send( void *p_session, const void *buf, int i_length )
148 {
149     int val;
150     tls_session_sys_t *p_sys;
151
152     p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
153
154     val = gnutls_record_send( p_sys->session, buf, i_length );
155     /* TODO: handle fatal error */
156     return val < 0 ? -1 : val;
157 }
158
159
160 /*****************************************************************************
161  * tls_Recv:
162  *****************************************************************************
163  * Receives data through a TLS session.
164  *****************************************************************************/
165 static int
166 gnutls_Recv( void *p_session, void *buf, int i_length )
167 {
168     int val;
169     tls_session_sys_t *p_sys;
170
171     p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
172
173     val = gnutls_record_recv( p_sys->session, buf, i_length );
174     /* TODO: handle fatal error */
175     return val < 0 ? -1 : val;
176 }
177
178
179 /*****************************************************************************
180  * tls_Session(Continue)?Handshake:
181  *****************************************************************************
182  * Establishes TLS session with a peer through socket <fd>.
183  * Returns -1 on error (you need not and must not call tls_SessionClose)
184  * 0 on succesful handshake completion, 1 if more would-be blocking recv is
185  * needed, 2 if more would-be blocking send is required.
186  *****************************************************************************/
187 static int
188 gnutls_SessionContinueHandshake( tls_session_t *p_session)
189 {
190     tls_session_sys_t *p_sys;
191     int val;
192
193     p_sys = (tls_session_sys_t *)(p_session->p_sys);
194
195      /* TODO: handle fatal error */
196     val = gnutls_handshake( p_sys->session );
197     if( ( val == GNUTLS_E_AGAIN ) || ( val == GNUTLS_E_INTERRUPTED ) )
198         return 1 + gnutls_record_get_direction( p_sys->session );
199
200     if( val < 0 )
201     {
202         gnutls_deinit( p_sys->session );
203         msg_Err( p_session->p_tls, "TLS handshake failed : %s",
204                  gnutls_strerror( val ) );
205         free( p_sys );
206         free( p_session );
207         return -1;
208     }
209
210     return 0;
211 }
212
213 static int
214 gnutls_SessionHandshake( tls_session_t *p_session, int fd )
215 {
216     tls_session_sys_t *p_sys;
217
218     p_sys = (tls_session_sys_t *)(p_session->p_sys);
219
220     gnutls_transport_set_ptr (p_sys->session, (gnutls_transport_ptr)fd);
221
222     return gnutls_SessionContinueHandshake( p_session );
223 }
224
225
226 /*****************************************************************************
227  * tls_SessionClose:
228  *****************************************************************************
229  * Terminates TLS session and releases session data.
230  *****************************************************************************/
231 static void
232 gnutls_SessionClose( tls_session_t *p_session )
233 {
234     tls_session_sys_t *p_sys;
235
236     p_sys = (tls_session_sys_t *)(p_session->p_sys);
237
238     /* On the client-side, credentials are re-allocated per session */
239     if( p_session->p_server == NULL )
240         gnutls_certificate_free_credentials( ((tls_client_sys_t *)p_sys)
241                                                 ->x509_cred );
242
243     gnutls_bye( p_sys->session, GNUTLS_SHUT_WR );
244     gnutls_deinit( p_sys->session );
245     free( p_sys );
246     free( p_session );
247 }
248
249
250 /*****************************************************************************
251  * tls_ClientCreate:
252  *****************************************************************************
253  * Initializes client-side TLS session data.
254  *****************************************************************************/
255 static tls_session_t *
256 gnutls_ClientCreate( tls_t *p_tls, const char *psz_ca_path )
257 {
258     tls_session_t *p_session;
259     tls_client_sys_t *p_sys;
260     int i_val;
261     const int cert_type_priority[3] =
262     {
263         GNUTLS_CRT_X509,
264         0
265     };
266
267     p_sys = (tls_client_sys_t *)malloc( sizeof(struct tls_client_sys_t) );
268     if( p_sys == NULL )
269         return NULL;
270
271     i_val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
272     if( i_val != 0 )
273     {
274         msg_Err( p_tls, "Cannot allocate X509 credentials : %s",
275                  gnutls_strerror( i_val ) );
276         free( p_sys );
277         return NULL;
278     }
279
280     if( psz_ca_path != NULL )
281     {
282         i_val = gnutls_certificate_set_x509_trust_file( p_sys->x509_cred,
283                                                         psz_ca_path,
284                                                         GNUTLS_X509_FMT_PEM );
285         if( i_val != 0 )
286         {
287             msg_Err( p_tls, "Cannot add trusted CA (%s) : %s", psz_ca_path,
288                      gnutls_strerror( i_val ) );
289             gnutls_certificate_free_credentials( p_sys->x509_cred );
290             free( p_sys );
291             return NULL;
292         }
293     }
294
295     i_val = gnutls_init( &p_sys->session.session, GNUTLS_CLIENT );
296     if( i_val != 0 )
297     {
298         msg_Err( p_tls, "Cannot initialize TLS session : %s",
299                  gnutls_strerror( i_val ) );
300         gnutls_certificate_free_credentials( p_sys->x509_cred );
301         free( p_sys );
302         return NULL;
303     }
304
305     i_val = gnutls_set_default_priority( p_sys->session.session );
306     if( i_val < 0 )
307     {
308         msg_Err( p_tls, "Cannot set ciphers priorities : %s",
309                  gnutls_strerror( i_val ) );
310         gnutls_deinit( p_sys->session.session );
311         gnutls_certificate_free_credentials( p_sys->x509_cred );
312         free( p_sys );
313         return NULL;
314     }
315
316     i_val = gnutls_certificate_type_set_priority( p_sys->session.session,
317                                                   cert_type_priority );
318     if( i_val < 0 )
319     {
320         msg_Err( p_tls, "Cannot set certificate type priorities : %s",
321                  gnutls_strerror( i_val ) );
322         gnutls_deinit( p_sys->session.session );
323         gnutls_certificate_free_credentials( p_sys->x509_cred );
324         free( p_sys );
325         return NULL;
326     }
327
328     i_val = gnutls_credentials_set( p_sys->session.session,
329                                     GNUTLS_CRD_CERTIFICATE,
330                                     p_sys->x509_cred );
331     if( i_val < 0 )
332     {
333         msg_Err( p_tls, "Cannot set TLS session credentials : %s",
334                  gnutls_strerror( i_val ) );
335         gnutls_deinit( p_sys->session.session );
336         gnutls_certificate_free_credentials( p_sys->x509_cred );
337         free( p_sys );
338         return NULL;
339     }
340
341     p_session = malloc( sizeof (struct tls_session_t) );
342     if( p_session == NULL )
343     {
344         gnutls_deinit( p_sys->session.session );
345         gnutls_certificate_free_credentials( p_sys->x509_cred );
346         free( p_sys );
347         return NULL;
348     }
349
350     p_session->p_tls = p_tls;
351     p_session->p_server = NULL;
352     p_session->p_sys = p_sys;
353     p_session->sock.p_sys = p_session;
354     p_session->sock.pf_send = gnutls_Send;
355     p_session->sock.pf_recv = gnutls_Recv;
356     p_session->pf_handshake = gnutls_SessionHandshake;
357     p_session->pf_handshake2 = gnutls_SessionContinueHandshake;
358     p_session->pf_close = gnutls_SessionClose;
359
360     return p_session;
361 }
362
363
364 /*****************************************************************************
365  * TLS session resumption callbacks
366  *****************************************************************************/
367 static int cb_store( void *p_server, gnutls_datum key, gnutls_datum data )
368 {
369     tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
370
371     if( ( p_sys->i_cache_size == 0 )
372      || ( key.size > MAX_SESSION_ID )
373      || ( data.size > MAX_SESSION_DATA ) )
374         return -1;
375
376     vlc_mutex_lock( &p_sys->cache_lock );
377
378     memcpy( p_sys->p_store->id, key.data, key.size);
379     memcpy( p_sys->p_store->data, data.data, data.size );
380     p_sys->p_store->i_idlen = key.size;
381     p_sys->p_store->i_datalen = data.size;
382
383     p_sys->p_store++;
384     if( ( p_sys->p_store - p_sys->p_cache ) == p_sys->i_cache_size )
385         p_sys->p_store = p_sys->p_cache;
386
387     vlc_mutex_unlock( &p_sys->cache_lock );
388
389     return 0;
390 }
391
392
393 static const gnutls_datum err_datum = { NULL, 0 };
394
395 static gnutls_datum cb_fetch( void *p_server, gnutls_datum key )
396 {
397     tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
398     saved_session_t *p_session, *p_end;
399
400     p_session = p_sys->p_cache;
401     p_end = p_session + p_sys->i_cache_size;
402
403     vlc_mutex_lock( &p_sys->cache_lock );
404
405     while( p_session < p_end )
406     {
407         if( ( p_session->i_idlen == key.size )
408          && !memcmp( p_session->id, key.data, key.size ) )
409         {
410             gnutls_datum data;
411
412             data.size = p_session->i_datalen;
413
414             data.data = gnutls_malloc( data.size );
415             if( data.data == NULL )
416             {
417                 vlc_mutex_unlock( &p_sys->cache_lock );
418                 return err_datum;
419             }
420
421             memcpy( data.data, p_session->data, data.size );
422             vlc_mutex_unlock( &p_sys->cache_lock );
423             return data;
424         }
425         p_session++;
426     }
427
428     vlc_mutex_unlock( &p_sys->cache_lock );
429
430     return err_datum;
431 }
432
433
434 static int cb_delete( void *p_server, gnutls_datum key )
435 {
436     tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
437     saved_session_t *p_session, *p_end;
438
439     p_session = p_sys->p_cache;
440     p_end = p_session + p_sys->i_cache_size;
441
442     vlc_mutex_lock( &p_sys->cache_lock );
443
444     while( p_session < p_end )
445     {
446         if( ( p_session->i_idlen == key.size )
447          && !memcmp( p_session->id, key.data, key.size ) )
448         {
449             p_session->i_datalen = p_session->i_idlen = 0;
450             vlc_mutex_unlock( &p_sys->cache_lock );
451             return 0;
452         }
453         p_session++;
454     }
455
456     vlc_mutex_unlock( &p_sys->cache_lock );
457
458     return -1;
459 }
460
461
462 /*****************************************************************************
463  * tls_ServerSessionPrepare:
464  *****************************************************************************
465  * Initializes server-side TLS session data.
466  *****************************************************************************/
467 static tls_session_t *
468 gnutls_ServerSessionPrepare( tls_server_t *p_server )
469 {
470     tls_session_t *p_session;
471     gnutls_session session;
472     int i_val;
473
474     i_val = gnutls_init( &session, GNUTLS_SERVER );
475     if( i_val != 0 )
476     {
477         msg_Err( p_server->p_tls, "Cannot initialize TLS session : %s",
478                  gnutls_strerror( i_val ) );
479         return NULL;
480     }
481    
482     i_val = gnutls_set_default_priority( session );
483     if( i_val < 0 )
484     {
485         msg_Err( p_server->p_tls, "Cannot set ciphers priorities : %s",
486                  gnutls_strerror( i_val ) );
487         gnutls_deinit( session );
488         return NULL;
489     }
490
491     i_val = gnutls_credentials_set( session, GNUTLS_CRD_CERTIFICATE,
492                                     ((tls_server_sys_t *)(p_server->p_sys))
493                                     ->x509_cred );
494     if( i_val < 0 )
495     {
496         msg_Err( p_server->p_tls, "Cannot set TLS session credentials : %s",
497                  gnutls_strerror( i_val ) );
498         gnutls_deinit( session );
499         return NULL;
500     }
501
502     /* TODO: support for client authentication */
503     /*gnutls_certificate_server_set_request( p_session->session,
504                                            GNUTLS_CERT_REQUEST ); */
505
506     gnutls_dh_set_prime_bits( session, get_Int( p_server->p_tls, "dh-bits" ) );
507
508     /* Session resumption support */
509     gnutls_db_set_cache_expiration( session, get_Int( p_server->p_tls,
510                                     "tls-cache-expiration" ) );
511     gnutls_db_set_retrieve_function( session, cb_fetch );
512     gnutls_db_set_remove_function( session, cb_delete );
513     gnutls_db_set_store_function( session, cb_store );
514     gnutls_db_set_ptr( session, p_server );
515
516     p_session = malloc( sizeof (struct tls_session_t) );
517     if( p_session == NULL )
518     {
519         gnutls_deinit( session );
520         return NULL;
521     }
522
523     p_session->p_sys = (tls_session_sys_t *)malloc( sizeof(struct tls_session_sys_t) );
524     if( p_session->p_sys == NULL )
525     {
526         gnutls_deinit( session );
527         free( p_session );
528         return NULL;
529     }
530
531     ((tls_session_sys_t *)p_session->p_sys)->session = session;
532
533     p_session->p_tls = p_server->p_tls;
534     p_session->p_server = p_server;
535     p_session->sock.p_sys = p_session;
536     p_session->sock.pf_send = gnutls_Send;
537     p_session->sock.pf_recv = gnutls_Recv;
538     p_session->pf_handshake = gnutls_SessionHandshake;
539     p_session->pf_handshake2 = gnutls_SessionContinueHandshake;
540     p_session->pf_close = gnutls_SessionClose;
541
542     return p_session;
543 }
544
545
546 /*****************************************************************************
547  * tls_ServerDelete:
548  *****************************************************************************
549  * Releases data allocated with tls_ServerCreate.
550  *****************************************************************************/
551 static void
552 gnutls_ServerDelete( tls_server_t *p_server )
553 {
554     tls_server_sys_t *p_sys;
555
556     p_sys = (tls_server_sys_t *)p_server->p_sys;
557
558     gnutls_certificate_free_credentials( p_sys->x509_cred );
559     free( p_sys->p_cache );
560     vlc_mutex_destroy( &p_sys->cache_lock );
561     free( p_sys );
562     free( p_server );
563 }
564
565
566 /*****************************************************************************
567  * tls_ServerAddCA:
568  *****************************************************************************
569  * Adds one or more certificate authorities.
570  * TODO: we are not able to check the client credentials yet, so this function
571  * is pretty useless.
572  * Returns -1 on error, 0 on success.
573  *****************************************************************************/
574 static int
575 gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path )
576 {
577     int val;
578
579     val = gnutls_certificate_set_x509_trust_file( ((tls_server_sys_t *)
580                                                   (p_server->p_sys))
581                                                     ->x509_cred,
582                                                   psz_ca_path,
583                                                   GNUTLS_X509_FMT_PEM );
584     if( val < 0 )
585     {
586         msg_Err( p_server->p_tls, "Cannot add trusted CA (%s) : %s",
587                  psz_ca_path, gnutls_strerror( val ) );
588         gnutls_ServerDelete( p_server );
589         return VLC_EGENERIC;
590     }
591     msg_Dbg( p_server->p_tls, " %d trusted CA added (%s)", val,
592              psz_ca_path );
593     return VLC_SUCCESS;
594 }
595
596
597 /*****************************************************************************
598  * tls_ServerAddCRL:
599  *****************************************************************************
600  * Adds a certificates revocation list to be sent to TLS clients.
601  * Returns -1 on error, 0 on success.
602  *****************************************************************************/
603 static int
604 gnutls_ServerAddCRL( tls_server_t *p_server, const char *psz_crl_path )
605 {
606     int val;
607
608     val = gnutls_certificate_set_x509_crl_file( ((tls_server_sys_t *)
609                                                 (p_server->p_sys))->x509_cred,
610                                                 psz_crl_path,
611                                                 GNUTLS_X509_FMT_PEM );
612     if( val < 0 )
613     {
614         msg_Err( p_server->p_tls, "Cannot add CRL (%s) : %s",
615                  psz_crl_path, gnutls_strerror( val ) );
616         gnutls_ServerDelete( p_server );
617         return VLC_EGENERIC;
618     }
619     msg_Dbg( p_server->p_tls, "%d CRL added (%s)", val, psz_crl_path );
620     return VLC_SUCCESS;
621 }
622     
623
624 /*****************************************************************************
625  * tls_ServerCreate:
626  *****************************************************************************
627  * Allocates a whole server's TLS credentials.
628  * Returns NULL on error.
629  *****************************************************************************/
630 static tls_server_t *
631 gnutls_ServerCreate( tls_t *p_this, const char *psz_cert_path,
632                   const char *psz_key_path )
633 {
634     tls_server_t *p_server;
635     tls_server_sys_t *p_server_sys;
636     int val;
637
638     msg_Dbg( p_this, "Creating TLS server" );
639
640     p_server_sys = (tls_server_sys_t *)malloc( sizeof(struct tls_server_sys_t) );
641     if( p_server_sys == NULL )
642         return NULL;
643
644     p_server_sys->i_cache_size = get_Int( p_this, "tls-cache-size" );
645     p_server_sys->p_cache = (struct saved_session_t *)
646                             calloc( p_server_sys->i_cache_size,
647                                     sizeof( struct saved_session_t ) );
648     if( p_server_sys->p_cache == NULL )
649     {
650         free( p_server_sys );
651         return NULL;
652     }
653     p_server_sys->p_store = p_server_sys->p_cache;
654     /* FIXME: check for errors */
655     vlc_mutex_init( p_this, &p_server_sys->cache_lock );
656
657     /* Sets server's credentials */
658     val = gnutls_certificate_allocate_credentials( &p_server_sys->x509_cred );
659     if( val != 0 )
660     {
661         msg_Err( p_this, "Cannot allocate X509 credentials : %s",
662                  gnutls_strerror( val ) );
663         free( p_server_sys );
664         return NULL;
665     }
666
667     val = gnutls_certificate_set_x509_key_file( p_server_sys->x509_cred,
668                                                 psz_cert_path, psz_key_path,
669                                                 GNUTLS_X509_FMT_PEM );
670     if( val < 0 )
671     {
672         msg_Err( p_this, "Cannot set certificate chain or private key : %s",
673                  gnutls_strerror( val ) );
674         gnutls_certificate_free_credentials( p_server_sys->x509_cred );
675         free( p_server_sys );
676         return NULL;
677     }
678
679     /* FIXME:
680      * - regenerate these regularly
681      * - support other ciper suites
682      */
683     val = gnutls_dh_params_init( &p_server_sys->dh_params );
684     if( val >= 0 )
685     {
686         msg_Dbg( p_this, "Computing Diffie Hellman ciphers parameters" );
687         val = gnutls_dh_params_generate2( p_server_sys->dh_params,
688                                           get_Int( p_this, "dh-bits" ) );
689     }
690     if( val < 0 )
691     {
692         msg_Err( p_this, "Cannot initialize DH cipher suites : %s",
693                  gnutls_strerror( val ) );
694         gnutls_certificate_free_credentials( p_server_sys->x509_cred );
695         free( p_server_sys );
696         return NULL;
697     }
698     msg_Dbg( p_this, "Ciphers parameters computed" );
699
700     gnutls_certificate_set_dh_params( p_server_sys->x509_cred,
701                                       p_server_sys->dh_params);
702
703     p_server = (tls_server_t *)malloc( sizeof(struct tls_server_t) );
704     if( p_server == NULL )
705     {
706         free( p_server_sys );
707         return NULL;
708     }
709
710     p_server->p_tls = p_this;
711     p_server->p_sys = p_server_sys;
712     p_server->pf_delete = gnutls_ServerDelete;
713     p_server->pf_add_CA = gnutls_ServerAddCA;
714     p_server->pf_add_CRL = gnutls_ServerAddCRL;
715     p_server->pf_session_prepare = gnutls_ServerSessionPrepare;
716
717     return p_server;
718 }
719
720
721 /*****************************************************************************
722  * gcrypt thread option VLC implementation:
723  *****************************************************************************/
724 vlc_object_t *__p_gcry_data;
725
726 static int gcry_vlc_mutex_init( void **p_sys )
727 {
728     int i_val;
729     vlc_mutex_t *p_lock = (vlc_mutex_t *)malloc( sizeof( vlc_mutex_t ) );
730
731     if( p_lock == NULL)
732         return ENOMEM;
733
734     i_val = vlc_mutex_init( __p_gcry_data, p_lock );
735     if( i_val )
736         free( p_lock );
737     else
738         *p_sys = p_lock;
739     return i_val;
740 }
741
742 static int gcry_vlc_mutex_destroy( void **p_sys )
743 {
744     int i_val;
745     vlc_mutex_t *p_lock = (vlc_mutex_t *)*p_sys;
746
747     i_val = vlc_mutex_destroy( p_lock );
748     free( p_lock );
749     return i_val;
750 }
751
752 static int gcry_vlc_mutex_lock( void **p_sys )
753 {
754     return vlc_mutex_lock( (vlc_mutex_t *)*p_sys );
755 }
756
757 static int gcry_vlc_mutex_unlock( void **lock )
758 {
759     return vlc_mutex_unlock( (vlc_mutex_t *)*lock );
760 }
761
762 static struct gcry_thread_cbs gcry_threads_vlc =
763 {
764     GCRY_THREAD_OPTION_USER,
765     NULL,
766     gcry_vlc_mutex_init,
767     gcry_vlc_mutex_destroy,
768     gcry_vlc_mutex_lock,
769     gcry_vlc_mutex_unlock
770 };
771
772
773 /*****************************************************************************
774  * Module initialization
775  *****************************************************************************/
776 static int
777 Open( vlc_object_t *p_this )
778 {
779     tls_t *p_tls = (tls_t *)p_this;
780
781     vlc_value_t lock, count;
782
783     var_Create( p_this->p_libvlc, "tls_mutex", VLC_VAR_MUTEX );
784     var_Get( p_this->p_libvlc, "tls_mutex", &lock );
785     vlc_mutex_lock( lock.p_address );
786
787     /* Initialize GnuTLS only once */
788     var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
789     var_Get( p_this->p_libvlc, "gnutls_count", &count);
790
791     if( count.i_int == 0)
792     {
793         __p_gcry_data = VLC_OBJECT( p_this->p_vlc );
794
795         gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_vlc);
796         if( gnutls_global_init( ) )
797         {
798             msg_Warn( p_this, "cannot initialize GNUTLS" );
799             vlc_mutex_unlock( lock.p_address );
800             return VLC_EGENERIC;
801         }
802         if( gnutls_check_version( "1.0.0" ) == NULL )
803         {
804             gnutls_global_deinit( );
805             vlc_mutex_unlock( lock.p_address );
806             msg_Err( p_this, "unsupported GNUTLS version" );
807             return VLC_EGENERIC;
808         }
809         msg_Dbg( p_this, "GNUTLS initialized" );
810     }
811
812     count.i_int++;
813     var_Set( p_this->p_libvlc, "gnutls_count", count);
814     vlc_mutex_unlock( lock.p_address );
815
816     p_tls->pf_server_create = gnutls_ServerCreate;
817     p_tls->pf_client_create = gnutls_ClientCreate;
818     return VLC_SUCCESS;
819 }
820
821
822 /*****************************************************************************
823  * Module deinitialization
824  *****************************************************************************/
825 static void
826 Close( vlc_object_t *p_this )
827 {
828     /*tls_t *p_tls = (tls_t *)p_this;
829     tls_sys_t *p_sys = (tls_sys_t *)(p_this->p_sys);*/
830
831     vlc_value_t lock, count;
832
833     var_Create( p_this->p_libvlc, "gnutls_mutex", VLC_VAR_MUTEX );
834     var_Get( p_this->p_libvlc, "gnutls_mutex", &lock );
835     vlc_mutex_lock( lock.p_address );
836
837     var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
838     var_Get( p_this->p_libvlc, "gnutls_count", &count);
839     count.i_int--;
840     var_Set( p_this->p_libvlc, "gnutls_count", count);
841
842     if( count.i_int == 0 )
843     {
844         gnutls_global_deinit( );
845         msg_Dbg( p_this, "GNUTLS deinitialized" );
846     }
847
848     vlc_mutex_unlock( lock.p_address);
849 }