1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2004-2005 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 * - server-side client cert validation,
28 * - client-side server cert validation (?).
32 /*****************************************************************************
34 *****************************************************************************/
42 #include <gnutls/gnutls.h>
45 #define CACHE_EXPIRATION 3600
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)." )
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 " \
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." )
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 );
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 );
86 #define MAX_SESSION_ID 32
87 #define MAX_SESSION_DATA 1024
89 typedef struct saved_session_t
91 char id[MAX_SESSION_ID];
92 char data[MAX_SESSION_DATA];
99 typedef struct tls_server_sys_t
101 gnutls_certificate_credentials x509_cred;
102 gnutls_dh_params dh_params;
104 struct saved_session_t *p_cache;
105 struct saved_session_t *p_store;
107 vlc_mutex_t cache_lock;
111 typedef struct tls_session_sys_t
113 gnutls_session session;
117 typedef struct tls_client_sys_t
119 struct tls_session_sys_t session;
120 gnutls_certificate_credentials x509_cred;
125 _get_Int (vlc_object_t *p_this, const char *var)
129 if( var_Get( p_this, var, &value ) != VLC_SUCCESS )
131 var_Create( p_this, var, VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
132 var_Get( p_this, var, &value );
138 #define get_Int( a, b ) _get_Int( (vlc_object_t *)(a), (b) )
141 /*****************************************************************************
143 *****************************************************************************
144 * Sends data through a TLS session.
145 *****************************************************************************/
147 gnutls_Send( void *p_session, const void *buf, int i_length )
150 tls_session_sys_t *p_sys;
152 p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
154 val = gnutls_record_send( p_sys->session, buf, i_length );
155 /* TODO: handle fatal error */
156 return val < 0 ? -1 : val;
160 /*****************************************************************************
162 *****************************************************************************
163 * Receives data through a TLS session.
164 *****************************************************************************/
166 gnutls_Recv( void *p_session, void *buf, int i_length )
169 tls_session_sys_t *p_sys;
171 p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
173 val = gnutls_record_recv( p_sys->session, buf, i_length );
174 /* TODO: handle fatal error */
175 return val < 0 ? -1 : val;
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 *****************************************************************************/
188 gnutls_SessionContinueHandshake( tls_session_t *p_session)
190 tls_session_sys_t *p_sys;
193 p_sys = (tls_session_sys_t *)(p_session->p_sys);
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 );
202 gnutls_deinit( p_sys->session );
203 msg_Err( p_session->p_tls, "TLS handshake failed : %s",
204 gnutls_strerror( val ) );
214 gnutls_SessionHandshake( tls_session_t *p_session, int fd )
216 tls_session_sys_t *p_sys;
218 p_sys = (tls_session_sys_t *)(p_session->p_sys);
220 gnutls_transport_set_ptr (p_sys->session, (gnutls_transport_ptr)fd);
222 return gnutls_SessionContinueHandshake( p_session );
226 /*****************************************************************************
228 *****************************************************************************
229 * Terminates TLS session and releases session data.
230 *****************************************************************************/
232 gnutls_SessionClose( tls_session_t *p_session )
234 tls_session_sys_t *p_sys;
236 p_sys = (tls_session_sys_t *)(p_session->p_sys);
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)
243 gnutls_bye( p_sys->session, GNUTLS_SHUT_WR );
244 gnutls_deinit( p_sys->session );
250 /*****************************************************************************
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 )
258 tls_session_t *p_session;
259 tls_client_sys_t *p_sys;
261 const int cert_type_priority[3] =
267 p_sys = (tls_client_sys_t *)malloc( sizeof(struct tls_client_sys_t) );
271 i_val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
274 msg_Err( p_tls, "Cannot allocate X509 credentials : %s",
275 gnutls_strerror( i_val ) );
280 if( psz_ca_path != NULL )
282 i_val = gnutls_certificate_set_x509_trust_file( p_sys->x509_cred,
284 GNUTLS_X509_FMT_PEM );
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 );
295 i_val = gnutls_init( &p_sys->session.session, GNUTLS_CLIENT );
298 msg_Err( p_tls, "Cannot initialize TLS session : %s",
299 gnutls_strerror( i_val ) );
300 gnutls_certificate_free_credentials( p_sys->x509_cred );
305 i_val = gnutls_set_default_priority( p_sys->session.session );
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 );
316 i_val = gnutls_certificate_type_set_priority( p_sys->session.session,
317 cert_type_priority );
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 );
328 i_val = gnutls_credentials_set( p_sys->session.session,
329 GNUTLS_CRD_CERTIFICATE,
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 );
341 p_session = malloc( sizeof (struct tls_session_t) );
342 if( p_session == NULL )
344 gnutls_deinit( p_sys->session.session );
345 gnutls_certificate_free_credentials( p_sys->x509_cred );
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;
364 /*****************************************************************************
365 * TLS session resumption callbacks
366 *****************************************************************************/
367 static int cb_store( void *p_server, gnutls_datum key, gnutls_datum data )
369 tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
371 if( ( p_sys->i_cache_size == 0 )
372 || ( key.size > MAX_SESSION_ID )
373 || ( data.size > MAX_SESSION_DATA ) )
376 vlc_mutex_lock( &p_sys->cache_lock );
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;
384 if( ( p_sys->p_store - p_sys->p_cache ) == p_sys->i_cache_size )
385 p_sys->p_store = p_sys->p_cache;
387 vlc_mutex_unlock( &p_sys->cache_lock );
393 static const gnutls_datum err_datum = { NULL, 0 };
395 static gnutls_datum cb_fetch( void *p_server, gnutls_datum key )
397 tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
398 saved_session_t *p_session, *p_end;
400 p_session = p_sys->p_cache;
401 p_end = p_session + p_sys->i_cache_size;
403 vlc_mutex_lock( &p_sys->cache_lock );
405 while( p_session < p_end )
407 if( ( p_session->i_idlen == key.size )
408 && !memcmp( p_session->id, key.data, key.size ) )
412 data.size = p_session->i_datalen;
414 data.data = gnutls_malloc( data.size );
415 if( data.data == NULL )
417 vlc_mutex_unlock( &p_sys->cache_lock );
421 memcpy( data.data, p_session->data, data.size );
422 vlc_mutex_unlock( &p_sys->cache_lock );
428 vlc_mutex_unlock( &p_sys->cache_lock );
434 static int cb_delete( void *p_server, gnutls_datum key )
436 tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
437 saved_session_t *p_session, *p_end;
439 p_session = p_sys->p_cache;
440 p_end = p_session + p_sys->i_cache_size;
442 vlc_mutex_lock( &p_sys->cache_lock );
444 while( p_session < p_end )
446 if( ( p_session->i_idlen == key.size )
447 && !memcmp( p_session->id, key.data, key.size ) )
449 p_session->i_datalen = p_session->i_idlen = 0;
450 vlc_mutex_unlock( &p_sys->cache_lock );
456 vlc_mutex_unlock( &p_sys->cache_lock );
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 )
470 tls_session_t *p_session;
471 gnutls_session session;
474 i_val = gnutls_init( &session, GNUTLS_SERVER );
477 msg_Err( p_server->p_tls, "Cannot initialize TLS session : %s",
478 gnutls_strerror( i_val ) );
482 i_val = gnutls_set_default_priority( session );
485 msg_Err( p_server->p_tls, "Cannot set ciphers priorities : %s",
486 gnutls_strerror( i_val ) );
487 gnutls_deinit( session );
491 i_val = gnutls_credentials_set( session, GNUTLS_CRD_CERTIFICATE,
492 ((tls_server_sys_t *)(p_server->p_sys))
496 msg_Err( p_server->p_tls, "Cannot set TLS session credentials : %s",
497 gnutls_strerror( i_val ) );
498 gnutls_deinit( session );
502 /* TODO: support for client authentication */
503 /*gnutls_certificate_server_set_request( p_session->session,
504 GNUTLS_CERT_REQUEST ); */
506 gnutls_dh_set_prime_bits( session, get_Int( p_server->p_tls, "dh-bits" ) );
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 );
516 p_session = malloc( sizeof (struct tls_session_t) );
517 if( p_session == NULL )
519 gnutls_deinit( session );
523 p_session->p_sys = (tls_session_sys_t *)malloc( sizeof(struct tls_session_sys_t) );
524 if( p_session->p_sys == NULL )
526 gnutls_deinit( session );
531 ((tls_session_sys_t *)p_session->p_sys)->session = session;
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;
546 /*****************************************************************************
548 *****************************************************************************
549 * Releases data allocated with tls_ServerCreate.
550 *****************************************************************************/
552 gnutls_ServerDelete( tls_server_t *p_server )
554 tls_server_sys_t *p_sys;
556 p_sys = (tls_server_sys_t *)p_server->p_sys;
558 gnutls_certificate_free_credentials( p_sys->x509_cred );
559 free( p_sys->p_cache );
560 vlc_mutex_destroy( &p_sys->cache_lock );
566 /*****************************************************************************
568 *****************************************************************************
569 * Adds one or more certificate authorities.
570 * TODO: we are not able to check the client credentials yet, so this function
572 * Returns -1 on error, 0 on success.
573 *****************************************************************************/
575 gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path )
579 val = gnutls_certificate_set_x509_trust_file( ((tls_server_sys_t *)
583 GNUTLS_X509_FMT_PEM );
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 );
591 msg_Dbg( p_server->p_tls, " %d trusted CA added (%s)", val,
597 /*****************************************************************************
599 *****************************************************************************
600 * Adds a certificates revocation list to be sent to TLS clients.
601 * Returns -1 on error, 0 on success.
602 *****************************************************************************/
604 gnutls_ServerAddCRL( tls_server_t *p_server, const char *psz_crl_path )
608 val = gnutls_certificate_set_x509_crl_file( ((tls_server_sys_t *)
609 (p_server->p_sys))->x509_cred,
611 GNUTLS_X509_FMT_PEM );
614 msg_Err( p_server->p_tls, "Cannot add CRL (%s) : %s",
615 psz_crl_path, gnutls_strerror( val ) );
616 gnutls_ServerDelete( p_server );
619 msg_Dbg( p_server->p_tls, "%d CRL added (%s)", val, psz_crl_path );
624 /*****************************************************************************
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 )
634 tls_server_t *p_server;
635 tls_server_sys_t *p_server_sys;
638 msg_Dbg( p_this, "Creating TLS server" );
640 p_server_sys = (tls_server_sys_t *)malloc( sizeof(struct tls_server_sys_t) );
641 if( p_server_sys == NULL )
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 )
650 free( p_server_sys );
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 );
657 /* Sets server's credentials */
658 val = gnutls_certificate_allocate_credentials( &p_server_sys->x509_cred );
661 msg_Err( p_this, "Cannot allocate X509 credentials : %s",
662 gnutls_strerror( val ) );
663 free( p_server_sys );
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 );
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 );
680 * - regenerate these regularly
681 * - support other ciper suites
683 val = gnutls_dh_params_init( &p_server_sys->dh_params );
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" ) );
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 );
698 msg_Dbg( p_this, "Ciphers parameters computed" );
700 gnutls_certificate_set_dh_params( p_server_sys->x509_cred,
701 p_server_sys->dh_params);
703 p_server = (tls_server_t *)malloc( sizeof(struct tls_server_t) );
704 if( p_server == NULL )
706 free( p_server_sys );
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;
721 /*****************************************************************************
722 * gcrypt thread option VLC implementation:
723 *****************************************************************************/
724 vlc_object_t *__p_gcry_data;
726 static int gcry_vlc_mutex_init( void **p_sys )
729 vlc_mutex_t *p_lock = (vlc_mutex_t *)malloc( sizeof( vlc_mutex_t ) );
734 i_val = vlc_mutex_init( __p_gcry_data, p_lock );
742 static int gcry_vlc_mutex_destroy( void **p_sys )
745 vlc_mutex_t *p_lock = (vlc_mutex_t *)*p_sys;
747 i_val = vlc_mutex_destroy( p_lock );
752 static int gcry_vlc_mutex_lock( void **p_sys )
754 return vlc_mutex_lock( (vlc_mutex_t *)*p_sys );
757 static int gcry_vlc_mutex_unlock( void **lock )
759 return vlc_mutex_unlock( (vlc_mutex_t *)*lock );
762 static struct gcry_thread_cbs gcry_threads_vlc =
764 GCRY_THREAD_OPTION_USER,
767 gcry_vlc_mutex_destroy,
769 gcry_vlc_mutex_unlock
773 /*****************************************************************************
774 * Module initialization
775 *****************************************************************************/
777 Open( vlc_object_t *p_this )
779 tls_t *p_tls = (tls_t *)p_this;
781 vlc_value_t lock, count;
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 );
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);
791 if( count.i_int == 0)
793 __p_gcry_data = VLC_OBJECT( p_this->p_vlc );
795 gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_vlc);
796 if( gnutls_global_init( ) )
798 msg_Warn( p_this, "cannot initialize GNUTLS" );
799 vlc_mutex_unlock( lock.p_address );
802 if( gnutls_check_version( "1.0.0" ) == NULL )
804 gnutls_global_deinit( );
805 vlc_mutex_unlock( lock.p_address );
806 msg_Err( p_this, "unsupported GNUTLS version" );
809 msg_Dbg( p_this, "GNUTLS initialized" );
813 var_Set( p_this->p_libvlc, "gnutls_count", count);
814 vlc_mutex_unlock( lock.p_address );
816 p_tls->pf_server_create = gnutls_ServerCreate;
817 p_tls->pf_client_create = gnutls_ClientCreate;
822 /*****************************************************************************
823 * Module deinitialization
824 *****************************************************************************/
826 Close( vlc_object_t *p_this )
828 /*tls_t *p_tls = (tls_t *)p_this;
829 tls_sys_t *p_sys = (tls_sys_t *)(p_this->p_sys);*/
831 vlc_value_t lock, count;
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 );
837 var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
838 var_Get( p_this->p_libvlc, "gnutls_count", &count);
840 var_Set( p_this->p_libvlc, "gnutls_count", count);
842 if( count.i_int == 0 )
844 gnutls_global_deinit( );
845 msg_Dbg( p_this, "GNUTLS deinitialized" );
848 vlc_mutex_unlock( lock.p_address);