]> git.sesse.net Git - vlc/blob - modules/misc/gnutls.c
072928a98ed24673c319042fc02707c7e647f10d
[vlc] / modules / misc / gnutls.c
1 /*****************************************************************************
2  * tls.c
3  *****************************************************************************
4  * Copyright (C) 2004 VideoLAN
5  * $Id: httpd.c 8263 2004-07-24 09:06:58Z courmisch $
6  *
7  * Authors: Remi Denis-Courmont <courmisch@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*
25  * TODO:
26  * - fix FIXMEs,
27  * - client side stuff,
28  * - server-side client cert validation,
29  * - client-side server cert validation (?).
30  */
31
32  
33 /*****************************************************************************
34  * Preamble
35  *****************************************************************************/
36 #include <stdlib.h>
37 #include <errno.h>
38 #include <vlc/vlc.h>
39
40 #include "vlc_tls.h"
41
42 #include <gcrypt.h>
43 #include <gnutls/gnutls.h>
44
45 #define DH_BITS 1024
46
47
48 /*****************************************************************************
49  * Module descriptor
50  *****************************************************************************/
51 static int  Open ( vlc_object_t * );
52 static void Close( vlc_object_t * );
53
54 #define DH_BITS_TEXT N_("Diffie-Hellman prime bits")
55 #define DH_BITS_LONGTEXT N_( \
56     "Allows you to modify the Diffie-Hellman prime's number of bits" \
57     "(used for TLS or SSL-based server-side encryption)." )
58
59 vlc_module_begin();
60     set_description( _("GnuTLS TLS encryption layer") );
61     set_capability( "tls", 1 );
62     set_callbacks( Open, Close );
63
64     add_integer( "dh-bits", DH_BITS, NULL, DH_BITS_TEXT,
65                  DH_BITS_LONGTEXT, VLC_TRUE );
66 vlc_module_end();
67
68
69
70 typedef struct tls_server_sys_t
71 {
72     gnutls_certificate_credentials x509_cred;
73     gnutls_dh_params dh_params;
74 } tls_server_sys_t;
75
76
77 /*****************************************************************************
78  * tls_Send:
79  *****************************************************************************
80  * Sends data through a TLS session.
81  *****************************************************************************/
82 static int
83 gnutls_Send( tls_session_t *p_session, const char *buf, int i_length )
84 {
85     int val;
86
87     val = gnutls_record_send( *(gnutls_session *)(p_session->p_sys),
88                               buf, i_length );
89     return val < 0 ? -1 : val;
90 }
91
92
93 /*****************************************************************************
94  * tls_Recv:
95  *****************************************************************************
96  * Receives data through a TLS session
97  *****************************************************************************/
98 static int
99 gnutls_Recv( tls_session_t *p_session, char *buf, int i_length )
100 {
101     int val;
102
103     val = gnutls_record_recv( *(gnutls_session *)(p_session->p_sys),
104                               buf, i_length );
105     return val < 0 ? -1 : val;
106 }
107
108
109 /*****************************************************************************
110  * tls_SessionHandshake:
111  *****************************************************************************
112  * Establishes TLS session with a peer through socket <fd>
113  * Returns NULL on error (do NOT call tls_SessionClose in case of error or
114  * re-use the session structure).
115  *****************************************************************************/
116 static tls_session_t *
117 gnutls_SessionHandshake( tls_session_t *p_session, int fd )
118 {
119     int val;
120     gnutls_session *p_sys;
121
122     p_sys = (gnutls_session *)(p_session->p_sys);
123
124     gnutls_transport_set_ptr( *p_sys, (gnutls_transport_ptr)fd);
125     val = gnutls_handshake( *p_sys );
126     if( val < 0 )
127     {
128         gnutls_deinit( *p_sys );
129         msg_Err( p_session->p_tls, "TLS handshake failed : %s",
130                  gnutls_strerror( val ) );
131         free( p_sys );
132         free( p_session );
133         return NULL;
134     }
135     return p_session;
136 }
137
138
139 /*****************************************************************************
140  * tls_ServerCreate:
141  *****************************************************************************
142  * Terminates a TLS session and releases session data.
143  *****************************************************************************/
144 static void
145 gnutls_SessionClose( tls_session_t *p_session )
146 {
147     gnutls_session *p_sys;
148
149     p_sys = (gnutls_session *)(p_session->p_sys);
150
151     gnutls_bye( *p_sys, GNUTLS_SHUT_WR );
152     gnutls_deinit ( *p_sys );
153     free( p_sys );
154     free( p_session );
155 }
156
157
158 /*****************************************************************************
159  * tls_ServerSessionPrepare:
160  *****************************************************************************
161  * Initializes a server-side TLS session data
162  *****************************************************************************/
163 static tls_session_t *
164 gnutls_ServerSessionPrepare( tls_server_t *p_server )
165 {
166     tls_session_t *p_session;
167     gnutls_session *p_sys;
168     int val;
169     vlc_value_t bits;
170
171     p_sys = (gnutls_session *)malloc( sizeof(gnutls_session) );
172     if( p_sys == NULL )
173         return NULL;
174
175     val = gnutls_init( p_sys, GNUTLS_SERVER );
176     if( val != 0 )
177     {
178         msg_Err( p_server->p_tls, "Cannot initialize TLS session : %s",
179                  gnutls_strerror( val ) );
180         free( p_sys );
181         return NULL;
182     }
183    
184     val = gnutls_set_default_priority( *p_sys );
185     if( val < 0 )
186     {
187         msg_Err( p_server->p_tls, "Cannot set ciphers priorities : %s",
188                  gnutls_strerror( val ) );
189         gnutls_deinit( *p_sys );
190         free( p_sys );
191         return NULL;
192     }
193
194     val = gnutls_credentials_set( *p_sys, GNUTLS_CRD_CERTIFICATE,
195                                   ((tls_server_sys_t *)(p_server->p_sys))
196                                   ->x509_cred );
197     if( val < 0 )
198     {
199         msg_Err( p_server->p_tls, "Cannot set TLS session credentials : %s",
200                  gnutls_strerror( val ) );
201         gnutls_deinit( *p_sys );
202         free( p_sys );
203         return NULL;
204     }
205
206     /* TODO: support for client authentication */
207     /*gnutls_certificate_server_set_request( p_session->session,
208                                            GNUTLS_CERT_REQUEST ); */
209
210     if( var_Get( p_server->p_tls, "dh-bits", &bits ) != VLC_SUCCESS )
211     {
212         var_Create( p_server->p_tls, "dh-bits",
213                     VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
214         var_Get( p_server->p_tls, "dh-bits", &bits );
215     }
216
217     gnutls_dh_set_prime_bits( *p_sys, bits.i_int );
218
219     p_session = malloc( sizeof (struct tls_session_t) );
220     if( p_session == NULL )
221     {
222         gnutls_deinit( *p_sys );
223         free( p_sys );
224         return NULL;
225     }
226
227     p_session->p_tls = p_server->p_tls;
228     p_session->p_server = p_server;
229     p_session->p_sys = p_sys;
230     p_session->pf_handshake =gnutls_SessionHandshake;
231     p_session->pf_close = gnutls_SessionClose;
232     p_session->pf_send = gnutls_Send;
233     p_session->pf_recv = gnutls_Recv;
234
235     return p_session;
236 }
237
238
239 /*****************************************************************************
240  * tls_ServerDelete:
241  *****************************************************************************
242  * Releases data allocated with tls_ServerCreate
243  *****************************************************************************/
244 static void
245 gnutls_ServerDelete( tls_server_t *p_server )
246 {
247     gnutls_certificate_free_credentials(
248                                        ((tls_server_sys_t *)(p_server->p_sys))
249                                          ->x509_cred );
250     free( p_server->p_sys );
251     free( p_server );
252 }
253
254
255 /*****************************************************************************
256  * tls_ServerAddCA:
257  *****************************************************************************
258  * Adds one or more certificate authorities.
259  * TODO: we are not able to check the client credentials yet, so this function
260  * is pretty useless.
261  * Returns -1 on error, 0 on success.
262  *****************************************************************************/
263 static int
264 gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path )
265 {
266     int val;
267
268     val = gnutls_certificate_set_x509_trust_file( ((tls_server_sys_t *)
269                                                   (p_server->p_sys))
270                                                     ->x509_cred,
271                                                   psz_ca_path,
272                                                   GNUTLS_X509_FMT_PEM );
273     if( val < 0 )
274     {
275         msg_Err( p_server->p_tls, "Cannot add trusted CA (%s) : %s",
276                  psz_ca_path, gnutls_strerror( val ) );
277         gnutls_ServerDelete( p_server );
278         return VLC_EGENERIC;
279     }
280     msg_Dbg( p_server->p_tls, " %d trusted CA added (%s)", val,
281              psz_ca_path );
282     return VLC_SUCCESS;
283 }
284
285
286 /*****************************************************************************
287  * tls_ServerAddCRL:
288  *****************************************************************************
289  * Adds a certificates revocation list to be sent to TLS clients.
290  * Returns -1 on error, 0 on success.
291  *****************************************************************************/
292 static int
293 gnutls_ServerAddCRL( tls_server_t *p_server, const char *psz_crl_path )
294 {
295     int val;
296
297     val = gnutls_certificate_set_x509_crl_file( ((tls_server_sys_t *)
298                                                 (p_server->p_sys))->x509_cred,
299                                                 psz_crl_path,
300                                                 GNUTLS_X509_FMT_PEM );
301     if( val < 0 )
302     {
303         msg_Err( p_server->p_tls, "Cannot add CRL (%s) : %s",
304                  psz_crl_path, gnutls_strerror( val ) );
305         gnutls_ServerDelete( p_server );
306         return VLC_EGENERIC;
307     }
308     msg_Dbg( p_server->p_tls, "%d CRL added (%s)", val, psz_crl_path );
309     return VLC_SUCCESS;
310 }
311     
312
313 /*****************************************************************************
314  * tls_ServerCreate:
315  *****************************************************************************
316  * Allocates a whole server's TLS credentials.
317  * Returns NULL on error.
318  *****************************************************************************/
319 static tls_server_t *
320 gnutls_ServerCreate( tls_t *p_this, const char *psz_cert_path,
321                   const char *psz_key_path )
322 {
323     tls_server_t *p_server;
324     tls_server_sys_t *p_server_sys;
325     int val;
326
327     msg_Dbg( p_this, "Creating TLS server" );
328
329     p_server_sys = (tls_server_sys_t *)malloc( sizeof(struct tls_server_sys_t) );
330     if( p_server_sys == NULL )
331         return NULL;
332
333     /* Sets server's credentials */
334     val = gnutls_certificate_allocate_credentials( &p_server_sys->x509_cred );
335     if( val != 0 )
336     {
337         msg_Err( p_this, "Cannot allocate X509 credentials : %s",
338                  gnutls_strerror( val ) );
339         free( p_server_sys );
340         return NULL;
341     }
342
343     val = gnutls_certificate_set_x509_key_file( p_server_sys->x509_cred,
344                                                 psz_cert_path, psz_key_path,
345                                                 GNUTLS_X509_FMT_PEM );
346     if( val < 0 )
347     {
348         msg_Err( p_this, "Cannot set certificate chain or private key : %s",
349                  gnutls_strerror( val ) );
350         gnutls_certificate_free_credentials( p_server_sys->x509_cred );
351         free( p_server_sys );
352         return NULL;
353     }
354
355     /* FIXME: regenerate these regularly */
356     val = gnutls_dh_params_init( &p_server_sys->dh_params );
357     if( val >= 0 )
358     {
359         vlc_value_t bits;
360
361         if( var_Get( p_this, "dh-bits", &bits ) != VLC_SUCCESS )
362         {
363             var_Create( p_this, "dh-bits",
364                         VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
365             var_Get( p_this, "dh-bits", &bits );
366         }
367
368         msg_Dbg( p_this, "Computing Diffie Hellman ciphers parameters" );
369         val = gnutls_dh_params_generate2( p_server_sys->dh_params,
370                                           bits.i_int );
371     }
372     if( val < 0 )
373     {
374         msg_Err( p_this, "Cannot initialize DH cipher suites : %s",
375                  gnutls_strerror( val ) );
376         gnutls_certificate_free_credentials( p_server_sys->x509_cred );
377         free( p_server_sys );
378         return NULL;
379     }
380     msg_Dbg( p_this, "Ciphers parameters computed" );
381
382     gnutls_certificate_set_dh_params( p_server_sys->x509_cred,
383                                       p_server_sys->dh_params);
384
385     p_server = (tls_server_t *)malloc( sizeof(struct tls_server_t) );
386     if( p_server == NULL )
387     {
388         free( p_server_sys );
389         return NULL;
390     }
391
392     p_server->p_tls = p_this;
393     p_server->p_sys = p_server_sys;
394     p_server->pf_delete = gnutls_ServerDelete;
395     p_server->pf_add_CA = gnutls_ServerAddCA;
396     p_server->pf_add_CRL = gnutls_ServerAddCRL;
397     p_server->pf_session_prepare = gnutls_ServerSessionPrepare;
398
399     return p_server;
400 }
401
402
403 /*
404  * gcrypt thread option VLC implementation
405  */
406 vlc_object_t *__p_gcry_data;
407
408 static int gcry_vlc_mutex_init (void **p_sys)
409 {
410     int i_val;
411     vlc_mutex_t *p_lock = (vlc_mutex_t *)malloc (sizeof (vlc_mutex_t));
412
413     if( p_lock == NULL)
414         return ENOMEM;
415
416     i_val = vlc_mutex_init( __p_gcry_data, p_lock);
417     if (i_val)
418         free (p_lock);
419     else
420         *p_sys = p_lock;
421     return i_val;
422 }
423
424 static int gcry_vlc_mutex_destroy (void **p_sys)
425 {
426     int i_val;
427     vlc_mutex_t *p_lock = (vlc_mutex_t *)*p_sys;
428
429     i_val = vlc_mutex_destroy (p_lock);
430     free (p_lock);
431     return i_val;
432 }
433
434 static int gcry_vlc_mutex_lock (void **p_sys)
435 {
436     return vlc_mutex_lock ((vlc_mutex_t *)*p_sys);
437 }
438
439 static int gcry_vlc_mutex_unlock (void **lock)
440 {
441     return vlc_mutex_unlock ((vlc_mutex_t *)*lock);
442 }
443
444 static struct gcry_thread_cbs gcry_threads_vlc =                        
445 {
446     GCRY_THREAD_OPTION_USER,
447     NULL,
448     gcry_vlc_mutex_init,
449     gcry_vlc_mutex_destroy,
450     gcry_vlc_mutex_lock,
451     gcry_vlc_mutex_unlock
452 };
453
454
455 static int
456 Open( vlc_object_t *p_this )
457 {
458     tls_t *p_tls = (tls_t *)p_this;
459
460     vlc_value_t lock, count;
461
462     var_Create( p_this->p_libvlc, "tls_mutex", VLC_VAR_MUTEX );
463     var_Get( p_this->p_libvlc, "tls_mutex", &lock );
464     vlc_mutex_lock( lock.p_address );
465
466     /* Initialize GnuTLS only once */
467     var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
468     var_Get( p_this->p_libvlc, "gnutls_count", &count);
469
470     if( count.i_int == 0)
471     {
472         __p_gcry_data = VLC_OBJECT( p_this->p_vlc );
473
474         gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_vlc);
475         if( gnutls_global_init( ) )
476         {
477             msg_Warn( p_this, "cannot initialize GNUTLS" );
478             vlc_mutex_unlock( lock.p_address );
479             return VLC_EGENERIC;
480         }
481         if( gnutls_check_version( "1.0.0" ) == NULL )
482         {
483             gnutls_global_deinit( );
484             vlc_mutex_unlock( lock.p_address );
485             msg_Err( p_this, "unsupported GNUTLS version" );
486             return VLC_EGENERIC;
487         }
488         msg_Dbg( p_this, "GNUTLS initialized" );
489     }
490
491     count.i_int++;
492     var_Set( p_this->p_libvlc, "gnutls_count", count);
493     vlc_mutex_unlock( lock.p_address );
494
495     p_tls->pf_server_create = gnutls_ServerCreate;
496     return VLC_SUCCESS;
497 }
498
499
500 static void
501 Close( vlc_object_t *p_this )
502 {
503     /*tls_t *p_tls = (tls_t *)p_this;
504     tls_sys_t *p_sys = (tls_sys_t *)(p_this->p_sys);*/
505
506     vlc_value_t lock, count;
507
508     var_Create( p_this->p_libvlc, "gnutls_mutex", VLC_VAR_MUTEX );
509     var_Get( p_this->p_libvlc, "gnutls_mutex", &lock );
510     vlc_mutex_lock( lock.p_address );
511
512     var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
513     var_Get( p_this->p_libvlc, "gnutls_count", &count);
514     count.i_int--;
515     var_Set( p_this->p_libvlc, "gnutls_count", count);
516
517     if( count.i_int == 0 )
518     {
519         gnutls_global_deinit( );
520         msg_Dbg( p_this, "GNUTLS deinitialized" );
521     }
522
523     vlc_mutex_unlock( lock.p_address);
524 }