1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2004 VideoLAN
5 * $Id: httpd.c 8263 2004-07-24 09:06:58Z courmisch $
7 * Authors: Remi Denis-Courmont <courmisch@via.ecp.fr>
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.
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.
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 *****************************************************************************/
27 * - client side stuff,
28 * - server-side client cert validation,
29 * - client-side server cert validation (?).
33 /*****************************************************************************
35 *****************************************************************************/
43 #include <gnutls/gnutls.h>
48 /*****************************************************************************
50 *****************************************************************************/
51 static int Open ( vlc_object_t * );
52 static void Close( vlc_object_t * );
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)." )
60 set_description( _("GnuTLS TLS encryption layer") );
61 set_capability( "tls", 1 );
62 set_callbacks( Open, Close );
64 add_integer( "dh-bits", DH_BITS, NULL, DH_BITS_TEXT,
65 DH_BITS_LONGTEXT, VLC_TRUE );
70 typedef struct tls_server_sys_t
72 gnutls_certificate_credentials x509_cred;
73 gnutls_dh_params dh_params;
77 /* client-side session private data */
78 typedef struct tls_client_sys_t
80 gnutls_session session;
81 gnutls_certificate_credentials x509_cred;
85 /*****************************************************************************
87 *****************************************************************************
88 * Sends data through a TLS session.
89 *****************************************************************************/
91 gnutls_Send( tls_session_t *p_session, const char *buf, int i_length )
95 val = gnutls_record_send( *(gnutls_session *)(p_session->p_sys),
97 return val < 0 ? -1 : val;
101 /*****************************************************************************
103 *****************************************************************************
104 * Receives data through a TLS session.
105 *****************************************************************************/
107 gnutls_Recv( tls_session_t *p_session, char *buf, int i_length )
111 val = gnutls_record_recv( *(gnutls_session *)(p_session->p_sys),
113 return val < 0 ? -1 : val;
117 /*****************************************************************************
118 * tls_SessionHandshake:
119 *****************************************************************************
120 * Establishes TLS session with a peer through socket <fd>
121 * Returns NULL on error (do NOT call tls_SessionClose in case of error or
122 * re-use the session structure).
123 *****************************************************************************/
124 static tls_session_t *
125 gnutls_SessionHandshake( tls_session_t *p_session, int fd )
128 gnutls_session *p_sys;
130 p_sys = (gnutls_session *)(p_session->p_sys);
132 gnutls_transport_set_ptr( *p_sys, (gnutls_transport_ptr)fd);
133 val = gnutls_handshake( *p_sys );
136 gnutls_deinit( *p_sys );
137 msg_Err( p_session->p_tls, "TLS handshake failed : %s",
138 gnutls_strerror( val ) );
147 /*****************************************************************************
149 *****************************************************************************
150 * Terminates TLS session and releases session data.
151 *****************************************************************************/
153 gnutls_SessionClose( tls_session_t *p_session )
155 gnutls_session *p_sys;
157 p_sys = (gnutls_session *)(p_session->p_sys);
159 /* On the client-side, credentials are re-allocated per session */
160 if( p_session->p_server == NULL )
161 gnutls_certificate_free_credentials( ((tls_client_sys_t *)p_sys)
164 gnutls_bye( *p_sys, GNUTLS_SHUT_WR );
165 gnutls_deinit( *p_sys );
171 /*****************************************************************************
173 *****************************************************************************
174 * Initializes client-side TLS session data.
175 *****************************************************************************/
176 static tls_session_t *
177 gnutls_ClientCreate( tls_t *p_tls, const char *psz_ca_path )
179 tls_session_t *p_session;
180 tls_client_sys_t *p_sys;
182 const int cert_type_priority[3] =
188 p_sys = (tls_client_sys_t *)malloc( sizeof(struct tls_client_sys_t) );
192 i_val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
195 msg_Err( p_tls, "Cannot allocate X509 credentials : %s",
196 gnutls_strerror( i_val ) );
201 if( psz_ca_path != NULL )
203 i_val = gnutls_certificate_set_x509_trust_file( p_sys->x509_cred,
205 GNUTLS_X509_FMT_PEM );
208 msg_Err( p_tls, "Cannot add trusted CA (%s) : %s", psz_ca_path,
209 gnutls_strerror( i_val ) );
210 gnutls_certificate_free_credentials( p_sys->x509_cred );
216 i_val = gnutls_init( &p_sys->session, GNUTLS_CLIENT );
219 msg_Err( p_tls, "Cannot initialize TLS session : %s",
220 gnutls_strerror( i_val ) );
221 gnutls_certificate_free_credentials( p_sys->x509_cred );
226 i_val = gnutls_set_default_priority( p_sys->session );
229 msg_Err( p_tls, "Cannot set ciphers priorities : %s",
230 gnutls_strerror( i_val ) );
231 gnutls_deinit( p_sys->session );
232 gnutls_certificate_free_credentials( p_sys->x509_cred );
237 i_val = gnutls_certificate_type_set_priority( p_sys->session, cert_type_priority );
240 msg_Err( p_tls, "Cannot set certificate type priorities : %s",
241 gnutls_strerror( i_val ) );
242 gnutls_deinit( p_sys->session );
243 gnutls_certificate_free_credentials( p_sys->x509_cred );
248 i_val = gnutls_credentials_set( p_sys->session, GNUTLS_CRD_CERTIFICATE,
252 msg_Err( p_tls, "Cannot set TLS session credentials : %s",
253 gnutls_strerror( i_val ) );
254 gnutls_deinit( p_sys->session );
255 gnutls_certificate_free_credentials( p_sys->x509_cred );
260 p_session = malloc( sizeof (struct tls_session_t) );
261 if( p_session == NULL )
263 gnutls_deinit( p_sys->session );
264 gnutls_certificate_free_credentials( p_sys->x509_cred );
269 p_session->p_tls = p_tls;
270 p_session->p_server = NULL;
271 p_session->p_sys = p_sys;
272 p_session->pf_handshake = gnutls_SessionHandshake;
273 p_session->pf_close = gnutls_SessionClose;
274 p_session->pf_send = gnutls_Send;
275 p_session->pf_recv = gnutls_Recv;
281 /*****************************************************************************
282 * tls_ServerSessionPrepare:
283 *****************************************************************************
284 * Initializes server-side TLS session data.
285 *****************************************************************************/
286 static tls_session_t *
287 gnutls_ServerSessionPrepare( tls_server_t *p_server )
289 tls_session_t *p_session;
290 gnutls_session *p_sys;
294 p_sys = (gnutls_session *)malloc( sizeof(gnutls_session) );
298 val = gnutls_init( p_sys, GNUTLS_SERVER );
301 msg_Err( p_server->p_tls, "Cannot initialize TLS session : %s",
302 gnutls_strerror( val ) );
307 val = gnutls_set_default_priority( *p_sys );
310 msg_Err( p_server->p_tls, "Cannot set ciphers priorities : %s",
311 gnutls_strerror( val ) );
312 gnutls_deinit( *p_sys );
317 val = gnutls_credentials_set( *p_sys, GNUTLS_CRD_CERTIFICATE,
318 ((tls_server_sys_t *)(p_server->p_sys))
322 msg_Err( p_server->p_tls, "Cannot set TLS session credentials : %s",
323 gnutls_strerror( val ) );
324 gnutls_deinit( *p_sys );
329 /* TODO: support for client authentication */
330 /*gnutls_certificate_server_set_request( p_session->session,
331 GNUTLS_CERT_REQUEST ); */
333 if( var_Get( p_server->p_tls, "dh-bits", &bits ) != VLC_SUCCESS )
335 var_Create( p_server->p_tls, "dh-bits",
336 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
337 var_Get( p_server->p_tls, "dh-bits", &bits );
340 gnutls_dh_set_prime_bits( *p_sys, bits.i_int );
342 p_session = malloc( sizeof (struct tls_session_t) );
343 if( p_session == NULL )
345 gnutls_deinit( *p_sys );
350 p_session->p_tls = p_server->p_tls;
351 p_session->p_server = p_server;
352 p_session->p_sys = p_sys;
353 p_session->pf_handshake = gnutls_SessionHandshake;
354 p_session->pf_close = gnutls_SessionClose;
355 p_session->pf_send = gnutls_Send;
356 p_session->pf_recv = gnutls_Recv;
362 /*****************************************************************************
364 *****************************************************************************
365 * Releases data allocated with tls_ServerCreate.
366 *****************************************************************************/
368 gnutls_ServerDelete( tls_server_t *p_server )
370 gnutls_certificate_free_credentials(
371 ((tls_server_sys_t *)(p_server->p_sys))
373 free( p_server->p_sys );
378 /*****************************************************************************
380 *****************************************************************************
381 * Adds one or more certificate authorities.
382 * TODO: we are not able to check the client credentials yet, so this function
384 * Returns -1 on error, 0 on success.
385 *****************************************************************************/
387 gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path )
391 val = gnutls_certificate_set_x509_trust_file( ((tls_server_sys_t *)
395 GNUTLS_X509_FMT_PEM );
398 msg_Err( p_server->p_tls, "Cannot add trusted CA (%s) : %s",
399 psz_ca_path, gnutls_strerror( val ) );
400 gnutls_ServerDelete( p_server );
403 msg_Dbg( p_server->p_tls, " %d trusted CA added (%s)", val,
409 /*****************************************************************************
411 *****************************************************************************
412 * Adds a certificates revocation list to be sent to TLS clients.
413 * Returns -1 on error, 0 on success.
414 *****************************************************************************/
416 gnutls_ServerAddCRL( tls_server_t *p_server, const char *psz_crl_path )
420 val = gnutls_certificate_set_x509_crl_file( ((tls_server_sys_t *)
421 (p_server->p_sys))->x509_cred,
423 GNUTLS_X509_FMT_PEM );
426 msg_Err( p_server->p_tls, "Cannot add CRL (%s) : %s",
427 psz_crl_path, gnutls_strerror( val ) );
428 gnutls_ServerDelete( p_server );
431 msg_Dbg( p_server->p_tls, "%d CRL added (%s)", val, psz_crl_path );
436 /*****************************************************************************
438 *****************************************************************************
439 * Allocates a whole server's TLS credentials.
440 * Returns NULL on error.
441 *****************************************************************************/
442 static tls_server_t *
443 gnutls_ServerCreate( tls_t *p_this, const char *psz_cert_path,
444 const char *psz_key_path )
446 tls_server_t *p_server;
447 tls_server_sys_t *p_server_sys;
450 msg_Dbg( p_this, "Creating TLS server" );
452 p_server_sys = (tls_server_sys_t *)malloc( sizeof(struct tls_server_sys_t) );
453 if( p_server_sys == NULL )
456 /* Sets server's credentials */
457 val = gnutls_certificate_allocate_credentials( &p_server_sys->x509_cred );
460 msg_Err( p_this, "Cannot allocate X509 credentials : %s",
461 gnutls_strerror( val ) );
462 free( p_server_sys );
466 val = gnutls_certificate_set_x509_key_file( p_server_sys->x509_cred,
467 psz_cert_path, psz_key_path,
468 GNUTLS_X509_FMT_PEM );
471 msg_Err( p_this, "Cannot set certificate chain or private key : %s",
472 gnutls_strerror( val ) );
473 gnutls_certificate_free_credentials( p_server_sys->x509_cred );
474 free( p_server_sys );
478 /* FIXME: regenerate these regularly */
479 val = gnutls_dh_params_init( &p_server_sys->dh_params );
484 if( var_Get( p_this, "dh-bits", &bits ) != VLC_SUCCESS )
486 var_Create( p_this, "dh-bits",
487 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
488 var_Get( p_this, "dh-bits", &bits );
491 msg_Dbg( p_this, "Computing Diffie Hellman ciphers parameters" );
492 val = gnutls_dh_params_generate2( p_server_sys->dh_params,
497 msg_Err( p_this, "Cannot initialize DH cipher suites : %s",
498 gnutls_strerror( val ) );
499 gnutls_certificate_free_credentials( p_server_sys->x509_cred );
500 free( p_server_sys );
503 msg_Dbg( p_this, "Ciphers parameters computed" );
505 gnutls_certificate_set_dh_params( p_server_sys->x509_cred,
506 p_server_sys->dh_params);
508 p_server = (tls_server_t *)malloc( sizeof(struct tls_server_t) );
509 if( p_server == NULL )
511 free( p_server_sys );
515 p_server->p_tls = p_this;
516 p_server->p_sys = p_server_sys;
517 p_server->pf_delete = gnutls_ServerDelete;
518 p_server->pf_add_CA = gnutls_ServerAddCA;
519 p_server->pf_add_CRL = gnutls_ServerAddCRL;
520 p_server->pf_session_prepare = gnutls_ServerSessionPrepare;
526 /*****************************************************************************
527 * gcrypt thread option VLC implementation:
528 *****************************************************************************/
529 vlc_object_t *__p_gcry_data;
531 static int gcry_vlc_mutex_init (void **p_sys)
534 vlc_mutex_t *p_lock = (vlc_mutex_t *)malloc (sizeof (vlc_mutex_t));
539 i_val = vlc_mutex_init( __p_gcry_data, p_lock);
547 static int gcry_vlc_mutex_destroy (void **p_sys)
550 vlc_mutex_t *p_lock = (vlc_mutex_t *)*p_sys;
552 i_val = vlc_mutex_destroy (p_lock);
557 static int gcry_vlc_mutex_lock (void **p_sys)
559 return vlc_mutex_lock ((vlc_mutex_t *)*p_sys);
562 static int gcry_vlc_mutex_unlock (void **lock)
564 return vlc_mutex_unlock ((vlc_mutex_t *)*lock);
567 static struct gcry_thread_cbs gcry_threads_vlc =
569 GCRY_THREAD_OPTION_USER,
572 gcry_vlc_mutex_destroy,
574 gcry_vlc_mutex_unlock
578 /*****************************************************************************
579 * Module initialization
580 *****************************************************************************/
582 Open( vlc_object_t *p_this )
584 tls_t *p_tls = (tls_t *)p_this;
586 vlc_value_t lock, count;
588 var_Create( p_this->p_libvlc, "tls_mutex", VLC_VAR_MUTEX );
589 var_Get( p_this->p_libvlc, "tls_mutex", &lock );
590 vlc_mutex_lock( lock.p_address );
592 /* Initialize GnuTLS only once */
593 var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
594 var_Get( p_this->p_libvlc, "gnutls_count", &count);
596 if( count.i_int == 0)
598 __p_gcry_data = VLC_OBJECT( p_this->p_vlc );
600 gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_vlc);
601 if( gnutls_global_init( ) )
603 msg_Warn( p_this, "cannot initialize GNUTLS" );
604 vlc_mutex_unlock( lock.p_address );
607 if( gnutls_check_version( "1.0.0" ) == NULL )
609 gnutls_global_deinit( );
610 vlc_mutex_unlock( lock.p_address );
611 msg_Err( p_this, "unsupported GNUTLS version" );
614 msg_Dbg( p_this, "GNUTLS initialized" );
618 var_Set( p_this->p_libvlc, "gnutls_count", count);
619 vlc_mutex_unlock( lock.p_address );
621 p_tls->pf_server_create = gnutls_ServerCreate;
622 p_tls->pf_client_create = gnutls_ClientCreate;
627 /*****************************************************************************
628 * Module deinitialization
629 *****************************************************************************/
631 Close( vlc_object_t *p_this )
633 /*tls_t *p_tls = (tls_t *)p_this;
634 tls_sys_t *p_sys = (tls_sys_t *)(p_this->p_sys);*/
636 vlc_value_t lock, count;
638 var_Create( p_this->p_libvlc, "gnutls_mutex", VLC_VAR_MUTEX );
639 var_Get( p_this->p_libvlc, "gnutls_mutex", &lock );
640 vlc_mutex_lock( lock.p_address );
642 var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
643 var_Get( p_this->p_libvlc, "gnutls_count", &count);
645 var_Set( p_this->p_libvlc, "gnutls_count", count);
647 if( count.i_int == 0 )
649 gnutls_global_deinit( );
650 msg_Dbg( p_this, "GNUTLS deinitialized" );
653 vlc_mutex_unlock( lock.p_address);